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