blob: 84b0d250733ce82c06e5180d6aa63539d57ed0de [file] [log] [blame]
Jason Hobbsafc8cd92011-06-29 11:25:18 -05001/*
2 * Copyright 2010-2011 Calxeda, Inc.
3 *
4 * This program is free software; you can redistribute it and/or modify it
5 * under the terms of the GNU General Public License as published by the Free
6 * Software Foundation; either version 2 of the License, or (at your option)
7 * any later version.
8 *
9 * This program is distributed in the hope it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
12 * more details.
13 *
14 * You should have received a copy of the GNU General Public License along with
15 * this program. If not, see <http://www.gnu.org/licenses/>.
16 */
17#include <common.h>
18#include <command.h>
19#include <malloc.h>
20#include <linux/string.h>
21#include <linux/ctype.h>
22#include <errno.h>
23#include <linux/list.h>
24
25#include "menu.h"
26
27#define MAX_TFTP_PATH_LEN 127
28
29static char *from_env(char *envvar)
30{
31 char *ret;
32
33 ret = getenv(envvar);
34
35 if (!ret)
36 printf("missing environment variable: %s\n", envvar);
37
38 return ret;
39}
40
41/*
42 * Returns the ethaddr environment variable formated with -'s instead of :'s
43 */
44static void format_mac_pxecfg(char **outbuf)
45{
46 char *p, *ethaddr;
47
48 *outbuf = NULL;
49
50 ethaddr = from_env("ethaddr");
51
52 if (!ethaddr)
John Rigbya7dbc122011-07-13 23:05:19 -060053 ethaddr = from_env("usbethaddr");
54
55 if (!ethaddr)
Jason Hobbsafc8cd92011-06-29 11:25:18 -050056 return;
57
Loïc Minier8ed2c442011-08-17 15:32:55 +020058 /* "01-" MAC type prefix, dash-separated MAC address, zero */
59 *outbuf = malloc(3 + strlen(ethaddr) + 1);
Jason Hobbsafc8cd92011-06-29 11:25:18 -050060
61 if (*outbuf == NULL)
62 return;
63
Loïc Minier8ed2c442011-08-17 15:32:55 +020064 /* MAC type 1; ideally should be read from the DHCP/BOOTP reply packet,
65 * but in practice always 1 for Ethernet */
66 sprintf(*outbuf, "01-%s", ethaddr);
67
68 for (p = *outbuf + 3; *p; p++) {
Jason Hobbsafc8cd92011-06-29 11:25:18 -050069 if (*p == ':')
70 *p = '-';
71 }
72}
73
74/*
75 * Returns the directory the file specified in the bootfile env variable is
76 * in.
77 */
78static char *get_bootfile_path(void)
79{
80 char *bootfile, *bootfile_path, *last_slash;
81 size_t path_len;
82
83 bootfile = from_env("bootfile");
84
85 if (!bootfile)
86 return NULL;
87
88 last_slash = strrchr(bootfile, '/');
89
90 if (last_slash == NULL)
91 return NULL;
92
93 path_len = (last_slash - bootfile) + 1;
94
95 bootfile_path = malloc(path_len + 1);
96
97 if (bootfile_path == NULL) {
98 printf("out of memory\n");
99 return NULL;
100 }
101
102 strncpy(bootfile_path, bootfile, path_len);
103
104 bootfile_path[path_len] = '\0';
105
106 return bootfile_path;
107}
108
109/*
110 * As in pxelinux, paths to files in pxecfg files are relative to the location
111 * of bootfile. get_relfile takes a path from a pxecfg file and joins it with
112 * the bootfile path to get the full path to the target file.
113 */
114static int get_relfile(char *file_path, void *file_addr)
115{
116 char *bootfile_path;
117 size_t path_len;
118 char relfile[MAX_TFTP_PATH_LEN+1];
119 char addr_buf[10];
120 char *tftp_argv[] = {"tftp", NULL, NULL, NULL};
121
122 bootfile_path = get_bootfile_path();
123
124 path_len = strlen(file_path);
125
126 if (bootfile_path)
127 path_len += strlen(bootfile_path);
128
129 if (path_len > MAX_TFTP_PATH_LEN) {
130 printf("Base path too long (%s%s)\n",
131 bootfile_path ? bootfile_path : "",
132 file_path);
133
134 if (bootfile_path)
135 free(bootfile_path);
136
137 return -ENAMETOOLONG;
138 }
139
140 sprintf(relfile, "%s%s",
141 bootfile_path ? bootfile_path : "",
142 file_path);
143
144 if (bootfile_path)
145 free(bootfile_path);
146
147 printf("Retreiving file: %s\n", relfile);
148
149 sprintf(addr_buf, "%p", file_addr);
150
151 tftp_argv[1] = addr_buf;
152 tftp_argv[2] = relfile;
153
154 if (do_tftpb(NULL, 0, 3, tftp_argv)) {
155 printf("File not found\n");
156 return -ENOENT;
157 }
158
159 return 1;
160}
161
162static int get_pxecfg_file(char *file_path, void *file_addr)
163{
164 unsigned long config_file_size;
165 int err;
166
167 err = get_relfile(file_path, file_addr);
168
169 if (err < 0)
170 return err;
171
172 config_file_size = simple_strtoul(getenv("filesize"), NULL, 16);
173 *(char *)(file_addr + config_file_size) = '\0';
174
175 return 1;
176}
177
178static int get_pxelinux_path(char *file, void *pxecfg_addr)
179{
180 size_t base_len = strlen("pxelinux.cfg/");
181 char path[MAX_TFTP_PATH_LEN+1];
182
183 if (base_len + strlen(file) > MAX_TFTP_PATH_LEN) {
184 printf("path too long, skipping\n");
185 return -ENAMETOOLONG;
186 }
187
188 sprintf(path, "pxelinux.cfg/%s", file);
189
190 return get_pxecfg_file(path, pxecfg_addr);
191}
192
193static int pxecfg_uuid_path(void *pxecfg_addr)
194{
195 char *uuid_str;
196
197 uuid_str = from_env("pxeuuid");
198
199 if (!uuid_str)
200 return -ENOENT;
201
202 return get_pxelinux_path(uuid_str, pxecfg_addr);
203}
204
205static int pxecfg_mac_path(void *pxecfg_addr)
206{
207 char *mac_str = NULL;
208
209 format_mac_pxecfg(&mac_str);
210
211 if (!mac_str)
212 return -ENOENT;
213
214 return get_pxelinux_path(mac_str, pxecfg_addr);
215}
216
217static int pxecfg_ipaddr_paths(void *pxecfg_addr)
218{
219 char ip_addr[9];
220 int mask_pos, err;
221
222 sprintf(ip_addr, "%08X", ntohl(NetOurIP));
223
224 for (mask_pos = 7; mask_pos >= 0; mask_pos--) {
225 err = get_pxelinux_path(ip_addr, pxecfg_addr);
226
227 if (err > 0)
228 return err;
229
230 ip_addr[mask_pos] = '\0';
231 }
232
233 return -ENOENT;
234}
235
236/*
237 * Follows pxelinux's rules to download a pxecfg file from a tftp server. The
238 * file is stored at the location given by the pxecfg_addr environment
239 * variable, which must be set.
240 *
241 * UUID comes from pxeuuid env variable, if defined
242 * MAC addr comes from ethaddr env variable, if defined
243 * IP
244 *
245 * see http://syslinux.zytor.com/wiki/index.php/PXELINUX
246 */
247static int get_pxecfg(int argc, char * const argv[])
248{
249 char *pxecfg_ram;
250 void *pxecfg_addr;
251
252 pxecfg_ram = from_env("pxecfg_ram");
253
254 if (!pxecfg_ram)
255 return 1;
256
257 pxecfg_addr = (void *)simple_strtoul(pxecfg_ram, NULL, 16);
258
259 if (pxecfg_uuid_path(pxecfg_addr) > 0
260 || pxecfg_mac_path(pxecfg_addr) > 0
261 || pxecfg_ipaddr_paths(pxecfg_addr) > 0
262 || get_pxelinux_path("default", pxecfg_addr) > 0) {
263
264 printf("Config file found\n");
265
266 return 1;
267 }
268
269 printf("Config file not found\n");
270
271 return 0;
272}
273
274static int get_relfile_envaddr(char *file_path, char *envaddr_name)
275{
276 void *file_addr;
277 char *envaddr;
278
279 envaddr = from_env(envaddr_name);
280
281 if (!envaddr)
282 return -ENOENT;
283
284 file_addr = (void *)simple_strtoul(envaddr, NULL, 16);
285
286 return get_relfile(file_path, file_addr);
287}
288
289struct pxecfg_label {
290 char *name;
291 char *kernel;
292 char *append;
293 char *initrd;
294 int attempted;
295 int localboot;
296 struct list_head list;
297};
298
299struct pxecfg {
300 char *title;
301 char *default_label;
302 int timeout;
303 int prompt;
304 struct list_head labels;
305};
306
307struct pxecfg_label *label_create(void)
308{
309 struct pxecfg_label *label;
310
311 label = malloc(sizeof *label);
312
313 if (!label)
314 return NULL;
315
316 label->name = NULL;
317 label->kernel = NULL;
318 label->append = NULL;
319 label->initrd = NULL;
320 label->localboot = 0;
321 label->attempted = 0;
322
323 return label;
324}
325
326static void label_destroy(struct pxecfg_label *label)
327{
328 if (label->name)
329 free(label->name);
330
331 if (label->kernel)
332 free(label->kernel);
333
334 if (label->append)
335 free(label->append);
336
337 if (label->initrd)
338 free(label->initrd);
339
340 free(label);
341}
342
343static void label_print(void *data)
344{
345 struct pxecfg_label *label = data;
346
347 printf("Label: %s\n", label->name);
348
349 if (label->kernel)
350 printf("\tkernel: %s\n", label->kernel);
351
352 if (label->append)
353 printf("\tappend: %s\n", label->append);
354
355 if (label->initrd)
356 printf("\tinitrd: %s\n", label->initrd);
357}
358
359static int label_localboot(struct pxecfg_label *label)
360{
361 char *localcmd, *dupcmd;
362 int ret;
363
364 localcmd = from_env("localcmd");
365
366 if (!localcmd)
367 return -ENOENT;
368
369 /*
370 * dup the command to avoid any issues with the version of it existing
371 * in the environment changing during the execution of the command.
372 */
373 dupcmd = strdup(localcmd);
374
375 if (!dupcmd) {
376 printf("out of memory\n");
377 return -ENOMEM;
378 }
379
380 if (label->append)
381 setenv("bootargs", label->append);
382
383 printf("running: %s\n", dupcmd);
384
385 ret = run_command2(dupcmd, 0);
386
387 free(dupcmd);
388
389 return ret;
390}
391
392/*
393 * Do what it takes to boot a chosen label.
394 *
395 * Retreive the kernel and initrd, and prepare bootargs.
396 */
397static void label_boot(struct pxecfg_label *label)
398{
399 char *bootm_argv[] = { "bootm", NULL, NULL, NULL, NULL };
400
401 label_print(label);
402
403 label->attempted = 1;
404
405 if (label->localboot) {
406 label_localboot(label);
407 return;
408 }
409
410 if (label->kernel == NULL) {
411 printf("No kernel given, skipping label\n");
412 return;
413 }
414
415 if (label->initrd) {
416 if (get_relfile_envaddr(label->initrd, "initrd_ram") < 0) {
417 printf("Skipping label\n");
418 return;
419 }
420
421 bootm_argv[2] = getenv("initrd_ram");
422 } else {
423 bootm_argv[2] = "-";
424 }
425
426 if (get_relfile_envaddr(label->kernel, "kernel_ram") < 0) {
427 printf("Skipping label\n");
428 return;
429 }
430
431 if (label->append)
432 setenv("bootargs", label->append);
433
434 bootm_argv[1] = getenv("kernel_ram");
435
436 /*
437 * fdt usage is optional - if unset, this stays NULL.
438 */
439 bootm_argv[3] = getenv("fdtaddr");
440
Ricardo Salveti de Araujo2f84e8e2011-07-11 01:37:45 -0300441 if (bootm_argv[3])
442 do_bootm(NULL, 0, 4, bootm_argv);
443 else
444 do_bootm(NULL, 0, 3, bootm_argv);
Jason Hobbsafc8cd92011-06-29 11:25:18 -0500445}
446
447enum token_type {
448 T_EOL,
449 T_STRING,
450 T_EOF,
451 T_MENU,
452 T_TITLE,
453 T_TIMEOUT,
454 T_LABEL,
455 T_KERNEL,
456 T_APPEND,
457 T_INITRD,
458 T_LOCALBOOT,
459 T_DEFAULT,
460 T_PROMPT,
461 T_INCLUDE,
462 T_INVALID
463};
464
465struct token {
466 char *val;
467 enum token_type type;
468};
469
470enum lex_state {
471 L_NORMAL = 0,
472 L_KEYWORD,
473 L_SLITERAL
474};
475
476static const struct token keywords[] = {
477 {"menu", T_MENU},
478 {"title", T_TITLE},
479 {"timeout", T_TIMEOUT},
480 {"default", T_DEFAULT},
481 {"prompt", T_PROMPT},
482 {"label", T_LABEL},
483 {"kernel", T_KERNEL},
484 {"localboot", T_LOCALBOOT},
485 {"append", T_APPEND},
486 {"initrd", T_INITRD},
487 {"include", T_INCLUDE},
488 {NULL, T_INVALID}
489};
490
491static char *get_string(char **p, struct token *t, char delim, int lower)
492{
493 char *b, *e;
494 size_t len, i;
495
496 b = e = *p;
497
498 while (*e) {
499 if ((delim == ' ' && isspace(*e)) || delim == *e)
500 break;
501 e++;
502 }
503
504 len = e - b;
505
506 t->val = malloc(len + 1);
507 if (!t->val) {
508 printf("out of memory\n");
509 return NULL;
510 }
511
512 for (i = 0; i < len; i++, b++) {
513 if (lower)
514 t->val[i] = tolower(*b);
515 else
516 t->val[i] = *b;
517 }
518
519 t->val[len] = '\0';
520
521 *p = e;
522
523 t->type = T_STRING;
524
525 return t->val;
526}
527
528static void get_keyword(struct token *t)
529{
530 int i;
531
532 for (i = 0; keywords[i].val; i++) {
533 if (!strcmp(t->val, keywords[i].val)) {
534 t->type = keywords[i].type;
535 break;
536 }
537 }
538}
539
540static void get_token(char **p, struct token *t, enum lex_state state)
541{
542 char *c = *p;
543
544 t->type = T_INVALID;
545
546 /* eat non EOL whitespace */
547 while (*c == ' ' || *c == '\t')
548 c++;
549
550 /* eat comments */
551 if (*c == '#') {
552 while (*c && *c != '\n')
553 c++;
554 }
555
556 if (*c == '\n') {
557 t->type = T_EOL;
558 c++;
559 } else if (*c == '\0') {
560 t->type = T_EOF;
561 c++;
562 } else if (state == L_SLITERAL) {
563 get_string(&c, t, '\n', 0);
564 } else if (state == L_KEYWORD) {
565 get_string(&c, t, ' ', 1);
566 get_keyword(t);
567 }
568
569 *p = c;
570}
571
572static void eol_or_eof(char **c)
573{
574 while (**c && **c != '\n')
575 (*c)++;
576}
577
578static int parse_sliteral(char **c, char **dst)
579{
580 struct token t;
581 char *s = *c;
582
583 get_token(c, &t, L_SLITERAL);
584
585 if (t.type != T_STRING) {
586 printf("Expected string literal: %.*s\n", (int)(*c - s), s);
587 return -EINVAL;
588 }
589
590 *dst = t.val;
591
592 return 1;
593}
594
595static int parse_integer(char **c, int *dst)
596{
597 struct token t;
598 char *s = *c;
599
600 get_token(c, &t, L_SLITERAL);
601
602 if (t.type != T_STRING) {
603 printf("Expected string: %.*s\n", (int)(*c - s), s);
604 return -EINVAL;
605 }
606
607 *dst = (int)simple_strtoul(t.val, NULL, 10);
608
609 free(t.val);
610
611 return 1;
612}
613
614static int parse_pxecfg_top(char *p, struct pxecfg *cfg, int nest_level);
615
616static int handle_include(char **c, char *base,
617 struct pxecfg *cfg, int nest_level)
618{
619 char *include_path;
620 int err;
621
622 err = parse_sliteral(c, &include_path);
623
624 if (err < 0) {
625 printf("Expected include path\n");
626 return err;
627 }
628
629 err = get_pxecfg_file(include_path, base);
630
631 if (err < 0) {
632 printf("Couldn't get %s\n", include_path);
633 return err;
634 }
635
636 return parse_pxecfg_top(base, cfg, nest_level);
637}
638
639static int parse_menu(char **c, struct pxecfg *cfg, char *b, int nest_level)
640{
641 struct token t;
642 char *s = *c;
643 int err;
644
645 get_token(c, &t, L_KEYWORD);
646
647 switch (t.type) {
648 case T_TITLE:
649 err = parse_sliteral(c, &cfg->title);
650
651 break;
652
653 case T_INCLUDE:
654 err = handle_include(c, b + strlen(b) + 1, cfg,
655 nest_level + 1);
656 break;
657
658 default:
659 printf("Ignoring malformed menu command: %.*s\n",
660 (int)(*c - s), s);
661 }
662
663 if (err < 0)
664 return err;
665
666 eol_or_eof(c);
667
668 return 1;
669}
670
671static int parse_label_menu(char **c, struct pxecfg *cfg,
672 struct pxecfg_label *label)
673{
674 struct token t;
675 char *s;
676
677 s = *c;
678
679 get_token(c, &t, L_KEYWORD);
680
681 switch (t.type) {
682 case T_DEFAULT:
683 if (cfg->default_label)
684 free(cfg->default_label);
685
686 cfg->default_label = strdup(label->name);
687
688 if (!cfg->default_label)
689 return -ENOMEM;
690
691 break;
692 default:
693 printf("Ignoring malformed menu command: %.*s\n",
694 (int)(*c - s), s);
695 }
696
697 eol_or_eof(c);
698
699 return 0;
700}
701
702static int parse_label(char **c, struct pxecfg *cfg)
703{
704 struct token t;
705 char *s;
706 struct pxecfg_label *label;
707 int err;
708
709 label = label_create();
710 if (!label)
711 return -ENOMEM;
712
713 err = parse_sliteral(c, &label->name);
714 if (err < 0) {
715 printf("Expected label name\n");
716 label_destroy(label);
717 return -EINVAL;
718 }
719
720 list_add_tail(&label->list, &cfg->labels);
721
722 while (1) {
723 s = *c;
724 get_token(c, &t, L_KEYWORD);
725
726 err = 0;
727 switch (t.type) {
728 case T_MENU:
729 err = parse_label_menu(c, cfg, label);
730 break;
731
732 case T_KERNEL:
733 err = parse_sliteral(c, &label->kernel);
734 break;
735
736 case T_APPEND:
737 err = parse_sliteral(c, &label->append);
738 break;
739
740 case T_INITRD:
741 err = parse_sliteral(c, &label->initrd);
742 break;
743
744 case T_LOCALBOOT:
745 err = parse_integer(c, &label->localboot);
746 break;
747
748 case T_EOL:
749 break;
750
751 /*
752 * A label ends when we either get to the end of a file, or
753 * get some input we otherwise don't have a handler defined
754 * for.
755 */
756 default:
757 /* put it back */
758 *c = s;
759 return 1;
760 }
761
762 if (err < 0)
763 return err;
764 }
765}
766
767#define MAX_NEST_LEVEL 16
768
769static int parse_pxecfg_top(char *p, struct pxecfg *cfg, int nest_level)
770{
771 struct token t;
772 char *s, *b, *label_name;
773 int err;
774
775 b = p;
776
777 if (nest_level > MAX_NEST_LEVEL) {
778 printf("Maximum nesting exceeded\n");
779 return -EMLINK;
780 }
781
782 while (1) {
783 s = p;
784
785 get_token(&p, &t, L_KEYWORD);
786
787 err = 0;
788 switch (t.type) {
789 case T_MENU:
790 err = parse_menu(&p, cfg, b, nest_level);
791 break;
792
793 case T_TIMEOUT:
794 err = parse_integer(&p, &cfg->timeout);
795 break;
796
797 case T_LABEL:
798 err = parse_label(&p, cfg);
799 break;
800
801 case T_DEFAULT:
802 err = parse_sliteral(&p, &label_name);
803
804 if (label_name) {
805 if (cfg->default_label)
806 free(cfg->default_label);
807
808 cfg->default_label = label_name;
809 }
810
811 break;
812
813 case T_PROMPT:
814 err = parse_integer(&p, &cfg->prompt);
815 break;
816
817 case T_EOL:
818 break;
819
820 case T_EOF:
821 return 1;
822
823 default:
824 printf("Ignoring unknown command: %.*s\n",
825 (int)(p - s), s);
826 eol_or_eof(&p);
827 }
828
829 if (err < 0)
830 return err;
831 }
832}
833
834static void destroy_pxecfg(struct pxecfg *cfg)
835{
836 struct list_head *pos, *n;
837 struct pxecfg_label *label;
838
839 if (cfg->title)
840 free(cfg->title);
841
842 if (cfg->default_label)
843 free(cfg->default_label);
844
845 list_for_each_safe(pos, n, &cfg->labels) {
846 label = list_entry(pos, struct pxecfg_label, list);
847
848 label_destroy(label);
849 }
850
851 free(cfg);
852}
853
854static struct pxecfg *parse_pxecfg(char *menucfg)
855{
856 struct pxecfg *cfg;
857
858 cfg = malloc(sizeof(*cfg));
859
860 if (!cfg) {
861 printf("Out of memory\n");
862 return NULL;
863 }
864
865 cfg->timeout = 0;
866 cfg->prompt = 0;
867 cfg->default_label = NULL;
868 cfg->title = NULL;
869
870 INIT_LIST_HEAD(&cfg->labels);
871
872 if (parse_pxecfg_top(menucfg, cfg, 1) < 0) {
873 destroy_pxecfg(cfg);
874 return NULL;
875 }
876
877 return cfg;
878}
879
880static struct menu *pxecfg_to_menu(struct pxecfg *cfg)
881{
882 struct pxecfg_label *label;
883 struct list_head *pos;
884 struct menu *m;
885 int err;
886
887 m = menu_create(cfg->title, cfg->timeout, cfg->prompt, label_print);
888
889 if (!m)
890 return NULL;
891
892 list_for_each(pos, &cfg->labels) {
893 label = list_entry(pos, struct pxecfg_label, list);
894
895 if (menu_item_add(m, label->name, label) != 1) {
896 menu_destroy(m);
897 return NULL;
898 }
899 }
900
901 if (cfg->default_label) {
902 err = menu_default_set(m, cfg->default_label);
903 if (err != 1) {
904 if (err != -ENOENT) {
905 menu_destroy(m);
906 return NULL;
907 }
908
909 printf("Missing default: %s\n", cfg->default_label);
910 }
911 }
912
913 return m;
914}
915
916static void boot_unattempted_labels(struct pxecfg *cfg)
917{
918 struct list_head *pos;
919 struct pxecfg_label *label;
920
921 list_for_each(pos, &cfg->labels) {
922 label = list_entry(pos, struct pxecfg_label, list);
923
924 if (!label->attempted)
925 label_boot(label);
926 }
927}
928
929static void handle_pxecfg(struct pxecfg *cfg)
930{
931 void *choice;
932 struct menu *m;
933
934 m = pxecfg_to_menu(cfg);
935 if (!m)
936 return;
937
938 if (menu_get_choice(m, &choice) == 1)
939 label_boot(choice);
940
941 menu_destroy(m);
942
943 boot_unattempted_labels(cfg);
944}
945
946static int boot_pxecfg(int argc, char * const argv[])
947{
948 unsigned long pxecfg_addr;
949 struct pxecfg *cfg;
950 char *pxecfg_ram;
951
952 if (argc == 2) {
953 pxecfg_ram = from_env("pxecfg_ram");
954 if (!pxecfg_ram)
955 return 1;
956
957 pxecfg_addr = simple_strtoul(pxecfg_ram, NULL, 16);
958 } else if (argc == 3) {
959 pxecfg_addr = simple_strtoul(argv[2], NULL, 16);
960 } else {
961 printf("Invalid number of arguments\n");
962 return 1;
963 }
964
965 cfg = parse_pxecfg((char *)(pxecfg_addr));
966
967 if (cfg == NULL) {
968 printf("Error parsing config file\n");
969 return 1;
970 }
971
972 handle_pxecfg(cfg);
973
974 destroy_pxecfg(cfg);
975
976 return 0;
977}
978
979int do_pxecfg(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
980{
981 if (argc < 2) {
982 printf("pxecfg requires at least one argument\n");
983 return EINVAL;
984 }
985
986 if (!strcmp(argv[1], "get"))
987 return get_pxecfg(argc, argv);
988
989 if (!strcmp(argv[1], "boot"))
990 return boot_pxecfg(argc, argv);
991
992 printf("Invalid pxecfg command: %s\n", argv[1]);
993
994 return EINVAL;
995}
996
997U_BOOT_CMD(
998 pxecfg, 2, 1, do_pxecfg,
999 "commands to get and boot from pxecfg files",
1000 "get - try to retrieve a pxecfg file using tftp\npxecfg "
1001 "boot [pxecfg_addr] - boot from the pxecfg file at pxecfg_addr\n"
1002);