2 * file.c - Filesystem related interfaces
4 * Copyright (C) 2001, 2002 Ethan Benson
8 * Copyright (C) 2001 Colin Walters
10 * Copyright (C) 1999 Benjamin Herrenschmidt
12 * This program is free software; you can redistribute it and/or modify
13 * it under the terms of the GNU General Public License as published by
14 * the Free Software Foundation; either version 2 of the License, or
15 * (at your option) any later version.
17 * This program is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 * GNU General Public License for more details.
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, write to the Free Software
24 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
34 #include "partition.h"
39 extern char bootdevice[];
41 /* Convert __u32 into std, dotted quad string, leaks like a sive :( */
45 char *buf = malloc(sizeof("000.000.000.000"));
47 sprintf(buf,"%u.%u.%u.%u",
48 (ip & 0xff000000) >> 24, (ip & 0x00ff0000) >> 16,
49 (ip & 0x0000ff00) >> 8, (ip & 0x000000ff));
55 * Copy the string from source to dest till newline or comma(,) is seen
57 * Move source and dest pointers respectively.
58 * Returns pointer to the start of the string that has just been copied.
61 scopy(char **dest, char **source)
68 while (**source != ',' && **source != '\0')
69 *(*dest)++ = *(*source)++;
78 * Extract all the ipv4 arguments from the bootpath provided and fill result
79 * Returns 1 on success, 0 on failure.
82 extract_ipv4_args(char *imagepath, struct boot_fspec_t *result)
84 char *tmp, *args, *str, *start;
86 args = strrchr(imagepath, ':');
90 start = args; /* used to see if we read any optional parameters */
92 /* The obp-tftp device arguments should be at the end of
93 * the argument list. Skip over any extra arguments (promiscuous,
94 * speed, duplex, bootp, rarp).
97 tmp = strstr(args, "promiscuous");
98 if (tmp && tmp > args)
99 args = tmp + strlen("promiscuous");
101 tmp = strstr(args, "speed=");
102 if (tmp && tmp > args)
103 args = tmp + strlen("speed=");
105 tmp = strstr(args, "duplex=");
106 if (tmp && tmp > args)
107 args = tmp + strlen("duplex=");
109 tmp = strstr(args, "bootp");
110 if (tmp && tmp > args)
111 args = tmp + strlen("bootp");
113 tmp = strstr(args, "rarp");
114 if (tmp && tmp > args)
115 args = tmp + strlen("rarp");
117 if (args != start) /* we read some parameters, so go past the next comma(,) */
118 args = strchr(args, ',');
122 str = malloc(strlen(args) + 1); /*long enough to hold all strings */
127 args++; /* If comma(,) is not immediately followed by ':' then go past the , */
130 * read the arguments in order: siaddr,filename,ciaddr,giaddr,
131 * bootp-retries,tftp-retries,addl_prameters
133 result->siaddr = scopy(&str, &args);
134 result->file = scopy(&str, &args);
135 result->ciaddr = scopy(&str, &args);
136 result->giaddr = scopy(&str, &args);
137 result->bootp_retries = scopy(&str, &args);
138 result->tftp_retries = scopy(&str, &args);
140 result->addl_params = strdup(args);
141 if (!result->addl_params)
148 * Check netinfo for ipv4 parameters and add them to the fspec iff the
149 * fspec has no existing value.
151 * Returns 1 on success, 0 on failure.
154 extract_netinfo_args(struct boot_fspec_t *result)
156 struct bootp_packet *packet;
158 /* Check to see if we can get the [scyg]iaddr fields from netinfo */
159 packet = prom_get_netinfo();
163 DEBUG_F("We have a boot packet\n");
164 DEBUG_F(" siaddr = <%x>\n", packet->siaddr);
165 DEBUG_F(" ciaddr = <%x>\n", packet->ciaddr);
166 DEBUG_F(" yiaddr = <%x>\n", packet->yiaddr);
167 DEBUG_F(" giaddr = <%x>\n", packet->giaddr);
169 /* Try to fallback to yiaddr if ciaddr is empty. Broken? */
170 if (packet->ciaddr == 0 && packet->yiaddr != 0)
171 packet->ciaddr = packet->yiaddr;
173 if ((result->siaddr == NULL || *(result->siaddr) == NULL)
174 && packet->siaddr != 0)
175 result->siaddr = ipv4_to_str(packet->siaddr);
176 if ((result->ciaddr == NULL || *(result->ciaddr) == NULL)
177 && packet->ciaddr != 0)
178 result->ciaddr = ipv4_to_str(packet->ciaddr);
179 if ((result->giaddr == NULL || *(result->giaddr) == NULL)
180 && packet->giaddr != 0)
181 result->giaddr = ipv4_to_str(packet->giaddr);
183 /* FIXME: Yck! if we /still/ do not have a gateway then "cheat" and use
184 * the server. This will be okay if the client and server are on
185 * the same IP network, if not then lets hope the server does ICMP
187 if (result->giaddr == NULL) {
188 result->giaddr = ipv4_to_str(packet->siaddr);
189 DEBUG_F("Forcing giaddr to siaddr <%s>\n", result->giaddr);
196 * Extract all the arguments provided in the imagepath and fill it in result.
197 * Returns 1 on success, 0 on failure.
200 extract_netboot_args(char *imagepath, struct boot_fspec_t *result)
204 DEBUG_F("imagepath = %s\n", imagepath);
209 ret = extract_ipv4_args(imagepath, result);
210 ret |= extract_netinfo_args(result);
212 DEBUG_F("siaddr = <%s>\n", result->siaddr);
213 DEBUG_F("file = <%s>\n", result->file);
214 DEBUG_F("ciaddr = <%s>\n", result->ciaddr);
215 DEBUG_F("giaddr = <%s>\n", result->giaddr);
216 DEBUG_F("bootp_retries = <%s>\n", result->bootp_retries);
217 DEBUG_F("tftp_retries = <%s>\n", result->tftp_retries);
218 DEBUG_F("addl_params = <%s>\n", result->addl_params);
223 static char *netdev_path_to_dev(const char *path)
228 DEBUG_F("path = %s\n", path);
233 tmp = strchr(path, ':');
238 len = tmp - path + 1;
242 strncpy(dev, path, len);
248 /* This function follows the device path in the devtree and separates
249 the device name, partition number, and other datas (mostly file name)
250 the string passed in parameters is changed since 0 are put in place
251 of some separators to terminate the various strings.
253 when a default device is supplied imagepath will be assumed to be a
254 plain filename unless it contains a : otherwise if defaultdev is
255 NULL imagepath will be assumed to be a device path.
257 returns 1 on success 0 on failure.
260 - /pci@80000000/pci-bridge@d/ADPT,2930CU@2/@1:4
261 - /pci@80000000/pci-bridge@d/ADPT,2930CU@2/@1:4,/boot/vmlinux
263 - enet:10.0.0.1,/tftpboot/vmlinux
264 - enet:,/tftpboot/vmlinux
267 - arguments for obp-tftp open as specified in section 4.1 of
268 http://playground.sun.com/1275/practice/obp-tftp/tftp1_0.pdf
269 [bootp,]siaddr,filename,ciaddr,giaddr,bootp-retries,tftp-retries
270 ex: enet:bootp,10.0.0.11,bootme,10.0.0.12,10.0.0.1,5,5
271 Supported only if defdevice == NULL
273 - any other device path lacking a :
274 Unsupported examples:
275 - hd:2,\\:tbxi <- no filename will be detected due to the extra :
276 - enet:192.168.2.1,bootme,c-iaddr,g-iaddr,subnet-mask,bootp-retries,tftp-retries */
279 parse_device_path(char *imagepath, char *defdevice, int defpart,
280 char *deffile, struct boot_fspec_t *result)
285 int device_kind = -1;
287 DEBUG_F("imagepath = %s; defdevice %s; defpart %d, deffile %s\n",
288 imagepath, defdevice, defpart, deffile);
298 * Do preliminary checking for an iscsi device; it may appear as
299 * pure a network device (device_type == "network") if this is
300 * ISWI. This is the case on IBM systems doing an iscsi OFW
303 if (strstr(imagepath, TOK_ISCSI)) {
305 * get the virtual device information from the
306 * "nas-bootdevice" property.
308 if (prom_get_chosen("nas-bootdevice", bootdevice, BOOTDEVSZ)) {
309 DEBUG_F("reset boot-device to"
310 " /chosen/nas-bootdevice = %s\n", bootdevice);
311 device_kind = FILE_DEVICE_ISCSI;
312 ipath = strdup(bootdevice);
319 else if (!(ipath = strdup(imagepath)))
323 defdev = strdup(defdevice);
324 device_kind = prom_get_devtype(defdev);
325 } else if (device_kind == -1)
326 device_kind = prom_get_devtype(ipath);
329 * When an iscsi iqn is present, it may have embedded colons, so
330 * don't parse off anything.
332 if (device_kind != FILE_DEVICE_NET &&
333 device_kind != FILE_DEVICE_ISCSI &&
334 strchr(defdev, ':') != NULL) {
335 if ((ptr = strrchr(defdev, ':')) != NULL)
336 *ptr = 0; /* remove trailing : from defdevice if necessary */
339 /* This will not properly handle an obp-tftp argument list
340 * with elements after the filename; that is handled below.
342 if (device_kind != FILE_DEVICE_NET &&
343 device_kind != FILE_DEVICE_ISCSI &&
344 strchr(ipath, ':') != NULL) {
345 if ((ptr = strrchr(ipath, ',')) != NULL) {
346 char *colon = strrchr(ipath, ':');
347 /* If a ':' occurs *after* a ',', then we assume that there is
349 if (!colon || colon < ptr) {
350 result->file = strdup(ptr+1);
351 /* Trim the filename off */
357 if (device_kind == FILE_DEVICE_NET) {
358 if (strchr(ipath, ':')) {
359 if (extract_netboot_args(ipath, result) == 0)
362 /* If we didn't get a ':' then look only in netinfo */
363 extract_netinfo_args(result);
364 result->file = strdup(ipath);
368 result->dev = netdev_path_to_dev(ipath);
369 } else if (device_kind != FILE_DEVICE_ISCSI &&
370 (ptr = strrchr(ipath, ':')) != NULL) {
372 result->dev = strdup(ipath);
374 result->part = simple_strtol(ptr+1, NULL, 10);
375 } else if (!defdev) {
376 result->dev = strdup(ipath);
377 } else if (strlen(ipath)) {
378 result->file = strdup(ipath);
384 if (!result->dev && defdev)
385 result->dev = strdup(defdev);
387 if (result->part < 0)
388 result->part = defpart;
391 result->file = strdup(deffile);
401 file_block_open( struct boot_file_t* file,
402 struct boot_fspec_t* fspec,
405 struct partition_t* parts;
406 struct partition_t* p;
407 struct partition_t* found;
409 parts = partitions_lookup(fspec->dev);
414 prom_printf("partitions:\n");
416 prom_printf("no partitions found.\n");
418 for (p = parts; p && !found; p=p->next) {
419 DEBUG_F("number: %02d, start: 0x%08lx, length: 0x%08lx\n",
420 p->part_number, p->part_start, p->part_size );
421 if (partition == -1) {
422 file->fs = fs_open( file, p, fspec );
423 if (file->fs == NULL || fserrorno != FILE_ERR_OK)
426 partition = p->part_number;
430 if ((partition >= 0) && (partition == p->part_number))
434 prom_printf(" (match)\n");
438 /* Note: we don't skip when found is NULL since we can, in some
439 * cases, let OF figure out a default partition.
441 DEBUG_F( "Using OF defaults.. (found = %p)\n", found );
442 file->fs = fs_open( file, found, fspec );
446 partitions_free(parts);
452 file_net_open(struct boot_file_t* file, struct boot_fspec_t *fspec)
454 file->fs = fs_of_netboot;
455 return fs_of_netboot->open(file, NULL, fspec);
459 default_read( struct boot_file_t* file,
463 prom_printf("WARNING ! default_read called !\n");
468 default_seek( struct boot_file_t* file,
471 prom_printf("WARNING ! default_seek called !\n");
476 default_close( struct boot_file_t* file)
478 prom_printf("WARNING ! default_close called !\n");
482 static struct fs_t fs_default =
492 int open_file(struct boot_fspec_t* spec, struct boot_file_t* file)
496 memset(file, 0, sizeof(struct boot_file_t*));
497 file->fs = &fs_default;
499 DEBUG_F("dev_path = %s\nfile_name = %s\npartition = %d\n",
500 spec->dev, spec->file, spec->part);
502 result = prom_get_devtype(spec->dev);
504 file->device_kind = result;
508 switch(file->device_kind) {
509 case FILE_DEVICE_BLOCK:
510 DEBUG_F("device is a block device\n");
511 return file_block_open(file, spec, spec->part);
512 case FILE_DEVICE_NET:
513 DEBUG_F("device is a network device\n");
514 return file_net_open(file, spec);
521 * c-file-style: "k&r"