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