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