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));
54 /* Ensure the string arg is a plausible IPv4 address */
55 static char * is_valid_ipv4_str(char *str)
60 char *ptr=str, *endptr;
65 for (i=0; i<4; i++, ptr = ++endptr) {
66 tmp = strtol(ptr, &endptr, 10);
67 if ((tmp & 0xff) != tmp)
70 /* If we reach the end of the string but we're not in the 4th octet
71 * we have an invalid IP */
72 if (*endptr == '\x0' && i!=3)
75 /* If we have anything other than a NULL or '.' we have an invlaid
77 if (*endptr != '\x0' && *endptr != '.')
80 ip += (tmp << (24-(i*8)));
83 if (ip == 0 || ip == ~0u)
91 * Copy the string from source to dest till newline or comma(,) is seen
93 * Move source and dest pointers respectively.
94 * Returns pointer to the start of the string that has just been copied.
97 scopy(char **dest, char **source)
104 while (**source != ',' && **source != '\0')
105 *(*dest)++ = *(*source)++;
106 if (**source != '\0')
114 * Extract all the ipv4 arguments from the bootpath provided and fill result
115 * Returns 1 on success, 0 on failure.
118 extract_ipv4_args(char *imagepath, struct boot_fspec_t *result)
120 char *tmp, *args, *str, *start;
122 args = strrchr(imagepath, ':');
126 start = args; /* used to see if we read any optional parameters */
128 /* The obp-tftp device arguments should be at the end of
129 * the argument list. Skip over any extra arguments (promiscuous,
130 * speed, duplex, bootp, rarp).
133 tmp = strstr(args, "promiscuous");
134 if (tmp && tmp > args)
135 args = tmp + strlen("promiscuous");
137 tmp = strstr(args, "speed=");
138 if (tmp && tmp > args)
139 args = tmp + strlen("speed=");
141 tmp = strstr(args, "duplex=");
142 if (tmp && tmp > args)
143 args = tmp + strlen("duplex=");
145 tmp = strstr(args, "bootp");
146 if (tmp && tmp > args)
147 args = tmp + strlen("bootp");
149 tmp = strstr(args, "rarp");
150 if (tmp && tmp > args)
151 args = tmp + strlen("rarp");
153 if (args != start) /* we read some parameters, so go past the next comma(,) */
154 args = strchr(args, ',');
158 str = malloc(strlen(args) + 1); /*long enough to hold all strings */
163 args++; /* If comma(,) is not immediately followed by ':' then go past the , */
166 * read the arguments in order: siaddr,filename,ciaddr,giaddr,
167 * bootp-retries,tftp-retries,addl_prameters
169 result->siaddr = is_valid_ipv4_str(scopy(&str, &args));
170 result->file = scopy(&str, &args);
171 result->ciaddr = is_valid_ipv4_str(scopy(&str, &args));
172 result->giaddr = is_valid_ipv4_str(scopy(&str, &args));
173 result->bootp_retries = scopy(&str, &args);
174 result->tftp_retries = scopy(&str, &args);
176 result->addl_params = strdup(args);
177 if (!result->addl_params)
192 #define DHCP_COOKIE 0x63825363
193 #define DHCP_COOKIE_SIZE 4
196 * Process the bootp reply packet's vendor extensions.
197 * Vendor extensions are detailed in: http://www.faqs.org/rfcs/rfc1084.html
200 extract_vendor_options(struct bootp_packet *packet, struct boot_fspec_t *result)
204 __u8 *options = &packet->options[0];
206 memcpy(&cookie, &options[i], DHCP_COOKIE_SIZE);
208 if (cookie != DHCP_COOKIE) {
209 prom_printf("EEEK! cookie is fubar got %08x expected %08x\n",
210 cookie, DHCP_COOKIE);
214 i += DHCP_COOKIE_SIZE;
216 /* FIXME: It may be possible to run off the end of a packet here /if/
217 * it's malformed. :( */
218 while (options[i] != DHCP_END) {
219 __u8 tag = options[i++], len;
226 memcpy(&value, &options[i], len);
230 DEBUG_F("tag=%2d, len=%2d, data=", tag, len);
232 for (j=0; j<len; j++)
233 prom_printf("%02x", options[i+j]);
240 /* FIXME: do we need to grok the subnet mask? */
243 if ((result->giaddr == NULL || *(result->giaddr) == '\x0')
245 result->giaddr = ipv4_to_str(value);
246 DEBUG_F("Storing %s as gateway from options\n",
256 * Check netinfo for ipv4 parameters and add them to the fspec iff the
257 * fspec has no existing value.
259 * Returns 1 on success, 0 on failure.
262 extract_netinfo_args(struct boot_fspec_t *result)
264 struct bootp_packet *packet;
266 /* Check to see if we can get the [scyg]iaddr fields from netinfo */
267 packet = prom_get_netinfo();
271 DEBUG_F("We have a boot packet\n");
272 DEBUG_F(" siaddr = <%x>\n", packet->siaddr);
273 DEBUG_F(" ciaddr = <%x>\n", packet->ciaddr);
274 DEBUG_F(" yiaddr = <%x>\n", packet->yiaddr);
275 DEBUG_F(" giaddr = <%x>\n", packet->giaddr);
277 /* Try to fallback to yiaddr if ciaddr is empty. Broken? */
278 if (packet->ciaddr == 0 && packet->yiaddr != 0)
279 packet->ciaddr = packet->yiaddr;
281 if ((result->siaddr == NULL || *(result->siaddr) == '\x0')
282 && packet->siaddr != 0)
283 result->siaddr = ipv4_to_str(packet->siaddr);
284 if ((result->ciaddr == NULL || *(result->ciaddr) == '\x0')
285 && packet->ciaddr != 0)
286 result->ciaddr = ipv4_to_str(packet->ciaddr);
287 if ((result->giaddr == NULL || *(result->giaddr) == '\x0')
288 && packet->giaddr != 0)
289 result->giaddr = ipv4_to_str(packet->giaddr);
291 extract_vendor_options(packet, result);
293 /* FIXME: Yck! if we /still/ do not have a gateway then "cheat" and use
294 * the server. This will be okay if the client and server are on
295 * the same IP network, if not then lets hope the server does ICMP
297 if (result->giaddr == NULL) {
298 result->giaddr = ipv4_to_str(packet->siaddr);
299 DEBUG_F("Forcing giaddr to siaddr <%s>\n", result->giaddr);
306 * Extract all the arguments provided in the imagepath and fill it in result.
307 * Returns 1 on success, 0 on failure.
310 extract_netboot_args(char *imagepath, struct boot_fspec_t *result)
314 DEBUG_F("imagepath = %s\n", imagepath);
319 ret = extract_ipv4_args(imagepath, result);
320 ret |= extract_netinfo_args(result);
322 DEBUG_F("siaddr = <%s>\n", result->siaddr);
323 DEBUG_F("file = <%s>\n", result->file);
324 DEBUG_F("ciaddr = <%s>\n", result->ciaddr);
325 DEBUG_F("giaddr = <%s>\n", result->giaddr);
326 DEBUG_F("bootp_retries = <%s>\n", result->bootp_retries);
327 DEBUG_F("tftp_retries = <%s>\n", result->tftp_retries);
328 DEBUG_F("addl_params = <%s>\n", result->addl_params);
333 static char *netdev_path_to_dev(const char *path)
338 DEBUG_F("path = %s\n", path);
343 tmp = strchr(path, ':');
348 len = tmp - path + 1;
352 strncpy(dev, path, len);
358 /* This function follows the device path in the devtree and separates
359 the device name, partition number, and other datas (mostly file name)
360 the string passed in parameters is changed since 0 are put in place
361 of some separators to terminate the various strings.
363 when a default device is supplied imagepath will be assumed to be a
364 plain filename unless it contains a : otherwise if defaultdev is
365 NULL imagepath will be assumed to be a device path.
367 returns 1 on success 0 on failure.
370 - /pci@80000000/pci-bridge@d/ADPT,2930CU@2/@1:4
371 - /pci@80000000/pci-bridge@d/ADPT,2930CU@2/@1:4,/boot/vmlinux
373 - enet:10.0.0.1,/tftpboot/vmlinux
374 - enet:,/tftpboot/vmlinux
377 - arguments for obp-tftp open as specified in section 4.1 of
378 http://playground.sun.com/1275/practice/obp-tftp/tftp1_0.pdf
379 [bootp,]siaddr,filename,ciaddr,giaddr,bootp-retries,tftp-retries
380 ex: enet:bootp,10.0.0.11,bootme,10.0.0.12,10.0.0.1,5,5
381 Supported only if defdevice == NULL
383 - any other device path lacking a :
384 Unsupported examples:
385 - hd:2,\\:tbxi <- no filename will be detected due to the extra :
386 - enet:192.168.2.1,bootme,c-iaddr,g-iaddr,subnet-mask,bootp-retries,tftp-retries */
389 parse_device_path(char *imagepath, char *defdevice, int defpart,
390 char *deffile, struct boot_fspec_t *result)
395 int device_kind = -1;
397 DEBUG_F("imagepath = %s; defdevice %s; defpart %d, deffile %s\n",
398 imagepath, defdevice, defpart, deffile);
408 * Do preliminary checking for an iscsi device; it may appear as
409 * pure a network device (device_type == "network") if this is
410 * ISWI. This is the case on IBM systems doing an iscsi OFW
413 if (strstr(imagepath, TOK_ISCSI)) {
415 * get the virtual device information from the
416 * "nas-bootdevice" property.
418 if (prom_get_chosen("nas-bootdevice", bootdevice, BOOTDEVSZ)) {
419 DEBUG_F("reset boot-device to"
420 " /chosen/nas-bootdevice = %s\n", bootdevice);
421 device_kind = FILE_DEVICE_ISCSI;
422 ipath = strdup(bootdevice);
429 else if (!(ipath = strdup(imagepath)))
433 defdev = strdup(defdevice);
434 device_kind = prom_get_devtype(defdev);
435 } else if (device_kind == -1)
436 device_kind = prom_get_devtype(ipath);
439 * When an iscsi iqn is present, it may have embedded colons, so
440 * don't parse off anything.
442 if (device_kind != FILE_DEVICE_NET &&
443 device_kind != FILE_DEVICE_ISCSI &&
444 strchr(defdev, ':') != NULL) {
445 if ((ptr = strrchr(defdev, ':')) != NULL)
446 *ptr = 0; /* remove trailing : from defdevice if necessary */
449 /* This will not properly handle an obp-tftp argument list
450 * with elements after the filename; that is handled below.
452 if (device_kind != FILE_DEVICE_NET &&
453 device_kind != FILE_DEVICE_ISCSI &&
454 strchr(ipath, ':') != NULL) {
455 if ((ptr = strrchr(ipath, ',')) != NULL) {
456 char *colon = strrchr(ipath, ':');
457 /* If a ':' occurs *after* a ',', then we assume that there is
459 if (!colon || colon < ptr) {
460 result->file = strdup(ptr+1);
461 /* Trim the filename off */
467 if (device_kind == FILE_DEVICE_NET) {
468 if (strchr(ipath, ':')) {
469 if (extract_netboot_args(ipath, result) == 0)
472 /* If we didn't get a ':' then look only in netinfo */
473 extract_netinfo_args(result);
474 result->file = strdup(ipath);
478 result->dev = netdev_path_to_dev(ipath);
479 } else if (device_kind != FILE_DEVICE_ISCSI &&
480 (ptr = strrchr(ipath, ':')) != NULL) {
482 result->dev = strdup(ipath);
484 result->part = simple_strtol(ptr+1, NULL, 10);
485 } else if (!defdev) {
486 result->dev = strdup(ipath);
487 } else if (strlen(ipath)) {
488 result->file = strdup(ipath);
494 if (!result->dev && defdev)
495 result->dev = strdup(defdev);
497 if (result->part < 0)
498 result->part = defpart;
501 result->file = strdup(deffile);
511 file_block_open( struct boot_file_t* file,
512 struct boot_fspec_t* fspec,
515 struct partition_t* parts;
516 struct partition_t* p;
517 struct partition_t* found;
519 parts = partitions_lookup(fspec->dev);
524 prom_printf("partitions:\n");
526 prom_printf("no partitions found.\n");
528 for (p = parts; p && !found; p=p->next) {
529 DEBUG_F("number: %02d, start: 0x%08lx, length: 0x%08lx\n",
530 p->part_number, p->part_start, p->part_size );
531 if (partition == -1) {
532 file->fs = fs_open( file, p, fspec );
533 if (file->fs == NULL || fserrorno != FILE_ERR_OK)
536 partition = p->part_number;
540 if ((partition >= 0) && (partition == p->part_number))
544 prom_printf(" (match)\n");
548 /* Note: we don't skip when found is NULL since we can, in some
549 * cases, let OF figure out a default partition.
551 DEBUG_F( "Using OF defaults.. (found = %p)\n", found );
552 file->fs = fs_open( file, found, fspec );
556 partitions_free(parts);
562 file_net_open(struct boot_file_t* file, struct boot_fspec_t *fspec)
564 file->fs = fs_of_netboot;
565 return fs_of_netboot->open(file, NULL, fspec);
569 default_read( struct boot_file_t* file,
573 prom_printf("WARNING ! default_read called !\n");
578 default_seek( struct boot_file_t* file,
581 prom_printf("WARNING ! default_seek called !\n");
586 default_close( struct boot_file_t* file)
588 prom_printf("WARNING ! default_close called !\n");
592 static struct fs_t fs_default =
602 int open_file(struct boot_fspec_t* spec, struct boot_file_t* file)
606 memset(file, 0, sizeof(struct boot_file_t*));
607 file->fs = &fs_default;
609 DEBUG_F("dev_path = %s\nfile_name = %s\npartition = %d\n",
610 spec->dev, spec->file, spec->part);
612 result = prom_get_devtype(spec->dev);
614 file->device_kind = result;
618 switch(file->device_kind) {
619 case FILE_DEVICE_BLOCK:
620 DEBUG_F("device is a block device\n");
621 return file_block_open(file, spec, spec->part);
622 case FILE_DEVICE_NET:
623 DEBUG_F("device is a network device\n");
624 return file_net_open(file, spec);
631 * c-file-style: "k&r"