Imported Upstream version 3.8
[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_core(CHAR16 *str, UINTN maxlen, BOOLEAN rhs)
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 == '=' && !rhs) 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 == CHAR_EOF || (ch == '=' && !rhs)) {
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 token_t
390 get_token(CHAR16 *str, UINTN maxlen)
391 {
392     return get_token_core(str, maxlen, FALSE);
393 }
394
395 static token_t
396 get_token_rhs(CHAR16 *str, UINTN maxlen)
397 {
398     return get_token_core(str, maxlen, TRUE);
399 }
400
401 static INTN
402 image_check(boot_image_t *img)
403 {
404         boot_image_t *b;
405
406         if (img == NULL) return -1;
407
408         /* do the obvious first */
409         if (img->label[0] == '\0') {
410                 config_error(L"image has no label");
411                 return -1;
412         }
413
414         /* image_list points to the */
415         for(b=image_list; b; b = b->next) {
416                 if (img == b) continue;
417                 if (!StrCmp(img->label, b->label)) {
418                         config_error(L"image with label %s already defined", img->label);
419                         return -1;
420                 }
421         }
422         return 0;
423 }
424
425 static INTN 
426 global_check(VOID)
427 {
428         return 0;
429 }
430
431 static INTN
432 final_check(VOID)
433 {
434         boot_image_t *b;
435
436         if (global_config.default_image_name[0]) {
437                 for(b=image_list; b; b = b->next) {
438                         if (!StrCmp(b->label, global_config.default_image_name)) goto found;
439                 }
440                 config_error(L"default image '%s' not defined ", global_config.default_image_name);
441                 return -1;
442         }
443         global_config.default_image = first_image;
444         return 0;
445 found:
446         global_config.default_image = b;
447         return 0;
448 }
449
450 /*
451  * depending on the active set of options
452  * adjust the option data pointer to:
453  * if global option set:
454  *      - just the straight value from p->data
455  * if image option set:
456  *      - adjust as offset to image
457  * Return:
458  *      - the adjusted pointer
459  */
460 static inline VOID *
461 adjust_pointer(config_option_t *p)
462 {
463         /*
464          * adjust pointer
465          */
466         if (OPTION_IS_GLOBAL(p)) return p->data;
467
468         if (OPTION_IS_IMG_SYS(p)) return (VOID *)((UINTN)&current_img->sys_img_opts + p->data);
469
470         return (VOID *)((UINTN)current_img + p->data);
471 }
472
473 /*
474  * create a new image entry
475  */
476 static INTN
477 do_image(config_option_t *p, VOID *str)
478 {
479         boot_image_t *img;
480         
481         /*
482          * if we move to a new image from the current one
483          * then we need to check for validity of current.
484          *
485          * if this is the first image, then check the global
486          * options.
487          */
488         if (current_img) {
489                 if (image_check(current_img) == -1) return -1;
490         } else if (global_check() == -1) return -1;
491
492         img = (boot_image_t *)alloc(sizeof(boot_image_t), EfiLoaderData);
493         if (img == NULL) return -1;
494
495         Memset(img, 0, sizeof(boot_image_t));
496
497         DBG_PRT((L"must do image on %s", (CHAR16 *)str));
498
499         /* copy kernel file name */
500         StrCpy(img->kname, str);
501
502         /* switch to image mode */
503         current_options = image_option_list;
504
505         /* keep track of first image in case no default is defined */
506         if (image_list == NULL) first_image = img;
507
508         /* append to end of image list, so when a chooser asks for the list
509          * it gets them in the order they were in in the config file
510          */
511         if (image_list == NULL)
512                 image_list = img;
513         else {
514                 boot_image_t * p = image_list;
515
516                 while (p->next)
517                         p = p->next;
518                 p->next = img;
519         }
520
521         /* update current image */
522         current_img = img;
523
524         return 0;
525 }
526
527 /*
528  * by default all boolean options are defined
529  * as false. This function sets the boolean
530  * to true
531  */
532 static INTN
533 do_boolean(config_option_t *p)
534 {
535         INT8 *buf;
536
537         buf = adjust_pointer(p);
538
539         if (p->action) return p->action(p, NULL);
540
541         /* set the field to true, overwrite if already defined */
542         *buf = 1;
543
544         return 0;
545 }
546         
547 /*
548  * the image option 'literal' requires special handling
549  * because it overrides any defined option be it global or
550  * local. so we use the option field and record the fact that
551  * it should be interpreted as literal
552  */
553 static INTN
554 do_literal(config_option_t *p, VOID *str)
555 {
556         /*
557          * we know we have a valid current image at this point
558          */
559         StrCpy(current_img->options, str);
560
561         current_img->literal = 1;
562
563         return 0;
564 }
565
566 static INTN
567 do_options(config_option_t *p, VOID *str)
568 {
569         /* we ignore append if literal is already defined */
570         if (current_img->literal) return 0;
571
572         /*
573          * we know we have a valid current image at this point
574          */
575         StrCpy(current_img->options, str);
576
577         return 0;
578 }
579
580 static INTN
581 do_numeric(config_option_t *p)
582 {
583         CHAR16 numstr[MAX_STRING];
584         CHAR16 *str;
585         token_t tok;
586         UINTN *buf;
587         UINTN tmp;
588
589         /*
590          * match the '=' sign
591          */
592         tok = get_token(numstr, MAX_STRING);
593         if (tok != TOK_EQUAL) {
594                 config_error(L"Option %s expects an equal signal + value", p->name);
595                 return -1;
596         }
597
598         /*
599          * now get the value
600          * XXX: = lexer should return TOK_NUM (and we'd be done)
601          */
602         tok = get_token(numstr, MAX_STRING);
603         if (tok != TOK_STR) {
604                 config_error(L"Option %s expects a value", p->name);
605                 return -1;
606         }
607         str = numstr;
608         /*
609          * if there is a customized way of doing the operation
610          * do it and bypass generic
611          */
612         if (p->action) return p->action(p, str);
613
614         /*
615          * no field to update
616          */
617         if (p->data == NULL) return 0;
618
619         buf = (UINTN *)adjust_pointer(p);
620
621         while (*str && *str >= CHAR_NUM0 && *str <= CHAR_NUM9) str++;
622         if (*str) {
623                 config_error(L"%s is expecting a numeric decimal value", p->name);
624                 return -1;
625         }
626
627         tmp = Atoi(numstr);
628
629         if (p->check && p->check(&tmp) == -1) return -1;
630
631         /*
632          * check for multiple definitions in the same context
633          * XXX: zero must not be a valid number !
634          */
635         if (*buf) {
636                 config_error(L"option %s is already defined in this context", p->name);
637                 return -1;
638         }
639
640         *buf = tmp;
641
642         return 0;
643 }
644
645 static INTN
646 check_verbosity(VOID *data)
647 {
648         UINTN *val = (UINTN *)data;
649
650         if (*val > 5) {
651                 config_error(L"Verbosity level must be in [0-5] and not %d", *val);
652                 return -1;
653         }
654
655         return 0;
656 }
657
658 /*
659  * here we simply check if chooser is compiled in. At this point, the chooser
660  * initialization is not done, so we don't know if that chooser will even be
661  * useable.
662  */
663 static INTN
664 check_chooser(VOID *data)
665 {
666         CHAR16 *chooser = (CHAR16 *)data;
667
668         if (exist_chooser(chooser) == -1) {
669                 config_error(L"chooser %s is unknown\n", chooser);
670                 return -1;
671         }
672         return 0;
673 }
674
675
676 static INTN
677 do_string_core(config_option_t *p, CHAR16 *str, UINTN maxlen, CHAR16 *msg)
678 {
679         token_t tok;
680         CHAR16 *buf;
681
682         /*
683          * match the '=' sign
684          */
685         tok = get_token(str, maxlen);
686         if (tok != TOK_EQUAL) {
687                 config_error(L"Option %s expects an equal signal + %s", p->name, msg);
688                 return -1;
689         }
690
691         /*
692          * now get the value
693          */
694         tok = get_token_rhs(str, maxlen);
695         if (tok != TOK_STR) {
696                 config_error(L"Option %s expects %s", p->name, msg);
697                 return -1;
698         }
699
700         /*
701          * if there is a customized way of doing the operation
702          * do it and bypass generic
703          */
704         if (p->action) return p->action(p, str);
705
706         /*
707          * no field to update
708          */
709         if (p->data == NULL) return 0;
710
711         buf = adjust_pointer(p);
712
713         if (*buf != CHAR_NULL) {
714                 config_error(L"'%s' already defined", p->name);
715                 return -1;
716         }
717         if (p->check && p->check(str) == -1) return -1;
718
719         /*
720          * initialize field
721          */
722         StrCpy(buf, str);
723
724         return 0;
725 }
726         
727 static INTN
728 do_string(config_option_t *p)
729 {
730         CHAR16 str[MAX_STRING];
731
732         return do_string_core(p, str, MAX_STRING, L"string");
733 }
734         
735 static INTN
736 do_file(config_option_t *p)
737 {
738         CHAR16 str[FILENAME_MAXLEN];
739
740         return do_string_core(p, str, FILENAME_MAXLEN, L"filename");
741 }
742
743
744 static INTN
745 do_cmd(config_option_t *p)
746 {
747         CHAR16 str[CMDLINE_MAXLEN];
748         return do_string_core(p, str, CMDLINE_MAXLEN, L"kernel options");
749 }
750
751
752 INTN
753 config_parse(VOID)
754 {
755         CHAR16 str[MAX_STRING];
756         INTN ret = -1;
757         token_t tok;
758         config_option_t *p;
759
760         for (;;) {
761                 tok = get_token(str, MAX_STRING);
762
763                 if (tok == TOK_EOF) break;
764
765                 if (tok == TOK_ERR) return -1;
766
767                 if ( (p = find_option(current_options, str)) == NULL) {
768                         config_error(L"Unkown option %s", str);
769                         return -1;
770                 }
771
772                 /* make sure we trap in case of error */
773                 ret = -1;
774
775                 switch(p->type) {
776                         case OPT_BOOL:
777                                 ret = do_boolean(p);
778                                 break;
779                         case OPT_STR:
780                                 ret = do_string(p);
781                                 break;
782                         case OPT_NUM:
783                                 ret = do_numeric(p);
784                                 break;
785                         case OPT_FILE:
786                                 ret = do_file(p);
787                                 break;
788                         case OPT_CMD:
789                                 ret = do_cmd(p);
790                                 break;
791                         default:
792                                 config_error(L"Unkown option type %d", p->type);
793                 }
794                 if (ret == -1) goto error;
795         }
796         if (current_img) {
797                 ret = image_check(current_img); 
798         } else {
799                 config_error(L"No image defined !");
800         }
801         if (ret == 0) ret = final_check();
802 error:
803         return ret;
804 }
805         
806 static VOID
807 update_elilo_opt(VOID)
808 {
809         /*
810          * update boolean options first
811          * Rule: by default not set unless explcitely requested on command line
812          * therefore we can just update from global_config is set there.
813          */
814         if (global_config.alt_check) elilo_opt.alt_check = 1;
815
816         if (global_config.debug) elilo_opt.debug   = 1;
817         if (global_config.prompt) elilo_opt.prompt = 1;
818
819         /*
820          * update only if not set on command line
821          * XXX: rely on the existence of a non-valid value as a marker than
822          * the option was not specified on the command line
823          */
824         if (global_config.verbose && elilo_opt.verbose == 0) 
825                 elilo_opt.verbose = global_config.verbose;
826
827         if (global_config.chooser[0] && elilo_opt.chooser[0] == 0)
828                 StrCpy(elilo_opt.chooser, global_config.chooser);
829
830         /*
831          * if edd30_no_force not set by command line option but via config
832          * file, then propagate
833          */
834         if (global_config.edd30_no_force && elilo_opt.edd30_no_force == 0)
835                 elilo_opt.edd30_no_force = 1;
836
837         /*
838          * Difficult case of numeric where 0 can be a valid value
839          * XXX: find a better way of handling these options!
840          */
841         if (global_config.delay && elilo_opt.delay_set == 0)
842                 elilo_opt.delay = global_config.delay;
843
844         /* readjusted by caller if necessary. 0 not a valid value here */
845         elilo_opt.timeout = global_config.timeout;
846
847         /* initrd is updated later when we have an image match */
848 }
849
850 /*
851  * When called after read_config(), this function returns the config file
852  * used by the loader, or NULL if no file was found.
853  */
854 CHAR16 *
855 get_config_file(VOID)
856 {
857         return global_config.config_file[0] ? global_config.config_file : NULL; 
858 }
859
860 EFI_STATUS
861 read_config(CHAR16 *filename)
862 {
863         EFI_STATUS status;
864         INTN ret;
865         
866         if (filename == NULL) return EFI_INVALID_PARAMETER;
867
868         VERB_PRT(3, Print(L"trying config file %s\n", filename));
869
870         StrCpy(global_config.config_file, filename);
871
872         status = fops_open(filename, &config_fd);
873         if (EFI_ERROR(status)) {
874                 VERB_PRT(3, Print(L"cannot open config file %s\n", filename));
875                 return status;
876         }
877         /*
878          * start numbering at line 1
879          */
880         line_num = 1;
881
882         ret = config_parse();
883
884         fops_close(config_fd);
885
886         DBG_PRT((L"done parsing config file\n"));
887
888         if (ret != 0) return EFI_INVALID_PARAMETER;
889
890         update_elilo_opt();
891
892         return EFI_SUCCESS;
893 }
894
895 VOID
896 print_label_list(VOID)
897 {
898         boot_image_t *img, *dfl = global_config.default_image;
899
900         if (dfl) Print(L"\t%s\n", dfl->label);
901
902         for (img = image_list; img; img = img->next) {
903                 if (img != dfl) Print(L"\t%s\n", img->label);
904         }
905 }
906
907 /* Make labels and corresponding descriptions available to choosers.  The
908  * idea is that the caller passes NULL for 'prev'; the first time, and
909  * passes the previously returned value on subsequent calls.  The caller
910  * repeats until this fn returns NULL.
911  */
912
913 VOID *
914 get_next_description(VOID *prev, CHAR16 **label, CHAR16 **description)
915 {
916         boot_image_t *img = (boot_image_t *)prev;
917
918         if (img == NULL)
919                 img = image_list;
920         else
921                 img = img->next;
922
923         if (img) {
924                 *label = img->label;
925                 *description = img->description;
926                 return (void *)img;
927         }
928         else
929                 return NULL;
930 }
931
932 /*
933  * find a description using the label name
934  * return NULL if label not found or no description defined
935  */
936 CHAR16 *
937 find_description(CHAR16 *label)
938 {
939         boot_image_t *img;
940
941         /* Attempt to find the image name now */
942         for (img = image_list; img; img = img->next) {
943                 if (StriCmp(img->label, label) == 0) {
944                         return img->description;
945                 }
946         }
947         return NULL;
948 }
949
950 static void
951 add_root_to_options(CHAR16 *options, CHAR16 *root, CHAR16 *vmcode)
952 {
953         CHAR16 *o, ko[CMDLINE_MAXLEN];
954
955         if (vmcode[0]) {
956                 for (o = options; *o; o++) {
957                         if (*o == '-' && *(o+1) == '-')
958                                 break;
959                 }
960                 if (! *o) {
961                         /* no separator found, add one */
962                         StrCpy(o, L" -- ");
963                         o++;
964                 }
965
966                 /* advance past separator and whitespace */
967                 o += 2;
968                 while (*o == ' ') o++;
969         } else {
970                 o = options;
971         }
972
973         /* insert root param at this point */
974         StrCpy(ko, o);
975         StrCpy(o, L"root=");
976         StrCat(o, root);
977         StrCat(o, L" ");
978         StrCat(o, ko);
979 }
980
981 INTN
982 find_label(CHAR16 *label, CHAR16 *kname, CHAR16 *options, CHAR16 *initrd, CHAR16 *vmcode)
983 {
984         boot_image_t *img;
985
986         if (label == NULL) {
987                 if (global_config.default_image == NULL) return -1;
988                 img = global_config.default_image;
989                 goto found;
990         }
991
992         options[0] = 0;
993
994         /* Attempt to find the image name now */
995         for (img = image_list; img; img = img->next) {
996                 if (StriCmp(img->label, label) == 0) {
997                         goto found;
998                 }
999         }
1000         /*
1001          * when the label does not exist, we still propagate the global options
1002          */
1003         if (global_config.options[0]) {
1004                 StrCat(options, L" ");
1005                 StrCat(options, global_config.options);
1006         }
1007         if (global_config.root[0])
1008                 add_root_to_options(options, global_config.root, global_config.vmcode);
1009         if (global_config.readonly) StrCat(options, L" ro");
1010
1011         if (global_config.initrd[0]) StrCpy(initrd, global_config.initrd);
1012         if (global_config.vmcode[0]) StrCpy(vmcode, global_config.vmcode);
1013
1014         /* make sure we don't get garbage here */
1015         elilo_opt.sys_img_opts = NULL;
1016
1017         return -1;
1018 found:
1019         StrCpy(kname, img->kname);
1020
1021         /* per image initrd has precedence over global initrd */
1022         if (img->initrd[0]) 
1023                 StrCpy(initrd, img->initrd);
1024         else
1025                 StrCpy(initrd, global_config.initrd);
1026
1027         /* per image vmcode has precedence over global vmcode */
1028         if (img->vmcode[0]) 
1029                 StrCpy(vmcode, img->vmcode);
1030         else
1031                 StrCpy(vmcode, global_config.vmcode);
1032
1033         /*
1034          * literal option overrides any other image-based or global option
1035          *
1036          * In any case, the image option has priority over the global option
1037          */
1038         if (img->literal == 0) {
1039                 /* XXX: check max length */
1040                 if (img->options[0] || global_config.options[0]) {
1041                         StrCat(options, L" ");
1042                         StrCat(options, img->options[0] ? img->options: global_config.options);
1043                 }
1044                 if (img->root[0] || global_config.root[0]) {
1045                         add_root_to_options(options, img->root[0] 
1046                                             ? img->root : global_config.root, vmcode);
1047                 }
1048                 if (img->readonly || global_config.readonly) {
1049                         StrCat(options, L" ro");
1050                 }
1051         } else {
1052                 /* literal options replace everything */
1053                 StrCpy(options, img->options);
1054         }
1055
1056         /*
1057          * point to architecture dependent options for this image
1058          */
1059         elilo_opt.sys_img_opts = &img->sys_img_opts;
1060
1061         DBG_PRT((L"label %s: kname=%s options=%s initrd=%s vmcode=%s", img->label, kname, options, initrd, vmcode));
1062
1063         return 0;
1064 }
1065
1066 static VOID
1067 print_options(config_option_group_t *grp, BOOLEAN first)
1068 {
1069         config_option_t *end, *p;
1070         CHAR16 *str;
1071
1072         while (grp) {
1073                 p = grp->options;
1074                 end = grp->options+grp->nentries;
1075                 while (p != end) {
1076                         str = NULL;
1077                         switch(p->type) {
1078                                 case OPT_BOOL:
1079                                         str = L"%s";
1080                                         break;
1081                                 case OPT_STR :
1082                                         str = L"%s=string";
1083                                         break;
1084                                 case OPT_FILE :
1085                                         str = L"%s=filename";
1086                                         break;
1087                                 case OPT_CMD :
1088                                         str = L"%s=kernel_options";
1089                                         break;
1090                                 case OPT_NUM :
1091                                         str = L"%s=number";
1092                                         break;
1093                                 default: 
1094                                         break;
1095                         }
1096                         if (str && first == FALSE) Print(L", ");
1097                         if (str) Print(str, p->name);
1098                         first = FALSE;
1099                         p++;
1100                 }
1101                 grp = grp->next;
1102         }
1103 }
1104
1105
1106 VOID
1107 print_config_options(VOID)
1108 {
1109         Print(L"Global options supported:\n");
1110
1111         print_options(global_option_list, TRUE);
1112         Print(L"\n\n");
1113
1114         Print(L"Image options supported:\n");
1115         print_options(image_option_list, TRUE);
1116         Print(L"\n");
1117 }
1118
1119
1120 /*
1121  * returns a pointer to filename used for message N (which). NULL if it does
1122  * not exist.
1123  */
1124 CHAR16 *
1125 get_message_filename(INTN which)
1126 {
1127         if (which < 0 || which >= MAX_MESSAGES) return NULL;
1128         return global_config.message_file[which];
1129 }
1130
1131 INTN
1132 register_config_options(config_option_t *opt, UINTN n, config_option_group_scope_t group)
1133 {
1134         config_option_group_t *newgrp, **grp;
1135
1136         if (opt == NULL || n == 0 || (group != OPTIONS_GROUP_GLOBAL && group != OPTIONS_GROUP_IMAGE)) return -1;
1137
1138         VERB_PRT(3, Print(L"registering %d options for group %s\n", n, group == OPTIONS_GROUP_GLOBAL ? L"global" : L"image"));
1139
1140         newgrp = alloc(sizeof(config_option_group_t), EfiLoaderData);
1141         if (newgrp == NULL) return -1;
1142
1143         grp = group == OPTIONS_GROUP_GLOBAL ? &global_option_list : &image_option_list;
1144
1145         if (*grp) while ((*grp)->next) grp = &(*grp)->next;
1146
1147         newgrp->options  = opt;
1148         newgrp->next     = NULL;
1149         newgrp->nentries = n;
1150
1151         if (*grp) { 
1152                 (*grp)->next = newgrp;
1153         } else {
1154                 *grp = newgrp;  
1155         }
1156         return 0;
1157 }
1158
1159 /*
1160  * initialize config options: register global and per image options
1161  */
1162 INTN
1163 config_init(VOID)
1164 {
1165         INTN ret;
1166
1167         ret = register_config_options(global_common_options, 
1168                                       sizeof(global_common_options)/sizeof(config_option_t),
1169                                       OPTIONS_GROUP_GLOBAL);
1170         if (ret == -1) return -1;
1171
1172         ret = register_config_options(image_common_options, 
1173                                       sizeof(image_common_options)/sizeof(config_option_t),
1174                                       OPTIONS_GROUP_IMAGE);
1175
1176         current_options = global_option_list;
1177
1178         return ret;
1179 }
1180