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 = 3;
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 } else {
656 bootm_argv[2] = "-";
657 }
658
659 if (get_relfile_envaddr(cmdtp, label->kernel, "kernel_addr_r") < 0) {
660 printf("Skipping %s for failure retrieving kernel\n",
661 label->name);
662 return 1;
663 }
664
665 if (label->ipappend & 0x1) {
666 sprintf(ip_str, " ip=%s:%s:%s:%s",
667 getenv("ipaddr"), getenv("serverip"),
668 getenv("gatewayip"), getenv("netmask"));
669 }
670
671#ifdef CONFIG_CMD_NET
672 if (label->ipappend & 0x2) {
673 int err;
674 strcpy(mac_str, " BOOTIF=");
675 err = format_mac_pxe(mac_str + 8, sizeof(mac_str) - 8);
676 if (err < 0)
677 mac_str[0] = '\0';
678 }
679#endif
680
681 if ((label->ipappend & 0x3) || label->append) {
682 char bootargs[CONFIG_SYS_CBSIZE] = "";
683 char finalbootargs[CONFIG_SYS_CBSIZE];
684
685 if (strlen(label->append ?: "") +
686 strlen(ip_str) + strlen(mac_str) + 1 > sizeof(bootargs)) {
687 printf("bootarg overflow %zd+%zd+%zd+1 > %zd\n",
688 strlen(label->append ?: ""),
689 strlen(ip_str), strlen(mac_str),
690 sizeof(bootargs));
691 return 1;
692 }
693
694 if (label->append)
695 strcpy(bootargs, label->append);
696 strcat(bootargs, ip_str);
697 strcat(bootargs, mac_str);
698
699 cli_simple_process_macros(bootargs, finalbootargs);
700 setenv("bootargs", finalbootargs);
701 printf("append: %s\n", finalbootargs);
702 }
703
704 bootm_argv[1] = getenv("kernel_addr_r");
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719 bootm_argv[3] = getenv("fdt_addr_r");
720
721
722 if (bootm_argv[3]) {
723 char *fdtfile = NULL;
724 char *fdtfilefree = NULL;
725
726 if (label->fdt) {
727 fdtfile = label->fdt;
728 } else if (label->fdtdir) {
729 char *f1, *f2, *f3, *f4, *slash;
730
731 f1 = getenv("fdtfile");
732 if (f1) {
733 f2 = "";
734 f3 = "";
735 f4 = "";
736 } else {
737
738
739
740
741
742
743
744 f1 = getenv("soc");
745 f2 = "-";
746 f3 = getenv("board");
747 f4 = ".dtb";
748 }
749
750 len = strlen(label->fdtdir);
751 if (!len)
752 slash = "./";
753 else if (label->fdtdir[len - 1] != '/')
754 slash = "/";
755 else
756 slash = "";
757
758 len = strlen(label->fdtdir) + strlen(slash) +
759 strlen(f1) + strlen(f2) + strlen(f3) +
760 strlen(f4) + 1;
761 fdtfilefree = malloc(len);
762 if (!fdtfilefree) {
763 printf("malloc fail (FDT filename)\n");
764 return 1;
765 }
766
767 snprintf(fdtfilefree, len, "%s%s%s%s%s%s",
768 label->fdtdir, slash, f1, f2, f3, f4);
769 fdtfile = fdtfilefree;
770 }
771
772 if (fdtfile) {
773 int err = get_relfile_envaddr(cmdtp, fdtfile, "fdt_addr_r");
774 free(fdtfilefree);
775 if (err < 0) {
776 printf("Skipping %s for failure retrieving fdt\n",
777 label->name);
778 return 1;
779 }
780 } else {
781 bootm_argv[3] = NULL;
782 }
783 }
784
785 if (!bootm_argv[3])
786 bootm_argv[3] = getenv("fdt_addr");
787
788 if (bootm_argv[3])
789 bootm_argc = 4;
790
791 kernel_addr = genimg_get_kernel_addr(bootm_argv[1]);
792 buf = map_sysmem(kernel_addr, 0);
793
794 if (genimg_get_format(buf) != IMAGE_FORMAT_INVALID)
795 do_bootm(cmdtp, 0, bootm_argc, bootm_argv);
796#ifdef CONFIG_CMD_BOOTI
797
798 else
799 do_booti(cmdtp, 0, bootm_argc, bootm_argv);
800#elif defined(CONFIG_CMD_BOOTZ)
801
802 else
803 do_bootz(cmdtp, 0, bootm_argc, bootm_argv);
804#endif
805 unmap_sysmem(buf);
806 return 1;
807}
808
809
810
811
812enum token_type {
813 T_EOL,
814 T_STRING,
815 T_EOF,
816 T_MENU,
817 T_TITLE,
818 T_TIMEOUT,
819 T_LABEL,
820 T_KERNEL,
821 T_LINUX,
822 T_APPEND,
823 T_INITRD,
824 T_LOCALBOOT,
825 T_DEFAULT,
826 T_PROMPT,
827 T_INCLUDE,
828 T_FDT,
829 T_FDTDIR,
830 T_ONTIMEOUT,
831 T_IPAPPEND,
832 T_INVALID
833};
834
835
836
837
838struct token {
839 char *val;
840 enum token_type type;
841};
842
843
844
845
846static const struct token keywords[] = {
847 {"menu", T_MENU},
848 {"title", T_TITLE},
849 {"timeout", T_TIMEOUT},
850 {"default", T_DEFAULT},
851 {"prompt", T_PROMPT},
852 {"label", T_LABEL},
853 {"kernel", T_KERNEL},
854 {"linux", T_LINUX},
855 {"localboot", T_LOCALBOOT},
856 {"append", T_APPEND},
857 {"initrd", T_INITRD},
858 {"include", T_INCLUDE},
859 {"devicetree", T_FDT},
860 {"fdt", T_FDT},
861 {"devicetreedir", T_FDTDIR},
862 {"fdtdir", T_FDTDIR},
863 {"ontimeout", T_ONTIMEOUT,},
864 {"ipappend", T_IPAPPEND,},
865 {NULL, T_INVALID}
866};
867
868
869
870
871
872
873enum lex_state {
874 L_NORMAL = 0,
875 L_KEYWORD,
876 L_SLITERAL
877};
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900static char *get_string(char **p, struct token *t, char delim, int lower)
901{
902 char *b, *e;
903 size_t len, i;
904
905
906
907
908
909
910
911 b = e = *p;
912
913 while (*e) {
914 if ((delim == ' ' && isspace(*e)) || delim == *e)
915 break;
916 e++;
917 }
918
919 len = e - b;
920
921
922
923
924
925 t->val = malloc(len + 1);
926 if (!t->val)
927 return NULL;
928
929 for (i = 0; i < len; i++, b++) {
930 if (lower)
931 t->val[i] = tolower(*b);
932 else
933 t->val[i] = *b;
934 }
935
936 t->val[len] = '\0';
937
938
939
940
941 *p = e;
942
943 t->type = T_STRING;
944
945 return t->val;
946}
947
948
949
950
951static void get_keyword(struct token *t)
952{
953 int i;
954
955 for (i = 0; keywords[i].val; i++) {
956 if (!strcmp(t->val, keywords[i].val)) {
957 t->type = keywords[i].type;
958 break;
959 }
960 }
961}
962
963
964
965
966
967
968
969static void get_token(char **p, struct token *t, enum lex_state state)
970{
971 char *c = *p;
972
973 t->type = T_INVALID;
974
975
976 while (isblank(*c))
977 c++;
978
979
980
981
982
983 if (*c == '#') {
984 while (*c && *c != '\n')
985 c++;
986 }
987
988 if (*c == '\n') {
989 t->type = T_EOL;
990 c++;
991 } else if (*c == '\0') {
992 t->type = T_EOF;
993 c++;
994 } else if (state == L_SLITERAL) {
995 get_string(&c, t, '\n', 0);
996 } else if (state == L_KEYWORD) {
997
998
999
1000
1001
1002
1003
1004 get_string(&c, t, ' ', 1);
1005 get_keyword(t);
1006 }
1007
1008 *p = c;
1009}
1010
1011
1012
1013
1014static void eol_or_eof(char **c)
1015{
1016 while (**c && **c != '\n')
1017 (*c)++;
1018}
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031static int parse_sliteral(char **c, char **dst)
1032{
1033 struct token t;
1034 char *s = *c;
1035
1036 get_token(c, &t, L_SLITERAL);
1037
1038 if (t.type != T_STRING) {
1039 printf("Expected string literal: %.*s\n", (int)(*c - s), s);
1040 return -EINVAL;
1041 }
1042
1043 *dst = t.val;
1044
1045 return 1;
1046}
1047
1048
1049
1050
1051static int parse_integer(char **c, int *dst)
1052{
1053 struct token t;
1054 char *s = *c;
1055
1056 get_token(c, &t, L_SLITERAL);
1057
1058 if (t.type != T_STRING) {
1059 printf("Expected string: %.*s\n", (int)(*c - s), s);
1060 return -EINVAL;
1061 }
1062
1063 *dst = simple_strtol(t.val, NULL, 10);
1064
1065 free(t.val);
1066
1067 return 1;
1068}
1069
1070static int parse_pxefile_top(cmd_tbl_t *cmdtp, char *p, unsigned long base,
1071 struct pxe_menu *cfg, int nest_level);
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081static int handle_include(cmd_tbl_t *cmdtp, char **c, unsigned long base,
1082 struct pxe_menu *cfg, int nest_level)
1083{
1084 char *include_path;
1085 char *s = *c;
1086 int err;
1087 char *buf;
1088 int ret;
1089
1090 err = parse_sliteral(c, &include_path);
1091
1092 if (err < 0) {
1093 printf("Expected include path: %.*s\n",
1094 (int)(*c - s), s);
1095 return err;
1096 }
1097
1098 err = get_pxe_file(cmdtp, include_path, base);
1099
1100 if (err < 0) {
1101 printf("Couldn't retrieve %s\n", include_path);
1102 return err;
1103 }
1104
1105 buf = map_sysmem(base, 0);
1106 ret = parse_pxefile_top(cmdtp, buf, base, cfg, nest_level);
1107 unmap_sysmem(buf);
1108
1109 return ret;
1110}
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122static int parse_menu(cmd_tbl_t *cmdtp, char **c, struct pxe_menu *cfg,
1123 unsigned long base, int nest_level)
1124{
1125 struct token t;
1126 char *s = *c;
1127 int err = 0;
1128
1129 get_token(c, &t, L_KEYWORD);
1130
1131 switch (t.type) {
1132 case T_TITLE:
1133 err = parse_sliteral(c, &cfg->title);
1134
1135 break;
1136
1137 case T_INCLUDE:
1138 err = handle_include(cmdtp, c, base, cfg,
1139 nest_level + 1);
1140 break;
1141
1142 default:
1143 printf("Ignoring malformed menu command: %.*s\n",
1144 (int)(*c - s), s);
1145 }
1146
1147 if (err < 0)
1148 return err;
1149
1150 eol_or_eof(c);
1151
1152 return 1;
1153}
1154
1155
1156
1157
1158static int parse_label_menu(char **c, struct pxe_menu *cfg,
1159 struct pxe_label *label)
1160{
1161 struct token t;
1162 char *s;
1163
1164 s = *c;
1165
1166 get_token(c, &t, L_KEYWORD);
1167
1168 switch (t.type) {
1169 case T_DEFAULT:
1170 if (!cfg->default_label)
1171 cfg->default_label = strdup(label->name);
1172
1173 if (!cfg->default_label)
1174 return -ENOMEM;
1175
1176 break;
1177 case T_LABEL:
1178 parse_sliteral(c, &label->menu);
1179 break;
1180 default:
1181 printf("Ignoring malformed menu command: %.*s\n",
1182 (int)(*c - s), s);
1183 }
1184
1185 eol_or_eof(c);
1186
1187 return 0;
1188}
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198static int parse_label(char **c, struct pxe_menu *cfg)
1199{
1200 struct token t;
1201 int len;
1202 char *s = *c;
1203 struct pxe_label *label;
1204 int err;
1205
1206 label = label_create();
1207 if (!label)
1208 return -ENOMEM;
1209
1210 err = parse_sliteral(c, &label->name);
1211 if (err < 0) {
1212 printf("Expected label name: %.*s\n", (int)(*c - s), s);
1213 label_destroy(label);
1214 return -EINVAL;
1215 }
1216
1217 list_add_tail(&label->list, &cfg->labels);
1218
1219 while (1) {
1220 s = *c;
1221 get_token(c, &t, L_KEYWORD);
1222
1223 err = 0;
1224 switch (t.type) {
1225 case T_MENU:
1226 err = parse_label_menu(c, cfg, label);
1227 break;
1228
1229 case T_KERNEL:
1230 case T_LINUX:
1231 err = parse_sliteral(c, &label->kernel);
1232 break;
1233
1234 case T_APPEND:
1235 err = parse_sliteral(c, &label->append);
1236 if (label->initrd)
1237 break;
1238 s = strstr(label->append, "initrd=");
1239 if (!s)
1240 break;
1241 s += 7;
1242 len = (int)(strchr(s, ' ') - s);
1243 label->initrd = malloc(len + 1);
1244 strncpy(label->initrd, s, len);
1245 label->initrd[len] = '\0';
1246
1247 break;
1248
1249 case T_INITRD:
1250 if (!label->initrd)
1251 err = parse_sliteral(c, &label->initrd);
1252 break;
1253
1254 case T_FDT:
1255 if (!label->fdt)
1256 err = parse_sliteral(c, &label->fdt);
1257 break;
1258
1259 case T_FDTDIR:
1260 if (!label->fdtdir)
1261 err = parse_sliteral(c, &label->fdtdir);
1262 break;
1263
1264 case T_LOCALBOOT:
1265 label->localboot = 1;
1266 err = parse_integer(c, &label->localboot_val);
1267 break;
1268
1269 case T_IPAPPEND:
1270 err = parse_integer(c, &label->ipappend);
1271 break;
1272
1273 case T_EOL:
1274 break;
1275
1276 default:
1277
1278
1279
1280
1281
1282 *c = s;
1283 return 1;
1284 }
1285
1286 if (err < 0)
1287 return err;
1288 }
1289}
1290
1291
1292
1293
1294
1295
1296
1297#define MAX_NEST_LEVEL 16
1298
1299
1300
1301
1302
1303
1304
1305static int parse_pxefile_top(cmd_tbl_t *cmdtp, char *p, unsigned long base,
1306 struct pxe_menu *cfg, int nest_level)
1307{
1308 struct token t;
1309 char *s, *b, *label_name;
1310 int err;
1311
1312 b = p;
1313
1314 if (nest_level > MAX_NEST_LEVEL) {
1315 printf("Maximum nesting (%d) exceeded\n", MAX_NEST_LEVEL);
1316 return -EMLINK;
1317 }
1318
1319 while (1) {
1320 s = p;
1321
1322 get_token(&p, &t, L_KEYWORD);
1323
1324 err = 0;
1325 switch (t.type) {
1326 case T_MENU:
1327 cfg->prompt = 1;
1328 err = parse_menu(cmdtp, &p, cfg,
1329 base + ALIGN(strlen(b) + 1, 4),
1330 nest_level);
1331 break;
1332
1333 case T_TIMEOUT:
1334 err = parse_integer(&p, &cfg->timeout);
1335 break;
1336
1337 case T_LABEL:
1338 err = parse_label(&p, cfg);
1339 break;
1340
1341 case T_DEFAULT:
1342 case T_ONTIMEOUT:
1343 err = parse_sliteral(&p, &label_name);
1344
1345 if (label_name) {
1346 if (cfg->default_label)
1347 free(cfg->default_label);
1348
1349 cfg->default_label = label_name;
1350 }
1351
1352 break;
1353
1354 case T_INCLUDE:
1355 err = handle_include(cmdtp, &p,
1356 base + ALIGN(strlen(b), 4), cfg,
1357 nest_level + 1);
1358 break;
1359
1360 case T_PROMPT:
1361 eol_or_eof(&p);
1362 break;
1363
1364 case T_EOL:
1365 break;
1366
1367 case T_EOF:
1368 return 1;
1369
1370 default:
1371 printf("Ignoring unknown command: %.*s\n",
1372 (int)(p - s), s);
1373 eol_or_eof(&p);
1374 }
1375
1376 if (err < 0)
1377 return err;
1378 }
1379}
1380
1381
1382
1383
1384static void destroy_pxe_menu(struct pxe_menu *cfg)
1385{
1386 struct list_head *pos, *n;
1387 struct pxe_label *label;
1388
1389 if (cfg->title)
1390 free(cfg->title);
1391
1392 if (cfg->default_label)
1393 free(cfg->default_label);
1394
1395 list_for_each_safe(pos, n, &cfg->labels) {
1396 label = list_entry(pos, struct pxe_label, list);
1397
1398 label_destroy(label);
1399 }
1400
1401 free(cfg);
1402}
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413static struct pxe_menu *parse_pxefile(cmd_tbl_t *cmdtp, unsigned long menucfg)
1414{
1415 struct pxe_menu *cfg;
1416 char *buf;
1417 int r;
1418
1419 cfg = malloc(sizeof(struct pxe_menu));
1420
1421 if (!cfg)
1422 return NULL;
1423
1424 memset(cfg, 0, sizeof(struct pxe_menu));
1425
1426 INIT_LIST_HEAD(&cfg->labels);
1427
1428 buf = map_sysmem(menucfg, 0);
1429 r = parse_pxefile_top(cmdtp, buf, menucfg, cfg, 1);
1430 unmap_sysmem(buf);
1431
1432 if (r < 0) {
1433 destroy_pxe_menu(cfg);
1434 return NULL;
1435 }
1436
1437 return cfg;
1438}
1439
1440
1441
1442
1443
1444static struct menu *pxe_menu_to_menu(struct pxe_menu *cfg)
1445{
1446 struct pxe_label *label;
1447 struct list_head *pos;
1448 struct menu *m;
1449 int err;
1450 int i = 1;
1451 char *default_num = NULL;
1452
1453
1454
1455
1456 m = menu_create(cfg->title, cfg->timeout, cfg->prompt, label_print,
1457 NULL, NULL);
1458
1459 if (!m)
1460 return NULL;
1461
1462 list_for_each(pos, &cfg->labels) {
1463 label = list_entry(pos, struct pxe_label, list);
1464
1465 sprintf(label->num, "%d", i++);
1466 if (menu_item_add(m, label->num, label) != 1) {
1467 menu_destroy(m);
1468 return NULL;
1469 }
1470 if (cfg->default_label &&
1471 (strcmp(label->name, cfg->default_label) == 0))
1472 default_num = label->num;
1473
1474 }
1475
1476
1477
1478
1479
1480 if (default_num) {
1481 err = menu_default_set(m, default_num);
1482 if (err != 1) {
1483 if (err != -ENOENT) {
1484 menu_destroy(m);
1485 return NULL;
1486 }
1487
1488 printf("Missing default: %s\n", cfg->default_label);
1489 }
1490 }
1491
1492 return m;
1493}
1494
1495
1496
1497
1498static void boot_unattempted_labels(cmd_tbl_t *cmdtp, struct pxe_menu *cfg)
1499{
1500 struct list_head *pos;
1501 struct pxe_label *label;
1502
1503 list_for_each(pos, &cfg->labels) {
1504 label = list_entry(pos, struct pxe_label, list);
1505
1506 if (!label->attempted)
1507 label_boot(cmdtp, label);
1508 }
1509}
1510
1511
1512
1513
1514
1515
1516
1517
1518
1519
1520
1521
1522
1523static void handle_pxe_menu(cmd_tbl_t *cmdtp, struct pxe_menu *cfg)
1524{
1525 void *choice;
1526 struct menu *m;
1527 int err;
1528
1529 m = pxe_menu_to_menu(cfg);
1530 if (!m)
1531 return;
1532
1533 err = menu_get_choice(m, &choice);
1534
1535 menu_destroy(m);
1536
1537
1538
1539
1540
1541
1542
1543
1544
1545
1546
1547
1548 if (err == 1) {
1549 err = label_boot(cmdtp, choice);
1550 if (!err)
1551 return;
1552 } else if (err != -ENOENT) {
1553 return;
1554 }
1555
1556 boot_unattempted_labels(cmdtp, cfg);
1557}
1558
1559#ifdef CONFIG_CMD_NET
1560
1561
1562
1563
1564
1565static int
1566do_pxe_boot(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
1567{
1568 unsigned long pxefile_addr_r;
1569 struct pxe_menu *cfg;
1570 char *pxefile_addr_str;
1571
1572 do_getfile = do_get_tftp;
1573
1574 if (argc == 1) {
1575 pxefile_addr_str = from_env("pxefile_addr_r");
1576 if (!pxefile_addr_str)
1577 return 1;
1578
1579 } else if (argc == 2) {
1580 pxefile_addr_str = argv[1];
1581 } else {
1582 return CMD_RET_USAGE;
1583 }
1584
1585 if (strict_strtoul(pxefile_addr_str, 16, &pxefile_addr_r) < 0) {
1586 printf("Invalid pxefile address: %s\n", pxefile_addr_str);
1587 return 1;
1588 }
1589
1590 cfg = parse_pxefile(cmdtp, pxefile_addr_r);
1591
1592 if (cfg == NULL) {
1593 printf("Error parsing config file\n");
1594 return 1;
1595 }
1596
1597 handle_pxe_menu(cmdtp, cfg);
1598
1599 destroy_pxe_menu(cfg);
1600
1601 copy_filename(net_boot_file_name, "", sizeof(net_boot_file_name));
1602
1603 return 0;
1604}
1605
1606static cmd_tbl_t cmd_pxe_sub[] = {
1607 U_BOOT_CMD_MKENT(get, 1, 1, do_pxe_get, "", ""),
1608 U_BOOT_CMD_MKENT(boot, 2, 1, do_pxe_boot, "", "")
1609};
1610
1611static int do_pxe(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
1612{
1613 cmd_tbl_t *cp;
1614
1615 if (argc < 2)
1616 return CMD_RET_USAGE;
1617
1618 is_pxe = true;
1619
1620
1621 argc--;
1622 argv++;
1623
1624 cp = find_cmd_tbl(argv[0], cmd_pxe_sub, ARRAY_SIZE(cmd_pxe_sub));
1625
1626 if (cp)
1627 return cp->cmd(cmdtp, flag, argc, argv);
1628
1629 return CMD_RET_USAGE;
1630}
1631
1632U_BOOT_CMD(
1633 pxe, 3, 1, do_pxe,
1634 "commands to get and boot from pxe files",
1635 "get - try to retrieve a pxe file using tftp\npxe "
1636 "boot [pxefile_addr_r] - boot from the pxe file at pxefile_addr_r\n"
1637);
1638#endif
1639
1640
1641
1642
1643
1644
1645static int do_sysboot(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
1646{
1647 unsigned long pxefile_addr_r;
1648 struct pxe_menu *cfg;
1649 char *pxefile_addr_str;
1650 char *filename;
1651 int prompt = 0;
1652
1653 is_pxe = false;
1654
1655 if (argc > 1 && strstr(argv[1], "-p")) {
1656 prompt = 1;
1657 argc--;
1658 argv++;
1659 }
1660
1661 if (argc < 4)
1662 return cmd_usage(cmdtp);
1663
1664 if (argc < 5) {
1665 pxefile_addr_str = from_env("pxefile_addr_r");
1666 if (!pxefile_addr_str)
1667 return 1;
1668 } else {
1669 pxefile_addr_str = argv[4];
1670 }
1671
1672 if (argc < 6)
1673 filename = getenv("bootfile");
1674 else {
1675 filename = argv[5];
1676 setenv("bootfile", filename);
1677 }
1678
1679 if (strstr(argv[3], "ext2"))
1680 do_getfile = do_get_ext2;
1681 else if (strstr(argv[3], "fat"))
1682 do_getfile = do_get_fat;
1683 else if (strstr(argv[3], "any"))
1684 do_getfile = do_get_any;
1685 else {
1686 printf("Invalid filesystem: %s\n", argv[3]);
1687 return 1;
1688 }
1689 fs_argv[1] = argv[1];
1690 fs_argv[2] = argv[2];
1691
1692 if (strict_strtoul(pxefile_addr_str, 16, &pxefile_addr_r) < 0) {
1693 printf("Invalid pxefile address: %s\n", pxefile_addr_str);
1694 return 1;
1695 }
1696
1697 if (get_pxe_file(cmdtp, filename, pxefile_addr_r) < 0) {
1698 printf("Error reading config file\n");
1699 return 1;
1700 }
1701
1702 cfg = parse_pxefile(cmdtp, pxefile_addr_r);
1703
1704 if (cfg == NULL) {
1705 printf("Error parsing config file\n");
1706 return 1;
1707 }
1708
1709 if (prompt)
1710 cfg->prompt = 1;
1711
1712 handle_pxe_menu(cmdtp, cfg);
1713
1714 destroy_pxe_menu(cfg);
1715
1716 return 0;
1717}
1718
1719U_BOOT_CMD(
1720 sysboot, 7, 1, do_sysboot,
1721 "command to get and boot from syslinux files",
1722 "[-p] <interface> <dev[:part]> <ext2|fat|any> [addr] [filename]\n"
1723 " - load and parse syslinux menu file 'filename' from ext2, fat\n"
1724 " or any filesystem on 'dev' on 'interface' to address 'addr'"
1725);
1726