]> git.kernelconcepts.de Git - karo-tx-uboot.git/blobdiff - common/cmd_pxe.c
gpio: remove gpiolib.c and define remaining functions as static inline in asm/gpio.h
[karo-tx-uboot.git] / common / cmd_pxe.c
index b3c1f67a33022c1ad5ec21781e07d3aba76e616c..7e32c95df3217bc575dd39be2a71fd3f6e0deea3 100644 (file)
@@ -1,19 +1,10 @@
 /*
  * Copyright 2010-2011 Calxeda, Inc.
+ * Copyright (c) 2014, NVIDIA CORPORATION.  All rights reserved.
  *
- * 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 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, see <http://www.gnu.org/licenses/>.
+ * SPDX-License-Identifier:    GPL-2.0+
  */
+
 #include <common.h>
 #include <command.h>
 #include <malloc.h>
 #include <linux/ctype.h>
 #include <errno.h>
 #include <linux/list.h>
+#include <fs.h>
 
 #include "menu.h"
+#include "cli.h"
 
 #define MAX_TFTP_PATH_LEN 127
 
+const char *pxe_default_paths[] = {
+#ifdef CONFIG_SYS_SOC
+       "default-" CONFIG_SYS_ARCH "-" CONFIG_SYS_SOC,
+#endif
+       "default-" CONFIG_SYS_ARCH,
+       "default",
+       NULL
+};
+
+static bool is_pxe;
 
 /*
  * Like getenv, but prints an error if envvar isn't defined in the
  * environment.  It always returns what getenv does, so it can be used in
  * place of getenv without changing error handling otherwise.
  */
-static char *from_env(char *envvar)
+static char *from_env(const char *envvar)
 {
        char *ret;
 
@@ -44,6 +47,7 @@ static char *from_env(char *envvar)
        return ret;
 }
 
+#ifdef CONFIG_CMD_NET
 /*
  * Convert an ethaddr from the environment to the format used by pxelinux
  * filenames based on mac addresses. Convert's ':' to '-', and adds "01-" to
@@ -56,69 +60,55 @@ static char *from_env(char *envvar)
  */
 static int format_mac_pxe(char *outbuf, size_t outbuf_len)
 {
-       size_t ethaddr_len;
-       char *p, *ethaddr;
-
-       ethaddr = from_env("ethaddr");
+       uchar ethaddr[6];
 
-       if (!ethaddr)
-               return -ENOENT;
-
-       ethaddr_len = strlen(ethaddr);
-
-       /*
-        * ethaddr_len + 4 gives room for "01-", ethaddr, and a NUL byte at
-        * the end.
-        */
-       if (outbuf_len < ethaddr_len + 4) {
-               printf("outbuf is too small (%d < %d)\n",
-                               outbuf_len, ethaddr_len + 4);
+       if (outbuf_len < 21) {
+               printf("outbuf is too small (%zd < 21)\n", outbuf_len);
 
                return -EINVAL;
        }
 
-       strcpy(outbuf, "01-");
-
-       for (p = outbuf + 3; *ethaddr; ethaddr++, p++) {
-               if (*ethaddr == ':')
-                       *p = '-';
-               else
-                       *p = tolower(*ethaddr);
-       }
+       if (!eth_getenv_enetaddr_by_index("eth", eth_get_dev_index(),
+                                         ethaddr))
+               return -ENOENT;
 
-       *p = '\0';
+       sprintf(outbuf, "01-%02x-%02x-%02x-%02x-%02x-%02x",
+               ethaddr[0], ethaddr[1], ethaddr[2],
+               ethaddr[3], ethaddr[4], ethaddr[5]);
 
        return 1;
 }
+#endif
 
 /*
  * Returns the directory the file specified in the bootfile env variable is
  * in. If bootfile isn't defined in the environment, return NULL, which should
  * be interpreted as "don't prepend anything to paths".
  */
-static int get_bootfile_path(char *bootfile_path, size_t bootfile_path_size)
+static int get_bootfile_path(const char *file_path, char *bootfile_path,
+                            size_t bootfile_path_size)
 {
        char *bootfile, *last_slash;
-       size_t path_len;
+       size_t path_len = 0;
+
+       /* Only syslinux allows absolute paths */
+       if (file_path[0] == '/' && !is_pxe)
+               goto ret;
 
        bootfile = from_env("bootfile");
 
-       if (!bootfile) {
-               bootfile_path[0] = '\0';
-               return 1;
-       }
+       if (!bootfile)
+               goto ret;
 
        last_slash = strrchr(bootfile, '/');
 
-       if (last_slash == NULL) {
-               bootfile_path[0] = '\0';
-               return 1;
-       }
+       if (last_slash == NULL)
+               goto ret;
 
        path_len = (last_slash - bootfile) + 1;
 
        if (bootfile_path_size < path_len) {
-               printf("bootfile_path too small. (%d < %d)\n",
+               printf("bootfile_path too small. (%zd < %zd)\n",
                                bootfile_path_size, path_len);
 
                return -1;
@@ -126,11 +116,70 @@ static int get_bootfile_path(char *bootfile_path, size_t bootfile_path_size)
 
        strncpy(bootfile_path, bootfile, path_len);
 
+ ret:
        bootfile_path[path_len] = '\0';
 
        return 1;
 }
 
+static int (*do_getfile)(cmd_tbl_t *cmdtp, const char *file_path, char *file_addr);
+
+#ifdef CONFIG_CMD_NET
+static int do_get_tftp(cmd_tbl_t *cmdtp, const char *file_path, char *file_addr)
+{
+       char *tftp_argv[] = {"tftp", NULL, NULL, NULL};
+
+       tftp_argv[1] = file_addr;
+       tftp_argv[2] = (void *)file_path;
+
+       if (do_tftpb(cmdtp, 0, 3, tftp_argv))
+               return -ENOENT;
+
+       return 1;
+}
+#endif
+
+static char *fs_argv[5];
+
+static int do_get_ext2(cmd_tbl_t *cmdtp, const char *file_path, char *file_addr)
+{
+#ifdef CONFIG_CMD_EXT2
+       fs_argv[0] = "ext2load";
+       fs_argv[3] = file_addr;
+       fs_argv[4] = (void *)file_path;
+
+       if (!do_ext2load(cmdtp, 0, 5, fs_argv))
+               return 1;
+#endif
+       return -ENOENT;
+}
+
+static int do_get_fat(cmd_tbl_t *cmdtp, const char *file_path, char *file_addr)
+{
+#ifdef CONFIG_CMD_FAT
+       fs_argv[0] = "fatload";
+       fs_argv[3] = file_addr;
+       fs_argv[4] = (void *)file_path;
+
+       if (!do_fat_fsload(cmdtp, 0, 5, fs_argv))
+               return 1;
+#endif
+       return -ENOENT;
+}
+
+static int do_get_any(cmd_tbl_t *cmdtp, const char *file_path, char *file_addr)
+{
+#ifdef CONFIG_CMD_FS_GENERIC
+       fs_argv[0] = "load";
+       fs_argv[3] = file_addr;
+       fs_argv[4] = (void *)file_path;
+
+       if (!do_load(cmdtp, 0, 5, fs_argv, FS_TYPE_ANY))
+               return 1;
+#endif
+       return -ENOENT;
+}
+
 /*
  * As in pxelinux, paths to files referenced from files we retrieve are
  * relative to the location of bootfile. get_relfile takes such a path and
@@ -139,15 +188,14 @@ static int get_bootfile_path(char *bootfile_path, size_t bootfile_path_size)
  *
  * Returns 1 for success, or < 0 on error.
  */
-static int get_relfile(char *file_path, void *file_addr)
+static int get_relfile(cmd_tbl_t *cmdtp, const char *file_path, void *file_addr)
 {
        size_t path_len;
        char relfile[MAX_TFTP_PATH_LEN+1];
        char addr_buf[10];
-       char *tftp_argv[] = {"tftp", NULL, NULL, NULL};
        int err;
 
-       err = get_bootfile_path(relfile, sizeof(relfile));
+       err = get_bootfile_path(file_path, relfile, sizeof(relfile));
 
        if (err < 0)
                return err;
@@ -169,13 +217,7 @@ static int get_relfile(char *file_path, void *file_addr)
 
        sprintf(addr_buf, "%p", file_addr);
 
-       tftp_argv[1] = addr_buf;
-       tftp_argv[2] = relfile;
-
-       if (do_tftpb(NULL, 0, 3, tftp_argv))
-               return -ENOENT;
-
-       return 1;
+       return do_getfile(cmdtp, relfile, addr_buf);
 }
 
 /*
@@ -185,13 +227,13 @@ static int get_relfile(char *file_path, void *file_addr)
  *
  * Returns 1 on success, or < 0 for error.
  */
-static int get_pxe_file(char *file_path, void *file_addr)
+static int get_pxe_file(cmd_tbl_t *cmdtp, const char *file_path, void *file_addr)
 {
        unsigned long config_file_size;
        char *tftp_filesize;
        int err;
 
-       err = get_relfile(file_path, file_addr);
+       err = get_relfile(cmdtp, file_path, file_addr);
 
        if (err < 0)
                return err;
@@ -213,6 +255,8 @@ static int get_pxe_file(char *file_path, void *file_addr)
        return 1;
 }
 
+#ifdef CONFIG_CMD_NET
+
 #define PXELINUX_DIR "pxelinux.cfg/"
 
 /*
@@ -222,7 +266,7 @@ static int get_pxe_file(char *file_path, void *file_addr)
  *
  * Returns 1 on success or < 0 on error.
  */
-static int get_pxelinux_path(char *file, void *pxefile_addr_r)
+static int get_pxelinux_path(cmd_tbl_t *cmdtp, const char *file, void *pxefile_addr_r)
 {
        size_t base_len = strlen(PXELINUX_DIR);
        char path[MAX_TFTP_PATH_LEN+1];
@@ -235,7 +279,7 @@ static int get_pxelinux_path(char *file, void *pxefile_addr_r)
 
        sprintf(path, PXELINUX_DIR "%s", file);
 
-       return get_pxe_file(path, pxefile_addr_r);
+       return get_pxe_file(cmdtp, path, pxefile_addr_r);
 }
 
 /*
@@ -243,7 +287,7 @@ static int get_pxelinux_path(char *file, void *pxefile_addr_r)
  *
  * Returns 1 on success or < 0 on error.
  */
-static int pxe_uuid_path(void *pxefile_addr_r)
+static int pxe_uuid_path(cmd_tbl_t *cmdtp, void *pxefile_addr_r)
 {
        char *uuid_str;
 
@@ -252,7 +296,7 @@ static int pxe_uuid_path(void *pxefile_addr_r)
        if (!uuid_str)
                return -ENOENT;
 
-       return get_pxelinux_path(uuid_str, pxefile_addr_r);
+       return get_pxelinux_path(cmdtp, uuid_str, pxefile_addr_r);
 }
 
 /*
@@ -261,7 +305,7 @@ static int pxe_uuid_path(void *pxefile_addr_r)
  *
  * Returns 1 on success or < 0 on error.
  */
-static int pxe_mac_path(void *pxefile_addr_r)
+static int pxe_mac_path(cmd_tbl_t *cmdtp, void *pxefile_addr_r)
 {
        char mac_str[21];
        int err;
@@ -271,7 +315,7 @@ static int pxe_mac_path(void *pxefile_addr_r)
        if (err < 0)
                return err;
 
-       return get_pxelinux_path(mac_str, pxefile_addr_r);
+       return get_pxelinux_path(cmdtp, mac_str, pxefile_addr_r);
 }
 
 /*
@@ -281,7 +325,7 @@ static int pxe_mac_path(void *pxefile_addr_r)
  *
  * Returns 1 on success or < 0 on error.
  */
-static int pxe_ipaddr_paths(void *pxefile_addr_r)
+static int pxe_ipaddr_paths(cmd_tbl_t *cmdtp, void *pxefile_addr_r)
 {
        char ip_addr[9];
        int mask_pos, err;
@@ -289,7 +333,7 @@ static int pxe_ipaddr_paths(void *pxefile_addr_r)
        sprintf(ip_addr, "%08X", ntohl(NetOurIP));
 
        for (mask_pos = 7; mask_pos >= 0;  mask_pos--) {
-               err = get_pxelinux_path(ip_addr, pxefile_addr_r);
+               err = get_pxelinux_path(cmdtp, ip_addr, pxefile_addr_r);
 
                if (err > 0)
                        return err;
@@ -319,12 +363,13 @@ do_pxe_get(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
 {
        char *pxefile_addr_str;
        unsigned long pxefile_addr_r;
-       int err;
+       int err, i = 0;
+
+       do_getfile = do_get_tftp;
 
        if (argc != 1)
                return CMD_RET_USAGE;
 
-
        pxefile_addr_str = from_env("pxefile_addr_r");
 
        if (!pxefile_addr_str)
@@ -339,20 +384,28 @@ do_pxe_get(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
         * Keep trying paths until we successfully get a file we're looking
         * for.
         */
-       if (pxe_uuid_path((void *)pxefile_addr_r) > 0
-               || pxe_mac_path((void *)pxefile_addr_r) > 0
-               || pxe_ipaddr_paths((void *)pxefile_addr_r) > 0
-               || get_pxelinux_path("default", (void *)pxefile_addr_r) > 0) {
-
+       if (pxe_uuid_path(cmdtp, (void *)pxefile_addr_r) > 0 ||
+           pxe_mac_path(cmdtp, (void *)pxefile_addr_r) > 0 ||
+           pxe_ipaddr_paths(cmdtp, (void *)pxefile_addr_r) > 0) {
                printf("Config file found\n");
 
                return 0;
        }
 
+       while (pxe_default_paths[i]) {
+               if (get_pxelinux_path(cmdtp, pxe_default_paths[i],
+                                     (void *)pxefile_addr_r) > 0) {
+                       printf("Config file found\n");
+                       return 0;
+               }
+               i++;
+       }
+
        printf("Config file not found\n");
 
        return 1;
 }
+#endif
 
 /*
  * Wrapper to make it easier to store the file at file_path in the location
@@ -361,7 +414,7 @@ do_pxe_get(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
  *
  * Returns 1 on success or < 0 on error.
  */
-static int get_relfile_envaddr(char *file_path, char *envaddr_name)
+static int get_relfile_envaddr(cmd_tbl_t *cmdtp, const char *file_path, const char *envaddr_name)
 {
        unsigned long file_addr;
        char *envaddr;
@@ -374,7 +427,7 @@ static int get_relfile_envaddr(char *file_path, char *envaddr_name)
        if (strict_strtoul(envaddr, 16, &file_addr) < 0)
                return -EINVAL;
 
-       return get_relfile(file_path, (void *)file_addr);
+       return get_relfile(cmdtp, file_path, (void *)file_addr);
 }
 
 /*
@@ -408,12 +461,18 @@ static int get_relfile_envaddr(char *file_path, char *envaddr_name)
  * list - lets these form a list, which a pxe_menu struct will hold.
  */
 struct pxe_label {
+       char num[4];
        char *name;
+       char *menu;
        char *kernel;
        char *append;
        char *initrd;
+       char *fdt;
+       char *fdtdir;
+       int ipappend;
        int attempted;
        int localboot;
+       int localboot_val;
        struct list_head list;
 };
 
@@ -479,6 +538,12 @@ static void label_destroy(struct pxe_label *label)
        if (label->initrd)
                free(label->initrd);
 
+       if (label->fdt)
+               free(label->fdt);
+
+       if (label->fdtdir)
+               free(label->fdtdir);
+
        free(label);
 }
 
@@ -491,17 +556,9 @@ static void label_destroy(struct pxe_label *label)
 static void label_print(void *data)
 {
        struct pxe_label *label = data;
+       const char *c = label->menu ? label->menu : label->name;
 
-       printf("Label: %s\n", label->name);
-
-       if (label->kernel)
-               printf("\tkernel: %s\n", label->kernel);
-
-       if (label->append)
-               printf("\tappend: %s\n", label->append);
-
-       if (label->initrd)
-               printf("\tinitrd: %s\n", label->initrd);
+       printf("%s:\t%s\n", label->num, c);
 }
 
 /*
@@ -515,33 +572,23 @@ static void label_print(void *data)
  */
 static int label_localboot(struct pxe_label *label)
 {
-       char *localcmd, *dupcmd;
-       int ret;
+       char *localcmd;
 
        localcmd = from_env("localcmd");
 
        if (!localcmd)
                return -ENOENT;
 
-       /*
-        * dup the command to avoid any issues with the version of it existing
-        * in the environment changing during the execution of the command.
-        */
-       dupcmd = strdup(localcmd);
-
-       if (!dupcmd)
-               return -ENOMEM;
+       if (label->append) {
+               char bootargs[CONFIG_SYS_CBSIZE];
 
-       if (label->append)
-               setenv("bootargs", label->append);
-
-       printf("running: %s\n", dupcmd);
-
-       ret = run_command(dupcmd, 0);
+               cli_simple_process_macros(label->append, bootargs);
+               setenv("bootargs", bootargs);
+       }
 
-       free(dupcmd);
+       debug("running: %s\n", localcmd);
 
-       return ret;
+       return run_command_list(localcmd, strlen(localcmd), 0);
 }
 
 /*
@@ -559,59 +606,191 @@ static int label_localboot(struct pxe_label *label)
  * If the label specifies an 'append' line, its contents will overwrite that
  * of the 'bootargs' environment variable.
  */
-static void label_boot(struct pxe_label *label)
+static int label_boot(cmd_tbl_t *cmdtp, struct pxe_label *label)
 {
        char *bootm_argv[] = { "bootm", NULL, NULL, NULL, NULL };
+       char initrd_str[22];
+       char mac_str[29] = "";
+       char ip_str[68] = "";
        int bootm_argc = 3;
+       int len = 0;
+       ulong kernel_addr;
+       void *buf;
 
        label_print(label);
 
        label->attempted = 1;
 
        if (label->localboot) {
-               label_localboot(label);
-               return;
+               if (label->localboot_val >= 0)
+                       label_localboot(label);
+               return 0;
        }
 
        if (label->kernel == NULL) {
                printf("No kernel given, skipping %s\n",
                                label->name);
-               return;
+               return 1;
        }
 
        if (label->initrd) {
-               if (get_relfile_envaddr(label->initrd, "ramdisk_addr_r") < 0) {
+               if (get_relfile_envaddr(cmdtp, label->initrd, "ramdisk_addr_r") < 0) {
                        printf("Skipping %s for failure retrieving initrd\n",
                                        label->name);
-                       return;
+                       return 1;
                }
 
-               bootm_argv[2] = getenv("ramdisk_addr_r");
+               bootm_argv[2] = initrd_str;
+               strcpy(bootm_argv[2], getenv("ramdisk_addr_r"));
+               strcat(bootm_argv[2], ":");
+               strcat(bootm_argv[2], getenv("filesize"));
        } else {
                bootm_argv[2] = "-";
        }
 
-       if (get_relfile_envaddr(label->kernel, "kernel_addr_r") < 0) {
+       if (get_relfile_envaddr(cmdtp, label->kernel, "kernel_addr_r") < 0) {
                printf("Skipping %s for failure retrieving kernel\n",
                                label->name);
-               return;
+               return 1;
        }
 
-       if (label->append)
-               setenv("bootargs", label->append);
+       if (label->ipappend & 0x1) {
+               sprintf(ip_str, " ip=%s:%s:%s:%s",
+                       getenv("ipaddr"), getenv("serverip"),
+                       getenv("gatewayip"), getenv("netmask"));
+       }
+
+#ifdef CONFIG_CMD_NET
+       if (label->ipappend & 0x2) {
+               int err;
+               strcpy(mac_str, " BOOTIF=");
+               err = format_mac_pxe(mac_str + 8, sizeof(mac_str) - 8);
+               if (err < 0)
+                       mac_str[0] = '\0';
+       }
+#endif
+
+       if ((label->ipappend & 0x3) || label->append) {
+               char bootargs[CONFIG_SYS_CBSIZE] = "";
+               char finalbootargs[CONFIG_SYS_CBSIZE];
+
+               if (strlen(label->append ?: "") +
+                   strlen(ip_str) + strlen(mac_str) + 1 > sizeof(bootargs)) {
+                       printf("bootarg overflow %zd+%zd+%zd+1 > %zd\n",
+                              strlen(label->append ?: ""),
+                              strlen(ip_str), strlen(mac_str),
+                              sizeof(bootargs));
+                       return 1;
+               }
+
+               if (label->append)
+                       strcpy(bootargs, label->append);
+               strcat(bootargs, ip_str);
+               strcat(bootargs, mac_str);
+
+               cli_simple_process_macros(bootargs, finalbootargs);
+               setenv("bootargs", finalbootargs);
+               printf("append: %s\n", finalbootargs);
+       }
 
        bootm_argv[1] = getenv("kernel_addr_r");
 
        /*
-        * fdt usage is optional.  If there is an fdt_addr specified, we will
-        * pass it along to bootm, and adjust argc appropriately.
+        * fdt usage is optional:
+        * It handles the following scenarios. All scenarios are exclusive
+        *
+        * Scenario 1: If fdt_addr_r specified and "fdt" label is defined in
+        * pxe file, retrieve fdt blob from server. Pass fdt_addr_r to bootm,
+        * and adjust argc appropriately.
+        *
+        * Scenario 2: If there is an fdt_addr specified, pass it along to
+        * bootm, and adjust argc appropriately.
+        *
+        * Scenario 3: fdt blob is not available.
         */
-       bootm_argv[3] = getenv("fdt_addr");
+       bootm_argv[3] = getenv("fdt_addr_r");
+
+       /* if fdt label is defined then get fdt from server */
+       if (bootm_argv[3]) {
+               char *fdtfile = NULL;
+               char *fdtfilefree = NULL;
+
+               if (label->fdt) {
+                       fdtfile = label->fdt;
+               } else if (label->fdtdir) {
+                       char *f1, *f2, *f3, *f4, *slash;
+
+                       f1 = getenv("fdtfile");
+                       if (f1) {
+                               f2 = "";
+                               f3 = "";
+                               f4 = "";
+                       } else {
+                               /*
+                                * For complex cases where this code doesn't
+                                * generate the correct filename, the board
+                                * code should set $fdtfile during early boot,
+                                * or the boot scripts should set $fdtfile
+                                * before invoking "pxe" or "sysboot".
+                                */
+                               f1 = getenv("soc");
+                               f2 = "-";
+                               f3 = getenv("board");
+                               f4 = ".dtb";
+                       }
+
+                       len = strlen(label->fdtdir);
+                       if (!len)
+                               slash = "./";
+                       else if (label->fdtdir[len - 1] != '/')
+                               slash = "/";
+                       else
+                               slash = "";
+
+                       len = strlen(label->fdtdir) + strlen(slash) +
+                               strlen(f1) + strlen(f2) + strlen(f3) +
+                               strlen(f4) + 1;
+                       fdtfilefree = malloc(len);
+                       if (!fdtfilefree) {
+                               printf("malloc fail (FDT filename)\n");
+                               return 1;
+                       }
+
+                       snprintf(fdtfilefree, len, "%s%s%s%s%s%s",
+                                label->fdtdir, slash, f1, f2, f3, f4);
+                       fdtfile = fdtfilefree;
+               }
+
+               if (fdtfile) {
+                       int err = get_relfile_envaddr(cmdtp, fdtfile, "fdt_addr_r");
+                       free(fdtfilefree);
+                       if (err < 0) {
+                               printf("Skipping %s for failure retrieving fdt\n",
+                                               label->name);
+                               return 1;
+                       }
+               } else {
+                       bootm_argv[3] = NULL;
+               }
+       }
+
+       if (!bootm_argv[3])
+               bootm_argv[3] = getenv("fdt_addr");
 
        if (bootm_argv[3])
                bootm_argc = 4;
 
-       do_bootm(NULL, 0, bootm_argc, bootm_argv);
+       kernel_addr = genimg_get_kernel_addr(bootm_argv[1]);
+       buf = map_sysmem(kernel_addr, 0);
+       /* Try bootm for legacy and FIT format image */
+       if (genimg_get_format(buf) != IMAGE_FORMAT_INVALID)
+               do_bootm(cmdtp, 0, bootm_argc, bootm_argv);
+#ifdef CONFIG_CMD_BOOTZ
+       /* Try booting a zImage */
+       else
+               do_bootz(cmdtp, 0, bootm_argc, bootm_argv);
+#endif
+       return 1;
 }
 
 /*
@@ -626,12 +805,17 @@ enum token_type {
        T_TIMEOUT,
        T_LABEL,
        T_KERNEL,
+       T_LINUX,
        T_APPEND,
        T_INITRD,
        T_LOCALBOOT,
        T_DEFAULT,
        T_PROMPT,
        T_INCLUDE,
+       T_FDT,
+       T_FDTDIR,
+       T_ONTIMEOUT,
+       T_IPAPPEND,
        T_INVALID
 };
 
@@ -654,10 +838,17 @@ static const struct token keywords[] = {
        {"prompt", T_PROMPT},
        {"label", T_LABEL},
        {"kernel", T_KERNEL},
+       {"linux", T_LINUX},
        {"localboot", T_LOCALBOOT},
        {"append", T_APPEND},
        {"initrd", T_INITRD},
        {"include", T_INCLUDE},
+       {"devicetree", T_FDT},
+       {"fdt", T_FDT},
+       {"devicetreedir", T_FDTDIR},
+       {"fdtdir", T_FDTDIR},
+       {"ontimeout", T_ONTIMEOUT,},
+       {"ipappend", T_IPAPPEND,},
        {NULL, T_INVALID}
 };
 
@@ -848,7 +1039,6 @@ static int parse_integer(char **c, int *dst)
 {
        struct token t;
        char *s = *c;
-       unsigned long temp;
 
        get_token(c, &t, L_SLITERAL);
 
@@ -857,19 +1047,14 @@ static int parse_integer(char **c, int *dst)
                return -EINVAL;
        }
 
-       if (strict_strtoul(t.val, 10, &temp) < 0) {
-               printf("Expected unsigned integer: %s\n", t.val);
-               return -EINVAL;
-       }
-
-       *dst = (int)temp;
+       *dst = simple_strtol(t.val, NULL, 10);
 
        free(t.val);
 
        return 1;
 }
 
-static int parse_pxefile_top(char *p, struct pxe_menu *cfg, int nest_level);
+static int parse_pxefile_top(cmd_tbl_t *cmdtp, char *p, struct pxe_menu *cfg, int nest_level);
 
 /*
  * Parse an include statement, and retrieve and parse the file it mentions.
@@ -879,7 +1064,7 @@ static int parse_pxefile_top(char *p, struct pxe_menu *cfg, int nest_level);
  * include, nest_level has already been incremented and doesn't need to be
  * incremented here.
  */
-static int handle_include(char **c, char *base,
+static int handle_include(cmd_tbl_t *cmdtp, char **c, char *base,
                                struct pxe_menu *cfg, int nest_level)
 {
        char *include_path;
@@ -894,14 +1079,14 @@ static int handle_include(char **c, char *base,
                return err;
        }
 
-       err = get_pxe_file(include_path, base);
+       err = get_pxe_file(cmdtp, include_path, base);
 
        if (err < 0) {
                printf("Couldn't retrieve %s\n", include_path);
                return err;
        }
 
-       return parse_pxefile_top(base, cfg, nest_level);
+       return parse_pxefile_top(cmdtp, base, cfg, nest_level);
 }
 
 /*
@@ -914,7 +1099,7 @@ static int handle_include(char **c, char *base,
  * nest_level should be 1 when parsing the top level pxe file, 2 when parsing
  * a file it includes, 3 when parsing a file included by that file, and so on.
  */
-static int parse_menu(char **c, struct pxe_menu *cfg, char *b, int nest_level)
+static int parse_menu(cmd_tbl_t *cmdtp, char **c, struct pxe_menu *cfg, char *b, int nest_level)
 {
        struct token t;
        char *s = *c;
@@ -929,7 +1114,7 @@ static int parse_menu(char **c, struct pxe_menu *cfg, char *b, int nest_level)
                break;
 
        case T_INCLUDE:
-               err = handle_include(c, b + strlen(b) + 1, cfg,
+               err = handle_include(cmdtp, c, b + strlen(b) + 1, cfg,
                                                nest_level + 1);
                break;
 
@@ -961,14 +1146,15 @@ static int parse_label_menu(char **c, struct pxe_menu *cfg,
 
        switch (t.type) {
        case T_DEFAULT:
-               if (cfg->default_label)
-                       free(cfg->default_label);
-
-               cfg->default_label = strdup(label->name);
+               if (!cfg->default_label)
+                       cfg->default_label = strdup(label->name);
 
                if (!cfg->default_label)
                        return -ENOMEM;
 
+               break;
+       case T_LABEL:
+               parse_sliteral(c, &label->menu);
                break;
        default:
                printf("Ignoring malformed menu command: %.*s\n",
@@ -991,6 +1177,7 @@ static int parse_label_menu(char **c, struct pxe_menu *cfg,
 static int parse_label(char **c, struct pxe_menu *cfg)
 {
        struct token t;
+       int len;
        char *s = *c;
        struct pxe_label *label;
        int err;
@@ -1019,19 +1206,47 @@ static int parse_label(char **c, struct pxe_menu *cfg)
                        break;
 
                case T_KERNEL:
+               case T_LINUX:
                        err = parse_sliteral(c, &label->kernel);
                        break;
 
                case T_APPEND:
                        err = parse_sliteral(c, &label->append);
+                       if (label->initrd)
+                               break;
+                       s = strstr(label->append, "initrd=");
+                       if (!s)
+                               break;
+                       s += 7;
+                       len = (int)(strchr(s, ' ') - s);
+                       label->initrd = malloc(len + 1);
+                       strncpy(label->initrd, s, len);
+                       label->initrd[len] = '\0';
+
                        break;
 
                case T_INITRD:
-                       err = parse_sliteral(c, &label->initrd);
+                       if (!label->initrd)
+                               err = parse_sliteral(c, &label->initrd);
+                       break;
+
+               case T_FDT:
+                       if (!label->fdt)
+                               err = parse_sliteral(c, &label->fdt);
+                       break;
+
+               case T_FDTDIR:
+                       if (!label->fdtdir)
+                               err = parse_sliteral(c, &label->fdtdir);
                        break;
 
                case T_LOCALBOOT:
-                       err = parse_integer(c, &label->localboot);
+                       label->localboot = 1;
+                       err = parse_integer(c, &label->localboot_val);
+                       break;
+
+               case T_IPAPPEND:
+                       err = parse_integer(c, &label->ipappend);
                        break;
 
                case T_EOL:
@@ -1066,7 +1281,7 @@ static int parse_label(char **c, struct pxe_menu *cfg)
  *
  * Returns 1 on success, < 0 on error.
  */
-static int parse_pxefile_top(char *p, struct pxe_menu *cfg, int nest_level)
+static int parse_pxefile_top(cmd_tbl_t *cmdtp, char *p, struct pxe_menu *cfg, int nest_level)
 {
        struct token t;
        char *s, *b, *label_name;
@@ -1087,7 +1302,8 @@ static int parse_pxefile_top(char *p, struct pxe_menu *cfg, int nest_level)
                err = 0;
                switch (t.type) {
                case T_MENU:
-                       err = parse_menu(&p, cfg, b, nest_level);
+                       cfg->prompt = 1;
+                       err = parse_menu(cmdtp, &p, cfg, b, nest_level);
                        break;
 
                case T_TIMEOUT:
@@ -1099,6 +1315,7 @@ static int parse_pxefile_top(char *p, struct pxe_menu *cfg, int nest_level)
                        break;
 
                case T_DEFAULT:
+               case T_ONTIMEOUT:
                        err = parse_sliteral(&p, &label_name);
 
                        if (label_name) {
@@ -1110,8 +1327,13 @@ static int parse_pxefile_top(char *p, struct pxe_menu *cfg, int nest_level)
 
                        break;
 
+               case T_INCLUDE:
+                       err = handle_include(cmdtp, &p, b + ALIGN(strlen(b), 4), cfg,
+                                                       nest_level + 1);
+                       break;
+
                case T_PROMPT:
-                       err = parse_integer(&p, &cfg->prompt);
+                       eol_or_eof(&p);
                        break;
 
                case T_EOL:
@@ -1163,7 +1385,7 @@ static void destroy_pxe_menu(struct pxe_menu *cfg)
  * files it includes). The resulting pxe_menu struct can be free()'d by using
  * the destroy_pxe_menu() function.
  */
-static struct pxe_menu *parse_pxefile(char *menucfg)
+static struct pxe_menu *parse_pxefile(cmd_tbl_t *cmdtp, char *menucfg)
 {
        struct pxe_menu *cfg;
 
@@ -1176,7 +1398,7 @@ static struct pxe_menu *parse_pxefile(char *menucfg)
 
        INIT_LIST_HEAD(&cfg->labels);
 
-       if (parse_pxefile_top(menucfg, cfg, 1) < 0) {
+       if (parse_pxefile_top(cmdtp, menucfg, cfg, 1) < 0) {
                destroy_pxe_menu(cfg);
                return NULL;
        }
@@ -1194,11 +1416,14 @@ static struct menu *pxe_menu_to_menu(struct pxe_menu *cfg)
        struct list_head *pos;
        struct menu *m;
        int err;
+       int i = 1;
+       char *default_num = NULL;
 
        /*
         * Create a menu and add items for all the labels.
         */
-       m = menu_create(cfg->title, cfg->timeout, cfg->prompt, label_print);
+       m = menu_create(cfg->title, cfg->timeout, cfg->prompt, label_print,
+                       NULL, NULL);
 
        if (!m)
                return NULL;
@@ -1206,18 +1431,23 @@ static struct menu *pxe_menu_to_menu(struct pxe_menu *cfg)
        list_for_each(pos, &cfg->labels) {
                label = list_entry(pos, struct pxe_label, list);
 
-               if (menu_item_add(m, label->name, label) != 1) {
+               sprintf(label->num, "%d", i++);
+               if (menu_item_add(m, label->num, label) != 1) {
                        menu_destroy(m);
                        return NULL;
                }
+               if (cfg->default_label &&
+                   (strcmp(label->name, cfg->default_label) == 0))
+                       default_num = label->num;
+
        }
 
        /*
         * After we've created items for each label in the menu, set the
         * menu's default label if one was specified.
         */
-       if (cfg->default_label) {
-               err = menu_default_set(m, cfg->default_label);
+       if (default_num) {
+               err = menu_default_set(m, default_num);
                if (err != 1) {
                        if (err != -ENOENT) {
                                menu_destroy(m);
@@ -1234,7 +1464,7 @@ static struct menu *pxe_menu_to_menu(struct pxe_menu *cfg)
 /*
  * Try to boot any labels we have yet to attempt to boot.
  */
-static void boot_unattempted_labels(struct pxe_menu *cfg)
+static void boot_unattempted_labels(cmd_tbl_t *cmdtp, struct pxe_menu *cfg)
 {
        struct list_head *pos;
        struct pxe_label *label;
@@ -1243,7 +1473,7 @@ static void boot_unattempted_labels(struct pxe_menu *cfg)
                label = list_entry(pos, struct pxe_label, list);
 
                if (!label->attempted)
-                       label_boot(label);
+                       label_boot(cmdtp, label);
        }
 }
 
@@ -1259,7 +1489,7 @@ static void boot_unattempted_labels(struct pxe_menu *cfg)
  * If this function returns, there weren't any labels that successfully
  * booted, or the user interrupted the menu selection via ctrl+c.
  */
-static void handle_pxe_menu(struct pxe_menu *cfg)
+static void handle_pxe_menu(cmd_tbl_t *cmdtp, struct pxe_menu *cfg)
 {
        void *choice;
        struct menu *m;
@@ -1284,14 +1514,18 @@ static void handle_pxe_menu(struct pxe_menu *cfg)
         * we give up.
         */
 
-       if (err == 1)
-               label_boot(choice);
-       else if (err != -ENOENT)
+       if (err == 1) {
+               err = label_boot(cmdtp, choice);
+               if (!err)
+                       return;
+       } else if (err != -ENOENT) {
                return;
+       }
 
-       boot_unattempted_labels(cfg);
+       boot_unattempted_labels(cmdtp, cfg);
 }
 
+#ifdef CONFIG_CMD_NET
 /*
  * Boots a system using a pxe file
  *
@@ -1304,6 +1538,8 @@ do_pxe_boot(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
        struct pxe_menu *cfg;
        char *pxefile_addr_str;
 
+       do_getfile = do_get_tftp;
+
        if (argc == 1) {
                pxefile_addr_str = from_env("pxefile_addr_r");
                if (!pxefile_addr_str)
@@ -1320,17 +1556,19 @@ do_pxe_boot(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
                return 1;
        }
 
-       cfg = parse_pxefile((char *)(pxefile_addr_r));
+       cfg = parse_pxefile(cmdtp, (char *)(pxefile_addr_r));
 
        if (cfg == NULL) {
                printf("Error parsing config file\n");
                return 1;
        }
 
-       handle_pxe_menu(cfg);
+       handle_pxe_menu(cmdtp, cfg);
 
        destroy_pxe_menu(cfg);
 
+       copy_filename(BootFile, "", sizeof(BootFile));
+
        return 0;
 }
 
@@ -1339,13 +1577,15 @@ static cmd_tbl_t cmd_pxe_sub[] = {
        U_BOOT_CMD_MKENT(boot, 2, 1, do_pxe_boot, "", "")
 };
 
-int do_pxe(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
+static int do_pxe(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
 {
        cmd_tbl_t *cp;
 
        if (argc < 2)
                return CMD_RET_USAGE;
 
+       is_pxe = true;
+
        /* drop initial "pxe" arg */
        argc--;
        argv++;
@@ -1364,3 +1604,91 @@ U_BOOT_CMD(
        "get - try to retrieve a pxe file using tftp\npxe "
        "boot [pxefile_addr_r] - boot from the pxe file at pxefile_addr_r\n"
 );
+#endif
+
+/*
+ * Boots a system using a local disk syslinux/extlinux file
+ *
+ * Returns 0 on success, 1 on error.
+ */
+static int do_sysboot(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
+{
+       unsigned long pxefile_addr_r;
+       struct pxe_menu *cfg;
+       char *pxefile_addr_str;
+       char *filename;
+       int prompt = 0;
+
+       is_pxe = false;
+
+       if (strstr(argv[1], "-p")) {
+               prompt = 1;
+               argc--;
+               argv++;
+       }
+
+       if (argc < 4)
+               return cmd_usage(cmdtp);
+
+       if (argc < 5) {
+               pxefile_addr_str = from_env("pxefile_addr_r");
+               if (!pxefile_addr_str)
+                       return 1;
+       } else {
+               pxefile_addr_str = argv[4];
+       }
+
+       if (argc < 6)
+               filename = getenv("bootfile");
+       else {
+               filename = argv[5];
+               setenv("bootfile", filename);
+       }
+
+       if (strstr(argv[3], "ext2"))
+               do_getfile = do_get_ext2;
+       else if (strstr(argv[3], "fat"))
+               do_getfile = do_get_fat;
+       else if (strstr(argv[3], "any"))
+               do_getfile = do_get_any;
+       else {
+               printf("Invalid filesystem: %s\n", argv[3]);
+               return 1;
+       }
+       fs_argv[1] = argv[1];
+       fs_argv[2] = argv[2];
+
+       if (strict_strtoul(pxefile_addr_str, 16, &pxefile_addr_r) < 0) {
+               printf("Invalid pxefile address: %s\n", pxefile_addr_str);
+               return 1;
+       }
+
+       if (get_pxe_file(cmdtp, filename, (void *)pxefile_addr_r) < 0) {
+               printf("Error reading config file\n");
+               return 1;
+       }
+
+       cfg = parse_pxefile(cmdtp, (char *)(pxefile_addr_r));
+
+       if (cfg == NULL) {
+               printf("Error parsing config file\n");
+               return 1;
+       }
+
+       if (prompt)
+               cfg->prompt = 1;
+
+       handle_pxe_menu(cmdtp, cfg);
+
+       destroy_pxe_menu(cfg);
+
+       return 0;
+}
+
+U_BOOT_CMD(
+       sysboot, 7, 1, do_sysboot,
+       "command to get and boot from syslinux files",
+       "[-p] <interface> <dev[:part]> <ext2|fat|any> [addr] [filename]\n"
+       "    - load and parse syslinux menu file 'filename' from ext2, fat\n"
+       "      or any filesystem on 'dev' on 'interface' to address 'addr'"
+);