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