From c3931154de674962db4a633794ec9dcd3f9d4fef Mon Sep 17 00:00:00 2001 From: Jean-Christophe PLAGNIOL-VILLARD Date: Wed, 15 Feb 2017 20:34:13 +0100 Subject: efi: move startup and payload to common/efi Signed-off-by: Jean-Christophe PLAGNIOL-VILLARD Signed-off-by: Sascha Hauer --- common/efi/efi-image.c | 293 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 293 insertions(+) create mode 100644 common/efi/efi-image.c (limited to 'common/efi/efi-image.c') diff --git a/common/efi/efi-image.c b/common/efi/efi-image.c new file mode 100644 index 0000000000..885348da45 --- /dev/null +++ b/common/efi/efi-image.c @@ -0,0 +1,293 @@ +/* + * efi-image.c - barebox EFI payload support + * + * Copyright (c) 2014 Sascha Hauer , Pengutronix + * + * See file CREDITS for list of people who contributed to this + * project. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct linux_kernel_header { + /* first sector of the image */ + uint8_t code1[0x0020]; + uint16_t cl_magic; /**< Magic number 0xA33F */ + uint16_t cl_offset; /**< The offset of command line */ + uint8_t code2[0x01F1 - 0x0020 - 2 - 2]; + uint8_t setup_sects; /**< The size of the setup in sectors */ + uint16_t root_flags; /**< If the root is mounted readonly */ + uint16_t syssize; /**< obsolete */ + uint16_t swap_dev; /**< obsolete */ + uint16_t ram_size; /**< obsolete */ + uint16_t vid_mode; /**< Video mode control */ + uint16_t root_dev; /**< Default root device number */ + uint16_t boot_flag; /**< 0xAA55 magic number */ + + /* second sector of the image */ + uint16_t jump; /**< Jump instruction (this is code!) */ + uint32_t header; /**< Magic signature "HdrS" */ + uint16_t version; /**< Boot protocol version supported */ + uint32_t realmode_swtch; /**< Boot loader hook */ + uint16_t start_sys; /**< The load-low segment (obsolete) */ + uint16_t kernel_version; /**< Points to kernel version string */ + uint8_t type_of_loader; /**< Boot loader identifier */ + uint8_t loadflags; /**< Boot protocol option flags */ + uint16_t setup_move_size; /**< Move to high memory size */ + uint32_t code32_start; /**< Boot loader hook */ + uint32_t ramdisk_image; /**< initrd load address */ + uint32_t ramdisk_size; /**< initrd size */ + uint32_t bootsect_kludge; /**< obsolete */ + uint16_t heap_end_ptr; /**< Free memory after setup end */ + uint8_t ext_loader_ver; /**< boot loader's extension of the version number */ + uint8_t ext_loader_type; /**< boot loader's extension of its type */ + uint32_t cmd_line_ptr; /**< Points to the kernel command line */ + uint32_t initrd_addr_max; /**< Highest address for initrd */ + uint32_t kernel_alignment; /**< Alignment unit required by the kernel */ + uint8_t relocatable_kernel; /** */ + uint8_t min_alignment; /** */ + uint16_t xloadflags; /** */ + uint32_t cmdline_size; /** */ + uint32_t hardware_subarch; /** */ + uint64_t hardware_subarch_data; /** */ + uint32_t payload_offset; /** */ + uint32_t payload_length; /** */ + uint64_t setup_data; /** */ + uint64_t pref_address; /** */ + uint32_t init_size; /** */ + uint32_t handover_offset; /** */ +} __attribute__ ((packed)); + +int efi_load_image(const char *file, efi_loaded_image_t **loaded_image, + efi_handle_t *h) +{ + void *exe; + size_t size; + efi_handle_t handle; + efi_status_t efiret = EFI_SUCCESS; + + exe = read_file(file, &size); + if (!exe) + return -EINVAL; + + efiret = BS->load_image(false, efi_parent_image, efi_device_path, exe, size, + &handle); + if (EFI_ERROR(efiret)) { + pr_err("failed to LoadImage: %s\n", efi_strerror(efiret)); + goto out; + }; + + efiret = BS->open_protocol(handle, &efi_loaded_image_protocol_guid, + (void **)loaded_image, + efi_parent_image, NULL, EFI_OPEN_PROTOCOL_GET_PROTOCOL); + if (EFI_ERROR(efiret)) { + pr_err("failed to OpenProtocol: %s\n", efi_strerror(efiret)); + BS->unload_image(handle); + goto out; + } + + *h = handle; +out: + memset(exe, 0, size); + free(exe); + return -efi_errno(efiret); +} + +static int efi_execute_image(const char *file) +{ + efi_handle_t handle; + efi_loaded_image_t *loaded_image; + efi_status_t efiret; + struct linux_kernel_header *image_header; + const char *options; + bool is_driver; + int ret; + + ret = efi_load_image(file, &loaded_image, &handle); + if (ret) + return ret; + + is_driver = (loaded_image->image_code_type == EFI_BOOT_SERVICES_CODE) || + (loaded_image->image_code_type == EFI_RUNTIME_SERVICES_CODE); + + image_header = (struct linux_kernel_header *)loaded_image->image_base; + if (image_header->boot_flag == 0xAA55 && + image_header->header == 0x53726448) { + pr_debug("Linux kernel detected. Adding bootargs."); + options = linux_bootargs_get(); + pr_err("add linux options '%s'\n", options); + loaded_image->load_options = xstrdup_char_to_wchar(options); + loaded_image->load_options_size = + (strlen(options) + 1) * sizeof(wchar_t); + shutdown_barebox(); + } + + efiret = BS->start_image(handle, NULL, NULL); + if (EFI_ERROR(efiret)) + pr_err("failed to StartImage: %s\n", efi_strerror(efiret)); + + if (!is_driver) + BS->unload_image(handle); + + efi_connect_all(); + efi_register_devices(); + + return -efi_errno(efiret); +} + +#ifdef __x86_64__ +typedef void(*handover_fn)(void *image, efi_system_table_t *table, + struct linux_kernel_header *header); + +static inline void linux_efi_handover(efi_handle_t handle, + struct linux_kernel_header *header) +{ + handover_fn handover; + + asm volatile ("cli"); + handover = (handover_fn)((long)header->code32_start + 512 + + header->handover_offset); + handover(handle, efi_sys_table, header); +} +#else +typedef void(*handover_fn)(VOID *image, EFI_SYSTEM_TABLE *table, + struct SetupHeader *setup) __attribute__((regparm(0))); + +static inline void linux_efi_handover(efi_handle_t handle, + struct linux_kernel_header *header) +{ + handover_fn handover; + + handover = (handover_fn)((long)header->code32_start + + header->handover_offset); + handover(handle, efi_sys_table, header); +} +#endif + +static int do_bootm_efi(struct image_data *data) +{ + void *tmp; + void *initrd = NULL; + size_t size; + efi_handle_t handle; + int ret; + const char *options; + efi_loaded_image_t *loaded_image; + struct linux_kernel_header *image_header, *boot_header; + + ret = efi_load_image(data->os_file, &loaded_image, &handle); + if (ret) + return ret; + + image_header = (struct linux_kernel_header *)loaded_image->image_base; + + if (image_header->boot_flag != 0xAA55 || + image_header->header != 0x53726448 || + image_header->version < 0x20b || + !image_header->relocatable_kernel) { + pr_err("Not a valid kernel image!\n"); + BS->unload_image(handle); + return -EINVAL; + } + + boot_header = xmalloc(0x4000); + memset(boot_header, 0, 0x4000); + memcpy(boot_header, image_header, sizeof(*image_header)); + + boot_header->type_of_loader = 0xff; + + if (data->initrd_file) { + tmp = read_file(data->initrd_file, &size); + initrd = xmemalign(PAGE_SIZE, PAGE_ALIGN(size)); + memcpy(initrd, tmp, size); + memset(initrd + size, 0, PAGE_ALIGN(size) - size); + free(tmp); + boot_header->ramdisk_image = (uint64_t)initrd; + boot_header->ramdisk_size = PAGE_ALIGN(size); + } + + options = linux_bootargs_get(); + boot_header->cmd_line_ptr = (uint64_t)options; + boot_header->cmdline_size = strlen(options); + + boot_header->code32_start = (uint64_t)loaded_image->image_base + + (image_header->setup_sects+1) * 512; + + if (bootm_verbose(data)) { + printf("\nStarting kernel at 0x%p", loaded_image->image_base); + if (data->initrd_file) + printf(", initrd at 0x%08x", + boot_header->ramdisk_image); + printf("...\n"); + } + + if (data->dryrun) { + BS->unload_image(handle); + free(boot_header); + free(initrd); + return 0; + } + + efi_set_variable_usec("LoaderTimeExecUSec", &efi_systemd_vendor_guid, + get_time_ns()/1000); + + shutdown_barebox(); + linux_efi_handover(handle, boot_header); + + return 0; +} + +static struct image_handler efi_handle_tr = { + .name = "EFI Application", + .bootm = do_bootm_efi, + .filetype = filetype_exe, +}; + +static int efi_execute(struct binfmt_hook *b, char *file, int argc, char **argv) +{ + return efi_execute_image(file); +} + +static struct binfmt_hook binfmt_efi_hook = { + .type = filetype_exe, + .hook = efi_execute, +}; + +static int efi_register_image_handler(void) +{ + register_image_handler(&efi_handle_tr); + binfmt_register(&binfmt_efi_hook); + + return 0; +} +late_initcall(efi_register_image_handler); -- cgit v1.2.3