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 = getenv(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_getenv_enetaddr_by_index("eth", eth_get_dev_index(),
74 ethaddr))
75 return -ENOENT;
76
77 sprintf(outbuf, "01-%02x-%02x-%02x-%02x-%02x-%02x",
78 ethaddr[0], ethaddr[1], ethaddr[2],
79 ethaddr[3], ethaddr[4], ethaddr[5]);
80
81 return 1;
82}
83#endif
84
85
86
87
88
89
90static int get_bootfile_path(const char *file_path, char *bootfile_path,
91 size_t bootfile_path_size)
92{
93 char *bootfile, *last_slash;
94 size_t path_len = 0;
95
96
97 if (file_path[0] == '/' && !is_pxe)
98 goto ret;
99
100 bootfile = from_env("bootfile");
101
102 if (!bootfile)
103 goto ret;
104
105 last_slash = strrchr(bootfile, '/');
106
107 if (last_slash == NULL)
108 goto ret;
109
110 path_len = (last_slash - bootfile) + 1;
111
112 if (bootfile_path_size < path_len) {
113 printf("bootfile_path too small. (%zd < %zd)\n",
114 bootfile_path_size, path_len);
115
116 return -1;
117 }
118
119 strncpy(bootfile_path, bootfile, path_len);
120
121 ret:
122 bootfile_path[path_len] = '\0';
123
124 return 1;
125}
126
127static int (*do_getfile)(cmd_tbl_t *cmdtp, const char *file_path, char *file_addr);
128
129#ifdef CONFIG_CMD_NET
130static int do_get_tftp(cmd_tbl_t *cmdtp, const char *file_path, char *file_addr)
131{
132 char *tftp_argv[] = {"tftp", NULL, NULL, NULL};
133
134 tftp_argv[1] = file_addr;
135 tftp_argv[2] = (void *)file_path;
136
137 if (do_tftpb(cmdtp, 0, 3, tftp_argv))
138 return -ENOENT;
139
140 return 1;
141}
142#endif
143
144static char *fs_argv[5];
145
146static int do_get_ext2(cmd_tbl_t *cmdtp, const char *file_path, char *file_addr)
147{
148#ifdef CONFIG_CMD_EXT2
149 fs_argv[0] = "ext2load";
150 fs_argv[3] = file_addr;
151 fs_argv[4] = (void *)file_path;
152
153 if (!do_ext2load(cmdtp, 0, 5, fs_argv))
154 return 1;
155#endif
156 return -ENOENT;
157}
158
159static int do_get_fat(cmd_tbl_t *cmdtp, const char *file_path, char *file_addr)
160{
161#ifdef CONFIG_CMD_FAT
162 fs_argv[0] = "fatload";
163 fs_argv[3] = file_addr;
164 fs_argv[4] = (void *)file_path;
165
166 if (!do_fat_fsload(cmdtp, 0, 5, fs_argv))
167 return 1;
168#endif
169 return -ENOENT;
170}
171
172static int do_get_any(cmd_tbl_t *cmdtp, const char *file_path, char *file_addr)
173{
174#ifdef CONFIG_CMD_FS_GENERIC
175 fs_argv[0] = "load";
176 fs_argv[3] = file_addr;
177 fs_argv[4] = (void *)file_path;
178
179 if (!do_load(cmdtp, 0, 5, fs_argv, FS_TYPE_ANY))
180 return 1;
181#endif
182 return -ENOENT;
183}
184
185
186
187
188
189
190
191
192
193static int get_relfile(cmd_tbl_t *cmdtp, const char *file_path,
194 unsigned long file_addr)
195{
196 size_t path_len;
197 char relfile[MAX_TFTP_PATH_LEN+1];
198 char addr_buf[18];
199 int err;
200
201 err = get_bootfile_path(file_path, relfile, sizeof(relfile));
202
203 if (err < 0)
204 return err;
205
206 path_len = strlen(file_path);
207 path_len += strlen(relfile);
208
209 if (path_len > MAX_TFTP_PATH_LEN) {
210 printf("Base path too long (%s%s)\n",
211 relfile,
212 file_path);
213
214 return -ENAMETOOLONG;
215 }
216
217 strcat(relfile, file_path);
218
219 printf("Retrieving file: %s\n", relfile);
220
221 sprintf(addr_buf, "%lx", file_addr);
222
223 return do_getfile(cmdtp, relfile, addr_buf);
224}
225
226
227
228
229
230
231
232
233static int get_pxe_file(cmd_tbl_t *cmdtp, const char *file_path,
234 unsigned long file_addr)
235{
236 unsigned long config_file_size;
237 char *tftp_filesize;
238 int err;
239 char *buf;
240
241 err = get_relfile(cmdtp, file_path, file_addr);
242
243 if (err < 0)
244 return err;
245
246
247
248
249
250 tftp_filesize = from_env("filesize");
251
252 if (!tftp_filesize)
253 return -ENOENT;
254
255 if (strict_strtoul(tftp_filesize, 16, &config_file_size) < 0)
256 return -EINVAL;
257
258 buf = map_sysmem(file_addr + config_file_size, 1);
259 *buf = '\0';
260 unmap_sysmem(buf);
261
262 return 1;
263}
264
265#ifdef CONFIG_CMD_NET
266
267#define PXELINUX_DIR "pxelinux.cfg/"
268
269
270
271
272
273
274
275
276static int get_pxelinux_path(cmd_tbl_t *cmdtp, const char *file,
277 unsigned long pxefile_addr_r)
278{
279 size_t base_len = strlen(PXELINUX_DIR);
280 char path[MAX_TFTP_PATH_LEN+1];
281
282 if (base_len + strlen(file) > MAX_TFTP_PATH_LEN) {
283 printf("path (%s%s) too long, skipping\n",
284 PXELINUX_DIR, file);
285 return -ENAMETOOLONG;
286 }
287
288 sprintf(path, PXELINUX_DIR "%s", file);
289
290 return get_pxe_file(cmdtp, path, pxefile_addr_r);
291}
292
293
294
295
296
297
298static int pxe_uuid_path(cmd_tbl_t *cmdtp, unsigned long pxefile_addr_r)
299{
300 char *uuid_str;
301
302 uuid_str = from_env("pxeuuid");
303
304 if (!uuid_str)
305 return -ENOENT;
306
307 return get_pxelinux_path(cmdtp, uuid_str, pxefile_addr_r);
308}
309
310
311
312
313
314
315
316static int pxe_mac_path(cmd_tbl_t *cmdtp, unsigned long pxefile_addr_r)
317{
318 char mac_str[21];
319 int err;
320
321 err = format_mac_pxe(mac_str, sizeof(mac_str));
322
323 if (err < 0)
324 return err;
325
326 return get_pxelinux_path(cmdtp, mac_str, pxefile_addr_r);
327}
328
329
330
331
332
333
334
335
336static int pxe_ipaddr_paths(cmd_tbl_t *cmdtp, unsigned long pxefile_addr_r)
337{
338 char ip_addr[9];
339 int mask_pos, err;
340
341 sprintf(ip_addr, "%08X", ntohl(net_ip.s_addr));
342
343 for (mask_pos = 7; mask_pos >= 0; mask_pos--) {
344 err = get_pxelinux_path(cmdtp, ip_addr, pxefile_addr_r);
345
346 if (err > 0)
347 return err;
348
349 ip_addr[mask_pos] = '\0';
350 }
351
352 return -ENOENT;
353}
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369static int
370do_pxe_get(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
371{
372 char *pxefile_addr_str;
373 unsigned long pxefile_addr_r;
374 int err, i = 0;
375
376 do_getfile = do_get_tftp;
377
378 if (argc != 1)
379 return CMD_RET_USAGE;
380
381 pxefile_addr_str = from_env("pxefile_addr_r");
382
383 if (!pxefile_addr_str)
384 return 1;
385
386 err = strict_strtoul(pxefile_addr_str, 16,
387 (unsigned long *)&pxefile_addr_r);
388 if (err < 0)
389 return 1;
390
391
392
393
394
395 if (pxe_uuid_path(cmdtp, pxefile_addr_r) > 0 ||
396 pxe_mac_path(cmdtp, pxefile_addr_r) > 0 ||
397 pxe_ipaddr_paths(cmdtp, pxefile_addr_r) > 0) {
398 printf("Config file found\n");
399
400 return 0;
401 }
402
403 while (pxe_default_paths[i]) {
404 if (get_pxelinux_path(cmdtp, pxe_default_paths[i],
405 pxefile_addr_r) > 0) {
406 printf("Config file found\n");
407 return 0;
408 }
409 i++;
410 }
411
412 printf("Config file not found\n");
413
414 return 1;
415}
416#endif
417
418
419
420
421
422
423
424
425static int get_relfile_envaddr(cmd_tbl_t *cmdtp, const char *file_path, const char *envaddr_name)
426{
427 unsigned long file_addr;
428 char *envaddr;
429
430 envaddr = from_env(envaddr_name);
431
432 if (!envaddr)
433 return -ENOENT;
434
435 if (strict_strtoul(envaddr, 16, &file_addr) < 0)
436 return -EINVAL;
437
438 return get_relfile(cmdtp, file_path, file_addr);
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
470
471struct pxe_label {
472 char num[4];
473 char *name;
474 char *menu;
475 char *kernel;
476 char *append;
477 char *initrd;
478 char *fdt;
479 char *fdtdir;
480 int ipappend;
481 int attempted;
482 int localboot;
483 int localboot_val;
484 struct list_head list;
485};
486
487
488
489
490
491
492
493
494
495
496
497
498
499struct pxe_menu {
500 char *title;
501 char *default_label;
502 int timeout;
503 int prompt;
504 struct list_head labels;
505};
506
507
508
509
510
511
512
513static struct pxe_label *label_create(void)
514{
515 struct pxe_label *label;
516
517 label = malloc(sizeof(struct pxe_label));
518
519 if (!label)
520 return NULL;
521
522 memset(label, 0, sizeof(struct pxe_label));
523
524 return label;
525}
526
527
528
529
530
531
532
533
534
535static void label_destroy(struct pxe_label *label)
536{
537 if (label->name)
538 free(label->name);
539
540 if (label->kernel)
541 free(label->kernel);
542
543 if (label->append)
544 free(label->append);
545
546 if (label->initrd)
547 free(label->initrd);
548
549 if (label->fdt)
550 free(label->fdt);
551
552 if (label->fdtdir)
553 free(label->fdtdir);
554
555 free(label);
556}
557
558
559
560
561
562
563
564static void label_print(void *data)
565{
566 struct pxe_label *label = data;
567 const char *c = label->menu ? label->menu : label->name;
568
569 printf("%s:\t%s\n", label->num, c);
570}
571
572
573
574
575
576
577
578
579
580
581static int label_localboot(struct pxe_label *label)
582{
583 char *localcmd;
584
585 localcmd = from_env("localcmd");
586
587 if (!localcmd)
588 return -ENOENT;
589
590 if (label->append) {
591 char bootargs[CONFIG_SYS_CBSIZE];
592
593 cli_simple_process_macros(label->append, bootargs);
594 setenv("bootargs", bootargs);
595 }
596
597 debug("running: %s\n", localcmd);
598
599 return run_command_list(localcmd, strlen(localcmd), 0);
600}
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617static int label_boot(cmd_tbl_t *cmdtp, struct pxe_label *label)
618{
619 char *bootm_argv[] = { "bootm", NULL, NULL, NULL, NULL };
620 char initrd_str[22];
621 char mac_str[29] = "";
622 char ip_str[68] = "";
623 int bootm_argc = 2;
624 int len = 0;
625 ulong kernel_addr;
626 void *buf;
627
628 label_print(label);
629
630 label->attempted = 1;
631
632 if (label->localboot) {
633 if (label->localboot_val >= 0)
634 label_localboot(label);
635 return 0;
636 }
637
638 if (label->kernel == NULL) {
639 printf("No kernel given, skipping %s\n",
640 label->name);
641 return 1;
642 }
643
644 if (label->initrd) {
645 if (get_relfile_envaddr(cmdtp, label->initrd, "ramdisk_addr_r") < 0) {
646 printf("Skipping %s for failure retrieving initrd\n",
647 label->name);
648 return 1;
649 }
650
651 bootm_argv[2] = initrd_str;
652 strcpy(bootm_argv[2], getenv("ramdisk_addr_r"));
653 strcat(bootm_argv[2], ":");
654 strcat(bootm_argv[2], getenv("filesize"));
655 }
656
657 if (get_relfile_envaddr(cmdtp, label->kernel, "kernel_addr_r") < 0) {
658 printf("Skipping %s for failure retrieving kernel\n",
659 label->name);
660 return 1;
661 }
662
663 if (label->ipappend & 0x1) {
664 sprintf(ip_str, " ip=%s:%s:%s:%s",
665 getenv("ipaddr"), getenv("serverip"),
666 getenv("gatewayip"), getenv("netmask"));
667 }
668
669#ifdef CONFIG_CMD_NET
670 if (label->ipappend & 0x2) {
671 int err;
672 strcpy(mac_str, " BOOTIF=");
673 err = format_mac_pxe(mac_str + 8, sizeof(mac_str) - 8);
674 if (err < 0)
675 mac_str[0] = '\0';
676 }
677#endif
678
679 if ((label->ipappend & 0x3) || label->append) {
680 char bootargs[CONFIG_SYS_CBSIZE] = "";
681 char finalbootargs[CONFIG_SYS_CBSIZE];
682
683 if (strlen(label->append ?: "") +
684 strlen(ip_str) + strlen(mac_str) + 1 > sizeof(bootargs)) {
685 printf("bootarg overflow %zd+%zd+%zd+1 > %zd\n",
686 strlen(label->append ?: ""),
687 strlen(ip_str), strlen(mac_str),
688 sizeof(bootargs));
689 return 1;
690 }
691
692 if (label->append)
693 strcpy(bootargs, label->append);
694 strcat(bootargs, ip_str);
695 strcat(bootargs, mac_str);
696
697 cli_simple_process_macros(bootargs, finalbootargs);
698 setenv("bootargs", finalbootargs);
699 printf("append: %s\n", finalbootargs);
700 }
701
702 bootm_argv[1] = getenv("kernel_addr_r");
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717 bootm_argv[3] = getenv("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 = getenv("fdtfile");
730 if (f1) {
731 f2 = "";
732 f3 = "";
733 f4 = "";
734 } else {
735
736
737
738
739
740
741
742 f1 = getenv("soc");
743 f2 = "-";
744 f3 = getenv("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] = getenv("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 = getenv("bootfile");
1675 else {
1676 filename = argv[5];
1677 setenv("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