726f313b1de2672e66f3ecbad69fcfc4a7d844f9
[yaboot.git] / second / file.c
1 /* File related stuff
2    
3    Copyright (C) 1999 Benjamin Herrenschmidt
4    
5    This program is free software; you can redistribute it and/or modify
6    it under the terms of the GNU General Public License as published by
7    the Free Software Foundation; either version 2 of the License, or
8    (at your option) any later version.
9    
10    This program is distributed in the hope that it will be useful,
11    but WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13    GNU General Public License for more details.
14
15    You should have received a copy of the GNU General Public License
16    along with this program; if not, write to the Free Software
17    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.  */
18
19 #include "ctype.h"
20 #include "types.h"
21 #include "stddef.h"
22 #include "stdlib.h"
23 #include "file.h"
24 #include "prom.h"
25 #include "string.h"
26 #include "partition.h"
27 #include "fs.h"
28
29 extern char bootdevice[1024];
30
31 /* This function follows the device path in the devtree and separates
32    the device name, partition number, and other datas (mostly file name)
33    the string passed in parameters is changed since 0 are put in place
34    of some separators to terminate the various strings
35  */
36 char *
37 parse_device_path(char *of_device, char **file_spec, int *partition)
38 {
39         char *p, *last;
40
41         if (file_spec)
42                 *file_spec = NULL;
43         if (partition)
44                 *partition = -1;
45
46         p = strchr(of_device, ':');
47         if (p)
48                 *p = 0;
49         else
50                 return of_device;
51         
52         last = ++p;
53         while(*p && *p != ',') {
54         if (!isdigit (*p)) {
55                         p = last;
56                         break;
57                 }
58                 ++p;
59         }
60         if (p != last) {
61                 *(p++) = 0;
62                 if (partition)
63             *partition = simple_strtol(last, NULL, 10);
64         }
65         if (*p && file_spec)
66                 *file_spec = p;
67                 
68         return of_device;
69
70 }
71
72 int
73 validate_fspec(         struct boot_fspec_t*    spec,
74                         char*                   default_device,
75                         int                     default_part)
76 {
77     if (!spec->file) {
78         spec->file = spec->dev;
79         spec->dev = NULL;
80     }
81     if (spec->part == -1)
82         spec->part = default_part;
83     if (!spec->dev)
84         spec->dev = default_device;
85     if (!spec->file)
86         return FILE_BAD_PATH;
87     else if (spec->file[0] == ',')
88         spec->file++;
89
90     return FILE_ERR_OK;
91 }
92
93 static int
94 file_block_open(        struct boot_file_t*     file,
95                         const char*             dev_name,
96                         const char*             file_name,
97                         int                     partition)
98 {
99         struct partition_t*     parts;
100         struct partition_t*     p;
101         struct partition_t*     found;
102         
103         parts = partitions_lookup(dev_name);
104         found = NULL;
105                         
106 #if DEBUG
107         if (parts)
108                 prom_printf("partitions:\n");
109         else
110                 prom_printf("no partitions found.\n");
111 #endif
112         for (p = parts; p && !found; p=p->next) {
113 #if DEBUG
114                 prom_printf("number: %02d, start: 0x%08lx, length: 0x%08lx\n",
115                         p->part_number, p->part_start, p->part_size );
116 #endif
117                 if (partition == -1) {
118                         file->fs = fs_open( file, dev_name, p, file_name );
119                         if (file->fs != NULL)
120                                 goto bail;
121                 }
122                 if ((partition >= 0) && (partition == p->part_number))
123                         found = p;
124 #if DEBUG
125                 if (found)
126                         prom_printf(" (match)\n");
127 #endif                                          
128         }
129
130         /* Note: we don't skip when found is NULL since we can, in some
131          * cases, let OF figure out a default partition.
132          */
133         DEBUG_F( "Using OF defaults.. (found = 0x%x)\n", found );
134         file->fs = fs_open( file, dev_name, found, file_name );
135
136 bail:
137         if (parts)
138                 partitions_free(parts);
139
140         return file->fs ? FILE_ERR_OK : FILE_ERR_NOTFOUND;
141 }
142
143 static int
144 file_net_open(  struct boot_file_t*     file,
145                 const char*             dev_name,
146                 const char*             file_name)
147 {
148     file->fs = fs_of_netboot;
149     return fs_of_netboot->open(file, dev_name, NULL, file_name);
150 }
151
152 static int
153 default_read(   struct boot_file_t*     file,
154                 unsigned int            size,
155                 void*                   buffer)
156 {
157         prom_printf("WARNING ! default_read called !\n");
158         return FILE_ERR_EOF;
159 }
160
161 static int
162 default_seek(   struct boot_file_t*     file,
163                 unsigned int            newpos)
164 {
165         prom_printf("WARNING ! default_seek called !\n");
166         return FILE_ERR_EOF;
167 }
168
169 static int
170 default_close(  struct boot_file_t*     file)
171 {
172         prom_printf("WARNING ! default_close called !\n");
173         return FILE_ERR_OK;
174 }
175
176 static struct fs_t fs_default =
177 {
178     "defaults",
179     NULL,
180     default_read,
181     default_seek,
182     default_close
183 };
184
185
186 int open_file(  const struct boot_fspec_t*      spec,
187                 struct boot_file_t*             file)
188 {
189         static char     temp[1024];
190         static char     temps[64];
191         char            *dev_name;
192         char            *file_name = NULL;
193         phandle         dev;
194         int             result;
195         int             partition;
196         
197         memset(file, 0, sizeof(struct boot_file_t*));
198         file->fs        = &fs_default;
199
200         /* Lookup the OF device path */
201         /* First, see if a device was specified for the kernel
202          * if not, we hope that the user wants a kernel on the same
203          * drive and partition as yaboot itself */
204         if (!spec->dev)
205                 strcpy(spec->dev, bootdevice);
206         strncpy(temp,spec->dev,1024);
207         dev_name = parse_device_path(temp, &file_name, &partition);
208         if (file_name == NULL)
209                 file_name = (char *)spec->file;
210         if (file_name == NULL) {
211                 prom_printf("booting without a file name not yet supported !\n");
212                 return FILE_ERR_NOTFOUND;
213         }
214         if (partition == -1)
215                 partition = spec->part;
216
217 #if DEBUG
218         prom_printf("dev_path = %s\nfile_name = %s\npartition = %d\n",
219                 dev_name, file_name, partition);
220 #endif  
221         /* Find OF device phandle */
222         dev = prom_finddevice(dev_name);
223         if (dev == PROM_INVALID_HANDLE) {
224                 prom_printf("device not found !\n");
225                 return FILE_ERR_NOTFOUND;
226         }
227 #if DEBUG
228         prom_printf("dev_phandle = %08lx\n", dev);
229 #endif  
230         /* Check the kind of device */
231         result = prom_getprop(dev, "device_type", temps, 63);
232         if (result == -1) {
233                 prom_printf("can't get <device_type> for device\n");
234                 return FILE_ERR_NOTFOUND;
235         }
236         temps[result] = 0;
237         if (!strcmp(temps, "block"))
238                 file->device_kind = FILE_DEVICE_BLOCK;
239         else if (!strcmp(temps, "network"))
240                 file->device_kind = FILE_DEVICE_NET;
241         else {
242                 prom_printf("Unkown device type <%s>\n", temps);
243                 return FILE_ERR_NOTFOUND;
244         }
245         
246         switch(file->device_kind) {
247             case FILE_DEVICE_BLOCK:
248 #if DEBUG
249                 prom_printf("device is a block device\n");
250 #endif
251                 return file_block_open(file, dev_name, file_name, partition);
252             case FILE_DEVICE_NET:
253 #if DEBUG
254                 prom_printf("device is a network device\n");
255 #endif
256                 return file_net_open(file, dev_name, file_name);
257         }
258         return 0;
259 }