X-Git-Url: https://git.kernelconcepts.de/?p=karo-tx-uboot.git;a=blobdiff_plain;f=common%2Fcmd_pxe.c;h=348332874b0105abc8055bafc84dfd12705dd0ec;hp=3efd7008e95fe1d74bb48e16a7969e6eec700f22;hb=eeb72e67619b98d2502fe634a3a5d9953de92ad0;hpb=1b81f017b04b5a176f5f9230ec979f0f3c55ba5b diff --git a/common/cmd_pxe.c b/common/cmd_pxe.c index 3efd7008e9..348332874b 100644 --- a/common/cmd_pxe.c +++ b/common/cmd_pxe.c @@ -1,19 +1,9 @@ /* * Copyright 2010-2011 Calxeda, Inc. * - * 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 . + * SPDX-License-Identifier: GPL-2.0+ */ + #include #include #include @@ -21,18 +11,29 @@ #include #include #include +#include #include "menu.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 +45,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 +58,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 +114,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 +186,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 +215,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 +225,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 +253,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 +264,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 +277,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 +285,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 +294,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 +303,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 +313,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 +323,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 +331,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; @@ -318,12 +360,13 @@ static int do_pxe_get(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) { char *pxefile_addr_str; - void *pxefile_addr_r; - int err; + unsigned long pxefile_addr_r; + int err, i = 0; - if (argc != 1) - return cmd_usage(cmdtp); + do_getfile = do_get_tftp; + if (argc != 1) + return CMD_RET_USAGE; pxefile_addr_str = from_env("pxefile_addr_r"); @@ -339,20 +382,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(pxefile_addr_r) > 0 - || pxe_mac_path(pxefile_addr_r) > 0 - || pxe_ipaddr_paths(pxefile_addr_r) > 0 - || get_pxelinux_path("default", 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,9 +412,9 @@ 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) { - void *file_addr; + unsigned long file_addr; char *envaddr; envaddr = from_env(envaddr_name); @@ -371,10 +422,10 @@ static int get_relfile_envaddr(char *file_path, char *envaddr_name) if (!envaddr) return -ENOENT; - if (strict_strtoul(envaddr, 16, (unsigned long *)&file_addr) < 0) + if (strict_strtoul(envaddr, 16, &file_addr) < 0) return -EINVAL; - return get_relfile(file_path, file_addr); + return get_relfile(cmdtp, file_path, (void *)file_addr); } /* @@ -408,12 +459,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 +536,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 +554,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 +570,19 @@ 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) setenv("bootargs", label->append); - printf("running: %s\n", dupcmd); - - ret = run_command2(dupcmd, 0); + debug("running: %s\n", localcmd); - free(dupcmd); - - return ret; + return run_command_list(localcmd, strlen(localcmd), 0); } /* @@ -559,59 +600,184 @@ 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] = ""; + char *bootargs; int bootm_argc = 3; + int len = 0; 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->ipappend & 0x1) { + sprintf(ip_str, " ip=%s:%s:%s:%s", + getenv("ipaddr"), getenv("serverip"), + getenv("gatewayip"), getenv("netmask")); + len += strlen(ip_str); } +#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'; + len += strlen(mac_str); + } +#endif + if (label->append) - setenv("bootargs", label->append); + len += strlen(label->append); + + if (len) { + bootargs = malloc(len + 1); + if (!bootargs) + return 1; + bootargs[0] = '\0'; + if (label->append) + strcpy(bootargs, label->append); + strcat(bootargs, ip_str); + strcat(bootargs, mac_str); + + setenv("bootargs", bootargs); + printf("append: %s\n", bootargs); + + free(bootargs); + } 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); + do_bootm(cmdtp, 0, bootm_argc, bootm_argv); + +#ifdef CONFIG_CMD_BOOTZ + /* Try booting a zImage if do_bootm returns */ + do_bootz(cmdtp, 0, bootm_argc, bootm_argv); +#endif + return 1; } /* @@ -626,12 +792,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 +825,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 +1026,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 +1034,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 +1051,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 +1066,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,11 +1086,11 @@ 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; - int err; + int err = 0; get_token(c, &t, L_KEYWORD); @@ -929,7 +1101,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 +1133,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 +1164,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 +1193,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 +1268,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 +1289,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 +1302,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 +1314,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 +1372,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 +1385,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 +1403,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 +1418,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 +1451,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 +1460,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 +1476,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; @@ -1273,14 +1490,29 @@ static void handle_pxe_menu(struct pxe_menu *cfg) menu_destroy(m); - if (err < 1) - return; + /* + * err == 1 means we got a choice back from menu_get_choice. + * + * err == -ENOENT if the menu was setup to select the default but no + * default was set. in that case, we should continue trying to boot + * labels that haven't been attempted yet. + * + * otherwise, the user interrupted or there was some other error and + * we give up. + */ - label_boot(choice); + 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 * @@ -1293,6 +1525,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) @@ -1301,7 +1535,7 @@ do_pxe_boot(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) } else if (argc == 2) { pxefile_addr_str = argv[1]; } else { - return cmd_usage(cmdtp); + return CMD_RET_USAGE; } if (strict_strtoul(pxefile_addr_str, 16, &pxefile_addr_r) < 0) { @@ -1309,14 +1543,14 @@ 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); @@ -1333,7 +1567,9 @@ int do_pxe(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) cmd_tbl_t *cp; if (argc < 2) - return cmd_usage(cmdtp); + return CMD_RET_USAGE; + + is_pxe = true; /* drop initial "pxe" arg */ argc--; @@ -1344,7 +1580,7 @@ int do_pxe(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) if (cp) return cp->cmd(cmdtp, flag, argc, argv); - return cmd_usage(cmdtp); + return CMD_RET_USAGE; } U_BOOT_CMD( @@ -1353,3 +1589,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. + */ +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] [addr] [filename]\n" + " - load and parse syslinux menu file 'filename' from ext2, fat\n" + " or any filesystem on 'dev' on 'interface' to address 'addr'" +);