From 4c87a1868835d05f1cadae7b8ad6a7c95d9d9c0e Mon Sep 17 00:00:00 2001 From: Ross Philipson Date: Tue, 14 Mar 2017 15:40:33 -0400 Subject: Initial commit of EFI TBOOT work from internal project. Signed-off-by: Ross Philipson --- tboot/efiboot.c | 613 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 613 insertions(+) create mode 100644 tboot/efiboot.c (limited to 'tboot/efiboot.c') diff --git a/tboot/efiboot.c b/tboot/efiboot.c new file mode 100644 index 0000000..37ff734 --- /dev/null +++ b/tboot/efiboot.c @@ -0,0 +1,613 @@ +/* + * efiboot.c: EFI boot entry, early relocation and load code. + * + * Copyright (c) 2017 Assured Information Security. + * + * Ross Philipson + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * * Neither the name of the Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static EFI_HANDLE g_image_handle; +static EFI_HANDLE g_device_handle; +static EFI_DEVICE_PATH *g_device_path; +static void *g_init_base; /* before reloc */ + void *g_image_base; /* after reloc */ + void *g_rtmem_base; /* base of TBOOT runtime memory */ + uint64_t g_image_size; + void *g_text_base; + uint64_t g_text_size; + void *g_bss_base; + uint64_t g_bss_size; + +static efi_file_t *g_configs; +static EFI_FILE_IO_INTERFACE *g_file_system = NULL; + +/* Store raw config files in MLE so they can be measured */ +static __text uint8_t g_tboot_config_file[EFI_MAX_CONFIG_FILE]; +static __text uint8_t g_xen_config_file[EFI_MAX_CONFIG_FILE]; + +#ifdef EFI_DEBUG +static void efi_debug_pause(void) +{ + EFI_STATUS status; + EFI_INPUT_KEY key; + + ST->ConIn->Reset(ST->ConIn, FALSE); + while ((status = ST->ConIn->ReadKeyStroke(ST->ConIn, &key)) == EFI_NOT_READY); +} + +static void efi_debug_print_g(void) +{ + printk("EFI global:\n"); + printk(" g_image_handle = %p\n", g_image_handle); + printk(" g_device_handle = %p\n", g_device_handle); + printk(" g_device_path = %p\n", g_device_path); + printk(" g_init_base = %p\n", g_init_base); + printk(" g_image_base = %p\n", g_image_base); + printk(" g_rtmem_base = %p\n", g_rtmem_base); + printk(" g_image_size = %x\n", (uint32_t)g_image_size); + + efi_debug_pause(); +} + +static void efi_begin_launch(efi_xen_tboot_data_t *xtd); + +static void efi_debug_print_v(efi_tboot_xen_var_t *v) +{ + printk("EFI shared variable:\n"); + printk(" revision = %llx\n", v->revision); + printk(" xen_config = %p\n", v->xen_config); + printk(" xen_config_size = %llx\n", v->xen_config_size); + printk(" begin_launch_cb = %llx\n", v->begin_launch_cb); + printk(" begin_launch = %p\n", efi_begin_launch); + + efi_debug_pause(); +} + +static void efi_debug_print_w(const char *pfx, const wchar_t *wstr) +{ + char *p = wtoa_alloc(wstr); + printk("%s %s\n", pfx, p); + BS->FreePool(p); +} + +#define efi_debug_print_s(p, s) printk("%s %s\n", p, s) + +#else +#define efi_debug_pause() +#define efi_debug_print_g() +#define efi_debug_print_v(s) +#define efi_debug_print_w(p, w) +#define efi_debug_print_s(p, s) +#endif + +static EFI_STATUS efi_start_next_image(const wchar_t *path) +{ + EFI_STATUS status = EFI_SUCCESS; + EFI_DEVICE_PATH *dev_path = NULL; + EFI_HANDLE image_handle = NULL; + EFI_LOADED_IMAGE *loaded_image; + + dev_path = efi_get_device_path(path, g_device_handle); + if (dev_path == NULL) { + char *p = wtoa_alloc(path); + printk("Failed to get device path for file %s\n", p); + BS->FreePool(p); + return EFI_INVALID_PARAMETER; + } + + status = BS->LoadImage(FALSE, + g_image_handle, + dev_path, + NULL, + 0, + &image_handle); + if (EFI_ERROR(status)) { + printk("Failed to load image - status: %d\n", status); + goto out; + } + + status = BS->HandleProtocol(image_handle, + &LoadedImageProtocol, + (VOID*)&loaded_image); + if (EFI_ERROR(status)) { + printk("Failed to get loaded image info - status: %d\n", status); + goto out; + } + efi_store_xen_info(loaded_image->ImageBase, loaded_image->ImageSize); + + status = BS->StartImage(image_handle, NULL, NULL); + if (EFI_ERROR(status)) + printk("Failed to start image - status: %d\n", status); + +out: + BS->FreePool(dev_path); + return status; +} + +void efi_launch_kernel(void) +{ + EFI_STATUS status; + wchar_t *file_path; + + file_path = atow_alloc(efi_cfg_get_value(EFI_CONFIG_TBOOT_PARSED, + SECTION_TBOOT, ITEM_XENPATH)); + if (!file_path) { + printk("Failed to allocate buffer for Xen file\n"); + status = EFI_OUT_OF_RESOURCES; + goto out; + } + + status = efi_start_next_image(file_path); + /* If we are still here then someting failed anyway */ +out: + ST->RuntimeServices->ResetSystem(EfiResetShutdown, EFI_OUT_OF_RESOURCES, 0, NULL); +} + +static void efi_begin_launch(efi_xen_tboot_data_t *xtd) +{ + g_post_ebs = true; + begin_launch(xtd); +} + +static EFI_STATUS efi_setup_tboot_xen_var(void) +{ + EFI_STATUS status = EFI_SUCCESS; + efi_tboot_xen_var_t var; + + memset(&var, 0, sizeof(efi_tboot_xen_var_t)); + var.revision = EFI_TBOOT_XEN_REV; + var.begin_launch_cb = (uint64_t)efi_begin_launch; + var.xen_config = g_configs[EFI_CONFIG_XEN].u.buffer; + var.xen_config_size = g_configs[EFI_CONFIG_XEN].size; + + status = RT->SetVariable(EFI_TBOOT_XEN_NAME, + &TbootXenGuid, + EFI_VARIABLE_BOOTSERVICE_ACCESS|EFI_VARIABLE_RUNTIME_ACCESS, + sizeof(efi_tboot_xen_var_t), + &var); + if (EFI_ERROR(status)) + printk("Failed to set shared RT variable - status: %d\n", status); + + efi_debug_print_v(&var); + + return status; +} + +static bool efi_is_platform_sinit_module(wchar_t *file_path, + efi_file_t *file_out) +{ + EFI_STATUS status; + + /* Read the TBOOT config into RT memory and store */ + status = efi_read_file(g_file_system, + file_path, + EfiRuntimeServicesData, + &file_out->size, + &file_out->u.addr); + if (EFI_ERROR(status)) { + printk("Failed to read ACM file - status: %d\n", status); + return false; + } + + if (is_sinit_acmod(file_out->u.buffer, file_out->size, true) && + does_acmod_match_platform((acm_hdr_t*)file_out->u.buffer)) { + printk(TBOOT_DETA"SINIT matches platform\n"); + return true; + } + + BS->FreePages(file_out->u.addr, PFN_UP(file_out->size)); + file_out->u.addr = 0; + file_out->size = 0; + + return false; +} + +static EFI_STATUS efi_load_core_files(void) +{ + EFI_STATUS status = EFI_SUCCESS; + int key; + char keystr[16]; + const char *value; + wchar_t *file_path; + uint32_t size; + efi_file_t sinit_file = {0, 0}; + + /* TODO we don't have any RACMs right now so kick the can down the road... */ + /* TODO we don't use an LCP so kick the can down the road... */ + + for (key = 0; ; key++) { + snprintf(keystr, 16, "%d", key); + value = efi_cfg_get_value(EFI_CONFIG_TBOOT_PARSED, + SECTION_ACM, keystr); + if (!value) + break; + + file_path = atow_cat(g_tboot_dir, value); + if (!file_path) { + printk("Failed to allocate buffer for ACM file name\n"); + status = EFI_OUT_OF_RESOURCES; + goto out; + } + + efi_debug_print_w("ACM:", file_path); + + /* Found one */ + if (efi_is_platform_sinit_module(file_path, &sinit_file)) { + BS->FreePool(file_path); + break; + } + + BS->FreePool(file_path); + + /* Errors not fatal but the config likely includes missing files */ + } + + if (!sinit_file.u.buffer) { + printk(TBOOT_ERR"no SINIT AC module found\n"); + return EFI_INVALID_PARAMETER; + } + + /* Set the files we found */ + efi_store_files(&sinit_file, NULL, NULL); +out: + return status; +} + +static void efi_form_config_path(wchar_t *path) +{ + wchar_t *ptr = path + wcslen(path); + + efi_debug_print_w("IMAGE PATH:", path); + + /* Form the config file path */ + while (ptr >= path) { + if (*ptr == L'.') { + memcpy((ptr + 1), L"cfg\0", 8); + break; + } + ptr--; + } + + efi_debug_print_w("CONFIG PATH:", path); +} + +static EFI_STATUS efi_load_configs(void) +{ + EFI_STATUS status; + wchar_t *file_path = NULL; + EFI_PHYSICAL_ADDRESS addr = TBOOT_MAX_IMAGE_MEM; + void *buffer = NULL; + uint64_t size; + + /* Get file path for TBOOT image and config */ + status = BS->AllocatePool(EfiLoaderData, + (EFI_MAX_PATH + 4)*sizeof(wchar_t), + (void**)&file_path); + if (EFI_ERROR(status)) { + printk("Failed to alloc image path buffer - status: %d\n", status); + return status; + } + + status = efi_device_path_to_text(g_device_path, + file_path, + EFI_MAX_PATH); + if (EFI_ERROR(status)) { + printk("Failed to get TBOOT config path - status: %d\n", status); + goto err; + } + + /* Save a copy of the TBOOT dir for later */ + if (!efi_cfg_copy_tboot_path(file_path)) { + status = EFI_INVALID_PARAMETER; + printk("Failed to save TBOOT path - status: %d\n", status); + goto err; + } + + efi_form_config_path(file_path); + + /* Read the TBOOT config into RT memory and store */ + status = efi_read_file(g_file_system, + file_path, + EfiRuntimeServicesData, + &size, + &addr); + if (EFI_ERROR(status)) { + printk("Failed to read TBOOT config file - status: %d\n", status); + goto err; + } + + if (size > EFI_MAX_CONFIG_FILE) { + status = EFI_INVALID_PARAMETER; + printk("TBOOT config file too big - size: %d\n", size); + goto err; + } + + /* Make a copy of the raw TBOOT config in the MLE */ + memcpy(g_tboot_config_file, (void*)addr, size); + g_configs[EFI_CONFIG_TBOOT].u.buffer = g_tboot_config_file; + g_configs[EFI_CONFIG_TBOOT].size = size; + + /* Parse original */ + g_configs[EFI_CONFIG_TBOOT_PARSED].u.addr = addr; + g_configs[EFI_CONFIG_TBOOT_PARSED].size = size; + efi_cfg_pre_parse(&g_configs[EFI_CONFIG_TBOOT_PARSED]); + BS->FreePool(file_path); + + /* Get file path for Xen image and config */ + file_path = atow_alloc(efi_cfg_get_value(EFI_CONFIG_TBOOT_PARSED, + SECTION_TBOOT, ITEM_XENPATH)); + if (!file_path) { + printk("Failed to allocate buffer for Xen config file\n"); + status = EFI_OUT_OF_RESOURCES; + goto err; + } + + efi_form_config_path(file_path); + + /* Read the Xen config (non-modified) into RT memory and store */ + status = efi_read_file(g_file_system, + file_path, + EfiRuntimeServicesData, + &size, + &addr); + if (EFI_ERROR(status)) { + printk("Failed to read Xen config file - status: %d\n", status); + goto err; + } + + if (size > EFI_MAX_CONFIG_FILE) { + status = EFI_INVALID_PARAMETER; + printk("Xen config file too big - size: %d\n", size); + goto err; + } + + /* Make a copy of the raw TBOOT config in the MLE */ + memcpy(g_xen_config_file, (void*)addr, size); + g_configs[EFI_CONFIG_XEN].u.buffer = g_xen_config_file; + g_configs[EFI_CONFIG_XEN].size = size; + + /* Parse original */ + g_configs[EFI_CONFIG_XEN_PARSED].u.addr = addr; + g_configs[EFI_CONFIG_XEN_PARSED].size = size; + efi_cfg_pre_parse(&g_configs[EFI_CONFIG_XEN_PARSED]); + BS->FreePool(file_path); + + /* Locate and split off the kernel cmdline */ + if (!efi_split_kernel_line()) { + printk("Failed to parse and find kernel entry in Xen config\n"); + status = EFI_INVALID_PARAMETER; + goto err; + } + efi_debug_print_s("KERNEL CMDLINE:", g_kernel_cmdline); + + return EFI_SUCCESS; + +err: + if (g_configs[EFI_CONFIG_XEN].u.buffer) + BS->FreePages(g_configs[EFI_CONFIG_XEN].u.addr, + PFN_UP(g_configs[EFI_CONFIG_XEN].size)); + if (g_configs[EFI_CONFIG_TBOOT].u.buffer) + BS->FreePages(g_configs[EFI_CONFIG_TBOOT].u.addr, + PFN_UP(g_configs[EFI_CONFIG_TBOOT].size)); + + if (file_path) + BS->FreePool(file_path); + + return status; +} + +EFI_STATUS efi_start(EFI_HANDLE ImageHandle, + EFI_SYSTEM_TABLE *SystemTable) +{ + EFI_STATUS status; + + printk("TBOOT START Entry Point: %p\n", efi_start); + efi_debug_print_g(); + + /* Open the file system for the boot partition once up front */ + status = BS->OpenProtocol(g_device_handle, + &FileSystemProtocol, + (void**)&g_file_system, + g_image_handle, + NULL, + EFI_OPEN_PROTOCOL_BY_HANDLE_PROTOCOL); + if (EFI_ERROR(status)) { + printk("Failed to open FileSystemProtocol - status: %d\n", status); + goto out; + } + + efi_cfg_init(); + g_configs = efi_get_configs(); + + /* Locate the .text section and length */ + g_text_base = efi_get_pe_section(".text", g_image_base, + &g_text_size); + if (g_text_base) { + printk("Located .text section: %p size: %llx\n", + g_text_base, g_text_size); + /* Sanity check the location of the .text section in the image */ + if ((g_text_base - g_image_base) != PAGE_SIZE) { + printk("The .text offset must be at 1 page into TBOOT image!\n"); + goto out; + } + } + else { + printk("Failed to locate .text section\n"); + status = EFI_INVALID_PARAMETER; + goto out; + } + + /* Locate the .bss section and length */ + g_bss_base = efi_get_pe_section(".bss", g_image_base, + &g_bss_size); + if (g_bss_base) { + printk("Located .bss section: %p size: %llx\n", + g_bss_base, g_bss_size); + } + else { + printk("Failed to locate .text section\n"); + status = EFI_INVALID_PARAMETER; + goto out; + } + + /* Load the configuration files and information */ + status = efi_load_configs(); + if (EFI_ERROR(status)) + goto out; + + /* Load the platform SINIT, RACM and LCP */ + status = efi_load_core_files(); + if (EFI_ERROR(status)) + goto out; + + /* Setup RT shared variable */ + status = efi_setup_tboot_xen_var(); + if (EFI_ERROR(status)) + goto out; + + /* Begin initial launch */ + begin_initial_launch(); + + /* Start Xen here */ + efi_launch_kernel(); + + /* SNO */ + printk("FATAL: could not launch Xen!\n"); + +out: + efi_debug_pause(); + + /* Should not reach here unless something failed */ + ST->RuntimeServices->ResetSystem(EfiResetShutdown, EFI_SUCCESS, 0, NULL); + + return EFI_SUCCESS; +} + +static EFI_STATUS efi_reloc_and_call(void) +{ + EFI_STATUS status; + uint64_t size; + EFI_PHYSICAL_ADDRESS addr = TBOOT_MAX_IMAGE_MEM; + uint64_t efi_start_ptr; + + /* + * TODO we may have to pick and force an address since right now we are at + * the mercy of EFI picking a runtime service code range. If this ends up + * outside the PMRs due to the crazy min_ram stuff then pain and misery + * will ensue. + */ + + status = BS->AllocatePages(AllocateMaxAddress, + EfiRuntimeServicesCode, + PFN_UP(g_image_size) + TBOOT_RTMEM_COUNT, + &addr); + if (EFI_ERROR(status)) + return status; + + g_rtmem_base = (void*)addr; + g_image_base = (void*)(addr + TBOOT_RTMEM_SIZE); + memset(g_rtmem_base, 0, g_image_size + TBOOT_RTMEM_SIZE); + + /* Copy me to new location */ + memcpy(g_image_base, g_init_base, g_image_size); + + efi_start_ptr = (uint64_t)g_image_base + + ((uint64_t)efi_start - (uint64_t)g_init_base); + + /* End of the line */ + __asm__ __volatile__ ( + "movq %0, %%rdx\n\t" + "movq %1, %%rcx\n\t" + "call *%%rax\n\t" + : + : "g" (ST), "g" (g_image_handle), "a" (efi_start_ptr)); + + return EFI_LOAD_ERROR; /* SNO! */ +} + +EFI_STATUS efi_main(EFI_HANDLE ImageHandle, + EFI_SYSTEM_TABLE *SystemTable) +{ + EFI_STATUS status; + EFI_LOADED_IMAGE *loaded_image; + + /* Store the system table for future use in other functions */ + ST = SystemTable; + BS = ST->BootServices; + RT = ST->RuntimeServices; + + /* So we can use printk via EFI console protocol */ + printk_init(INIT_EARLY_EFI); + printk("TBOOT EFI Entry Point: %p\n", efi_main); + + status = BS->HandleProtocol(ImageHandle, + &LoadedImageProtocol, + (VOID*)&loaded_image); + + if (!EFI_ERROR(status)) { + /* Device we were loaded from, EFI partition */ + g_device_handle = loaded_image->DeviceHandle; + g_device_path = loaded_image->FilePath; + g_init_base = loaded_image->ImageBase; + g_image_size = loaded_image->ImageSize; + g_image_handle = ImageHandle; + } + else { + printk("TBOOT FATAL! Cannot get loaded image information\n"); + ST->RuntimeServices->ResetSystem(EfiResetShutdown, status, 0, NULL); + } + + efi_debug_print_g(); + + /* Relocate this image and call, never return */ + status = efi_reloc_and_call(); + + /* Should not be here! */ + printk("TBOOT FATAL! relocate and call failed - status: %x\n", status); + ST->RuntimeServices->ResetSystem(EfiResetShutdown, EFI_SUCCESS, 0, NULL); + + return status; +} -- cgit v1.2.3