/* * elilo.c - IA-64/IA-32/x86_64 EFI Linux loader * * Copyright (C) 1999-2003 Hewlett-Packard Co. * Contributed by David Mosberger . * Contributed by Stephane Eranian * * Copyright (C) 1999-2000 VA Linux Systems * Contributed by Johannes Erdfelt . * * Copyright (C) 2006-2009 Intel Corporation * Contributed by Fenghua Yu * Contributed by Bibo Mao * Contributed by Chandramouli Narayanan * * This file is part of the ELILO, the EFI Linux boot loader. * * ELILO is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2, or (at your option) * any later version. * * ELILO is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with ELILO; see the file COPYING. If not, write to the Free * Software Foundation, 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * * Please check out the elilo.txt for complete documentation on how * to use this program. */ #include #include #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_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: * kd = address of kernel entry point * + end address of kernel code+data+bss * + kernel entry point * Return: * ELILO_LOAD_ERROR : if something went wrong * ELILO_LOAD_ABORTED : user interruption while loading * ELILO_LOAD_SUCCESS : otherwise */ static INTN do_kernel_load(CHAR16 *kname, kdesc_t *kd) { loader_ops_t *ldops; EFI_STATUS status; fops_fd_t fd; status = fops_open(kname, &fd); if (EFI_ERROR(status)) { ERR_PRT((L"Kernel file not found %s", kname)); return ELILO_LOAD_ERROR; } fops_close(fd); ldops = loader_probe(kname); if (ldops == NULL) { ERR_PRT((L"Cannot find a loader for %s", kname)); return ELILO_LOAD_ERROR; } VERB_PRT(1,Print(L"Using %s loader\n", ldops->ld_name)); return ldops->ld_load_kernel(kname, kd); } INTN 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(kernel, kd)) { case ELILO_LOAD_SUCCESS: break; case ELILO_LOAD_ERROR: /* XXX: add fallback support */ return ELILO_LOAD_ERROR; 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; elilo_opt.timeout = ELILO_DEFAULT_TIMEOUT; elilo_opt.delay = 0; return ELILO_LOAD_RETRY; } 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_file(elilo_opt.initrd, imem)) { case ELILO_LOAD_SUCCESS: break; case ELILO_LOAD_ERROR: goto exit_error; case ELILO_LOAD_ABORTED: 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; } static INTN main_loop(EFI_HANDLE dev, CHAR16 **argv, INTN argc, INTN index, EFI_HANDLE image) { CHAR16 kname[FILENAME_MAXLEN]; CHAR16 cmdline_tmp[CMDLINE_MAXLEN]; CHAR16 cmdline[CMDLINE_MAXLEN]; VOID *bp; UINTN cookie; EFI_STATUS status = EFI_SUCCESS; kdesc_t kd; memdesc_t imem, mmem; INTN r, retries=0; /* * First place where we potentially do system dependent * operations. It is a one time opportunity before entering * the main loop */ if (sysdeps_preloop_actions(dev, argv, argc, index, image) == -1) return -1; for(;;) { kname[0] = cmdline_tmp[0] = cmdline[0] = CHAR_NULL; 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, &mmem)) { case ELILO_LOAD_SUCCESS: goto do_launch; case ELILO_LOAD_ERROR: goto exit_error; /* otherwise we retry ! */ } } do_launch: r =subst_vars(cmdline_tmp, cmdline, CMDLINE_MAXLEN); 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, &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; } } start_kernel(kd.kentry, bp); /* NOT REACHED */ ERR_PRT((L"start_kernel() return !")); bad_exit: /* * we are still in BootService mode */ ERR_PRT((L"ExitBootServices failed %r", status)); 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; } static VOID elilo_help(VOID) { Print(L"-d secs timeout in 10th of second before booting\n"); Print(L"-h this help text\n"); Print(L"-V print version\n"); 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"); Print(L"-p force interactive mode\n"); Print(L"-c name image chooser to use\n"); Print(L"-E do not force EDD30 variable\n"); sysdeps_print_cmdline_opts(); Print(L"\n"); print_config_options(); } /* * XXX: hack to work around a problem with EFI command line argument when booted * from network. In this case, it looks like LoadOptions/LoadOptionsSize contain * garbage */ static CHAR16 *default_load_options; static UINTN default_load_options_size; static INTN done_fixups; static VOID fixupargs(EFI_LOADED_IMAGE *info) { EFI_STATUS status; EFI_PXE_BASE_CODE *pxe; #define FAKE_ELILONAME L"elilo-forced" status = uefi_call_wrapper(BS->HandleProtocol, 3, info->DeviceHandle, &PxeBaseCodeProtocol, (VOID **)&pxe); if (EFI_ERROR(status)) return; default_load_options = info->LoadOptions; default_load_options_size = info->LoadOptionsSize; info->LoadOptions = FAKE_ELILONAME; info->LoadOptionsSize = StrLen(info->LoadOptions)*sizeof(CHAR16); done_fixups = 1; } /* * we restore the arguments in case we modified them just to make sure * we don't confuse caller. */ static VOID unfixupargs(EFI_LOADED_IMAGE *info) { if (done_fixups == 0) return; info->LoadOptions = default_load_options; info->LoadOptionsSize = default_load_options_size; } /* * in order to get fully detailed EFI path names to devices, EDD3.0 specification must * be turned on. On new versions of EFI, this is the default. An environment variable * called EDD30 reflects the current settings. If true, then EDD3.0 is enabled * and device path names show the detailed device types. If false, then a default * generic name is used instead. This has some implications of ELILO's ability to * provide a better naming scheme for detected devices. If EDD3.0 is enabled * then much more precise names can be used which makes it easy to use. * If EDD3.0 is nont enabled, ELILO will still work but will use very generic names * for devices. * * ELILO will check the value of the variable. If not true, then it will force it to * true. This will require a reboot for EFI to make use of the new value. * Return: * EFI_SUCCESS: if variable is already true or was set to true * EFI error code: otherwise, like when could not forced variable or unrecognized variable content */ #define EDD30_GUID \ {0x964e5b21, 0x6459, 0x11d2, { 0x8e, 0x39, 0x00, 0xa0, 0xc9, 0x69, 0x72, 0x3b}} #define EDD30_ATTR (EFI_VARIABLE_RUNTIME_ACCESS|EFI_VARIABLE_BOOTSERVICE_ACCESS|EFI_VARIABLE_NON_VOLATILE) static EFI_GUID edd30_guid = EDD30_GUID; static INTN check_edd30(VOID) { EFI_STATUS status; UINTN l = sizeof(BOOLEAN); UINT8 bool = FALSE; INTN ret = -1; 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; } if (status == EFI_SUCCESS && bool == TRUE) { VERB_PRT(3, Print(L"EDD30 is TRUE\n")); elilo_opt.edd30_on = TRUE; ret = 0; } else { VERB_PRT(4, if (status != EFI_SUCCESS) { Print(L"EDD30 EFI variable not defined\n"); } else { Print(L"EDD30 EFI variable is false\n"); } ); } return ret; } static INTN force_edd30(VOID) { EFI_STATUS status; UINTN l = sizeof(BOOLEAN); UINT8 bool; bool = TRUE; 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; } VERB_PRT(3, Print(L"EDD30 variable forced to TRUE. You should reboot to take advantage of EDD3.0.\n")); return 0; } /* * That's equivalent of main(): main entry point */ EFI_STATUS efi_main (EFI_HANDLE image, EFI_SYSTEM_TABLE *system_tab) { CHAR16 *argv[MAX_ARGS]; CHAR16 optstring[MAX_ARGS]; EFI_LOADED_IMAGE *info; EFI_STATUS status, ret = EFI_LOAD_ERROR; INTN argc = 0, c; INTN edd30_status, retry; CHAR16 *ptr, *arglist = NULL; BOOLEAN devices_initialized = FALSE; CHAR16 dpath[FILENAME_MAXLEN]; CHAR16 *devpath; elilo_opt.verbose=0; elilo_opt.debug=0; /* initialize global variable */ systab = system_tab; /* initialize EFI library */ InitializeLib(image, systab); /* * disable the platform watchdog timer if we go interactive * XXX: we should reinstate it once we're done * It seems like with PXE, the timer is set to a few minutes * and sometimes triggers if we stay too long in interactive * mode. * XXX: clean this up ! */ uefi_call_wrapper(BS->SetWatchdogTimer, 4, 0, 0x0, 0, NULL); /* initialize memory allocator */ if (alloc_init() == -1) return EFI_LOAD_ERROR; 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 " 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 */ edd30_status = check_edd30(); /* * initialize configuration empire */ if (config_init() == -1) goto do_exit; /* * architecture-specific initializations */ if (sysdeps_init(info->DeviceHandle) == -1) goto do_exit; if (sysdeps_register_options() == -1) goto do_exit; /* * This may be required in case Elilo was booted with absolutely no arguments * Elilo's logic is that just like normal Linux programs at least one argument * (argv[0]) exists at all times and that it usually gives the name of the program * (the command used to start it). ERR_PRT((L"LoadOptions=%x OpenSize=%d", info->LoadOptions, info->LoadOptionsSize)); */ /* * in case there is something wrong with the default boot_device * we default to basic fixups and we need to force interactive * mode to make sure the user has a chance of specifying a kernel */ fixupargs(info); #if 0 Print(L"LoadOptions=%x OpenSize=%d\n", info->LoadOptions, info->LoadOptionsSize); { INTN i; for (i=0; i< info->LoadOptionsSize>>1; i++) Print(L"options[%d]=%d (%c)\n", i, ((CHAR16 *)info->LoadOptions)[i], ((CHAR16 *)info->LoadOptions)[i]); } #endif /* * we must copy argument because argify modifies the string. * This caused problems when arguments are coming from NVRAM * as passed by the EFI boot manager * * We add an extra character to the buffer in case the LoadOptions is not * NULL terminated. The extra space will be used to ADD the extra terminator. */ arglist = alloc(info->LoadOptionsSize+sizeof(CHAR16), EfiLoaderData); if (arglist == NULL) { ERR_PRT((L"cannot copy argument list")); return EFI_OUT_OF_RESOURCES; } Memcpy(arglist, info->LoadOptions, info->LoadOptionsSize); argc = argify(arglist,info->LoadOptionsSize, argv); StrCpy(optstring, ELILO_SHARED_CMDLINE_OPTS); StrCat(optstring, sysdeps_get_cmdline_opts()); while ((c=Getopt(argc, argv, optstring)) != -1 ) { switch(c) { case 'a': elilo_opt.alt_check = 1; break; case 'D': elilo_opt.debug = 1; break; case 'p': elilo_opt.prompt = 1; break; case 'v': elilo_opt.verbose++; if (elilo_opt.verbose > 5) elilo_opt.verbose = 5; break; case 'h': elilo_help(); ret = EFI_SUCCESS; goto do_exit; case 'd': /* * zero is a valid value here, so we use the delay-set to mark the * fact that the user specified a value on cmdline. See config.c */ elilo_opt.delay = Atoi(Optarg); elilo_opt.delay_set = 1; break; case 'E': /* don't force EDD30 EFI variable if not already set */ elilo_opt.edd30_no_force = 1; break; case 'i': if (StrLen(Optarg) >= FILENAME_MAXLEN-1) { Print(L"initrd filename is limited to %d characters\n", FILENAME_MAXLEN); goto do_exit; } 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; } StrCpy(elilo_opt.config, Optarg); break; case 'M': /* builtin debug tool */ { mmap_desc_t mdesc; if (get_memmap(&mdesc) == -1) { Print(L"Cannot get memory map\n"); return EFI_LOAD_ERROR; } print_memmap(&mdesc); ret = EFI_SUCCESS; goto do_exit; } case 'V': Print(L"ELILO v%s for EFI/%a\n", ELILO_VERSION, ELILO_ARCH); ret = EFI_SUCCESS; goto do_exit; case 'P': /* cmdline only option */ elilo_opt.parse_only = 1; break; case 'c': if (StrLen(Optarg) >= FILENAME_MAXLEN-1) { Print(L"chooser name is limited to %d characters\n", FILENAME_MAXLEN); goto do_exit; } StrCpy(elilo_opt.chooser, Optarg); break; default: /* * try system dependent options before declaring error */ if (sysdeps_getopt(c, Optind, Optarg) == 0) continue; Print(L"Unknown option -%c\n", (CHAR16)c); goto do_exit; } } DBG_PRT((L"Optind=%d optarg=" PTR_FMT " argc=%d", Optind, Optarg, argc)); /* * we can't defer this phase any longer... * Must be done after the elilo_opt are initialized (at least partially) */ if (init_devices(info->DeviceHandle) == -1) goto do_exit; devices_initialized = TRUE; devpath = DevicePathToStr(info->FilePath); /* * set per fileops defaults files for configuration and kernel */ 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_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_configs[0].fname) : (retry=0,elilo_opt.config); /* * parse config file (verbose becomes visible if set) */ 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 * - the user did not explicitely specify the config file */ if (ret == EFI_NOT_FOUND || ret == EFI_TFTP_ERROR) { if (elilo_opt.prompt == 0 && elilo_opt.config[0] != CHAR_NULL) { Print(L"config file %s not found\n", ptr); goto do_exit; } fops_getdefault_path(dpath, FILENAME_MAXLEN); if (ret == EFI_TFTP_ERROR) Print(L"no config file found on TFTP server in %s\n", dpath); else Print(L"no config file found in %s\n", dpath); } /* * stop if just doing parsing */ if (elilo_opt.parse_only) { if (ret == EFI_SUCCESS) Print(L"Config file %s parsed successfully\n", ptr); goto do_exit; } /* * 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 != 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 * we try to force it. This will take effect at the next reboot. * * Some controllers don't have EDD30 support, in this case forcing it * may cause problems, therefore we check the edd_no_force option * before making the call. */ if (edd30_status == -1 && elilo_opt.edd30_no_force == 0) { force_edd30(); } ret = EFI_LOAD_ERROR; /* set default timeout if going interactive */ if ((elilo_opt.prompt && elilo_opt.timeout == 0)) { elilo_opt.timeout = ELILO_DEFAULT_TIMEOUT; } /* * which chooser we will use */ if (init_chooser(info->DeviceHandle) == -1) { ERR_PRT((L"Cannot find a decent chooser\n")); goto do_exit; } //if (elilo_opt.prompt == 0) VERB_PRT(1, print_devices()); main_loop(info->DeviceHandle, argv, argc, Optind, image); /* should not return */ do_exit: unfixupargs(info); //if (arglist) free(arglist); /* free all resources assiocated with file accesses */ if (devices_initialized) close_devices(); /* garbage collect all remaining allocations */ free_all_memory(); return ret; }