Imported Debian patch 3.6-1
[debian/elilo] / config.c
1 /*
2  *  Copyright (C) 2001-2003 Hewlett-Packard Co.
3  *      Contributed by Stephane Eranian <eranian@hpl.hp.com>
4  *
5  * This file is part of the ELILO, the EFI Linux boot loader.
6  *
7  *  ELILO is free software; you can redistribute it and/or modify
8  *  it under the terms of the GNU General Public License as published by
9  *  the Free Software Foundation; either version 2, or (at your option)
10  *  any later version.
11  *
12  *  ELILO is distributed in the hope that it will be useful,
13  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
14  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  *  GNU General Public License for more details.
16  *
17  *  You should have received a copy of the GNU General Public License
18  *  along with ELILO; see the file COPYING.  If not, write to the Free
19  *  Software Foundation, 59 Temple Place - Suite 330, Boston, MA
20  *  02111-1307, USA.
21  *
22  * Please check out the elilo.txt for complete documentation on how
23  * to use this program.
24  *
25  * Portions of this file are derived from the  LILO/x86
26  * Copyright 1992-1997 Werner Almesberger. 
27  */
28
29 #include <efi.h>
30 #include <efilib.h>
31 #include <efistdarg.h>
32
33 #include "elilo.h"
34 #include "config.h"
35
36 /*
37  * The first default config file is architecture dependent. This is useful
38  * in case of network booting where the server is used for both types of
39  * architectures.
40  */
41 #if defined(CONFIG_ia64)
42 #define ELILO_ARCH_DEFAULT_CONFIG       L"elilo-ia64.conf"
43 #elif defined (CONFIG_ia32)
44 #define ELILO_ARCH_DEFAULT_CONFIG       L"elilo-ia32.conf"
45 #else
46 #error "You need to specfy your default arch config file"
47 #endif
48
49 /* 
50  * last resort config file. Common to all architectures
51  */
52 #define ELILO_DEFAULT_CONFIG    L"elilo.conf"
53
54 #define MAX_STRING      CMDLINE_MAXLEN
55 #define CONFIG_BUFSIZE  512     /* input buffer size */
56
57 /*
58  * maximum number of message files.
59  *
60  * main message= goes at entry 0, entries [1-12] are used for function keys
61  *
62  */
63 #define MAX_MESSAGES    13
64
65 typedef struct boot_image {
66         struct boot_image *next;
67         CHAR16  label[MAX_STRING];
68         CHAR16  kname[FILENAME_MAXLEN];
69         CHAR16  options[MAX_STRING];
70         CHAR16  initrd[FILENAME_MAXLEN];
71         CHAR16  vmcode[FILENAME_MAXLEN];
72         CHAR16  root[FILENAME_MAXLEN];
73         CHAR16  fallback[MAX_STRING];
74         CHAR16  description[MAX_STRING];
75
76         UINTN   ramdisk;
77         UINTN   readonly;
78         UINTN   literal;
79
80         sys_img_options_t sys_img_opts; /* architecture specific image options */
81 } boot_image_t;
82
83 typedef enum {
84         TOK_ERR,
85         TOK_EQUAL,
86         TOK_STR,
87         TOK_EOF
88 } token_t;
89
90 /*
91  * global shared options
92  * architecture specific global options are private to each architecture
93  */
94 typedef struct {
95         CHAR16          root[FILENAME_MAXLEN];  /* globally defined root fs */
96         CHAR16          initrd[FILENAME_MAXLEN];/* globally defined initrd  */
97         CHAR16          vmcode[FILENAME_MAXLEN];/* globally defined boot-time module  */
98         CHAR16          options[MAX_STRING];
99         CHAR16          default_image_name[MAX_STRING];
100         CHAR16          message_file[MAX_MESSAGES][FILENAME_MAXLEN]; 
101         CHAR16          chooser[FILENAME_MAXLEN];/* which image chooser to use */
102         CHAR16          config_file[FILENAME_MAXLEN];
103         boot_image_t    *default_image;
104
105         UINTN           readonly;
106
107         /* 
108          * options that may affect global elilo options
109          */
110         UINTN alt_check;
111         UINTN debug;
112         UINTN delay;
113         UINTN prompt;
114         UINTN timeout;
115         UINTN verbose;
116         UINTN edd30_no_force; /* don't force EDD30 if not set */
117 } global_config_t;
118
119 /*
120  * structure used to point to a group of options.
121  * Several group for the same category are supported via a linked list.
122  */
123 typedef struct _config_option_group {
124         struct _config_option_group     *next;    /* pointer to next group */
125         config_option_t                 *options; /* the table of options for this group */
126         UINTN                           nentries; /* number of entries for this group */
127 } config_option_group_t;
128
129 static option_action_t do_image, do_literal, do_options;
130 static INTN check_verbosity(VOID *), check_chooser(VOID *);
131
132 static global_config_t global_config;   /* options shared by all images */
133
134 /*
135  * Core global options: shared by all architectures, all modules
136  */
137 static config_option_t global_common_options[]={
138 {OPT_STR,       OPT_GLOBAL,     L"default",     NULL,           NULL,                   global_config.default_image_name},
139 {OPT_NUM,       OPT_GLOBAL,     L"timeout",     NULL,           NULL,                   &global_config.timeout},
140 {OPT_NUM,       OPT_GLOBAL,     L"delay",       NULL,           NULL,                   &global_config.delay},
141 {OPT_BOOL,      OPT_GLOBAL,     L"debug",       NULL,           NULL,                   &global_config.debug},
142 {OPT_BOOL,      OPT_GLOBAL,     L"prompt",      NULL,           NULL,                   &global_config.prompt},
143 {OPT_NUM,       OPT_GLOBAL,     L"verbose",     NULL,           check_verbosity,        &global_config.verbose},
144 {OPT_FILE,      OPT_GLOBAL,     L"root",        NULL,           NULL,                   global_config.root},
145 {OPT_BOOL,      OPT_GLOBAL,     L"read-only",   NULL,           NULL,                   &global_config.readonly},
146 {OPT_BOOL,      OPT_GLOBAL,     L"noedd30",     NULL,           NULL,                   &global_config.edd30_no_force},
147 {OPT_CMD,       OPT_GLOBAL,     L"append",      NULL,           NULL,                   global_config.options},
148 {OPT_FILE,      OPT_GLOBAL,     L"initrd",      NULL,           NULL,                   global_config.initrd},
149 {OPT_FILE,      OPT_GLOBAL,     L"vmm",         NULL,           NULL,                   global_config.vmcode},
150 {OPT_FILE,      OPT_GLOBAL,     L"image",       do_image,       NULL,                   opt_offsetof(kname)},
151 {OPT_BOOL,      OPT_GLOBAL,     L"checkalt",    NULL,           NULL,                   &global_config.alt_check},
152 {OPT_STR,       OPT_GLOBAL,     L"chooser",     NULL,           check_chooser,          global_config.chooser},
153 {OPT_FILE,      OPT_GLOBAL,     L"message",     NULL,           NULL,                   global_config.message_file[0]},
154 {OPT_FILE,      OPT_GLOBAL,     L"f1",          NULL,           NULL,                   global_config.message_file[1]},
155 {OPT_FILE,      OPT_GLOBAL,     L"f2",          NULL,           NULL,                   global_config.message_file[2]},
156 {OPT_FILE,      OPT_GLOBAL,     L"f3",          NULL,           NULL,                   global_config.message_file[3]},
157 {OPT_FILE,      OPT_GLOBAL,     L"f4",          NULL,           NULL,                   global_config.message_file[4]},
158 {OPT_FILE,      OPT_GLOBAL,     L"f5",          NULL,           NULL,                   global_config.message_file[5]},
159 {OPT_FILE,      OPT_GLOBAL,     L"f6",          NULL,           NULL,                   global_config.message_file[6]},
160 {OPT_FILE,      OPT_GLOBAL,     L"f7",          NULL,           NULL,                   global_config.message_file[7]},
161 {OPT_FILE,      OPT_GLOBAL,     L"f8",          NULL,           NULL,                   global_config.message_file[8]},
162 {OPT_FILE,      OPT_GLOBAL,     L"f9",          NULL,           NULL,                   global_config.message_file[9]},
163 {OPT_FILE,      OPT_GLOBAL,     L"f10",         NULL,           NULL,                   global_config.message_file[10]},
164 {OPT_FILE,      OPT_GLOBAL,     L"f11",         NULL,           NULL,                   global_config.message_file[11]},
165 {OPT_FILE,      OPT_GLOBAL,     L"f12",         NULL,           NULL,                   global_config.message_file[12]}
166 };
167
168 static config_option_t image_common_options[]={
169     {OPT_FILE,  OPT_IMAGE,      L"root",        NULL,           NULL,   opt_offsetof(root)},
170     {OPT_BOOL,  OPT_IMAGE,      L"read-only",   NULL,           NULL,   opt_offsetof(readonly)},
171     {OPT_CMD,   OPT_IMAGE,      L"append",      do_options,     NULL,   opt_offsetof(options)},
172     {OPT_CMD,   OPT_IMAGE,      L"literal",     do_literal,     NULL,   NULL},
173     {OPT_FILE,  OPT_IMAGE,      L"initrd",      NULL,           NULL,   opt_offsetof(initrd)},
174     {OPT_FILE,  OPT_IMAGE,      L"vmm",         NULL,           NULL,   opt_offsetof(vmcode)},
175     {OPT_STR,   OPT_IMAGE,      L"label",       NULL,           NULL,   opt_offsetof(label)},
176     {OPT_FILE,  OPT_IMAGE,      L"image",       do_image,       NULL,   opt_offsetof(kname)},
177     {OPT_STR,   OPT_IMAGE,      L"description", NULL,           NULL,   opt_offsetof(description)},
178 };
179
180 #define OPTION_IS_GLOBAL(p)     ((p)->scope == OPT_GLOBAL)
181 #define OPTION_IS_IMG_SYS(p)    ((p)->scope == OPT_IMAGE_SYS)
182
183 #define CHAR_EOF        (CHAR16)-1      /* Unicode version of EOF */
184 #define CHAR_NUM0       L'0'
185 #define CHAR_NUM9       L'9'
186
187 static UINTN line_num;
188 static INTN back; /* can go back by one char */
189
190 static config_option_group_t *global_option_list;
191 static config_option_group_t *image_option_list;
192
193
194 static config_option_group_t *current_options;
195
196 static boot_image_t *image_list, *first_image;
197 static boot_image_t *current_img;
198
199 static INT8 config_buf[CONFIG_BUFSIZE]; /* input buffer: file must be in ASCII! */
200 static UINTN buf_max, buf_pos;
201
202 static fops_fd_t config_fd;
203
204 static VOID 
205 config_error(CHAR16 *msg,...)
206 {
207     va_list ap;
208     extern UINTN _IPrint (UINTN, UINTN, SIMPLE_TEXT_OUTPUT_INTERFACE *, CHAR16 *, CHAR8 *, va_list);
209
210     Print(L"near line %d: ",line_num);
211
212     va_start(ap,msg);
213     _IPrint((UINTN)-1, (UINTN)-1, systab->ConOut, msg, NULL, ap);
214     va_end(ap);
215     Print(L"\n");
216 }
217
218 /*
219  * low level read routine
220  * Return:
221  *
222  * Success:
223  *      - the next available unicode character
224  * Error:
225  *      - CHAR_EOF : indicating error or EOF
226  *
227  * XXX: we suppose that the file is in ASCII format!
228  */
229 static CHAR16
230 getc(VOID)
231 {
232         EFI_STATUS status;
233
234         if (buf_pos == 0 || buf_pos == buf_max) {
235                 buf_max = CONFIG_BUFSIZE;
236                 status = fops_read(config_fd, config_buf, &buf_max);
237                 if (EFI_ERROR(status) || buf_max == 0) return CHAR_EOF;
238
239                 buf_pos  = 0;
240         }
241         return (CHAR16)config_buf[buf_pos++];
242 }
243
244
245 /*
246  * get the next unicode character with a one character
247  * rewind buffer.
248  */
249 static CHAR16
250 next(VOID)
251 {
252     CHAR16 ch;
253
254     if (back) {
255         ch = back;
256         back = 0;
257         return ch;
258     }
259     return getc();
260 }
261
262 /*
263  * rewind by one character
264  */
265 static VOID
266 again(CHAR16 ch)
267 {
268     if (back) { config_error(L"config: again invoked twice"); }
269     back = ch;
270 }
271
272 /*
273  * Look for matching option in the current group
274  *
275  * Return:
276  *      - pointer to option if found
277  *      - NULL if not found
278  */
279 static config_option_t *
280 find_option(config_option_group_t *grp, CHAR16 *str)
281 {
282         config_option_t *p = NULL;
283         config_option_t *end;
284
285         while(grp) {
286                 p = grp->options;
287                 end = grp->options+grp->nentries;
288
289                 while (p != end) {
290                         if (!StrCmp(str, p->name)) return p;
291                         p++;
292                 }
293                 grp = grp->next;
294         }
295         return NULL;
296 }
297
298 /*
299  * main parser function
300  * Return:
301  *      - a token code representing the kind of element found
302  *      - TOK_EOF: end-of-file (or error) detected
303  *      - TOK_STR: if string is found
304  *      - TOK_EQUAL: for the '=' sign
305  *      - TOK_ERR: in case of (parsing) error
306  */
307 static token_t
308 get_token(CHAR16 *str, UINTN maxlen)
309 {
310     INTN ch, escaped;
311     CHAR16 *here;
312
313     for (;;) {
314         while ((ch = next()), ch == ' ' || ch == '\t' || ch == '\n') if (ch == '\n') line_num++;
315
316         if (ch == CHAR_EOF) return TOK_EOF;
317
318         if (ch != '#') break;
319
320         /* skip comment line */
321         while ((ch = next()), ch != '\n') if (ch == CHAR_EOF) return TOK_EOF;
322         line_num++;
323     }
324     if (ch == '=') return TOK_EQUAL;
325
326     if (ch == '"') {
327         here = str;
328         while (here-str < maxlen) {
329             if ((ch = next()) == CHAR_EOF) {
330                     config_error(L"EOF in quoted string");
331                     return TOK_ERR;
332             }
333             if (ch == '"') {
334                 *here = 0;
335                 return TOK_STR;
336             }
337             if (ch == '\\') {
338                 ch = next();
339                 if (ch != '"' && ch != '\\' && ch != '\n') {
340                     config_error(L"Bad use of \\ in quoted string");
341                     return TOK_ERR;
342                 }
343                 if (ch == '\n') continue;
344 #if 0
345                     while ((ch = next()), ch == ' ' || ch == '\t');
346                     if (!ch) continue;
347                     again(ch);
348                     ch = ' ';
349                 }
350 #endif
351             }
352             if (ch == '\n' || ch == '\t') {
353                 config_error(L"\\n and \\t are not allowed in quoted strings");
354                 return TOK_ERR;
355             }
356             *here++ = ch;
357         }
358         config_error(L"Quoted string is too long");
359         return TOK_ERR;
360     }
361
362     here    = str;
363     escaped = 0;
364
365     while (here-str < maxlen) {
366         if (escaped) {
367             if (ch == CHAR_EOF) {
368                     config_error(L"\\ precedes EOF");
369                     return TOK_ERR;
370             }
371             if (ch == '\n') line_num++;
372             else *here++ = ch == '\t' ? ' ' : ch;
373             escaped = 0;
374         }
375         else {
376             if (ch == ' ' || ch == '\t' || ch == '\n' || ch == '#' ||
377               ch == '=' || ch == CHAR_EOF) {
378                 again(ch);
379                 *here = 0;
380                 return TOK_STR;
381             }
382             if (!(escaped = (ch == '\\'))) *here++ = ch;
383         }
384         ch = next();
385     }
386     config_error(L"Token is too long");
387     return TOK_ERR;
388 }
389
390 static INTN
391 image_check(boot_image_t *img)
392 {
393         boot_image_t *b;
394
395         if (img == NULL) return -1;
396
397         /* do the obvious first */
398         if (img->label[0] == '\0') {
399                 config_error(L"image has no label");
400                 return -1;
401         }
402
403         /* image_list points to the */
404         for(b=image_list; b; b = b->next) {
405                 if (img == b) continue;
406                 if (!StrCmp(img->label, b->label)) {
407                         config_error(L"image with label %s already defined", img->label);
408                         return -1;
409                 }
410         }
411         return 0;
412 }
413
414 static INTN 
415 global_check(VOID)
416 {
417         return 0;
418 }
419
420 static INTN
421 final_check(VOID)
422 {
423         boot_image_t *b;
424
425         if (global_config.default_image_name[0]) {
426                 for(b=image_list; b; b = b->next) {
427                         if (!StrCmp(b->label, global_config.default_image_name)) goto found;
428                 }
429                 config_error(L"default image '%s' not defined ", global_config.default_image_name);
430                 return -1;
431         }
432         global_config.default_image = first_image;
433         return 0;
434 found:
435         global_config.default_image = b;
436         return 0;
437 }
438
439 /*
440  * depending on the active set of options
441  * adjust the option data pointer to:
442  * if global option set:
443  *      - just the straight value from p->data
444  * if image option set:
445  *      - adjust as offset to image
446  * Return:
447  *      - the adjusted pointer
448  */
449 static inline VOID *
450 adjust_pointer(config_option_t *p)
451 {
452         /*
453          * adjust pointer
454          */
455         if (OPTION_IS_GLOBAL(p)) return p->data;
456
457         if (OPTION_IS_IMG_SYS(p)) return (VOID *)((UINTN)&current_img->sys_img_opts + p->data);
458
459         return (VOID *)((UINTN)current_img + p->data);
460 }
461
462 /*
463  * create a new image entry
464  */
465 static INTN
466 do_image(config_option_t *p, VOID *str)
467 {
468         boot_image_t *img;
469         
470         /*
471          * if we move to a new image from the current one
472          * then we need to check for validity of current.
473          *
474          * if this is the first image, then check the global
475          * options.
476          */
477         if (current_img) {
478                 if (image_check(current_img) == -1) return -1;
479         } else if (global_check() == -1) return -1;
480
481         img = (boot_image_t *)alloc(sizeof(boot_image_t), EfiLoaderData);
482         if (img == NULL) return -1;
483
484         Memset(img, 0, sizeof(boot_image_t));
485
486         DBG_PRT((L"must do image on %s", (CHAR16 *)str));
487
488         /* copy kernel file name */
489         StrCpy(img->kname, str);
490
491         /* switch to image mode */
492         current_options = image_option_list;
493
494         /* keep track of first image in case no default is defined */
495         if (image_list == NULL) first_image = img;
496
497         /* append to end of image list, so when a chooser asks for the list
498          * it gets them in the order they were in in the config file
499          */
500         if (image_list == NULL)
501                 image_list = img;
502         else {
503                 boot_image_t * p = image_list;
504
505                 while (p->next)
506                         p = p->next;
507                 p->next = img;
508         }
509
510         /* update current image */
511         current_img = img;
512
513         return 0;
514 }
515
516 /*
517  * by default all boolean options are defined
518  * as false. This function sets the boolean
519  * to true
520  */
521 static INTN
522 do_boolean(config_option_t *p)
523 {
524         INT8 *buf;
525
526         buf = adjust_pointer(p);
527
528         if (p->action) return p->action(p, NULL);
529
530         /* set the field to true, overwrite if already defined */
531         *buf = 1;
532
533         return 0;
534 }
535         
536 /*
537  * the image option 'literal' requires special handling
538  * because it overrides any defined option be it global or
539  * local. so we use the option field and record the fact that
540  * it should be interpreted as literal
541  */
542 static INTN
543 do_literal(config_option_t *p, VOID *str)
544 {
545         /*
546          * we know we have a valid current image at this point
547          */
548         StrCpy(current_img->options, str);
549
550         current_img->literal = 1;
551
552         return 0;
553 }
554
555 static INTN
556 do_options(config_option_t *p, VOID *str)
557 {
558         /* we ignore append if literal is already defined */
559         if (current_img->literal) return 0;
560
561         /*
562          * we know we have a valid current image at this point
563          */
564         StrCpy(current_img->options, str);
565
566         return 0;
567 }
568
569 static INTN
570 do_numeric(config_option_t *p)
571 {
572         CHAR16 numstr[MAX_STRING];
573         CHAR16 *str;
574         token_t tok;
575         UINTN *buf;
576         UINTN tmp;
577
578         /*
579          * match the '=' sign
580          */
581         tok = get_token(numstr, MAX_STRING);
582         if (tok != TOK_EQUAL) {
583                 config_error(L"Option %s expects an equal signal + value", p->name);
584                 return -1;
585         }
586
587         /*
588          * now get the value
589          * XXX: = lexer should return TOK_NUM (and we'd be done)
590          */
591         tok = get_token(numstr, MAX_STRING);
592         if (tok != TOK_STR) {
593                 config_error(L"Option %s expects a value", p->name);
594                 return -1;
595         }
596         str = numstr;
597         /*
598          * if there is a customized way of doing the operation
599          * do it and bypass generic
600          */
601         if (p->action) return p->action(p, str);
602
603         /*
604          * no field to update
605          */
606         if (p->data == NULL) return 0;
607
608         buf = (UINTN *)adjust_pointer(p);
609
610         while (*str && *str >= CHAR_NUM0 && *str <= CHAR_NUM9) str++;
611         if (*str) {
612                 config_error(L"%s is expecting a numeric decimal value", p->name);
613                 return -1;
614         }
615
616         tmp = Atoi(numstr);
617
618         if (p->check && p->check(&tmp) == -1) return -1;
619
620         /*
621          * check for multiple definitions in the same context
622          * XXX: zero must not be a valid number !
623          */
624         if (*buf) {
625                 config_error(L"option %s is already defined in this context", p->name);
626                 return -1;
627         }
628
629         *buf = tmp;
630
631         return 0;
632 }
633
634 static INTN
635 check_verbosity(VOID *data)
636 {
637         UINTN *val = (UINTN *)data;
638
639         if (*val > 5) {
640                 config_error(L"Verbosity level must be in [0-5] and not %d", *val);
641                 return -1;
642         }
643
644         return 0;
645 }
646
647 /*
648  * here we simply check if chooser is compiled in. At this point, the chooser
649  * initialization is not done, so we don't know if that chooser will even be
650  * useable.
651  */
652 static INTN
653 check_chooser(VOID *data)
654 {
655         CHAR16 *chooser = (CHAR16 *)data;
656
657         if (exist_chooser(chooser) == -1) {
658                 config_error(L"chooser %s is unknown\n", chooser);
659                 return -1;
660         }
661         return 0;
662 }
663
664
665 static INTN
666 do_string_core(config_option_t *p, CHAR16 *str, UINTN maxlen, CHAR16 *msg)
667 {
668         token_t tok;
669         CHAR16 *buf;
670
671         /*
672          * match the '=' sign
673          */
674         tok = get_token(str, maxlen);
675         if (tok != TOK_EQUAL) {
676                 config_error(L"Option %s expects an equal signal + %s", p->name, msg);
677                 return -1;
678         }
679
680         /*
681          * now get the value
682          */
683         tok = get_token(str, maxlen);
684         if (tok != TOK_STR) {
685                 config_error(L"Option %s expects %s", p->name, msg);
686                 return -1;
687         }
688
689         /*
690          * if there is a customized way of doing the operation
691          * do it and bypass generic
692          */
693         if (p->action) return p->action(p, str);
694
695         /*
696          * no field to update
697          */
698         if (p->data == NULL) return 0;
699
700         buf = adjust_pointer(p);
701
702         if (*buf != CHAR_NULL) {
703                 config_error(L"'%s' already defined", p->name);
704                 return -1;
705         }
706         if (p->check && p->check(str) == -1) return -1;
707
708         /*
709          * initialize field
710          */
711         StrCpy(buf, str);
712
713         return 0;
714 }
715         
716 static INTN
717 do_string(config_option_t *p)
718 {
719         CHAR16 str[MAX_STRING];
720
721         return do_string_core(p, str, MAX_STRING, L"string");
722 }
723         
724 static INTN
725 do_file(config_option_t *p)
726 {
727         CHAR16 str[FILENAME_MAXLEN];
728
729         return do_string_core(p, str, FILENAME_MAXLEN, L"filename");
730 }
731
732
733 static INTN
734 do_cmd(config_option_t *p)
735 {
736         CHAR16 str[CMDLINE_MAXLEN];
737         return do_string_core(p, str, CMDLINE_MAXLEN, L"kernel options");
738 }
739
740
741 INTN
742 config_parse(VOID)
743 {
744         CHAR16 str[MAX_STRING];
745         INTN ret = -1;
746         token_t tok;
747         config_option_t *p;
748
749         for (;;) {
750                 tok = get_token(str, MAX_STRING);
751
752                 if (tok == TOK_EOF) break;
753
754                 if (tok == TOK_ERR) return -1;
755
756                 if ( (p = find_option(current_options, str)) == NULL) {
757                         config_error(L"Unkown option %s", str);
758                         return -1;
759                 }
760
761                 /* make sure we trap in case of error */
762                 ret = -1;
763
764                 switch(p->type) {
765                         case OPT_BOOL:
766                                 ret = do_boolean(p);
767                                 break;
768                         case OPT_STR:
769                                 ret = do_string(p);
770                                 break;
771                         case OPT_NUM:
772                                 ret = do_numeric(p);
773                                 break;
774                         case OPT_FILE:
775                                 ret = do_file(p);
776                                 break;
777                         case OPT_CMD:
778                                 ret = do_cmd(p);
779                                 break;
780                         default:
781                                 config_error(L"Unkown option type %d", p->type);
782                 }
783                 if (ret == -1) goto error;
784         }
785         if (current_img) {
786                 ret = image_check(current_img); 
787         } else {
788                 config_error(L"No image defined !");
789         }
790         if (ret == 0) ret = final_check();
791 error:
792         return ret;
793 }
794         
795 static VOID
796 update_elilo_opt(VOID)
797 {
798         /*
799          * update boolean options first
800          * Rule: by default not set unless explcitely requested on command line
801          * therefore we can just update from global_config is set there.
802          */
803         if (global_config.alt_check) elilo_opt.alt_check = 1;
804
805         if (global_config.debug) elilo_opt.debug   = 1;
806         if (global_config.prompt) elilo_opt.prompt = 1;
807
808         /*
809          * update only if not set on command line
810          * XXX: rely on the existence of a non-valid value as a marker than
811          * the option was not specified on the command line
812          */
813         if (global_config.verbose && elilo_opt.verbose == 0) 
814                 elilo_opt.verbose = global_config.verbose;
815
816         if (global_config.chooser[0] && elilo_opt.chooser[0] == 0)
817                 StrCpy(elilo_opt.chooser, global_config.chooser);
818
819         /*
820          * if edd30_no_force not set by command line option but via config
821          * file, then propagate
822          */
823         if (global_config.edd30_no_force && elilo_opt.edd30_no_force == 0)
824                 elilo_opt.edd30_no_force = 1;
825
826         /*
827          * Difficult case of numeric where 0 can be a valid value
828          * XXX: find a better way of handling these options!
829          */
830         if (global_config.delay && elilo_opt.delay_set == 0)
831                 elilo_opt.delay = global_config.delay;
832
833         /* readjusted by caller if necessary. 0 not a valid value here */
834         elilo_opt.timeout = global_config.timeout;
835
836         /* initrd is updated later when we have an image match */
837 }
838
839 /*
840  * When called after read_config(), this function returns the config file
841  * used by the loader, or NULL if no file was found.
842  */
843 CHAR16 *
844 get_config_file(VOID)
845 {
846         return global_config.config_file[0] ? global_config.config_file : NULL; 
847 }
848
849 EFI_STATUS
850 read_config(CHAR16 *filename)
851 {
852         EFI_STATUS status;
853         INTN ret;
854         
855         if (filename == NULL) return EFI_INVALID_PARAMETER;
856
857         VERB_PRT(3, Print(L"trying config file %s\n", filename));
858
859         StrCpy(global_config.config_file, filename);
860
861         status = fops_open(filename, &config_fd);
862         if (EFI_ERROR(status)) {
863                 VERB_PRT(3, Print(L"cannot open config file %s\n", filename));
864                 return status;
865         }
866         /*
867          * start numbering at line 1
868          */
869         line_num = 1;
870
871         ret = config_parse();
872
873         fops_close(config_fd);
874
875         DBG_PRT((L"done parsing config file\n"));
876
877         if (ret != 0) return EFI_INVALID_PARAMETER;
878
879         update_elilo_opt();
880
881         return EFI_SUCCESS;
882 }
883
884 VOID
885 print_label_list(VOID)
886 {
887         boot_image_t *img, *dfl = global_config.default_image;
888
889         if (dfl) Print(L"\t%s\n", dfl->label);
890
891         for (img = image_list; img; img = img->next) {
892                 if (img != dfl) Print(L"\t%s\n", img->label);
893         }
894 }
895
896 /* Make labels and corresponding descriptions available to choosers.  The
897  * idea is that the caller passes NULL for 'prev'; the first time, and
898  * passes the previously returned value on subsequent calls.  The caller
899  * repeats until this fn returns NULL.
900  */
901
902 VOID *
903 get_next_description(VOID *prev, CHAR16 **label, CHAR16 **description)
904 {
905         boot_image_t *img = (boot_image_t *)prev;
906
907         if (img == NULL)
908                 img = image_list;
909         else
910                 img = img->next;
911
912         if (img) {
913                 *label = img->label;
914                 *description = img->description;
915                 return (void *)img;
916         }
917         else
918                 return NULL;
919 }
920
921 /*
922  * find a description using the label name
923  * return NULL if label not found or no description defined
924  */
925 CHAR16 *
926 find_description(CHAR16 *label)
927 {
928         boot_image_t *img;
929
930         /* Attempt to find the image name now */
931         for (img = image_list; img; img = img->next) {
932                 if (StriCmp(img->label, label) == 0) {
933                         return img->description;
934                 }
935         }
936         return NULL;
937 }
938
939 INTN
940 find_label(CHAR16 *label, CHAR16 *kname, CHAR16 *options, CHAR16 *initrd, CHAR16 *vmcode)
941 {
942         boot_image_t *img;
943
944         if (label == NULL) {
945                 if (global_config.default_image == NULL) return -1;
946                 img = global_config.default_image;
947                 goto found;
948         }
949
950         options[0] = 0;
951
952         /* Attempt to find the image name now */
953         for (img = image_list; img; img = img->next) {
954                 if (StriCmp(img->label, label) == 0) {
955                         goto found;
956                 }
957         }
958         /*
959          * when the label does not exist, we still propagate the global options
960          */
961         if (global_config.root[0]) {
962                 StrCpy(options, L" root=");
963                 StrCat(options, global_config.root);
964         }
965
966         if (global_config.options[0]) {
967                 StrCat(options, L" ");
968                 StrCat(options, global_config.options);
969         }
970         if (global_config.readonly) StrCat(options, L" ro");
971
972         if (global_config.initrd[0]) StrCpy(initrd, global_config.initrd);
973         if (global_config.vmcode[0]) StrCpy(vmcode, global_config.vmcode);
974
975         /* make sure we don't get garbage here */
976         elilo_opt.sys_img_opts = NULL;
977
978         return -1;
979 found:
980         StrCpy(kname, img->kname);
981
982         /*
983          * literal option overrides any other image-based or global option
984          *
985          * In any case, the image option has priority over the global option
986          */
987         if (img->literal == 0) {
988                 if (img->root[0] || global_config.root[0]) {
989                         StrCat(options, L"root=");
990                         StrCat(options, img->root[0] ? img->root : global_config.root);
991                 }
992                 /* XXX: check max length */
993                 if (img->options[0] || global_config.options[0]) {
994                         StrCat(options, L" ");
995                         StrCat(options, img->options[0] ? img->options: global_config.options);
996                 }
997                 if (img->readonly || global_config.readonly) {
998                         StrCat(options, L" ro");
999                 }
1000         } else {
1001                 /* literal options replace everything */
1002                 StrCpy(options, img->options);
1003         }
1004
1005         /* per image initrd has precedence over global initrd */
1006         if (img->initrd[0]) 
1007                 StrCpy(initrd, img->initrd);
1008         else if (global_config.initrd[0])
1009                 StrCpy(initrd, global_config.initrd);
1010
1011         if (img->vmcode[0]) 
1012                 StrCpy(vmcode, img->vmcode);
1013         else if (global_config.vmcode[0])
1014                 StrCpy(vmcode, global_config.vmcode);
1015
1016         /*
1017          * point to architecture dependent options for this image
1018          */
1019         elilo_opt.sys_img_opts = &img->sys_img_opts;
1020
1021         DBG_PRT((L"label %s: kname=%s options=%s initrd=%s vmcode=%s", img->label, kname, options, initrd, vmcode));
1022
1023         return 0;
1024 }
1025
1026 static VOID
1027 print_options(config_option_group_t *grp, BOOLEAN first)
1028 {
1029         config_option_t *end, *p;
1030         CHAR16 *str;
1031
1032         while (grp) {
1033                 p = grp->options;
1034                 end = grp->options+grp->nentries;
1035                 while (p != end) {
1036                         str = NULL;
1037                         switch(p->type) {
1038                                 case OPT_BOOL:
1039                                         str = L"%s";
1040                                         break;
1041                                 case OPT_STR :
1042                                         str = L"%s=string";
1043                                         break;
1044                                 case OPT_FILE :
1045                                         str = L"%s=filename";
1046                                         break;
1047                                 case OPT_CMD :
1048                                         str = L"%s=kernel_options";
1049                                         break;
1050                                 case OPT_NUM :
1051                                         str = L"%s=number";
1052                                         break;
1053                                 default: 
1054                                         break;
1055                         }
1056                         if (str && first == FALSE) Print(L", ");
1057                         if (str) Print(str, p->name);
1058                         first = FALSE;
1059                         p++;
1060                 }
1061                 grp = grp->next;
1062         }
1063 }
1064
1065
1066 VOID
1067 print_config_options(VOID)
1068 {
1069         Print(L"Global options supported:\n");
1070
1071         print_options(global_option_list, TRUE);
1072         Print(L"\n\n");
1073
1074         Print(L"Image options supported:\n");
1075         print_options(image_option_list, TRUE);
1076         Print(L"\n");
1077 }
1078
1079
1080 /*
1081  * returns a pointer to filename used for message N (which). NULL if it does
1082  * not exist.
1083  */
1084 CHAR16 *
1085 get_message_filename(INTN which)
1086 {
1087         if (which < 0 || which >= MAX_MESSAGES) return NULL;
1088         return global_config.message_file[which];
1089 }
1090
1091 INTN
1092 register_config_options(config_option_t *opt, UINTN n, config_option_group_scope_t group)
1093 {
1094         config_option_group_t *newgrp, **grp;
1095
1096         if (opt == NULL || n == 0 || (group != OPTIONS_GROUP_GLOBAL && group != OPTIONS_GROUP_IMAGE)) return -1;
1097
1098         VERB_PRT(3, Print(L"registering %d options for group %s\n", n, group == OPTIONS_GROUP_GLOBAL ? L"global" : L"image"));
1099
1100         newgrp = alloc(sizeof(config_option_group_t), EfiLoaderData);
1101         if (newgrp == NULL) return -1;
1102
1103         grp = group == OPTIONS_GROUP_GLOBAL ? &global_option_list : &image_option_list;
1104
1105         if (*grp) while ((*grp)->next) grp = &(*grp)->next;
1106
1107         newgrp->options  = opt;
1108         newgrp->next     = NULL;
1109         newgrp->nentries = n;
1110
1111         if (*grp) { 
1112                 (*grp)->next = newgrp;
1113         } else {
1114                 *grp = newgrp;  
1115         }
1116         return 0;
1117 }
1118
1119 /*
1120  * initialize config options: register global and per image options
1121  */
1122 INTN
1123 config_init(VOID)
1124 {
1125         INTN ret;
1126
1127         ret = register_config_options(global_common_options, 
1128                                       sizeof(global_common_options)/sizeof(config_option_t),
1129                                       OPTIONS_GROUP_GLOBAL);
1130         if (ret == -1) return -1;
1131
1132         ret = register_config_options(image_common_options, 
1133                                       sizeof(image_common_options)/sizeof(config_option_t),
1134                                       OPTIONS_GROUP_IMAGE);
1135
1136         current_options = global_option_list;
1137
1138         return ret;
1139 }
1140