update patch
[debian/elilo] / elilo.c
diff --git a/elilo.c b/elilo.c
index 0959c7133295c1a266589920e4f35ffab5ef8f9d..ade977d401c1b654a411bc7eaec5b95b14f2b95f 100644 (file)
--- a/elilo.c
+++ b/elilo.c
@@ -1,5 +1,5 @@
 /* 
- * elilo.c - IA-64/IA-32 EFI Linux loader
+ * elilo.c - IA-64/IA-32/x86_64 EFI Linux loader
  *
  *  Copyright (C) 1999-2003 Hewlett-Packard Co.
  *     Contributed by David Mosberger <davidm@hpl.hp.com>.
@@ -8,6 +8,11 @@
  *  Copyright (C) 1999-2000 VA Linux Systems
  *       Contributed by Johannes Erdfelt <jerdfelt@valinux.com>.
  *
+ *  Copyright (C) 2006-2009 Intel Corporation
+ *     Contributed by Fenghua Yu <fenghua.yu@intel.com>
+ *     Contributed by Bibo Mao <bibo.mao@intel.com>
+ *     Contributed by Chandramouli Narayanan <mouli@linux.intel.com>
+ *
  * This file is part of the ELILO, the EFI Linux boot loader.
  *
  *  ELILO is free software; you can redistribute it and/or modify
 
 #include "elilo.h"
 #include "vars.h"
+#include "gzip.h"
 
 #include "getopt.h"
 #include "fileops.h"
 #include "loader.h"
 #include "config.h" /* for config_init() */
 
-#define ELILO_VERSION                  L"3.4"
 #define ELILO_SHARED_CMDLINE_OPTS      L"pPMC:aDhd:i:vVc:E"
 
 elilo_config_t elilo_opt;
 
 EFI_SYSTEM_TABLE *systab;      /* pointer to EFI system table */
 
+extern INTN wait_timeout (UINTN);
+
 /*
  * Load the Linux kernel in memory from the boot media
  * Output:
@@ -84,13 +91,23 @@ do_kernel_load(CHAR16 *kname, kdesc_t *kd)
 }
 
 INTN
-kernel_load(EFI_HANDLE image, CHAR16 *kname, kdesc_t *kd, memdesc_t *imem)
+kernel_load(EFI_HANDLE image, CHAR16 *kname, kdesc_t *kd, memdesc_t *imem, memdesc_t *mmem)
 {
+       CHAR16 kernel[FILENAME_MAXLEN];
 
+       /*
+        * Do the vm image switch here
+        * if there is "vmm=" then elilo should load image specified
+        * in "vmm=" and then give the "image" to vmm as target kernel image
+        */
+       if (elilo_opt.vmcode[0])
+               StrCpy(kernel, elilo_opt.vmcode);
+       else
+               StrCpy(kernel, kname);
        /*
         * Now let's try to load the kernel !
         */
-       switch(do_kernel_load(kname, kd)) {
+       switch(do_kernel_load(kernel, kd)) {
                case ELILO_LOAD_SUCCESS:
                        break;
 
@@ -101,6 +118,7 @@ kernel_load(EFI_HANDLE image, CHAR16 *kname, kdesc_t *kd, memdesc_t *imem)
                case ELILO_LOAD_ABORTED:
                        /* we drop initrd in case we aborted the load */
                        elilo_opt.initrd[0] = CHAR_NULL;
+                       elilo_opt.vmcode[0] = CHAR_NULL;
 
                        /* will go back to interactive selection */
                        elilo_opt.prompt  = 1; 
@@ -110,34 +128,77 @@ kernel_load(EFI_HANDLE image, CHAR16 *kname, kdesc_t *kd, memdesc_t *imem)
                        return ELILO_LOAD_RETRY;        
        }
 
-       VERB_PRT(3, Print(L"kernel loaded in [0x%lx-0x%lx] entry=0x%lx\n", 
-                         (UINT64)kd->kstart, (UINT64)kd->kend, (UINT64)kd->kentry));
+       VERB_PRT(3, Print(L"kernel loaded in [" PTR_FMT "-" PTR_FMT "] entry=" PTR_FMT "\n", 
+                         kd->kstart, kd->kend, kd->kentry));
 
        if (elilo_opt.initrd[0]) {
 
+               /* ramdisk image is moved to the top of available extended memory later by start_kernel() */
                if (sysdeps_initrd_get_addr(kd, imem) == -1) goto exit_error;
 
-               switch(load_initrd(elilo_opt.initrd, imem)) {
+               switch(load_file(elilo_opt.initrd, imem)) {
                        case ELILO_LOAD_SUCCESS:
                                break;
                        case ELILO_LOAD_ERROR:
                                goto exit_error;
                        case ELILO_LOAD_ABORTED:
-                               /* the free_kmem() is the responsibility of the loader */
+                               free_kmem();
+                               /* we drop initrd in case we aborted the load */
+                               elilo_opt.initrd[0] = CHAR_NULL;
+                               elilo_opt.vmcode[0] = CHAR_NULL;
+                               elilo_opt.prompt    = 1; 
+                               elilo_opt.timeout   = ELILO_DEFAULT_TIMEOUT;
+                               elilo_opt.delay     = 0;
+
+                               return ELILO_LOAD_RETRY;
+               }
+       }
+
+       if (elilo_opt.vmcode[0]) {
+
+               mmem->start_addr = 0; /* let the allocator decide */
 
+               switch(load_file(kname, mmem)) {
+                       case ELILO_LOAD_SUCCESS:
+                               break;
+                       case ELILO_LOAD_ERROR:
+                               goto exit_error;
+                       case ELILO_LOAD_ABORTED:
+                               if (imem->start_addr)
+                                       free(imem->start_addr);
+                               free_kmem();
                                /* we drop initrd in case we aborted the load */
                                elilo_opt.initrd[0] = CHAR_NULL;
+                               elilo_opt.vmcode[0] = CHAR_NULL;
                                elilo_opt.prompt    = 1; 
                                elilo_opt.timeout   = ELILO_DEFAULT_TIMEOUT;
                                elilo_opt.delay     = 0;
 
                                return ELILO_LOAD_RETRY;
                }
+
+               /* Test for a compressed image and unzip if found */
+               if (gzip_probe(mmem->start_addr, mmem->size) == 0 &&
+                   gunzip_image(mmem) != ELILO_LOAD_SUCCESS) {
+                       if (imem->start_addr)
+                               free(imem->start_addr);
+                       free(mmem->start_addr);
+                       free_kmem();
+                       /* we drop initrd in case we aborted the load */
+                       elilo_opt.initrd[0] = CHAR_NULL;
+                       elilo_opt.vmcode[0] = CHAR_NULL;
+                       elilo_opt.prompt    = 1; 
+                       elilo_opt.timeout   = ELILO_DEFAULT_TIMEOUT;
+                       elilo_opt.delay     = 0;
+
+                       return ELILO_LOAD_RETRY;
+               }
        }
        return ELILO_LOAD_SUCCESS;
 exit_error:
        free_kmem();
        if (imem->start_addr) free(imem->start_addr);
+       if (mmem->start_addr) free(mmem->start_addr);
 
        return ELILO_LOAD_ERROR;
 }
@@ -152,8 +213,8 @@ main_loop(EFI_HANDLE dev, CHAR16 **argv, INTN argc, INTN index, EFI_HANDLE image
        UINTN cookie;
        EFI_STATUS status = EFI_SUCCESS;
        kdesc_t kd;
-       memdesc_t imem;
-       INTN r;
+       memdesc_t imem, mmem;
+       INTN r, retries=0;
 
        /*
         * First place where we potentially do system dependent
@@ -164,12 +225,12 @@ main_loop(EFI_HANDLE dev, CHAR16 **argv, INTN argc, INTN index, EFI_HANDLE image
 
        for(;;) {
                kname[0] = cmdline_tmp[0] = cmdline[0] = CHAR_NULL;
-               imem.start_addr = 0; imem.pgcnt = 0;
+               imem.start_addr = 0; imem.pgcnt = 0; imem.size = 0;
                elilo_opt.sys_img_opts = NULL;
 
                if (kernel_chooser(argv, argc, index, kname, cmdline_tmp) == -1) goto exit_error;
 
-               switch (kernel_load(image, kname, &kd, &imem)) {
+               switch (kernel_load(image, kname, &kd, &imem, &mmem)) {
                        case ELILO_LOAD_SUCCESS: 
                                goto do_launch;
                        case ELILO_LOAD_ERROR:
@@ -183,15 +244,49 @@ do_launch:
 
        VERB_PRT(3, Print(L"final cmdline(%d): %s\n", r, cmdline));
 
+       /* Give user time to see the output before launch */
+       if (elilo_opt.debug || elilo_opt.verbose) {
+               r = wait_timeout(150);
+       /* have to turn off all console output(except error output) now before final get_mmemap()
+        * call or it can cause the efi map key to change and the ExitBootSvc call to fail,
+        * forcing debug and verbose options off is the surest way to enforce this.
+        */
+               elilo_opt.debug=0;
+               elilo_opt.verbose=0; 
+       }
+
        /* free resources associated with file accesses (before ExitBootServices) */
        close_devices();
 
        /* No console output permitted after create_boot_params()! */
-       if ((bp=create_boot_params(cmdline, &imem, &cookie)) == 0) goto error;
+       if ((bp=create_boot_params(cmdline, &imem, &mmem, &cookie)) == 0) goto error;
+
+       /* terminate bootservices 
+       * efi ExitBootSvcs spec: *note, get_memmap is called by create_boot_params() 
+       * An EFI OS loader must ensure that it has the system's current memory map at the time 
+       * it calls ExitBootServices(). This is done by passing in the current memory map's 
+       * MapKey value as returned by GetMemoryMap(). Care must be taken to ensure that the 
+       * memory map does not change between these two calls. It is suggested that 
+       * GetMemoryMap()be called immediately before calling ExitBootServices(). */
+
+retry:
+       status = uefi_call_wrapper(BS->ExitBootServices, 2, image, cookie);
+       if (EFI_ERROR(status)) 
+       {
+               ERR_PRT((L"\nExitBootSvcs: failed, memory map has changed.\n"));
+               if (retries < 2)
+               {       
+                       ERR_PRT((L"Main_Loop: Retrying,... have to rebuild boot params"));
+                       retries++;
+                       free_boot_params(bp);
+                       if ((bp=create_boot_params(cmdline, &imem, &mmem, &cookie)) == 0) goto error;                   
+                       goto retry;
+               } else {
+                       ERR_PRT((L"\nMain_Loop: tried ExitBootSvcs 3 times... retries exceeded.... giving up\n"));
+                       goto bad_exit;
+               }
+       }
 
-       /* terminate bootservices */
-       status = BS->ExitBootServices(image, cookie);
-       if (EFI_ERROR(status)) goto bad_exit;
 
        start_kernel(kd.kentry, bp);
        /* NOT REACHED */
@@ -205,6 +300,7 @@ bad_exit:
 error:
        free_kmem();
        if (imem.start_addr) free(imem.start_addr);
+       if (mmem.start_addr) free(mmem.start_addr);
        if (bp) free_boot_params(bp);
 exit_error:
        return ELILO_LOAD_ERROR;
@@ -221,6 +317,7 @@ elilo_help(VOID)
        Print(L"-v        verbose level(can appear multiple times)\n");
        Print(L"-a        always check for alternate kernel image\n");
        Print(L"-i file   load file as the initial ramdisk\n");
+       Print(L"-m file   load file as additional boot time vmm module\n");
        Print(L"-C file   indicate the config file to use\n");
        Print(L"-P        parse config file only (verify syntax)\n");
        Print(L"-D        enable debug prints\n");
@@ -252,7 +349,7 @@ fixupargs(EFI_LOADED_IMAGE *info)
 
 #define FAKE_ELILONAME L"elilo-forced"
 
-       status = BS->HandleProtocol (info->DeviceHandle, &PxeBaseCodeProtocol, (VOID **)&pxe);
+       status = uefi_call_wrapper(BS->HandleProtocol, 3, info->DeviceHandle, &PxeBaseCodeProtocol, (VOID **)&pxe);
        if (EFI_ERROR(status)) return;
 
        default_load_options      = info->LoadOptions;
@@ -310,7 +407,7 @@ check_edd30(VOID)
        UINT8           bool = FALSE;
        INTN            ret = -1;
 
-       status = RT->GetVariable(L"EDD30", &edd30_guid, NULL, &l, &bool);
+       status = uefi_call_wrapper(RT->GetVariable, 5, L"EDD30", &edd30_guid, NULL, &l, &bool);
        if (status == EFI_BUFFER_TOO_SMALL || (bool != TRUE && bool != FALSE)) {
                ERR_PRT((L"Warning: EDD30 EFI variable is not boolean value: forcing it to TRUE"));
                return -1;
@@ -340,7 +437,7 @@ force_edd30(VOID)
        UINT8           bool;
 
        bool = TRUE;
-       status = RT->SetVariable(L"EDD30", &edd30_guid, EDD30_ATTR, l, &bool);
+       status = uefi_call_wrapper(RT->SetVariable, 5, L"EDD30", &edd30_guid, EDD30_ATTR, l, &bool);
        if (EFI_ERROR(status)) {
                ERR_PRT((L"can't set EDD30 variable: ignoring it"));
                return -1;
@@ -368,8 +465,8 @@ efi_main (EFI_HANDLE image, EFI_SYSTEM_TABLE *system_tab)
        CHAR16 dpath[FILENAME_MAXLEN];
        CHAR16 *devpath;
 
-       //elilo_opt.verbose=3;
-       //elilo_opt.debug=1;
+       elilo_opt.verbose=0;
+       elilo_opt.debug=0;
 
        /* initialize global variable */
        systab = system_tab;
@@ -384,19 +481,18 @@ efi_main (EFI_HANDLE image, EFI_SYSTEM_TABLE *system_tab)
         * mode.
         * XXX: clean this up !
         */
-       BS->SetWatchdogTimer(0, 0x0, 0, NULL);
+       uefi_call_wrapper(BS->SetWatchdogTimer, 4, 0, 0x0, 0, NULL);
 
        /* initialize memory allocator */
        if (alloc_init() == -1) return EFI_LOAD_ERROR;
 
-       status = BS->HandleProtocol(image, &LoadedImageProtocol, (VOID **) &info);
+       status = uefi_call_wrapper(BS->HandleProtocol, 3, image, &LoadedImageProtocol, (VOID **) &info);
        if (EFI_ERROR(status)) {
                ERR_PRT((L"image handle does not support LOADED_IMAGE protocol"));
                return EFI_LOAD_ERROR;
        }
 
-       VERB_PRT(5,Print(L"Loaded at 0x%lx size=%d bytes code=%d data=%d\n", info->ImageBase, info->ImageSize, info->ImageCodeType, info->ImageDataType));
-
+       VERB_PRT(5,Print(L"Loaded at " PTR_FMT " size=%ld bytes code=%d data=%d\n", info->ImageBase, info->ImageSize, info->ImageCodeType, info->ImageDataType));
        /*
         * verify EDD3.0 status. Users may have to reboot 
         */
@@ -491,6 +587,13 @@ efi_main (EFI_HANDLE image, EFI_SYSTEM_TABLE *system_tab)
                                }
                                StrCpy(elilo_opt.initrd, Optarg);
                                break;
+                       case 'm':
+                               if (StrLen(Optarg) >= FILENAME_MAXLEN-1) {
+                                       Print(L"vmm module filename is limited to %d characters\n", FILENAME_MAXLEN);
+                                       goto do_exit;
+                               }
+                               StrCpy(elilo_opt.vmcode, Optarg);
+                               break;
                        case 'C':
                                if (StrLen(Optarg) >= FILENAME_MAXLEN-1) {
                                        Print(L"config filename is limited to %d characters\n", FILENAME_MAXLEN);
@@ -533,7 +636,7 @@ efi_main (EFI_HANDLE image, EFI_SYSTEM_TABLE *system_tab)
                                goto do_exit;
                }
        }
-       DBG_PRT((L"Optind=%d optarg=%x argc=%d", Optind, Optarg, argc));
+       DBG_PRT((L"Optind=%d optarg=" PTR_FMT " argc=%d", Optind, Optarg, argc));
 
        /*
         * we can't defer this phase any longer...
@@ -548,23 +651,37 @@ efi_main (EFI_HANDLE image, EFI_SYSTEM_TABLE *system_tab)
        /*
         * set per fileops defaults files for configuration and kernel
         */
-       fops_setdefaults(elilo_opt.default_config, elilo_opt.default_kernel, FILENAME_MAXLEN, devpath);
+       fops_setdefaults(elilo_opt.default_configs, elilo_opt.default_kernel, FILENAME_MAXLEN, devpath);
 
        /*
         * XXX: won't be visible if verbose not required from command line
         */
        VERB_PRT(2,Print(L"Default config: %s\nDefault_kernel: %s\n",
-                       elilo_opt.default_config,elilo_opt.default_kernel));
+                       elilo_opt.default_configs[0].fname, elilo_opt.default_kernel));
        /*
         * use default config file if not specified by user
         */
-       ptr = elilo_opt.config[0] == CHAR_NULL ?  (retry=1,elilo_opt.default_config) : (retry=0,elilo_opt.config);
+       ptr = elilo_opt.config[0] == CHAR_NULL ?  (retry=1,elilo_opt.default_configs[0].fname) : (retry=0,elilo_opt.config);
 
        /*
         * parse config file (verbose becomes visible if set)
         */
-       ret = read_config(ptr, retry);
-       Print(L"read_config=%r\n", ret);
+       ret = read_config(ptr);
+       VERB_PRT(1,Print(L"read_config=%r\n", ret));
+
+        /* Only try the default config filenames if user did not specify a
+         * config filename on the command line */
+        if (elilo_opt.config[0] == CHAR_NULL) {
+                while ((ret != EFI_SUCCESS) &&
+                       (retry < MAX_DEFAULT_CONFIGS) &&
+                       (elilo_opt.default_configs[retry].fname[0] != CHAR_NULL)) {
+
+                        ptr = elilo_opt.default_configs[retry].fname;
+                        ret = read_config(ptr);
+                        VERB_PRT(1,Print(L"read_config=%r\n", ret));
+                        retry += 1;
+                }
+        }
        /*
         * when the config file is not found, we fail only if:
         *      - the user did not specified interactive mode
@@ -593,11 +710,19 @@ efi_main (EFI_HANDLE image, EFI_SYSTEM_TABLE *system_tab)
         * if there was an error when parsing the config file, then
         * we force interactive mode to give a chance to the user.
         * We also clear the error. 
-        */
-       if (ret && argc == 1) {
-               Print(L"forcing interactive mode because of errors\n");
+         */
+       if (ret != EFI_SUCCESS) {
+               Print(L"forcing interactive mode due to config file error(s)\n");
                elilo_opt.prompt = 1;
        }
+        /*
+        * However, if the user specified a kernel on the command line
+        * then we don't go to interactive mode, even if there was an option in
+         * the config file telling us to do so.
+        */
+       if (argc > Optind) {
+            elilo_opt.prompt = 0;
+        }
 
        /*
         * If EDD30 EFI variable was not set to TRUE (or not defined), we
@@ -613,13 +738,6 @@ efi_main (EFI_HANDLE image, EFI_SYSTEM_TABLE *system_tab)
 
        ret = EFI_LOAD_ERROR;
 
-       /*
-        * if the user specified a kernel on the command line
-        * then we don't go to interactive mode even if it
-        * was set in the config file or set because of an
-        * error parsing the config file.
-        */
-       if (argc > Optind) elilo_opt.prompt = 0;
 
 
        /* set default timeout if going interactive */