12e192b24SSimon Glass /* 22e192b24SSimon Glass * Copyright 2010-2011 Calxeda, Inc. 32e192b24SSimon Glass * Copyright (c) 2014, NVIDIA CORPORATION. All rights reserved. 42e192b24SSimon Glass * 52e192b24SSimon Glass * SPDX-License-Identifier: GPL-2.0+ 62e192b24SSimon Glass */ 72e192b24SSimon Glass 82e192b24SSimon Glass #include <common.h> 92e192b24SSimon Glass #include <command.h> 102e192b24SSimon Glass #include <malloc.h> 112e192b24SSimon Glass #include <mapmem.h> 122e192b24SSimon Glass #include <linux/string.h> 132e192b24SSimon Glass #include <linux/ctype.h> 142e192b24SSimon Glass #include <errno.h> 152e192b24SSimon Glass #include <linux/list.h> 162e192b24SSimon Glass #include <fs.h> 172e192b24SSimon Glass #include <asm/io.h> 182e192b24SSimon Glass 192e192b24SSimon Glass #include "menu.h" 202e192b24SSimon Glass #include "cli.h" 212e192b24SSimon Glass 222e192b24SSimon Glass #define MAX_TFTP_PATH_LEN 127 232e192b24SSimon Glass 242e192b24SSimon Glass const char *pxe_default_paths[] = { 252e192b24SSimon Glass #ifdef CONFIG_SYS_SOC 262e192b24SSimon Glass "default-" CONFIG_SYS_ARCH "-" CONFIG_SYS_SOC, 272e192b24SSimon Glass #endif 282e192b24SSimon Glass "default-" CONFIG_SYS_ARCH, 292e192b24SSimon Glass "default", 302e192b24SSimon Glass NULL 312e192b24SSimon Glass }; 322e192b24SSimon Glass 332e192b24SSimon Glass static bool is_pxe; 342e192b24SSimon Glass 352e192b24SSimon Glass /* 362e192b24SSimon Glass * Like getenv, but prints an error if envvar isn't defined in the 372e192b24SSimon Glass * environment. It always returns what getenv does, so it can be used in 382e192b24SSimon Glass * place of getenv without changing error handling otherwise. 392e192b24SSimon Glass */ 402e192b24SSimon Glass static char *from_env(const char *envvar) 412e192b24SSimon Glass { 422e192b24SSimon Glass char *ret; 432e192b24SSimon Glass 442e192b24SSimon Glass ret = getenv(envvar); 452e192b24SSimon Glass 462e192b24SSimon Glass if (!ret) 472e192b24SSimon Glass printf("missing environment variable: %s\n", envvar); 482e192b24SSimon Glass 492e192b24SSimon Glass return ret; 502e192b24SSimon Glass } 512e192b24SSimon Glass 522e192b24SSimon Glass #ifdef CONFIG_CMD_NET 532e192b24SSimon Glass /* 542e192b24SSimon Glass * Convert an ethaddr from the environment to the format used by pxelinux 552e192b24SSimon Glass * filenames based on mac addresses. Convert's ':' to '-', and adds "01-" to 562e192b24SSimon Glass * the beginning of the ethernet address to indicate a hardware type of 572e192b24SSimon Glass * Ethernet. Also converts uppercase hex characters into lowercase, to match 582e192b24SSimon Glass * pxelinux's behavior. 592e192b24SSimon Glass * 602e192b24SSimon Glass * Returns 1 for success, -ENOENT if 'ethaddr' is undefined in the 612e192b24SSimon Glass * environment, or some other value < 0 on error. 622e192b24SSimon Glass */ 632e192b24SSimon Glass static int format_mac_pxe(char *outbuf, size_t outbuf_len) 642e192b24SSimon Glass { 652e192b24SSimon Glass uchar ethaddr[6]; 662e192b24SSimon Glass 672e192b24SSimon Glass if (outbuf_len < 21) { 682e192b24SSimon Glass printf("outbuf is too small (%zd < 21)\n", outbuf_len); 692e192b24SSimon Glass 702e192b24SSimon Glass return -EINVAL; 712e192b24SSimon Glass } 722e192b24SSimon Glass 732e192b24SSimon Glass if (!eth_getenv_enetaddr_by_index("eth", eth_get_dev_index(), 742e192b24SSimon Glass ethaddr)) 752e192b24SSimon Glass return -ENOENT; 762e192b24SSimon Glass 772e192b24SSimon Glass sprintf(outbuf, "01-%02x-%02x-%02x-%02x-%02x-%02x", 782e192b24SSimon Glass ethaddr[0], ethaddr[1], ethaddr[2], 792e192b24SSimon Glass ethaddr[3], ethaddr[4], ethaddr[5]); 802e192b24SSimon Glass 812e192b24SSimon Glass return 1; 822e192b24SSimon Glass } 832e192b24SSimon Glass #endif 842e192b24SSimon Glass 852e192b24SSimon Glass /* 862e192b24SSimon Glass * Returns the directory the file specified in the bootfile env variable is 872e192b24SSimon Glass * in. If bootfile isn't defined in the environment, return NULL, which should 882e192b24SSimon Glass * be interpreted as "don't prepend anything to paths". 892e192b24SSimon Glass */ 902e192b24SSimon Glass static int get_bootfile_path(const char *file_path, char *bootfile_path, 912e192b24SSimon Glass size_t bootfile_path_size) 922e192b24SSimon Glass { 932e192b24SSimon Glass char *bootfile, *last_slash; 942e192b24SSimon Glass size_t path_len = 0; 952e192b24SSimon Glass 962e192b24SSimon Glass /* Only syslinux allows absolute paths */ 972e192b24SSimon Glass if (file_path[0] == '/' && !is_pxe) 982e192b24SSimon Glass goto ret; 992e192b24SSimon Glass 1002e192b24SSimon Glass bootfile = from_env("bootfile"); 1012e192b24SSimon Glass 1022e192b24SSimon Glass if (!bootfile) 1032e192b24SSimon Glass goto ret; 1042e192b24SSimon Glass 1052e192b24SSimon Glass last_slash = strrchr(bootfile, '/'); 1062e192b24SSimon Glass 1072e192b24SSimon Glass if (last_slash == NULL) 1082e192b24SSimon Glass goto ret; 1092e192b24SSimon Glass 1102e192b24SSimon Glass path_len = (last_slash - bootfile) + 1; 1112e192b24SSimon Glass 1122e192b24SSimon Glass if (bootfile_path_size < path_len) { 1132e192b24SSimon Glass printf("bootfile_path too small. (%zd < %zd)\n", 1142e192b24SSimon Glass bootfile_path_size, path_len); 1152e192b24SSimon Glass 1162e192b24SSimon Glass return -1; 1172e192b24SSimon Glass } 1182e192b24SSimon Glass 1192e192b24SSimon Glass strncpy(bootfile_path, bootfile, path_len); 1202e192b24SSimon Glass 1212e192b24SSimon Glass ret: 1222e192b24SSimon Glass bootfile_path[path_len] = '\0'; 1232e192b24SSimon Glass 1242e192b24SSimon Glass return 1; 1252e192b24SSimon Glass } 1262e192b24SSimon Glass 1272e192b24SSimon Glass static int (*do_getfile)(cmd_tbl_t *cmdtp, const char *file_path, char *file_addr); 1282e192b24SSimon Glass 1292e192b24SSimon Glass #ifdef CONFIG_CMD_NET 1302e192b24SSimon Glass static int do_get_tftp(cmd_tbl_t *cmdtp, const char *file_path, char *file_addr) 1312e192b24SSimon Glass { 1322e192b24SSimon Glass char *tftp_argv[] = {"tftp", NULL, NULL, NULL}; 1332e192b24SSimon Glass 1342e192b24SSimon Glass tftp_argv[1] = file_addr; 1352e192b24SSimon Glass tftp_argv[2] = (void *)file_path; 1362e192b24SSimon Glass 1372e192b24SSimon Glass if (do_tftpb(cmdtp, 0, 3, tftp_argv)) 1382e192b24SSimon Glass return -ENOENT; 1392e192b24SSimon Glass 1402e192b24SSimon Glass return 1; 1412e192b24SSimon Glass } 1422e192b24SSimon Glass #endif 1432e192b24SSimon Glass 1442e192b24SSimon Glass static char *fs_argv[5]; 1452e192b24SSimon Glass 1462e192b24SSimon Glass static int do_get_ext2(cmd_tbl_t *cmdtp, const char *file_path, char *file_addr) 1472e192b24SSimon Glass { 1482e192b24SSimon Glass #ifdef CONFIG_CMD_EXT2 1492e192b24SSimon Glass fs_argv[0] = "ext2load"; 1502e192b24SSimon Glass fs_argv[3] = file_addr; 1512e192b24SSimon Glass fs_argv[4] = (void *)file_path; 1522e192b24SSimon Glass 1532e192b24SSimon Glass if (!do_ext2load(cmdtp, 0, 5, fs_argv)) 1542e192b24SSimon Glass return 1; 1552e192b24SSimon Glass #endif 1562e192b24SSimon Glass return -ENOENT; 1572e192b24SSimon Glass } 1582e192b24SSimon Glass 1592e192b24SSimon Glass static int do_get_fat(cmd_tbl_t *cmdtp, const char *file_path, char *file_addr) 1602e192b24SSimon Glass { 1612e192b24SSimon Glass #ifdef CONFIG_CMD_FAT 1622e192b24SSimon Glass fs_argv[0] = "fatload"; 1632e192b24SSimon Glass fs_argv[3] = file_addr; 1642e192b24SSimon Glass fs_argv[4] = (void *)file_path; 1652e192b24SSimon Glass 1662e192b24SSimon Glass if (!do_fat_fsload(cmdtp, 0, 5, fs_argv)) 1672e192b24SSimon Glass return 1; 1682e192b24SSimon Glass #endif 1692e192b24SSimon Glass return -ENOENT; 1702e192b24SSimon Glass } 1712e192b24SSimon Glass 1722e192b24SSimon Glass static int do_get_any(cmd_tbl_t *cmdtp, const char *file_path, char *file_addr) 1732e192b24SSimon Glass { 1742e192b24SSimon Glass #ifdef CONFIG_CMD_FS_GENERIC 1752e192b24SSimon Glass fs_argv[0] = "load"; 1762e192b24SSimon Glass fs_argv[3] = file_addr; 1772e192b24SSimon Glass fs_argv[4] = (void *)file_path; 1782e192b24SSimon Glass 1792e192b24SSimon Glass if (!do_load(cmdtp, 0, 5, fs_argv, FS_TYPE_ANY)) 1802e192b24SSimon Glass return 1; 1812e192b24SSimon Glass #endif 1822e192b24SSimon Glass return -ENOENT; 1832e192b24SSimon Glass } 1842e192b24SSimon Glass 1852e192b24SSimon Glass /* 1862e192b24SSimon Glass * As in pxelinux, paths to files referenced from files we retrieve are 1872e192b24SSimon Glass * relative to the location of bootfile. get_relfile takes such a path and 1882e192b24SSimon Glass * joins it with the bootfile path to get the full path to the target file. If 1892e192b24SSimon Glass * the bootfile path is NULL, we use file_path as is. 1902e192b24SSimon Glass * 1912e192b24SSimon Glass * Returns 1 for success, or < 0 on error. 1922e192b24SSimon Glass */ 1932e192b24SSimon Glass static int get_relfile(cmd_tbl_t *cmdtp, const char *file_path, 1942e192b24SSimon Glass unsigned long file_addr) 1952e192b24SSimon Glass { 1962e192b24SSimon Glass size_t path_len; 1972e192b24SSimon Glass char relfile[MAX_TFTP_PATH_LEN+1]; 1982e192b24SSimon Glass char addr_buf[18]; 1992e192b24SSimon Glass int err; 2002e192b24SSimon Glass 2012e192b24SSimon Glass err = get_bootfile_path(file_path, relfile, sizeof(relfile)); 2022e192b24SSimon Glass 2032e192b24SSimon Glass if (err < 0) 2042e192b24SSimon Glass return err; 2052e192b24SSimon Glass 2062e192b24SSimon Glass path_len = strlen(file_path); 2072e192b24SSimon Glass path_len += strlen(relfile); 2082e192b24SSimon Glass 2092e192b24SSimon Glass if (path_len > MAX_TFTP_PATH_LEN) { 2102e192b24SSimon Glass printf("Base path too long (%s%s)\n", 2112e192b24SSimon Glass relfile, 2122e192b24SSimon Glass file_path); 2132e192b24SSimon Glass 2142e192b24SSimon Glass return -ENAMETOOLONG; 2152e192b24SSimon Glass } 2162e192b24SSimon Glass 2172e192b24SSimon Glass strcat(relfile, file_path); 2182e192b24SSimon Glass 2192e192b24SSimon Glass printf("Retrieving file: %s\n", relfile); 2202e192b24SSimon Glass 2212e192b24SSimon Glass sprintf(addr_buf, "%lx", file_addr); 2222e192b24SSimon Glass 2232e192b24SSimon Glass return do_getfile(cmdtp, relfile, addr_buf); 2242e192b24SSimon Glass } 2252e192b24SSimon Glass 2262e192b24SSimon Glass /* 2272e192b24SSimon Glass * Retrieve the file at 'file_path' to the locate given by 'file_addr'. If 2282e192b24SSimon Glass * 'bootfile' was specified in the environment, the path to bootfile will be 2292e192b24SSimon Glass * prepended to 'file_path' and the resulting path will be used. 2302e192b24SSimon Glass * 2312e192b24SSimon Glass * Returns 1 on success, or < 0 for error. 2322e192b24SSimon Glass */ 2332e192b24SSimon Glass static int get_pxe_file(cmd_tbl_t *cmdtp, const char *file_path, 2342e192b24SSimon Glass unsigned long file_addr) 2352e192b24SSimon Glass { 2362e192b24SSimon Glass unsigned long config_file_size; 2372e192b24SSimon Glass char *tftp_filesize; 2382e192b24SSimon Glass int err; 2392e192b24SSimon Glass char *buf; 2402e192b24SSimon Glass 2412e192b24SSimon Glass err = get_relfile(cmdtp, file_path, file_addr); 2422e192b24SSimon Glass 2432e192b24SSimon Glass if (err < 0) 2442e192b24SSimon Glass return err; 2452e192b24SSimon Glass 2462e192b24SSimon Glass /* 2472e192b24SSimon Glass * the file comes without a NUL byte at the end, so find out its size 2482e192b24SSimon Glass * and add the NUL byte. 2492e192b24SSimon Glass */ 2502e192b24SSimon Glass tftp_filesize = from_env("filesize"); 2512e192b24SSimon Glass 2522e192b24SSimon Glass if (!tftp_filesize) 2532e192b24SSimon Glass return -ENOENT; 2542e192b24SSimon Glass 2552e192b24SSimon Glass if (strict_strtoul(tftp_filesize, 16, &config_file_size) < 0) 2562e192b24SSimon Glass return -EINVAL; 2572e192b24SSimon Glass 2582e192b24SSimon Glass buf = map_sysmem(file_addr + config_file_size, 1); 2592e192b24SSimon Glass *buf = '\0'; 2602e192b24SSimon Glass unmap_sysmem(buf); 2612e192b24SSimon Glass 2622e192b24SSimon Glass return 1; 2632e192b24SSimon Glass } 2642e192b24SSimon Glass 2652e192b24SSimon Glass #ifdef CONFIG_CMD_NET 2662e192b24SSimon Glass 2672e192b24SSimon Glass #define PXELINUX_DIR "pxelinux.cfg/" 2682e192b24SSimon Glass 2692e192b24SSimon Glass /* 2702e192b24SSimon Glass * Retrieves a file in the 'pxelinux.cfg' folder. Since this uses get_pxe_file 2712e192b24SSimon Glass * to do the hard work, the location of the 'pxelinux.cfg' folder is generated 2722e192b24SSimon Glass * from the bootfile path, as described above. 2732e192b24SSimon Glass * 2742e192b24SSimon Glass * Returns 1 on success or < 0 on error. 2752e192b24SSimon Glass */ 2762e192b24SSimon Glass static int get_pxelinux_path(cmd_tbl_t *cmdtp, const char *file, 2772e192b24SSimon Glass unsigned long pxefile_addr_r) 2782e192b24SSimon Glass { 2792e192b24SSimon Glass size_t base_len = strlen(PXELINUX_DIR); 2802e192b24SSimon Glass char path[MAX_TFTP_PATH_LEN+1]; 2812e192b24SSimon Glass 2822e192b24SSimon Glass if (base_len + strlen(file) > MAX_TFTP_PATH_LEN) { 2832e192b24SSimon Glass printf("path (%s%s) too long, skipping\n", 2842e192b24SSimon Glass PXELINUX_DIR, file); 2852e192b24SSimon Glass return -ENAMETOOLONG; 2862e192b24SSimon Glass } 2872e192b24SSimon Glass 2882e192b24SSimon Glass sprintf(path, PXELINUX_DIR "%s", file); 2892e192b24SSimon Glass 2902e192b24SSimon Glass return get_pxe_file(cmdtp, path, pxefile_addr_r); 2912e192b24SSimon Glass } 2922e192b24SSimon Glass 2932e192b24SSimon Glass /* 2942e192b24SSimon Glass * Looks for a pxe file with a name based on the pxeuuid environment variable. 2952e192b24SSimon Glass * 2962e192b24SSimon Glass * Returns 1 on success or < 0 on error. 2972e192b24SSimon Glass */ 2982e192b24SSimon Glass static int pxe_uuid_path(cmd_tbl_t *cmdtp, unsigned long pxefile_addr_r) 2992e192b24SSimon Glass { 3002e192b24SSimon Glass char *uuid_str; 3012e192b24SSimon Glass 3022e192b24SSimon Glass uuid_str = from_env("pxeuuid"); 3032e192b24SSimon Glass 3042e192b24SSimon Glass if (!uuid_str) 3052e192b24SSimon Glass return -ENOENT; 3062e192b24SSimon Glass 3072e192b24SSimon Glass return get_pxelinux_path(cmdtp, uuid_str, pxefile_addr_r); 3082e192b24SSimon Glass } 3092e192b24SSimon Glass 3102e192b24SSimon Glass /* 3112e192b24SSimon Glass * Looks for a pxe file with a name based on the 'ethaddr' environment 3122e192b24SSimon Glass * variable. 3132e192b24SSimon Glass * 3142e192b24SSimon Glass * Returns 1 on success or < 0 on error. 3152e192b24SSimon Glass */ 3162e192b24SSimon Glass static int pxe_mac_path(cmd_tbl_t *cmdtp, unsigned long pxefile_addr_r) 3172e192b24SSimon Glass { 3182e192b24SSimon Glass char mac_str[21]; 3192e192b24SSimon Glass int err; 3202e192b24SSimon Glass 3212e192b24SSimon Glass err = format_mac_pxe(mac_str, sizeof(mac_str)); 3222e192b24SSimon Glass 3232e192b24SSimon Glass if (err < 0) 3242e192b24SSimon Glass return err; 3252e192b24SSimon Glass 3262e192b24SSimon Glass return get_pxelinux_path(cmdtp, mac_str, pxefile_addr_r); 3272e192b24SSimon Glass } 3282e192b24SSimon Glass 3292e192b24SSimon Glass /* 3302e192b24SSimon Glass * Looks for pxe files with names based on our IP address. See pxelinux 3312e192b24SSimon Glass * documentation for details on what these file names look like. We match 3322e192b24SSimon Glass * that exactly. 3332e192b24SSimon Glass * 3342e192b24SSimon Glass * Returns 1 on success or < 0 on error. 3352e192b24SSimon Glass */ 3362e192b24SSimon Glass static int pxe_ipaddr_paths(cmd_tbl_t *cmdtp, unsigned long pxefile_addr_r) 3372e192b24SSimon Glass { 3382e192b24SSimon Glass char ip_addr[9]; 3392e192b24SSimon Glass int mask_pos, err; 3402e192b24SSimon Glass 3412e192b24SSimon Glass sprintf(ip_addr, "%08X", ntohl(net_ip.s_addr)); 3422e192b24SSimon Glass 3432e192b24SSimon Glass for (mask_pos = 7; mask_pos >= 0; mask_pos--) { 3442e192b24SSimon Glass err = get_pxelinux_path(cmdtp, ip_addr, pxefile_addr_r); 3452e192b24SSimon Glass 3462e192b24SSimon Glass if (err > 0) 3472e192b24SSimon Glass return err; 3482e192b24SSimon Glass 3492e192b24SSimon Glass ip_addr[mask_pos] = '\0'; 3502e192b24SSimon Glass } 3512e192b24SSimon Glass 3522e192b24SSimon Glass return -ENOENT; 3532e192b24SSimon Glass } 3542e192b24SSimon Glass 3552e192b24SSimon Glass /* 3562e192b24SSimon Glass * Entry point for the 'pxe get' command. 3572e192b24SSimon Glass * This Follows pxelinux's rules to download a config file from a tftp server. 3582e192b24SSimon Glass * The file is stored at the location given by the pxefile_addr_r environment 3592e192b24SSimon Glass * variable, which must be set. 3602e192b24SSimon Glass * 3612e192b24SSimon Glass * UUID comes from pxeuuid env variable, if defined 3622e192b24SSimon Glass * MAC addr comes from ethaddr env variable, if defined 3632e192b24SSimon Glass * IP 3642e192b24SSimon Glass * 3652e192b24SSimon Glass * see http://syslinux.zytor.com/wiki/index.php/PXELINUX 3662e192b24SSimon Glass * 3672e192b24SSimon Glass * Returns 0 on success or 1 on error. 3682e192b24SSimon Glass */ 3692e192b24SSimon Glass static int 3702e192b24SSimon Glass do_pxe_get(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) 3712e192b24SSimon Glass { 3722e192b24SSimon Glass char *pxefile_addr_str; 3732e192b24SSimon Glass unsigned long pxefile_addr_r; 3742e192b24SSimon Glass int err, i = 0; 3752e192b24SSimon Glass 3762e192b24SSimon Glass do_getfile = do_get_tftp; 3772e192b24SSimon Glass 3782e192b24SSimon Glass if (argc != 1) 3792e192b24SSimon Glass return CMD_RET_USAGE; 3802e192b24SSimon Glass 3812e192b24SSimon Glass pxefile_addr_str = from_env("pxefile_addr_r"); 3822e192b24SSimon Glass 3832e192b24SSimon Glass if (!pxefile_addr_str) 3842e192b24SSimon Glass return 1; 3852e192b24SSimon Glass 3862e192b24SSimon Glass err = strict_strtoul(pxefile_addr_str, 16, 3872e192b24SSimon Glass (unsigned long *)&pxefile_addr_r); 3882e192b24SSimon Glass if (err < 0) 3892e192b24SSimon Glass return 1; 3902e192b24SSimon Glass 3912e192b24SSimon Glass /* 3922e192b24SSimon Glass * Keep trying paths until we successfully get a file we're looking 3932e192b24SSimon Glass * for. 3942e192b24SSimon Glass */ 3952e192b24SSimon Glass if (pxe_uuid_path(cmdtp, pxefile_addr_r) > 0 || 3962e192b24SSimon Glass pxe_mac_path(cmdtp, pxefile_addr_r) > 0 || 3972e192b24SSimon Glass pxe_ipaddr_paths(cmdtp, pxefile_addr_r) > 0) { 3982e192b24SSimon Glass printf("Config file found\n"); 3992e192b24SSimon Glass 4002e192b24SSimon Glass return 0; 4012e192b24SSimon Glass } 4022e192b24SSimon Glass 4032e192b24SSimon Glass while (pxe_default_paths[i]) { 4042e192b24SSimon Glass if (get_pxelinux_path(cmdtp, pxe_default_paths[i], 4052e192b24SSimon Glass pxefile_addr_r) > 0) { 4062e192b24SSimon Glass printf("Config file found\n"); 4072e192b24SSimon Glass return 0; 4082e192b24SSimon Glass } 4092e192b24SSimon Glass i++; 4102e192b24SSimon Glass } 4112e192b24SSimon Glass 4122e192b24SSimon Glass printf("Config file not found\n"); 4132e192b24SSimon Glass 4142e192b24SSimon Glass return 1; 4152e192b24SSimon Glass } 4162e192b24SSimon Glass #endif 4172e192b24SSimon Glass 4182e192b24SSimon Glass /* 4192e192b24SSimon Glass * Wrapper to make it easier to store the file at file_path in the location 4202e192b24SSimon Glass * specified by envaddr_name. file_path will be joined to the bootfile path, 4212e192b24SSimon Glass * if any is specified. 4222e192b24SSimon Glass * 4232e192b24SSimon Glass * Returns 1 on success or < 0 on error. 4242e192b24SSimon Glass */ 4252e192b24SSimon Glass static int get_relfile_envaddr(cmd_tbl_t *cmdtp, const char *file_path, const char *envaddr_name) 4262e192b24SSimon Glass { 4272e192b24SSimon Glass unsigned long file_addr; 4282e192b24SSimon Glass char *envaddr; 4292e192b24SSimon Glass 4302e192b24SSimon Glass envaddr = from_env(envaddr_name); 4312e192b24SSimon Glass 4322e192b24SSimon Glass if (!envaddr) 4332e192b24SSimon Glass return -ENOENT; 4342e192b24SSimon Glass 4352e192b24SSimon Glass if (strict_strtoul(envaddr, 16, &file_addr) < 0) 4362e192b24SSimon Glass return -EINVAL; 4372e192b24SSimon Glass 4382e192b24SSimon Glass return get_relfile(cmdtp, file_path, file_addr); 4392e192b24SSimon Glass } 4402e192b24SSimon Glass 4412e192b24SSimon Glass /* 4422e192b24SSimon Glass * A note on the pxe file parser. 4432e192b24SSimon Glass * 4442e192b24SSimon Glass * We're parsing files that use syslinux grammar, which has a few quirks. 4452e192b24SSimon Glass * String literals must be recognized based on context - there is no 4462e192b24SSimon Glass * quoting or escaping support. There's also nothing to explicitly indicate 4472e192b24SSimon Glass * when a label section completes. We deal with that by ending a label 4482e192b24SSimon Glass * section whenever we see a line that doesn't include. 4492e192b24SSimon Glass * 4502e192b24SSimon Glass * As with the syslinux family, this same file format could be reused in the 4512e192b24SSimon Glass * future for non pxe purposes. The only action it takes during parsing that 4522e192b24SSimon Glass * would throw this off is handling of include files. It assumes we're using 4532e192b24SSimon Glass * pxe, and does a tftp download of a file listed as an include file in the 4542e192b24SSimon Glass * middle of the parsing operation. That could be handled by refactoring it to 4552e192b24SSimon Glass * take a 'include file getter' function. 4562e192b24SSimon Glass */ 4572e192b24SSimon Glass 4582e192b24SSimon Glass /* 4592e192b24SSimon Glass * Describes a single label given in a pxe file. 4602e192b24SSimon Glass * 4612e192b24SSimon Glass * Create these with the 'label_create' function given below. 4622e192b24SSimon Glass * 4632e192b24SSimon Glass * name - the name of the menu as given on the 'menu label' line. 4642e192b24SSimon Glass * kernel - the path to the kernel file to use for this label. 4652e192b24SSimon Glass * append - kernel command line to use when booting this label 4662e192b24SSimon Glass * initrd - path to the initrd to use for this label. 4672e192b24SSimon Glass * attempted - 0 if we haven't tried to boot this label, 1 if we have. 4682e192b24SSimon Glass * localboot - 1 if this label specified 'localboot', 0 otherwise. 4692e192b24SSimon Glass * list - lets these form a list, which a pxe_menu struct will hold. 4702e192b24SSimon Glass */ 4712e192b24SSimon Glass struct pxe_label { 4722e192b24SSimon Glass char num[4]; 4732e192b24SSimon Glass char *name; 4742e192b24SSimon Glass char *menu; 4752e192b24SSimon Glass char *kernel; 4762e192b24SSimon Glass char *append; 4772e192b24SSimon Glass char *initrd; 4782e192b24SSimon Glass char *fdt; 4792e192b24SSimon Glass char *fdtdir; 4802e192b24SSimon Glass int ipappend; 4812e192b24SSimon Glass int attempted; 4822e192b24SSimon Glass int localboot; 4832e192b24SSimon Glass int localboot_val; 4842e192b24SSimon Glass struct list_head list; 4852e192b24SSimon Glass }; 4862e192b24SSimon Glass 4872e192b24SSimon Glass /* 4882e192b24SSimon Glass * Describes a pxe menu as given via pxe files. 4892e192b24SSimon Glass * 4902e192b24SSimon Glass * title - the name of the menu as given by a 'menu title' line. 4912e192b24SSimon Glass * default_label - the name of the default label, if any. 4922e192b24SSimon Glass * timeout - time in tenths of a second to wait for a user key-press before 4932e192b24SSimon Glass * booting the default label. 4942e192b24SSimon Glass * prompt - if 0, don't prompt for a choice unless the timeout period is 4952e192b24SSimon Glass * interrupted. If 1, always prompt for a choice regardless of 4962e192b24SSimon Glass * timeout. 4972e192b24SSimon Glass * labels - a list of labels defined for the menu. 4982e192b24SSimon Glass */ 4992e192b24SSimon Glass struct pxe_menu { 5002e192b24SSimon Glass char *title; 5012e192b24SSimon Glass char *default_label; 5022e192b24SSimon Glass int timeout; 5032e192b24SSimon Glass int prompt; 5042e192b24SSimon Glass struct list_head labels; 5052e192b24SSimon Glass }; 5062e192b24SSimon Glass 5072e192b24SSimon Glass /* 5082e192b24SSimon Glass * Allocates memory for and initializes a pxe_label. This uses malloc, so the 5092e192b24SSimon Glass * result must be free()'d to reclaim the memory. 5102e192b24SSimon Glass * 5112e192b24SSimon Glass * Returns NULL if malloc fails. 5122e192b24SSimon Glass */ 5132e192b24SSimon Glass static struct pxe_label *label_create(void) 5142e192b24SSimon Glass { 5152e192b24SSimon Glass struct pxe_label *label; 5162e192b24SSimon Glass 5172e192b24SSimon Glass label = malloc(sizeof(struct pxe_label)); 5182e192b24SSimon Glass 5192e192b24SSimon Glass if (!label) 5202e192b24SSimon Glass return NULL; 5212e192b24SSimon Glass 5222e192b24SSimon Glass memset(label, 0, sizeof(struct pxe_label)); 5232e192b24SSimon Glass 5242e192b24SSimon Glass return label; 5252e192b24SSimon Glass } 5262e192b24SSimon Glass 5272e192b24SSimon Glass /* 5282e192b24SSimon Glass * Free the memory used by a pxe_label, including that used by its name, 5292e192b24SSimon Glass * kernel, append and initrd members, if they're non NULL. 5302e192b24SSimon Glass * 5312e192b24SSimon Glass * So - be sure to only use dynamically allocated memory for the members of 5322e192b24SSimon Glass * the pxe_label struct, unless you want to clean it up first. These are 5332e192b24SSimon Glass * currently only created by the pxe file parsing code. 5342e192b24SSimon Glass */ 5352e192b24SSimon Glass static void label_destroy(struct pxe_label *label) 5362e192b24SSimon Glass { 5372e192b24SSimon Glass if (label->name) 5382e192b24SSimon Glass free(label->name); 5392e192b24SSimon Glass 5402e192b24SSimon Glass if (label->kernel) 5412e192b24SSimon Glass free(label->kernel); 5422e192b24SSimon Glass 5432e192b24SSimon Glass if (label->append) 5442e192b24SSimon Glass free(label->append); 5452e192b24SSimon Glass 5462e192b24SSimon Glass if (label->initrd) 5472e192b24SSimon Glass free(label->initrd); 5482e192b24SSimon Glass 5492e192b24SSimon Glass if (label->fdt) 5502e192b24SSimon Glass free(label->fdt); 5512e192b24SSimon Glass 5522e192b24SSimon Glass if (label->fdtdir) 5532e192b24SSimon Glass free(label->fdtdir); 5542e192b24SSimon Glass 5552e192b24SSimon Glass free(label); 5562e192b24SSimon Glass } 5572e192b24SSimon Glass 5582e192b24SSimon Glass /* 5592e192b24SSimon Glass * Print a label and its string members if they're defined. 5602e192b24SSimon Glass * 5612e192b24SSimon Glass * This is passed as a callback to the menu code for displaying each 5622e192b24SSimon Glass * menu entry. 5632e192b24SSimon Glass */ 5642e192b24SSimon Glass static void label_print(void *data) 5652e192b24SSimon Glass { 5662e192b24SSimon Glass struct pxe_label *label = data; 5672e192b24SSimon Glass const char *c = label->menu ? label->menu : label->name; 5682e192b24SSimon Glass 5692e192b24SSimon Glass printf("%s:\t%s\n", label->num, c); 5702e192b24SSimon Glass } 5712e192b24SSimon Glass 5722e192b24SSimon Glass /* 5732e192b24SSimon Glass * Boot a label that specified 'localboot'. This requires that the 'localcmd' 574a187559eSBin Meng * environment variable is defined. Its contents will be executed as U-Boot 5752e192b24SSimon Glass * command. If the label specified an 'append' line, its contents will be 5762e192b24SSimon Glass * used to overwrite the contents of the 'bootargs' environment variable prior 5772e192b24SSimon Glass * to running 'localcmd'. 5782e192b24SSimon Glass * 5792e192b24SSimon Glass * Returns 1 on success or < 0 on error. 5802e192b24SSimon Glass */ 5812e192b24SSimon Glass static int label_localboot(struct pxe_label *label) 5822e192b24SSimon Glass { 5832e192b24SSimon Glass char *localcmd; 5842e192b24SSimon Glass 5852e192b24SSimon Glass localcmd = from_env("localcmd"); 5862e192b24SSimon Glass 5872e192b24SSimon Glass if (!localcmd) 5882e192b24SSimon Glass return -ENOENT; 5892e192b24SSimon Glass 5902e192b24SSimon Glass if (label->append) { 5912e192b24SSimon Glass char bootargs[CONFIG_SYS_CBSIZE]; 5922e192b24SSimon Glass 5932e192b24SSimon Glass cli_simple_process_macros(label->append, bootargs); 594*382bee57SSimon Glass env_set("bootargs", bootargs); 5952e192b24SSimon Glass } 5962e192b24SSimon Glass 5972e192b24SSimon Glass debug("running: %s\n", localcmd); 5982e192b24SSimon Glass 5992e192b24SSimon Glass return run_command_list(localcmd, strlen(localcmd), 0); 6002e192b24SSimon Glass } 6012e192b24SSimon Glass 6022e192b24SSimon Glass /* 6032e192b24SSimon Glass * Boot according to the contents of a pxe_label. 6042e192b24SSimon Glass * 6052e192b24SSimon Glass * If we can't boot for any reason, we return. A successful boot never 6062e192b24SSimon Glass * returns. 6072e192b24SSimon Glass * 6082e192b24SSimon Glass * The kernel will be stored in the location given by the 'kernel_addr_r' 6092e192b24SSimon Glass * environment variable. 6102e192b24SSimon Glass * 6112e192b24SSimon Glass * If the label specifies an initrd file, it will be stored in the location 6122e192b24SSimon Glass * given by the 'ramdisk_addr_r' environment variable. 6132e192b24SSimon Glass * 6142e192b24SSimon Glass * If the label specifies an 'append' line, its contents will overwrite that 6152e192b24SSimon Glass * of the 'bootargs' environment variable. 6162e192b24SSimon Glass */ 6172e192b24SSimon Glass static int label_boot(cmd_tbl_t *cmdtp, struct pxe_label *label) 6182e192b24SSimon Glass { 6192e192b24SSimon Glass char *bootm_argv[] = { "bootm", NULL, NULL, NULL, NULL }; 6202e192b24SSimon Glass char initrd_str[22]; 6212e192b24SSimon Glass char mac_str[29] = ""; 6222e192b24SSimon Glass char ip_str[68] = ""; 623f63963f0SYork Sun int bootm_argc = 2; 6242e192b24SSimon Glass int len = 0; 6252e192b24SSimon Glass ulong kernel_addr; 6262e192b24SSimon Glass void *buf; 6272e192b24SSimon Glass 6282e192b24SSimon Glass label_print(label); 6292e192b24SSimon Glass 6302e192b24SSimon Glass label->attempted = 1; 6312e192b24SSimon Glass 6322e192b24SSimon Glass if (label->localboot) { 6332e192b24SSimon Glass if (label->localboot_val >= 0) 6342e192b24SSimon Glass label_localboot(label); 6352e192b24SSimon Glass return 0; 6362e192b24SSimon Glass } 6372e192b24SSimon Glass 6382e192b24SSimon Glass if (label->kernel == NULL) { 6392e192b24SSimon Glass printf("No kernel given, skipping %s\n", 6402e192b24SSimon Glass label->name); 6412e192b24SSimon Glass return 1; 6422e192b24SSimon Glass } 6432e192b24SSimon Glass 6442e192b24SSimon Glass if (label->initrd) { 6452e192b24SSimon Glass if (get_relfile_envaddr(cmdtp, label->initrd, "ramdisk_addr_r") < 0) { 6462e192b24SSimon Glass printf("Skipping %s for failure retrieving initrd\n", 6472e192b24SSimon Glass label->name); 6482e192b24SSimon Glass return 1; 6492e192b24SSimon Glass } 6502e192b24SSimon Glass 6512e192b24SSimon Glass bootm_argv[2] = initrd_str; 6522e192b24SSimon Glass strcpy(bootm_argv[2], getenv("ramdisk_addr_r")); 6532e192b24SSimon Glass strcat(bootm_argv[2], ":"); 6542e192b24SSimon Glass strcat(bootm_argv[2], getenv("filesize")); 6552e192b24SSimon Glass } 6562e192b24SSimon Glass 6572e192b24SSimon Glass if (get_relfile_envaddr(cmdtp, label->kernel, "kernel_addr_r") < 0) { 6582e192b24SSimon Glass printf("Skipping %s for failure retrieving kernel\n", 6592e192b24SSimon Glass label->name); 6602e192b24SSimon Glass return 1; 6612e192b24SSimon Glass } 6622e192b24SSimon Glass 6632e192b24SSimon Glass if (label->ipappend & 0x1) { 6642e192b24SSimon Glass sprintf(ip_str, " ip=%s:%s:%s:%s", 6652e192b24SSimon Glass getenv("ipaddr"), getenv("serverip"), 6662e192b24SSimon Glass getenv("gatewayip"), getenv("netmask")); 6672e192b24SSimon Glass } 6682e192b24SSimon Glass 6692e192b24SSimon Glass #ifdef CONFIG_CMD_NET 6702e192b24SSimon Glass if (label->ipappend & 0x2) { 6712e192b24SSimon Glass int err; 6722e192b24SSimon Glass strcpy(mac_str, " BOOTIF="); 6732e192b24SSimon Glass err = format_mac_pxe(mac_str + 8, sizeof(mac_str) - 8); 6742e192b24SSimon Glass if (err < 0) 6752e192b24SSimon Glass mac_str[0] = '\0'; 6762e192b24SSimon Glass } 6772e192b24SSimon Glass #endif 6782e192b24SSimon Glass 6792e192b24SSimon Glass if ((label->ipappend & 0x3) || label->append) { 6802e192b24SSimon Glass char bootargs[CONFIG_SYS_CBSIZE] = ""; 6812e192b24SSimon Glass char finalbootargs[CONFIG_SYS_CBSIZE]; 6822e192b24SSimon Glass 6832e192b24SSimon Glass if (strlen(label->append ?: "") + 6842e192b24SSimon Glass strlen(ip_str) + strlen(mac_str) + 1 > sizeof(bootargs)) { 6852e192b24SSimon Glass printf("bootarg overflow %zd+%zd+%zd+1 > %zd\n", 6862e192b24SSimon Glass strlen(label->append ?: ""), 6872e192b24SSimon Glass strlen(ip_str), strlen(mac_str), 6882e192b24SSimon Glass sizeof(bootargs)); 6892e192b24SSimon Glass return 1; 6902e192b24SSimon Glass } 6912e192b24SSimon Glass 6922e192b24SSimon Glass if (label->append) 6932e192b24SSimon Glass strcpy(bootargs, label->append); 6942e192b24SSimon Glass strcat(bootargs, ip_str); 6952e192b24SSimon Glass strcat(bootargs, mac_str); 6962e192b24SSimon Glass 6972e192b24SSimon Glass cli_simple_process_macros(bootargs, finalbootargs); 698*382bee57SSimon Glass env_set("bootargs", finalbootargs); 6992e192b24SSimon Glass printf("append: %s\n", finalbootargs); 7002e192b24SSimon Glass } 7012e192b24SSimon Glass 7022e192b24SSimon Glass bootm_argv[1] = getenv("kernel_addr_r"); 7032e192b24SSimon Glass 7042e192b24SSimon Glass /* 7052e192b24SSimon Glass * fdt usage is optional: 7062e192b24SSimon Glass * It handles the following scenarios. All scenarios are exclusive 7072e192b24SSimon Glass * 7082e192b24SSimon Glass * Scenario 1: If fdt_addr_r specified and "fdt" label is defined in 7092e192b24SSimon Glass * pxe file, retrieve fdt blob from server. Pass fdt_addr_r to bootm, 7102e192b24SSimon Glass * and adjust argc appropriately. 7112e192b24SSimon Glass * 7122e192b24SSimon Glass * Scenario 2: If there is an fdt_addr specified, pass it along to 7132e192b24SSimon Glass * bootm, and adjust argc appropriately. 7142e192b24SSimon Glass * 7152e192b24SSimon Glass * Scenario 3: fdt blob is not available. 7162e192b24SSimon Glass */ 7172e192b24SSimon Glass bootm_argv[3] = getenv("fdt_addr_r"); 7182e192b24SSimon Glass 7192e192b24SSimon Glass /* if fdt label is defined then get fdt from server */ 7202e192b24SSimon Glass if (bootm_argv[3]) { 7212e192b24SSimon Glass char *fdtfile = NULL; 7222e192b24SSimon Glass char *fdtfilefree = NULL; 7232e192b24SSimon Glass 7242e192b24SSimon Glass if (label->fdt) { 7252e192b24SSimon Glass fdtfile = label->fdt; 7262e192b24SSimon Glass } else if (label->fdtdir) { 7272e192b24SSimon Glass char *f1, *f2, *f3, *f4, *slash; 7282e192b24SSimon Glass 7292e192b24SSimon Glass f1 = getenv("fdtfile"); 7302e192b24SSimon Glass if (f1) { 7312e192b24SSimon Glass f2 = ""; 7322e192b24SSimon Glass f3 = ""; 7332e192b24SSimon Glass f4 = ""; 7342e192b24SSimon Glass } else { 7352e192b24SSimon Glass /* 7362e192b24SSimon Glass * For complex cases where this code doesn't 7372e192b24SSimon Glass * generate the correct filename, the board 7382e192b24SSimon Glass * code should set $fdtfile during early boot, 7392e192b24SSimon Glass * or the boot scripts should set $fdtfile 7402e192b24SSimon Glass * before invoking "pxe" or "sysboot". 7412e192b24SSimon Glass */ 7422e192b24SSimon Glass f1 = getenv("soc"); 7432e192b24SSimon Glass f2 = "-"; 7442e192b24SSimon Glass f3 = getenv("board"); 7452e192b24SSimon Glass f4 = ".dtb"; 7462e192b24SSimon Glass } 7472e192b24SSimon Glass 7482e192b24SSimon Glass len = strlen(label->fdtdir); 7492e192b24SSimon Glass if (!len) 7502e192b24SSimon Glass slash = "./"; 7512e192b24SSimon Glass else if (label->fdtdir[len - 1] != '/') 7522e192b24SSimon Glass slash = "/"; 7532e192b24SSimon Glass else 7542e192b24SSimon Glass slash = ""; 7552e192b24SSimon Glass 7562e192b24SSimon Glass len = strlen(label->fdtdir) + strlen(slash) + 7572e192b24SSimon Glass strlen(f1) + strlen(f2) + strlen(f3) + 7582e192b24SSimon Glass strlen(f4) + 1; 7592e192b24SSimon Glass fdtfilefree = malloc(len); 7602e192b24SSimon Glass if (!fdtfilefree) { 7612e192b24SSimon Glass printf("malloc fail (FDT filename)\n"); 7622e192b24SSimon Glass return 1; 7632e192b24SSimon Glass } 7642e192b24SSimon Glass 7652e192b24SSimon Glass snprintf(fdtfilefree, len, "%s%s%s%s%s%s", 7662e192b24SSimon Glass label->fdtdir, slash, f1, f2, f3, f4); 7672e192b24SSimon Glass fdtfile = fdtfilefree; 7682e192b24SSimon Glass } 7692e192b24SSimon Glass 7702e192b24SSimon Glass if (fdtfile) { 7712e192b24SSimon Glass int err = get_relfile_envaddr(cmdtp, fdtfile, "fdt_addr_r"); 7722e192b24SSimon Glass free(fdtfilefree); 7732e192b24SSimon Glass if (err < 0) { 7742e192b24SSimon Glass printf("Skipping %s for failure retrieving fdt\n", 7752e192b24SSimon Glass label->name); 7762e192b24SSimon Glass return 1; 7772e192b24SSimon Glass } 7782e192b24SSimon Glass } else { 7792e192b24SSimon Glass bootm_argv[3] = NULL; 7802e192b24SSimon Glass } 7812e192b24SSimon Glass } 7822e192b24SSimon Glass 7832e192b24SSimon Glass if (!bootm_argv[3]) 7842e192b24SSimon Glass bootm_argv[3] = getenv("fdt_addr"); 7852e192b24SSimon Glass 786f63963f0SYork Sun if (bootm_argv[3]) { 787f63963f0SYork Sun if (!bootm_argv[2]) 788f63963f0SYork Sun bootm_argv[2] = "-"; 7892e192b24SSimon Glass bootm_argc = 4; 790f63963f0SYork Sun } 7912e192b24SSimon Glass 7922e192b24SSimon Glass kernel_addr = genimg_get_kernel_addr(bootm_argv[1]); 7932e192b24SSimon Glass buf = map_sysmem(kernel_addr, 0); 7942e192b24SSimon Glass /* Try bootm for legacy and FIT format image */ 7952e192b24SSimon Glass if (genimg_get_format(buf) != IMAGE_FORMAT_INVALID) 7962e192b24SSimon Glass do_bootm(cmdtp, 0, bootm_argc, bootm_argv); 7972e192b24SSimon Glass #ifdef CONFIG_CMD_BOOTI 7982e192b24SSimon Glass /* Try booting an AArch64 Linux kernel image */ 7992e192b24SSimon Glass else 8002e192b24SSimon Glass do_booti(cmdtp, 0, bootm_argc, bootm_argv); 8012e192b24SSimon Glass #elif defined(CONFIG_CMD_BOOTZ) 8022e192b24SSimon Glass /* Try booting a Image */ 8032e192b24SSimon Glass else 8042e192b24SSimon Glass do_bootz(cmdtp, 0, bootm_argc, bootm_argv); 8052e192b24SSimon Glass #endif 8062e192b24SSimon Glass unmap_sysmem(buf); 8072e192b24SSimon Glass return 1; 8082e192b24SSimon Glass } 8092e192b24SSimon Glass 8102e192b24SSimon Glass /* 8112e192b24SSimon Glass * Tokens for the pxe file parser. 8122e192b24SSimon Glass */ 8132e192b24SSimon Glass enum token_type { 8142e192b24SSimon Glass T_EOL, 8152e192b24SSimon Glass T_STRING, 8162e192b24SSimon Glass T_EOF, 8172e192b24SSimon Glass T_MENU, 8182e192b24SSimon Glass T_TITLE, 8192e192b24SSimon Glass T_TIMEOUT, 8202e192b24SSimon Glass T_LABEL, 8212e192b24SSimon Glass T_KERNEL, 8222e192b24SSimon Glass T_LINUX, 8232e192b24SSimon Glass T_APPEND, 8242e192b24SSimon Glass T_INITRD, 8252e192b24SSimon Glass T_LOCALBOOT, 8262e192b24SSimon Glass T_DEFAULT, 8272e192b24SSimon Glass T_PROMPT, 8282e192b24SSimon Glass T_INCLUDE, 8292e192b24SSimon Glass T_FDT, 8302e192b24SSimon Glass T_FDTDIR, 8312e192b24SSimon Glass T_ONTIMEOUT, 8322e192b24SSimon Glass T_IPAPPEND, 8332e192b24SSimon Glass T_INVALID 8342e192b24SSimon Glass }; 8352e192b24SSimon Glass 8362e192b24SSimon Glass /* 8372e192b24SSimon Glass * A token - given by a value and a type. 8382e192b24SSimon Glass */ 8392e192b24SSimon Glass struct token { 8402e192b24SSimon Glass char *val; 8412e192b24SSimon Glass enum token_type type; 8422e192b24SSimon Glass }; 8432e192b24SSimon Glass 8442e192b24SSimon Glass /* 8452e192b24SSimon Glass * Keywords recognized. 8462e192b24SSimon Glass */ 8472e192b24SSimon Glass static const struct token keywords[] = { 8482e192b24SSimon Glass {"menu", T_MENU}, 8492e192b24SSimon Glass {"title", T_TITLE}, 8502e192b24SSimon Glass {"timeout", T_TIMEOUT}, 8512e192b24SSimon Glass {"default", T_DEFAULT}, 8522e192b24SSimon Glass {"prompt", T_PROMPT}, 8532e192b24SSimon Glass {"label", T_LABEL}, 8542e192b24SSimon Glass {"kernel", T_KERNEL}, 8552e192b24SSimon Glass {"linux", T_LINUX}, 8562e192b24SSimon Glass {"localboot", T_LOCALBOOT}, 8572e192b24SSimon Glass {"append", T_APPEND}, 8582e192b24SSimon Glass {"initrd", T_INITRD}, 8592e192b24SSimon Glass {"include", T_INCLUDE}, 8602e192b24SSimon Glass {"devicetree", T_FDT}, 8612e192b24SSimon Glass {"fdt", T_FDT}, 8622e192b24SSimon Glass {"devicetreedir", T_FDTDIR}, 8632e192b24SSimon Glass {"fdtdir", T_FDTDIR}, 8642e192b24SSimon Glass {"ontimeout", T_ONTIMEOUT,}, 8652e192b24SSimon Glass {"ipappend", T_IPAPPEND,}, 8662e192b24SSimon Glass {NULL, T_INVALID} 8672e192b24SSimon Glass }; 8682e192b24SSimon Glass 8692e192b24SSimon Glass /* 8702e192b24SSimon Glass * Since pxe(linux) files don't have a token to identify the start of a 8712e192b24SSimon Glass * literal, we have to keep track of when we're in a state where a literal is 8722e192b24SSimon Glass * expected vs when we're in a state a keyword is expected. 8732e192b24SSimon Glass */ 8742e192b24SSimon Glass enum lex_state { 8752e192b24SSimon Glass L_NORMAL = 0, 8762e192b24SSimon Glass L_KEYWORD, 8772e192b24SSimon Glass L_SLITERAL 8782e192b24SSimon Glass }; 8792e192b24SSimon Glass 8802e192b24SSimon Glass /* 8812e192b24SSimon Glass * get_string retrieves a string from *p and stores it as a token in 8822e192b24SSimon Glass * *t. 8832e192b24SSimon Glass * 8842e192b24SSimon Glass * get_string used for scanning both string literals and keywords. 8852e192b24SSimon Glass * 8862e192b24SSimon Glass * Characters from *p are copied into t-val until a character equal to 8872e192b24SSimon Glass * delim is found, or a NUL byte is reached. If delim has the special value of 8882e192b24SSimon Glass * ' ', any whitespace character will be used as a delimiter. 8892e192b24SSimon Glass * 8902e192b24SSimon Glass * If lower is unequal to 0, uppercase characters will be converted to 8912e192b24SSimon Glass * lowercase in the result. This is useful to make keywords case 8922e192b24SSimon Glass * insensitive. 8932e192b24SSimon Glass * 8942e192b24SSimon Glass * The location of *p is updated to point to the first character after the end 8952e192b24SSimon Glass * of the token - the ending delimiter. 8962e192b24SSimon Glass * 8972e192b24SSimon Glass * On success, the new value of t->val is returned. Memory for t->val is 8982e192b24SSimon Glass * allocated using malloc and must be free()'d to reclaim it. If insufficient 8992e192b24SSimon Glass * memory is available, NULL is returned. 9002e192b24SSimon Glass */ 9012e192b24SSimon Glass static char *get_string(char **p, struct token *t, char delim, int lower) 9022e192b24SSimon Glass { 9032e192b24SSimon Glass char *b, *e; 9042e192b24SSimon Glass size_t len, i; 9052e192b24SSimon Glass 9062e192b24SSimon Glass /* 9072e192b24SSimon Glass * b and e both start at the beginning of the input stream. 9082e192b24SSimon Glass * 9092e192b24SSimon Glass * e is incremented until we find the ending delimiter, or a NUL byte 9102e192b24SSimon Glass * is reached. Then, we take e - b to find the length of the token. 9112e192b24SSimon Glass */ 9122e192b24SSimon Glass b = e = *p; 9132e192b24SSimon Glass 9142e192b24SSimon Glass while (*e) { 9152e192b24SSimon Glass if ((delim == ' ' && isspace(*e)) || delim == *e) 9162e192b24SSimon Glass break; 9172e192b24SSimon Glass e++; 9182e192b24SSimon Glass } 9192e192b24SSimon Glass 9202e192b24SSimon Glass len = e - b; 9212e192b24SSimon Glass 9222e192b24SSimon Glass /* 9232e192b24SSimon Glass * Allocate memory to hold the string, and copy it in, converting 9242e192b24SSimon Glass * characters to lowercase if lower is != 0. 9252e192b24SSimon Glass */ 9262e192b24SSimon Glass t->val = malloc(len + 1); 9272e192b24SSimon Glass if (!t->val) 9282e192b24SSimon Glass return NULL; 9292e192b24SSimon Glass 9302e192b24SSimon Glass for (i = 0; i < len; i++, b++) { 9312e192b24SSimon Glass if (lower) 9322e192b24SSimon Glass t->val[i] = tolower(*b); 9332e192b24SSimon Glass else 9342e192b24SSimon Glass t->val[i] = *b; 9352e192b24SSimon Glass } 9362e192b24SSimon Glass 9372e192b24SSimon Glass t->val[len] = '\0'; 9382e192b24SSimon Glass 9392e192b24SSimon Glass /* 9402e192b24SSimon Glass * Update *p so the caller knows where to continue scanning. 9412e192b24SSimon Glass */ 9422e192b24SSimon Glass *p = e; 9432e192b24SSimon Glass 9442e192b24SSimon Glass t->type = T_STRING; 9452e192b24SSimon Glass 9462e192b24SSimon Glass return t->val; 9472e192b24SSimon Glass } 9482e192b24SSimon Glass 9492e192b24SSimon Glass /* 9502e192b24SSimon Glass * Populate a keyword token with a type and value. 9512e192b24SSimon Glass */ 9522e192b24SSimon Glass static void get_keyword(struct token *t) 9532e192b24SSimon Glass { 9542e192b24SSimon Glass int i; 9552e192b24SSimon Glass 9562e192b24SSimon Glass for (i = 0; keywords[i].val; i++) { 9572e192b24SSimon Glass if (!strcmp(t->val, keywords[i].val)) { 9582e192b24SSimon Glass t->type = keywords[i].type; 9592e192b24SSimon Glass break; 9602e192b24SSimon Glass } 9612e192b24SSimon Glass } 9622e192b24SSimon Glass } 9632e192b24SSimon Glass 9642e192b24SSimon Glass /* 9652e192b24SSimon Glass * Get the next token. We have to keep track of which state we're in to know 9662e192b24SSimon Glass * if we're looking to get a string literal or a keyword. 9672e192b24SSimon Glass * 9682e192b24SSimon Glass * *p is updated to point at the first character after the current token. 9692e192b24SSimon Glass */ 9702e192b24SSimon Glass static void get_token(char **p, struct token *t, enum lex_state state) 9712e192b24SSimon Glass { 9722e192b24SSimon Glass char *c = *p; 9732e192b24SSimon Glass 9742e192b24SSimon Glass t->type = T_INVALID; 9752e192b24SSimon Glass 9762e192b24SSimon Glass /* eat non EOL whitespace */ 9772e192b24SSimon Glass while (isblank(*c)) 9782e192b24SSimon Glass c++; 9792e192b24SSimon Glass 9802e192b24SSimon Glass /* 9812e192b24SSimon Glass * eat comments. note that string literals can't begin with #, but 9822e192b24SSimon Glass * can contain a # after their first character. 9832e192b24SSimon Glass */ 9842e192b24SSimon Glass if (*c == '#') { 9852e192b24SSimon Glass while (*c && *c != '\n') 9862e192b24SSimon Glass c++; 9872e192b24SSimon Glass } 9882e192b24SSimon Glass 9892e192b24SSimon Glass if (*c == '\n') { 9902e192b24SSimon Glass t->type = T_EOL; 9912e192b24SSimon Glass c++; 9922e192b24SSimon Glass } else if (*c == '\0') { 9932e192b24SSimon Glass t->type = T_EOF; 9942e192b24SSimon Glass c++; 9952e192b24SSimon Glass } else if (state == L_SLITERAL) { 9962e192b24SSimon Glass get_string(&c, t, '\n', 0); 9972e192b24SSimon Glass } else if (state == L_KEYWORD) { 9982e192b24SSimon Glass /* 9992e192b24SSimon Glass * when we expect a keyword, we first get the next string 10002e192b24SSimon Glass * token delimited by whitespace, and then check if it 10012e192b24SSimon Glass * matches a keyword in our keyword list. if it does, it's 10022e192b24SSimon Glass * converted to a keyword token of the appropriate type, and 10032e192b24SSimon Glass * if not, it remains a string token. 10042e192b24SSimon Glass */ 10052e192b24SSimon Glass get_string(&c, t, ' ', 1); 10062e192b24SSimon Glass get_keyword(t); 10072e192b24SSimon Glass } 10082e192b24SSimon Glass 10092e192b24SSimon Glass *p = c; 10102e192b24SSimon Glass } 10112e192b24SSimon Glass 10122e192b24SSimon Glass /* 10132e192b24SSimon Glass * Increment *c until we get to the end of the current line, or EOF. 10142e192b24SSimon Glass */ 10152e192b24SSimon Glass static void eol_or_eof(char **c) 10162e192b24SSimon Glass { 10172e192b24SSimon Glass while (**c && **c != '\n') 10182e192b24SSimon Glass (*c)++; 10192e192b24SSimon Glass } 10202e192b24SSimon Glass 10212e192b24SSimon Glass /* 10222e192b24SSimon Glass * All of these parse_* functions share some common behavior. 10232e192b24SSimon Glass * 10242e192b24SSimon Glass * They finish with *c pointing after the token they parse, and return 1 on 10252e192b24SSimon Glass * success, or < 0 on error. 10262e192b24SSimon Glass */ 10272e192b24SSimon Glass 10282e192b24SSimon Glass /* 10292e192b24SSimon Glass * Parse a string literal and store a pointer it at *dst. String literals 10302e192b24SSimon Glass * terminate at the end of the line. 10312e192b24SSimon Glass */ 10322e192b24SSimon Glass static int parse_sliteral(char **c, char **dst) 10332e192b24SSimon Glass { 10342e192b24SSimon Glass struct token t; 10352e192b24SSimon Glass char *s = *c; 10362e192b24SSimon Glass 10372e192b24SSimon Glass get_token(c, &t, L_SLITERAL); 10382e192b24SSimon Glass 10392e192b24SSimon Glass if (t.type != T_STRING) { 10402e192b24SSimon Glass printf("Expected string literal: %.*s\n", (int)(*c - s), s); 10412e192b24SSimon Glass return -EINVAL; 10422e192b24SSimon Glass } 10432e192b24SSimon Glass 10442e192b24SSimon Glass *dst = t.val; 10452e192b24SSimon Glass 10462e192b24SSimon Glass return 1; 10472e192b24SSimon Glass } 10482e192b24SSimon Glass 10492e192b24SSimon Glass /* 10502e192b24SSimon Glass * Parse a base 10 (unsigned) integer and store it at *dst. 10512e192b24SSimon Glass */ 10522e192b24SSimon Glass static int parse_integer(char **c, int *dst) 10532e192b24SSimon Glass { 10542e192b24SSimon Glass struct token t; 10552e192b24SSimon Glass char *s = *c; 10562e192b24SSimon Glass 10572e192b24SSimon Glass get_token(c, &t, L_SLITERAL); 10582e192b24SSimon Glass 10592e192b24SSimon Glass if (t.type != T_STRING) { 10602e192b24SSimon Glass printf("Expected string: %.*s\n", (int)(*c - s), s); 10612e192b24SSimon Glass return -EINVAL; 10622e192b24SSimon Glass } 10632e192b24SSimon Glass 10642e192b24SSimon Glass *dst = simple_strtol(t.val, NULL, 10); 10652e192b24SSimon Glass 10662e192b24SSimon Glass free(t.val); 10672e192b24SSimon Glass 10682e192b24SSimon Glass return 1; 10692e192b24SSimon Glass } 10702e192b24SSimon Glass 10712e192b24SSimon Glass static int parse_pxefile_top(cmd_tbl_t *cmdtp, char *p, unsigned long base, 10722e192b24SSimon Glass struct pxe_menu *cfg, int nest_level); 10732e192b24SSimon Glass 10742e192b24SSimon Glass /* 10752e192b24SSimon Glass * Parse an include statement, and retrieve and parse the file it mentions. 10762e192b24SSimon Glass * 10772e192b24SSimon Glass * base should point to a location where it's safe to store the file, and 10782e192b24SSimon Glass * nest_level should indicate how many nested includes have occurred. For this 10792e192b24SSimon Glass * include, nest_level has already been incremented and doesn't need to be 10802e192b24SSimon Glass * incremented here. 10812e192b24SSimon Glass */ 10822e192b24SSimon Glass static int handle_include(cmd_tbl_t *cmdtp, char **c, unsigned long base, 10832e192b24SSimon Glass struct pxe_menu *cfg, int nest_level) 10842e192b24SSimon Glass { 10852e192b24SSimon Glass char *include_path; 10862e192b24SSimon Glass char *s = *c; 10872e192b24SSimon Glass int err; 10882e192b24SSimon Glass char *buf; 10892e192b24SSimon Glass int ret; 10902e192b24SSimon Glass 10912e192b24SSimon Glass err = parse_sliteral(c, &include_path); 10922e192b24SSimon Glass 10932e192b24SSimon Glass if (err < 0) { 10942e192b24SSimon Glass printf("Expected include path: %.*s\n", 10952e192b24SSimon Glass (int)(*c - s), s); 10962e192b24SSimon Glass return err; 10972e192b24SSimon Glass } 10982e192b24SSimon Glass 10992e192b24SSimon Glass err = get_pxe_file(cmdtp, include_path, base); 11002e192b24SSimon Glass 11012e192b24SSimon Glass if (err < 0) { 11022e192b24SSimon Glass printf("Couldn't retrieve %s\n", include_path); 11032e192b24SSimon Glass return err; 11042e192b24SSimon Glass } 11052e192b24SSimon Glass 11062e192b24SSimon Glass buf = map_sysmem(base, 0); 11072e192b24SSimon Glass ret = parse_pxefile_top(cmdtp, buf, base, cfg, nest_level); 11082e192b24SSimon Glass unmap_sysmem(buf); 11092e192b24SSimon Glass 11102e192b24SSimon Glass return ret; 11112e192b24SSimon Glass } 11122e192b24SSimon Glass 11132e192b24SSimon Glass /* 11142e192b24SSimon Glass * Parse lines that begin with 'menu'. 11152e192b24SSimon Glass * 11162e192b24SSimon Glass * base and nest are provided to handle the 'menu include' case. 11172e192b24SSimon Glass * 11182e192b24SSimon Glass * base should point to a location where it's safe to store the included file. 11192e192b24SSimon Glass * 11202e192b24SSimon Glass * nest_level should be 1 when parsing the top level pxe file, 2 when parsing 11212e192b24SSimon Glass * a file it includes, 3 when parsing a file included by that file, and so on. 11222e192b24SSimon Glass */ 11232e192b24SSimon Glass static int parse_menu(cmd_tbl_t *cmdtp, char **c, struct pxe_menu *cfg, 11242e192b24SSimon Glass unsigned long base, int nest_level) 11252e192b24SSimon Glass { 11262e192b24SSimon Glass struct token t; 11272e192b24SSimon Glass char *s = *c; 11282e192b24SSimon Glass int err = 0; 11292e192b24SSimon Glass 11302e192b24SSimon Glass get_token(c, &t, L_KEYWORD); 11312e192b24SSimon Glass 11322e192b24SSimon Glass switch (t.type) { 11332e192b24SSimon Glass case T_TITLE: 11342e192b24SSimon Glass err = parse_sliteral(c, &cfg->title); 11352e192b24SSimon Glass 11362e192b24SSimon Glass break; 11372e192b24SSimon Glass 11382e192b24SSimon Glass case T_INCLUDE: 11392e192b24SSimon Glass err = handle_include(cmdtp, c, base, cfg, 11402e192b24SSimon Glass nest_level + 1); 11412e192b24SSimon Glass break; 11422e192b24SSimon Glass 11432e192b24SSimon Glass default: 11442e192b24SSimon Glass printf("Ignoring malformed menu command: %.*s\n", 11452e192b24SSimon Glass (int)(*c - s), s); 11462e192b24SSimon Glass } 11472e192b24SSimon Glass 11482e192b24SSimon Glass if (err < 0) 11492e192b24SSimon Glass return err; 11502e192b24SSimon Glass 11512e192b24SSimon Glass eol_or_eof(c); 11522e192b24SSimon Glass 11532e192b24SSimon Glass return 1; 11542e192b24SSimon Glass } 11552e192b24SSimon Glass 11562e192b24SSimon Glass /* 11572e192b24SSimon Glass * Handles parsing a 'menu line' when we're parsing a label. 11582e192b24SSimon Glass */ 11592e192b24SSimon Glass static int parse_label_menu(char **c, struct pxe_menu *cfg, 11602e192b24SSimon Glass struct pxe_label *label) 11612e192b24SSimon Glass { 11622e192b24SSimon Glass struct token t; 11632e192b24SSimon Glass char *s; 11642e192b24SSimon Glass 11652e192b24SSimon Glass s = *c; 11662e192b24SSimon Glass 11672e192b24SSimon Glass get_token(c, &t, L_KEYWORD); 11682e192b24SSimon Glass 11692e192b24SSimon Glass switch (t.type) { 11702e192b24SSimon Glass case T_DEFAULT: 11712e192b24SSimon Glass if (!cfg->default_label) 11722e192b24SSimon Glass cfg->default_label = strdup(label->name); 11732e192b24SSimon Glass 11742e192b24SSimon Glass if (!cfg->default_label) 11752e192b24SSimon Glass return -ENOMEM; 11762e192b24SSimon Glass 11772e192b24SSimon Glass break; 11782e192b24SSimon Glass case T_LABEL: 11792e192b24SSimon Glass parse_sliteral(c, &label->menu); 11802e192b24SSimon Glass break; 11812e192b24SSimon Glass default: 11822e192b24SSimon Glass printf("Ignoring malformed menu command: %.*s\n", 11832e192b24SSimon Glass (int)(*c - s), s); 11842e192b24SSimon Glass } 11852e192b24SSimon Glass 11862e192b24SSimon Glass eol_or_eof(c); 11872e192b24SSimon Glass 11882e192b24SSimon Glass return 0; 11892e192b24SSimon Glass } 11902e192b24SSimon Glass 11912e192b24SSimon Glass /* 11922e192b24SSimon Glass * Parses a label and adds it to the list of labels for a menu. 11932e192b24SSimon Glass * 11942e192b24SSimon Glass * A label ends when we either get to the end of a file, or 11952e192b24SSimon Glass * get some input we otherwise don't have a handler defined 11962e192b24SSimon Glass * for. 11972e192b24SSimon Glass * 11982e192b24SSimon Glass */ 11992e192b24SSimon Glass static int parse_label(char **c, struct pxe_menu *cfg) 12002e192b24SSimon Glass { 12012e192b24SSimon Glass struct token t; 12022e192b24SSimon Glass int len; 12032e192b24SSimon Glass char *s = *c; 12042e192b24SSimon Glass struct pxe_label *label; 12052e192b24SSimon Glass int err; 12062e192b24SSimon Glass 12072e192b24SSimon Glass label = label_create(); 12082e192b24SSimon Glass if (!label) 12092e192b24SSimon Glass return -ENOMEM; 12102e192b24SSimon Glass 12112e192b24SSimon Glass err = parse_sliteral(c, &label->name); 12122e192b24SSimon Glass if (err < 0) { 12132e192b24SSimon Glass printf("Expected label name: %.*s\n", (int)(*c - s), s); 12142e192b24SSimon Glass label_destroy(label); 12152e192b24SSimon Glass return -EINVAL; 12162e192b24SSimon Glass } 12172e192b24SSimon Glass 12182e192b24SSimon Glass list_add_tail(&label->list, &cfg->labels); 12192e192b24SSimon Glass 12202e192b24SSimon Glass while (1) { 12212e192b24SSimon Glass s = *c; 12222e192b24SSimon Glass get_token(c, &t, L_KEYWORD); 12232e192b24SSimon Glass 12242e192b24SSimon Glass err = 0; 12252e192b24SSimon Glass switch (t.type) { 12262e192b24SSimon Glass case T_MENU: 12272e192b24SSimon Glass err = parse_label_menu(c, cfg, label); 12282e192b24SSimon Glass break; 12292e192b24SSimon Glass 12302e192b24SSimon Glass case T_KERNEL: 12312e192b24SSimon Glass case T_LINUX: 12322e192b24SSimon Glass err = parse_sliteral(c, &label->kernel); 12332e192b24SSimon Glass break; 12342e192b24SSimon Glass 12352e192b24SSimon Glass case T_APPEND: 12362e192b24SSimon Glass err = parse_sliteral(c, &label->append); 12372e192b24SSimon Glass if (label->initrd) 12382e192b24SSimon Glass break; 12392e192b24SSimon Glass s = strstr(label->append, "initrd="); 12402e192b24SSimon Glass if (!s) 12412e192b24SSimon Glass break; 12422e192b24SSimon Glass s += 7; 12432e192b24SSimon Glass len = (int)(strchr(s, ' ') - s); 12442e192b24SSimon Glass label->initrd = malloc(len + 1); 12452e192b24SSimon Glass strncpy(label->initrd, s, len); 12462e192b24SSimon Glass label->initrd[len] = '\0'; 12472e192b24SSimon Glass 12482e192b24SSimon Glass break; 12492e192b24SSimon Glass 12502e192b24SSimon Glass case T_INITRD: 12512e192b24SSimon Glass if (!label->initrd) 12522e192b24SSimon Glass err = parse_sliteral(c, &label->initrd); 12532e192b24SSimon Glass break; 12542e192b24SSimon Glass 12552e192b24SSimon Glass case T_FDT: 12562e192b24SSimon Glass if (!label->fdt) 12572e192b24SSimon Glass err = parse_sliteral(c, &label->fdt); 12582e192b24SSimon Glass break; 12592e192b24SSimon Glass 12602e192b24SSimon Glass case T_FDTDIR: 12612e192b24SSimon Glass if (!label->fdtdir) 12622e192b24SSimon Glass err = parse_sliteral(c, &label->fdtdir); 12632e192b24SSimon Glass break; 12642e192b24SSimon Glass 12652e192b24SSimon Glass case T_LOCALBOOT: 12662e192b24SSimon Glass label->localboot = 1; 12672e192b24SSimon Glass err = parse_integer(c, &label->localboot_val); 12682e192b24SSimon Glass break; 12692e192b24SSimon Glass 12702e192b24SSimon Glass case T_IPAPPEND: 12712e192b24SSimon Glass err = parse_integer(c, &label->ipappend); 12722e192b24SSimon Glass break; 12732e192b24SSimon Glass 12742e192b24SSimon Glass case T_EOL: 12752e192b24SSimon Glass break; 12762e192b24SSimon Glass 12772e192b24SSimon Glass default: 12782e192b24SSimon Glass /* 12792e192b24SSimon Glass * put the token back! we don't want it - it's the end 12802e192b24SSimon Glass * of a label and whatever token this is, it's 12812e192b24SSimon Glass * something for the menu level context to handle. 12822e192b24SSimon Glass */ 12832e192b24SSimon Glass *c = s; 12842e192b24SSimon Glass return 1; 12852e192b24SSimon Glass } 12862e192b24SSimon Glass 12872e192b24SSimon Glass if (err < 0) 12882e192b24SSimon Glass return err; 12892e192b24SSimon Glass } 12902e192b24SSimon Glass } 12912e192b24SSimon Glass 12922e192b24SSimon Glass /* 12932e192b24SSimon Glass * This 16 comes from the limit pxelinux imposes on nested includes. 12942e192b24SSimon Glass * 12952e192b24SSimon Glass * There is no reason at all we couldn't do more, but some limit helps prevent 12962e192b24SSimon Glass * infinite (until crash occurs) recursion if a file tries to include itself. 12972e192b24SSimon Glass */ 12982e192b24SSimon Glass #define MAX_NEST_LEVEL 16 12992e192b24SSimon Glass 13002e192b24SSimon Glass /* 13012e192b24SSimon Glass * Entry point for parsing a menu file. nest_level indicates how many times 13022e192b24SSimon Glass * we've nested in includes. It will be 1 for the top level menu file. 13032e192b24SSimon Glass * 13042e192b24SSimon Glass * Returns 1 on success, < 0 on error. 13052e192b24SSimon Glass */ 13062e192b24SSimon Glass static int parse_pxefile_top(cmd_tbl_t *cmdtp, char *p, unsigned long base, 13072e192b24SSimon Glass struct pxe_menu *cfg, int nest_level) 13082e192b24SSimon Glass { 13092e192b24SSimon Glass struct token t; 13102e192b24SSimon Glass char *s, *b, *label_name; 13112e192b24SSimon Glass int err; 13122e192b24SSimon Glass 13132e192b24SSimon Glass b = p; 13142e192b24SSimon Glass 13152e192b24SSimon Glass if (nest_level > MAX_NEST_LEVEL) { 13162e192b24SSimon Glass printf("Maximum nesting (%d) exceeded\n", MAX_NEST_LEVEL); 13172e192b24SSimon Glass return -EMLINK; 13182e192b24SSimon Glass } 13192e192b24SSimon Glass 13202e192b24SSimon Glass while (1) { 13212e192b24SSimon Glass s = p; 13222e192b24SSimon Glass 13232e192b24SSimon Glass get_token(&p, &t, L_KEYWORD); 13242e192b24SSimon Glass 13252e192b24SSimon Glass err = 0; 13262e192b24SSimon Glass switch (t.type) { 13272e192b24SSimon Glass case T_MENU: 13282e192b24SSimon Glass cfg->prompt = 1; 13292e192b24SSimon Glass err = parse_menu(cmdtp, &p, cfg, 13302e192b24SSimon Glass base + ALIGN(strlen(b) + 1, 4), 13312e192b24SSimon Glass nest_level); 13322e192b24SSimon Glass break; 13332e192b24SSimon Glass 13342e192b24SSimon Glass case T_TIMEOUT: 13352e192b24SSimon Glass err = parse_integer(&p, &cfg->timeout); 13362e192b24SSimon Glass break; 13372e192b24SSimon Glass 13382e192b24SSimon Glass case T_LABEL: 13392e192b24SSimon Glass err = parse_label(&p, cfg); 13402e192b24SSimon Glass break; 13412e192b24SSimon Glass 13422e192b24SSimon Glass case T_DEFAULT: 13432e192b24SSimon Glass case T_ONTIMEOUT: 13442e192b24SSimon Glass err = parse_sliteral(&p, &label_name); 13452e192b24SSimon Glass 13462e192b24SSimon Glass if (label_name) { 13472e192b24SSimon Glass if (cfg->default_label) 13482e192b24SSimon Glass free(cfg->default_label); 13492e192b24SSimon Glass 13502e192b24SSimon Glass cfg->default_label = label_name; 13512e192b24SSimon Glass } 13522e192b24SSimon Glass 13532e192b24SSimon Glass break; 13542e192b24SSimon Glass 13552e192b24SSimon Glass case T_INCLUDE: 13562e192b24SSimon Glass err = handle_include(cmdtp, &p, 13572e192b24SSimon Glass base + ALIGN(strlen(b), 4), cfg, 13582e192b24SSimon Glass nest_level + 1); 13592e192b24SSimon Glass break; 13602e192b24SSimon Glass 13612e192b24SSimon Glass case T_PROMPT: 13622e192b24SSimon Glass eol_or_eof(&p); 13632e192b24SSimon Glass break; 13642e192b24SSimon Glass 13652e192b24SSimon Glass case T_EOL: 13662e192b24SSimon Glass break; 13672e192b24SSimon Glass 13682e192b24SSimon Glass case T_EOF: 13692e192b24SSimon Glass return 1; 13702e192b24SSimon Glass 13712e192b24SSimon Glass default: 13722e192b24SSimon Glass printf("Ignoring unknown command: %.*s\n", 13732e192b24SSimon Glass (int)(p - s), s); 13742e192b24SSimon Glass eol_or_eof(&p); 13752e192b24SSimon Glass } 13762e192b24SSimon Glass 13772e192b24SSimon Glass if (err < 0) 13782e192b24SSimon Glass return err; 13792e192b24SSimon Glass } 13802e192b24SSimon Glass } 13812e192b24SSimon Glass 13822e192b24SSimon Glass /* 13832e192b24SSimon Glass * Free the memory used by a pxe_menu and its labels. 13842e192b24SSimon Glass */ 13852e192b24SSimon Glass static void destroy_pxe_menu(struct pxe_menu *cfg) 13862e192b24SSimon Glass { 13872e192b24SSimon Glass struct list_head *pos, *n; 13882e192b24SSimon Glass struct pxe_label *label; 13892e192b24SSimon Glass 13902e192b24SSimon Glass if (cfg->title) 13912e192b24SSimon Glass free(cfg->title); 13922e192b24SSimon Glass 13932e192b24SSimon Glass if (cfg->default_label) 13942e192b24SSimon Glass free(cfg->default_label); 13952e192b24SSimon Glass 13962e192b24SSimon Glass list_for_each_safe(pos, n, &cfg->labels) { 13972e192b24SSimon Glass label = list_entry(pos, struct pxe_label, list); 13982e192b24SSimon Glass 13992e192b24SSimon Glass label_destroy(label); 14002e192b24SSimon Glass } 14012e192b24SSimon Glass 14022e192b24SSimon Glass free(cfg); 14032e192b24SSimon Glass } 14042e192b24SSimon Glass 14052e192b24SSimon Glass /* 14062e192b24SSimon Glass * Entry point for parsing a pxe file. This is only used for the top level 14072e192b24SSimon Glass * file. 14082e192b24SSimon Glass * 14092e192b24SSimon Glass * Returns NULL if there is an error, otherwise, returns a pointer to a 14102e192b24SSimon Glass * pxe_menu struct populated with the results of parsing the pxe file (and any 14112e192b24SSimon Glass * files it includes). The resulting pxe_menu struct can be free()'d by using 14122e192b24SSimon Glass * the destroy_pxe_menu() function. 14132e192b24SSimon Glass */ 14142e192b24SSimon Glass static struct pxe_menu *parse_pxefile(cmd_tbl_t *cmdtp, unsigned long menucfg) 14152e192b24SSimon Glass { 14162e192b24SSimon Glass struct pxe_menu *cfg; 14172e192b24SSimon Glass char *buf; 14182e192b24SSimon Glass int r; 14192e192b24SSimon Glass 14202e192b24SSimon Glass cfg = malloc(sizeof(struct pxe_menu)); 14212e192b24SSimon Glass 14222e192b24SSimon Glass if (!cfg) 14232e192b24SSimon Glass return NULL; 14242e192b24SSimon Glass 14252e192b24SSimon Glass memset(cfg, 0, sizeof(struct pxe_menu)); 14262e192b24SSimon Glass 14272e192b24SSimon Glass INIT_LIST_HEAD(&cfg->labels); 14282e192b24SSimon Glass 14292e192b24SSimon Glass buf = map_sysmem(menucfg, 0); 14302e192b24SSimon Glass r = parse_pxefile_top(cmdtp, buf, menucfg, cfg, 1); 14312e192b24SSimon Glass unmap_sysmem(buf); 14322e192b24SSimon Glass 14332e192b24SSimon Glass if (r < 0) { 14342e192b24SSimon Glass destroy_pxe_menu(cfg); 14352e192b24SSimon Glass return NULL; 14362e192b24SSimon Glass } 14372e192b24SSimon Glass 14382e192b24SSimon Glass return cfg; 14392e192b24SSimon Glass } 14402e192b24SSimon Glass 14412e192b24SSimon Glass /* 1442a187559eSBin Meng * Converts a pxe_menu struct into a menu struct for use with U-Boot's generic 14432e192b24SSimon Glass * menu code. 14442e192b24SSimon Glass */ 14452e192b24SSimon Glass static struct menu *pxe_menu_to_menu(struct pxe_menu *cfg) 14462e192b24SSimon Glass { 14472e192b24SSimon Glass struct pxe_label *label; 14482e192b24SSimon Glass struct list_head *pos; 14492e192b24SSimon Glass struct menu *m; 14502e192b24SSimon Glass int err; 14512e192b24SSimon Glass int i = 1; 14522e192b24SSimon Glass char *default_num = NULL; 14532e192b24SSimon Glass 14542e192b24SSimon Glass /* 14552e192b24SSimon Glass * Create a menu and add items for all the labels. 14562e192b24SSimon Glass */ 14572e192b24SSimon Glass m = menu_create(cfg->title, cfg->timeout, cfg->prompt, label_print, 14582e192b24SSimon Glass NULL, NULL); 14592e192b24SSimon Glass 14602e192b24SSimon Glass if (!m) 14612e192b24SSimon Glass return NULL; 14622e192b24SSimon Glass 14632e192b24SSimon Glass list_for_each(pos, &cfg->labels) { 14642e192b24SSimon Glass label = list_entry(pos, struct pxe_label, list); 14652e192b24SSimon Glass 14662e192b24SSimon Glass sprintf(label->num, "%d", i++); 14672e192b24SSimon Glass if (menu_item_add(m, label->num, label) != 1) { 14682e192b24SSimon Glass menu_destroy(m); 14692e192b24SSimon Glass return NULL; 14702e192b24SSimon Glass } 14712e192b24SSimon Glass if (cfg->default_label && 14722e192b24SSimon Glass (strcmp(label->name, cfg->default_label) == 0)) 14732e192b24SSimon Glass default_num = label->num; 14742e192b24SSimon Glass 14752e192b24SSimon Glass } 14762e192b24SSimon Glass 14772e192b24SSimon Glass /* 14782e192b24SSimon Glass * After we've created items for each label in the menu, set the 14792e192b24SSimon Glass * menu's default label if one was specified. 14802e192b24SSimon Glass */ 14812e192b24SSimon Glass if (default_num) { 14822e192b24SSimon Glass err = menu_default_set(m, default_num); 14832e192b24SSimon Glass if (err != 1) { 14842e192b24SSimon Glass if (err != -ENOENT) { 14852e192b24SSimon Glass menu_destroy(m); 14862e192b24SSimon Glass return NULL; 14872e192b24SSimon Glass } 14882e192b24SSimon Glass 14892e192b24SSimon Glass printf("Missing default: %s\n", cfg->default_label); 14902e192b24SSimon Glass } 14912e192b24SSimon Glass } 14922e192b24SSimon Glass 14932e192b24SSimon Glass return m; 14942e192b24SSimon Glass } 14952e192b24SSimon Glass 14962e192b24SSimon Glass /* 14972e192b24SSimon Glass * Try to boot any labels we have yet to attempt to boot. 14982e192b24SSimon Glass */ 14992e192b24SSimon Glass static void boot_unattempted_labels(cmd_tbl_t *cmdtp, struct pxe_menu *cfg) 15002e192b24SSimon Glass { 15012e192b24SSimon Glass struct list_head *pos; 15022e192b24SSimon Glass struct pxe_label *label; 15032e192b24SSimon Glass 15042e192b24SSimon Glass list_for_each(pos, &cfg->labels) { 15052e192b24SSimon Glass label = list_entry(pos, struct pxe_label, list); 15062e192b24SSimon Glass 15072e192b24SSimon Glass if (!label->attempted) 15082e192b24SSimon Glass label_boot(cmdtp, label); 15092e192b24SSimon Glass } 15102e192b24SSimon Glass } 15112e192b24SSimon Glass 15122e192b24SSimon Glass /* 15132e192b24SSimon Glass * Boot the system as prescribed by a pxe_menu. 15142e192b24SSimon Glass * 15152e192b24SSimon Glass * Use the menu system to either get the user's choice or the default, based 15162e192b24SSimon Glass * on config or user input. If there is no default or user's choice, 15172e192b24SSimon Glass * attempted to boot labels in the order they were given in pxe files. 15182e192b24SSimon Glass * If the default or user's choice fails to boot, attempt to boot other 15192e192b24SSimon Glass * labels in the order they were given in pxe files. 15202e192b24SSimon Glass * 15212e192b24SSimon Glass * If this function returns, there weren't any labels that successfully 15222e192b24SSimon Glass * booted, or the user interrupted the menu selection via ctrl+c. 15232e192b24SSimon Glass */ 15242e192b24SSimon Glass static void handle_pxe_menu(cmd_tbl_t *cmdtp, struct pxe_menu *cfg) 15252e192b24SSimon Glass { 15262e192b24SSimon Glass void *choice; 15272e192b24SSimon Glass struct menu *m; 15282e192b24SSimon Glass int err; 15292e192b24SSimon Glass 15302e192b24SSimon Glass m = pxe_menu_to_menu(cfg); 15312e192b24SSimon Glass if (!m) 15322e192b24SSimon Glass return; 15332e192b24SSimon Glass 15342e192b24SSimon Glass err = menu_get_choice(m, &choice); 15352e192b24SSimon Glass 15362e192b24SSimon Glass menu_destroy(m); 15372e192b24SSimon Glass 15382e192b24SSimon Glass /* 15392e192b24SSimon Glass * err == 1 means we got a choice back from menu_get_choice. 15402e192b24SSimon Glass * 15412e192b24SSimon Glass * err == -ENOENT if the menu was setup to select the default but no 15422e192b24SSimon Glass * default was set. in that case, we should continue trying to boot 15432e192b24SSimon Glass * labels that haven't been attempted yet. 15442e192b24SSimon Glass * 15452e192b24SSimon Glass * otherwise, the user interrupted or there was some other error and 15462e192b24SSimon Glass * we give up. 15472e192b24SSimon Glass */ 15482e192b24SSimon Glass 15492e192b24SSimon Glass if (err == 1) { 15502e192b24SSimon Glass err = label_boot(cmdtp, choice); 15512e192b24SSimon Glass if (!err) 15522e192b24SSimon Glass return; 15532e192b24SSimon Glass } else if (err != -ENOENT) { 15542e192b24SSimon Glass return; 15552e192b24SSimon Glass } 15562e192b24SSimon Glass 15572e192b24SSimon Glass boot_unattempted_labels(cmdtp, cfg); 15582e192b24SSimon Glass } 15592e192b24SSimon Glass 15602e192b24SSimon Glass #ifdef CONFIG_CMD_NET 15612e192b24SSimon Glass /* 15622e192b24SSimon Glass * Boots a system using a pxe file 15632e192b24SSimon Glass * 15642e192b24SSimon Glass * Returns 0 on success, 1 on error. 15652e192b24SSimon Glass */ 15662e192b24SSimon Glass static int 15672e192b24SSimon Glass do_pxe_boot(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) 15682e192b24SSimon Glass { 15692e192b24SSimon Glass unsigned long pxefile_addr_r; 15702e192b24SSimon Glass struct pxe_menu *cfg; 15712e192b24SSimon Glass char *pxefile_addr_str; 15722e192b24SSimon Glass 15732e192b24SSimon Glass do_getfile = do_get_tftp; 15742e192b24SSimon Glass 15752e192b24SSimon Glass if (argc == 1) { 15762e192b24SSimon Glass pxefile_addr_str = from_env("pxefile_addr_r"); 15772e192b24SSimon Glass if (!pxefile_addr_str) 15782e192b24SSimon Glass return 1; 15792e192b24SSimon Glass 15802e192b24SSimon Glass } else if (argc == 2) { 15812e192b24SSimon Glass pxefile_addr_str = argv[1]; 15822e192b24SSimon Glass } else { 15832e192b24SSimon Glass return CMD_RET_USAGE; 15842e192b24SSimon Glass } 15852e192b24SSimon Glass 15862e192b24SSimon Glass if (strict_strtoul(pxefile_addr_str, 16, &pxefile_addr_r) < 0) { 15872e192b24SSimon Glass printf("Invalid pxefile address: %s\n", pxefile_addr_str); 15882e192b24SSimon Glass return 1; 15892e192b24SSimon Glass } 15902e192b24SSimon Glass 15912e192b24SSimon Glass cfg = parse_pxefile(cmdtp, pxefile_addr_r); 15922e192b24SSimon Glass 15932e192b24SSimon Glass if (cfg == NULL) { 15942e192b24SSimon Glass printf("Error parsing config file\n"); 15952e192b24SSimon Glass return 1; 15962e192b24SSimon Glass } 15972e192b24SSimon Glass 15982e192b24SSimon Glass handle_pxe_menu(cmdtp, cfg); 15992e192b24SSimon Glass 16002e192b24SSimon Glass destroy_pxe_menu(cfg); 16012e192b24SSimon Glass 16022e192b24SSimon Glass copy_filename(net_boot_file_name, "", sizeof(net_boot_file_name)); 16032e192b24SSimon Glass 16042e192b24SSimon Glass return 0; 16052e192b24SSimon Glass } 16062e192b24SSimon Glass 16072e192b24SSimon Glass static cmd_tbl_t cmd_pxe_sub[] = { 16082e192b24SSimon Glass U_BOOT_CMD_MKENT(get, 1, 1, do_pxe_get, "", ""), 16092e192b24SSimon Glass U_BOOT_CMD_MKENT(boot, 2, 1, do_pxe_boot, "", "") 16102e192b24SSimon Glass }; 16112e192b24SSimon Glass 16122e192b24SSimon Glass static int do_pxe(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) 16132e192b24SSimon Glass { 16142e192b24SSimon Glass cmd_tbl_t *cp; 16152e192b24SSimon Glass 16162e192b24SSimon Glass if (argc < 2) 16172e192b24SSimon Glass return CMD_RET_USAGE; 16182e192b24SSimon Glass 16192e192b24SSimon Glass is_pxe = true; 16202e192b24SSimon Glass 16212e192b24SSimon Glass /* drop initial "pxe" arg */ 16222e192b24SSimon Glass argc--; 16232e192b24SSimon Glass argv++; 16242e192b24SSimon Glass 16252e192b24SSimon Glass cp = find_cmd_tbl(argv[0], cmd_pxe_sub, ARRAY_SIZE(cmd_pxe_sub)); 16262e192b24SSimon Glass 16272e192b24SSimon Glass if (cp) 16282e192b24SSimon Glass return cp->cmd(cmdtp, flag, argc, argv); 16292e192b24SSimon Glass 16302e192b24SSimon Glass return CMD_RET_USAGE; 16312e192b24SSimon Glass } 16322e192b24SSimon Glass 16332e192b24SSimon Glass U_BOOT_CMD( 16342e192b24SSimon Glass pxe, 3, 1, do_pxe, 16352e192b24SSimon Glass "commands to get and boot from pxe files", 16362e192b24SSimon Glass "get - try to retrieve a pxe file using tftp\npxe " 16372e192b24SSimon Glass "boot [pxefile_addr_r] - boot from the pxe file at pxefile_addr_r\n" 16382e192b24SSimon Glass ); 16392e192b24SSimon Glass #endif 16402e192b24SSimon Glass 16412e192b24SSimon Glass /* 16422e192b24SSimon Glass * Boots a system using a local disk syslinux/extlinux file 16432e192b24SSimon Glass * 16442e192b24SSimon Glass * Returns 0 on success, 1 on error. 16452e192b24SSimon Glass */ 16462e192b24SSimon Glass static int do_sysboot(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) 16472e192b24SSimon Glass { 16482e192b24SSimon Glass unsigned long pxefile_addr_r; 16492e192b24SSimon Glass struct pxe_menu *cfg; 16502e192b24SSimon Glass char *pxefile_addr_str; 16512e192b24SSimon Glass char *filename; 16522e192b24SSimon Glass int prompt = 0; 16532e192b24SSimon Glass 16542e192b24SSimon Glass is_pxe = false; 16552e192b24SSimon Glass 16562e192b24SSimon Glass if (argc > 1 && strstr(argv[1], "-p")) { 16572e192b24SSimon Glass prompt = 1; 16582e192b24SSimon Glass argc--; 16592e192b24SSimon Glass argv++; 16602e192b24SSimon Glass } 16612e192b24SSimon Glass 16622e192b24SSimon Glass if (argc < 4) 16632e192b24SSimon Glass return cmd_usage(cmdtp); 16642e192b24SSimon Glass 16652e192b24SSimon Glass if (argc < 5) { 16662e192b24SSimon Glass pxefile_addr_str = from_env("pxefile_addr_r"); 16672e192b24SSimon Glass if (!pxefile_addr_str) 16682e192b24SSimon Glass return 1; 16692e192b24SSimon Glass } else { 16702e192b24SSimon Glass pxefile_addr_str = argv[4]; 16712e192b24SSimon Glass } 16722e192b24SSimon Glass 16732e192b24SSimon Glass if (argc < 6) 16742e192b24SSimon Glass filename = getenv("bootfile"); 16752e192b24SSimon Glass else { 16762e192b24SSimon Glass filename = argv[5]; 1677*382bee57SSimon Glass env_set("bootfile", filename); 16782e192b24SSimon Glass } 16792e192b24SSimon Glass 16802e192b24SSimon Glass if (strstr(argv[3], "ext2")) 16812e192b24SSimon Glass do_getfile = do_get_ext2; 16822e192b24SSimon Glass else if (strstr(argv[3], "fat")) 16832e192b24SSimon Glass do_getfile = do_get_fat; 16842e192b24SSimon Glass else if (strstr(argv[3], "any")) 16852e192b24SSimon Glass do_getfile = do_get_any; 16862e192b24SSimon Glass else { 16872e192b24SSimon Glass printf("Invalid filesystem: %s\n", argv[3]); 16882e192b24SSimon Glass return 1; 16892e192b24SSimon Glass } 16902e192b24SSimon Glass fs_argv[1] = argv[1]; 16912e192b24SSimon Glass fs_argv[2] = argv[2]; 16922e192b24SSimon Glass 16932e192b24SSimon Glass if (strict_strtoul(pxefile_addr_str, 16, &pxefile_addr_r) < 0) { 16942e192b24SSimon Glass printf("Invalid pxefile address: %s\n", pxefile_addr_str); 16952e192b24SSimon Glass return 1; 16962e192b24SSimon Glass } 16972e192b24SSimon Glass 16982e192b24SSimon Glass if (get_pxe_file(cmdtp, filename, pxefile_addr_r) < 0) { 16992e192b24SSimon Glass printf("Error reading config file\n"); 17002e192b24SSimon Glass return 1; 17012e192b24SSimon Glass } 17022e192b24SSimon Glass 17032e192b24SSimon Glass cfg = parse_pxefile(cmdtp, pxefile_addr_r); 17042e192b24SSimon Glass 17052e192b24SSimon Glass if (cfg == NULL) { 17062e192b24SSimon Glass printf("Error parsing config file\n"); 17072e192b24SSimon Glass return 1; 17082e192b24SSimon Glass } 17092e192b24SSimon Glass 17102e192b24SSimon Glass if (prompt) 17112e192b24SSimon Glass cfg->prompt = 1; 17122e192b24SSimon Glass 17132e192b24SSimon Glass handle_pxe_menu(cmdtp, cfg); 17142e192b24SSimon Glass 17152e192b24SSimon Glass destroy_pxe_menu(cfg); 17162e192b24SSimon Glass 17172e192b24SSimon Glass return 0; 17182e192b24SSimon Glass } 17192e192b24SSimon Glass 17202e192b24SSimon Glass U_BOOT_CMD( 17212e192b24SSimon Glass sysboot, 7, 1, do_sysboot, 17222e192b24SSimon Glass "command to get and boot from syslinux files", 17232e192b24SSimon Glass "[-p] <interface> <dev[:part]> <ext2|fat|any> [addr] [filename]\n" 17242e192b24SSimon Glass " - load and parse syslinux menu file 'filename' from ext2, fat\n" 17252e192b24SSimon Glass " or any filesystem on 'dev' on 'interface' to address 'addr'" 17262e192b24SSimon Glass ); 1727