xref: /rk3399_rockchip-uboot/cmd/pxe.c (revision c9bf1d5a23dde69f477e1c9c2fa6c32582d2395c)
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  */
from_env(const char * envvar)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  */
format_mac_pxe(char * outbuf,size_t outbuf_len)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  */
get_bootfile_path(const char * file_path,char * bootfile_path,size_t bootfile_path_size)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
do_get_tftp(cmd_tbl_t * cmdtp,const char * file_path,char * file_addr)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 
do_get_ext2(cmd_tbl_t * cmdtp,const char * file_path,char * file_addr)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 
do_get_fat(cmd_tbl_t * cmdtp,const char * file_path,char * file_addr)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 
do_get_any(cmd_tbl_t * cmdtp,const char * file_path,char * file_addr)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  */
get_relfile(cmd_tbl_t * cmdtp,const char * file_path,unsigned long file_addr)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  */
get_pxe_file(cmd_tbl_t * cmdtp,const char * file_path,unsigned long file_addr)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  */
get_pxelinux_path(cmd_tbl_t * cmdtp,const char * file,unsigned long pxefile_addr_r)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  */
pxe_uuid_path(cmd_tbl_t * cmdtp,unsigned long pxefile_addr_r)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  */
pxe_mac_path(cmd_tbl_t * cmdtp,unsigned long pxefile_addr_r)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  */
pxe_ipaddr_paths(cmd_tbl_t * cmdtp,unsigned long pxefile_addr_r)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
do_pxe_get(cmd_tbl_t * cmdtp,int flag,int argc,char * const argv[])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  */
get_relfile_envaddr(cmd_tbl_t * cmdtp,const char * file_path,const char * envaddr_name)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  */
label_create(void)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  */
label_destroy(struct pxe_label * label)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  */
label_print(void * data)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  */
label_localboot(struct pxe_label * label)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  */
label_boot(cmd_tbl_t * cmdtp,struct pxe_label * label)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 };
6197f2f3661STom 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;
6517f2f3661STom Rini 		strncpy(bootm_argv[2], env_get("ramdisk_addr_r"), 18);
6522e192b24SSimon Glass 		strcat(bootm_argv[2], ":");
6537f2f3661STom 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;
689*c9bf1d5aSTom Rini 		} else {
6902e192b24SSimon Glass 			if (label->append)
691*c9bf1d5aSTom Rini 				strncpy(bootargs, label->append,
692*c9bf1d5aSTom Rini 					sizeof(bootargs));
693*c9bf1d5aSTom Rini 			strcat(bootargs, ip_str);
694*c9bf1d5aSTom Rini 			strcat(bootargs, mac_str);
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 		}
700*c9bf1d5aSTom Rini 	}
7012e192b24SSimon Glass 
70200caae6dSSimon Glass 	bootm_argv[1] = env_get("kernel_addr_r");
7032e192b24SSimon Glass 
7042e192b24SSimon Glass 	/*
7052e192b24SSimon Glass 	 * fdt usage is optional:
7062e192b24SSimon Glass 	 * It handles the following scenarios. All scenarios are exclusive
7072e192b24SSimon Glass 	 *
7082e192b24SSimon Glass 	 * Scenario 1: If fdt_addr_r specified and "fdt" label is defined in
7092e192b24SSimon Glass 	 * pxe file, retrieve fdt blob from server. Pass fdt_addr_r to bootm,
7102e192b24SSimon Glass 	 * and adjust argc appropriately.
7112e192b24SSimon Glass 	 *
7122e192b24SSimon Glass 	 * Scenario 2: If there is an fdt_addr specified, pass it along to
7132e192b24SSimon Glass 	 * bootm, and adjust argc appropriately.
7142e192b24SSimon Glass 	 *
7152e192b24SSimon Glass 	 * Scenario 3: fdt blob is not available.
7162e192b24SSimon Glass 	 */
71700caae6dSSimon Glass 	bootm_argv[3] = env_get("fdt_addr_r");
7182e192b24SSimon Glass 
7192e192b24SSimon Glass 	/* if fdt label is defined then get fdt from server */
7202e192b24SSimon Glass 	if (bootm_argv[3]) {
7212e192b24SSimon Glass 		char *fdtfile = NULL;
7222e192b24SSimon Glass 		char *fdtfilefree = NULL;
7232e192b24SSimon Glass 
7242e192b24SSimon Glass 		if (label->fdt) {
7252e192b24SSimon Glass 			fdtfile = label->fdt;
7262e192b24SSimon Glass 		} else if (label->fdtdir) {
7272e192b24SSimon Glass 			char *f1, *f2, *f3, *f4, *slash;
7282e192b24SSimon Glass 
72900caae6dSSimon Glass 			f1 = env_get("fdtfile");
7302e192b24SSimon Glass 			if (f1) {
7312e192b24SSimon Glass 				f2 = "";
7322e192b24SSimon Glass 				f3 = "";
7332e192b24SSimon Glass 				f4 = "";
7342e192b24SSimon Glass 			} else {
7352e192b24SSimon Glass 				/*
7362e192b24SSimon Glass 				 * For complex cases where this code doesn't
7372e192b24SSimon Glass 				 * generate the correct filename, the board
7382e192b24SSimon Glass 				 * code should set $fdtfile during early boot,
7392e192b24SSimon Glass 				 * or the boot scripts should set $fdtfile
7402e192b24SSimon Glass 				 * before invoking "pxe" or "sysboot".
7412e192b24SSimon Glass 				 */
74200caae6dSSimon Glass 				f1 = env_get("soc");
7432e192b24SSimon Glass 				f2 = "-";
74400caae6dSSimon Glass 				f3 = env_get("board");
7452e192b24SSimon Glass 				f4 = ".dtb";
7462e192b24SSimon Glass 			}
7472e192b24SSimon Glass 
7482e192b24SSimon Glass 			len = strlen(label->fdtdir);
7492e192b24SSimon Glass 			if (!len)
7502e192b24SSimon Glass 				slash = "./";
7512e192b24SSimon Glass 			else if (label->fdtdir[len - 1] != '/')
7522e192b24SSimon Glass 				slash = "/";
7532e192b24SSimon Glass 			else
7542e192b24SSimon Glass 				slash = "";
7552e192b24SSimon Glass 
7562e192b24SSimon Glass 			len = strlen(label->fdtdir) + strlen(slash) +
7572e192b24SSimon Glass 				strlen(f1) + strlen(f2) + strlen(f3) +
7582e192b24SSimon Glass 				strlen(f4) + 1;
7592e192b24SSimon Glass 			fdtfilefree = malloc(len);
7602e192b24SSimon Glass 			if (!fdtfilefree) {
7612e192b24SSimon Glass 				printf("malloc fail (FDT filename)\n");
7622e192b24SSimon Glass 				return 1;
7632e192b24SSimon Glass 			}
7642e192b24SSimon Glass 
7652e192b24SSimon Glass 			snprintf(fdtfilefree, len, "%s%s%s%s%s%s",
7662e192b24SSimon Glass 				 label->fdtdir, slash, f1, f2, f3, f4);
7672e192b24SSimon Glass 			fdtfile = fdtfilefree;
7682e192b24SSimon Glass 		}
7692e192b24SSimon Glass 
7702e192b24SSimon Glass 		if (fdtfile) {
7712e192b24SSimon Glass 			int err = get_relfile_envaddr(cmdtp, fdtfile, "fdt_addr_r");
7722e192b24SSimon Glass 			free(fdtfilefree);
7732e192b24SSimon Glass 			if (err < 0) {
7742e192b24SSimon Glass 				printf("Skipping %s for failure retrieving fdt\n",
7752e192b24SSimon Glass 						label->name);
7762e192b24SSimon Glass 				return 1;
7772e192b24SSimon Glass 			}
7782e192b24SSimon Glass 		} else {
7792e192b24SSimon Glass 			bootm_argv[3] = NULL;
7802e192b24SSimon Glass 		}
7812e192b24SSimon Glass 	}
7822e192b24SSimon Glass 
7832e192b24SSimon Glass 	if (!bootm_argv[3])
78400caae6dSSimon Glass 		bootm_argv[3] = env_get("fdt_addr");
7852e192b24SSimon Glass 
786f63963f0SYork Sun 	if (bootm_argv[3]) {
787f63963f0SYork Sun 		if (!bootm_argv[2])
788f63963f0SYork Sun 			bootm_argv[2] = "-";
7892e192b24SSimon Glass 		bootm_argc = 4;
790f63963f0SYork Sun 	}
7912e192b24SSimon Glass 
7922e192b24SSimon Glass 	kernel_addr = genimg_get_kernel_addr(bootm_argv[1]);
7932e192b24SSimon Glass 	buf = map_sysmem(kernel_addr, 0);
7942e192b24SSimon Glass 	/* Try bootm for legacy and FIT format image */
7952e192b24SSimon Glass 	if (genimg_get_format(buf) != IMAGE_FORMAT_INVALID)
7962e192b24SSimon Glass 		do_bootm(cmdtp, 0, bootm_argc, bootm_argv);
7972e192b24SSimon Glass #ifdef CONFIG_CMD_BOOTI
7982e192b24SSimon Glass 	/* Try booting an AArch64 Linux kernel image */
7992e192b24SSimon Glass 	else
8002e192b24SSimon Glass 		do_booti(cmdtp, 0, bootm_argc, bootm_argv);
8012e192b24SSimon Glass #elif defined(CONFIG_CMD_BOOTZ)
8022e192b24SSimon Glass 	/* Try booting a Image */
8032e192b24SSimon Glass 	else
8042e192b24SSimon Glass 		do_bootz(cmdtp, 0, bootm_argc, bootm_argv);
8052e192b24SSimon Glass #endif
8062e192b24SSimon Glass 	unmap_sysmem(buf);
8072e192b24SSimon Glass 	return 1;
8082e192b24SSimon Glass }
8092e192b24SSimon Glass 
8102e192b24SSimon Glass /*
8112e192b24SSimon Glass  * Tokens for the pxe file parser.
8122e192b24SSimon Glass  */
8132e192b24SSimon Glass enum token_type {
8142e192b24SSimon Glass 	T_EOL,
8152e192b24SSimon Glass 	T_STRING,
8162e192b24SSimon Glass 	T_EOF,
8172e192b24SSimon Glass 	T_MENU,
8182e192b24SSimon Glass 	T_TITLE,
8192e192b24SSimon Glass 	T_TIMEOUT,
8202e192b24SSimon Glass 	T_LABEL,
8212e192b24SSimon Glass 	T_KERNEL,
8222e192b24SSimon Glass 	T_LINUX,
8232e192b24SSimon Glass 	T_APPEND,
8242e192b24SSimon Glass 	T_INITRD,
8252e192b24SSimon Glass 	T_LOCALBOOT,
8262e192b24SSimon Glass 	T_DEFAULT,
8272e192b24SSimon Glass 	T_PROMPT,
8282e192b24SSimon Glass 	T_INCLUDE,
8292e192b24SSimon Glass 	T_FDT,
8302e192b24SSimon Glass 	T_FDTDIR,
8312e192b24SSimon Glass 	T_ONTIMEOUT,
8322e192b24SSimon Glass 	T_IPAPPEND,
8332e192b24SSimon Glass 	T_INVALID
8342e192b24SSimon Glass };
8352e192b24SSimon Glass 
8362e192b24SSimon Glass /*
8372e192b24SSimon Glass  * A token - given by a value and a type.
8382e192b24SSimon Glass  */
8392e192b24SSimon Glass struct token {
8402e192b24SSimon Glass 	char *val;
8412e192b24SSimon Glass 	enum token_type type;
8422e192b24SSimon Glass };
8432e192b24SSimon Glass 
8442e192b24SSimon Glass /*
8452e192b24SSimon Glass  * Keywords recognized.
8462e192b24SSimon Glass  */
8472e192b24SSimon Glass static const struct token keywords[] = {
8482e192b24SSimon Glass 	{"menu", T_MENU},
8492e192b24SSimon Glass 	{"title", T_TITLE},
8502e192b24SSimon Glass 	{"timeout", T_TIMEOUT},
8512e192b24SSimon Glass 	{"default", T_DEFAULT},
8522e192b24SSimon Glass 	{"prompt", T_PROMPT},
8532e192b24SSimon Glass 	{"label", T_LABEL},
8542e192b24SSimon Glass 	{"kernel", T_KERNEL},
8552e192b24SSimon Glass 	{"linux", T_LINUX},
8562e192b24SSimon Glass 	{"localboot", T_LOCALBOOT},
8572e192b24SSimon Glass 	{"append", T_APPEND},
8582e192b24SSimon Glass 	{"initrd", T_INITRD},
8592e192b24SSimon Glass 	{"include", T_INCLUDE},
8602e192b24SSimon Glass 	{"devicetree", T_FDT},
8612e192b24SSimon Glass 	{"fdt", T_FDT},
8622e192b24SSimon Glass 	{"devicetreedir", T_FDTDIR},
8632e192b24SSimon Glass 	{"fdtdir", T_FDTDIR},
8642e192b24SSimon Glass 	{"ontimeout", T_ONTIMEOUT,},
8652e192b24SSimon Glass 	{"ipappend", T_IPAPPEND,},
8662e192b24SSimon Glass 	{NULL, T_INVALID}
8672e192b24SSimon Glass };
8682e192b24SSimon Glass 
8692e192b24SSimon Glass /*
8702e192b24SSimon Glass  * Since pxe(linux) files don't have a token to identify the start of a
8712e192b24SSimon Glass  * literal, we have to keep track of when we're in a state where a literal is
8722e192b24SSimon Glass  * expected vs when we're in a state a keyword is expected.
8732e192b24SSimon Glass  */
8742e192b24SSimon Glass enum lex_state {
8752e192b24SSimon Glass 	L_NORMAL = 0,
8762e192b24SSimon Glass 	L_KEYWORD,
8772e192b24SSimon Glass 	L_SLITERAL
8782e192b24SSimon Glass };
8792e192b24SSimon Glass 
8802e192b24SSimon Glass /*
8812e192b24SSimon Glass  * get_string retrieves a string from *p and stores it as a token in
8822e192b24SSimon Glass  * *t.
8832e192b24SSimon Glass  *
8842e192b24SSimon Glass  * get_string used for scanning both string literals and keywords.
8852e192b24SSimon Glass  *
8862e192b24SSimon Glass  * Characters from *p are copied into t-val until a character equal to
8872e192b24SSimon Glass  * delim is found, or a NUL byte is reached. If delim has the special value of
8882e192b24SSimon Glass  * ' ', any whitespace character will be used as a delimiter.
8892e192b24SSimon Glass  *
8902e192b24SSimon Glass  * If lower is unequal to 0, uppercase characters will be converted to
8912e192b24SSimon Glass  * lowercase in the result. This is useful to make keywords case
8922e192b24SSimon Glass  * insensitive.
8932e192b24SSimon Glass  *
8942e192b24SSimon Glass  * The location of *p is updated to point to the first character after the end
8952e192b24SSimon Glass  * of the token - the ending delimiter.
8962e192b24SSimon Glass  *
8972e192b24SSimon Glass  * On success, the new value of t->val is returned. Memory for t->val is
8982e192b24SSimon Glass  * allocated using malloc and must be free()'d to reclaim it.  If insufficient
8992e192b24SSimon Glass  * memory is available, NULL is returned.
9002e192b24SSimon Glass  */
get_string(char ** p,struct token * t,char delim,int lower)9012e192b24SSimon Glass static char *get_string(char **p, struct token *t, char delim, int lower)
9022e192b24SSimon Glass {
9032e192b24SSimon Glass 	char *b, *e;
9042e192b24SSimon Glass 	size_t len, i;
9052e192b24SSimon Glass 
9062e192b24SSimon Glass 	/*
9072e192b24SSimon Glass 	 * b and e both start at the beginning of the input stream.
9082e192b24SSimon Glass 	 *
9092e192b24SSimon Glass 	 * e is incremented until we find the ending delimiter, or a NUL byte
9102e192b24SSimon Glass 	 * is reached. Then, we take e - b to find the length of the token.
9112e192b24SSimon Glass 	 */
9122e192b24SSimon Glass 	b = e = *p;
9132e192b24SSimon Glass 
9142e192b24SSimon Glass 	while (*e) {
9152e192b24SSimon Glass 		if ((delim == ' ' && isspace(*e)) || delim == *e)
9162e192b24SSimon Glass 			break;
9172e192b24SSimon Glass 		e++;
9182e192b24SSimon Glass 	}
9192e192b24SSimon Glass 
9202e192b24SSimon Glass 	len = e - b;
9212e192b24SSimon Glass 
9222e192b24SSimon Glass 	/*
9232e192b24SSimon Glass 	 * Allocate memory to hold the string, and copy it in, converting
9242e192b24SSimon Glass 	 * characters to lowercase if lower is != 0.
9252e192b24SSimon Glass 	 */
9262e192b24SSimon Glass 	t->val = malloc(len + 1);
9272e192b24SSimon Glass 	if (!t->val)
9282e192b24SSimon Glass 		return NULL;
9292e192b24SSimon Glass 
9302e192b24SSimon Glass 	for (i = 0; i < len; i++, b++) {
9312e192b24SSimon Glass 		if (lower)
9322e192b24SSimon Glass 			t->val[i] = tolower(*b);
9332e192b24SSimon Glass 		else
9342e192b24SSimon Glass 			t->val[i] = *b;
9352e192b24SSimon Glass 	}
9362e192b24SSimon Glass 
9372e192b24SSimon Glass 	t->val[len] = '\0';
9382e192b24SSimon Glass 
9392e192b24SSimon Glass 	/*
9402e192b24SSimon Glass 	 * Update *p so the caller knows where to continue scanning.
9412e192b24SSimon Glass 	 */
9422e192b24SSimon Glass 	*p = e;
9432e192b24SSimon Glass 
9442e192b24SSimon Glass 	t->type = T_STRING;
9452e192b24SSimon Glass 
9462e192b24SSimon Glass 	return t->val;
9472e192b24SSimon Glass }
9482e192b24SSimon Glass 
9492e192b24SSimon Glass /*
9502e192b24SSimon Glass  * Populate a keyword token with a type and value.
9512e192b24SSimon Glass  */
get_keyword(struct token * t)9522e192b24SSimon Glass static void get_keyword(struct token *t)
9532e192b24SSimon Glass {
9542e192b24SSimon Glass 	int i;
9552e192b24SSimon Glass 
9562e192b24SSimon Glass 	for (i = 0; keywords[i].val; i++) {
9572e192b24SSimon Glass 		if (!strcmp(t->val, keywords[i].val)) {
9582e192b24SSimon Glass 			t->type = keywords[i].type;
9592e192b24SSimon Glass 			break;
9602e192b24SSimon Glass 		}
9612e192b24SSimon Glass 	}
9622e192b24SSimon Glass }
9632e192b24SSimon Glass 
9642e192b24SSimon Glass /*
9652e192b24SSimon Glass  * Get the next token.  We have to keep track of which state we're in to know
9662e192b24SSimon Glass  * if we're looking to get a string literal or a keyword.
9672e192b24SSimon Glass  *
9682e192b24SSimon Glass  * *p is updated to point at the first character after the current token.
9692e192b24SSimon Glass  */
get_token(char ** p,struct token * t,enum lex_state state)9702e192b24SSimon Glass static void get_token(char **p, struct token *t, enum lex_state state)
9712e192b24SSimon Glass {
9722e192b24SSimon Glass 	char *c = *p;
9732e192b24SSimon Glass 
9742e192b24SSimon Glass 	t->type = T_INVALID;
9752e192b24SSimon Glass 
9762e192b24SSimon Glass 	/* eat non EOL whitespace */
9772e192b24SSimon Glass 	while (isblank(*c))
9782e192b24SSimon Glass 		c++;
9792e192b24SSimon Glass 
9802e192b24SSimon Glass 	/*
9812e192b24SSimon Glass 	 * eat comments. note that string literals can't begin with #, but
9822e192b24SSimon Glass 	 * can contain a # after their first character.
9832e192b24SSimon Glass 	 */
9842e192b24SSimon Glass 	if (*c == '#') {
9852e192b24SSimon Glass 		while (*c && *c != '\n')
9862e192b24SSimon Glass 			c++;
9872e192b24SSimon Glass 	}
9882e192b24SSimon Glass 
9892e192b24SSimon Glass 	if (*c == '\n') {
9902e192b24SSimon Glass 		t->type = T_EOL;
9912e192b24SSimon Glass 		c++;
9922e192b24SSimon Glass 	} else if (*c == '\0') {
9932e192b24SSimon Glass 		t->type = T_EOF;
9942e192b24SSimon Glass 		c++;
9952e192b24SSimon Glass 	} else if (state == L_SLITERAL) {
9962e192b24SSimon Glass 		get_string(&c, t, '\n', 0);
9972e192b24SSimon Glass 	} else if (state == L_KEYWORD) {
9982e192b24SSimon Glass 		/*
9992e192b24SSimon Glass 		 * when we expect a keyword, we first get the next string
10002e192b24SSimon Glass 		 * token delimited by whitespace, and then check if it
10012e192b24SSimon Glass 		 * matches a keyword in our keyword list. if it does, it's
10022e192b24SSimon Glass 		 * converted to a keyword token of the appropriate type, and
10032e192b24SSimon Glass 		 * if not, it remains a string token.
10042e192b24SSimon Glass 		 */
10052e192b24SSimon Glass 		get_string(&c, t, ' ', 1);
10062e192b24SSimon Glass 		get_keyword(t);
10072e192b24SSimon Glass 	}
10082e192b24SSimon Glass 
10092e192b24SSimon Glass 	*p = c;
10102e192b24SSimon Glass }
10112e192b24SSimon Glass 
10122e192b24SSimon Glass /*
10132e192b24SSimon Glass  * Increment *c until we get to the end of the current line, or EOF.
10142e192b24SSimon Glass  */
eol_or_eof(char ** c)10152e192b24SSimon Glass static void eol_or_eof(char **c)
10162e192b24SSimon Glass {
10172e192b24SSimon Glass 	while (**c && **c != '\n')
10182e192b24SSimon Glass 		(*c)++;
10192e192b24SSimon Glass }
10202e192b24SSimon Glass 
10212e192b24SSimon Glass /*
10222e192b24SSimon Glass  * All of these parse_* functions share some common behavior.
10232e192b24SSimon Glass  *
10242e192b24SSimon Glass  * They finish with *c pointing after the token they parse, and return 1 on
10252e192b24SSimon Glass  * success, or < 0 on error.
10262e192b24SSimon Glass  */
10272e192b24SSimon Glass 
10282e192b24SSimon Glass /*
10292e192b24SSimon Glass  * Parse a string literal and store a pointer it at *dst. String literals
10302e192b24SSimon Glass  * terminate at the end of the line.
10312e192b24SSimon Glass  */
parse_sliteral(char ** c,char ** dst)10322e192b24SSimon Glass static int parse_sliteral(char **c, char **dst)
10332e192b24SSimon Glass {
10342e192b24SSimon Glass 	struct token t;
10352e192b24SSimon Glass 	char *s = *c;
10362e192b24SSimon Glass 
10372e192b24SSimon Glass 	get_token(c, &t, L_SLITERAL);
10382e192b24SSimon Glass 
10392e192b24SSimon Glass 	if (t.type != T_STRING) {
10402e192b24SSimon Glass 		printf("Expected string literal: %.*s\n", (int)(*c - s), s);
10412e192b24SSimon Glass 		return -EINVAL;
10422e192b24SSimon Glass 	}
10432e192b24SSimon Glass 
10442e192b24SSimon Glass 	*dst = t.val;
10452e192b24SSimon Glass 
10462e192b24SSimon Glass 	return 1;
10472e192b24SSimon Glass }
10482e192b24SSimon Glass 
10492e192b24SSimon Glass /*
10502e192b24SSimon Glass  * Parse a base 10 (unsigned) integer and store it at *dst.
10512e192b24SSimon Glass  */
parse_integer(char ** c,int * dst)10522e192b24SSimon Glass static int parse_integer(char **c, int *dst)
10532e192b24SSimon Glass {
10542e192b24SSimon Glass 	struct token t;
10552e192b24SSimon Glass 	char *s = *c;
10562e192b24SSimon Glass 
10572e192b24SSimon Glass 	get_token(c, &t, L_SLITERAL);
10582e192b24SSimon Glass 
10592e192b24SSimon Glass 	if (t.type != T_STRING) {
10602e192b24SSimon Glass 		printf("Expected string: %.*s\n", (int)(*c - s), s);
10612e192b24SSimon Glass 		return -EINVAL;
10622e192b24SSimon Glass 	}
10632e192b24SSimon Glass 
10642e192b24SSimon Glass 	*dst = simple_strtol(t.val, NULL, 10);
10652e192b24SSimon Glass 
10662e192b24SSimon Glass 	free(t.val);
10672e192b24SSimon Glass 
10682e192b24SSimon Glass 	return 1;
10692e192b24SSimon Glass }
10702e192b24SSimon Glass 
10712e192b24SSimon Glass static int parse_pxefile_top(cmd_tbl_t *cmdtp, char *p, unsigned long base,
10722e192b24SSimon Glass 	struct pxe_menu *cfg, int nest_level);
10732e192b24SSimon Glass 
10742e192b24SSimon Glass /*
10752e192b24SSimon Glass  * Parse an include statement, and retrieve and parse the file it mentions.
10762e192b24SSimon Glass  *
10772e192b24SSimon Glass  * base should point to a location where it's safe to store the file, and
10782e192b24SSimon Glass  * nest_level should indicate how many nested includes have occurred. For this
10792e192b24SSimon Glass  * include, nest_level has already been incremented and doesn't need to be
10802e192b24SSimon Glass  * incremented here.
10812e192b24SSimon Glass  */
handle_include(cmd_tbl_t * cmdtp,char ** c,unsigned long base,struct pxe_menu * cfg,int nest_level)10822e192b24SSimon Glass static int handle_include(cmd_tbl_t *cmdtp, char **c, unsigned long base,
10832e192b24SSimon Glass 				struct pxe_menu *cfg, int nest_level)
10842e192b24SSimon Glass {
10852e192b24SSimon Glass 	char *include_path;
10862e192b24SSimon Glass 	char *s = *c;
10872e192b24SSimon Glass 	int err;
10882e192b24SSimon Glass 	char *buf;
10892e192b24SSimon Glass 	int ret;
10902e192b24SSimon Glass 
10912e192b24SSimon Glass 	err = parse_sliteral(c, &include_path);
10922e192b24SSimon Glass 
10932e192b24SSimon Glass 	if (err < 0) {
10942e192b24SSimon Glass 		printf("Expected include path: %.*s\n",
10952e192b24SSimon Glass 				 (int)(*c - s), s);
10962e192b24SSimon Glass 		return err;
10972e192b24SSimon Glass 	}
10982e192b24SSimon Glass 
10992e192b24SSimon Glass 	err = get_pxe_file(cmdtp, include_path, base);
11002e192b24SSimon Glass 
11012e192b24SSimon Glass 	if (err < 0) {
11022e192b24SSimon Glass 		printf("Couldn't retrieve %s\n", include_path);
11032e192b24SSimon Glass 		return err;
11042e192b24SSimon Glass 	}
11052e192b24SSimon Glass 
11062e192b24SSimon Glass 	buf = map_sysmem(base, 0);
11072e192b24SSimon Glass 	ret = parse_pxefile_top(cmdtp, buf, base, cfg, nest_level);
11082e192b24SSimon Glass 	unmap_sysmem(buf);
11092e192b24SSimon Glass 
11102e192b24SSimon Glass 	return ret;
11112e192b24SSimon Glass }
11122e192b24SSimon Glass 
11132e192b24SSimon Glass /*
11142e192b24SSimon Glass  * Parse lines that begin with 'menu'.
11152e192b24SSimon Glass  *
11162e192b24SSimon Glass  * base and nest are provided to handle the 'menu include' case.
11172e192b24SSimon Glass  *
11182e192b24SSimon Glass  * base should point to a location where it's safe to store the included file.
11192e192b24SSimon Glass  *
11202e192b24SSimon Glass  * nest_level should be 1 when parsing the top level pxe file, 2 when parsing
11212e192b24SSimon Glass  * a file it includes, 3 when parsing a file included by that file, and so on.
11222e192b24SSimon Glass  */
parse_menu(cmd_tbl_t * cmdtp,char ** c,struct pxe_menu * cfg,unsigned long base,int nest_level)11232e192b24SSimon Glass static int parse_menu(cmd_tbl_t *cmdtp, char **c, struct pxe_menu *cfg,
11242e192b24SSimon Glass 				unsigned long base, int nest_level)
11252e192b24SSimon Glass {
11262e192b24SSimon Glass 	struct token t;
11272e192b24SSimon Glass 	char *s = *c;
11282e192b24SSimon Glass 	int err = 0;
11292e192b24SSimon Glass 
11302e192b24SSimon Glass 	get_token(c, &t, L_KEYWORD);
11312e192b24SSimon Glass 
11322e192b24SSimon Glass 	switch (t.type) {
11332e192b24SSimon Glass 	case T_TITLE:
11342e192b24SSimon Glass 		err = parse_sliteral(c, &cfg->title);
11352e192b24SSimon Glass 
11362e192b24SSimon Glass 		break;
11372e192b24SSimon Glass 
11382e192b24SSimon Glass 	case T_INCLUDE:
11392e192b24SSimon Glass 		err = handle_include(cmdtp, c, base, cfg,
11402e192b24SSimon Glass 						nest_level + 1);
11412e192b24SSimon Glass 		break;
11422e192b24SSimon Glass 
11432e192b24SSimon Glass 	default:
11442e192b24SSimon Glass 		printf("Ignoring malformed menu command: %.*s\n",
11452e192b24SSimon Glass 				(int)(*c - s), s);
11462e192b24SSimon Glass 	}
11472e192b24SSimon Glass 
11482e192b24SSimon Glass 	if (err < 0)
11492e192b24SSimon Glass 		return err;
11502e192b24SSimon Glass 
11512e192b24SSimon Glass 	eol_or_eof(c);
11522e192b24SSimon Glass 
11532e192b24SSimon Glass 	return 1;
11542e192b24SSimon Glass }
11552e192b24SSimon Glass 
11562e192b24SSimon Glass /*
11572e192b24SSimon Glass  * Handles parsing a 'menu line' when we're parsing a label.
11582e192b24SSimon Glass  */
parse_label_menu(char ** c,struct pxe_menu * cfg,struct pxe_label * label)11592e192b24SSimon Glass static int parse_label_menu(char **c, struct pxe_menu *cfg,
11602e192b24SSimon Glass 				struct pxe_label *label)
11612e192b24SSimon Glass {
11622e192b24SSimon Glass 	struct token t;
11632e192b24SSimon Glass 	char *s;
11642e192b24SSimon Glass 
11652e192b24SSimon Glass 	s = *c;
11662e192b24SSimon Glass 
11672e192b24SSimon Glass 	get_token(c, &t, L_KEYWORD);
11682e192b24SSimon Glass 
11692e192b24SSimon Glass 	switch (t.type) {
11702e192b24SSimon Glass 	case T_DEFAULT:
11712e192b24SSimon Glass 		if (!cfg->default_label)
11722e192b24SSimon Glass 			cfg->default_label = strdup(label->name);
11732e192b24SSimon Glass 
11742e192b24SSimon Glass 		if (!cfg->default_label)
11752e192b24SSimon Glass 			return -ENOMEM;
11762e192b24SSimon Glass 
11772e192b24SSimon Glass 		break;
11782e192b24SSimon Glass 	case T_LABEL:
11792e192b24SSimon Glass 		parse_sliteral(c, &label->menu);
11802e192b24SSimon Glass 		break;
11812e192b24SSimon Glass 	default:
11822e192b24SSimon Glass 		printf("Ignoring malformed menu command: %.*s\n",
11832e192b24SSimon Glass 				(int)(*c - s), s);
11842e192b24SSimon Glass 	}
11852e192b24SSimon Glass 
11862e192b24SSimon Glass 	eol_or_eof(c);
11872e192b24SSimon Glass 
11882e192b24SSimon Glass 	return 0;
11892e192b24SSimon Glass }
11902e192b24SSimon Glass 
11912e192b24SSimon Glass /*
11922e192b24SSimon Glass  * Parses a label and adds it to the list of labels for a menu.
11932e192b24SSimon Glass  *
11942e192b24SSimon Glass  * A label ends when we either get to the end of a file, or
11952e192b24SSimon Glass  * get some input we otherwise don't have a handler defined
11962e192b24SSimon Glass  * for.
11972e192b24SSimon Glass  *
11982e192b24SSimon Glass  */
parse_label(char ** c,struct pxe_menu * cfg)11992e192b24SSimon Glass static int parse_label(char **c, struct pxe_menu *cfg)
12002e192b24SSimon Glass {
12012e192b24SSimon Glass 	struct token t;
12022e192b24SSimon Glass 	int len;
12032e192b24SSimon Glass 	char *s = *c;
12042e192b24SSimon Glass 	struct pxe_label *label;
12052e192b24SSimon Glass 	int err;
12062e192b24SSimon Glass 
12072e192b24SSimon Glass 	label = label_create();
12082e192b24SSimon Glass 	if (!label)
12092e192b24SSimon Glass 		return -ENOMEM;
12102e192b24SSimon Glass 
12112e192b24SSimon Glass 	err = parse_sliteral(c, &label->name);
12122e192b24SSimon Glass 	if (err < 0) {
12132e192b24SSimon Glass 		printf("Expected label name: %.*s\n", (int)(*c - s), s);
12142e192b24SSimon Glass 		label_destroy(label);
12152e192b24SSimon Glass 		return -EINVAL;
12162e192b24SSimon Glass 	}
12172e192b24SSimon Glass 
12182e192b24SSimon Glass 	list_add_tail(&label->list, &cfg->labels);
12192e192b24SSimon Glass 
12202e192b24SSimon Glass 	while (1) {
12212e192b24SSimon Glass 		s = *c;
12222e192b24SSimon Glass 		get_token(c, &t, L_KEYWORD);
12232e192b24SSimon Glass 
12242e192b24SSimon Glass 		err = 0;
12252e192b24SSimon Glass 		switch (t.type) {
12262e192b24SSimon Glass 		case T_MENU:
12272e192b24SSimon Glass 			err = parse_label_menu(c, cfg, label);
12282e192b24SSimon Glass 			break;
12292e192b24SSimon Glass 
12302e192b24SSimon Glass 		case T_KERNEL:
12312e192b24SSimon Glass 		case T_LINUX:
12322e192b24SSimon Glass 			err = parse_sliteral(c, &label->kernel);
12332e192b24SSimon Glass 			break;
12342e192b24SSimon Glass 
12352e192b24SSimon Glass 		case T_APPEND:
12362e192b24SSimon Glass 			err = parse_sliteral(c, &label->append);
12372e192b24SSimon Glass 			if (label->initrd)
12382e192b24SSimon Glass 				break;
12392e192b24SSimon Glass 			s = strstr(label->append, "initrd=");
12402e192b24SSimon Glass 			if (!s)
12412e192b24SSimon Glass 				break;
12422e192b24SSimon Glass 			s += 7;
12432e192b24SSimon Glass 			len = (int)(strchr(s, ' ') - s);
12442e192b24SSimon Glass 			label->initrd = malloc(len + 1);
12452e192b24SSimon Glass 			strncpy(label->initrd, s, len);
12462e192b24SSimon Glass 			label->initrd[len] = '\0';
12472e192b24SSimon Glass 
12482e192b24SSimon Glass 			break;
12492e192b24SSimon Glass 
12502e192b24SSimon Glass 		case T_INITRD:
12512e192b24SSimon Glass 			if (!label->initrd)
12522e192b24SSimon Glass 				err = parse_sliteral(c, &label->initrd);
12532e192b24SSimon Glass 			break;
12542e192b24SSimon Glass 
12552e192b24SSimon Glass 		case T_FDT:
12562e192b24SSimon Glass 			if (!label->fdt)
12572e192b24SSimon Glass 				err = parse_sliteral(c, &label->fdt);
12582e192b24SSimon Glass 			break;
12592e192b24SSimon Glass 
12602e192b24SSimon Glass 		case T_FDTDIR:
12612e192b24SSimon Glass 			if (!label->fdtdir)
12622e192b24SSimon Glass 				err = parse_sliteral(c, &label->fdtdir);
12632e192b24SSimon Glass 			break;
12642e192b24SSimon Glass 
12652e192b24SSimon Glass 		case T_LOCALBOOT:
12662e192b24SSimon Glass 			label->localboot = 1;
12672e192b24SSimon Glass 			err = parse_integer(c, &label->localboot_val);
12682e192b24SSimon Glass 			break;
12692e192b24SSimon Glass 
12702e192b24SSimon Glass 		case T_IPAPPEND:
12712e192b24SSimon Glass 			err = parse_integer(c, &label->ipappend);
12722e192b24SSimon Glass 			break;
12732e192b24SSimon Glass 
12742e192b24SSimon Glass 		case T_EOL:
12752e192b24SSimon Glass 			break;
12762e192b24SSimon Glass 
12772e192b24SSimon Glass 		default:
12782e192b24SSimon Glass 			/*
12792e192b24SSimon Glass 			 * put the token back! we don't want it - it's the end
12802e192b24SSimon Glass 			 * of a label and whatever token this is, it's
12812e192b24SSimon Glass 			 * something for the menu level context to handle.
12822e192b24SSimon Glass 			 */
12832e192b24SSimon Glass 			*c = s;
12842e192b24SSimon Glass 			return 1;
12852e192b24SSimon Glass 		}
12862e192b24SSimon Glass 
12872e192b24SSimon Glass 		if (err < 0)
12882e192b24SSimon Glass 			return err;
12892e192b24SSimon Glass 	}
12902e192b24SSimon Glass }
12912e192b24SSimon Glass 
12922e192b24SSimon Glass /*
12932e192b24SSimon Glass  * This 16 comes from the limit pxelinux imposes on nested includes.
12942e192b24SSimon Glass  *
12952e192b24SSimon Glass  * There is no reason at all we couldn't do more, but some limit helps prevent
12962e192b24SSimon Glass  * infinite (until crash occurs) recursion if a file tries to include itself.
12972e192b24SSimon Glass  */
12982e192b24SSimon Glass #define MAX_NEST_LEVEL 16
12992e192b24SSimon Glass 
13002e192b24SSimon Glass /*
13012e192b24SSimon Glass  * Entry point for parsing a menu file. nest_level indicates how many times
13022e192b24SSimon Glass  * we've nested in includes.  It will be 1 for the top level menu file.
13032e192b24SSimon Glass  *
13042e192b24SSimon Glass  * Returns 1 on success, < 0 on error.
13052e192b24SSimon Glass  */
parse_pxefile_top(cmd_tbl_t * cmdtp,char * p,unsigned long base,struct pxe_menu * cfg,int nest_level)13062e192b24SSimon Glass static int parse_pxefile_top(cmd_tbl_t *cmdtp, char *p, unsigned long base,
13072e192b24SSimon Glass 				struct pxe_menu *cfg, int nest_level)
13082e192b24SSimon Glass {
13092e192b24SSimon Glass 	struct token t;
13102e192b24SSimon Glass 	char *s, *b, *label_name;
13112e192b24SSimon Glass 	int err;
13122e192b24SSimon Glass 
13132e192b24SSimon Glass 	b = p;
13142e192b24SSimon Glass 
13152e192b24SSimon Glass 	if (nest_level > MAX_NEST_LEVEL) {
13162e192b24SSimon Glass 		printf("Maximum nesting (%d) exceeded\n", MAX_NEST_LEVEL);
13172e192b24SSimon Glass 		return -EMLINK;
13182e192b24SSimon Glass 	}
13192e192b24SSimon Glass 
13202e192b24SSimon Glass 	while (1) {
13212e192b24SSimon Glass 		s = p;
13222e192b24SSimon Glass 
13232e192b24SSimon Glass 		get_token(&p, &t, L_KEYWORD);
13242e192b24SSimon Glass 
13252e192b24SSimon Glass 		err = 0;
13262e192b24SSimon Glass 		switch (t.type) {
13272e192b24SSimon Glass 		case T_MENU:
13282e192b24SSimon Glass 			cfg->prompt = 1;
13292e192b24SSimon Glass 			err = parse_menu(cmdtp, &p, cfg,
13302e192b24SSimon Glass 				base + ALIGN(strlen(b) + 1, 4),
13312e192b24SSimon Glass 				nest_level);
13322e192b24SSimon Glass 			break;
13332e192b24SSimon Glass 
13342e192b24SSimon Glass 		case T_TIMEOUT:
13352e192b24SSimon Glass 			err = parse_integer(&p, &cfg->timeout);
13362e192b24SSimon Glass 			break;
13372e192b24SSimon Glass 
13382e192b24SSimon Glass 		case T_LABEL:
13392e192b24SSimon Glass 			err = parse_label(&p, cfg);
13402e192b24SSimon Glass 			break;
13412e192b24SSimon Glass 
13422e192b24SSimon Glass 		case T_DEFAULT:
13432e192b24SSimon Glass 		case T_ONTIMEOUT:
13442e192b24SSimon Glass 			err = parse_sliteral(&p, &label_name);
13452e192b24SSimon Glass 
13462e192b24SSimon Glass 			if (label_name) {
13472e192b24SSimon Glass 				if (cfg->default_label)
13482e192b24SSimon Glass 					free(cfg->default_label);
13492e192b24SSimon Glass 
13502e192b24SSimon Glass 				cfg->default_label = label_name;
13512e192b24SSimon Glass 			}
13522e192b24SSimon Glass 
13532e192b24SSimon Glass 			break;
13542e192b24SSimon Glass 
13552e192b24SSimon Glass 		case T_INCLUDE:
13562e192b24SSimon Glass 			err = handle_include(cmdtp, &p,
13572e192b24SSimon Glass 				base + ALIGN(strlen(b), 4), cfg,
13582e192b24SSimon Glass 				nest_level + 1);
13592e192b24SSimon Glass 			break;
13602e192b24SSimon Glass 
13612e192b24SSimon Glass 		case T_PROMPT:
13622e192b24SSimon Glass 			eol_or_eof(&p);
13632e192b24SSimon Glass 			break;
13642e192b24SSimon Glass 
13652e192b24SSimon Glass 		case T_EOL:
13662e192b24SSimon Glass 			break;
13672e192b24SSimon Glass 
13682e192b24SSimon Glass 		case T_EOF:
13692e192b24SSimon Glass 			return 1;
13702e192b24SSimon Glass 
13712e192b24SSimon Glass 		default:
13722e192b24SSimon Glass 			printf("Ignoring unknown command: %.*s\n",
13732e192b24SSimon Glass 							(int)(p - s), s);
13742e192b24SSimon Glass 			eol_or_eof(&p);
13752e192b24SSimon Glass 		}
13762e192b24SSimon Glass 
13772e192b24SSimon Glass 		if (err < 0)
13782e192b24SSimon Glass 			return err;
13792e192b24SSimon Glass 	}
13802e192b24SSimon Glass }
13812e192b24SSimon Glass 
13822e192b24SSimon Glass /*
13832e192b24SSimon Glass  * Free the memory used by a pxe_menu and its labels.
13842e192b24SSimon Glass  */
destroy_pxe_menu(struct pxe_menu * cfg)13852e192b24SSimon Glass static void destroy_pxe_menu(struct pxe_menu *cfg)
13862e192b24SSimon Glass {
13872e192b24SSimon Glass 	struct list_head *pos, *n;
13882e192b24SSimon Glass 	struct pxe_label *label;
13892e192b24SSimon Glass 
13902e192b24SSimon Glass 	if (cfg->title)
13912e192b24SSimon Glass 		free(cfg->title);
13922e192b24SSimon Glass 
13932e192b24SSimon Glass 	if (cfg->default_label)
13942e192b24SSimon Glass 		free(cfg->default_label);
13952e192b24SSimon Glass 
13962e192b24SSimon Glass 	list_for_each_safe(pos, n, &cfg->labels) {
13972e192b24SSimon Glass 		label = list_entry(pos, struct pxe_label, list);
13982e192b24SSimon Glass 
13992e192b24SSimon Glass 		label_destroy(label);
14002e192b24SSimon Glass 	}
14012e192b24SSimon Glass 
14022e192b24SSimon Glass 	free(cfg);
14032e192b24SSimon Glass }
14042e192b24SSimon Glass 
14052e192b24SSimon Glass /*
14062e192b24SSimon Glass  * Entry point for parsing a pxe file. This is only used for the top level
14072e192b24SSimon Glass  * file.
14082e192b24SSimon Glass  *
14092e192b24SSimon Glass  * Returns NULL if there is an error, otherwise, returns a pointer to a
14102e192b24SSimon Glass  * pxe_menu struct populated with the results of parsing the pxe file (and any
14112e192b24SSimon Glass  * files it includes). The resulting pxe_menu struct can be free()'d by using
14122e192b24SSimon Glass  * the destroy_pxe_menu() function.
14132e192b24SSimon Glass  */
parse_pxefile(cmd_tbl_t * cmdtp,unsigned long menucfg)14142e192b24SSimon Glass static struct pxe_menu *parse_pxefile(cmd_tbl_t *cmdtp, unsigned long menucfg)
14152e192b24SSimon Glass {
14162e192b24SSimon Glass 	struct pxe_menu *cfg;
14172e192b24SSimon Glass 	char *buf;
14182e192b24SSimon Glass 	int r;
14192e192b24SSimon Glass 
14202e192b24SSimon Glass 	cfg = malloc(sizeof(struct pxe_menu));
14212e192b24SSimon Glass 
14222e192b24SSimon Glass 	if (!cfg)
14232e192b24SSimon Glass 		return NULL;
14242e192b24SSimon Glass 
14252e192b24SSimon Glass 	memset(cfg, 0, sizeof(struct pxe_menu));
14262e192b24SSimon Glass 
14272e192b24SSimon Glass 	INIT_LIST_HEAD(&cfg->labels);
14282e192b24SSimon Glass 
14292e192b24SSimon Glass 	buf = map_sysmem(menucfg, 0);
14302e192b24SSimon Glass 	r = parse_pxefile_top(cmdtp, buf, menucfg, cfg, 1);
14312e192b24SSimon Glass 	unmap_sysmem(buf);
14322e192b24SSimon Glass 
14332e192b24SSimon Glass 	if (r < 0) {
14342e192b24SSimon Glass 		destroy_pxe_menu(cfg);
14352e192b24SSimon Glass 		return NULL;
14362e192b24SSimon Glass 	}
14372e192b24SSimon Glass 
14382e192b24SSimon Glass 	return cfg;
14392e192b24SSimon Glass }
14402e192b24SSimon Glass 
14412e192b24SSimon Glass /*
1442a187559eSBin Meng  * Converts a pxe_menu struct into a menu struct for use with U-Boot's generic
14432e192b24SSimon Glass  * menu code.
14442e192b24SSimon Glass  */
pxe_menu_to_menu(struct pxe_menu * cfg)14452e192b24SSimon Glass static struct menu *pxe_menu_to_menu(struct pxe_menu *cfg)
14462e192b24SSimon Glass {
14472e192b24SSimon Glass 	struct pxe_label *label;
14482e192b24SSimon Glass 	struct list_head *pos;
14492e192b24SSimon Glass 	struct menu *m;
14502e192b24SSimon Glass 	int err;
14512e192b24SSimon Glass 	int i = 1;
14522e192b24SSimon Glass 	char *default_num = NULL;
14532e192b24SSimon Glass 
14542e192b24SSimon Glass 	/*
14552e192b24SSimon Glass 	 * Create a menu and add items for all the labels.
14562e192b24SSimon Glass 	 */
14572e192b24SSimon Glass 	m = menu_create(cfg->title, cfg->timeout, cfg->prompt, label_print,
14582e192b24SSimon Glass 			NULL, NULL);
14592e192b24SSimon Glass 
14602e192b24SSimon Glass 	if (!m)
14612e192b24SSimon Glass 		return NULL;
14622e192b24SSimon Glass 
14632e192b24SSimon Glass 	list_for_each(pos, &cfg->labels) {
14642e192b24SSimon Glass 		label = list_entry(pos, struct pxe_label, list);
14652e192b24SSimon Glass 
14662e192b24SSimon Glass 		sprintf(label->num, "%d", i++);
14672e192b24SSimon Glass 		if (menu_item_add(m, label->num, label) != 1) {
14682e192b24SSimon Glass 			menu_destroy(m);
14692e192b24SSimon Glass 			return NULL;
14702e192b24SSimon Glass 		}
14712e192b24SSimon Glass 		if (cfg->default_label &&
14722e192b24SSimon Glass 		    (strcmp(label->name, cfg->default_label) == 0))
14732e192b24SSimon Glass 			default_num = label->num;
14742e192b24SSimon Glass 
14752e192b24SSimon Glass 	}
14762e192b24SSimon Glass 
14772e192b24SSimon Glass 	/*
14782e192b24SSimon Glass 	 * After we've created items for each label in the menu, set the
14792e192b24SSimon Glass 	 * menu's default label if one was specified.
14802e192b24SSimon Glass 	 */
14812e192b24SSimon Glass 	if (default_num) {
14822e192b24SSimon Glass 		err = menu_default_set(m, default_num);
14832e192b24SSimon Glass 		if (err != 1) {
14842e192b24SSimon Glass 			if (err != -ENOENT) {
14852e192b24SSimon Glass 				menu_destroy(m);
14862e192b24SSimon Glass 				return NULL;
14872e192b24SSimon Glass 			}
14882e192b24SSimon Glass 
14892e192b24SSimon Glass 			printf("Missing default: %s\n", cfg->default_label);
14902e192b24SSimon Glass 		}
14912e192b24SSimon Glass 	}
14922e192b24SSimon Glass 
14932e192b24SSimon Glass 	return m;
14942e192b24SSimon Glass }
14952e192b24SSimon Glass 
14962e192b24SSimon Glass /*
14972e192b24SSimon Glass  * Try to boot any labels we have yet to attempt to boot.
14982e192b24SSimon Glass  */
boot_unattempted_labels(cmd_tbl_t * cmdtp,struct pxe_menu * cfg)14992e192b24SSimon Glass static void boot_unattempted_labels(cmd_tbl_t *cmdtp, struct pxe_menu *cfg)
15002e192b24SSimon Glass {
15012e192b24SSimon Glass 	struct list_head *pos;
15022e192b24SSimon Glass 	struct pxe_label *label;
15032e192b24SSimon Glass 
15042e192b24SSimon Glass 	list_for_each(pos, &cfg->labels) {
15052e192b24SSimon Glass 		label = list_entry(pos, struct pxe_label, list);
15062e192b24SSimon Glass 
15072e192b24SSimon Glass 		if (!label->attempted)
15082e192b24SSimon Glass 			label_boot(cmdtp, label);
15092e192b24SSimon Glass 	}
15102e192b24SSimon Glass }
15112e192b24SSimon Glass 
15122e192b24SSimon Glass /*
15132e192b24SSimon Glass  * Boot the system as prescribed by a pxe_menu.
15142e192b24SSimon Glass  *
15152e192b24SSimon Glass  * Use the menu system to either get the user's choice or the default, based
15162e192b24SSimon Glass  * on config or user input.  If there is no default or user's choice,
15172e192b24SSimon Glass  * attempted to boot labels in the order they were given in pxe files.
15182e192b24SSimon Glass  * If the default or user's choice fails to boot, attempt to boot other
15192e192b24SSimon Glass  * labels in the order they were given in pxe files.
15202e192b24SSimon Glass  *
15212e192b24SSimon Glass  * If this function returns, there weren't any labels that successfully
15222e192b24SSimon Glass  * booted, or the user interrupted the menu selection via ctrl+c.
15232e192b24SSimon Glass  */
handle_pxe_menu(cmd_tbl_t * cmdtp,struct pxe_menu * cfg)15242e192b24SSimon Glass static void handle_pxe_menu(cmd_tbl_t *cmdtp, struct pxe_menu *cfg)
15252e192b24SSimon Glass {
15262e192b24SSimon Glass 	void *choice;
15272e192b24SSimon Glass 	struct menu *m;
15282e192b24SSimon Glass 	int err;
15292e192b24SSimon Glass 
15302e192b24SSimon Glass 	m = pxe_menu_to_menu(cfg);
15312e192b24SSimon Glass 	if (!m)
15322e192b24SSimon Glass 		return;
15332e192b24SSimon Glass 
15342e192b24SSimon Glass 	err = menu_get_choice(m, &choice);
15352e192b24SSimon Glass 
15362e192b24SSimon Glass 	menu_destroy(m);
15372e192b24SSimon Glass 
15382e192b24SSimon Glass 	/*
15392e192b24SSimon Glass 	 * err == 1 means we got a choice back from menu_get_choice.
15402e192b24SSimon Glass 	 *
15412e192b24SSimon Glass 	 * err == -ENOENT if the menu was setup to select the default but no
15422e192b24SSimon Glass 	 * default was set. in that case, we should continue trying to boot
15432e192b24SSimon Glass 	 * labels that haven't been attempted yet.
15442e192b24SSimon Glass 	 *
15452e192b24SSimon Glass 	 * otherwise, the user interrupted or there was some other error and
15462e192b24SSimon Glass 	 * we give up.
15472e192b24SSimon Glass 	 */
15482e192b24SSimon Glass 
15492e192b24SSimon Glass 	if (err == 1) {
15502e192b24SSimon Glass 		err = label_boot(cmdtp, choice);
15512e192b24SSimon Glass 		if (!err)
15522e192b24SSimon Glass 			return;
15532e192b24SSimon Glass 	} else if (err != -ENOENT) {
15542e192b24SSimon Glass 		return;
15552e192b24SSimon Glass 	}
15562e192b24SSimon Glass 
15572e192b24SSimon Glass 	boot_unattempted_labels(cmdtp, cfg);
15582e192b24SSimon Glass }
15592e192b24SSimon Glass 
15602e192b24SSimon Glass #ifdef CONFIG_CMD_NET
15612e192b24SSimon Glass /*
15622e192b24SSimon Glass  * Boots a system using a pxe file
15632e192b24SSimon Glass  *
15642e192b24SSimon Glass  * Returns 0 on success, 1 on error.
15652e192b24SSimon Glass  */
15662e192b24SSimon Glass static int
do_pxe_boot(cmd_tbl_t * cmdtp,int flag,int argc,char * const argv[])15672e192b24SSimon Glass do_pxe_boot(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
15682e192b24SSimon Glass {
15692e192b24SSimon Glass 	unsigned long pxefile_addr_r;
15702e192b24SSimon Glass 	struct pxe_menu *cfg;
15712e192b24SSimon Glass 	char *pxefile_addr_str;
15722e192b24SSimon Glass 
15732e192b24SSimon Glass 	do_getfile = do_get_tftp;
15742e192b24SSimon Glass 
15752e192b24SSimon Glass 	if (argc == 1) {
15762e192b24SSimon Glass 		pxefile_addr_str = from_env("pxefile_addr_r");
15772e192b24SSimon Glass 		if (!pxefile_addr_str)
15782e192b24SSimon Glass 			return 1;
15792e192b24SSimon Glass 
15802e192b24SSimon Glass 	} else if (argc == 2) {
15812e192b24SSimon Glass 		pxefile_addr_str = argv[1];
15822e192b24SSimon Glass 	} else {
15832e192b24SSimon Glass 		return CMD_RET_USAGE;
15842e192b24SSimon Glass 	}
15852e192b24SSimon Glass 
15862e192b24SSimon Glass 	if (strict_strtoul(pxefile_addr_str, 16, &pxefile_addr_r) < 0) {
15872e192b24SSimon Glass 		printf("Invalid pxefile address: %s\n", pxefile_addr_str);
15882e192b24SSimon Glass 		return 1;
15892e192b24SSimon Glass 	}
15902e192b24SSimon Glass 
15912e192b24SSimon Glass 	cfg = parse_pxefile(cmdtp, pxefile_addr_r);
15922e192b24SSimon Glass 
15932e192b24SSimon Glass 	if (cfg == NULL) {
15942e192b24SSimon Glass 		printf("Error parsing config file\n");
15952e192b24SSimon Glass 		return 1;
15962e192b24SSimon Glass 	}
15972e192b24SSimon Glass 
15982e192b24SSimon Glass 	handle_pxe_menu(cmdtp, cfg);
15992e192b24SSimon Glass 
16002e192b24SSimon Glass 	destroy_pxe_menu(cfg);
16012e192b24SSimon Glass 
16022e192b24SSimon Glass 	copy_filename(net_boot_file_name, "", sizeof(net_boot_file_name));
16032e192b24SSimon Glass 
16042e192b24SSimon Glass 	return 0;
16052e192b24SSimon Glass }
16062e192b24SSimon Glass 
16072e192b24SSimon Glass static cmd_tbl_t cmd_pxe_sub[] = {
16082e192b24SSimon Glass 	U_BOOT_CMD_MKENT(get, 1, 1, do_pxe_get, "", ""),
16092e192b24SSimon Glass 	U_BOOT_CMD_MKENT(boot, 2, 1, do_pxe_boot, "", "")
16102e192b24SSimon Glass };
16112e192b24SSimon Glass 
do_pxe(cmd_tbl_t * cmdtp,int flag,int argc,char * const argv[])16122e192b24SSimon Glass static int do_pxe(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
16132e192b24SSimon Glass {
16142e192b24SSimon Glass 	cmd_tbl_t *cp;
16152e192b24SSimon Glass 
16162e192b24SSimon Glass 	if (argc < 2)
16172e192b24SSimon Glass 		return CMD_RET_USAGE;
16182e192b24SSimon Glass 
16192e192b24SSimon Glass 	is_pxe = true;
16202e192b24SSimon Glass 
16212e192b24SSimon Glass 	/* drop initial "pxe" arg */
16222e192b24SSimon Glass 	argc--;
16232e192b24SSimon Glass 	argv++;
16242e192b24SSimon Glass 
16252e192b24SSimon Glass 	cp = find_cmd_tbl(argv[0], cmd_pxe_sub, ARRAY_SIZE(cmd_pxe_sub));
16262e192b24SSimon Glass 
16272e192b24SSimon Glass 	if (cp)
16282e192b24SSimon Glass 		return cp->cmd(cmdtp, flag, argc, argv);
16292e192b24SSimon Glass 
16302e192b24SSimon Glass 	return CMD_RET_USAGE;
16312e192b24SSimon Glass }
16322e192b24SSimon Glass 
16332e192b24SSimon Glass U_BOOT_CMD(
16342e192b24SSimon Glass 	pxe, 3, 1, do_pxe,
16352e192b24SSimon Glass 	"commands to get and boot from pxe files",
16362e192b24SSimon Glass 	"get - try to retrieve a pxe file using tftp\npxe "
16372e192b24SSimon Glass 	"boot [pxefile_addr_r] - boot from the pxe file at pxefile_addr_r\n"
16382e192b24SSimon Glass );
16392e192b24SSimon Glass #endif
16402e192b24SSimon Glass 
16412e192b24SSimon Glass /*
16422e192b24SSimon Glass  * Boots a system using a local disk syslinux/extlinux file
16432e192b24SSimon Glass  *
16442e192b24SSimon Glass  * Returns 0 on success, 1 on error.
16452e192b24SSimon Glass  */
do_sysboot(cmd_tbl_t * cmdtp,int flag,int argc,char * const argv[])16462e192b24SSimon Glass static int do_sysboot(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
16472e192b24SSimon Glass {
16482e192b24SSimon Glass 	unsigned long pxefile_addr_r;
16492e192b24SSimon Glass 	struct pxe_menu *cfg;
16502e192b24SSimon Glass 	char *pxefile_addr_str;
16512e192b24SSimon Glass 	char *filename;
16522e192b24SSimon Glass 	int prompt = 0;
16532e192b24SSimon Glass 
16542e192b24SSimon Glass 	is_pxe = false;
16552e192b24SSimon Glass 
16562e192b24SSimon Glass 	if (argc > 1 && strstr(argv[1], "-p")) {
16572e192b24SSimon Glass 		prompt = 1;
16582e192b24SSimon Glass 		argc--;
16592e192b24SSimon Glass 		argv++;
16602e192b24SSimon Glass 	}
16612e192b24SSimon Glass 
16622e192b24SSimon Glass 	if (argc < 4)
16632e192b24SSimon Glass 		return cmd_usage(cmdtp);
16642e192b24SSimon Glass 
16652e192b24SSimon Glass 	if (argc < 5) {
16662e192b24SSimon Glass 		pxefile_addr_str = from_env("pxefile_addr_r");
16672e192b24SSimon Glass 		if (!pxefile_addr_str)
16682e192b24SSimon Glass 			return 1;
16692e192b24SSimon Glass 	} else {
16702e192b24SSimon Glass 		pxefile_addr_str = argv[4];
16712e192b24SSimon Glass 	}
16722e192b24SSimon Glass 
16732e192b24SSimon Glass 	if (argc < 6)
167400caae6dSSimon Glass 		filename = env_get("bootfile");
16752e192b24SSimon Glass 	else {
16762e192b24SSimon Glass 		filename = argv[5];
1677382bee57SSimon Glass 		env_set("bootfile", filename);
16782e192b24SSimon Glass 	}
16792e192b24SSimon Glass 
16802e192b24SSimon Glass 	if (strstr(argv[3], "ext2"))
16812e192b24SSimon Glass 		do_getfile = do_get_ext2;
16822e192b24SSimon Glass 	else if (strstr(argv[3], "fat"))
16832e192b24SSimon Glass 		do_getfile = do_get_fat;
16842e192b24SSimon Glass 	else if (strstr(argv[3], "any"))
16852e192b24SSimon Glass 		do_getfile = do_get_any;
16862e192b24SSimon Glass 	else {
16872e192b24SSimon Glass 		printf("Invalid filesystem: %s\n", argv[3]);
16882e192b24SSimon Glass 		return 1;
16892e192b24SSimon Glass 	}
16902e192b24SSimon Glass 	fs_argv[1] = argv[1];
16912e192b24SSimon Glass 	fs_argv[2] = argv[2];
16922e192b24SSimon Glass 
16932e192b24SSimon Glass 	if (strict_strtoul(pxefile_addr_str, 16, &pxefile_addr_r) < 0) {
16942e192b24SSimon Glass 		printf("Invalid pxefile address: %s\n", pxefile_addr_str);
16952e192b24SSimon Glass 		return 1;
16962e192b24SSimon Glass 	}
16972e192b24SSimon Glass 
16982e192b24SSimon Glass 	if (get_pxe_file(cmdtp, filename, pxefile_addr_r) < 0) {
16992e192b24SSimon Glass 		printf("Error reading config file\n");
17002e192b24SSimon Glass 		return 1;
17012e192b24SSimon Glass 	}
17022e192b24SSimon Glass 
17032e192b24SSimon Glass 	cfg = parse_pxefile(cmdtp, pxefile_addr_r);
17042e192b24SSimon Glass 
17052e192b24SSimon Glass 	if (cfg == NULL) {
17062e192b24SSimon Glass 		printf("Error parsing config file\n");
17072e192b24SSimon Glass 		return 1;
17082e192b24SSimon Glass 	}
17092e192b24SSimon Glass 
17102e192b24SSimon Glass 	if (prompt)
17112e192b24SSimon Glass 		cfg->prompt = 1;
17122e192b24SSimon Glass 
17132e192b24SSimon Glass 	handle_pxe_menu(cmdtp, cfg);
17142e192b24SSimon Glass 
17152e192b24SSimon Glass 	destroy_pxe_menu(cfg);
17162e192b24SSimon Glass 
17172e192b24SSimon Glass 	return 0;
17182e192b24SSimon Glass }
17192e192b24SSimon Glass 
17202e192b24SSimon Glass U_BOOT_CMD(
17212e192b24SSimon Glass 	sysboot, 7, 1, do_sysboot,
17222e192b24SSimon Glass 	"command to get and boot from syslinux files",
17232e192b24SSimon Glass 	"[-p] <interface> <dev[:part]> <ext2|fat|any> [addr] [filename]\n"
17242e192b24SSimon Glass 	"    - load and parse syslinux menu file 'filename' from ext2, fat\n"
17252e192b24SSimon Glass 	"      or any filesystem on 'dev' on 'interface' to address 'addr'"
17262e192b24SSimon Glass );
1727