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