Imported Upstream version 3.10
[debian/elilo] / choosers / simple.c
1 /* 
2  *  Copyright (C) 2001-2003 Hewlett-Packard Co.
3  *      Contributed by Stephane Eranian <eranian@hpl.hp.com>
4  *  Copyright (C) 2006-2009 Intel Corporation
5  *      Contributed by Fenghua Yu <fenghua.yu@intel.com>
6  *      Contributed by Bibo Mao <bibo.mao@intel.com>
7  *      Contributed by Chandramouli Narayanan <mouli@linux.intel.com>
8  *
9  * This file is part of the ELILO, the EFI Linux boot loader.
10  *
11  *  ELILO is free software; you can redistribute it and/or modify
12  *  it under the terms of the GNU General Public License as published by
13  *  the Free Software Foundation; either version 2, or (at your option)
14  *  any later version.
15  *
16  *  ELILO is distributed in the hope that it will be useful,
17  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
18  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19  *  GNU General Public License for more details.
20  *
21  *  You should have received a copy of the GNU General Public License
22  *  along with ELILO; see the file COPYING.  If not, write to the Free
23  *  Software Foundation, 59 Temple Place - Suite 330, Boston, MA
24  *  02111-1307, USA.
25  *
26  * Please check out the elilo.txt for complete documentation on how
27  * to use this program.
28  */
29
30 #include <efi.h>
31 #include <efilib.h>
32
33 #include "elilo.h"
34 #include "vars.h"
35
36 /* static is ugly but does the job here! */
37 static CHAR16 **alt_argv;
38
39 static VOID
40 display_label_info(CHAR16 *name)
41 {
42         CHAR16 *desc;
43         CHAR16 initrd_name[CMDLINE_MAXLEN];
44         CHAR16 vmcode_name[CMDLINE_MAXLEN];
45         CHAR16 options_tmp[CMDLINE_MAXLEN];
46         CHAR16 options[CMDLINE_MAXLEN];
47         CHAR16 kname[FILENAME_MAXLEN];
48
49         desc = find_description(name);
50         if (desc) {
51                 Print(L"desc   : %s\n", desc);
52         }
53
54         initrd_name[0] = vmcode_name[0] = options_tmp[0] = kname[0] = CHAR_NULL;
55
56         if (find_label(name, kname, options_tmp, initrd_name, vmcode_name) == -1) {
57                 StrCpy(kname, name);
58                 Print(L"\n");
59         }
60         subst_vars(options_tmp, options, CMDLINE_MAXLEN);
61
62         Print(L"cmdline: %s %s\n", kname, options);
63         if (initrd_name[0]) Print(L"initrd : %s\n", initrd_name);
64         if (vmcode_name[0]) Print(L"vmcode : %s\n", vmcode_name);
65 }
66
67 static VOID
68 print_infos(int force)
69 {
70         CHAR16 *config_file;
71         CHAR16 dpath[FILENAME_MAXLEN];
72         CHAR16 *boot_dev_name;
73         UINT8 is_abs;
74
75         boot_dev_name = fops_bootdev_name();
76         config_file   = get_config_file();
77
78         fops_getdefault_path(dpath, FILENAME_MAXLEN);
79
80         if (force || elilo_opt.verbose > 0)
81                 Print(L"default file path: %s:%s\n", boot_dev_name, dpath);
82
83         is_abs = config_file && (config_file[0] == CHAR_BACKSLASH || config_file[0] == CHAR_SLASH) ? 1 : 0;
84
85         if (force || elilo_opt.verbose > 0)
86                         Print(L"config file      : %s%s\n", config_file && is_abs == 0 ? dpath : L"", config_file ? config_file : L"none used");
87
88         if (alt_argv) {
89                 CHAR16 **p = alt_argv;
90                 Print(L"found alternate default choice :");
91                 while (*p) Print(L" %s", *p++);
92                 Print(L"\n");
93         }
94 }
95
96 static VOID
97 print_help(int force)
98 {
99         if (force || elilo_opt.verbose > 0)
100                 Print(L"command list (must be first character):\n=:print device list, %%:print variable list, &:print paths, ?:help\nTAB:print label information\n");
101 }
102
103 /*
104  * interactively select a kernel image and options.
105  * The kernel can be an actual filename or a label in the config file
106  * Return:
107  *      -1: if unsucessful
108  *       0: otherwise
109  */
110 static INTN
111 select_kernel(CHAR16 *buffer, INTN size)
112 {
113 #define CHAR_CTRL_C     L'\003' /* Unicode CTRL-C */
114 #define CHAR_CTRL_D     L'\004' /* Unicode CTRL-D */
115 #define CHAR_CTRL_U     L'\025' /* Unicode CTRL-U */
116 //#define CHAR_TAB      L'\t'
117         SIMPLE_INPUT_INTERFACE *ip = systab->ConIn;
118         EFI_INPUT_KEY key;
119         EFI_STATUS status;
120         INTN pos = 0, ret;
121         INT8 first_time = 1;
122
123         /* 
124          * let's give some help first
125          */
126         print_help(0);
127
128         print_infos(0);
129
130 reprint:
131         buffer[pos] = CHAR_NULL;
132
133         Print(L"\nELILO boot: %s", buffer);
134         /*
135          * autoboot with default choice after timeout expires
136          */
137         if (first_time && (ret=wait_timeout(elilo_opt.timeout)) != 1) {
138                 return ret == -1 ? -1: 0;
139         }
140         first_time = 0;
141
142         for (;;) {
143                 while ((status = uefi_call_wrapper(ip->ReadKeyStroke, 2, ip, &key))
144                                  == EFI_NOT_READY);
145                 if (EFI_ERROR(status)) {
146                         ERR_PRT((L"select_kernel readkey: %r", status));
147                         return -1;
148                 } 
149                 switch (key.UnicodeChar) {
150                         case CHAR_TAB:
151                                 Print(L"\n");
152                                 if (pos == 0) {
153                                         print_label_list();
154                                         Print(L"(or a kernel file name: [[dev_name:/]path/]kernel_image cmdline options)\n");
155                                 } else {
156                                         buffer[pos] = CHAR_NULL;
157                                         display_label_info(buffer);
158                                 }
159                                 goto reprint;
160                         case L'%':
161                                 if (pos>0) goto normal_char;
162                                 Print(L"\n");
163                                 print_vars();
164                                 goto reprint;
165                         case L'?':
166                                 if (pos>0) goto normal_char;
167                                 Print(L"\n");
168                                 print_help(1);
169                                 goto reprint;
170                         case L'&':
171                                 if (pos>0) goto normal_char;
172                                 Print(L"\n");
173                                 print_infos(1);
174                                 goto reprint;
175                         case L'=':
176                                 if (pos>0) goto normal_char;
177                                 Print(L"\n");
178                                 print_devices();
179                                 goto reprint;
180                         case CHAR_BACKSPACE:
181                                 if (pos == 0) break;
182                                 pos--;
183                                 Print(L"\b \b");
184                                 break;
185                         case CHAR_CTRL_U: /* clear line */
186                                 while (pos) {
187                                         Print(L"\b \b");
188                                         pos--;
189                                 }
190                                 break;
191                         case CHAR_CTRL_C: /* kill line */
192                                 pos = 0;
193                                 goto reprint;
194                         case CHAR_LINEFEED:
195                         case CHAR_CARRIAGE_RETURN:
196                                 buffer[pos]   = CHAR_NULL;
197                                 Print(L"\n");
198                                 return 0;
199                         default:
200 normal_char:
201                                 if (key.UnicodeChar == CHAR_CTRL_D || key.ScanCode == 0x17 ) {
202                                         Print(L"\nGiving up then...\n");
203                                         return  -1;
204                                 }
205                                 if (key.UnicodeChar == CHAR_NULL) break;
206
207                                 if (pos > size-1) break;
208
209                                 buffer[pos++] = key.UnicodeChar;
210
211                                 /* Write the character out */
212                                 Print(L"%c", key.UnicodeChar);
213                 }
214         }
215         return 0;
216 }
217
218 static VOID
219 display_message(VOID)
220 {
221         fops_fd_t fd;
222         EFI_STATUS status;
223         UINTN len, i;
224         CHAR16 *filename;
225         CHAR8 buf[256];
226
227         if ((filename = get_message_filename(0)) == NULL) return;
228
229         if (*filename == CHAR_NULL) return;
230
231         VERB_PRT(3, Print(L"opening message file %s\n", filename));
232
233         status = fops_open(filename, &fd);
234         if (EFI_ERROR(status)) {
235                 VERB_PRT(3, Print(L"message file %s not found\n", filename));
236                 return;
237         }
238         len = 256;
239         Print(L"\n");
240         while ((status = fops_read(fd, buf, &len)) == EFI_SUCCESS) {
241                 /* XXX: ugly ! */
242                 for (i=0; i < len; i++) {
243                         Print(L"%c", (CHAR16)buf[i]);
244                 }
245                 if (len < 256) break;
246         }
247         fops_close(fd);
248 }
249
250 static INTN
251 simple_choose(CHAR16 **argv, INTN argc, INTN index, CHAR16 *kname, CHAR16 *cmdline)
252 {       
253 #       define BOOT_IMG_STR     L"BOOT_IMAGE="
254         CHAR16 buffer[CMDLINE_MAXLEN];
255         CHAR16 alt_buffer[CMDLINE_MAXLEN];
256         CHAR16 initrd_name[CMDLINE_MAXLEN];
257         CHAR16 vmcode_name[CMDLINE_MAXLEN];
258         CHAR16 args[CMDLINE_MAXLEN];
259         CHAR16 devname[CMDLINE_MAXLEN];
260         CHAR16 dpath[FILENAME_MAXLEN];
261         CHAR16 *slash_pos, *colon_pos, *backslash_pos;
262         UINTN len;
263         INTN ret;
264
265         buffer[0] = alt_buffer[0] = CHAR_NULL;
266
267         display_message();
268
269 restart:
270         initrd_name[0] = vmcode_name[0] = kname[0] = cmdline[0] = args[0] = CHAR_NULL;
271
272         /* reset per image loader options */
273         Memset(&elilo_opt.img_opt, 0, sizeof(elilo_opt.img_opt));
274
275         /*
276          * check for alternate kernel image and params in EFI variable
277          */
278         if (elilo_opt.alt_check && alternate_kernel(alt_buffer, sizeof(alt_buffer)) == 0) {
279                 argc     = argify(alt_buffer,sizeof(alt_buffer), argv); 
280                 alt_argv = argv;
281                 index    = 0;
282                 args[0]  = initrd_name[0] = vmcode_name[0] = 0;
283                 /* 
284                  * don't check twice because the variable is deleted after
285                  * first access
286                  */
287                 elilo_opt.alt_check = 0; 
288         }
289
290         if (elilo_opt.prompt) {
291                 ret = select_kernel(buffer, sizeof(buffer));
292                 if (ret == -1) return -1;
293                 argc    = argify(buffer,sizeof(buffer), argv); 
294                 index   = 0;
295         }
296
297         /*
298          * if we found an alternate choice and the user
299          * did not force it manually, then use the alternate
300          * option.
301          */
302         if (alt_buffer[0] && buffer[0] == CHAR_NULL) {
303                 StrCpy(buffer, alt_buffer);
304         }
305
306         /*
307          * First search for matching label in the config file
308          * if options were specified on command line, they take
309          * precedence over the ones in the config file
310          *
311          * if no match is found, the args and initrd arguments may
312          * still be modified by global options in the config file.
313          */
314         ret = find_label((index < argc) ? argv[index] : NULL, kname, args, initrd_name, vmcode_name);
315
316         /*
317          * not found, so assume first argument is kernel name and
318          * not label name 
319          */
320         if (ret == -1) {
321                 if ((index < argc) && argv[index]) 
322                         StrCpy(kname, argv[index]);
323                 else
324                         StrCpy(kname, elilo_opt.default_kernel);
325         }
326         /*
327          * no matter what happened for kname, if user specified
328          * additional options, they override the ones in the
329          * config file 
330          */
331         if (argc > 1+index) {
332                 /*StrCpy(args, argv[++index]);*/
333                 while (++index < argc) {
334                         StrCat(args, L" ");
335                         StrCat(args, argv[index]);
336                 }
337         }
338         /*
339          * if initrd specified on command line, it overrides
340          * the one defined in config file, if any
341          */
342         if (elilo_opt.initrd[0] == CHAR_NULL && initrd_name[0] != CHAR_NULL) {
343                 StrCpy(elilo_opt.initrd, initrd_name);
344         }
345
346         if (elilo_opt.vmcode[0] == CHAR_NULL && vmcode_name[0] != CHAR_NULL) {
347                 StrCpy(elilo_opt.vmcode, vmcode_name);
348         }
349
350         VERB_PRT(1,  { Print(L"kernel     is  '%s'\n", kname);
351                        Print(L"arguments  are '%s'\n", args);
352                         if (elilo_opt.initrd[0]) Print(L"initrd      is '%s'\n", elilo_opt.initrd);
353                         if (elilo_opt.vmcode[0]) Print(L"vmm         is '%s'\n", elilo_opt.vmcode);
354                       });
355
356         if (elilo_opt.prompt == 0) {
357                 /* minimal printing */
358                 Print(L"ELILO\n");
359                 ret = wait_timeout(elilo_opt.delay);
360                 if (ret != 0) {
361                         elilo_opt.prompt = 1;
362                         elilo_opt.initrd[0] = elilo_opt.vmcode[0] = CHAR_NULL;
363                         elilo_opt.timeout =  ELILO_TIMEOUT_INFINITY;
364                         goto restart;
365                 }
366         }
367         /*
368          * add the device name, if not already specified, 
369          * so that we know where we came from
370          */
371         slash_pos     = StrChr(kname, L'/');
372         backslash_pos = StrChr(kname, L'\\');
373         colon_pos     = StrChr(kname, L':');
374
375         if (backslash_pos && backslash_pos < slash_pos) slash_pos = backslash_pos;
376
377         if (colon_pos == NULL || (slash_pos && (slash_pos < colon_pos))) {
378                 StrCpy(devname, fops_bootdev_name());
379                 StrCat(devname, L":");
380
381                 /* the default path is always terminated with a separator */
382                 if (kname[0] != L'/' && kname[0] != L'\\') {
383                         fops_getdefault_path(dpath,FILENAME_MAXLEN); 
384                         StrCat(devname, dpath);
385                 }
386         } else {
387                 devname[0] = CHAR_NULL;
388         }
389
390         /*
391          * create final argument list to the kernel
392          */
393         len = StrLen(BOOT_IMG_STR)      /* BOOT_IMAGE= */
394              +StrLen(devname)           /* device name */
395                                         /* kernel name */
396              +elilo_opt.vmcode[0] ? StrLen(elilo_opt.vmcode) : StrLen(kname)
397              +1                         /* space */
398              +StrLen(args);             /* args length */
399
400         if (len >= CMDLINE_MAXLEN-1) {
401                 ERR_PRT((L" arguments list too long cannot fit BOOT_IMAGE\n"));
402                 return -1;
403         }
404         StrCpy(cmdline, L"BOOT_IMAGE=");
405         StrCat(cmdline, devname);
406         if (elilo_opt.vmcode[0])
407                 StrCat(cmdline, elilo_opt.vmcode);
408         else
409                 StrCat(cmdline, kname);
410         StrCat(cmdline, L" ");
411         StrCat(cmdline, args);
412
413         return 0;
414 }
415
416 static INTN
417 simple_probe(EFI_HANDLE dev)
418 {
419         /* this chooser always work */
420         return 0;
421 }
422
423 chooser_t simple_chooser={
424         L"simple",
425         simple_probe,
426         simple_choose
427 };
428