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/eficore.c | 534 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 534 insertions(+) create mode 100644 tboot/eficore.c (limited to 'tboot/eficore.c') diff --git a/tboot/eficore.c b/tboot/eficore.c new file mode 100644 index 0000000..71cdc09 --- /dev/null +++ b/tboot/eficore.c @@ -0,0 +1,534 @@ +/* + * eficore.c: EFI core support 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 + +/* Global Table Pointers */ +EFI_SYSTEM_TABLE *ST; +EFI_BOOT_SERVICES *BS; +EFI_RUNTIME_SERVICES *RT; + +/* Local device paths */ +EFI_DEVICE_PATH EfiRootDevicePath[] = { + {END_DEVICE_PATH_TYPE, END_ENTIRE_DEVICE_PATH_SUBTYPE, {END_DEVICE_PATH_LENGTH,0}} +}; + +EFI_DEVICE_PATH EfiEndDevicePath[] = { + {END_DEVICE_PATH_TYPE, END_ENTIRE_DEVICE_PATH_SUBTYPE, {END_DEVICE_PATH_LENGTH, 0}} +}; + +EFI_DEVICE_PATH EfiEndInstanceDevicePath[] = { + {END_DEVICE_PATH_TYPE, END_INSTANCE_DEVICE_PATH_SUBTYPE, {END_DEVICE_PATH_LENGTH, 0}} +}; + + +/* EFI IDs */ +EFI_GUID EfiGlobalVariable = EFI_GLOBAL_VARIABLE; +EFI_GUID NullGuid = { 0,0,0,{0,0,0,0,0,0,0,0} }; +EFI_GUID UnknownDevice = UNKNOWN_DEVICE_GUID; + +/* Protocol IDs */ +EFI_GUID DevicePathProtocol = DEVICE_PATH_PROTOCOL; +EFI_GUID LoadedImageProtocol = LOADED_IMAGE_PROTOCOL; +EFI_GUID FileSystemProtocol = SIMPLE_FILE_SYSTEM_PROTOCOL; + +/* File system information IDs */ +EFI_GUID GenericFileInfo = EFI_FILE_INFO_ID; + +/* Configuration Table GUIDs */ +EFI_GUID AcpiTableGuid = ACPI_TABLE_GUID; +EFI_GUID Acpi20TableGuid = ACPI_20_TABLE_GUID; +EFI_GUID SMBIOSTableGuid = SMBIOS_TABLE_GUID; + +/* TBOOT/Xen */ +EFI_GUID TbootXenGuid = EFI_TBOOT_XEN_GUID; + +void atow(wchar_t *dst, const char *src, uint64_t count) +{ + uint64_t i; + + for (i = 0; i < count; i++, dst++, src++) + *dst = (*src & 0x7f); +} + +bool wtoa(char *dst, const wchar_t *src, uint64_t count) +{ + uint64_t i; + bool r = true; + + for (i = 0; i < count; i++, dst++, src++) { + if (*dst <= 0x7f) { + *dst = *src; + } + else { + r = false; + *dst = '_'; + } + } + + return r; +} + +uint64_t wcslen(const wchar_t *str) +{ + uint64_t len; + + for (len = 0; *str != L'\0'; str++, len++) + ; + + return ++len; +} + +wchar_t *atow_alloc(const char *src) +{ + EFI_STATUS status = EFI_SUCCESS; + wchar_t *dst; + uint64_t count, size; + + if (!src) + return NULL; + + count = strlen(src); + size = (count + 1)*sizeof(wchar_t); + + status = BS->AllocatePool(EfiLoaderData, size, (void**)&dst); + if (EFI_ERROR(status)) + return NULL; + + memset(dst, 0, size); + + atow(dst, src, count); + return dst; +} + +char *wtoa_alloc(const wchar_t *src) +{ + EFI_STATUS status = EFI_SUCCESS; + char *dst; + uint64_t count, size; + + if (!src) + return NULL; + + count = wcslen(src); + size = (count + 1)*sizeof(char); + + status = BS->AllocatePool(EfiLoaderData, size, (void**)&dst); + if (EFI_ERROR(status)) + return NULL; + + memset(dst, 0, size); + + wtoa(dst, src, count); + return dst; +} + +wchar_t *atow_cat(const wchar_t *base, const char *tail) +{ + EFI_STATUS status = EFI_SUCCESS; + wchar_t *dst; + uint64_t wcount, scount, size; + + if (!base || !tail) + return NULL; + + scount = strlen(tail); + wcount = wcslen(base); + size = (wcount + scount + 1)*sizeof(wchar_t); + + status = BS->AllocatePool(EfiLoaderData, size, (void**)&dst); + if (EFI_ERROR(status)) + return NULL; + + memset(dst, 0, size); + memcpy(dst, g_tboot_dir, wcount*sizeof(wchar_t)); + atow((dst + wcount - 1), tail, scount); + + return dst; +} + +uint8_t *efi_get_rsdp(void) +{ + EFI_CONFIGURATION_TABLE *conf_table = ST->ConfigurationTable; + uint8_t *acpi_rsdp = NULL; + uint64_t count; + + for (count = 0; count < ST->NumberOfTableEntries; count++, conf_table++) { + if (!memcmp(&Acpi20TableGuid, &conf_table->VendorGuid, sizeof(EFI_GUID)) || + !memcmp(&AcpiTableGuid, &conf_table->VendorGuid, sizeof(EFI_GUID))) { + acpi_rsdp = conf_table->VendorTable; + break; + } + } + + return acpi_rsdp; +} + +void *efi_get_pe_section(const char *name, void *image_base, + uint64_t *size_out) +{ +#define calc_addr(b, o) (void*)(((uint8_t*)b) + o); + IMAGE_DOS_HEADER *dosh; + IMAGE_NT_HEADERS *nth; + IMAGE_FILE_HEADER *fh; + IMAGE_SECTION_HEADER *sh; + uint16_t i; + void *text = NULL; + size_t length = strlen(name); + + dosh = (IMAGE_DOS_HEADER*)image_base; + nth = (IMAGE_NT_HEADERS*)calc_addr(dosh, dosh->e_lfanew); + + if (dosh->e_magic != IMAGE_DOS_SIGNATURE) { + printk("Invalid DOS header signature: %x\n", dosh->e_magic); + return NULL; + } + + if (nth->Signature != IMAGE_NT_SIGNATURE) { + printk("Invalid NT header signature: %x\n", nth->Signature); + return NULL; + } + + fh = (IMAGE_FILE_HEADER*)calc_addr(nth, sizeof(uint32_t)); + sh = (IMAGE_SECTION_HEADER*)calc_addr(fh, sizeof(IMAGE_FILE_HEADER) + + fh->SizeOfOptionalHeader); + + for (i = 0; i < fh->NumberOfSections; i++, sh++) { + if (!memcmp(name, sh->Name, length)) { + text = (void*)((uint8_t*)image_base + sh->VirtualAddress); + *size_out = sh->Misc.VirtualSize; + break; + } + } + + return text; +} + +void efi_shutdown_system(uint32_t shutdown_type) +{ + switch (shutdown_type) { + case TB_SHUTDOWN_S5: + RT->ResetSystem(EfiResetShutdown, EFI_SUCCESS, 0, NULL); + case TB_SHUTDOWN_REBOOT: + RT->ResetSystem(EfiResetCold, EFI_SUCCESS, 0, NULL); + defaut: + ; + }; +} + +/* Logging support */ +static void atow_log(CHAR16 *dst, const char *src, uint32_t count) +{ + uint32_t i; + char c; + + for (i = 0; i < count; i++, dst++, src++) { + c = (*src & 0x7f); + if (c == '\n') { + *dst = L'\r'; + dst++; + } + *dst = c; + } +} + +void efi_puts(const char *s, unsigned int count) +{ + CHAR16 wbuf[2*TBOOT_LOGBUF_SIZE]; + + memset(wbuf, 0, sizeof(CHAR16)*2*TBOOT_LOGBUF_SIZE); + atow_log(wbuf, s, count); + (void)ST->ConOut->OutputString(ST->ConOut, wbuf); +} + +static uint64_t efi_device_path_size(EFI_DEVICE_PATH *dpath) +{ + EFI_DEVICE_PATH *tpath = dpath; + + while (!IsDevicePathEnd(tpath)) + tpath = NextDevicePathNode(tpath); + + return ((uint64_t)tpath - (uint64_t)dpath) + sizeof(EFI_DEVICE_PATH); +} + +static EFI_DEVICE_PATH *efi_device_path_instance(EFI_DEVICE_PATH **dpath, + uint64_t *size) +{ + EFI_DEVICE_PATH *spath = *dpath, *npath, *tpath = *dpath; + + if (!tpath) + return NULL; + + for ( ; ; ) { + npath = NextDevicePathNode(tpath); + + if (IsDevicePathEndType(tpath)) + break; + + tpath = npath; + } + + if (DevicePathSubType(tpath) == END_ENTIRE_DEVICE_PATH_SUBTYPE) + npath = NULL; + + *dpath = npath; + + if (size) + *size = ((uint64_t)tpath) - ((uint64_t)spath); + return spath; +} + +static EFI_DEVICE_PATH *efi_append_device_path(EFI_DEVICE_PATH *dpath1, + EFI_DEVICE_PATH *dpath2) +{ + EFI_STATUS status = EFI_SUCCESS; + uint64_t size1, size2, inst = 0, size; + EFI_DEVICE_PATH *tpath = dpath1, *ipath, *opath; + uint8_t *pos; + + size1 = efi_device_path_size(dpath1); + size2 = efi_device_path_size(dpath2); + + while (efi_device_path_instance(&tpath, NULL)) + inst++; + + size = (size1 * inst) + size2; + + status = BS->AllocatePool(EfiLoaderData, size, (void**)&opath); + if (EFI_ERROR(status)) + return NULL; + + pos = (uint8_t*)opath; + ipath = efi_device_path_instance(&dpath1, &size); + + for ( ; ; ) { + if (!ipath) + break; + + memcpy(pos, ipath, size); + pos += size; + memcpy(pos, dpath2, size2); + pos += size2; + memcpy(pos, EfiEndInstanceDevicePath, sizeof(EFI_DEVICE_PATH)); + pos += sizeof(EFI_DEVICE_PATH); + + ipath = efi_device_path_instance(&dpath1, &size); + } + + pos -= sizeof(EFI_DEVICE_PATH); + memcpy(pos, EfiEndDevicePath, sizeof(EFI_DEVICE_PATH)); + + return opath; +} + +EFI_DEVICE_PATH *efi_get_device_path(const wchar_t *path, EFI_HANDLE parent) +{ + EFI_STATUS status = EFI_SUCCESS; + FILEPATH_DEVICE_PATH *file_path = NULL; + EFI_DEVICE_PATH *dev_path = NULL, *parent_path, *eo_path; + uint64_t size = wcslen(path)*sizeof(wchar_t); + + status = BS->AllocatePool(EfiLoaderData, + size + SIZE_OF_FILEPATH_DEVICE_PATH + sizeof(EFI_DEVICE_PATH), + (void**)&file_path); + if (EFI_ERROR(status)) + return NULL; + + status = BS->HandleProtocol(parent, &DevicePathProtocol, (void**)&parent_path); + if (EFI_ERROR(status)) { + BS->FreePool(file_path); + return NULL; + } + + file_path->Header.Type = MEDIA_DEVICE_PATH; + file_path->Header.SubType = MEDIA_FILEPATH_DP; + SetDevicePathNodeLength(&file_path->Header, size + SIZE_OF_FILEPATH_DEVICE_PATH); + memcpy(file_path->PathName, path, size); + eo_path = NextDevicePathNode(&file_path->Header); + SetDevicePathEndNode(eo_path); + + dev_path = efi_append_device_path(parent_path, (EFI_DEVICE_PATH*)file_path); + BS->FreePool(file_path); + + return dev_path; +} + +EFI_STATUS efi_device_path_to_text(EFI_DEVICE_PATH *dev_path, + wchar_t *path_out, + uint64_t count) +{ +#define size_needed(d) (DevicePathNodeLength(d) - \ + (sizeof(EFI_DEVICE_PATH) - sizeof(wchar_t))) +#define copy_size(d) (DevicePathNodeLength(d) - sizeof(EFI_DEVICE_PATH)) + EFI_STATUS status = EFI_SUCCESS; + FILEPATH_DEVICE_PATH *file_path; + uint64_t size = count*sizeof(wchar_t); + + while (DevicePathType(dev_path) != END_DEVICE_PATH_TYPE) { + /* Space check first */ + if (size < size_needed(dev_path)) + return EFI_BUFFER_TOO_SMALL; + + /* Copy in latest bits of the path */ + file_path = (FILEPATH_DEVICE_PATH*)dev_path; + memcpy(path_out, file_path->PathName, copy_size(dev_path)); + path_out[copy_size(dev_path)/sizeof(wchar_t)] = 0; + dev_path = (EFI_DEVICE_PATH*)((uint64_t)dev_path + + DevicePathNodeLength(dev_path)); + } + + return EFI_SUCCESS; +} + +EFI_FILE_INFO *efi_get_file_info(EFI_FILE *target_file, + EFI_MEMORY_TYPE mem_type) +{ + EFI_STATUS status; + void *buffer = NULL; + UINTN size = SIZE_OF_EFI_FILE_INFO + 256; + uint32_t i; + + for (i = 0; i < 2; i++) { + status = BS->AllocatePool(mem_type, size, &buffer); + if (!buffer) + return NULL; + + status = target_file->GetInfo(target_file, + &GenericFileInfo, + &size, + buffer); + if (EFI_ERROR(status)) { + BS->FreePool(buffer); + buffer = NULL; + if (status != EFI_BUFFER_TOO_SMALL) + return NULL; + continue; + } + break; + } + + return buffer; +} + +EFI_STATUS efi_read_file(EFI_FILE_IO_INTERFACE *file_system, + wchar_t *file_name, + EFI_MEMORY_TYPE mem_type, + uint64_t *size_out, + EFI_PHYSICAL_ADDRESS *addr_out) +{ + EFI_FILE *root_file = NULL; + EFI_FILE *target_file = NULL; + EFI_FILE_INFO *file_info; + EFI_STATUS status = EFI_SUCCESS; + uint64_t size; + EFI_PHYSICAL_ADDRESS addr = TBOOT_MAX_IMAGE_MEM; + char *print_name = wtoa_alloc(file_name); + + *size_out = 0; + *addr_out = 0; + + status = file_system->OpenVolume(file_system, &root_file); + if (EFI_ERROR(status)) { + printk("Failed to open root File handle - status: %d\n", status); + goto out; + } + + status = root_file->Open(root_file, + &target_file, + file_name, + EFI_FILE_MODE_READ, + EFI_FILE_READ_ONLY); + if (EFI_ERROR(status)) { + printk("Failed to open file %s - status: %d\n", print_name, status); + goto out; + } + + status = target_file->SetPosition(target_file, 0); + if (EFI_ERROR(status)) { + printk("Failed to seek 0 file %s - status: %d\n", print_name, status); + goto out; + } + + file_info = efi_get_file_info(target_file, mem_type); + if (!file_info) { + printk("Failed to get file %s information\n", print_name); + status = EFI_OUT_OF_RESOURCES; + goto out; + } + size = file_info->FileSize; + BS->FreePool(file_info); + + status = BS->AllocatePages(AllocateMaxAddress, + mem_type, + PFN_UP(size), + &addr); + if (EFI_ERROR(status)) { + printk("Failed to allocate buffer to read file %s\n", print_name); + status = EFI_OUT_OF_RESOURCES; + goto out; + } + + status = target_file->Read(target_file, &size, (void*)addr); + if (EFI_ERROR(status)) { + printk("Failed to read file %s\n", print_name); + BS->FreePages(addr, PFN_UP(size)); + goto out; + } + + *size_out = size; + *addr_out = addr; + +out: + if (target_file) + target_file->Close(target_file); + + if (root_file) + root_file->Close(root_file); + + if (print_name) + BS->FreePool(print_name); + + return status; +} -- cgit v1.2.3