#define MAX_TFTP_PATH_LEN 127
-
/*
* 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
* 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;
strncpy(bootfile_path, bootfile, path_len);
+ ret:
bootfile_path[path_len] = '\0';
return 1;
}
+static int (*do_getfile)(char *file_path, char *file_addr);
+
+static int do_get_tftp(char *file_path, char *file_addr)
+{
+ char *tftp_argv[] = {"tftp", NULL, NULL, NULL};
+
+ tftp_argv[1] = file_addr;
+ tftp_argv[2] = file_path;
+
+ if (do_tftpb(NULL, 0, 3, tftp_argv))
+ return -ENOENT;
+
+ return 1;
+}
+
+static char *fs_argv[5];
+
+static int do_get_ext2(char *file_path, char *file_addr)
+{
+#ifdef CONFIG_CMD_EXT2
+ fs_argv[0] = "ext2load";
+ fs_argv[3] = file_addr;
+ fs_argv[4] = file_path;
+
+ if (!do_ext2load(NULL, 0, 5, fs_argv))
+ return 1;
+#endif
+ return -ENOENT;
+}
+
+static int do_get_fat(char *file_path, char *file_addr)
+{
+#ifdef CONFIG_CMD_FAT
+ fs_argv[0] = "fatload";
+ fs_argv[3] = file_addr;
+ fs_argv[4] = 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
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;
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);
}
/*
do_pxe_get(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
{
char *pxefile_addr_str;
- void *pxefile_addr_r;
+ unsigned long pxefile_addr_r;
int err;
- 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");
* 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((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) {
printf("Config file found\n");
*/
static int get_relfile_envaddr(char *file_path, char *envaddr_name)
{
- void *file_addr;
+ unsigned long file_addr;
char *envaddr;
envaddr = from_env(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(file_path, (void *)file_addr);
}
/*
*/
struct pxe_label {
char *name;
+ char *menu;
char *kernel;
char *append;
char *initrd;
+ char *fdt;
int attempted;
int localboot;
struct list_head list;
if (label->initrd)
free(label->initrd);
+ if (label->fdt)
+ free(label->fdt);
+
free(label);
}
static void label_print(void *data)
{
struct pxe_label *label = data;
+ const char *c = label->menu ? label->menu : label->kernel;
- printf("Label: %s\n", label->name);
+ printf("%s:\t%s\n", label->name, c);
if (label->kernel)
- printf("\tkernel: %s\n", label->kernel);
+ printf("\t\tkernel: %s\n", label->kernel);
if (label->append)
- printf("\tappend: %s\n", label->append);
+ printf("\t\tappend: %s\n", label->append);
if (label->initrd)
- printf("\tinitrd: %s\n", label->initrd);
+ printf("\t\tinitrd: %s\n", label->initrd);
+
+ if (label->fdt)
+ printf("\tfdt: %s\n", label->fdt);
}
/*
*/
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);
}
/*
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;
+ }
+ } else
+ bootm_argv[3] = getenv("fdt_addr");
if (bootm_argv[3])
bootm_argc = 4;
T_TIMEOUT,
T_LABEL,
T_KERNEL,
+ T_LINUX,
T_APPEND,
T_INITRD,
T_LOCALBOOT,
T_DEFAULT,
T_PROMPT,
T_INCLUDE,
+ T_FDT,
T_INVALID
};
{"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},
{NULL, T_INVALID}
};
{
struct token t;
char *s = *c;
- int err;
+ int err = 0;
get_token(c, &t, L_KEYWORD);
return -ENOMEM;
break;
+ case T_LABEL:
+ parse_sliteral(c, &label->menu);
+ break;
default:
printf("Ignoring malformed menu command: %.*s\n",
(int)(*c - s), s);
static int parse_label(char **c, struct pxe_menu *cfg)
{
struct token t;
+ int len;
char *s = *c;
struct pxe_label *label;
int err;
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:
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);
break;
/*
* 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;
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)
+ label_boot(choice);
+ else if (err != -ENOENT)
+ return;
boot_unattempted_labels(cfg);
}
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)
} 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) {
cmd_tbl_t *cp;
if (argc < 2)
- return cmd_usage(cmdtp);
+ return CMD_RET_USAGE;
/* drop initial "pxe" arg */
argc--;
if (cp)
return cp->cmd(cmdtp, flag, argc, argv);
- return cmd_usage(cmdtp);
+ return CMD_RET_USAGE;
}
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] <interface> <dev[:part]> <ext2|fat> [addr] [filename]\n"
+ " - load and parse syslinux menu file 'filename' from ext2 or fat\n"
+ " filesystem on 'dev' on 'interface' to address 'addr'"
+);