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