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)
156 #define DHCP_COOKIE 0x63825363
157 #define DHCP_COOKIE_SIZE 4
160 * Process the bootp reply packet's vendor extensions.
161 * Vendor extensions are detailed in: http://www.faqs.org/rfcs/rfc1084.html
164 extract_vendor_options(struct bootp_packet *packet, struct boot_fspec_t *result)
168 __u8 *options = &packet->options[0];
170 memcpy(&cookie, &options[i], DHCP_COOKIE_SIZE);
172 if (cookie != DHCP_COOKIE) {
173 prom_printf("EEEK! cookie is fubar got %08x expected %08x\n",
174 cookie, DHCP_COOKIE);
178 i += DHCP_COOKIE_SIZE;
180 /* FIXME: It may be possible to run off the end of a packet here /if/
181 * it's malformed. :( */
182 while (options[i] != DHCP_END) {
183 __u8 tag = options[i++], len;
190 memcpy(&value, &options[i], len);
194 DEBUG_F("tag=%2d, len=%2d, data=", tag, len);
196 for (j=0; j<len; j++)
197 prom_printf("%02x", options[i+j]);
204 /* FIXME: do we need to grok the subnet mask? */
207 if ((result->giaddr == NULL || *(result->giaddr) == '\x0')
209 result->giaddr = ipv4_to_str(value);
210 DEBUG_F("Storing %s as gateway from options\n",
220 * Check netinfo for ipv4 parameters and add them to the fspec iff the
221 * fspec has no existing value.
223 * Returns 1 on success, 0 on failure.
226 extract_netinfo_args(struct boot_fspec_t *result)
228 struct bootp_packet *packet;
230 /* Check to see if we can get the [scyg]iaddr fields from netinfo */
231 packet = prom_get_netinfo();
235 DEBUG_F("We have a boot packet\n");
236 DEBUG_F(" siaddr = <%x>\n", packet->siaddr);
237 DEBUG_F(" ciaddr = <%x>\n", packet->ciaddr);
238 DEBUG_F(" yiaddr = <%x>\n", packet->yiaddr);
239 DEBUG_F(" giaddr = <%x>\n", packet->giaddr);
241 /* Try to fallback to yiaddr if ciaddr is empty. Broken? */
242 if (packet->ciaddr == 0 && packet->yiaddr != 0)
243 packet->ciaddr = packet->yiaddr;
245 if ((result->siaddr == NULL || *(result->siaddr) == '\x0')
246 && packet->siaddr != 0)
247 result->siaddr = ipv4_to_str(packet->siaddr);
248 if ((result->ciaddr == NULL || *(result->ciaddr) == '\x0')
249 && packet->ciaddr != 0)
250 result->ciaddr = ipv4_to_str(packet->ciaddr);
251 if ((result->giaddr == NULL || *(result->giaddr) == '\x0')
252 && packet->giaddr != 0)
253 result->giaddr = ipv4_to_str(packet->giaddr);
255 extract_vendor_options(packet, result);
257 /* FIXME: Yck! if we /still/ do not have a gateway then "cheat" and use
258 * the server. This will be okay if the client and server are on
259 * the same IP network, if not then lets hope the server does ICMP
261 if (result->giaddr == NULL) {
262 result->giaddr = ipv4_to_str(packet->siaddr);
263 DEBUG_F("Forcing giaddr to siaddr <%s>\n", result->giaddr);
270 * Extract all the arguments provided in the imagepath and fill it in result.
271 * Returns 1 on success, 0 on failure.
274 extract_netboot_args(char *imagepath, struct boot_fspec_t *result)
278 DEBUG_F("imagepath = %s\n", imagepath);
283 ret = extract_ipv4_args(imagepath, result);
284 ret |= extract_netinfo_args(result);
286 DEBUG_F("siaddr = <%s>\n", result->siaddr);
287 DEBUG_F("file = <%s>\n", result->file);
288 DEBUG_F("ciaddr = <%s>\n", result->ciaddr);
289 DEBUG_F("giaddr = <%s>\n", result->giaddr);
290 DEBUG_F("bootp_retries = <%s>\n", result->bootp_retries);
291 DEBUG_F("tftp_retries = <%s>\n", result->tftp_retries);
292 DEBUG_F("addl_params = <%s>\n", result->addl_params);
297 static char *netdev_path_to_dev(const char *path)
302 DEBUG_F("path = %s\n", path);
307 tmp = strchr(path, ':');
312 len = tmp - path + 1;
316 strncpy(dev, path, len);
322 /* This function follows the device path in the devtree and separates
323 the device name, partition number, and other datas (mostly file name)
324 the string passed in parameters is changed since 0 are put in place
325 of some separators to terminate the various strings.
327 when a default device is supplied imagepath will be assumed to be a
328 plain filename unless it contains a : otherwise if defaultdev is
329 NULL imagepath will be assumed to be a device path.
331 returns 1 on success 0 on failure.
334 - /pci@80000000/pci-bridge@d/ADPT,2930CU@2/@1:4
335 - /pci@80000000/pci-bridge@d/ADPT,2930CU@2/@1:4,/boot/vmlinux
337 - enet:10.0.0.1,/tftpboot/vmlinux
338 - enet:,/tftpboot/vmlinux
341 - arguments for obp-tftp open as specified in section 4.1 of
342 http://playground.sun.com/1275/practice/obp-tftp/tftp1_0.pdf
343 [bootp,]siaddr,filename,ciaddr,giaddr,bootp-retries,tftp-retries
344 ex: enet:bootp,10.0.0.11,bootme,10.0.0.12,10.0.0.1,5,5
345 Supported only if defdevice == NULL
347 - any other device path lacking a :
348 Unsupported examples:
349 - hd:2,\\:tbxi <- no filename will be detected due to the extra :
350 - enet:192.168.2.1,bootme,c-iaddr,g-iaddr,subnet-mask,bootp-retries,tftp-retries */
353 parse_device_path(char *imagepath, char *defdevice, int defpart,
354 char *deffile, struct boot_fspec_t *result)
359 int device_kind = -1;
361 DEBUG_F("imagepath = %s; defdevice %s; defpart %d, deffile %s\n",
362 imagepath, defdevice, defpart, deffile);
372 * Do preliminary checking for an iscsi device; it may appear as
373 * pure a network device (device_type == "network") if this is
374 * ISWI. This is the case on IBM systems doing an iscsi OFW
377 if (strstr(imagepath, TOK_ISCSI)) {
379 * get the virtual device information from the
380 * "nas-bootdevice" property.
382 if (prom_get_chosen("nas-bootdevice", bootdevice, BOOTDEVSZ)) {
383 DEBUG_F("reset boot-device to"
384 " /chosen/nas-bootdevice = %s\n", bootdevice);
385 device_kind = FILE_DEVICE_ISCSI;
386 ipath = strdup(bootdevice);
393 else if (!(ipath = strdup(imagepath)))
397 defdev = strdup(defdevice);
398 device_kind = prom_get_devtype(defdev);
399 } else if (device_kind == -1)
400 device_kind = prom_get_devtype(ipath);
403 * When an iscsi iqn is present, it may have embedded colons, so
404 * don't parse off anything.
406 if (device_kind != FILE_DEVICE_NET &&
407 device_kind != FILE_DEVICE_ISCSI &&
408 strchr(defdev, ':') != NULL) {
409 if ((ptr = strrchr(defdev, ':')) != NULL)
410 *ptr = 0; /* remove trailing : from defdevice if necessary */
413 /* This will not properly handle an obp-tftp argument list
414 * with elements after the filename; that is handled below.
416 if (device_kind != FILE_DEVICE_NET &&
417 device_kind != FILE_DEVICE_ISCSI &&
418 strchr(ipath, ':') != NULL) {
419 if ((ptr = strrchr(ipath, ',')) != NULL) {
420 char *colon = strrchr(ipath, ':');
421 /* If a ':' occurs *after* a ',', then we assume that there is
423 if (!colon || colon < ptr) {
424 result->file = strdup(ptr+1);
425 /* Trim the filename off */
431 if (device_kind == FILE_DEVICE_NET) {
432 if (strchr(ipath, ':')) {
433 if (extract_netboot_args(ipath, result) == 0)
436 /* If we didn't get a ':' then look only in netinfo */
437 extract_netinfo_args(result);
438 result->file = strdup(ipath);
442 result->dev = netdev_path_to_dev(ipath);
443 } else if (device_kind != FILE_DEVICE_ISCSI &&
444 (ptr = strrchr(ipath, ':')) != NULL) {
446 result->dev = strdup(ipath);
448 result->part = simple_strtol(ptr+1, NULL, 10);
449 } else if (!defdev) {
450 result->dev = strdup(ipath);
451 } else if (strlen(ipath)) {
452 result->file = strdup(ipath);
458 if (!result->dev && defdev)
459 result->dev = strdup(defdev);
461 if (result->part < 0)
462 result->part = defpart;
465 result->file = strdup(deffile);
475 file_block_open( struct boot_file_t* file,
476 struct boot_fspec_t* fspec,
479 struct partition_t* parts;
480 struct partition_t* p;
481 struct partition_t* found;
483 parts = partitions_lookup(fspec->dev);
488 prom_printf("partitions:\n");
490 prom_printf("no partitions found.\n");
492 for (p = parts; p && !found; p=p->next) {
493 DEBUG_F("number: %02d, start: 0x%08lx, length: 0x%08lx\n",
494 p->part_number, p->part_start, p->part_size );
495 if (partition == -1) {
496 file->fs = fs_open( file, p, fspec );
497 if (file->fs == NULL || fserrorno != FILE_ERR_OK)
500 partition = p->part_number;
504 if ((partition >= 0) && (partition == p->part_number))
508 prom_printf(" (match)\n");
512 /* Note: we don't skip when found is NULL since we can, in some
513 * cases, let OF figure out a default partition.
515 DEBUG_F( "Using OF defaults.. (found = %p)\n", found );
516 file->fs = fs_open( file, found, fspec );
520 partitions_free(parts);
526 file_net_open(struct boot_file_t* file, struct boot_fspec_t *fspec)
528 file->fs = fs_of_netboot;
529 return fs_of_netboot->open(file, NULL, fspec);
533 default_read( struct boot_file_t* file,
537 prom_printf("WARNING ! default_read called !\n");
542 default_seek( struct boot_file_t* file,
545 prom_printf("WARNING ! default_seek called !\n");
550 default_close( struct boot_file_t* file)
552 prom_printf("WARNING ! default_close called !\n");
556 static struct fs_t fs_default =
566 int open_file(struct boot_fspec_t* spec, struct boot_file_t* file)
570 memset(file, 0, sizeof(struct boot_file_t*));
571 file->fs = &fs_default;
573 DEBUG_F("dev_path = %s\nfile_name = %s\npartition = %d\n",
574 spec->dev, spec->file, spec->part);
576 result = prom_get_devtype(spec->dev);
578 file->device_kind = result;
582 switch(file->device_kind) {
583 case FILE_DEVICE_BLOCK:
584 DEBUG_F("device is a block device\n");
585 return file_block_open(file, spec, spec->part);
586 case FILE_DEVICE_NET:
587 DEBUG_F("device is a network device\n");
588 return file_net_open(file, spec);
595 * c-file-style: "k&r"