1*2e192b24SSimon Glass /* 2*2e192b24SSimon Glass * Copyright 2010-2011 Calxeda, Inc. 3*2e192b24SSimon Glass * Copyright (c) 2014, NVIDIA CORPORATION. All rights reserved. 4*2e192b24SSimon Glass * 5*2e192b24SSimon Glass * SPDX-License-Identifier: GPL-2.0+ 6*2e192b24SSimon Glass */ 7*2e192b24SSimon Glass 8*2e192b24SSimon Glass #include <common.h> 9*2e192b24SSimon Glass #include <command.h> 10*2e192b24SSimon Glass #include <malloc.h> 11*2e192b24SSimon Glass #include <mapmem.h> 12*2e192b24SSimon Glass #include <linux/string.h> 13*2e192b24SSimon Glass #include <linux/ctype.h> 14*2e192b24SSimon Glass #include <errno.h> 15*2e192b24SSimon Glass #include <linux/list.h> 16*2e192b24SSimon Glass #include <fs.h> 17*2e192b24SSimon Glass #include <asm/io.h> 18*2e192b24SSimon Glass 19*2e192b24SSimon Glass #include "menu.h" 20*2e192b24SSimon Glass #include "cli.h" 21*2e192b24SSimon Glass 22*2e192b24SSimon Glass #define MAX_TFTP_PATH_LEN 127 23*2e192b24SSimon Glass 24*2e192b24SSimon Glass const char *pxe_default_paths[] = { 25*2e192b24SSimon Glass #ifdef CONFIG_SYS_SOC 26*2e192b24SSimon Glass "default-" CONFIG_SYS_ARCH "-" CONFIG_SYS_SOC, 27*2e192b24SSimon Glass #endif 28*2e192b24SSimon Glass "default-" CONFIG_SYS_ARCH, 29*2e192b24SSimon Glass "default", 30*2e192b24SSimon Glass NULL 31*2e192b24SSimon Glass }; 32*2e192b24SSimon Glass 33*2e192b24SSimon Glass static bool is_pxe; 34*2e192b24SSimon Glass 35*2e192b24SSimon Glass /* 36*2e192b24SSimon Glass * Like getenv, but prints an error if envvar isn't defined in the 37*2e192b24SSimon Glass * environment. It always returns what getenv does, so it can be used in 38*2e192b24SSimon Glass * place of getenv without changing error handling otherwise. 39*2e192b24SSimon Glass */ 40*2e192b24SSimon Glass static char *from_env(const char *envvar) 41*2e192b24SSimon Glass { 42*2e192b24SSimon Glass char *ret; 43*2e192b24SSimon Glass 44*2e192b24SSimon Glass ret = getenv(envvar); 45*2e192b24SSimon Glass 46*2e192b24SSimon Glass if (!ret) 47*2e192b24SSimon Glass printf("missing environment variable: %s\n", envvar); 48*2e192b24SSimon Glass 49*2e192b24SSimon Glass return ret; 50*2e192b24SSimon Glass } 51*2e192b24SSimon Glass 52*2e192b24SSimon Glass #ifdef CONFIG_CMD_NET 53*2e192b24SSimon Glass /* 54*2e192b24SSimon Glass * Convert an ethaddr from the environment to the format used by pxelinux 55*2e192b24SSimon Glass * filenames based on mac addresses. Convert's ':' to '-', and adds "01-" to 56*2e192b24SSimon Glass * the beginning of the ethernet address to indicate a hardware type of 57*2e192b24SSimon Glass * Ethernet. Also converts uppercase hex characters into lowercase, to match 58*2e192b24SSimon Glass * pxelinux's behavior. 59*2e192b24SSimon Glass * 60*2e192b24SSimon Glass * Returns 1 for success, -ENOENT if 'ethaddr' is undefined in the 61*2e192b24SSimon Glass * environment, or some other value < 0 on error. 62*2e192b24SSimon Glass */ 63*2e192b24SSimon Glass static int format_mac_pxe(char *outbuf, size_t outbuf_len) 64*2e192b24SSimon Glass { 65*2e192b24SSimon Glass uchar ethaddr[6]; 66*2e192b24SSimon Glass 67*2e192b24SSimon Glass if (outbuf_len < 21) { 68*2e192b24SSimon Glass printf("outbuf is too small (%zd < 21)\n", outbuf_len); 69*2e192b24SSimon Glass 70*2e192b24SSimon Glass return -EINVAL; 71*2e192b24SSimon Glass } 72*2e192b24SSimon Glass 73*2e192b24SSimon Glass if (!eth_getenv_enetaddr_by_index("eth", eth_get_dev_index(), 74*2e192b24SSimon Glass ethaddr)) 75*2e192b24SSimon Glass return -ENOENT; 76*2e192b24SSimon Glass 77*2e192b24SSimon Glass sprintf(outbuf, "01-%02x-%02x-%02x-%02x-%02x-%02x", 78*2e192b24SSimon Glass ethaddr[0], ethaddr[1], ethaddr[2], 79*2e192b24SSimon Glass ethaddr[3], ethaddr[4], ethaddr[5]); 80*2e192b24SSimon Glass 81*2e192b24SSimon Glass return 1; 82*2e192b24SSimon Glass } 83*2e192b24SSimon Glass #endif 84*2e192b24SSimon Glass 85*2e192b24SSimon Glass /* 86*2e192b24SSimon Glass * Returns the directory the file specified in the bootfile env variable is 87*2e192b24SSimon Glass * in. If bootfile isn't defined in the environment, return NULL, which should 88*2e192b24SSimon Glass * be interpreted as "don't prepend anything to paths". 89*2e192b24SSimon Glass */ 90*2e192b24SSimon Glass static int get_bootfile_path(const char *file_path, char *bootfile_path, 91*2e192b24SSimon Glass size_t bootfile_path_size) 92*2e192b24SSimon Glass { 93*2e192b24SSimon Glass char *bootfile, *last_slash; 94*2e192b24SSimon Glass size_t path_len = 0; 95*2e192b24SSimon Glass 96*2e192b24SSimon Glass /* Only syslinux allows absolute paths */ 97*2e192b24SSimon Glass if (file_path[0] == '/' && !is_pxe) 98*2e192b24SSimon Glass goto ret; 99*2e192b24SSimon Glass 100*2e192b24SSimon Glass bootfile = from_env("bootfile"); 101*2e192b24SSimon Glass 102*2e192b24SSimon Glass if (!bootfile) 103*2e192b24SSimon Glass goto ret; 104*2e192b24SSimon Glass 105*2e192b24SSimon Glass last_slash = strrchr(bootfile, '/'); 106*2e192b24SSimon Glass 107*2e192b24SSimon Glass if (last_slash == NULL) 108*2e192b24SSimon Glass goto ret; 109*2e192b24SSimon Glass 110*2e192b24SSimon Glass path_len = (last_slash - bootfile) + 1; 111*2e192b24SSimon Glass 112*2e192b24SSimon Glass if (bootfile_path_size < path_len) { 113*2e192b24SSimon Glass printf("bootfile_path too small. (%zd < %zd)\n", 114*2e192b24SSimon Glass bootfile_path_size, path_len); 115*2e192b24SSimon Glass 116*2e192b24SSimon Glass return -1; 117*2e192b24SSimon Glass } 118*2e192b24SSimon Glass 119*2e192b24SSimon Glass strncpy(bootfile_path, bootfile, path_len); 120*2e192b24SSimon Glass 121*2e192b24SSimon Glass ret: 122*2e192b24SSimon Glass bootfile_path[path_len] = '\0'; 123*2e192b24SSimon Glass 124*2e192b24SSimon Glass return 1; 125*2e192b24SSimon Glass } 126*2e192b24SSimon Glass 127*2e192b24SSimon Glass static int (*do_getfile)(cmd_tbl_t *cmdtp, const char *file_path, char *file_addr); 128*2e192b24SSimon Glass 129*2e192b24SSimon Glass #ifdef CONFIG_CMD_NET 130*2e192b24SSimon Glass static int do_get_tftp(cmd_tbl_t *cmdtp, const char *file_path, char *file_addr) 131*2e192b24SSimon Glass { 132*2e192b24SSimon Glass char *tftp_argv[] = {"tftp", NULL, NULL, NULL}; 133*2e192b24SSimon Glass 134*2e192b24SSimon Glass tftp_argv[1] = file_addr; 135*2e192b24SSimon Glass tftp_argv[2] = (void *)file_path; 136*2e192b24SSimon Glass 137*2e192b24SSimon Glass if (do_tftpb(cmdtp, 0, 3, tftp_argv)) 138*2e192b24SSimon Glass return -ENOENT; 139*2e192b24SSimon Glass 140*2e192b24SSimon Glass return 1; 141*2e192b24SSimon Glass } 142*2e192b24SSimon Glass #endif 143*2e192b24SSimon Glass 144*2e192b24SSimon Glass static char *fs_argv[5]; 145*2e192b24SSimon Glass 146*2e192b24SSimon Glass static int do_get_ext2(cmd_tbl_t *cmdtp, const char *file_path, char *file_addr) 147*2e192b24SSimon Glass { 148*2e192b24SSimon Glass #ifdef CONFIG_CMD_EXT2 149*2e192b24SSimon Glass fs_argv[0] = "ext2load"; 150*2e192b24SSimon Glass fs_argv[3] = file_addr; 151*2e192b24SSimon Glass fs_argv[4] = (void *)file_path; 152*2e192b24SSimon Glass 153*2e192b24SSimon Glass if (!do_ext2load(cmdtp, 0, 5, fs_argv)) 154*2e192b24SSimon Glass return 1; 155*2e192b24SSimon Glass #endif 156*2e192b24SSimon Glass return -ENOENT; 157*2e192b24SSimon Glass } 158*2e192b24SSimon Glass 159*2e192b24SSimon Glass static int do_get_fat(cmd_tbl_t *cmdtp, const char *file_path, char *file_addr) 160*2e192b24SSimon Glass { 161*2e192b24SSimon Glass #ifdef CONFIG_CMD_FAT 162*2e192b24SSimon Glass fs_argv[0] = "fatload"; 163*2e192b24SSimon Glass fs_argv[3] = file_addr; 164*2e192b24SSimon Glass fs_argv[4] = (void *)file_path; 165*2e192b24SSimon Glass 166*2e192b24SSimon Glass if (!do_fat_fsload(cmdtp, 0, 5, fs_argv)) 167*2e192b24SSimon Glass return 1; 168*2e192b24SSimon Glass #endif 169*2e192b24SSimon Glass return -ENOENT; 170*2e192b24SSimon Glass } 171*2e192b24SSimon Glass 172*2e192b24SSimon Glass static int do_get_any(cmd_tbl_t *cmdtp, const char *file_path, char *file_addr) 173*2e192b24SSimon Glass { 174*2e192b24SSimon Glass #ifdef CONFIG_CMD_FS_GENERIC 175*2e192b24SSimon Glass fs_argv[0] = "load"; 176*2e192b24SSimon Glass fs_argv[3] = file_addr; 177*2e192b24SSimon Glass fs_argv[4] = (void *)file_path; 178*2e192b24SSimon Glass 179*2e192b24SSimon Glass if (!do_load(cmdtp, 0, 5, fs_argv, FS_TYPE_ANY)) 180*2e192b24SSimon Glass return 1; 181*2e192b24SSimon Glass #endif 182*2e192b24SSimon Glass return -ENOENT; 183*2e192b24SSimon Glass } 184*2e192b24SSimon Glass 185*2e192b24SSimon Glass /* 186*2e192b24SSimon Glass * As in pxelinux, paths to files referenced from files we retrieve are 187*2e192b24SSimon Glass * relative to the location of bootfile. get_relfile takes such a path and 188*2e192b24SSimon Glass * joins it with the bootfile path to get the full path to the target file. If 189*2e192b24SSimon Glass * the bootfile path is NULL, we use file_path as is. 190*2e192b24SSimon Glass * 191*2e192b24SSimon Glass * Returns 1 for success, or < 0 on error. 192*2e192b24SSimon Glass */ 193*2e192b24SSimon Glass static int get_relfile(cmd_tbl_t *cmdtp, const char *file_path, 194*2e192b24SSimon Glass unsigned long file_addr) 195*2e192b24SSimon Glass { 196*2e192b24SSimon Glass size_t path_len; 197*2e192b24SSimon Glass char relfile[MAX_TFTP_PATH_LEN+1]; 198*2e192b24SSimon Glass char addr_buf[18]; 199*2e192b24SSimon Glass int err; 200*2e192b24SSimon Glass 201*2e192b24SSimon Glass err = get_bootfile_path(file_path, relfile, sizeof(relfile)); 202*2e192b24SSimon Glass 203*2e192b24SSimon Glass if (err < 0) 204*2e192b24SSimon Glass return err; 205*2e192b24SSimon Glass 206*2e192b24SSimon Glass path_len = strlen(file_path); 207*2e192b24SSimon Glass path_len += strlen(relfile); 208*2e192b24SSimon Glass 209*2e192b24SSimon Glass if (path_len > MAX_TFTP_PATH_LEN) { 210*2e192b24SSimon Glass printf("Base path too long (%s%s)\n", 211*2e192b24SSimon Glass relfile, 212*2e192b24SSimon Glass file_path); 213*2e192b24SSimon Glass 214*2e192b24SSimon Glass return -ENAMETOOLONG; 215*2e192b24SSimon Glass } 216*2e192b24SSimon Glass 217*2e192b24SSimon Glass strcat(relfile, file_path); 218*2e192b24SSimon Glass 219*2e192b24SSimon Glass printf("Retrieving file: %s\n", relfile); 220*2e192b24SSimon Glass 221*2e192b24SSimon Glass sprintf(addr_buf, "%lx", file_addr); 222*2e192b24SSimon Glass 223*2e192b24SSimon Glass return do_getfile(cmdtp, relfile, addr_buf); 224*2e192b24SSimon Glass } 225*2e192b24SSimon Glass 226*2e192b24SSimon Glass /* 227*2e192b24SSimon Glass * Retrieve the file at 'file_path' to the locate given by 'file_addr'. If 228*2e192b24SSimon Glass * 'bootfile' was specified in the environment, the path to bootfile will be 229*2e192b24SSimon Glass * prepended to 'file_path' and the resulting path will be used. 230*2e192b24SSimon Glass * 231*2e192b24SSimon Glass * Returns 1 on success, or < 0 for error. 232*2e192b24SSimon Glass */ 233*2e192b24SSimon Glass static int get_pxe_file(cmd_tbl_t *cmdtp, const char *file_path, 234*2e192b24SSimon Glass unsigned long file_addr) 235*2e192b24SSimon Glass { 236*2e192b24SSimon Glass unsigned long config_file_size; 237*2e192b24SSimon Glass char *tftp_filesize; 238*2e192b24SSimon Glass int err; 239*2e192b24SSimon Glass char *buf; 240*2e192b24SSimon Glass 241*2e192b24SSimon Glass err = get_relfile(cmdtp, file_path, file_addr); 242*2e192b24SSimon Glass 243*2e192b24SSimon Glass if (err < 0) 244*2e192b24SSimon Glass return err; 245*2e192b24SSimon Glass 246*2e192b24SSimon Glass /* 247*2e192b24SSimon Glass * the file comes without a NUL byte at the end, so find out its size 248*2e192b24SSimon Glass * and add the NUL byte. 249*2e192b24SSimon Glass */ 250*2e192b24SSimon Glass tftp_filesize = from_env("filesize"); 251*2e192b24SSimon Glass 252*2e192b24SSimon Glass if (!tftp_filesize) 253*2e192b24SSimon Glass return -ENOENT; 254*2e192b24SSimon Glass 255*2e192b24SSimon Glass if (strict_strtoul(tftp_filesize, 16, &config_file_size) < 0) 256*2e192b24SSimon Glass return -EINVAL; 257*2e192b24SSimon Glass 258*2e192b24SSimon Glass buf = map_sysmem(file_addr + config_file_size, 1); 259*2e192b24SSimon Glass *buf = '\0'; 260*2e192b24SSimon Glass unmap_sysmem(buf); 261*2e192b24SSimon Glass 262*2e192b24SSimon Glass return 1; 263*2e192b24SSimon Glass } 264*2e192b24SSimon Glass 265*2e192b24SSimon Glass #ifdef CONFIG_CMD_NET 266*2e192b24SSimon Glass 267*2e192b24SSimon Glass #define PXELINUX_DIR "pxelinux.cfg/" 268*2e192b24SSimon Glass 269*2e192b24SSimon Glass /* 270*2e192b24SSimon Glass * Retrieves a file in the 'pxelinux.cfg' folder. Since this uses get_pxe_file 271*2e192b24SSimon Glass * to do the hard work, the location of the 'pxelinux.cfg' folder is generated 272*2e192b24SSimon Glass * from the bootfile path, as described above. 273*2e192b24SSimon Glass * 274*2e192b24SSimon Glass * Returns 1 on success or < 0 on error. 275*2e192b24SSimon Glass */ 276*2e192b24SSimon Glass static int get_pxelinux_path(cmd_tbl_t *cmdtp, const char *file, 277*2e192b24SSimon Glass unsigned long pxefile_addr_r) 278*2e192b24SSimon Glass { 279*2e192b24SSimon Glass size_t base_len = strlen(PXELINUX_DIR); 280*2e192b24SSimon Glass char path[MAX_TFTP_PATH_LEN+1]; 281*2e192b24SSimon Glass 282*2e192b24SSimon Glass if (base_len + strlen(file) > MAX_TFTP_PATH_LEN) { 283*2e192b24SSimon Glass printf("path (%s%s) too long, skipping\n", 284*2e192b24SSimon Glass PXELINUX_DIR, file); 285*2e192b24SSimon Glass return -ENAMETOOLONG; 286*2e192b24SSimon Glass } 287*2e192b24SSimon Glass 288*2e192b24SSimon Glass sprintf(path, PXELINUX_DIR "%s", file); 289*2e192b24SSimon Glass 290*2e192b24SSimon Glass return get_pxe_file(cmdtp, path, pxefile_addr_r); 291*2e192b24SSimon Glass } 292*2e192b24SSimon Glass 293*2e192b24SSimon Glass /* 294*2e192b24SSimon Glass * Looks for a pxe file with a name based on the pxeuuid environment variable. 295*2e192b24SSimon Glass * 296*2e192b24SSimon Glass * Returns 1 on success or < 0 on error. 297*2e192b24SSimon Glass */ 298*2e192b24SSimon Glass static int pxe_uuid_path(cmd_tbl_t *cmdtp, unsigned long pxefile_addr_r) 299*2e192b24SSimon Glass { 300*2e192b24SSimon Glass char *uuid_str; 301*2e192b24SSimon Glass 302*2e192b24SSimon Glass uuid_str = from_env("pxeuuid"); 303*2e192b24SSimon Glass 304*2e192b24SSimon Glass if (!uuid_str) 305*2e192b24SSimon Glass return -ENOENT; 306*2e192b24SSimon Glass 307*2e192b24SSimon Glass return get_pxelinux_path(cmdtp, uuid_str, pxefile_addr_r); 308*2e192b24SSimon Glass } 309*2e192b24SSimon Glass 310*2e192b24SSimon Glass /* 311*2e192b24SSimon Glass * Looks for a pxe file with a name based on the 'ethaddr' environment 312*2e192b24SSimon Glass * variable. 313*2e192b24SSimon Glass * 314*2e192b24SSimon Glass * Returns 1 on success or < 0 on error. 315*2e192b24SSimon Glass */ 316*2e192b24SSimon Glass static int pxe_mac_path(cmd_tbl_t *cmdtp, unsigned long pxefile_addr_r) 317*2e192b24SSimon Glass { 318*2e192b24SSimon Glass char mac_str[21]; 319*2e192b24SSimon Glass int err; 320*2e192b24SSimon Glass 321*2e192b24SSimon Glass err = format_mac_pxe(mac_str, sizeof(mac_str)); 322*2e192b24SSimon Glass 323*2e192b24SSimon Glass if (err < 0) 324*2e192b24SSimon Glass return err; 325*2e192b24SSimon Glass 326*2e192b24SSimon Glass return get_pxelinux_path(cmdtp, mac_str, pxefile_addr_r); 327*2e192b24SSimon Glass } 328*2e192b24SSimon Glass 329*2e192b24SSimon Glass /* 330*2e192b24SSimon Glass * Looks for pxe files with names based on our IP address. See pxelinux 331*2e192b24SSimon Glass * documentation for details on what these file names look like. We match 332*2e192b24SSimon Glass * that exactly. 333*2e192b24SSimon Glass * 334*2e192b24SSimon Glass * Returns 1 on success or < 0 on error. 335*2e192b24SSimon Glass */ 336*2e192b24SSimon Glass static int pxe_ipaddr_paths(cmd_tbl_t *cmdtp, unsigned long pxefile_addr_r) 337*2e192b24SSimon Glass { 338*2e192b24SSimon Glass char ip_addr[9]; 339*2e192b24SSimon Glass int mask_pos, err; 340*2e192b24SSimon Glass 341*2e192b24SSimon Glass sprintf(ip_addr, "%08X", ntohl(net_ip.s_addr)); 342*2e192b24SSimon Glass 343*2e192b24SSimon Glass for (mask_pos = 7; mask_pos >= 0; mask_pos--) { 344*2e192b24SSimon Glass err = get_pxelinux_path(cmdtp, ip_addr, pxefile_addr_r); 345*2e192b24SSimon Glass 346*2e192b24SSimon Glass if (err > 0) 347*2e192b24SSimon Glass return err; 348*2e192b24SSimon Glass 349*2e192b24SSimon Glass ip_addr[mask_pos] = '\0'; 350*2e192b24SSimon Glass } 351*2e192b24SSimon Glass 352*2e192b24SSimon Glass return -ENOENT; 353*2e192b24SSimon Glass } 354*2e192b24SSimon Glass 355*2e192b24SSimon Glass /* 356*2e192b24SSimon Glass * Entry point for the 'pxe get' command. 357*2e192b24SSimon Glass * This Follows pxelinux's rules to download a config file from a tftp server. 358*2e192b24SSimon Glass * The file is stored at the location given by the pxefile_addr_r environment 359*2e192b24SSimon Glass * variable, which must be set. 360*2e192b24SSimon Glass * 361*2e192b24SSimon Glass * UUID comes from pxeuuid env variable, if defined 362*2e192b24SSimon Glass * MAC addr comes from ethaddr env variable, if defined 363*2e192b24SSimon Glass * IP 364*2e192b24SSimon Glass * 365*2e192b24SSimon Glass * see http://syslinux.zytor.com/wiki/index.php/PXELINUX 366*2e192b24SSimon Glass * 367*2e192b24SSimon Glass * Returns 0 on success or 1 on error. 368*2e192b24SSimon Glass */ 369*2e192b24SSimon Glass static int 370*2e192b24SSimon Glass do_pxe_get(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) 371*2e192b24SSimon Glass { 372*2e192b24SSimon Glass char *pxefile_addr_str; 373*2e192b24SSimon Glass unsigned long pxefile_addr_r; 374*2e192b24SSimon Glass int err, i = 0; 375*2e192b24SSimon Glass 376*2e192b24SSimon Glass do_getfile = do_get_tftp; 377*2e192b24SSimon Glass 378*2e192b24SSimon Glass if (argc != 1) 379*2e192b24SSimon Glass return CMD_RET_USAGE; 380*2e192b24SSimon Glass 381*2e192b24SSimon Glass pxefile_addr_str = from_env("pxefile_addr_r"); 382*2e192b24SSimon Glass 383*2e192b24SSimon Glass if (!pxefile_addr_str) 384*2e192b24SSimon Glass return 1; 385*2e192b24SSimon Glass 386*2e192b24SSimon Glass err = strict_strtoul(pxefile_addr_str, 16, 387*2e192b24SSimon Glass (unsigned long *)&pxefile_addr_r); 388*2e192b24SSimon Glass if (err < 0) 389*2e192b24SSimon Glass return 1; 390*2e192b24SSimon Glass 391*2e192b24SSimon Glass /* 392*2e192b24SSimon Glass * Keep trying paths until we successfully get a file we're looking 393*2e192b24SSimon Glass * for. 394*2e192b24SSimon Glass */ 395*2e192b24SSimon Glass if (pxe_uuid_path(cmdtp, pxefile_addr_r) > 0 || 396*2e192b24SSimon Glass pxe_mac_path(cmdtp, pxefile_addr_r) > 0 || 397*2e192b24SSimon Glass pxe_ipaddr_paths(cmdtp, pxefile_addr_r) > 0) { 398*2e192b24SSimon Glass printf("Config file found\n"); 399*2e192b24SSimon Glass 400*2e192b24SSimon Glass return 0; 401*2e192b24SSimon Glass } 402*2e192b24SSimon Glass 403*2e192b24SSimon Glass while (pxe_default_paths[i]) { 404*2e192b24SSimon Glass if (get_pxelinux_path(cmdtp, pxe_default_paths[i], 405*2e192b24SSimon Glass pxefile_addr_r) > 0) { 406*2e192b24SSimon Glass printf("Config file found\n"); 407*2e192b24SSimon Glass return 0; 408*2e192b24SSimon Glass } 409*2e192b24SSimon Glass i++; 410*2e192b24SSimon Glass } 411*2e192b24SSimon Glass 412*2e192b24SSimon Glass printf("Config file not found\n"); 413*2e192b24SSimon Glass 414*2e192b24SSimon Glass return 1; 415*2e192b24SSimon Glass } 416*2e192b24SSimon Glass #endif 417*2e192b24SSimon Glass 418*2e192b24SSimon Glass /* 419*2e192b24SSimon Glass * Wrapper to make it easier to store the file at file_path in the location 420*2e192b24SSimon Glass * specified by envaddr_name. file_path will be joined to the bootfile path, 421*2e192b24SSimon Glass * if any is specified. 422*2e192b24SSimon Glass * 423*2e192b24SSimon Glass * Returns 1 on success or < 0 on error. 424*2e192b24SSimon Glass */ 425*2e192b24SSimon Glass static int get_relfile_envaddr(cmd_tbl_t *cmdtp, const char *file_path, const char *envaddr_name) 426*2e192b24SSimon Glass { 427*2e192b24SSimon Glass unsigned long file_addr; 428*2e192b24SSimon Glass char *envaddr; 429*2e192b24SSimon Glass 430*2e192b24SSimon Glass envaddr = from_env(envaddr_name); 431*2e192b24SSimon Glass 432*2e192b24SSimon Glass if (!envaddr) 433*2e192b24SSimon Glass return -ENOENT; 434*2e192b24SSimon Glass 435*2e192b24SSimon Glass if (strict_strtoul(envaddr, 16, &file_addr) < 0) 436*2e192b24SSimon Glass return -EINVAL; 437*2e192b24SSimon Glass 438*2e192b24SSimon Glass return get_relfile(cmdtp, file_path, file_addr); 439*2e192b24SSimon Glass } 440*2e192b24SSimon Glass 441*2e192b24SSimon Glass /* 442*2e192b24SSimon Glass * A note on the pxe file parser. 443*2e192b24SSimon Glass * 444*2e192b24SSimon Glass * We're parsing files that use syslinux grammar, which has a few quirks. 445*2e192b24SSimon Glass * String literals must be recognized based on context - there is no 446*2e192b24SSimon Glass * quoting or escaping support. There's also nothing to explicitly indicate 447*2e192b24SSimon Glass * when a label section completes. We deal with that by ending a label 448*2e192b24SSimon Glass * section whenever we see a line that doesn't include. 449*2e192b24SSimon Glass * 450*2e192b24SSimon Glass * As with the syslinux family, this same file format could be reused in the 451*2e192b24SSimon Glass * future for non pxe purposes. The only action it takes during parsing that 452*2e192b24SSimon Glass * would throw this off is handling of include files. It assumes we're using 453*2e192b24SSimon Glass * pxe, and does a tftp download of a file listed as an include file in the 454*2e192b24SSimon Glass * middle of the parsing operation. That could be handled by refactoring it to 455*2e192b24SSimon Glass * take a 'include file getter' function. 456*2e192b24SSimon Glass */ 457*2e192b24SSimon Glass 458*2e192b24SSimon Glass /* 459*2e192b24SSimon Glass * Describes a single label given in a pxe file. 460*2e192b24SSimon Glass * 461*2e192b24SSimon Glass * Create these with the 'label_create' function given below. 462*2e192b24SSimon Glass * 463*2e192b24SSimon Glass * name - the name of the menu as given on the 'menu label' line. 464*2e192b24SSimon Glass * kernel - the path to the kernel file to use for this label. 465*2e192b24SSimon Glass * append - kernel command line to use when booting this label 466*2e192b24SSimon Glass * initrd - path to the initrd to use for this label. 467*2e192b24SSimon Glass * attempted - 0 if we haven't tried to boot this label, 1 if we have. 468*2e192b24SSimon Glass * localboot - 1 if this label specified 'localboot', 0 otherwise. 469*2e192b24SSimon Glass * list - lets these form a list, which a pxe_menu struct will hold. 470*2e192b24SSimon Glass */ 471*2e192b24SSimon Glass struct pxe_label { 472*2e192b24SSimon Glass char num[4]; 473*2e192b24SSimon Glass char *name; 474*2e192b24SSimon Glass char *menu; 475*2e192b24SSimon Glass char *kernel; 476*2e192b24SSimon Glass char *append; 477*2e192b24SSimon Glass char *initrd; 478*2e192b24SSimon Glass char *fdt; 479*2e192b24SSimon Glass char *fdtdir; 480*2e192b24SSimon Glass int ipappend; 481*2e192b24SSimon Glass int attempted; 482*2e192b24SSimon Glass int localboot; 483*2e192b24SSimon Glass int localboot_val; 484*2e192b24SSimon Glass struct list_head list; 485*2e192b24SSimon Glass }; 486*2e192b24SSimon Glass 487*2e192b24SSimon Glass /* 488*2e192b24SSimon Glass * Describes a pxe menu as given via pxe files. 489*2e192b24SSimon Glass * 490*2e192b24SSimon Glass * title - the name of the menu as given by a 'menu title' line. 491*2e192b24SSimon Glass * default_label - the name of the default label, if any. 492*2e192b24SSimon Glass * timeout - time in tenths of a second to wait for a user key-press before 493*2e192b24SSimon Glass * booting the default label. 494*2e192b24SSimon Glass * prompt - if 0, don't prompt for a choice unless the timeout period is 495*2e192b24SSimon Glass * interrupted. If 1, always prompt for a choice regardless of 496*2e192b24SSimon Glass * timeout. 497*2e192b24SSimon Glass * labels - a list of labels defined for the menu. 498*2e192b24SSimon Glass */ 499*2e192b24SSimon Glass struct pxe_menu { 500*2e192b24SSimon Glass char *title; 501*2e192b24SSimon Glass char *default_label; 502*2e192b24SSimon Glass int timeout; 503*2e192b24SSimon Glass int prompt; 504*2e192b24SSimon Glass struct list_head labels; 505*2e192b24SSimon Glass }; 506*2e192b24SSimon Glass 507*2e192b24SSimon Glass /* 508*2e192b24SSimon Glass * Allocates memory for and initializes a pxe_label. This uses malloc, so the 509*2e192b24SSimon Glass * result must be free()'d to reclaim the memory. 510*2e192b24SSimon Glass * 511*2e192b24SSimon Glass * Returns NULL if malloc fails. 512*2e192b24SSimon Glass */ 513*2e192b24SSimon Glass static struct pxe_label *label_create(void) 514*2e192b24SSimon Glass { 515*2e192b24SSimon Glass struct pxe_label *label; 516*2e192b24SSimon Glass 517*2e192b24SSimon Glass label = malloc(sizeof(struct pxe_label)); 518*2e192b24SSimon Glass 519*2e192b24SSimon Glass if (!label) 520*2e192b24SSimon Glass return NULL; 521*2e192b24SSimon Glass 522*2e192b24SSimon Glass memset(label, 0, sizeof(struct pxe_label)); 523*2e192b24SSimon Glass 524*2e192b24SSimon Glass return label; 525*2e192b24SSimon Glass } 526*2e192b24SSimon Glass 527*2e192b24SSimon Glass /* 528*2e192b24SSimon Glass * Free the memory used by a pxe_label, including that used by its name, 529*2e192b24SSimon Glass * kernel, append and initrd members, if they're non NULL. 530*2e192b24SSimon Glass * 531*2e192b24SSimon Glass * So - be sure to only use dynamically allocated memory for the members of 532*2e192b24SSimon Glass * the pxe_label struct, unless you want to clean it up first. These are 533*2e192b24SSimon Glass * currently only created by the pxe file parsing code. 534*2e192b24SSimon Glass */ 535*2e192b24SSimon Glass static void label_destroy(struct pxe_label *label) 536*2e192b24SSimon Glass { 537*2e192b24SSimon Glass if (label->name) 538*2e192b24SSimon Glass free(label->name); 539*2e192b24SSimon Glass 540*2e192b24SSimon Glass if (label->kernel) 541*2e192b24SSimon Glass free(label->kernel); 542*2e192b24SSimon Glass 543*2e192b24SSimon Glass if (label->append) 544*2e192b24SSimon Glass free(label->append); 545*2e192b24SSimon Glass 546*2e192b24SSimon Glass if (label->initrd) 547*2e192b24SSimon Glass free(label->initrd); 548*2e192b24SSimon Glass 549*2e192b24SSimon Glass if (label->fdt) 550*2e192b24SSimon Glass free(label->fdt); 551*2e192b24SSimon Glass 552*2e192b24SSimon Glass if (label->fdtdir) 553*2e192b24SSimon Glass free(label->fdtdir); 554*2e192b24SSimon Glass 555*2e192b24SSimon Glass free(label); 556*2e192b24SSimon Glass } 557*2e192b24SSimon Glass 558*2e192b24SSimon Glass /* 559*2e192b24SSimon Glass * Print a label and its string members if they're defined. 560*2e192b24SSimon Glass * 561*2e192b24SSimon Glass * This is passed as a callback to the menu code for displaying each 562*2e192b24SSimon Glass * menu entry. 563*2e192b24SSimon Glass */ 564*2e192b24SSimon Glass static void label_print(void *data) 565*2e192b24SSimon Glass { 566*2e192b24SSimon Glass struct pxe_label *label = data; 567*2e192b24SSimon Glass const char *c = label->menu ? label->menu : label->name; 568*2e192b24SSimon Glass 569*2e192b24SSimon Glass printf("%s:\t%s\n", label->num, c); 570*2e192b24SSimon Glass } 571*2e192b24SSimon Glass 572*2e192b24SSimon Glass /* 573*2e192b24SSimon Glass * Boot a label that specified 'localboot'. This requires that the 'localcmd' 574*2e192b24SSimon Glass * environment variable is defined. Its contents will be executed as U-boot 575*2e192b24SSimon Glass * command. If the label specified an 'append' line, its contents will be 576*2e192b24SSimon Glass * used to overwrite the contents of the 'bootargs' environment variable prior 577*2e192b24SSimon Glass * to running 'localcmd'. 578*2e192b24SSimon Glass * 579*2e192b24SSimon Glass * Returns 1 on success or < 0 on error. 580*2e192b24SSimon Glass */ 581*2e192b24SSimon Glass static int label_localboot(struct pxe_label *label) 582*2e192b24SSimon Glass { 583*2e192b24SSimon Glass char *localcmd; 584*2e192b24SSimon Glass 585*2e192b24SSimon Glass localcmd = from_env("localcmd"); 586*2e192b24SSimon Glass 587*2e192b24SSimon Glass if (!localcmd) 588*2e192b24SSimon Glass return -ENOENT; 589*2e192b24SSimon Glass 590*2e192b24SSimon Glass if (label->append) { 591*2e192b24SSimon Glass char bootargs[CONFIG_SYS_CBSIZE]; 592*2e192b24SSimon Glass 593*2e192b24SSimon Glass cli_simple_process_macros(label->append, bootargs); 594*2e192b24SSimon Glass setenv("bootargs", bootargs); 595*2e192b24SSimon Glass } 596*2e192b24SSimon Glass 597*2e192b24SSimon Glass debug("running: %s\n", localcmd); 598*2e192b24SSimon Glass 599*2e192b24SSimon Glass return run_command_list(localcmd, strlen(localcmd), 0); 600*2e192b24SSimon Glass } 601*2e192b24SSimon Glass 602*2e192b24SSimon Glass /* 603*2e192b24SSimon Glass * Boot according to the contents of a pxe_label. 604*2e192b24SSimon Glass * 605*2e192b24SSimon Glass * If we can't boot for any reason, we return. A successful boot never 606*2e192b24SSimon Glass * returns. 607*2e192b24SSimon Glass * 608*2e192b24SSimon Glass * The kernel will be stored in the location given by the 'kernel_addr_r' 609*2e192b24SSimon Glass * environment variable. 610*2e192b24SSimon Glass * 611*2e192b24SSimon Glass * If the label specifies an initrd file, it will be stored in the location 612*2e192b24SSimon Glass * given by the 'ramdisk_addr_r' environment variable. 613*2e192b24SSimon Glass * 614*2e192b24SSimon Glass * If the label specifies an 'append' line, its contents will overwrite that 615*2e192b24SSimon Glass * of the 'bootargs' environment variable. 616*2e192b24SSimon Glass */ 617*2e192b24SSimon Glass static int label_boot(cmd_tbl_t *cmdtp, struct pxe_label *label) 618*2e192b24SSimon Glass { 619*2e192b24SSimon Glass char *bootm_argv[] = { "bootm", NULL, NULL, NULL, NULL }; 620*2e192b24SSimon Glass char initrd_str[22]; 621*2e192b24SSimon Glass char mac_str[29] = ""; 622*2e192b24SSimon Glass char ip_str[68] = ""; 623*2e192b24SSimon Glass int bootm_argc = 3; 624*2e192b24SSimon Glass int len = 0; 625*2e192b24SSimon Glass ulong kernel_addr; 626*2e192b24SSimon Glass void *buf; 627*2e192b24SSimon Glass 628*2e192b24SSimon Glass label_print(label); 629*2e192b24SSimon Glass 630*2e192b24SSimon Glass label->attempted = 1; 631*2e192b24SSimon Glass 632*2e192b24SSimon Glass if (label->localboot) { 633*2e192b24SSimon Glass if (label->localboot_val >= 0) 634*2e192b24SSimon Glass label_localboot(label); 635*2e192b24SSimon Glass return 0; 636*2e192b24SSimon Glass } 637*2e192b24SSimon Glass 638*2e192b24SSimon Glass if (label->kernel == NULL) { 639*2e192b24SSimon Glass printf("No kernel given, skipping %s\n", 640*2e192b24SSimon Glass label->name); 641*2e192b24SSimon Glass return 1; 642*2e192b24SSimon Glass } 643*2e192b24SSimon Glass 644*2e192b24SSimon Glass if (label->initrd) { 645*2e192b24SSimon Glass if (get_relfile_envaddr(cmdtp, label->initrd, "ramdisk_addr_r") < 0) { 646*2e192b24SSimon Glass printf("Skipping %s for failure retrieving initrd\n", 647*2e192b24SSimon Glass label->name); 648*2e192b24SSimon Glass return 1; 649*2e192b24SSimon Glass } 650*2e192b24SSimon Glass 651*2e192b24SSimon Glass bootm_argv[2] = initrd_str; 652*2e192b24SSimon Glass strcpy(bootm_argv[2], getenv("ramdisk_addr_r")); 653*2e192b24SSimon Glass strcat(bootm_argv[2], ":"); 654*2e192b24SSimon Glass strcat(bootm_argv[2], getenv("filesize")); 655*2e192b24SSimon Glass } else { 656*2e192b24SSimon Glass bootm_argv[2] = "-"; 657*2e192b24SSimon Glass } 658*2e192b24SSimon Glass 659*2e192b24SSimon Glass if (get_relfile_envaddr(cmdtp, label->kernel, "kernel_addr_r") < 0) { 660*2e192b24SSimon Glass printf("Skipping %s for failure retrieving kernel\n", 661*2e192b24SSimon Glass label->name); 662*2e192b24SSimon Glass return 1; 663*2e192b24SSimon Glass } 664*2e192b24SSimon Glass 665*2e192b24SSimon Glass if (label->ipappend & 0x1) { 666*2e192b24SSimon Glass sprintf(ip_str, " ip=%s:%s:%s:%s", 667*2e192b24SSimon Glass getenv("ipaddr"), getenv("serverip"), 668*2e192b24SSimon Glass getenv("gatewayip"), getenv("netmask")); 669*2e192b24SSimon Glass } 670*2e192b24SSimon Glass 671*2e192b24SSimon Glass #ifdef CONFIG_CMD_NET 672*2e192b24SSimon Glass if (label->ipappend & 0x2) { 673*2e192b24SSimon Glass int err; 674*2e192b24SSimon Glass strcpy(mac_str, " BOOTIF="); 675*2e192b24SSimon Glass err = format_mac_pxe(mac_str + 8, sizeof(mac_str) - 8); 676*2e192b24SSimon Glass if (err < 0) 677*2e192b24SSimon Glass mac_str[0] = '\0'; 678*2e192b24SSimon Glass } 679*2e192b24SSimon Glass #endif 680*2e192b24SSimon Glass 681*2e192b24SSimon Glass if ((label->ipappend & 0x3) || label->append) { 682*2e192b24SSimon Glass char bootargs[CONFIG_SYS_CBSIZE] = ""; 683*2e192b24SSimon Glass char finalbootargs[CONFIG_SYS_CBSIZE]; 684*2e192b24SSimon Glass 685*2e192b24SSimon Glass if (strlen(label->append ?: "") + 686*2e192b24SSimon Glass strlen(ip_str) + strlen(mac_str) + 1 > sizeof(bootargs)) { 687*2e192b24SSimon Glass printf("bootarg overflow %zd+%zd+%zd+1 > %zd\n", 688*2e192b24SSimon Glass strlen(label->append ?: ""), 689*2e192b24SSimon Glass strlen(ip_str), strlen(mac_str), 690*2e192b24SSimon Glass sizeof(bootargs)); 691*2e192b24SSimon Glass return 1; 692*2e192b24SSimon Glass } 693*2e192b24SSimon Glass 694*2e192b24SSimon Glass if (label->append) 695*2e192b24SSimon Glass strcpy(bootargs, label->append); 696*2e192b24SSimon Glass strcat(bootargs, ip_str); 697*2e192b24SSimon Glass strcat(bootargs, mac_str); 698*2e192b24SSimon Glass 699*2e192b24SSimon Glass cli_simple_process_macros(bootargs, finalbootargs); 700*2e192b24SSimon Glass setenv("bootargs", finalbootargs); 701*2e192b24SSimon Glass printf("append: %s\n", finalbootargs); 702*2e192b24SSimon Glass } 703*2e192b24SSimon Glass 704*2e192b24SSimon Glass bootm_argv[1] = getenv("kernel_addr_r"); 705*2e192b24SSimon Glass 706*2e192b24SSimon Glass /* 707*2e192b24SSimon Glass * fdt usage is optional: 708*2e192b24SSimon Glass * It handles the following scenarios. All scenarios are exclusive 709*2e192b24SSimon Glass * 710*2e192b24SSimon Glass * Scenario 1: If fdt_addr_r specified and "fdt" label is defined in 711*2e192b24SSimon Glass * pxe file, retrieve fdt blob from server. Pass fdt_addr_r to bootm, 712*2e192b24SSimon Glass * and adjust argc appropriately. 713*2e192b24SSimon Glass * 714*2e192b24SSimon Glass * Scenario 2: If there is an fdt_addr specified, pass it along to 715*2e192b24SSimon Glass * bootm, and adjust argc appropriately. 716*2e192b24SSimon Glass * 717*2e192b24SSimon Glass * Scenario 3: fdt blob is not available. 718*2e192b24SSimon Glass */ 719*2e192b24SSimon Glass bootm_argv[3] = getenv("fdt_addr_r"); 720*2e192b24SSimon Glass 721*2e192b24SSimon Glass /* if fdt label is defined then get fdt from server */ 722*2e192b24SSimon Glass if (bootm_argv[3]) { 723*2e192b24SSimon Glass char *fdtfile = NULL; 724*2e192b24SSimon Glass char *fdtfilefree = NULL; 725*2e192b24SSimon Glass 726*2e192b24SSimon Glass if (label->fdt) { 727*2e192b24SSimon Glass fdtfile = label->fdt; 728*2e192b24SSimon Glass } else if (label->fdtdir) { 729*2e192b24SSimon Glass char *f1, *f2, *f3, *f4, *slash; 730*2e192b24SSimon Glass 731*2e192b24SSimon Glass f1 = getenv("fdtfile"); 732*2e192b24SSimon Glass if (f1) { 733*2e192b24SSimon Glass f2 = ""; 734*2e192b24SSimon Glass f3 = ""; 735*2e192b24SSimon Glass f4 = ""; 736*2e192b24SSimon Glass } else { 737*2e192b24SSimon Glass /* 738*2e192b24SSimon Glass * For complex cases where this code doesn't 739*2e192b24SSimon Glass * generate the correct filename, the board 740*2e192b24SSimon Glass * code should set $fdtfile during early boot, 741*2e192b24SSimon Glass * or the boot scripts should set $fdtfile 742*2e192b24SSimon Glass * before invoking "pxe" or "sysboot". 743*2e192b24SSimon Glass */ 744*2e192b24SSimon Glass f1 = getenv("soc"); 745*2e192b24SSimon Glass f2 = "-"; 746*2e192b24SSimon Glass f3 = getenv("board"); 747*2e192b24SSimon Glass f4 = ".dtb"; 748*2e192b24SSimon Glass } 749*2e192b24SSimon Glass 750*2e192b24SSimon Glass len = strlen(label->fdtdir); 751*2e192b24SSimon Glass if (!len) 752*2e192b24SSimon Glass slash = "./"; 753*2e192b24SSimon Glass else if (label->fdtdir[len - 1] != '/') 754*2e192b24SSimon Glass slash = "/"; 755*2e192b24SSimon Glass else 756*2e192b24SSimon Glass slash = ""; 757*2e192b24SSimon Glass 758*2e192b24SSimon Glass len = strlen(label->fdtdir) + strlen(slash) + 759*2e192b24SSimon Glass strlen(f1) + strlen(f2) + strlen(f3) + 760*2e192b24SSimon Glass strlen(f4) + 1; 761*2e192b24SSimon Glass fdtfilefree = malloc(len); 762*2e192b24SSimon Glass if (!fdtfilefree) { 763*2e192b24SSimon Glass printf("malloc fail (FDT filename)\n"); 764*2e192b24SSimon Glass return 1; 765*2e192b24SSimon Glass } 766*2e192b24SSimon Glass 767*2e192b24SSimon Glass snprintf(fdtfilefree, len, "%s%s%s%s%s%s", 768*2e192b24SSimon Glass label->fdtdir, slash, f1, f2, f3, f4); 769*2e192b24SSimon Glass fdtfile = fdtfilefree; 770*2e192b24SSimon Glass } 771*2e192b24SSimon Glass 772*2e192b24SSimon Glass if (fdtfile) { 773*2e192b24SSimon Glass int err = get_relfile_envaddr(cmdtp, fdtfile, "fdt_addr_r"); 774*2e192b24SSimon Glass free(fdtfilefree); 775*2e192b24SSimon Glass if (err < 0) { 776*2e192b24SSimon Glass printf("Skipping %s for failure retrieving fdt\n", 777*2e192b24SSimon Glass label->name); 778*2e192b24SSimon Glass return 1; 779*2e192b24SSimon Glass } 780*2e192b24SSimon Glass } else { 781*2e192b24SSimon Glass bootm_argv[3] = NULL; 782*2e192b24SSimon Glass } 783*2e192b24SSimon Glass } 784*2e192b24SSimon Glass 785*2e192b24SSimon Glass if (!bootm_argv[3]) 786*2e192b24SSimon Glass bootm_argv[3] = getenv("fdt_addr"); 787*2e192b24SSimon Glass 788*2e192b24SSimon Glass if (bootm_argv[3]) 789*2e192b24SSimon Glass bootm_argc = 4; 790*2e192b24SSimon Glass 791*2e192b24SSimon Glass kernel_addr = genimg_get_kernel_addr(bootm_argv[1]); 792*2e192b24SSimon Glass buf = map_sysmem(kernel_addr, 0); 793*2e192b24SSimon Glass /* Try bootm for legacy and FIT format image */ 794*2e192b24SSimon Glass if (genimg_get_format(buf) != IMAGE_FORMAT_INVALID) 795*2e192b24SSimon Glass do_bootm(cmdtp, 0, bootm_argc, bootm_argv); 796*2e192b24SSimon Glass #ifdef CONFIG_CMD_BOOTI 797*2e192b24SSimon Glass /* Try booting an AArch64 Linux kernel image */ 798*2e192b24SSimon Glass else 799*2e192b24SSimon Glass do_booti(cmdtp, 0, bootm_argc, bootm_argv); 800*2e192b24SSimon Glass #elif defined(CONFIG_CMD_BOOTZ) 801*2e192b24SSimon Glass /* Try booting a Image */ 802*2e192b24SSimon Glass else 803*2e192b24SSimon Glass do_bootz(cmdtp, 0, bootm_argc, bootm_argv); 804*2e192b24SSimon Glass #endif 805*2e192b24SSimon Glass unmap_sysmem(buf); 806*2e192b24SSimon Glass return 1; 807*2e192b24SSimon Glass } 808*2e192b24SSimon Glass 809*2e192b24SSimon Glass /* 810*2e192b24SSimon Glass * Tokens for the pxe file parser. 811*2e192b24SSimon Glass */ 812*2e192b24SSimon Glass enum token_type { 813*2e192b24SSimon Glass T_EOL, 814*2e192b24SSimon Glass T_STRING, 815*2e192b24SSimon Glass T_EOF, 816*2e192b24SSimon Glass T_MENU, 817*2e192b24SSimon Glass T_TITLE, 818*2e192b24SSimon Glass T_TIMEOUT, 819*2e192b24SSimon Glass T_LABEL, 820*2e192b24SSimon Glass T_KERNEL, 821*2e192b24SSimon Glass T_LINUX, 822*2e192b24SSimon Glass T_APPEND, 823*2e192b24SSimon Glass T_INITRD, 824*2e192b24SSimon Glass T_LOCALBOOT, 825*2e192b24SSimon Glass T_DEFAULT, 826*2e192b24SSimon Glass T_PROMPT, 827*2e192b24SSimon Glass T_INCLUDE, 828*2e192b24SSimon Glass T_FDT, 829*2e192b24SSimon Glass T_FDTDIR, 830*2e192b24SSimon Glass T_ONTIMEOUT, 831*2e192b24SSimon Glass T_IPAPPEND, 832*2e192b24SSimon Glass T_INVALID 833*2e192b24SSimon Glass }; 834*2e192b24SSimon Glass 835*2e192b24SSimon Glass /* 836*2e192b24SSimon Glass * A token - given by a value and a type. 837*2e192b24SSimon Glass */ 838*2e192b24SSimon Glass struct token { 839*2e192b24SSimon Glass char *val; 840*2e192b24SSimon Glass enum token_type type; 841*2e192b24SSimon Glass }; 842*2e192b24SSimon Glass 843*2e192b24SSimon Glass /* 844*2e192b24SSimon Glass * Keywords recognized. 845*2e192b24SSimon Glass */ 846*2e192b24SSimon Glass static const struct token keywords[] = { 847*2e192b24SSimon Glass {"menu", T_MENU}, 848*2e192b24SSimon Glass {"title", T_TITLE}, 849*2e192b24SSimon Glass {"timeout", T_TIMEOUT}, 850*2e192b24SSimon Glass {"default", T_DEFAULT}, 851*2e192b24SSimon Glass {"prompt", T_PROMPT}, 852*2e192b24SSimon Glass {"label", T_LABEL}, 853*2e192b24SSimon Glass {"kernel", T_KERNEL}, 854*2e192b24SSimon Glass {"linux", T_LINUX}, 855*2e192b24SSimon Glass {"localboot", T_LOCALBOOT}, 856*2e192b24SSimon Glass {"append", T_APPEND}, 857*2e192b24SSimon Glass {"initrd", T_INITRD}, 858*2e192b24SSimon Glass {"include", T_INCLUDE}, 859*2e192b24SSimon Glass {"devicetree", T_FDT}, 860*2e192b24SSimon Glass {"fdt", T_FDT}, 861*2e192b24SSimon Glass {"devicetreedir", T_FDTDIR}, 862*2e192b24SSimon Glass {"fdtdir", T_FDTDIR}, 863*2e192b24SSimon Glass {"ontimeout", T_ONTIMEOUT,}, 864*2e192b24SSimon Glass {"ipappend", T_IPAPPEND,}, 865*2e192b24SSimon Glass {NULL, T_INVALID} 866*2e192b24SSimon Glass }; 867*2e192b24SSimon Glass 868*2e192b24SSimon Glass /* 869*2e192b24SSimon Glass * Since pxe(linux) files don't have a token to identify the start of a 870*2e192b24SSimon Glass * literal, we have to keep track of when we're in a state where a literal is 871*2e192b24SSimon Glass * expected vs when we're in a state a keyword is expected. 872*2e192b24SSimon Glass */ 873*2e192b24SSimon Glass enum lex_state { 874*2e192b24SSimon Glass L_NORMAL = 0, 875*2e192b24SSimon Glass L_KEYWORD, 876*2e192b24SSimon Glass L_SLITERAL 877*2e192b24SSimon Glass }; 878*2e192b24SSimon Glass 879*2e192b24SSimon Glass /* 880*2e192b24SSimon Glass * get_string retrieves a string from *p and stores it as a token in 881*2e192b24SSimon Glass * *t. 882*2e192b24SSimon Glass * 883*2e192b24SSimon Glass * get_string used for scanning both string literals and keywords. 884*2e192b24SSimon Glass * 885*2e192b24SSimon Glass * Characters from *p are copied into t-val until a character equal to 886*2e192b24SSimon Glass * delim is found, or a NUL byte is reached. If delim has the special value of 887*2e192b24SSimon Glass * ' ', any whitespace character will be used as a delimiter. 888*2e192b24SSimon Glass * 889*2e192b24SSimon Glass * If lower is unequal to 0, uppercase characters will be converted to 890*2e192b24SSimon Glass * lowercase in the result. This is useful to make keywords case 891*2e192b24SSimon Glass * insensitive. 892*2e192b24SSimon Glass * 893*2e192b24SSimon Glass * The location of *p is updated to point to the first character after the end 894*2e192b24SSimon Glass * of the token - the ending delimiter. 895*2e192b24SSimon Glass * 896*2e192b24SSimon Glass * On success, the new value of t->val is returned. Memory for t->val is 897*2e192b24SSimon Glass * allocated using malloc and must be free()'d to reclaim it. If insufficient 898*2e192b24SSimon Glass * memory is available, NULL is returned. 899*2e192b24SSimon Glass */ 900*2e192b24SSimon Glass static char *get_string(char **p, struct token *t, char delim, int lower) 901*2e192b24SSimon Glass { 902*2e192b24SSimon Glass char *b, *e; 903*2e192b24SSimon Glass size_t len, i; 904*2e192b24SSimon Glass 905*2e192b24SSimon Glass /* 906*2e192b24SSimon Glass * b and e both start at the beginning of the input stream. 907*2e192b24SSimon Glass * 908*2e192b24SSimon Glass * e is incremented until we find the ending delimiter, or a NUL byte 909*2e192b24SSimon Glass * is reached. Then, we take e - b to find the length of the token. 910*2e192b24SSimon Glass */ 911*2e192b24SSimon Glass b = e = *p; 912*2e192b24SSimon Glass 913*2e192b24SSimon Glass while (*e) { 914*2e192b24SSimon Glass if ((delim == ' ' && isspace(*e)) || delim == *e) 915*2e192b24SSimon Glass break; 916*2e192b24SSimon Glass e++; 917*2e192b24SSimon Glass } 918*2e192b24SSimon Glass 919*2e192b24SSimon Glass len = e - b; 920*2e192b24SSimon Glass 921*2e192b24SSimon Glass /* 922*2e192b24SSimon Glass * Allocate memory to hold the string, and copy it in, converting 923*2e192b24SSimon Glass * characters to lowercase if lower is != 0. 924*2e192b24SSimon Glass */ 925*2e192b24SSimon Glass t->val = malloc(len + 1); 926*2e192b24SSimon Glass if (!t->val) 927*2e192b24SSimon Glass return NULL; 928*2e192b24SSimon Glass 929*2e192b24SSimon Glass for (i = 0; i < len; i++, b++) { 930*2e192b24SSimon Glass if (lower) 931*2e192b24SSimon Glass t->val[i] = tolower(*b); 932*2e192b24SSimon Glass else 933*2e192b24SSimon Glass t->val[i] = *b; 934*2e192b24SSimon Glass } 935*2e192b24SSimon Glass 936*2e192b24SSimon Glass t->val[len] = '\0'; 937*2e192b24SSimon Glass 938*2e192b24SSimon Glass /* 939*2e192b24SSimon Glass * Update *p so the caller knows where to continue scanning. 940*2e192b24SSimon Glass */ 941*2e192b24SSimon Glass *p = e; 942*2e192b24SSimon Glass 943*2e192b24SSimon Glass t->type = T_STRING; 944*2e192b24SSimon Glass 945*2e192b24SSimon Glass return t->val; 946*2e192b24SSimon Glass } 947*2e192b24SSimon Glass 948*2e192b24SSimon Glass /* 949*2e192b24SSimon Glass * Populate a keyword token with a type and value. 950*2e192b24SSimon Glass */ 951*2e192b24SSimon Glass static void get_keyword(struct token *t) 952*2e192b24SSimon Glass { 953*2e192b24SSimon Glass int i; 954*2e192b24SSimon Glass 955*2e192b24SSimon Glass for (i = 0; keywords[i].val; i++) { 956*2e192b24SSimon Glass if (!strcmp(t->val, keywords[i].val)) { 957*2e192b24SSimon Glass t->type = keywords[i].type; 958*2e192b24SSimon Glass break; 959*2e192b24SSimon Glass } 960*2e192b24SSimon Glass } 961*2e192b24SSimon Glass } 962*2e192b24SSimon Glass 963*2e192b24SSimon Glass /* 964*2e192b24SSimon Glass * Get the next token. We have to keep track of which state we're in to know 965*2e192b24SSimon Glass * if we're looking to get a string literal or a keyword. 966*2e192b24SSimon Glass * 967*2e192b24SSimon Glass * *p is updated to point at the first character after the current token. 968*2e192b24SSimon Glass */ 969*2e192b24SSimon Glass static void get_token(char **p, struct token *t, enum lex_state state) 970*2e192b24SSimon Glass { 971*2e192b24SSimon Glass char *c = *p; 972*2e192b24SSimon Glass 973*2e192b24SSimon Glass t->type = T_INVALID; 974*2e192b24SSimon Glass 975*2e192b24SSimon Glass /* eat non EOL whitespace */ 976*2e192b24SSimon Glass while (isblank(*c)) 977*2e192b24SSimon Glass c++; 978*2e192b24SSimon Glass 979*2e192b24SSimon Glass /* 980*2e192b24SSimon Glass * eat comments. note that string literals can't begin with #, but 981*2e192b24SSimon Glass * can contain a # after their first character. 982*2e192b24SSimon Glass */ 983*2e192b24SSimon Glass if (*c == '#') { 984*2e192b24SSimon Glass while (*c && *c != '\n') 985*2e192b24SSimon Glass c++; 986*2e192b24SSimon Glass } 987*2e192b24SSimon Glass 988*2e192b24SSimon Glass if (*c == '\n') { 989*2e192b24SSimon Glass t->type = T_EOL; 990*2e192b24SSimon Glass c++; 991*2e192b24SSimon Glass } else if (*c == '\0') { 992*2e192b24SSimon Glass t->type = T_EOF; 993*2e192b24SSimon Glass c++; 994*2e192b24SSimon Glass } else if (state == L_SLITERAL) { 995*2e192b24SSimon Glass get_string(&c, t, '\n', 0); 996*2e192b24SSimon Glass } else if (state == L_KEYWORD) { 997*2e192b24SSimon Glass /* 998*2e192b24SSimon Glass * when we expect a keyword, we first get the next string 999*2e192b24SSimon Glass * token delimited by whitespace, and then check if it 1000*2e192b24SSimon Glass * matches a keyword in our keyword list. if it does, it's 1001*2e192b24SSimon Glass * converted to a keyword token of the appropriate type, and 1002*2e192b24SSimon Glass * if not, it remains a string token. 1003*2e192b24SSimon Glass */ 1004*2e192b24SSimon Glass get_string(&c, t, ' ', 1); 1005*2e192b24SSimon Glass get_keyword(t); 1006*2e192b24SSimon Glass } 1007*2e192b24SSimon Glass 1008*2e192b24SSimon Glass *p = c; 1009*2e192b24SSimon Glass } 1010*2e192b24SSimon Glass 1011*2e192b24SSimon Glass /* 1012*2e192b24SSimon Glass * Increment *c until we get to the end of the current line, or EOF. 1013*2e192b24SSimon Glass */ 1014*2e192b24SSimon Glass static void eol_or_eof(char **c) 1015*2e192b24SSimon Glass { 1016*2e192b24SSimon Glass while (**c && **c != '\n') 1017*2e192b24SSimon Glass (*c)++; 1018*2e192b24SSimon Glass } 1019*2e192b24SSimon Glass 1020*2e192b24SSimon Glass /* 1021*2e192b24SSimon Glass * All of these parse_* functions share some common behavior. 1022*2e192b24SSimon Glass * 1023*2e192b24SSimon Glass * They finish with *c pointing after the token they parse, and return 1 on 1024*2e192b24SSimon Glass * success, or < 0 on error. 1025*2e192b24SSimon Glass */ 1026*2e192b24SSimon Glass 1027*2e192b24SSimon Glass /* 1028*2e192b24SSimon Glass * Parse a string literal and store a pointer it at *dst. String literals 1029*2e192b24SSimon Glass * terminate at the end of the line. 1030*2e192b24SSimon Glass */ 1031*2e192b24SSimon Glass static int parse_sliteral(char **c, char **dst) 1032*2e192b24SSimon Glass { 1033*2e192b24SSimon Glass struct token t; 1034*2e192b24SSimon Glass char *s = *c; 1035*2e192b24SSimon Glass 1036*2e192b24SSimon Glass get_token(c, &t, L_SLITERAL); 1037*2e192b24SSimon Glass 1038*2e192b24SSimon Glass if (t.type != T_STRING) { 1039*2e192b24SSimon Glass printf("Expected string literal: %.*s\n", (int)(*c - s), s); 1040*2e192b24SSimon Glass return -EINVAL; 1041*2e192b24SSimon Glass } 1042*2e192b24SSimon Glass 1043*2e192b24SSimon Glass *dst = t.val; 1044*2e192b24SSimon Glass 1045*2e192b24SSimon Glass return 1; 1046*2e192b24SSimon Glass } 1047*2e192b24SSimon Glass 1048*2e192b24SSimon Glass /* 1049*2e192b24SSimon Glass * Parse a base 10 (unsigned) integer and store it at *dst. 1050*2e192b24SSimon Glass */ 1051*2e192b24SSimon Glass static int parse_integer(char **c, int *dst) 1052*2e192b24SSimon Glass { 1053*2e192b24SSimon Glass struct token t; 1054*2e192b24SSimon Glass char *s = *c; 1055*2e192b24SSimon Glass 1056*2e192b24SSimon Glass get_token(c, &t, L_SLITERAL); 1057*2e192b24SSimon Glass 1058*2e192b24SSimon Glass if (t.type != T_STRING) { 1059*2e192b24SSimon Glass printf("Expected string: %.*s\n", (int)(*c - s), s); 1060*2e192b24SSimon Glass return -EINVAL; 1061*2e192b24SSimon Glass } 1062*2e192b24SSimon Glass 1063*2e192b24SSimon Glass *dst = simple_strtol(t.val, NULL, 10); 1064*2e192b24SSimon Glass 1065*2e192b24SSimon Glass free(t.val); 1066*2e192b24SSimon Glass 1067*2e192b24SSimon Glass return 1; 1068*2e192b24SSimon Glass } 1069*2e192b24SSimon Glass 1070*2e192b24SSimon Glass static int parse_pxefile_top(cmd_tbl_t *cmdtp, char *p, unsigned long base, 1071*2e192b24SSimon Glass struct pxe_menu *cfg, int nest_level); 1072*2e192b24SSimon Glass 1073*2e192b24SSimon Glass /* 1074*2e192b24SSimon Glass * Parse an include statement, and retrieve and parse the file it mentions. 1075*2e192b24SSimon Glass * 1076*2e192b24SSimon Glass * base should point to a location where it's safe to store the file, and 1077*2e192b24SSimon Glass * nest_level should indicate how many nested includes have occurred. For this 1078*2e192b24SSimon Glass * include, nest_level has already been incremented and doesn't need to be 1079*2e192b24SSimon Glass * incremented here. 1080*2e192b24SSimon Glass */ 1081*2e192b24SSimon Glass static int handle_include(cmd_tbl_t *cmdtp, char **c, unsigned long base, 1082*2e192b24SSimon Glass struct pxe_menu *cfg, int nest_level) 1083*2e192b24SSimon Glass { 1084*2e192b24SSimon Glass char *include_path; 1085*2e192b24SSimon Glass char *s = *c; 1086*2e192b24SSimon Glass int err; 1087*2e192b24SSimon Glass char *buf; 1088*2e192b24SSimon Glass int ret; 1089*2e192b24SSimon Glass 1090*2e192b24SSimon Glass err = parse_sliteral(c, &include_path); 1091*2e192b24SSimon Glass 1092*2e192b24SSimon Glass if (err < 0) { 1093*2e192b24SSimon Glass printf("Expected include path: %.*s\n", 1094*2e192b24SSimon Glass (int)(*c - s), s); 1095*2e192b24SSimon Glass return err; 1096*2e192b24SSimon Glass } 1097*2e192b24SSimon Glass 1098*2e192b24SSimon Glass err = get_pxe_file(cmdtp, include_path, base); 1099*2e192b24SSimon Glass 1100*2e192b24SSimon Glass if (err < 0) { 1101*2e192b24SSimon Glass printf("Couldn't retrieve %s\n", include_path); 1102*2e192b24SSimon Glass return err; 1103*2e192b24SSimon Glass } 1104*2e192b24SSimon Glass 1105*2e192b24SSimon Glass buf = map_sysmem(base, 0); 1106*2e192b24SSimon Glass ret = parse_pxefile_top(cmdtp, buf, base, cfg, nest_level); 1107*2e192b24SSimon Glass unmap_sysmem(buf); 1108*2e192b24SSimon Glass 1109*2e192b24SSimon Glass return ret; 1110*2e192b24SSimon Glass } 1111*2e192b24SSimon Glass 1112*2e192b24SSimon Glass /* 1113*2e192b24SSimon Glass * Parse lines that begin with 'menu'. 1114*2e192b24SSimon Glass * 1115*2e192b24SSimon Glass * base and nest are provided to handle the 'menu include' case. 1116*2e192b24SSimon Glass * 1117*2e192b24SSimon Glass * base should point to a location where it's safe to store the included file. 1118*2e192b24SSimon Glass * 1119*2e192b24SSimon Glass * nest_level should be 1 when parsing the top level pxe file, 2 when parsing 1120*2e192b24SSimon Glass * a file it includes, 3 when parsing a file included by that file, and so on. 1121*2e192b24SSimon Glass */ 1122*2e192b24SSimon Glass static int parse_menu(cmd_tbl_t *cmdtp, char **c, struct pxe_menu *cfg, 1123*2e192b24SSimon Glass unsigned long base, int nest_level) 1124*2e192b24SSimon Glass { 1125*2e192b24SSimon Glass struct token t; 1126*2e192b24SSimon Glass char *s = *c; 1127*2e192b24SSimon Glass int err = 0; 1128*2e192b24SSimon Glass 1129*2e192b24SSimon Glass get_token(c, &t, L_KEYWORD); 1130*2e192b24SSimon Glass 1131*2e192b24SSimon Glass switch (t.type) { 1132*2e192b24SSimon Glass case T_TITLE: 1133*2e192b24SSimon Glass err = parse_sliteral(c, &cfg->title); 1134*2e192b24SSimon Glass 1135*2e192b24SSimon Glass break; 1136*2e192b24SSimon Glass 1137*2e192b24SSimon Glass case T_INCLUDE: 1138*2e192b24SSimon Glass err = handle_include(cmdtp, c, base, cfg, 1139*2e192b24SSimon Glass nest_level + 1); 1140*2e192b24SSimon Glass break; 1141*2e192b24SSimon Glass 1142*2e192b24SSimon Glass default: 1143*2e192b24SSimon Glass printf("Ignoring malformed menu command: %.*s\n", 1144*2e192b24SSimon Glass (int)(*c - s), s); 1145*2e192b24SSimon Glass } 1146*2e192b24SSimon Glass 1147*2e192b24SSimon Glass if (err < 0) 1148*2e192b24SSimon Glass return err; 1149*2e192b24SSimon Glass 1150*2e192b24SSimon Glass eol_or_eof(c); 1151*2e192b24SSimon Glass 1152*2e192b24SSimon Glass return 1; 1153*2e192b24SSimon Glass } 1154*2e192b24SSimon Glass 1155*2e192b24SSimon Glass /* 1156*2e192b24SSimon Glass * Handles parsing a 'menu line' when we're parsing a label. 1157*2e192b24SSimon Glass */ 1158*2e192b24SSimon Glass static int parse_label_menu(char **c, struct pxe_menu *cfg, 1159*2e192b24SSimon Glass struct pxe_label *label) 1160*2e192b24SSimon Glass { 1161*2e192b24SSimon Glass struct token t; 1162*2e192b24SSimon Glass char *s; 1163*2e192b24SSimon Glass 1164*2e192b24SSimon Glass s = *c; 1165*2e192b24SSimon Glass 1166*2e192b24SSimon Glass get_token(c, &t, L_KEYWORD); 1167*2e192b24SSimon Glass 1168*2e192b24SSimon Glass switch (t.type) { 1169*2e192b24SSimon Glass case T_DEFAULT: 1170*2e192b24SSimon Glass if (!cfg->default_label) 1171*2e192b24SSimon Glass cfg->default_label = strdup(label->name); 1172*2e192b24SSimon Glass 1173*2e192b24SSimon Glass if (!cfg->default_label) 1174*2e192b24SSimon Glass return -ENOMEM; 1175*2e192b24SSimon Glass 1176*2e192b24SSimon Glass break; 1177*2e192b24SSimon Glass case T_LABEL: 1178*2e192b24SSimon Glass parse_sliteral(c, &label->menu); 1179*2e192b24SSimon Glass break; 1180*2e192b24SSimon Glass default: 1181*2e192b24SSimon Glass printf("Ignoring malformed menu command: %.*s\n", 1182*2e192b24SSimon Glass (int)(*c - s), s); 1183*2e192b24SSimon Glass } 1184*2e192b24SSimon Glass 1185*2e192b24SSimon Glass eol_or_eof(c); 1186*2e192b24SSimon Glass 1187*2e192b24SSimon Glass return 0; 1188*2e192b24SSimon Glass } 1189*2e192b24SSimon Glass 1190*2e192b24SSimon Glass /* 1191*2e192b24SSimon Glass * Parses a label and adds it to the list of labels for a menu. 1192*2e192b24SSimon Glass * 1193*2e192b24SSimon Glass * A label ends when we either get to the end of a file, or 1194*2e192b24SSimon Glass * get some input we otherwise don't have a handler defined 1195*2e192b24SSimon Glass * for. 1196*2e192b24SSimon Glass * 1197*2e192b24SSimon Glass */ 1198*2e192b24SSimon Glass static int parse_label(char **c, struct pxe_menu *cfg) 1199*2e192b24SSimon Glass { 1200*2e192b24SSimon Glass struct token t; 1201*2e192b24SSimon Glass int len; 1202*2e192b24SSimon Glass char *s = *c; 1203*2e192b24SSimon Glass struct pxe_label *label; 1204*2e192b24SSimon Glass int err; 1205*2e192b24SSimon Glass 1206*2e192b24SSimon Glass label = label_create(); 1207*2e192b24SSimon Glass if (!label) 1208*2e192b24SSimon Glass return -ENOMEM; 1209*2e192b24SSimon Glass 1210*2e192b24SSimon Glass err = parse_sliteral(c, &label->name); 1211*2e192b24SSimon Glass if (err < 0) { 1212*2e192b24SSimon Glass printf("Expected label name: %.*s\n", (int)(*c - s), s); 1213*2e192b24SSimon Glass label_destroy(label); 1214*2e192b24SSimon Glass return -EINVAL; 1215*2e192b24SSimon Glass } 1216*2e192b24SSimon Glass 1217*2e192b24SSimon Glass list_add_tail(&label->list, &cfg->labels); 1218*2e192b24SSimon Glass 1219*2e192b24SSimon Glass while (1) { 1220*2e192b24SSimon Glass s = *c; 1221*2e192b24SSimon Glass get_token(c, &t, L_KEYWORD); 1222*2e192b24SSimon Glass 1223*2e192b24SSimon Glass err = 0; 1224*2e192b24SSimon Glass switch (t.type) { 1225*2e192b24SSimon Glass case T_MENU: 1226*2e192b24SSimon Glass err = parse_label_menu(c, cfg, label); 1227*2e192b24SSimon Glass break; 1228*2e192b24SSimon Glass 1229*2e192b24SSimon Glass case T_KERNEL: 1230*2e192b24SSimon Glass case T_LINUX: 1231*2e192b24SSimon Glass err = parse_sliteral(c, &label->kernel); 1232*2e192b24SSimon Glass break; 1233*2e192b24SSimon Glass 1234*2e192b24SSimon Glass case T_APPEND: 1235*2e192b24SSimon Glass err = parse_sliteral(c, &label->append); 1236*2e192b24SSimon Glass if (label->initrd) 1237*2e192b24SSimon Glass break; 1238*2e192b24SSimon Glass s = strstr(label->append, "initrd="); 1239*2e192b24SSimon Glass if (!s) 1240*2e192b24SSimon Glass break; 1241*2e192b24SSimon Glass s += 7; 1242*2e192b24SSimon Glass len = (int)(strchr(s, ' ') - s); 1243*2e192b24SSimon Glass label->initrd = malloc(len + 1); 1244*2e192b24SSimon Glass strncpy(label->initrd, s, len); 1245*2e192b24SSimon Glass label->initrd[len] = '\0'; 1246*2e192b24SSimon Glass 1247*2e192b24SSimon Glass break; 1248*2e192b24SSimon Glass 1249*2e192b24SSimon Glass case T_INITRD: 1250*2e192b24SSimon Glass if (!label->initrd) 1251*2e192b24SSimon Glass err = parse_sliteral(c, &label->initrd); 1252*2e192b24SSimon Glass break; 1253*2e192b24SSimon Glass 1254*2e192b24SSimon Glass case T_FDT: 1255*2e192b24SSimon Glass if (!label->fdt) 1256*2e192b24SSimon Glass err = parse_sliteral(c, &label->fdt); 1257*2e192b24SSimon Glass break; 1258*2e192b24SSimon Glass 1259*2e192b24SSimon Glass case T_FDTDIR: 1260*2e192b24SSimon Glass if (!label->fdtdir) 1261*2e192b24SSimon Glass err = parse_sliteral(c, &label->fdtdir); 1262*2e192b24SSimon Glass break; 1263*2e192b24SSimon Glass 1264*2e192b24SSimon Glass case T_LOCALBOOT: 1265*2e192b24SSimon Glass label->localboot = 1; 1266*2e192b24SSimon Glass err = parse_integer(c, &label->localboot_val); 1267*2e192b24SSimon Glass break; 1268*2e192b24SSimon Glass 1269*2e192b24SSimon Glass case T_IPAPPEND: 1270*2e192b24SSimon Glass err = parse_integer(c, &label->ipappend); 1271*2e192b24SSimon Glass break; 1272*2e192b24SSimon Glass 1273*2e192b24SSimon Glass case T_EOL: 1274*2e192b24SSimon Glass break; 1275*2e192b24SSimon Glass 1276*2e192b24SSimon Glass default: 1277*2e192b24SSimon Glass /* 1278*2e192b24SSimon Glass * put the token back! we don't want it - it's the end 1279*2e192b24SSimon Glass * of a label and whatever token this is, it's 1280*2e192b24SSimon Glass * something for the menu level context to handle. 1281*2e192b24SSimon Glass */ 1282*2e192b24SSimon Glass *c = s; 1283*2e192b24SSimon Glass return 1; 1284*2e192b24SSimon Glass } 1285*2e192b24SSimon Glass 1286*2e192b24SSimon Glass if (err < 0) 1287*2e192b24SSimon Glass return err; 1288*2e192b24SSimon Glass } 1289*2e192b24SSimon Glass } 1290*2e192b24SSimon Glass 1291*2e192b24SSimon Glass /* 1292*2e192b24SSimon Glass * This 16 comes from the limit pxelinux imposes on nested includes. 1293*2e192b24SSimon Glass * 1294*2e192b24SSimon Glass * There is no reason at all we couldn't do more, but some limit helps prevent 1295*2e192b24SSimon Glass * infinite (until crash occurs) recursion if a file tries to include itself. 1296*2e192b24SSimon Glass */ 1297*2e192b24SSimon Glass #define MAX_NEST_LEVEL 16 1298*2e192b24SSimon Glass 1299*2e192b24SSimon Glass /* 1300*2e192b24SSimon Glass * Entry point for parsing a menu file. nest_level indicates how many times 1301*2e192b24SSimon Glass * we've nested in includes. It will be 1 for the top level menu file. 1302*2e192b24SSimon Glass * 1303*2e192b24SSimon Glass * Returns 1 on success, < 0 on error. 1304*2e192b24SSimon Glass */ 1305*2e192b24SSimon Glass static int parse_pxefile_top(cmd_tbl_t *cmdtp, char *p, unsigned long base, 1306*2e192b24SSimon Glass struct pxe_menu *cfg, int nest_level) 1307*2e192b24SSimon Glass { 1308*2e192b24SSimon Glass struct token t; 1309*2e192b24SSimon Glass char *s, *b, *label_name; 1310*2e192b24SSimon Glass int err; 1311*2e192b24SSimon Glass 1312*2e192b24SSimon Glass b = p; 1313*2e192b24SSimon Glass 1314*2e192b24SSimon Glass if (nest_level > MAX_NEST_LEVEL) { 1315*2e192b24SSimon Glass printf("Maximum nesting (%d) exceeded\n", MAX_NEST_LEVEL); 1316*2e192b24SSimon Glass return -EMLINK; 1317*2e192b24SSimon Glass } 1318*2e192b24SSimon Glass 1319*2e192b24SSimon Glass while (1) { 1320*2e192b24SSimon Glass s = p; 1321*2e192b24SSimon Glass 1322*2e192b24SSimon Glass get_token(&p, &t, L_KEYWORD); 1323*2e192b24SSimon Glass 1324*2e192b24SSimon Glass err = 0; 1325*2e192b24SSimon Glass switch (t.type) { 1326*2e192b24SSimon Glass case T_MENU: 1327*2e192b24SSimon Glass cfg->prompt = 1; 1328*2e192b24SSimon Glass err = parse_menu(cmdtp, &p, cfg, 1329*2e192b24SSimon Glass base + ALIGN(strlen(b) + 1, 4), 1330*2e192b24SSimon Glass nest_level); 1331*2e192b24SSimon Glass break; 1332*2e192b24SSimon Glass 1333*2e192b24SSimon Glass case T_TIMEOUT: 1334*2e192b24SSimon Glass err = parse_integer(&p, &cfg->timeout); 1335*2e192b24SSimon Glass break; 1336*2e192b24SSimon Glass 1337*2e192b24SSimon Glass case T_LABEL: 1338*2e192b24SSimon Glass err = parse_label(&p, cfg); 1339*2e192b24SSimon Glass break; 1340*2e192b24SSimon Glass 1341*2e192b24SSimon Glass case T_DEFAULT: 1342*2e192b24SSimon Glass case T_ONTIMEOUT: 1343*2e192b24SSimon Glass err = parse_sliteral(&p, &label_name); 1344*2e192b24SSimon Glass 1345*2e192b24SSimon Glass if (label_name) { 1346*2e192b24SSimon Glass if (cfg->default_label) 1347*2e192b24SSimon Glass free(cfg->default_label); 1348*2e192b24SSimon Glass 1349*2e192b24SSimon Glass cfg->default_label = label_name; 1350*2e192b24SSimon Glass } 1351*2e192b24SSimon Glass 1352*2e192b24SSimon Glass break; 1353*2e192b24SSimon Glass 1354*2e192b24SSimon Glass case T_INCLUDE: 1355*2e192b24SSimon Glass err = handle_include(cmdtp, &p, 1356*2e192b24SSimon Glass base + ALIGN(strlen(b), 4), cfg, 1357*2e192b24SSimon Glass nest_level + 1); 1358*2e192b24SSimon Glass break; 1359*2e192b24SSimon Glass 1360*2e192b24SSimon Glass case T_PROMPT: 1361*2e192b24SSimon Glass eol_or_eof(&p); 1362*2e192b24SSimon Glass break; 1363*2e192b24SSimon Glass 1364*2e192b24SSimon Glass case T_EOL: 1365*2e192b24SSimon Glass break; 1366*2e192b24SSimon Glass 1367*2e192b24SSimon Glass case T_EOF: 1368*2e192b24SSimon Glass return 1; 1369*2e192b24SSimon Glass 1370*2e192b24SSimon Glass default: 1371*2e192b24SSimon Glass printf("Ignoring unknown command: %.*s\n", 1372*2e192b24SSimon Glass (int)(p - s), s); 1373*2e192b24SSimon Glass eol_or_eof(&p); 1374*2e192b24SSimon Glass } 1375*2e192b24SSimon Glass 1376*2e192b24SSimon Glass if (err < 0) 1377*2e192b24SSimon Glass return err; 1378*2e192b24SSimon Glass } 1379*2e192b24SSimon Glass } 1380*2e192b24SSimon Glass 1381*2e192b24SSimon Glass /* 1382*2e192b24SSimon Glass * Free the memory used by a pxe_menu and its labels. 1383*2e192b24SSimon Glass */ 1384*2e192b24SSimon Glass static void destroy_pxe_menu(struct pxe_menu *cfg) 1385*2e192b24SSimon Glass { 1386*2e192b24SSimon Glass struct list_head *pos, *n; 1387*2e192b24SSimon Glass struct pxe_label *label; 1388*2e192b24SSimon Glass 1389*2e192b24SSimon Glass if (cfg->title) 1390*2e192b24SSimon Glass free(cfg->title); 1391*2e192b24SSimon Glass 1392*2e192b24SSimon Glass if (cfg->default_label) 1393*2e192b24SSimon Glass free(cfg->default_label); 1394*2e192b24SSimon Glass 1395*2e192b24SSimon Glass list_for_each_safe(pos, n, &cfg->labels) { 1396*2e192b24SSimon Glass label = list_entry(pos, struct pxe_label, list); 1397*2e192b24SSimon Glass 1398*2e192b24SSimon Glass label_destroy(label); 1399*2e192b24SSimon Glass } 1400*2e192b24SSimon Glass 1401*2e192b24SSimon Glass free(cfg); 1402*2e192b24SSimon Glass } 1403*2e192b24SSimon Glass 1404*2e192b24SSimon Glass /* 1405*2e192b24SSimon Glass * Entry point for parsing a pxe file. This is only used for the top level 1406*2e192b24SSimon Glass * file. 1407*2e192b24SSimon Glass * 1408*2e192b24SSimon Glass * Returns NULL if there is an error, otherwise, returns a pointer to a 1409*2e192b24SSimon Glass * pxe_menu struct populated with the results of parsing the pxe file (and any 1410*2e192b24SSimon Glass * files it includes). The resulting pxe_menu struct can be free()'d by using 1411*2e192b24SSimon Glass * the destroy_pxe_menu() function. 1412*2e192b24SSimon Glass */ 1413*2e192b24SSimon Glass static struct pxe_menu *parse_pxefile(cmd_tbl_t *cmdtp, unsigned long menucfg) 1414*2e192b24SSimon Glass { 1415*2e192b24SSimon Glass struct pxe_menu *cfg; 1416*2e192b24SSimon Glass char *buf; 1417*2e192b24SSimon Glass int r; 1418*2e192b24SSimon Glass 1419*2e192b24SSimon Glass cfg = malloc(sizeof(struct pxe_menu)); 1420*2e192b24SSimon Glass 1421*2e192b24SSimon Glass if (!cfg) 1422*2e192b24SSimon Glass return NULL; 1423*2e192b24SSimon Glass 1424*2e192b24SSimon Glass memset(cfg, 0, sizeof(struct pxe_menu)); 1425*2e192b24SSimon Glass 1426*2e192b24SSimon Glass INIT_LIST_HEAD(&cfg->labels); 1427*2e192b24SSimon Glass 1428*2e192b24SSimon Glass buf = map_sysmem(menucfg, 0); 1429*2e192b24SSimon Glass r = parse_pxefile_top(cmdtp, buf, menucfg, cfg, 1); 1430*2e192b24SSimon Glass unmap_sysmem(buf); 1431*2e192b24SSimon Glass 1432*2e192b24SSimon Glass if (r < 0) { 1433*2e192b24SSimon Glass destroy_pxe_menu(cfg); 1434*2e192b24SSimon Glass return NULL; 1435*2e192b24SSimon Glass } 1436*2e192b24SSimon Glass 1437*2e192b24SSimon Glass return cfg; 1438*2e192b24SSimon Glass } 1439*2e192b24SSimon Glass 1440*2e192b24SSimon Glass /* 1441*2e192b24SSimon Glass * Converts a pxe_menu struct into a menu struct for use with U-boot's generic 1442*2e192b24SSimon Glass * menu code. 1443*2e192b24SSimon Glass */ 1444*2e192b24SSimon Glass static struct menu *pxe_menu_to_menu(struct pxe_menu *cfg) 1445*2e192b24SSimon Glass { 1446*2e192b24SSimon Glass struct pxe_label *label; 1447*2e192b24SSimon Glass struct list_head *pos; 1448*2e192b24SSimon Glass struct menu *m; 1449*2e192b24SSimon Glass int err; 1450*2e192b24SSimon Glass int i = 1; 1451*2e192b24SSimon Glass char *default_num = NULL; 1452*2e192b24SSimon Glass 1453*2e192b24SSimon Glass /* 1454*2e192b24SSimon Glass * Create a menu and add items for all the labels. 1455*2e192b24SSimon Glass */ 1456*2e192b24SSimon Glass m = menu_create(cfg->title, cfg->timeout, cfg->prompt, label_print, 1457*2e192b24SSimon Glass NULL, NULL); 1458*2e192b24SSimon Glass 1459*2e192b24SSimon Glass if (!m) 1460*2e192b24SSimon Glass return NULL; 1461*2e192b24SSimon Glass 1462*2e192b24SSimon Glass list_for_each(pos, &cfg->labels) { 1463*2e192b24SSimon Glass label = list_entry(pos, struct pxe_label, list); 1464*2e192b24SSimon Glass 1465*2e192b24SSimon Glass sprintf(label->num, "%d", i++); 1466*2e192b24SSimon Glass if (menu_item_add(m, label->num, label) != 1) { 1467*2e192b24SSimon Glass menu_destroy(m); 1468*2e192b24SSimon Glass return NULL; 1469*2e192b24SSimon Glass } 1470*2e192b24SSimon Glass if (cfg->default_label && 1471*2e192b24SSimon Glass (strcmp(label->name, cfg->default_label) == 0)) 1472*2e192b24SSimon Glass default_num = label->num; 1473*2e192b24SSimon Glass 1474*2e192b24SSimon Glass } 1475*2e192b24SSimon Glass 1476*2e192b24SSimon Glass /* 1477*2e192b24SSimon Glass * After we've created items for each label in the menu, set the 1478*2e192b24SSimon Glass * menu's default label if one was specified. 1479*2e192b24SSimon Glass */ 1480*2e192b24SSimon Glass if (default_num) { 1481*2e192b24SSimon Glass err = menu_default_set(m, default_num); 1482*2e192b24SSimon Glass if (err != 1) { 1483*2e192b24SSimon Glass if (err != -ENOENT) { 1484*2e192b24SSimon Glass menu_destroy(m); 1485*2e192b24SSimon Glass return NULL; 1486*2e192b24SSimon Glass } 1487*2e192b24SSimon Glass 1488*2e192b24SSimon Glass printf("Missing default: %s\n", cfg->default_label); 1489*2e192b24SSimon Glass } 1490*2e192b24SSimon Glass } 1491*2e192b24SSimon Glass 1492*2e192b24SSimon Glass return m; 1493*2e192b24SSimon Glass } 1494*2e192b24SSimon Glass 1495*2e192b24SSimon Glass /* 1496*2e192b24SSimon Glass * Try to boot any labels we have yet to attempt to boot. 1497*2e192b24SSimon Glass */ 1498*2e192b24SSimon Glass static void boot_unattempted_labels(cmd_tbl_t *cmdtp, struct pxe_menu *cfg) 1499*2e192b24SSimon Glass { 1500*2e192b24SSimon Glass struct list_head *pos; 1501*2e192b24SSimon Glass struct pxe_label *label; 1502*2e192b24SSimon Glass 1503*2e192b24SSimon Glass list_for_each(pos, &cfg->labels) { 1504*2e192b24SSimon Glass label = list_entry(pos, struct pxe_label, list); 1505*2e192b24SSimon Glass 1506*2e192b24SSimon Glass if (!label->attempted) 1507*2e192b24SSimon Glass label_boot(cmdtp, label); 1508*2e192b24SSimon Glass } 1509*2e192b24SSimon Glass } 1510*2e192b24SSimon Glass 1511*2e192b24SSimon Glass /* 1512*2e192b24SSimon Glass * Boot the system as prescribed by a pxe_menu. 1513*2e192b24SSimon Glass * 1514*2e192b24SSimon Glass * Use the menu system to either get the user's choice or the default, based 1515*2e192b24SSimon Glass * on config or user input. If there is no default or user's choice, 1516*2e192b24SSimon Glass * attempted to boot labels in the order they were given in pxe files. 1517*2e192b24SSimon Glass * If the default or user's choice fails to boot, attempt to boot other 1518*2e192b24SSimon Glass * labels in the order they were given in pxe files. 1519*2e192b24SSimon Glass * 1520*2e192b24SSimon Glass * If this function returns, there weren't any labels that successfully 1521*2e192b24SSimon Glass * booted, or the user interrupted the menu selection via ctrl+c. 1522*2e192b24SSimon Glass */ 1523*2e192b24SSimon Glass static void handle_pxe_menu(cmd_tbl_t *cmdtp, struct pxe_menu *cfg) 1524*2e192b24SSimon Glass { 1525*2e192b24SSimon Glass void *choice; 1526*2e192b24SSimon Glass struct menu *m; 1527*2e192b24SSimon Glass int err; 1528*2e192b24SSimon Glass 1529*2e192b24SSimon Glass m = pxe_menu_to_menu(cfg); 1530*2e192b24SSimon Glass if (!m) 1531*2e192b24SSimon Glass return; 1532*2e192b24SSimon Glass 1533*2e192b24SSimon Glass err = menu_get_choice(m, &choice); 1534*2e192b24SSimon Glass 1535*2e192b24SSimon Glass menu_destroy(m); 1536*2e192b24SSimon Glass 1537*2e192b24SSimon Glass /* 1538*2e192b24SSimon Glass * err == 1 means we got a choice back from menu_get_choice. 1539*2e192b24SSimon Glass * 1540*2e192b24SSimon Glass * err == -ENOENT if the menu was setup to select the default but no 1541*2e192b24SSimon Glass * default was set. in that case, we should continue trying to boot 1542*2e192b24SSimon Glass * labels that haven't been attempted yet. 1543*2e192b24SSimon Glass * 1544*2e192b24SSimon Glass * otherwise, the user interrupted or there was some other error and 1545*2e192b24SSimon Glass * we give up. 1546*2e192b24SSimon Glass */ 1547*2e192b24SSimon Glass 1548*2e192b24SSimon Glass if (err == 1) { 1549*2e192b24SSimon Glass err = label_boot(cmdtp, choice); 1550*2e192b24SSimon Glass if (!err) 1551*2e192b24SSimon Glass return; 1552*2e192b24SSimon Glass } else if (err != -ENOENT) { 1553*2e192b24SSimon Glass return; 1554*2e192b24SSimon Glass } 1555*2e192b24SSimon Glass 1556*2e192b24SSimon Glass boot_unattempted_labels(cmdtp, cfg); 1557*2e192b24SSimon Glass } 1558*2e192b24SSimon Glass 1559*2e192b24SSimon Glass #ifdef CONFIG_CMD_NET 1560*2e192b24SSimon Glass /* 1561*2e192b24SSimon Glass * Boots a system using a pxe file 1562*2e192b24SSimon Glass * 1563*2e192b24SSimon Glass * Returns 0 on success, 1 on error. 1564*2e192b24SSimon Glass */ 1565*2e192b24SSimon Glass static int 1566*2e192b24SSimon Glass do_pxe_boot(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) 1567*2e192b24SSimon Glass { 1568*2e192b24SSimon Glass unsigned long pxefile_addr_r; 1569*2e192b24SSimon Glass struct pxe_menu *cfg; 1570*2e192b24SSimon Glass char *pxefile_addr_str; 1571*2e192b24SSimon Glass 1572*2e192b24SSimon Glass do_getfile = do_get_tftp; 1573*2e192b24SSimon Glass 1574*2e192b24SSimon Glass if (argc == 1) { 1575*2e192b24SSimon Glass pxefile_addr_str = from_env("pxefile_addr_r"); 1576*2e192b24SSimon Glass if (!pxefile_addr_str) 1577*2e192b24SSimon Glass return 1; 1578*2e192b24SSimon Glass 1579*2e192b24SSimon Glass } else if (argc == 2) { 1580*2e192b24SSimon Glass pxefile_addr_str = argv[1]; 1581*2e192b24SSimon Glass } else { 1582*2e192b24SSimon Glass return CMD_RET_USAGE; 1583*2e192b24SSimon Glass } 1584*2e192b24SSimon Glass 1585*2e192b24SSimon Glass if (strict_strtoul(pxefile_addr_str, 16, &pxefile_addr_r) < 0) { 1586*2e192b24SSimon Glass printf("Invalid pxefile address: %s\n", pxefile_addr_str); 1587*2e192b24SSimon Glass return 1; 1588*2e192b24SSimon Glass } 1589*2e192b24SSimon Glass 1590*2e192b24SSimon Glass cfg = parse_pxefile(cmdtp, pxefile_addr_r); 1591*2e192b24SSimon Glass 1592*2e192b24SSimon Glass if (cfg == NULL) { 1593*2e192b24SSimon Glass printf("Error parsing config file\n"); 1594*2e192b24SSimon Glass return 1; 1595*2e192b24SSimon Glass } 1596*2e192b24SSimon Glass 1597*2e192b24SSimon Glass handle_pxe_menu(cmdtp, cfg); 1598*2e192b24SSimon Glass 1599*2e192b24SSimon Glass destroy_pxe_menu(cfg); 1600*2e192b24SSimon Glass 1601*2e192b24SSimon Glass copy_filename(net_boot_file_name, "", sizeof(net_boot_file_name)); 1602*2e192b24SSimon Glass 1603*2e192b24SSimon Glass return 0; 1604*2e192b24SSimon Glass } 1605*2e192b24SSimon Glass 1606*2e192b24SSimon Glass static cmd_tbl_t cmd_pxe_sub[] = { 1607*2e192b24SSimon Glass U_BOOT_CMD_MKENT(get, 1, 1, do_pxe_get, "", ""), 1608*2e192b24SSimon Glass U_BOOT_CMD_MKENT(boot, 2, 1, do_pxe_boot, "", "") 1609*2e192b24SSimon Glass }; 1610*2e192b24SSimon Glass 1611*2e192b24SSimon Glass static int do_pxe(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) 1612*2e192b24SSimon Glass { 1613*2e192b24SSimon Glass cmd_tbl_t *cp; 1614*2e192b24SSimon Glass 1615*2e192b24SSimon Glass if (argc < 2) 1616*2e192b24SSimon Glass return CMD_RET_USAGE; 1617*2e192b24SSimon Glass 1618*2e192b24SSimon Glass is_pxe = true; 1619*2e192b24SSimon Glass 1620*2e192b24SSimon Glass /* drop initial "pxe" arg */ 1621*2e192b24SSimon Glass argc--; 1622*2e192b24SSimon Glass argv++; 1623*2e192b24SSimon Glass 1624*2e192b24SSimon Glass cp = find_cmd_tbl(argv[0], cmd_pxe_sub, ARRAY_SIZE(cmd_pxe_sub)); 1625*2e192b24SSimon Glass 1626*2e192b24SSimon Glass if (cp) 1627*2e192b24SSimon Glass return cp->cmd(cmdtp, flag, argc, argv); 1628*2e192b24SSimon Glass 1629*2e192b24SSimon Glass return CMD_RET_USAGE; 1630*2e192b24SSimon Glass } 1631*2e192b24SSimon Glass 1632*2e192b24SSimon Glass U_BOOT_CMD( 1633*2e192b24SSimon Glass pxe, 3, 1, do_pxe, 1634*2e192b24SSimon Glass "commands to get and boot from pxe files", 1635*2e192b24SSimon Glass "get - try to retrieve a pxe file using tftp\npxe " 1636*2e192b24SSimon Glass "boot [pxefile_addr_r] - boot from the pxe file at pxefile_addr_r\n" 1637*2e192b24SSimon Glass ); 1638*2e192b24SSimon Glass #endif 1639*2e192b24SSimon Glass 1640*2e192b24SSimon Glass /* 1641*2e192b24SSimon Glass * Boots a system using a local disk syslinux/extlinux file 1642*2e192b24SSimon Glass * 1643*2e192b24SSimon Glass * Returns 0 on success, 1 on error. 1644*2e192b24SSimon Glass */ 1645*2e192b24SSimon Glass static int do_sysboot(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) 1646*2e192b24SSimon Glass { 1647*2e192b24SSimon Glass unsigned long pxefile_addr_r; 1648*2e192b24SSimon Glass struct pxe_menu *cfg; 1649*2e192b24SSimon Glass char *pxefile_addr_str; 1650*2e192b24SSimon Glass char *filename; 1651*2e192b24SSimon Glass int prompt = 0; 1652*2e192b24SSimon Glass 1653*2e192b24SSimon Glass is_pxe = false; 1654*2e192b24SSimon Glass 1655*2e192b24SSimon Glass if (argc > 1 && strstr(argv[1], "-p")) { 1656*2e192b24SSimon Glass prompt = 1; 1657*2e192b24SSimon Glass argc--; 1658*2e192b24SSimon Glass argv++; 1659*2e192b24SSimon Glass } 1660*2e192b24SSimon Glass 1661*2e192b24SSimon Glass if (argc < 4) 1662*2e192b24SSimon Glass return cmd_usage(cmdtp); 1663*2e192b24SSimon Glass 1664*2e192b24SSimon Glass if (argc < 5) { 1665*2e192b24SSimon Glass pxefile_addr_str = from_env("pxefile_addr_r"); 1666*2e192b24SSimon Glass if (!pxefile_addr_str) 1667*2e192b24SSimon Glass return 1; 1668*2e192b24SSimon Glass } else { 1669*2e192b24SSimon Glass pxefile_addr_str = argv[4]; 1670*2e192b24SSimon Glass } 1671*2e192b24SSimon Glass 1672*2e192b24SSimon Glass if (argc < 6) 1673*2e192b24SSimon Glass filename = getenv("bootfile"); 1674*2e192b24SSimon Glass else { 1675*2e192b24SSimon Glass filename = argv[5]; 1676*2e192b24SSimon Glass setenv("bootfile", filename); 1677*2e192b24SSimon Glass } 1678*2e192b24SSimon Glass 1679*2e192b24SSimon Glass if (strstr(argv[3], "ext2")) 1680*2e192b24SSimon Glass do_getfile = do_get_ext2; 1681*2e192b24SSimon Glass else if (strstr(argv[3], "fat")) 1682*2e192b24SSimon Glass do_getfile = do_get_fat; 1683*2e192b24SSimon Glass else if (strstr(argv[3], "any")) 1684*2e192b24SSimon Glass do_getfile = do_get_any; 1685*2e192b24SSimon Glass else { 1686*2e192b24SSimon Glass printf("Invalid filesystem: %s\n", argv[3]); 1687*2e192b24SSimon Glass return 1; 1688*2e192b24SSimon Glass } 1689*2e192b24SSimon Glass fs_argv[1] = argv[1]; 1690*2e192b24SSimon Glass fs_argv[2] = argv[2]; 1691*2e192b24SSimon Glass 1692*2e192b24SSimon Glass if (strict_strtoul(pxefile_addr_str, 16, &pxefile_addr_r) < 0) { 1693*2e192b24SSimon Glass printf("Invalid pxefile address: %s\n", pxefile_addr_str); 1694*2e192b24SSimon Glass return 1; 1695*2e192b24SSimon Glass } 1696*2e192b24SSimon Glass 1697*2e192b24SSimon Glass if (get_pxe_file(cmdtp, filename, pxefile_addr_r) < 0) { 1698*2e192b24SSimon Glass printf("Error reading config file\n"); 1699*2e192b24SSimon Glass return 1; 1700*2e192b24SSimon Glass } 1701*2e192b24SSimon Glass 1702*2e192b24SSimon Glass cfg = parse_pxefile(cmdtp, pxefile_addr_r); 1703*2e192b24SSimon Glass 1704*2e192b24SSimon Glass if (cfg == NULL) { 1705*2e192b24SSimon Glass printf("Error parsing config file\n"); 1706*2e192b24SSimon Glass return 1; 1707*2e192b24SSimon Glass } 1708*2e192b24SSimon Glass 1709*2e192b24SSimon Glass if (prompt) 1710*2e192b24SSimon Glass cfg->prompt = 1; 1711*2e192b24SSimon Glass 1712*2e192b24SSimon Glass handle_pxe_menu(cmdtp, cfg); 1713*2e192b24SSimon Glass 1714*2e192b24SSimon Glass destroy_pxe_menu(cfg); 1715*2e192b24SSimon Glass 1716*2e192b24SSimon Glass return 0; 1717*2e192b24SSimon Glass } 1718*2e192b24SSimon Glass 1719*2e192b24SSimon Glass U_BOOT_CMD( 1720*2e192b24SSimon Glass sysboot, 7, 1, do_sysboot, 1721*2e192b24SSimon Glass "command to get and boot from syslinux files", 1722*2e192b24SSimon Glass "[-p] <interface> <dev[:part]> <ext2|fat|any> [addr] [filename]\n" 1723*2e192b24SSimon Glass " - load and parse syslinux menu file 'filename' from ext2, fat\n" 1724*2e192b24SSimon Glass " or any filesystem on 'dev' on 'interface' to address 'addr'" 1725*2e192b24SSimon Glass ); 1726