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