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