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