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