/*
- * 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>.
* 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:
}
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[CMDLINE_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;
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;
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;
}
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
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:
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 */
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;
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");
#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;
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;
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;
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;
* 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
*/
}
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);
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...
/*
* 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
* 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
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 */