]> git.ozlabs.org Git - yaboot.git/blobdiff - second/file.c
Fix netboot fucntionality to use the parameters specified.
[yaboot.git] / second / file.c
index b5763a235f6d5db62ad5fb1b4c92254b89775270..e5135995a74e33fb73ebec5e318b8baa71c5f033 100644 (file)
 #include "errors.h"
 #include "debug.h"
 
-extern char bootdevice[1024];
+extern char bootdevice[];
+
+/*
+ * 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 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)
+{
+     char *tmp, *args, *str, *start;
+
+     DEBUG_F("imagepath = %s\n", imagepath);
+
+     if (!imagepath)
+         return 1;
+
+     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;
+     }
+
+     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 1;
+}
+
+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.  
+   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
@@ -57,6 +188,10 @@ extern char bootdevice[1024];
     - 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 :
@@ -71,6 +206,10 @@ parse_device_path(char *imagepath, char *defdevice, int defpart,
      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;
@@ -78,23 +217,55 @@ parse_device_path(char *imagepath, char *defdevice, int defpart,
 
      if (!imagepath)
          return 0;
-     else
-         ipath = strdup(imagepath);
 
-     if (defdevice)
+      /*
+       * 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);
 
-     if (defdev) {
-         if (!strstr(defdev, "ethernet") && !strstr(defdev, "enet")) {
-              if ((ptr = strrchr(defdev, ':')) != NULL)
-                   *ptr = 0; /* remove trailing : from defdevice if necessary */
-         }
+     /*
+      * 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 */
      }
 
-     /* if there is no : then there is no filename or partition.  must
-        use strrchr() since enet:,10.0.0.1,file is legal */
-
-     if (strchr(ipath, ':') != NULL) {
+     /* 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
@@ -107,54 +278,58 @@ parse_device_path(char *imagepath, char *defdevice, int defpart,
          }
      }
 
-     if (strstr(ipath, "ethernet") || strstr(ipath, "enet"))
-         if ((ptr = strstr(ipath, "bootp")) != NULL) { /* `n' key booting boots enet:bootp */
-              *ptr = 0;
-              result->dev = strdup(ipath);
+     if (device_kind == FILE_DEVICE_NET) {
+         if (strchr(ipath, ':')) {
+              if (extract_args_from_netdev_path(ipath, result) == 0)
+                  return 0;
          } else
-              result->dev = strdup(ipath);
-     else if ((ptr = strchr(ipath, ':')) != NULL) {
+              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); 
+         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);
+          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);
+
+     parts = partitions_lookup(fspec->dev);
      found = NULL;
-                       
+
 #if DEBUG
      if (parts)
          prom_printf("partitions:\n");
@@ -165,7 +340,7 @@ file_block_open(    struct boot_file_t*     file,
          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, dev_name, p, file_name );
+              file->fs = fs_open( file, p, fspec );
               if (file->fs == NULL || fserrorno != FILE_ERR_OK)
                    continue;
               else {
@@ -178,14 +353,14 @@ file_block_open(  struct boot_file_t*     file,
 #if DEBUG
          if (found)
               prom_printf(" (match)\n");
-#endif                                         
+#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 = %p)\n", found );
-     file->fs = fs_open( file, dev_name, found, file_name );
+     file->fs = fs_open( file, found, fspec );
 
 done:
      if (parts)
@@ -195,12 +370,10 @@ done:
 }
 
 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);
+     return fs_of_netboot->open(file, NULL, fspec);
 }
 
 static int
@@ -237,10 +410,10 @@ static struct fs_t fs_default =
 };
 
 
-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)
 {
      int result;
-       
+
      memset(file, 0, sizeof(struct boot_file_t*));
      file->fs        = &fs_default;
 
@@ -252,19 +425,19 @@ int open_file(const struct boot_fspec_t* spec, struct boot_file_t* file)
          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->dev, spec->file, spec->part);
+         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->dev, spec->file);
+         return file_net_open(file, spec);
      }
      return 0;
 }
 
-/* 
+/*
  * Local variables:
  * c-file-style: "k&r"
  * c-basic-offset: 5