X-Git-Url: https://git.kernelconcepts.de/?a=blobdiff_plain;f=common%2Fcmd_pxe.c;h=1fb75d8ae9c1dcdf03e56bf5a3de8eaddfa1bafc;hb=3f4978c713255c8406875fbdf23ffed1129bc44b;hp=b3c1f67a33022c1ad5ec21781e07d3aba76e616c;hpb=5c877b1ae0a4219ed6bd8d32cf3f7106b81ecb3b;p=karo-tx-uboot.git diff --git a/common/cmd_pxe.c b/common/cmd_pxe.c index b3c1f67a33..1fb75d8ae9 100644 --- a/common/cmd_pxe.c +++ b/common/cmd_pxe.c @@ -26,13 +26,21 @@ #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 +}; /* * 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; @@ -56,37 +64,21 @@ 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"); - - if (!ethaddr) - return -ENOENT; + uchar ethaddr[6]; - 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 (%d < 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; } @@ -96,24 +88,24 @@ static int format_mac_pxe(char *outbuf, size_t outbuf_len) * 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; + + if (file_path[0] == '/') + 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; @@ -126,11 +118,55 @@ 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)(const char *file_path, char *file_addr); + +static int do_get_tftp(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(NULL, 0, 3, tftp_argv)) + return -ENOENT; + + return 1; +} + +static char *fs_argv[5]; + +static int do_get_ext2(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(NULL, 0, 5, fs_argv)) + return 1; +#endif + return -ENOENT; +} + +static int do_get_fat(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(NULL, 0, 5, fs_argv)) + 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 +175,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(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 +204,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(relfile, addr_buf); } /* @@ -185,7 +214,7 @@ 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(const char *file_path, void *file_addr) { unsigned long config_file_size; char *tftp_filesize; @@ -222,7 +251,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(const char *file, void *pxefile_addr_r) { size_t base_len = strlen(PXELINUX_DIR); char path[MAX_TFTP_PATH_LEN+1]; @@ -319,12 +348,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,16 +369,23 @@ 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((void *)pxefile_addr_r) > 0 || + pxe_mac_path((void *)pxefile_addr_r) > 0 || + pxe_ipaddr_paths((void *)pxefile_addr_r) > 0) { printf("Config file found\n"); return 0; } + while (pxe_default_paths[i]) { + if (get_pxelinux_path(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; @@ -361,7 +398,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(const char *file_path, const char *envaddr_name) { unsigned long file_addr; char *envaddr; @@ -408,12 +445,17 @@ 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; + int ipappend; int attempted; int localboot; + int localboot_val; struct list_head list; }; @@ -479,6 +521,9 @@ static void label_destroy(struct pxe_label *label) if (label->initrd) free(label->initrd); + if (label->fdt) + free(label->fdt); + free(label); } @@ -491,17 +536,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 +552,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_command(dupcmd, 0); + debug("running: %s\n", localcmd); - free(dupcmd); - - return ret; + return run_command_list(localcmd, strlen(localcmd), 0); } /* @@ -559,34 +582,43 @@ 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(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) { 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] = "-"; } @@ -594,24 +626,81 @@ static void label_boot(struct pxe_label *label) if (get_relfile_envaddr(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); + } + + 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); } 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] && label->fdt) { + if (get_relfile_envaddr(label->fdt, "fdt_addr_r") < 0) { + printf("Skipping %s for failure retrieving fdt\n", + label->name); + return 1; + } + } else + bootm_argv[3] = getenv("fdt_addr"); if (bootm_argv[3]) bootm_argc = 4; do_bootm(NULL, 0, bootm_argc, bootm_argv); + +#ifdef CONFIG_CMD_BOOTZ + /* Try booting a zImage if do_bootm returns */ + do_bootz(NULL, 0, bootm_argc, bootm_argv); +#endif + return 1; } /* @@ -626,12 +715,16 @@ 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_ONTIMEOUT, + T_IPAPPEND, T_INVALID }; @@ -654,10 +747,14 @@ 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}, + {"fdt", T_FDT}, + {"ontimeout", T_ONTIMEOUT,}, + {"ipappend", T_IPAPPEND,}, {NULL, T_INVALID} }; @@ -848,7 +945,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,12 +953,7 @@ 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); @@ -961,14 +1052,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 +1083,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 +1112,42 @@ 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_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: @@ -1087,6 +1203,7 @@ static int parse_pxefile_top(char *p, struct pxe_menu *cfg, int nest_level) err = 0; switch (t.type) { case T_MENU: + cfg->prompt = 1; err = parse_menu(&p, cfg, b, nest_level); break; @@ -1099,6 +1216,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 +1228,13 @@ static int parse_pxefile_top(char *p, struct pxe_menu *cfg, int nest_level) break; + case T_INCLUDE: + err = handle_include(&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: @@ -1194,11 +1317,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 +1332,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); @@ -1284,10 +1415,13 @@ 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(choice); + if (!err) + return; + } else if (err != -ENOENT) { return; + } boot_unattempted_labels(cfg); } @@ -1304,6 +1438,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) @@ -1364,3 +1500,86 @@ 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" ); + +/* + * 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; + + 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 { + 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(filename, (void *)pxefile_addr_r) < 0) { + printf("Error reading config file\n"); + return 1; + } + + cfg = parse_pxefile((char *)(pxefile_addr_r)); + + if (cfg == NULL) { + printf("Error parsing config file\n"); + return 1; + } + + if (prompt) + cfg->prompt = 1; + + handle_pxe_menu(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 or fat\n" + " filesystem on 'dev' on 'interface' to address 'addr'" +);