-/* File related stuff
-
- Copyright (C) 1999 Benjamin Herrenschmidt
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+/*
+ * file.c - Filesystem related interfaces
+ *
+ * Copyright (C) 2001, 2002 Ethan Benson
+ *
+ * parse_device_path()
+ *
+ * Copyright (C) 2001 Colin Walters
+ *
+ * Copyright (C) 1999 Benjamin Herrenschmidt
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
#include "ctype.h"
#include "types.h"
#include "string.h"
#include "partition.h"
#include "fs.h"
+#include "errors.h"
+#include "debug.h"
-extern char bootdevice[1024];
+extern char bootdevice[];
-/* This function follows the device path in the devtree and separates
- the device name, partition number, and other datas (mostly file name)
- the string passed in parameters is changed since 0 are put in place
- of some separators to terminate the various strings
+/*
+ * Copy the string from source to dest till newline or comma(,) is seen
+ * in the source.
+ * Move source and dest pointers respectively.
+ * Returns pointer to the start of the string that has just been copied.
+ */
+static char *
+scopy(char **dest, char **source)
+{
+ char *ret = *dest;
+
+ if (!**source)
+ return NULL;
+
+ while (**source != ',' && **source != '\0')
+ *(*dest)++ = *(*source)++;
+ if (**source != '\0')
+ *(*source)++;
+ **dest = '\0';
+ *(*dest)++;
+ return ret;
+}
+
+/*
+ * Extract all the ipv4 arguments from the bootpath provided and fill result
+ * Returns 1 on success, 0 on failure.
*/
-char *
-parse_device_path(char *of_device, char **file_spec, int *partition)
+static int
+extract_ipv4_args(char *imagepath, struct boot_fspec_t *result)
{
- char *p, *last;
-
- if (file_spec)
- *file_spec = NULL;
- if (partition)
- *partition = -1;
-
- p = strchr(of_device, ':');
- if (p)
- *p = 0;
- else
- return of_device;
-
- last = ++p;
- while(*p && *p != ',') {
- if (!isdigit (*p)) {
- p = last;
- break;
- }
- ++p;
- }
- if (p != last) {
- *(p++) = 0;
- if (partition)
- *partition = simple_strtol(last, NULL, 10);
- }
- if (*p && file_spec)
- *file_spec = p;
-
- return of_device;
+ char *tmp, *args, *str, *start;
+
+ args = strrchr(imagepath, ':');
+ if (!args)
+ return 1;
+
+ start = args; /* used to see if we read any optional parameters */
+
+ /* The obp-tftp device arguments should be at the end of
+ * the argument list. Skip over any extra arguments (promiscuous,
+ * speed, duplex, bootp, rarp).
+ */
+
+ tmp = strstr(args, "promiscuous");
+ if (tmp && tmp > args)
+ args = tmp + strlen("promiscuous");
+
+ tmp = strstr(args, "speed=");
+ if (tmp && tmp > args)
+ args = tmp + strlen("speed=");
+
+ tmp = strstr(args, "duplex=");
+ if (tmp && tmp > args)
+ args = tmp + strlen("duplex=");
+
+ tmp = strstr(args, "bootp");
+ if (tmp && tmp > args)
+ args = tmp + strlen("bootp");
+
+ tmp = strstr(args, "rarp");
+ if (tmp && tmp > args)
+ args = tmp + strlen("rarp");
+
+ if (args != start) /* we read some parameters, so go past the next comma(,) */
+ args = strchr(args, ',');
+ if (!args)
+ return 1;
+
+ str = malloc(strlen(args) + 1); /*long enough to hold all strings */
+ if (!str)
+ return 0;
+
+ if (args[-1] != ':')
+ args++; /* If comma(,) is not immediately followed by ':' then go past the , */
+
+ /*
+ * read the arguments in order: siaddr,filename,ciaddr,giaddr,
+ * bootp-retries,tftp-retries,addl_prameters
+ */
+ result->siaddr = scopy(&str, &args);
+ result->file = scopy(&str, &args);
+ result->ciaddr = scopy(&str, &args);
+ result->giaddr = scopy(&str, &args);
+ result->bootp_retries = scopy(&str, &args);
+ result->tftp_retries = scopy(&str, &args);
+ if (*args) {
+ result->addl_params = strdup(args);
+ if (!result->addl_params)
+ return 0;
+ }
+ return 1;
+}
+
+/*
+ * Extract all the arguments provided in the imagepath and fill it in result.
+ * Returns 1 on success, 0 on failure.
+ */
+static int
+extract_args_from_netdev_path(char *imagepath, struct boot_fspec_t *result)
+{
+ int ret;
+
+ DEBUG_F("imagepath = %s\n", imagepath);
+
+ if (!imagepath)
+ return 1;
+
+ ret = extract_ipv4_args(imagepath, result);
+
+ DEBUG_F("siaddr = <%s>\n", result->siaddr);
+ DEBUG_F("file = <%s>\n", result->file);
+ DEBUG_F("ciaddr = <%s>\n", result->ciaddr);
+ DEBUG_F("giaddr = <%s>\n", result->giaddr);
+ DEBUG_F("bootp_retries = <%s>\n", result->bootp_retries);
+ DEBUG_F("tftp_retries = <%s>\n", result->tftp_retries);
+ DEBUG_F("addl_params = <%s>\n", result->addl_params);
+ return ret;
+}
+
+static char *netdev_path_to_dev(const char *path)
+{
+ char *dev, *tmp;
+ size_t len;
+
+ DEBUG_F("path = %s\n", path);
+
+ if (!path)
+ return NULL;
+
+ tmp = strchr(path, ':');
+ if (!tmp)
+ return strdup(path);
+ tmp++;
+
+ len = tmp - path + 1;
+ dev = malloc(len);
+ if (dev) {
+ strncpy(dev, path, len);
+ dev[len - 1] = '\0';
+ }
+ return dev;
}
+/* This function follows the device path in the devtree and separates
+ the device name, partition number, and other datas (mostly file name)
+ the string passed in parameters is changed since 0 are put in place
+ of some separators to terminate the various strings.
+
+ when a default device is supplied imagepath will be assumed to be a
+ plain filename unless it contains a : otherwise if defaultdev is
+ NULL imagepath will be assumed to be a device path.
+
+ returns 1 on success 0 on failure.
+
+ Supported examples:
+ - /pci@80000000/pci-bridge@d/ADPT,2930CU@2/@1:4
+ - /pci@80000000/pci-bridge@d/ADPT,2930CU@2/@1:4,/boot/vmlinux
+ - hd:3,/boot/vmlinux
+ - enet:10.0.0.1,/tftpboot/vmlinux
+ - enet:,/tftpboot/vmlinux
+ - enet:bootp
+ - enet:0
+ - arguments for obp-tftp open as specified in section 4.1 of
+ http://playground.sun.com/1275/practice/obp-tftp/tftp1_0.pdf
+ [bootp,]siaddr,filename,ciaddr,giaddr,bootp-retries,tftp-retries
+ ex: enet:bootp,10.0.0.11,bootme,10.0.0.12,10.0.0.1,5,5
+ Supported only if defdevice == NULL
+ - disc
+ - any other device path lacking a :
+ Unsupported examples:
+ - hd:2,\\:tbxi <- no filename will be detected due to the extra :
+ - enet:192.168.2.1,bootme,c-iaddr,g-iaddr,subnet-mask,bootp-retries,tftp-retries */
+
int
-validate_fspec( struct boot_fspec_t* spec,
- char* default_device,
- int default_part)
+parse_device_path(char *imagepath, char *defdevice, int defpart,
+ char *deffile, struct boot_fspec_t *result)
{
- if (!spec->file) {
- spec->file = spec->dev;
- spec->dev = NULL;
- }
- if (spec->part == -1)
- spec->part = default_part;
- if (!spec->dev)
- spec->dev = default_device;
- if (!spec->file)
- return FILE_BAD_PATH;
- else if (spec->file[0] == ',')
- spec->file++;
-
- return FILE_ERR_OK;
+ char *ptr;
+ char *ipath = NULL;
+ char *defdev = NULL;
+ int device_kind = -1;
+
+ DEBUG_F("imagepath = %s; defdevice %s; defpart %d, deffile %s\n",
+ imagepath, defdevice, defpart, deffile);
+
+ result->dev = NULL;
+ result->part = -1;
+ result->file = NULL;
+
+ if (!imagepath)
+ return 0;
+
+ /*
+ * Do preliminary checking for an iscsi device; it may appear as
+ * pure a network device (device_type == "network") if this is
+ * ISWI. This is the case on IBM systems doing an iscsi OFW
+ * boot.
+ */
+ if (strstr(imagepath, TOK_ISCSI)) {
+ /*
+ * get the virtual device information from the
+ * "nas-bootdevice" property.
+ */
+ if (prom_get_chosen("nas-bootdevice", bootdevice, BOOTDEVSZ)) {
+ DEBUG_F("reset boot-device to"
+ " /chosen/nas-bootdevice = %s\n", bootdevice);
+ device_kind = FILE_DEVICE_ISCSI;
+ ipath = strdup(bootdevice);
+ if (!ipath)
+ return 0;
+ }
+ else
+ return 0;
+ }
+ else if (!(ipath = strdup(imagepath)))
+ return 0;
+
+ if (defdevice) {
+ defdev = strdup(defdevice);
+ device_kind = prom_get_devtype(defdev);
+ } else if (device_kind == -1)
+ device_kind = prom_get_devtype(ipath);
+
+ /*
+ * When an iscsi iqn is present, it may have embedded colons, so
+ * don't parse off anything.
+ */
+ if (device_kind != FILE_DEVICE_NET &&
+ device_kind != FILE_DEVICE_ISCSI &&
+ strchr(defdev, ':') != NULL) {
+ if ((ptr = strrchr(defdev, ':')) != NULL)
+ *ptr = 0; /* remove trailing : from defdevice if necessary */
+ }
+
+ /* This will not properly handle an obp-tftp argument list
+ * with elements after the filename; that is handled below.
+ */
+ if (device_kind != FILE_DEVICE_NET &&
+ device_kind != FILE_DEVICE_ISCSI &&
+ strchr(ipath, ':') != NULL) {
+ if ((ptr = strrchr(ipath, ',')) != NULL) {
+ char *colon = strrchr(ipath, ':');
+ /* If a ':' occurs *after* a ',', then we assume that there is
+ no filename */
+ if (!colon || colon < ptr) {
+ result->file = strdup(ptr+1);
+ /* Trim the filename off */
+ *ptr = 0;
+ }
+ }
+ }
+
+ if (device_kind == FILE_DEVICE_NET) {
+ if (strchr(ipath, ':')) {
+ if (extract_args_from_netdev_path(ipath, result) == 0)
+ return 0;
+ } else
+ result->file = strdup(ipath);
+
+ if (!defdev)
+ result->dev = netdev_path_to_dev(ipath);
+ } else if (device_kind != FILE_DEVICE_ISCSI &&
+ (ptr = strrchr(ipath, ':')) != NULL) {
+ *ptr = 0;
+ result->dev = strdup(ipath);
+ if (*(ptr+1))
+ result->part = simple_strtol(ptr+1, NULL, 10);
+ } else if (!defdev) {
+ result->dev = strdup(ipath);
+ } else if (strlen(ipath)) {
+ result->file = strdup(ipath);
+ } else {
+ free(defdev);
+ return 0;
+ }
+
+ if (!result->dev && defdev)
+ result->dev = strdup(defdev);
+
+ if (result->part < 0)
+ result->part = defpart;
+
+ if (!result->file)
+ result->file = strdup(deffile);
+
+ free(ipath);
+ if (defdev)
+ free(defdev);
+ return 1;
}
+
static int
file_block_open( struct boot_file_t* file,
- const char* dev_name,
- const char* file_name,
+ struct boot_fspec_t* fspec,
int partition)
{
- struct partition_t* parts;
- struct partition_t* p;
- struct partition_t* found;
-
- parts = partitions_lookup(dev_name);
- found = NULL;
-
+ struct partition_t* parts;
+ struct partition_t* p;
+ struct partition_t* found;
+
+ parts = partitions_lookup(fspec->dev);
+ found = NULL;
+
#if DEBUG
- if (parts)
- prom_printf("partitions:\n");
- else
- prom_printf("no partitions found.\n");
+ if (parts)
+ prom_printf("partitions:\n");
+ else
+ prom_printf("no partitions found.\n");
#endif
- for (p = parts; p && !found; p=p->next) {
+ for (p = parts; p && !found; p=p->next) {
+ DEBUG_F("number: %02d, start: 0x%08lx, length: 0x%08lx\n",
+ p->part_number, p->part_start, p->part_size );
+ if (partition == -1) {
+ file->fs = fs_open( file, p, fspec );
+ if (file->fs == NULL || fserrorno != FILE_ERR_OK)
+ continue;
+ else {
+ partition = p->part_number;
+ goto done;
+ }
+ }
+ if ((partition >= 0) && (partition == p->part_number))
+ found = p;
#if DEBUG
- prom_printf("number: %02d, start: 0x%08lx, length: 0x%08lx\n",
- p->part_number, p->part_start, p->part_size );
+ if (found)
+ prom_printf(" (match)\n");
#endif
- if (partition == -1) {
- file->fs = fs_open( file, dev_name, p, file_name );
- if (file->fs != NULL)
- goto bail;
- }
- if ((partition >= 0) && (partition == p->part_number))
- found = p;
-#if DEBUG
- if (found)
- prom_printf(" (match)\n");
-#endif
- }
-
- /* Note: we don't skip when found is NULL since we can, in some
- * cases, let OF figure out a default partition.
- */
- DEBUG_F( "Using OF defaults.. (found = 0x%x)\n", found );
- file->fs = fs_open( file, dev_name, found, file_name );
-
-bail:
- if (parts)
- partitions_free(parts);
-
- return file->fs ? FILE_ERR_OK : FILE_ERR_NOTFOUND;
+ }
+
+ /* Note: we don't skip when found is NULL since we can, in some
+ * cases, let OF figure out a default partition.
+ */
+ DEBUG_F( "Using OF defaults.. (found = %p)\n", found );
+ file->fs = fs_open( file, found, fspec );
+
+done:
+ if (parts)
+ partitions_free(parts);
+
+ return fserrorno;
}
static int
-file_net_open( struct boot_file_t* file,
- const char* dev_name,
- const char* file_name)
+file_net_open(struct boot_file_t* file, struct boot_fspec_t *fspec)
{
- file->fs = fs_of_netboot;
- return fs_of_netboot->open(file, dev_name, NULL, file_name);
+ file->fs = fs_of_netboot;
+ return fs_of_netboot->open(file, NULL, fspec);
}
static int
unsigned int size,
void* buffer)
{
- prom_printf("WARNING ! default_read called !\n");
- return FILE_ERR_EOF;
+ prom_printf("WARNING ! default_read called !\n");
+ return FILE_ERR_EOF;
}
static int
default_seek( struct boot_file_t* file,
unsigned int newpos)
{
- prom_printf("WARNING ! default_seek called !\n");
- return FILE_ERR_EOF;
+ prom_printf("WARNING ! default_seek called !\n");
+ return FILE_ERR_EOF;
}
static int
default_close( struct boot_file_t* file)
{
- prom_printf("WARNING ! default_close called !\n");
- return FILE_ERR_OK;
+ prom_printf("WARNING ! default_close called !\n");
+ return FILE_ERR_OK;
}
static struct fs_t fs_default =
{
- "defaults",
- NULL,
- default_read,
- default_seek,
- default_close
+ "defaults",
+ NULL,
+ default_read,
+ default_seek,
+ default_close
};
-int open_file( const struct boot_fspec_t* spec,
- struct boot_file_t* file)
+int open_file(struct boot_fspec_t* spec, struct boot_file_t* file)
{
- static char temp[1024];
- static char temps[64];
- char *dev_name;
- char *file_name = NULL;
- phandle dev;
- int result;
- int partition;
-
- memset(file, 0, sizeof(struct boot_file_t*));
- file->fs = &fs_default;
-
- /* Lookup the OF device path */
- /* First, see if a device was specified for the kernel
- * if not, we hope that the user wants a kernel on the same
- * drive and partition as yaboot itself */
- if (!spec->dev)
- strcpy(spec->dev, bootdevice);
- strncpy(temp,spec->dev,1024);
- dev_name = parse_device_path(temp, &file_name, &partition);
- if (file_name == NULL)
- file_name = (char *)spec->file;
- if (file_name == NULL) {
- prom_printf("booting without a file name not yet supported !\n");
- return FILE_ERR_NOTFOUND;
- }
- if (partition == -1)
- partition = spec->part;
+ int result;
-#if DEBUG
- prom_printf("dev_path = %s\nfile_name = %s\npartition = %d\n",
- dev_name, file_name, partition);
-#endif
- /* Find OF device phandle */
- dev = prom_finddevice(dev_name);
- if (dev == PROM_INVALID_HANDLE) {
- prom_printf("device not found !\n");
- return FILE_ERR_NOTFOUND;
- }
-#if DEBUG
- prom_printf("dev_phandle = %08lx\n", dev);
-#endif
- /* Check the kind of device */
- result = prom_getprop(dev, "device_type", temps, 63);
- if (result == -1) {
- prom_printf("can't get <device_type> for device\n");
- return FILE_ERR_NOTFOUND;
- }
- temps[result] = 0;
- if (!strcmp(temps, "block"))
- file->device_kind = FILE_DEVICE_BLOCK;
- else if (!strcmp(temps, "network"))
- file->device_kind = FILE_DEVICE_NET;
- else {
- prom_printf("Unkown device type <%s>\n", temps);
- return FILE_ERR_NOTFOUND;
- }
-
- switch(file->device_kind) {
- case FILE_DEVICE_BLOCK:
-#if DEBUG
- prom_printf("device is a block device\n");
-#endif
- return file_block_open(file, dev_name, file_name, partition);
- case FILE_DEVICE_NET:
-#if DEBUG
- prom_printf("device is a network device\n");
-#endif
- return file_net_open(file, dev_name, file_name);
- }
- return 0;
+ memset(file, 0, sizeof(struct boot_file_t*));
+ file->fs = &fs_default;
+
+ DEBUG_F("dev_path = %s\nfile_name = %s\npartition = %d\n",
+ spec->dev, spec->file, spec->part);
+
+ result = prom_get_devtype(spec->dev);
+ if (result > 0)
+ file->device_kind = result;
+ else
+ return result;
+
+ switch(file->device_kind) {
+ case FILE_DEVICE_BLOCK:
+ DEBUG_F("device is a block device\n");
+ return file_block_open(file, spec, spec->part);
+ case FILE_DEVICE_NET:
+ DEBUG_F("device is a network device\n");
+ return file_net_open(file, spec);
+ }
+ return 0;
}
+
+/*
+ * Local variables:
+ * c-file-style: "k&r"
+ * c-basic-offset: 5
+ * End:
+ */