From 6b903f480c98bd45c21486f8da7223fdb4cfcc79 Mon Sep 17 00:00:00 2001 From: Kelvin Zhang Date: Tue, 8 Oct 2024 10:56:11 -0700 Subject: [PATCH 01/11] [git] Ignore common linux build files, update README --- .gitignore | 2 ++ lib/uefi/README.md | 4 +++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index cc5c6cfdf..3d1356146 100644 --- a/.gitignore +++ b/.gitignore @@ -17,3 +17,5 @@ tagit TAGS tags toolchain +.cache +vmlinux diff --git a/lib/uefi/README.md b/lib/uefi/README.md index 3b3179271..ec25f2369 100644 --- a/lib/uefi/README.md +++ b/lib/uefi/README.md @@ -4,11 +4,13 @@ make qemu-virt-arm64-test ``` +Note, this may fail if your system does not have `aarch64-elf-gcc` installed. To address, download from [here](https://newos.org/toolchains/aarch64-elf-14.2.0-Linux-x86_64.tar.xz), unzip, and add the extracted dir to PATH. + ## Run ``` qemu-system-aarch64 -cpu max -m 512 -smp 1 -machine virt,highmem=off \ - -kernel qemu-virt-arm64-test/lk.elf \ + -kernel build-qemu-virt-arm64-test/lk.elf \ -net none -nographic \ -drive if=none,file=lib/uefi/helloworld_aa64.efi,id=blk,format=raw \ -device virtio-blk-device,drive=blk From 5ea19f1b0a0a0ead5c29897fd304582867ba0b15 Mon Sep 17 00:00:00 2001 From: Kelvin Zhang Date: Wed, 24 Jul 2024 10:46:19 -0700 Subject: [PATCH 02/11] [app][uefi] Setup skeleton for UEFI boot service --- lib/uefi/boot_service_provider.cpp | 150 +++++++++++++++++++++++++++++ lib/uefi/boot_service_provider.h | 78 +++++++++++++++ lib/uefi/include/system_table.h | 3 +- lib/uefi/pe.h | 51 ++++++++++ lib/uefi/rules.mk | 2 + lib/uefi/text_protocol.cpp | 42 ++++++++ lib/uefi/text_protocol.h | 25 +++++ lib/uefi/uefi.cpp | 66 +++++++------ 8 files changed, 388 insertions(+), 29 deletions(-) create mode 100644 lib/uefi/boot_service_provider.cpp create mode 100644 lib/uefi/boot_service_provider.h create mode 100644 lib/uefi/text_protocol.cpp create mode 100644 lib/uefi/text_protocol.h diff --git a/lib/uefi/boot_service_provider.cpp b/lib/uefi/boot_service_provider.cpp new file mode 100644 index 000000000..04caca8f9 --- /dev/null +++ b/lib/uefi/boot_service_provider.cpp @@ -0,0 +1,150 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +#include "boot_service_provider.h" +#include "types.h" +#include +#include +#include + +namespace { + +EfiStatus unload(EfiHandle handle) { return SUCCESS; } + +bool guid_eq(const EfiGuid *a, const EfiGuid *b) { + return memcmp(a, b, sizeof(*a)) == 0; +} + +bool guid_eq(const EfiGuid *a, const EfiGuid &b) { + return memcmp(a, &b, sizeof(*a)) == 0; +} + +EfiStatus handle_protocol(EfiHandle handle, const EfiGuid *protocol, + void **intf) { + if (guid_eq(protocol, LOADED_IMAGE_PROTOCOL_GUID)) { + printf("handle_protocol(%p, LOADED_IMAGE_PROTOCOL_GUID, %p);\n", handle, + intf); + auto loaded_image = static_cast( + malloc(sizeof(EFI_LOADED_IMAGE_PROTOCOL))); + loaded_image = {}; + loaded_image->Revision = EFI_LOADED_IMAGE_PROTOCOL_REVISION; + loaded_image->ParentHandle = nullptr; + loaded_image->SystemTable = nullptr; + loaded_image->LoadOptionsSize = 0; + loaded_image->LoadOptions = nullptr; + loaded_image->Unload = unload; + + *intf = loaded_image; + return SUCCESS; + } else if (guid_eq(protocol, LINUX_EFI_LOADED_IMAGE_FIXED_GUID)) { + printf("handle_protocol(%p, LINUX_EFI_LOADED_IMAGE_FIXED_GUID, %p);\n", + handle, intf); + return SUCCESS; + } else { + printf("handle_protocol(%p, %p, %p);\n", handle, protocol, intf); + } + return UNSUPPORTED; +} + +EfiStatus allocate_pool(EfiMemoryType pool_type, size_t size, void **buf) { + if (buf == nullptr) { + return INVALID_PARAMETER; + } + if (size == 0) { + *buf = nullptr; + return SUCCESS; + } + *buf = malloc(size); + if (*buf != nullptr) { + return SUCCESS; + } + return OUT_OF_RESOURCES; +} + +EfiStatus free_pool(void *mem) { + free(mem); + return SUCCESS; +} + +EfiStatus get_memory_map(size_t *memory_map_size, + EfiMemoryDescriptor *memory_map, size_t *map_key, + size_t *desc_size, uint32_t *desc_version) { + if (memory_map_size) { + *memory_map_size = 0; + } + if (memory_map) { + memset(memory_map, 0, sizeof(*memory_map)); + } + if (map_key) { + *map_key = 0; + } + if (desc_size) { + *desc_size = sizeof(EfiMemoryDescriptor); + } + if (desc_version) { + *desc_version = 0; + } + + printf("%s(%zu) is unsupported\n", __FUNCTION__, *memory_map_size); + return UNSUPPORTED; +} + +EfiStatus register_protocol_notify(const EfiGuid *protocol, EfiEvent event, + void **registration) { + printf("%s is unsupported\n", __FUNCTION__); + return UNSUPPORTED; +} + +EfiStatus locate_handle(EfiLocateHandleSearchType search_type, + const EfiGuid *protocol, void *search_key, + size_t *buf_size, EfiHandle *buf) { + + printf("%s is unsupported\n", __FUNCTION__); + return UNSUPPORTED; +} + +EfiStatus locate_protocol(const EfiGuid *protocol, void *registration, + void **intf) { + + printf("%s is unsupported\n", __FUNCTION__); + return UNSUPPORTED; +} + +EfiStatus allocate_pages(EfiAllocatorType type, EfiMemoryType memory_type, + size_t pages, EfiPhysicalAddr *memory) { + printf("%s is unsupported\n", __FUNCTION__); + return UNSUPPORTED; +} + +EfiStatus free_pages(EfiPhysicalAddr memory, size_t pages) { + + printf("%s is unsupported\n", __FUNCTION__); + return UNSUPPORTED; +} + +} // namespace + +void setup_boot_service_table(EfiBootService *service) { + service->handle_protocol = handle_protocol; + service->allocate_pool = allocate_pool; + service->free_pool = free_pool; + service->get_memory_map = get_memory_map; + service->register_protocol_notify = register_protocol_notify; + service->locate_handle = locate_handle; + service->locate_protocol = locate_protocol; + service->allocate_pages = allocate_pages; + service->free_pages = free_pages; +} \ No newline at end of file diff --git a/lib/uefi/boot_service_provider.h b/lib/uefi/boot_service_provider.h new file mode 100644 index 000000000..f87340293 --- /dev/null +++ b/lib/uefi/boot_service_provider.h @@ -0,0 +1,78 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +#ifndef __BOOT_SERVICE_PROVIDER_ +#define __BOOT_SERVICE_PROVIDER_ + +#include "boot_service.h" +#include "system_table.h" + +void setup_boot_service_table(EfiBootService *service); + +static constexpr auto LOADED_IMAGE_PROTOCOL_GUID = + EfiGuid{0x5b1b31a1, + 0x9562, + 0x11d2, + {0x8e, 0x3f, 0x00, 0xa0, 0xc9, 0x69, 0x72, 0x3b}}; + +static constexpr auto EFI_DEVICE_PATH_PROTOCOL_GUID = + EfiGuid{0x09576e91, + 0x6d3f, + 0x11d2, + {0x8e, 0x39, 0x00, 0xa0, 0xc9, 0x69, 0x72, 0x3b}}; + +static constexpr auto LINUX_EFI_LOADED_IMAGE_FIXED_GUID = + EfiGuid{0xf5a37b6d, + 0x3344, + 0x42a5, + {0xb6, 0xbb, 0x97, 0x86, 0x48, 0xc1, 0x89, 0x0a}}; + +using EFI_IMAGE_UNLOAD = EfiStatus (*)(EfiHandle); + +//****************************************************** +// EFI_DEVICE_PATH_PROTOCOL +//****************************************************** +struct EFI_DEVICE_PATH_PROTOCOL { + uint8_t Type; + uint8_t SubType; + uint8_t Length[2]; +}; + +struct EFI_LOADED_IMAGE_PROTOCOL { + uint32_t Revision; + EfiHandle ParentHandle; + EfiSystemTable *SystemTable; + + // Source location of the image + EfiHandle DeviceHandle; + EFI_DEVICE_PATH_PROTOCOL *FilePath; + void *Reserved; + + // Image’s load options + uint32_t LoadOptionsSize; + void *LoadOptions; + + // Location where image was loaded + void *ImageBase; + uint64_t ImageSize; + EFI_MEMORY_TYPE ImageCodeType; + EFI_MEMORY_TYPE ImageDataType; + EFI_IMAGE_UNLOAD Unload; +}; + +static constexpr size_t EFI_LOADED_IMAGE_PROTOCOL_REVISION = 0x1000; + +#endif diff --git a/lib/uefi/include/system_table.h b/lib/uefi/include/system_table.h index 92beb6896..404cfc812 100644 --- a/lib/uefi/include/system_table.h +++ b/lib/uefi/include/system_table.h @@ -21,6 +21,7 @@ #include "types.h" #include "boot_service.h" +#include "protocols/simple_text_input_protocol.h" #include "protocols/simple_text_output_protocol.h" typedef struct { @@ -33,7 +34,7 @@ typedef struct EfiSystemTable { char16_t* firmware_vendor; uint32_t firmware_revision; EfiHandle console_in_handle; - void* con_in; + EfiSimpleTextInputProtocol *con_in; EfiHandle console_out_handle; EfiSimpleTextOutputProtocol* con_out; EfiHandle standard_error_handle; diff --git a/lib/uefi/pe.h b/lib/uefi/pe.h index b776fed48..294a087aa 100644 --- a/lib/uefi/pe.h +++ b/lib/uefi/pe.h @@ -1,3 +1,20 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + #ifndef __PE_HEADER_ #define __PE_HEADER_ @@ -184,6 +201,40 @@ struct IMAGE_OPTIONAL_HEADER64 { IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES]; } __attribute__((packed)); +struct IMAGE_OPTIONAL_HEADER32 { + u16 Magic; + u8 MajorLinkerVersion; + u8 MinorLinkerVersion; + u32 SizeOfCode; + u32 SizeOfInitializedData; + u32 SizeOfUninitializedData; + u32 AddressOfEntryPoint; + u32 BaseOfCode; + u32 BaseOfData; + u32 ImageBase; + u32 SectionAlignment; + u32 FileAlignment; + u16 MajorOperatingSystemVersion; + u16 MinorOperatingSystemVersion; + u16 MajorImageVersion; + u16 MinorImageVersion; + u16 MajorSubsystemVersion; + u16 MinorSubsystemVersion; + u32 Win32VersionValue; + u32 SizeOfImage; + u32 SizeOfHeaders; + u32 CheckSum; + u16 Subsystem; + u16 DllCharacteristics; + u32 SizeOfStackReserve; + u32 SizeOfStackCommit; + u32 SizeOfHeapReserve; + u32 SizeOfHeapCommit; + u32 LoaderFlags; + u32 NumberOfRvaAndSizes; + IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES]; +} __attribute__((packed)); + struct IMAGE_SECTION_HEADER { char Name[IMAGE_SIZEOF_SHORT_NAME]; union { diff --git a/lib/uefi/rules.mk b/lib/uefi/rules.mk index 4b250d3e0..842cdc08f 100644 --- a/lib/uefi/rules.mk +++ b/lib/uefi/rules.mk @@ -6,5 +6,7 @@ MODULE_INCLUDES += $(LOCAL_DIR)/include MODULE_SRCS += \ $(LOCAL_DIR)/uefi.cpp \ + $(LOCAL_DIR)/text_protocol.cpp \ + $(LOCAL_DIR)/boot_service_provider.cpp \ include make/module.mk diff --git a/lib/uefi/text_protocol.cpp b/lib/uefi/text_protocol.cpp new file mode 100644 index 000000000..19fed7028 --- /dev/null +++ b/lib/uefi/text_protocol.cpp @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include "text_protocol.h" +#include + +EfiStatus output_string(struct EfiSimpleTextOutputProtocol *self, + char16_t *string) { + char buffer[512]; + size_t i = 0; + while (string[i]) { + size_t j = 0; + for (j = 0; j < sizeof(buffer) - 1 && string[i + j]; j++) { + buffer[j] = string[i + j]; + } + i += j; + buffer[j] = 0; + + printf("%s", reinterpret_cast(buffer)); + } + return SUCCESS; +} + +EfiSimpleTextOutputProtocol get_text_output_protocol() { + EfiSimpleTextOutputProtocol console_out; + console_out.output_string = output_string; + return console_out; +} \ No newline at end of file diff --git a/lib/uefi/text_protocol.h b/lib/uefi/text_protocol.h new file mode 100644 index 000000000..0002d67d7 --- /dev/null +++ b/lib/uefi/text_protocol.h @@ -0,0 +1,25 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef __TEXT_PROTOCOL_H__ +#define __TEXT_PROTOCOL_H__ + +#include "protocols/simple_text_output_protocol.h" + +EfiSimpleTextOutputProtocol get_text_output_protocol(); + +#endif \ No newline at end of file diff --git a/lib/uefi/uefi.cpp b/lib/uefi/uefi.cpp index 8eb280ec3..69e68bd31 100644 --- a/lib/uefi/uefi.cpp +++ b/lib/uefi/uefi.cpp @@ -1,3 +1,5 @@ +#include "boot_service.h" +#include "boot_service_provider.h" #include "defer.h" #include "kernel/vm.h" #include "pe.h" @@ -13,28 +15,16 @@ #include #include -#include "efi.h" +#include "protocols/simple_text_output_protocol.h" +#include "system_table.h" +#include "text_protocol.h" -// ASCII "PE\x0\x0" - -EfiStatus output_string(struct EfiSimpleTextOutputProtocol *self, - char16_t *string) { - char buffer[512]; - size_t i = 0; - while (string[i]) { - size_t j = 0; - for (j = 0; j < sizeof(buffer) - 1 && string[i + j]; j++) { - buffer[j] = string[i + j]; - } - i += j; - buffer[j] = 0; +constexpr auto EFI_SYSTEM_TABLE_SIGNATURE = + static_cast(0x5453595320494249ULL); - printf("%s", reinterpret_cast(buffer)); - } - return SUCCESS; -} +// ASCII "PE\x0\x0" -typedef int (*EfiEntry)(void *handle, struct EfiSystemTable *system); +using EfiEntry = int (*)(void *, struct EfiSystemTable *); void *alloc_page(size_t size) { void *vptr{}; @@ -47,13 +37,24 @@ void *alloc_page(size_t size) { return vptr; } +template void fill(T *data, size_t skip, uint8_t begin = 0) { + auto ptr = reinterpret_cast(data); + for (size_t i = 0; i < sizeof(T); i++) { + if (i < skip) { + continue; + } + ptr[i] = begin++; + } +} + int load_sections_and_execute(bdev_t *dev, const IMAGE_NT_HEADERS64 *pe_header) { const auto file_header = &pe_header->FileHeader; const auto optional_header = &pe_header->OptionalHeader; const auto sections = file_header->NumberOfSections; const auto section_header = reinterpret_cast( - reinterpret_cast(pe_header) + sizeof(*pe_header)); + reinterpret_cast(pe_header) + sizeof(IMAGE_FILE_HEADER) + + file_header->SizeOfOptionalHeader); for (size_t i = 0; i < sections; i++) { if (section_header[i].NumberOfRelocations != 0) { printf("Section %s requires relocation, which is not supported.\n", @@ -76,11 +77,15 @@ int load_sections_and_execute(bdev_t *dev, optional_header->AddressOfEntryPoint); printf("Entry function located at %p\n", entry); - EfiSystemTable table; - EfiSimpleTextOutputProtocol console_out; - console_out.output_string = output_string; + EfiSystemTable table{}; + EfiBootService boot_service{}; + fill(&boot_service, 0); + table.boot_services = &boot_service; + setup_boot_service_table(table.boot_services); + table.header.signature = EFI_SYSTEM_TABLE_SIGNATURE; + EfiSimpleTextOutputProtocol console_out = get_text_output_protocol(); table.con_out = &console_out; - return entry(nullptr, &table); + return entry(image_base, &table); } int load_pe_file(const char *blkdev) { @@ -93,8 +98,8 @@ int load_pe_file(const char *blkdev) { constexpr size_t kBlocKSize = 4096; lk_time_t t = current_time(); - uint8_t *address = (uint8_t *)malloc(kBlocKSize); - ssize_t err = bio_read(dev, (void *)address, 0, kBlocKSize); + uint8_t *address = static_cast(malloc(kBlocKSize)); + ssize_t err = bio_read(dev, static_cast(address), 0, kBlocKSize); t = current_time() - t; dprintf(INFO, "bio_read returns %d, took %u msecs (%d bytes/sec)\n", (int)err, (uint)t, (uint32_t)((uint64_t)err * 1000 / t)); @@ -117,7 +122,10 @@ int load_pe_file(const char *blkdev) { } printf("PE header machine type: %x\n", static_cast(file_header->Machine)); - if (file_header->SizeOfOptionalHeader != sizeof(IMAGE_OPTIONAL_HEADER64)) { + if (file_header->SizeOfOptionalHeader > sizeof(IMAGE_OPTIONAL_HEADER64) || + file_header->SizeOfOptionalHeader < + sizeof(IMAGE_OPTIONAL_HEADER64) - + sizeof(IMAGE_OPTIONAL_HEADER64::DataDirectory)) { printf("Unexpected size of optional header %d, expected %zu\n", file_header->SizeOfOptionalHeader, sizeof(IMAGE_OPTIONAL_HEADER64)); return -5; @@ -128,7 +136,9 @@ int load_pe_file(const char *blkdev) { ToString(optional_header->Subsystem)); } printf("Valid UEFI application found.\n"); - return load_sections_and_execute(dev, pe_header); + auto ret = load_sections_and_execute(dev, pe_header); + printf("UEFI Application return code: %d\n", ret); + return ret; } int cmd_uefi_load(int argc, const console_cmd_args *argv) { From 9fc4532ad740794602f937078f28ea98fb5948ba Mon Sep 17 00:00:00 2001 From: Kelvin Zhang Date: Mon, 12 Aug 2024 17:48:49 -0700 Subject: [PATCH 03/11] [app][uefi] Imlement get_memory_map UEFI protocol --- lib/uefi/boot_service_provider.cpp | 114 ++++++++++++++++++++++++++--- lib/uefi/boot_service_provider.h | 16 ++++ lib/uefi/include/types.h | 92 ++++++++++++++++++----- lib/uefi/text_protocol.h | 2 + 4 files changed, 194 insertions(+), 30 deletions(-) diff --git a/lib/uefi/boot_service_provider.cpp b/lib/uefi/boot_service_provider.cpp index 04caca8f9..c19170a79 100644 --- a/lib/uefi/boot_service_provider.cpp +++ b/lib/uefi/boot_service_provider.cpp @@ -15,6 +15,8 @@ * */ #include "boot_service_provider.h" +#include "boot_service.h" +#include "kernel/vm.h" #include "types.h" #include #include @@ -82,11 +84,8 @@ EfiStatus free_pool(void *mem) { EfiStatus get_memory_map(size_t *memory_map_size, EfiMemoryDescriptor *memory_map, size_t *map_key, size_t *desc_size, uint32_t *desc_version) { - if (memory_map_size) { - *memory_map_size = 0; - } - if (memory_map) { - memset(memory_map, 0, sizeof(*memory_map)); + if (memory_map_size == nullptr) { + return INVALID_PARAMETER; } if (map_key) { *map_key = 0; @@ -95,11 +94,30 @@ EfiStatus get_memory_map(size_t *memory_map_size, *desc_size = sizeof(EfiMemoryDescriptor); } if (desc_version) { - *desc_version = 0; + *desc_version = 1; + } + vmm_region_t *region = nullptr; + auto aspace = vmm_get_kernel_aspace(); + size_t num_entries = 0; + list_for_every_entry(&aspace->region_list, region, vmm_region_t, node) { + num_entries++; + } + const size_t size_needed = num_entries * sizeof(EfiMemoryDescriptor); + if (*memory_map_size < size_needed) { + *memory_map_size = size_needed; + return BUFFER_TOO_SMALL; + } + *memory_map_size = size_needed; + size_t i = 0; + memset(memory_map, 0, size_needed); + list_for_every_entry(&aspace->region_list, region, vmm_region_t, node) { + memory_map[i].virtual_start = region->base; + memory_map[i].physical_start = memory_map[i].virtual_start; + memory_map[i].number_of_pages = region->size / PAGE_SIZE; + i++; } - printf("%s(%zu) is unsupported\n", __FUNCTION__, *memory_map_size); - return UNSUPPORTED; + return SUCCESS; } EfiStatus register_protocol_notify(const EfiGuid *protocol, EfiEvent event, @@ -118,23 +136,89 @@ EfiStatus locate_handle(EfiLocateHandleSearchType search_type, EfiStatus locate_protocol(const EfiGuid *protocol, void *registration, void **intf) { + if (protocol == nullptr) { + return INVALID_PARAMETER; + } + if (memcmp(protocol, &EFI_RNG_PROTOCOL_GUID, sizeof(*protocol)) == 0) { + printf("%s(EFI_RNG_PROTOCOL_GUID) is unsupported.\n", __FUNCTION__); + return UNSUPPORTED; + } + if (memcmp(protocol, &EFI_TCG2_PROTOCOL_GUID, sizeof(*protocol)) == 0) { + printf("%s(EFI_TCG2_PROTOCOL_GUID) is unsupported.\n", __FUNCTION__); + return NOT_FOUND; + } - printf("%s is unsupported\n", __FUNCTION__); + printf("%s(%x %x %x %llx) is unsupported\n", __FUNCTION__, protocol->data1, + protocol->data2, protocol->data3, + *reinterpret_cast(&protocol->data4)); return UNSUPPORTED; } EfiStatus allocate_pages(EfiAllocatorType type, EfiMemoryType memory_type, size_t pages, EfiPhysicalAddr *memory) { + if (memory == nullptr) { + return INVALID_PARAMETER; + } + if (type == ALLOCATE_MAX_ADDRESS && *memory != 0xffffffffffffffff) { + printf("allocate_pages(%d, %d, %zu, 0x%llx) unsupported\n", type, + memory_type, pages, *memory); + return UNSUPPORTED; + } + *memory = + reinterpret_cast(memalign(PAGE_SIZE, pages * PAGE_SIZE)); + if (*memory == 0) { + return OUT_OF_RESOURCES; + } + return SUCCESS; +} + +EfiStatus free_pages(EfiPhysicalAddr memory, size_t pages) { printf("%s is unsupported\n", __FUNCTION__); return UNSUPPORTED; } -EfiStatus free_pages(EfiPhysicalAddr memory, size_t pages) { +EfiStatus uninstall_multiple_protocol_interfaces(EfiHandle handle, ...) { + printf("%s is unsupported\n", __FUNCTION__); + return UNSUPPORTED; +} +EfiStatus calculate_crc32(void *data, size_t len, uint32_t *crc32) { + printf("%s is unsupported\n", __FUNCTION__); + return UNSUPPORTED; +} + +EfiStatus uninstall_protocol_interface(EfiHandle handle, + const EfiGuid *protocol, void *intf) { + printf("%s is unsupported\n", __FUNCTION__); + return UNSUPPORTED; +} + +EfiStatus load_image(bool boot_policy, EfiHandle parent_image_handle, + EfiDevicePathProtocol *path, void *src, size_t src_size, + EfiHandle *image_handle) { + printf("%s is unsupported\n", __FUNCTION__); + return UNSUPPORTED; +} +EfiStatus locate_device_path(const EfiGuid *protocol, + EfiDevicePathProtocol **path, EfiHandle *device) { + if (memcmp(protocol, &EFI_LOAD_FILE2_PROTOCOL_GUID, + sizeof(EFI_LOAD_FILE2_PROTOCOL_GUID)) == 0) { + return NOT_FOUND; + } printf("%s is unsupported\n", __FUNCTION__); return UNSUPPORTED; } +EfiStatus install_configuration_table(const EfiGuid *guid, void *table) { + printf("%s is unsupported\n", __FUNCTION__); + return UNSUPPORTED; +} + +EfiStatus exit_boot_services(EfiHandle image_handle, size_t map_key) { + printf("%s is called\n", __FUNCTION__); + return SUCCESS; +} + } // namespace void setup_boot_service_table(EfiBootService *service) { @@ -147,4 +231,12 @@ void setup_boot_service_table(EfiBootService *service) { service->locate_protocol = locate_protocol; service->allocate_pages = allocate_pages; service->free_pages = free_pages; -} \ No newline at end of file + service->uninstall_multiple_protocol_interfaces = + uninstall_multiple_protocol_interfaces; + service->calculate_crc32 = calculate_crc32; + service->uninstall_protocol_interface = uninstall_protocol_interface; + service->load_image = load_image; + service->locate_device_path = locate_device_path; + service->install_configuration_table = install_configuration_table; + service->exit_boot_services = exit_boot_services; +} diff --git a/lib/uefi/boot_service_provider.h b/lib/uefi/boot_service_provider.h index f87340293..afb3a7bcf 100644 --- a/lib/uefi/boot_service_provider.h +++ b/lib/uefi/boot_service_provider.h @@ -39,7 +39,23 @@ static constexpr auto LINUX_EFI_LOADED_IMAGE_FIXED_GUID = 0x3344, 0x42a5, {0xb6, 0xbb, 0x97, 0x86, 0x48, 0xc1, 0x89, 0x0a}}; +static constexpr auto EFI_RNG_PROTOCOL_GUID = + EfiGuid{0x3152bca5, + 0xeade, + 0x433d, + {0x86, 0x2e, 0xc0, 0x1c, 0xdc, 0x29, 0x1f, 0x44}}; +static constexpr auto EFI_TCG2_PROTOCOL_GUID = + EfiGuid{0x607f766c, + 0x7455, + 0x42be, + {0x93, 0x0b, 0xe4, 0xd7, 0x6d, 0xb2, 0x72, 0x0f}}; + +static constexpr auto EFI_LOAD_FILE2_PROTOCOL_GUID = + EfiGuid{0x4006c0c1, + 0xfcb3, + 0x403e, + {0x99, 0x6d, 0x4a, 0x6c, 0x87, 0x24, 0xe0, 0x6d}}; using EFI_IMAGE_UNLOAD = EfiStatus (*)(EfiHandle); //****************************************************** diff --git a/lib/uefi/include/types.h b/lib/uefi/include/types.h index 6787cef61..eea059cd8 100644 --- a/lib/uefi/include/types.h +++ b/lib/uefi/include/types.h @@ -20,51 +20,51 @@ #include #include -typedef struct EfiTableHeader { +using EfiTableHeader = struct EfiTableHeader { uint64_t signature; uint32_t revision; uint32_t header_size; uint32_t crc32; uint32_t reserved; -} EfiTableHeader; +}; -typedef struct EfiGuid { +using EfiGuid = struct EfiGuid { uint32_t data1; uint16_t data2; uint16_t data3; uint8_t data4[8]; -} EfiGuid; +}; -typedef void *EfiHandle; -typedef void *EfiEvent; -typedef uint64_t EfiPhysicalAddr; -typedef uint64_t EfiVirtualAddr; +using EfiHandle = void *; +using EfiEvent = void *; +using EfiPhysicalAddr = uint64_t; +using EfiVirtualAddr = uint64_t; -typedef void (*EfiEventNotify)(EfiEvent event, void *ctx); +using EfiEventNotify = void (*)(EfiEvent, void *); -typedef enum EFI_EVENT_TYPE : uint32_t { +using EfiEventType = enum EFI_EVENT_TYPE : uint32_t { TIMER = 0x80000000, RUNTIME = 0x40000000, NOTIFY_WAIT = 0x00000100, NOTIFY_SIGNAL = 0x00000200, SIGNAL_EXIT_BOOT_SERVICES = 0x00000201, SIGNAL_VIRTUAL_ADDRESS_CHANGE = 0x60000202, -} EfiEventType; +}; -typedef enum EFI_TPL : size_t { +using EfiTpl = enum EFI_TPL : size_t { APPLICATION = 4, CALLBACK = 8, NOTIFY = 16, HIGH_LEVEL = 31, -} EfiTpl; +}; -typedef enum EFI_TIMER_DELAY { +using EfiTimerDelay = enum EFI_TIMER_DELAY { TIMER_CANCEL, TIMER_PERIODIC, TIMER_RELATIVE -} EfiTimerDelay; +}; -typedef enum { +using EFI_MEMORY_TYPE = enum { RESERVED_MEMORY_TYPE, LOADER_CODE, LOADER_DATA, @@ -81,14 +81,14 @@ typedef enum { PAL_CODE, PERSISTENT_MEMORY, MAX_MEMORY_TYPE -} EFI_MEMORY_TYPE; +}; typedef EFI_MEMORY_TYPE EfiMemoryType; #define EFI_ERROR_MASK ((uintptr_t)INTPTR_MAX + 1) #define EFI_ERR(x) (EFI_ERROR_MASK | (x)) -typedef enum EFI_STATUS : uintptr_t { +using EfiStatus = enum EFI_STATUS : uintptr_t { SUCCESS = 0u, LOAD_ERROR = EFI_ERR(1), INVALID_PARAMETER = EFI_ERR(2), @@ -126,6 +126,60 @@ typedef enum EFI_STATUS : uintptr_t { CONNECTION_FIN = EFI_ERR(104), CONNECTION_RESET = EFI_ERR(105), CONNECTION_REFUSED = EFI_ERR(106), -} EfiStatus; +}; + +/// +/// EFI Time Abstraction: +/// Year: 1900 - 9999 +/// Month: 1 - 12 +/// Day: 1 - 31 +/// Hour: 0 - 23 +/// Minute: 0 - 59 +/// Second: 0 - 59 +/// Nanosecond: 0 - 999,999,999 +/// TimeZone: -1440 to 1440 or 2047 +/// +using EfiTime = struct { + uint16_t Year; + uint8_t Month; + uint8_t Day; + uint8_t Hour; + uint8_t Minute; + uint8_t Second; + uint8_t Pad1; + uint32_t Nanosecond; + int16_t TimeZone; + uint8_t Daylight; + uint8_t Pad2; +}; + +/// +/// This provides the capabilities of the +/// real time clock device as exposed through the EFI interfaces. +/// +using EFI_TIME_CAPABILITIES = struct { + /// + /// Provides the reporting resolution of the real-time clock device in + /// counts per second. For a normal PC-AT CMOS RTC device, this + /// value would be 1 Hz, or 1, to indicate that the device only reports + /// the time to the resolution of 1 second. + /// + uint32_t Resolution; + /// + /// Provides the timekeeping accuracy of the real-time clock in an + /// error rate of 1E-6 parts per million. For a clock with an accuracy + /// of 50 parts per million, the value in this field would be + /// 50,000,000. + /// + uint32_t Accuracy; + /// + /// A TRUE indicates that a time set operation clears the device's + /// time below the Resolution reporting level. A FALSE + /// indicates that the state below the Resolution level of the + /// device is not cleared when the time is set. Normal PC-AT CMOS + /// RTC devices set this value to FALSE. + /// + bool SetsToZero; +}; #endif // __EFI_TYPES_H__ diff --git a/lib/uefi/text_protocol.h b/lib/uefi/text_protocol.h index 0002d67d7..ee8465d53 100644 --- a/lib/uefi/text_protocol.h +++ b/lib/uefi/text_protocol.h @@ -21,5 +21,7 @@ #include "protocols/simple_text_output_protocol.h" EfiSimpleTextOutputProtocol get_text_output_protocol(); +EfiStatus output_string(struct EfiSimpleTextOutputProtocol *self, + char16_t *string); #endif \ No newline at end of file From 6a37823f713cd14d9744c8c1b70e42cbd8bc148e Mon Sep 17 00:00:00 2001 From: Kelvin Zhang Date: Mon, 12 Aug 2024 17:48:49 -0700 Subject: [PATCH 04/11] [app][uefi] Add runtime service support --- lib/uefi/include/runtime_service.h | 531 ++++++++++++++++++++++++++ lib/uefi/include/system_table.h | 3 +- lib/uefi/rules.mk | 1 + lib/uefi/runtime_service_provider.cpp | 69 ++++ lib/uefi/runtime_service_provider.h | 25 ++ lib/uefi/uefi.cpp | 8 +- 6 files changed, 635 insertions(+), 2 deletions(-) create mode 100644 lib/uefi/include/runtime_service.h create mode 100644 lib/uefi/runtime_service_provider.cpp create mode 100644 lib/uefi/runtime_service_provider.h diff --git a/lib/uefi/include/runtime_service.h b/lib/uefi/include/runtime_service.h new file mode 100644 index 000000000..d6fc98e85 --- /dev/null +++ b/lib/uefi/include/runtime_service.h @@ -0,0 +1,531 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except compliance with the License. + * You may obtaa copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHWARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef __RUNTIME_SERVICE_H_ +#define __RUNTIME_SERVICE_H_ + +#include "boot_service.h" +#include "types.h" +#define EFI_RUNTIME_SERVICES_SIGNATURE 0x56524553544e5552 +#define EFI_2_80_SYSTEM_TABLE_REVISION ((2 << 16) | (80)) +#define EFI_2_70_SYSTEM_TABLE_REVISION ((2 << 16) | (70)) +#define EFI_2_60_SYSTEM_TABLE_REVISION ((2 << 16) | (60)) +#define EFI_2_50_SYSTEM_TABLE_REVISION ((2 << 16) | (50)) +#define EFI_2_40_SYSTEM_TABLE_REVISION ((2 << 16) | (40)) +#define EFI_2_31_SYSTEM_TABLE_REVISION ((2 << 16) | (31)) +#define EFI_2_30_SYSTEM_TABLE_REVISION ((2 << 16) | (30)) +#define EFI_2_20_SYSTEM_TABLE_REVISION ((2 << 16) | (20)) +#define EFI_2_10_SYSTEM_TABLE_REVISION ((2 << 16) | (10)) +#define EFI_2_00_SYSTEM_TABLE_REVISION ((2 << 16) | (00)) +#define EFI_1_10_SYSTEM_TABLE_REVISION ((1 << 16) | (10)) +#define EFI_1_02_SYSTEM_TABLE_REVISION ((1 << 16) | (02)) +#define EFI_SYSTEM_TABLE_REVISION EFI_2_70_SYSTEM_TABLE_REVISION +#define EFI_SPECIFICATION_VERSION EFI_SYSTEM_TABLE_REVISION + +#define EFI_RUNTIME_SERVICES_REVISION EFI_SPECIFICATION_VERSION + +/** + Returns the current time and date information, and the time-keeping + capabilities of the hardware platform. + + @param[out] Time A pointer to storage to receive a snapshot of + the current time. + @param[out] Capabilities An optional pointer to a buffer to receive the + real time clock device's capabilities. + + @retval EFI_SUCCESS The operation completed successfully. + @retval EFI_INVALID_PARAMETER Time is NULL. + @retval EFI_DEVICE_ERROR The time could not be retrieved due to hardware + error. + @retval EFI_UNSUPPORTED This call is not supported by this platform at + the time the call is made. The platform should describe this runtime service as + unsupported at runtime via an EFI_RT_PROPERTIES_TABLE configuration table. + + **/ +typedef EFI_STATUS (*EFI_GET_TIME)(EfiTime *Time, + EFI_TIME_CAPABILITIES *Capabilities); + +/** + Sets the current local time and date information. + + @param[in] Time A pointer to the current time. + + @retval EFI_SUCCESS The operation completed successfully. + @retval EFI_INVALID_PARAMETER A time field is of range. + @retval EFI_DEVICE_ERROR The time could not be set due due to hardware +error. + @retval EFI_UNSUPPORTED This call is not supported by this platform at +the time the call is made. The platform should describe this runtime service as +unsupported at runtime via an EFI_RT_PROPERTIES_TABLE configuration table. + +**/ +typedef EFI_STATUS (*EFI_SET_TIME)(EfiTime *Time); + +/** + Returns the current wakeup alarm clock setting. + + @param[out] Enabled Indicates if the alarm is currently enabled or +disabled. + @param[out] Pending Indicates if the alarm signal is pending and +requires acknowledgement. + @param[out] Time The current alarm setting. + + @retval EFI_SUCCESS The alarm settings were returned. + @retval EFI_INVALID_PARAMETER Enabled is NULL. + @retval EFI_INVALID_PARAMETER Pending is NULL. + @retval EFI_INVALID_PARAMETER Time is NULL. + @retval EFI_DEVICE_ERROR The wakeup time could not be retrieved due to a +hardware error. + @retval EFI_UNSUPPORTED This call is not supported by this platform at +the time the call is made. The platform should describe this runtime service as +unsupported at runtime via an EFI_RT_PROPERTIES_TABLE configuration table. + +**/ +typedef EFI_STATUS (*EFI_GET_WAKEUP_TIME)(bool *Enabled, bool *Pending, + EfiTime *Time); + +/** + Sets the system wakeup alarm clock time. + + @param[in] Enable Enable or disable the wakeup alarm. + @param[in] Time If Enable is TRUE, the time to set the wakeup +alarm for. If Enable is FALSE, then this parameter is optional, and may be NULL. + + @retval EFI_SUCCESS If Enable is TRUE, then the wakeup alarm was +enabled. If Enable is FALSE, then the wakeup alarm was disabled. + @retval EFI_INVALID_PARAMETER A time field is of range. + @retval EFI_DEVICE_ERROR The wakeup time could not be set due to a +hardware error. + @retval EFI_UNSUPPORTED This call is not supported by this platform at +the time the call is made. The platform should describe this runtime service as +unsupported at runtime via an EFI_RT_PROPERTIES_TABLE configuration table. + +**/ +typedef EFI_STATUS (*EFI_SET_WAKEUP_TIME)(bool Enable, EfiTime *Time); + +/** + Changes the runtime addressing mode of EFI firmware from physical to virtual. + + @param[in] MemoryMapSize The size in bytes of VirtualMap. + @param[in] DescriptorSize The size in bytes of an entry in the VirtualMap. + @param[in] DescriptorVersion The version of the structure entries in +VirtualMap. + @param[in] VirtualMap An array of memory descriptors which contain new +virtual address mapping information for all runtime ranges. + + @retval EFI_SUCCESS The virtual address map has been applied. + @retval EFI_UNSUPPORTED EFI firmware is not at runtime, or the EFI +firmware is already in virtual address mapped mode. + @retval EFI_INVALID_PARAMETER DescriptorSize or DescriptorVersion is invalid. + @retval EFI_NO_MAPPING A virtual address was not supplied for a range +in the memory map that requires a mapping. + @retval EFI_NOT_FOUND A virtual address was supplied for an address +that is not found in the memory map. + @retval EFI_UNSUPPORTED This call is not supported by this platform at +the time the call is made. The platform should describe this runtime service as +unsupported at runtime via an EFI_RT_PROPERTIES_TABLE configuration table. + +**/ +typedef EFI_STATUS (*EFI_SET_VIRTUAL_ADDRESS_MAP)( + size_t MemoryMapSize, size_t DescriptorSize, uint32_t DescriptorVersion, + EfiMemoryDescriptor *VirtualMap); + +/** + Determines the new virtual address that is to be used on subsequent memory +accesses. + + @param[in] DebugDisposition Supplies type information for the pointer +being converted. + @param[in, out] Address A pointer to a pointer that is to be fixed +to be the value needed for the new virtual address mappings being applied. + + @retval EFI_SUCCESS The pointer pointed to by Address was modified. + @retval EFI_NOT_FOUND The pointer pointed to by Address was not found +to be part of the current memory map. This is normally fatal. + @retval EFI_INVALID_PARAMETER Address is NULL. + @retval EFI_INVALID_PARAMETER *Address is NULL and DebugDisposition does + not have the EFI_OPTIONAL_PTR bit set. + @retval EFI_UNSUPPORTED This call is not supported by this platform at +the time the call is made. The platform should describe this runtime service as +unsupported at runtime via an EFI_RT_PROPERTIES_TABLE configuration table. + +**/ +typedef EFI_STATUS (*EFI_CONVERT_POINTER)(size_t DebugDisposition, + void **Address); + +/** + Returns the value of a variable. + + @param[in] VariableName A Null-terminated string that is the name of + the vendor's variable. + @param[in] VendorGuid A unique identifier for the vendor. + @param[out] Attributes If not NULL, a pointer to the memory location + to return the attributes bitmask for the variable. + @param[in, out] DataSize On input, the size in bytes of the return Data + buffer. On output the size of data returned in Data. + @param[out] Data The buffer to return the contents of the + variable. May be NULL with a zero DataSize in order to determine the size + buffer needed. + + @retval EFI_SUCCESS The function completed successfully. + @retval EFI_NOT_FOUND The variable was not found. + @retval EFI_BUFFER_TOO_SMALL The DataSize is too small for the result. + @retval EFI_INVALID_PARAMETER VariableName is NULL. + @retval EFI_INVALID_PARAMETER VendorGuid is NULL. + @retval EFI_INVALID_PARAMETER DataSize is NULL. + @retval EFI_INVALID_PARAMETER The DataSize is not too small and Data is + NULL. + @retval EFI_DEVICE_ERROR The variable could not be retrieved due to a + hardware error. + @retval EFI_SECURITY_VIOLATION The variable could not be retrieved due to an + authentication failure. + @retval EFI_UNSUPPORTED After ExitBootServices() has been called, this + return code may be returned if no variable storage is supported. The platform + should describe this runtime service as unsupported at runtime via an + EFI_RT_PROPERTIES_TABLE configuration table. + + **/ +typedef EFI_STATUS (*EFI_GET_VARIABLE)(char16_t *VariableName, + EfiGuid *VendorGuid, + uint32_t *Attributes, size_t *DataSize, + void *Data); + +/** + Enumerates the current variable names. + + @param[in, out] VariableNameSize The size of the VariableName buffer. The +size must be large enough to fit input string supplied in VariableName buffer. + @param[in, out] VariableName On input, supplies the last VariableName +that was returned by GetNextVariableName(). On output, returns the +Nullterminated string of the current variable. + @param[in, out] VendorGuid On input, supplies the last VendorGuid that +was returned by GetNextVariableName(). On output, returns the VendorGuid of the +current variable. + + @retval EFI_SUCCESS The function completed successfully. + @retval EFI_NOT_FOUND The next variable was not found. + @retval EFI_BUFFER_TOO_SMALL The VariableNameSize is too small for the +result. VariableNameSize has been updated with the size needed to complete the +request. + @retval EFI_INVALID_PARAMETER VariableNameSize is NULL. + @retval EFI_INVALID_PARAMETER VariableName is NULL. + @retval EFI_INVALID_PARAMETER VendorGuid is NULL. + @retval EFI_INVALID_PARAMETER The input values of VariableName and VendorGuid +are not a name and GUID of an existing variable. + @retval EFI_INVALID_PARAMETER Null-terminator is not found in the first +VariableNameSize bytes of the input VariableName buffer. + @retval EFI_DEVICE_ERROR The variable could not be retrieved due to a +hardware error. + @retval EFI_UNSUPPORTED After ExitBootServices() has been called, this +return code may be returned if no variable storage is supported. The platform +should describe this runtime service as unsupported at runtime via an +EFI_RT_PROPERTIES_TABLE configuration table. + +**/ +typedef EFI_STATUS (*EFI_GET_NEXT_VARIABLE_NAME)(size_t *VariableNameSize, + char16_t *VariableName, + EfiGuid *VendorGuid); + +/** + Sets the value of a variable. + + @param[in] VariableName A Null-terminated string that is the name of +the vendor's variable. Each VariableName is unique for each VendorGuid. +VariableName must contain 1 or more characters. If VariableName is an empty +string, then EFI_INVALID_PARAMETER is returned. + @param[in] VendorGuid A unique identifier for the vendor. + @param[in] Attributes Attributes bitmask to set for the variable. + @param[in] DataSize The size in bytes of the Data buffer. Unless +the EFI_VARIABLE_APPEND_WRITE or + EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS +attribute is set, a size of zero causes the variable to be deleted. When the +EFI_VARIABLE_APPEND_WRITE attribute is set, then a SetVariable() call with a +DataSize of zero will not cause any change to the variable value (the timestamp +associated with the variable may be updated however even if no new data value is +provided,see the description of the EFI_VARIABLE_AUTHENTICATION_2 descriptor +below. In this case the DataSize will not be zero since the +EFI_VARIABLE_AUTHENTICATION_2 descriptor will be populated). + @param[in] Data The contents for the variable. + + @retval EFI_SUCCESS The firmware has successfully stored the +variable and its data as defined by the Attributes. + @retval EFI_INVALID_PARAMETER An invalid combination of attribute bits, name, +and GUID was supplied, or the DataSize exceeds the maximum allowed. + @retval EFI_INVALID_PARAMETER VariableName is an empty string. + @retval EFI_OUT_OF_RESOURCES Not enough storage is available to hold the +variable and its data. + @retval EFI_DEVICE_ERROR The variable could not be retrieved due to a +hardware error. + @retval EFI_WRITE_PROTECTED The variable in question is read-only. + @retval EFI_WRITE_PROTECTED The variable in question cannot be deleted. + @retval EFI_SECURITY_VIOLATION The variable could not be written due to +EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACESS being set, but the AuthInfo +does NOT pass the validation check carried out by the firmware. + + @retval EFI_NOT_FOUND The variable trying to be updated or deleted +was not found. + @retval EFI_UNSUPPORTED This call is not supported by this platform at +the time the call is made. The platform should describe this runtime service as +unsupported at runtime via an EFI_RT_PROPERTIES_TABLE configuration table. + +**/ +typedef EFI_STATUS (*EFI_SET_VARIABLE)(char16_t *VariableName, + EfiGuid *VendorGuid, uint32_t Attributes, + size_t DataSize, void *Data); + +/** +Returns the next high 32 bits of the platform's monotonic counter. + +@param[out] HighCount The pointer to returned value. + +@retval EFI_SUCCESS The next high monotonic count was returned. +@retval EFI_INVALID_PARAMETER HighCount is NULL. +@retval EFI_DEVICE_ERROR The device is not functioning properly. +@retval EFI_UNSUPPORTED This call is not supported by this platform at the +time the call is made. The platform should describe this runtime service as +unsupported at runtime via an EFI_RT_PROPERTIES_TABLE configuration table. + +**/ +typedef EFI_STATUS (*EFI_GET_NEXT_HIGH_MONO_COUNT)(uint32_t *HighCount); + +/// +/// Enumeration of reset types. +/// +typedef enum { + /// + /// Used to induce a system-wide reset. This sets all circuitry within the + /// system to its initial state. This type of reset is asynchronous to system + /// operation and operates withgout regard to cycle boundaries. EfiColdReset + /// is tantamount to a system power cycle. + /// + EfiResetCold, + /// + /// Used to induce a system-wide initialization. The processors are set to + /// their + /// initial state, and pending cycles are not corrupted. If the system does + /// not support this reset type, then an EfiResetCold must be performed. + /// + EfiResetWarm, + /// + /// Used to induce an entry into a power state equivalent to the ACPI G2/S5 or + /// G3 + /// state. If the system does not support this reset type, then when the + /// system + /// is rebooted, it should exhibit the EfiResetCold attributes. + /// + EfiResetShutdown, + /// + /// Used to induce a system-wide reset. The exact type of the reset is defined + /// by + /// the EFI_GUID that follows the Null-terminated Unicode string passed into + /// ResetData. If the platform does not recognize the EFI_GUID in ResetData + /// the + /// platform must pick a supported reset type to perform. The platform may + /// optionally log the parameters from any non-normal reset that occurs. + /// + EfiResetPlatformSpecific +} EFI_RESET_TYPE; + +/** + Resets the entire platform. + + @param[in] ResetType The type of reset to perform. + @param[in] ResetStatus The status code for the reset. + @param[in] DataSize The size, in bytes, of ResetData. + @param[in] ResetData For a ResetType of EfiResetCold, EfiResetWarm, +or EfiResetShutdown the data buffer starts with a Null-terminated string, +optionally followed by additional binary data. The string is a description that +the caller may use to further indicate the reason for the system reset. For a +ResetType of EfiResetPlatformSpecific the data buffer also starts with a +Null-terminated string that is followed by an EfiGuid that describes the +specific type of reset to perform. +**/ +typedef void (*EFI_RESET_SYSTEM)(EFI_RESET_TYPE ResetType, + EFI_STATUS ResetStatus, size_t DataSize, + void *ResetData); + +/// +/// EFI Capsule Header. +/// +typedef struct { + /// + /// A GUID that defines the contents of a capsule. + /// + EfiGuid CapsuleGuid; + /// + /// The size of the capsule header. This may be larger than the size of + /// the EFI_CAPSULE_HEADER since CapsuleGuid may imply + /// extended header entries + /// + uint32_t HeaderSize; + /// + /// Bit-mapped list describing the capsule attributes. The Flag values + /// of 0x0000 - 0xFFFF are defined by CapsuleGuid. Flag values + /// of 0x10000 - 0xFFFFFFFF are defined by this specification + /// + uint32_t Flags; + /// + /// Size in bytes of the capsule (including capsule header). + /// + uint32_t CapsuleImageSize; +} EFI_CAPSULE_HEADER; + +/** + Passes capsules to the firmware with both virtual and physical mapping. +Depending on the intended consumption, the firmware may process the capsule +immediately. If the payload should persist across a system reset, the reset +value returned from EFI_QueryCapsuleCapabilities must be passed into +ResetSystem() and will cause the capsule to be processed by the firmware as part +of the reset process. + + @param[in] CapsuleHeaderArray Virtual pointer to an array of virtual pointers +to the capsules being passed into update capsule. + @param[in] CapsuleCount Number of pointers to EFI_CAPSULE_HEADER in + CaspuleHeaderArray. + @param[in] ScatterGatherList Physical pointer to a set of + EFI_CAPSULE_BLOCK_DESCRIPTOR that describes the + location in physical memory of a set of +capsules. + + @retval EFI_SUCCESS Valid capsule was passed. If + CAPSULE_FLAGS_PERSIT_ACROSS_RESET is not set, +the capsule has been successfully processed by the firmware. + @retval EFI_INVALID_PARAMETER CapsuleSize is NULL, or an incompatible set of +flags were set in the capsule header. + @retval EFI_INVALID_PARAMETER CapsuleCount is 0. + @retval EFI_DEVICE_ERROR The capsule update was started, but failed due +to a device error. + @retval EFI_UNSUPPORTED The capsule type is not supported on this +platform. + @retval EFI_OUT_OF_RESOURCES When ExitBootServices() has been previously +called this error indicates the capsule is compatible with this platform but is +not capable of being submitted or processed in runtime. The caller may resubmit +the capsule prior to ExitBootServices(). + @retval EFI_OUT_OF_RESOURCES When ExitBootServices() has not been previously +called then this error indicates the capsule is compatible with this platform +but there are insufficient resources to process. + @retval EFI_UNSUPPORTED This call is not supported by this platform at +the time the call is made. The platform should describe this runtime service as +unsupported at runtime via an EFI_RT_PROPERTIES_TABLE configuration table. + +**/ +typedef EFI_STATUS (*EFI_UPDATE_CAPSULE)( + EFI_CAPSULE_HEADER **CapsuleHeaderArray, size_t CapsuleCount, + uint64_t ScatterGatherList); + +/** + Returns if the capsule can be supported via UpdateCapsule(). + + @param[in] CapsuleHeaderArray Virtual pointer to an array of virtual +pointers to the capsules being passed into update capsule. + @param[in] CapsuleCount Number of pointers to EFI_CAPSULE_HEADER in + CaspuleHeaderArray. + @param[out] MaxiumCapsuleSize On output the maximum size that +UpdateCapsule() can support as an argument to UpdateCapsule() via + CapsuleHeaderArray and ScatterGatherList. + @param[out] ResetType Returns the type of reset required for the +capsule update. + + @retval EFI_SUCCESS Valid answer returned. + @retval EFI_UNSUPPORTED The capsule type is not supported on this +platform, and MaximumCapsuleSize and ResetType are undefined. + @retval EFI_INVALID_PARAMETER MaximumCapsuleSize is NULL. + @retval EFI_OUT_OF_RESOURCES When ExitBootServices() has been previously +called this error indicates the capsule is compatible with this platform but is +not capable of being submitted or processed in runtime. The caller may resubmit +the capsule prior to ExitBootServices(). + @retval EFI_OUT_OF_RESOURCES When ExitBootServices() has not been previously +called then this error indicates the capsule is compatible with this platform +but there are insufficient resources to process. + @retval EFI_UNSUPPORTED This call is not supported by this platform at +the time the call is made. The platform should describe this runtime service as +unsupported at runtime via an EFI_RT_PROPERTIES_TABLE configuration table. + +**/ +typedef EFI_STATUS (*EFI_QUERY_CAPSULE_CAPABILITIES)( + EFI_CAPSULE_HEADER **CapsuleHeaderArray, size_t CapsuleCount, + uint64_t *MaximumCapsuleSize, EFI_RESET_TYPE *ResetType); + +/** + Returns information about the EFI variables. + + @param[in] Attributes Attributes bitmask to specify the +type of variables on which to return information. + @param[out] MaximumVariableStorageSize On output the maximum size of the +storage space available for the EFI variables associated with the attributes +specified. + @param[out] RemainingVariableStorageSize Returns the remaining size of the +storage space available for the EFI variables associated with the attributes +specified. + @param[out] MaximumVariableSize Returns the maximum size of the +individual EFI variables associated with the attributes specified. + + @retval EFI_SUCCESS Valid answer returned. + @retval EFI_INVALID_PARAMETER An invalid combination of attribute bits +was supplied + @retval EFI_UNSUPPORTED The attribute is not supported on this +platform, and the MaximumVariableStorageSize, RemainingVariableStorageSize, +MaximumVariableSize are undefined. + +**/ +typedef EFI_STATUS (*EFI_QUERY_VARIABLE_INFO)( + uint32_t Attributes, uint64_t *MaximumVariableStorageSize, + uint64_t *RemainingVariableStorageSize, uint64_t *MaximumVariableSize); + +typedef struct { + EfiTableHeader Hdr; + + // + // Time Services + // + EFI_GET_TIME GetTime; + EFI_SET_TIME SetTime; + EFI_GET_WAKEUP_TIME GetWakeupTime; + EFI_SET_WAKEUP_TIME SetWakeupTime; + + // + // Virtual Memory Services + // + EFI_SET_VIRTUAL_ADDRESS_MAP SetVirtualAddressMap; + EFI_CONVERT_POINTER ConvertPointer; + + // + // Variable Services + // + EFI_GET_VARIABLE GetVariable; + EFI_GET_NEXT_VARIABLE_NAME GetNextVariableName; + EFI_SET_VARIABLE SetVariable; + + // + // Miscellaneous Services + // + EFI_GET_NEXT_HIGH_MONO_COUNT GetNextHighMonotonicCount; + EFI_RESET_SYSTEM ResetSystem; + + // + // UEFI 2.0 Capsule Services + // + EFI_UPDATE_CAPSULE UpdateCapsule; + EFI_QUERY_CAPSULE_CAPABILITIES QueryCapsuleCapabilities; + + // + // Miscellaneous UEFI 2.0 Service + // + EFI_QUERY_VARIABLE_INFO QueryVariableInfo; +} EfiRuntimeService; + +#endif diff --git a/lib/uefi/include/system_table.h b/lib/uefi/include/system_table.h index 404cfc812..9d8fbdd2c 100644 --- a/lib/uefi/include/system_table.h +++ b/lib/uefi/include/system_table.h @@ -23,6 +23,7 @@ #include "boot_service.h" #include "protocols/simple_text_input_protocol.h" #include "protocols/simple_text_output_protocol.h" +#include "runtime_service.h" typedef struct { EfiGuid vendor_guid; @@ -39,7 +40,7 @@ typedef struct EfiSystemTable { EfiSimpleTextOutputProtocol* con_out; EfiHandle standard_error_handle; EfiSimpleTextOutputProtocol* std_err; - void* runtime_service; + EfiRuntimeService *runtime_service; EfiBootService* boot_services; size_t number_of_table_entries; const EfiConfigurationTable* configuration_table; diff --git a/lib/uefi/rules.mk b/lib/uefi/rules.mk index 842cdc08f..37718c1ce 100644 --- a/lib/uefi/rules.mk +++ b/lib/uefi/rules.mk @@ -8,5 +8,6 @@ MODULE_SRCS += \ $(LOCAL_DIR)/uefi.cpp \ $(LOCAL_DIR)/text_protocol.cpp \ $(LOCAL_DIR)/boot_service_provider.cpp \ + $(LOCAL_DIR)/runtime_service_provider.cpp \ include make/module.mk diff --git a/lib/uefi/runtime_service_provider.cpp b/lib/uefi/runtime_service_provider.cpp new file mode 100644 index 000000000..c3e8d9899 --- /dev/null +++ b/lib/uefi/runtime_service_provider.cpp @@ -0,0 +1,69 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include "runtime_service_provider.h" +#include "types.h" + +#include +#include + +constexpr auto &&kSecureBoot = L"SecureBoot"; + +EFI_STATUS GetVariable(char16_t *VariableName, EfiGuid *VendorGuid, + uint32_t *Attributes, size_t *DataSize, void *Data) { + char buffer[512]; + size_t i = 0; + while (VariableName[i] && i < sizeof(buffer)) { + size_t j = 0; + for (j = 0; j < sizeof(buffer) - 1 && VariableName[i + j]; j++) { + buffer[j] = VariableName[i + j]; + } + i += j; + } + buffer[i] = 0; + if (strcmp(buffer, "SecureBoot") == 0 || strcmp(buffer, "SetupMode") == 0) { + if (DataSize) { + *DataSize = 1; + } + if (Data) { + memset(Data, 0, 1); + } + return SUCCESS; + } + + printf("%s(%s) is unsupported\n", __FUNCTION__, buffer); + return UNSUPPORTED; +} + +EFI_STATUS SetVirtualAddressMap(size_t MemoryMapSize, size_t DescriptorSize, + uint32_t DescriptorVersion, + EfiMemoryDescriptor *VirtualMap) { + printf("%s is unsupported\n", __FUNCTION__); + return UNSUPPORTED; +} + +EFI_STATUS SetVariable(char16_t *VariableName, EfiGuid *VendorGuid, + uint32_t Attributes, size_t DataSize, void *Data) { + printf("%s is unsupported\n", __FUNCTION__); + return UNSUPPORTED; +} + +void setup_runtime_service_table(EfiRuntimeService *service) { + service->GetVariable = GetVariable; + service->SetVariable = SetVariable; + service->SetVirtualAddressMap = SetVirtualAddressMap; +} diff --git a/lib/uefi/runtime_service_provider.h b/lib/uefi/runtime_service_provider.h new file mode 100644 index 000000000..579ed905b --- /dev/null +++ b/lib/uefi/runtime_service_provider.h @@ -0,0 +1,25 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef __RUNTIME_SERVICE_PROVIDER_ +#define __RUNTIME_SERVICE_PROVIDER_ + +#include "runtime_service.h" + +void setup_runtime_service_table(EfiRuntimeService *service); + +#endif diff --git a/lib/uefi/uefi.cpp b/lib/uefi/uefi.cpp index 69e68bd31..c6a60ea1d 100644 --- a/lib/uefi/uefi.cpp +++ b/lib/uefi/uefi.cpp @@ -16,6 +16,8 @@ #include #include "protocols/simple_text_output_protocol.h" +#include "runtime_service.h" +#include "runtime_service_provider.h" #include "system_table.h" #include "text_protocol.h" @@ -79,9 +81,13 @@ int load_sections_and_execute(bdev_t *dev, EfiSystemTable table{}; EfiBootService boot_service{}; + EfiRuntimeService runtime_service{}; + fill(&runtime_service, 0); fill(&boot_service, 0); + setup_runtime_service_table(&runtime_service); + setup_boot_service_table(&boot_service); + table.runtime_service = &runtime_service; table.boot_services = &boot_service; - setup_boot_service_table(table.boot_services); table.header.signature = EFI_SYSTEM_TABLE_SIGNATURE; EfiSimpleTextOutputProtocol console_out = get_text_output_protocol(); table.con_out = &console_out; From 253e3099ec6796b153e227aafb64405072670013 Mon Sep 17 00:00:00 2001 From: Kelvin Zhang Date: Tue, 13 Aug 2024 21:05:49 -0700 Subject: [PATCH 05/11] [app][uefi] Add relocation support for UEFI binary --- lib/uefi/boot_service_provider.cpp | 3 +- lib/uefi/pe.h | 32 +++++ lib/uefi/uefi.cpp | 217 ++++++++++++++++++++++++++++- 3 files changed, 246 insertions(+), 6 deletions(-) diff --git a/lib/uefi/boot_service_provider.cpp b/lib/uefi/boot_service_provider.cpp index c19170a79..b2f012be1 100644 --- a/lib/uefi/boot_service_provider.cpp +++ b/lib/uefi/boot_service_provider.cpp @@ -34,7 +34,7 @@ bool guid_eq(const EfiGuid *a, const EfiGuid &b) { return memcmp(a, &b, sizeof(*a)) == 0; } -EfiStatus handle_protocol(EfiHandle handle, const EfiGuid *protocol, +EfiStatus handle_protocol(const EfiHandle handle, const EfiGuid *protocol, void **intf) { if (guid_eq(protocol, LOADED_IMAGE_PROTOCOL_GUID)) { printf("handle_protocol(%p, LOADED_IMAGE_PROTOCOL_GUID, %p);\n", handle, @@ -48,6 +48,7 @@ EfiStatus handle_protocol(EfiHandle handle, const EfiGuid *protocol, loaded_image->LoadOptionsSize = 0; loaded_image->LoadOptions = nullptr; loaded_image->Unload = unload; + loaded_image->ImageBase = handle; *intf = loaded_image; return SUCCESS; diff --git a/lib/uefi/pe.h b/lib/uefi/pe.h index 294a087aa..882d499c7 100644 --- a/lib/uefi/pe.h +++ b/lib/uefi/pe.h @@ -21,6 +21,21 @@ #include #include +// +// Directory Entries +// +static constexpr size_t IMAGE_DIRECTORY_ENTRY_EXPORT = 0; +static constexpr size_t IMAGE_DIRECTORY_ENTRY_IMPORT = 1; +static constexpr size_t IMAGE_DIRECTORY_ENTRY_RESOURCE = 2; +static constexpr size_t IMAGE_DIRECTORY_ENTRY_EXCEPTION = 3; +static constexpr size_t IMAGE_DIRECTORY_ENTRY_SECURITY = 4; +static constexpr size_t IMAGE_DIRECTORY_ENTRY_BASERELOC = 5; +static constexpr size_t IMAGE_DIRECTORY_ENTRY_DEBUG = 6; +static constexpr size_t IMAGE_DIRECTORY_ENTRY_COPYRIGHT = 7; +static constexpr size_t IMAGE_DIRECTORY_ENTRY_GLOBALPTR = 8; +static constexpr size_t IMAGE_DIRECTORY_ENTRY_TLS = 9; +static constexpr size_t IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG = 10; + static constexpr size_t IMAGE_NUMBEROF_DIRECTORY_ENTRIES = 16; static constexpr size_t IMAGE_SIZEOF_SHORT_NAME = 8; @@ -256,4 +271,21 @@ struct IMAGE_NT_HEADERS64 { IMAGE_OPTIONAL_HEADER64 OptionalHeader; } __attribute__((packed)); +struct EFI_IMAGE_BASE_RELOCATION { + uint32_t VirtualAddress; + uint32_t SizeOfBlock; +}; + +static constexpr size_t EFI_IMAGE_REL_BASED_ABSOLUTE = 0; +static constexpr size_t EFI_IMAGE_REL_BASED_HIGH = 1; +static constexpr size_t EFI_IMAGE_REL_BASED_LOW = 2; +static constexpr size_t EFI_IMAGE_REL_BASED_HIGHLOW = 3; +static constexpr size_t EFI_IMAGE_REL_BASED_HIGHADJ = 4; +static constexpr size_t EFI_IMAGE_REL_BASED_MIPS_JMPADDR = 5; +static constexpr size_t EFI_IMAGE_REL_BASED_ARM_MOV32A = 5; +static constexpr size_t EFI_IMAGE_REL_BASED_ARM_MOV32T = 7; +static constexpr size_t EFI_IMAGE_REL_BASED_IA64_IMM64 = 9; +static constexpr size_t EFI_IMAGE_REL_BASED_MIPS_JMPADDR16 = 9; +static constexpr size_t EFI_IMAGE_REL_BASED_DIR64 = 10; + #endif diff --git a/lib/uefi/uefi.cpp b/lib/uefi/uefi.cpp index c6a60ea1d..0c18dd2f6 100644 --- a/lib/uefi/uefi.cpp +++ b/lib/uefi/uefi.cpp @@ -30,8 +30,8 @@ using EfiEntry = int (*)(void *, struct EfiSystemTable *); void *alloc_page(size_t size) { void *vptr{}; - status_t err = vmm_alloc_contiguous(vmm_get_kernel_aspace(), "uefi_program", - size, &vptr, 0, 0, 0); + status_t err = + vmm_alloc(vmm_get_kernel_aspace(), "uefi_program", size, &vptr, 0, 0, 0); if (err) { printf("Failed to allocate memory for uefi program %d\n", err); return nullptr; @@ -39,6 +39,21 @@ void *alloc_page(size_t size) { return vptr; } +void *alloc_page(void *addr, size_t size) { + if (addr == nullptr) { + return alloc_page(size); + } + auto err = vmm_alloc(vmm_get_kernel_aspace(), "uefi_program", size, &addr, + PAGE_SIZE_SHIFT, VMM_FLAG_VALLOC_SPECIFIC, 0); + if (err) { + printf("Failed to allocate memory for uefi program @ fixed address %p %d , " + "falling back to non-fixed allocation\n", + addr, err); + return alloc_page(size); + } + return addr; +} + template void fill(T *data, size_t skip, uint8_t begin = 0) { auto ptr = reinterpret_cast(data); for (size_t i = 0; i < sizeof(T); i++) { @@ -49,6 +64,185 @@ template void fill(T *data, size_t skip, uint8_t begin = 0) { } } +static constexpr size_t BIT26 = 1 << 26; +static constexpr size_t BIT11 = 1 << 11; +static constexpr size_t BIT10 = 1 << 10; + +/** + Pass in a pointer to an ARM MOVT or MOVW immediate instruciton and + return the immediate data encoded in the instruction. + + @param Instruction Pointer to ARM MOVT or MOVW immediate instruction + + @return Immediate address encoded in the instruction + +**/ +uint16_t ThumbMovtImmediateAddress(uint16_t *Instruction) { + uint32_t Movt; + uint16_t Address; + + // Thumb2 is two 16-bit instructions working together. Not a single 32-bit + // instruction Example MOVT R0, #0 is 0x0000f2c0 or 0xf2c0 0x0000 + Movt = (*Instruction << 16) | (*(Instruction + 1)); + + // imm16 = imm4:i:imm3:imm8 + // imm4 -> Bit19:Bit16 + // i -> Bit26 + // imm3 -> Bit14:Bit12 + // imm8 -> Bit7:Bit0 + Address = (uint16_t)(Movt & 0x000000ff); // imm8 + Address |= (uint16_t)((Movt >> 4) & 0x0000f700); // imm4 imm3 + Address |= (((Movt & BIT26) != 0) ? BIT11 : 0); // i + return Address; +} + +/** + Pass in a pointer to an ARM MOVW/MOVT instruciton pair and + return the immediate data encoded in the two` instruction. + + @param Instructions Pointer to ARM MOVW/MOVT insturction pair + + @return Immediate address encoded in the instructions + +**/ +uint32_t ThumbMovwMovtImmediateAddress(uint16_t *Instructions) { + uint16_t *Word; + uint16_t *Top; + + Word = Instructions; // MOVW + Top = Word + 2; // MOVT + + return (ThumbMovtImmediateAddress(Top) << 16) + + ThumbMovtImmediateAddress(Word); +} + +/** + Update an ARM MOVT or MOVW immediate instruction immediate data. + + @param Instruction Pointer to ARM MOVT or MOVW immediate instruction + @param Address New addres to patch into the instruction +**/ +void ThumbMovtImmediatePatch(uint16_t *Instruction, uint16_t Address) { + uint16_t Patch; + + // First 16-bit chunk of instruciton + Patch = ((Address >> 12) & 0x000f); // imm4 + Patch |= (((Address & BIT11) != 0) ? BIT10 : 0); // i + // Mask out instruction bits and or in address + *(Instruction) = (*Instruction & ~0x040f) | Patch; + + // Second 16-bit chunk of instruction + Patch = Address & 0x000000ff; // imm8 + Patch |= ((Address << 4) & 0x00007000); // imm3 + // Mask out instruction bits and or in address + Instruction++; + *Instruction = (*Instruction & ~0x70ff) | Patch; +} + +/** + Update an ARM MOVW/MOVT immediate instruction instruction pair. + + @param Instructions Pointer to ARM MOVW/MOVT instruction pair + @param Address New addres to patch into the instructions +**/ +void ThumbMovwMovtImmediatePatch(uint16_t *Instructions, uint32_t Address) { + uint16_t *Word; + uint16_t *Top; + + Word = Instructions; // MOVW + Top = Word + 2; // MOVT + + ThumbMovtImmediatePatch(Word, (uint16_t)(Address & 0xffff)); + ThumbMovtImmediatePatch(Top, (uint16_t)(Address >> 16)); +} + +int relocate_image(char *image) { + const auto dos_header = reinterpret_cast(image); + const auto pe_header = dos_header->GetPEHeader(); + const auto optional_header = &pe_header->OptionalHeader; + const auto reloc_directory = + optional_header->DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC]; + if (reloc_directory.Size == 0) { + printf("Relocation section empty\n"); + return 0; + } + auto RelocBase = reinterpret_cast( + image + reloc_directory.VirtualAddress); + const auto RelocBaseEnd = reinterpret_cast( + (char *)RelocBase + reloc_directory.Size); + const auto Adjust = + reinterpret_cast(image - optional_header->ImageBase); + // + // Run this relocation record + // + while (RelocBase < RelocBaseEnd) { + auto Reloc = + (uint16_t *)((char *)RelocBase + sizeof(EFI_IMAGE_BASE_RELOCATION)); + auto RelocEnd = reinterpret_cast((char *)RelocBase + + RelocBase->SizeOfBlock); + if (RelocBase->SizeOfBlock == 0) { + printf("Found relocation block of size 0, this is wrong\n"); + return -1; + } + while (Reloc < RelocEnd) { + auto Fixup = image + RelocBase->VirtualAddress + (*Reloc & 0xFFF); + if (Fixup == nullptr) { + return 0; + } + + auto Fixup16 = reinterpret_cast(Fixup); + auto Fixup32 = reinterpret_cast(Fixup); + auto Fixup64 = reinterpret_cast(Fixup); + uint32_t FixupVal = 0; + switch ((*Reloc) >> 12) { + case EFI_IMAGE_REL_BASED_ABSOLUTE: + break; + + case EFI_IMAGE_REL_BASED_HIGH: + *Fixup16 = (uint16_t)(*Fixup16 + ((uint16_t)((uint32_t)Adjust >> 16))); + + break; + + case EFI_IMAGE_REL_BASED_LOW: + *Fixup16 = (uint16_t)(*Fixup16 + ((uint16_t)Adjust & 0xffff)); + + break; + + case EFI_IMAGE_REL_BASED_HIGHLOW: + *Fixup32 = *Fixup32 + (uint32_t)Adjust; + break; + + case EFI_IMAGE_REL_BASED_DIR64: + *Fixup64 = *Fixup64 + (uint64_t)Adjust; + break; + + case EFI_IMAGE_REL_BASED_ARM_MOV32T: + FixupVal = ThumbMovwMovtImmediateAddress(Fixup16) + (uint32_t)Adjust; + ThumbMovwMovtImmediatePatch(Fixup16, FixupVal); + + break; + + case EFI_IMAGE_REL_BASED_ARM_MOV32A: + printf("Unsupported relocation type: EFI_IMAGE_REL_BASED_ARM_MOV32A\n"); + // break omitted - ARM instruction encoding not implemented + break; + + default: + printf("Unsupported relocation type: %d\n", (*Reloc) >> 12); + return -1; + } + + // + // Next relocation record + // + Reloc += 1; + } + RelocBase = reinterpret_cast(RelocEnd); + } + optional_header->ImageBase = reinterpret_cast(image); + return 0; +} + int load_sections_and_execute(bdev_t *dev, const IMAGE_NT_HEADERS64 *pe_header) { const auto file_header = &pe_header->FileHeader; @@ -57,6 +251,10 @@ int load_sections_and_execute(bdev_t *dev, const auto section_header = reinterpret_cast( reinterpret_cast(pe_header) + sizeof(IMAGE_FILE_HEADER) + file_header->SizeOfOptionalHeader); + if (sections <= 0) { + printf("This PE file does not have any sections, unsupported.\n"); + return -8; + } for (size_t i = 0; i < sections; i++) { if (section_header[i].NumberOfRelocations != 0) { printf("Section %s requires relocation, which is not supported.\n", @@ -67,14 +265,22 @@ int load_sections_and_execute(bdev_t *dev, const auto &last_section = section_header[sections - 1]; const auto virtual_size = last_section.VirtualAddress + last_section.Misc.VirtualSize; - const auto image_base = reinterpret_cast(alloc_page(virtual_size)); + const auto image_base = reinterpret_cast(alloc_page( + reinterpret_cast(optional_header->ImageBase), virtual_size)); + if (image_base == nullptr) { + return -7; + } memset(image_base, 0, virtual_size); + bio_read(dev, image_base, 0, section_header[0].PointerToRawData); for (size_t i = 0; i < sections; i++) { const auto §ion = section_header[i]; bio_read(dev, image_base + section.VirtualAddress, section.PointerToRawData, section.SizeOfRawData); } + printf("Relocating image from 0x%llx to %p\n", optional_header->ImageBase, + image_base); + relocate_image(image_base); auto entry = reinterpret_cast(image_base + optional_header->AddressOfEntryPoint); printf("Entry function located at %p\n", entry); @@ -116,8 +322,9 @@ int load_pe_file(const char *blkdev) { return -2; } if (dos_header->e_lfanew > kBlocKSize - sizeof(IMAGE_FILE_HEADER)) { - printf("Invalid PE header offset %d exceeds maximum read size of %zu - %zu\n", - dos_header->e_lfanew, kBlocKSize, sizeof(IMAGE_FILE_HEADER)); + printf( + "Invalid PE header offset %d exceeds maximum read size of %zu - %zu\n", + dos_header->e_lfanew, kBlocKSize, sizeof(IMAGE_FILE_HEADER)); return -3; } const auto pe_header = dos_header->GetPEHeader(); From 347db099593cae9ede256d95f3b857b03bb2ec19 Mon Sep 17 00:00:00 2001 From: Kelvin Zhang Date: Tue, 20 Aug 2024 12:31:06 -0700 Subject: [PATCH 06/11] [app][uefi] Place linux kernel at identity mapped virtual memory addresses --- kernel/include/kernel/vm.h | 2 +- kernel/vm/vmm.c | 12 +++-- lib/uefi/boot_service_provider.cpp | 82 +++++++++++++++++++++++++++++- lib/uefi/boot_service_provider.h | 6 +++ lib/uefi/include/types.h | 2 +- lib/uefi/uefi.cpp | 32 ++---------- 6 files changed, 101 insertions(+), 35 deletions(-) diff --git a/kernel/include/kernel/vm.h b/kernel/include/kernel/vm.h index b06c28d5f..c7300527e 100644 --- a/kernel/include/kernel/vm.h +++ b/kernel/include/kernel/vm.h @@ -259,7 +259,7 @@ void vmm_context_switch(vmm_aspace_t *oldspace, vmm_aspace_t *newaspace); /* set the current user aspace as active on the current thread. NULL is a valid argument, which unmaps the current user address space */ -void vmm_set_active_aspace(vmm_aspace_t *aspace); +vmm_aspace_t* vmm_set_active_aspace(vmm_aspace_t *aspace); __END_CDECLS diff --git a/kernel/vm/vmm.c b/kernel/vm/vmm.c index 99fd3cdb2..876926e29 100644 --- a/kernel/vm/vmm.c +++ b/kernel/vm/vmm.c @@ -12,6 +12,7 @@ #include #include #include +#include "kernel/thread.h" #include "vm_priv.h" #define LOCAL_TRACE 0 @@ -751,21 +752,24 @@ void vmm_context_switch(vmm_aspace_t *oldspace, vmm_aspace_t *newaspace) { arch_mmu_context_switch(newaspace ? &newaspace->arch_aspace : NULL); } -void vmm_set_active_aspace(vmm_aspace_t *aspace) { +vmm_aspace_t* vmm_set_active_aspace(vmm_aspace_t *aspace) { LTRACEF("aspace %p\n", aspace); thread_t *t = get_current_thread(); DEBUG_ASSERT(t); if (aspace == t->aspace) - return; + return aspace; /* grab the thread lock and switch to the new address space */ THREAD_LOCK(state); vmm_aspace_t *old = t->aspace; - t->aspace = aspace; - vmm_context_switch(old, t->aspace); + if (old != aspace) { + t->aspace = aspace; + vmm_context_switch(old, t->aspace); + } THREAD_UNLOCK(state); + return old; } static void dump_region(const vmm_region_t *r) { diff --git a/lib/uefi/boot_service_provider.cpp b/lib/uefi/boot_service_provider.cpp index b2f012be1..551813cf7 100644 --- a/lib/uefi/boot_service_provider.cpp +++ b/lib/uefi/boot_service_provider.cpp @@ -22,6 +22,79 @@ #include #include +static vmm_aspace_t *old_aspace = nullptr; + +vmm_aspace_t *set_boot_aspace() { + static vmm_aspace_t *aspace = nullptr; + if (aspace == nullptr) { + auto err = vmm_create_aspace(&aspace, "linux_kernel", 0); + if (err) { + printf("Failed to create address space for linux kernel %d\n", err); + return nullptr; + } + old_aspace = vmm_set_active_aspace(aspace); + } + return aspace; +} + +void restore_aspace() { vmm_set_active_aspace(old_aspace); } + +void *identity_map(void *addr, size_t size) { + size = ROUNDUP(size, PAGE_SIZE); + auto vaddr = reinterpret_cast(addr); + paddr_t pa{}; + uint flags{}; + auto aspace = set_boot_aspace(); + auto err = arch_mmu_query(&aspace->arch_aspace, vaddr, &pa, &flags); + if (err) { + printf("Failed to query physical address for memory 0x%p\n", addr); + return nullptr; + } + + err = arch_mmu_unmap(&aspace->arch_aspace, vaddr, size / PAGE_SIZE); + if (err) { + printf("Failed to unmap virtual address 0x%lx\n", vaddr); + return nullptr; + } + arch_mmu_map(&aspace->arch_aspace, pa, pa, size / PAGE_SIZE, flags); + if (err) { + printf("Failed to identity map physical address 0x%lx\n", pa); + return nullptr; + } + printf("Identity mapped physical address 0x%lx flags 0x%x\n", pa, flags); + + return reinterpret_cast(pa); +} + +void *alloc_page(size_t size, size_t align_log2) { + auto aspace = set_boot_aspace(); + void *vptr{}; + status_t err = vmm_alloc_contiguous(aspace, "uefi_program", size, &vptr, + align_log2, 0, 0); + if (err) { + printf("Failed to allocate memory for uefi program %d\n", err); + return nullptr; + } + return identity_map(vptr, size); +} + +void *alloc_page(void *addr, size_t size, size_t align_log2) { + if (addr == nullptr) { + return alloc_page(size, align_log2); + } + auto err = + vmm_alloc_contiguous(set_boot_aspace(), "uefi_program", size, &addr, + align_log2, VMM_FLAG_VALLOC_SPECIFIC, 0); + if (err) { + printf( + "Failed to allocate memory for uefi program @ fixed address 0x%p %d , " + "falling back to non-fixed allocation\n", + addr, err); + return alloc_page(size, align_log2); + } + return identity_map(addr, size); +} + namespace { EfiStatus unload(EfiHandle handle) { return SUCCESS; } @@ -34,7 +107,7 @@ bool guid_eq(const EfiGuid *a, const EfiGuid &b) { return memcmp(a, &b, sizeof(*a)) == 0; } -EfiStatus handle_protocol(const EfiHandle handle, const EfiGuid *protocol, +EfiStatus handle_protocol(EfiHandle handle, const EfiGuid *protocol, void **intf) { if (guid_eq(protocol, LOADED_IMAGE_PROTOCOL_GUID)) { printf("handle_protocol(%p, LOADED_IMAGE_PROTOCOL_GUID, %p);\n", handle, @@ -115,6 +188,13 @@ EfiStatus get_memory_map(size_t *memory_map_size, memory_map[i].virtual_start = region->base; memory_map[i].physical_start = memory_map[i].virtual_start; memory_map[i].number_of_pages = region->size / PAGE_SIZE; + paddr_t pa{}; + uint flags{}; + status_t err = + arch_mmu_query(&aspace->arch_aspace, region->base, &pa, &flags); + if (err >= 0) { + memory_map[i].physical_start = pa; + } i++; } diff --git a/lib/uefi/boot_service_provider.h b/lib/uefi/boot_service_provider.h index afb3a7bcf..22ded7b6d 100644 --- a/lib/uefi/boot_service_provider.h +++ b/lib/uefi/boot_service_provider.h @@ -17,7 +17,9 @@ #ifndef __BOOT_SERVICE_PROVIDER_ #define __BOOT_SERVICE_PROVIDER_ +#include "arch/defines.h" #include "boot_service.h" +#include "kernel/vm.h" #include "system_table.h" void setup_boot_service_table(EfiBootService *service); @@ -90,5 +92,9 @@ struct EFI_LOADED_IMAGE_PROTOCOL { }; static constexpr size_t EFI_LOADED_IMAGE_PROTOCOL_REVISION = 0x1000; +vmm_aspace_t *set_boot_aspace(); + +void *alloc_page(void *addr, size_t size, size_t align_log2 = PAGE_SIZE_SHIFT); +void *alloc_page(size_t size, size_t align_log2 = PAGE_SIZE_SHIFT); #endif diff --git a/lib/uefi/include/types.h b/lib/uefi/include/types.h index eea059cd8..106b8fe48 100644 --- a/lib/uefi/include/types.h +++ b/lib/uefi/include/types.h @@ -64,7 +64,7 @@ using EfiTimerDelay = enum EFI_TIMER_DELAY { TIMER_RELATIVE }; -using EFI_MEMORY_TYPE = enum { +enum EFI_MEMORY_TYPE { RESERVED_MEMORY_TYPE, LOADER_CODE, LOADER_DATA, diff --git a/lib/uefi/uefi.cpp b/lib/uefi/uefi.cpp index 0c18dd2f6..642109e8e 100644 --- a/lib/uefi/uefi.cpp +++ b/lib/uefi/uefi.cpp @@ -1,3 +1,4 @@ +#include "arch/mmu.h" #include "boot_service.h" #include "boot_service_provider.h" #include "defer.h" @@ -28,32 +29,6 @@ constexpr auto EFI_SYSTEM_TABLE_SIGNATURE = using EfiEntry = int (*)(void *, struct EfiSystemTable *); -void *alloc_page(size_t size) { - void *vptr{}; - status_t err = - vmm_alloc(vmm_get_kernel_aspace(), "uefi_program", size, &vptr, 0, 0, 0); - if (err) { - printf("Failed to allocate memory for uefi program %d\n", err); - return nullptr; - } - return vptr; -} - -void *alloc_page(void *addr, size_t size) { - if (addr == nullptr) { - return alloc_page(size); - } - auto err = vmm_alloc(vmm_get_kernel_aspace(), "uefi_program", size, &addr, - PAGE_SIZE_SHIFT, VMM_FLAG_VALLOC_SPECIFIC, 0); - if (err) { - printf("Failed to allocate memory for uefi program @ fixed address %p %d , " - "falling back to non-fixed allocation\n", - addr, err); - return alloc_page(size); - } - return addr; -} - template void fill(T *data, size_t skip, uint8_t begin = 0) { auto ptr = reinterpret_cast(data); for (size_t i = 0; i < sizeof(T); i++) { @@ -265,8 +240,9 @@ int load_sections_and_execute(bdev_t *dev, const auto &last_section = section_header[sections - 1]; const auto virtual_size = last_section.VirtualAddress + last_section.Misc.VirtualSize; - const auto image_base = reinterpret_cast(alloc_page( - reinterpret_cast(optional_header->ImageBase), virtual_size)); + const auto image_base = reinterpret_cast( + alloc_page(reinterpret_cast(optional_header->ImageBase), + virtual_size, 21 /* Kernel requires 2MB alignment */)); if (image_base == nullptr) { return -7; } From 763ff7ce702599a583da45b8435fbb152728d5e1 Mon Sep 17 00:00:00 2001 From: Kelvin Zhang Date: Tue, 10 Sep 2024 15:54:26 -0700 Subject: [PATCH 07/11] [app][uefi] Call kernel at an identity mapped stack --- lib/uefi/rules.mk | 1 + lib/uefi/switch_stack.S | 38 ++++++++++++++++++++++++++++++++++++++ lib/uefi/switch_stack.h | 27 +++++++++++++++++++++++++++ lib/uefi/uefi.cpp | 10 +++++++--- 4 files changed, 73 insertions(+), 3 deletions(-) create mode 100644 lib/uefi/switch_stack.S create mode 100644 lib/uefi/switch_stack.h diff --git a/lib/uefi/rules.mk b/lib/uefi/rules.mk index 37718c1ce..c5a1e59ca 100644 --- a/lib/uefi/rules.mk +++ b/lib/uefi/rules.mk @@ -9,5 +9,6 @@ MODULE_SRCS += \ $(LOCAL_DIR)/text_protocol.cpp \ $(LOCAL_DIR)/boot_service_provider.cpp \ $(LOCAL_DIR)/runtime_service_provider.cpp \ + $(LOCAL_DIR)/switch_stack.S \ include make/module.mk diff --git a/lib/uefi/switch_stack.S b/lib/uefi/switch_stack.S new file mode 100644 index 000000000..ea188c554 --- /dev/null +++ b/lib/uefi/switch_stack.S @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + + +// int call_with_stack(void *stack, int (*fp)(), int param1, int param2); +FUNCTION(call_with_stack) +stp fp, lr, [sp, #-16]! +mov fp, sp + +sub x0,x0,16 +mov x6,sp +str x6,[x0] +mov sp,x0 +mov x5,x1 +mov x0,x2 +mov x1,x3 +blr x5 +ldr x6,[sp] +mov sp,x6 + +ldp fp, lr, [sp], 16 +ret lr diff --git a/lib/uefi/switch_stack.h b/lib/uefi/switch_stack.h new file mode 100644 index 000000000..572559f34 --- /dev/null +++ b/lib/uefi/switch_stack.h @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifdef __cplusplus +extern "C" { + +#endif + +int call_with_stack(void *stack, int (*fp)(void *, void *), void *param1, + void *param2); +#ifdef __cplusplus +} +#endif \ No newline at end of file diff --git a/lib/uefi/uefi.cpp b/lib/uefi/uefi.cpp index 642109e8e..a86a50b4c 100644 --- a/lib/uefi/uefi.cpp +++ b/lib/uefi/uefi.cpp @@ -19,6 +19,7 @@ #include "protocols/simple_text_output_protocol.h" #include "runtime_service.h" #include "runtime_service_provider.h" +#include "switch_stack.h" #include "system_table.h" #include "text_protocol.h" @@ -257,8 +258,8 @@ int load_sections_and_execute(bdev_t *dev, printf("Relocating image from 0x%llx to %p\n", optional_header->ImageBase, image_base); relocate_image(image_base); - auto entry = reinterpret_cast(image_base + - optional_header->AddressOfEntryPoint); + auto entry = reinterpret_cast( + image_base + optional_header->AddressOfEntryPoint); printf("Entry function located at %p\n", entry); EfiSystemTable table{}; @@ -273,7 +274,10 @@ int load_sections_and_execute(bdev_t *dev, table.header.signature = EFI_SYSTEM_TABLE_SIGNATURE; EfiSimpleTextOutputProtocol console_out = get_text_output_protocol(); table.con_out = &console_out; - return entry(image_base, &table); + constexpr size_t kStackSize = 1024ul * 1024; + auto stack = reinterpret_cast(alloc_page(kStackSize, PAGE_SIZE)); + memset(stack, 0, kStackSize); + return call_with_stack(stack + kStackSize, entry, image_base, &table); } int load_pe_file(const char *blkdev) { From 7d77e28430fd9ebb83dcb2ca795c49a10f83b84b Mon Sep 17 00:00:00 2001 From: Kelvin Zhang Date: Tue, 8 Oct 2024 10:38:02 -0700 Subject: [PATCH 08/11] [app][uefi] Enable mspace feature on dlmalloc This allows us to use an identitiy mapped piece of memory as the heap for UEFI app. UEFI app's allocation would go from this heap. --- external/lib/heap/dlmalloc/rules.mk | 9 +++++++++ lib/uefi/rules.mk | 2 ++ 2 files changed, 11 insertions(+) diff --git a/external/lib/heap/dlmalloc/rules.mk b/external/lib/heap/dlmalloc/rules.mk index 3a5ae667b..1870baaf6 100644 --- a/external/lib/heap/dlmalloc/rules.mk +++ b/external/lib/heap/dlmalloc/rules.mk @@ -2,6 +2,15 @@ LOCAL_DIR := $(GET_LOCAL_DIR) MODULE := $(LOCAL_DIR) +# MSPACE=1 enables mspace_malloc and other mspace_* routines. +# They allow users to use preallocated memory for heap allocations +# It's common for VM applications to preallocate backing memory for +# the guest, then free the entire backing memory at once after guest +# exits. This ensures no memory leak even if guest doesn't free its +# memory properly. Or hypervisor may wish that the guest memory +# are all contigous, etc. +MODULE_DEFINES=MSPACES=1 + MODULE_SRCS += \ $(LOCAL_DIR)/dlmalloc.c diff --git a/lib/uefi/rules.mk b/lib/uefi/rules.mk index c5a1e59ca..6dabf4ab7 100644 --- a/lib/uefi/rules.mk +++ b/lib/uefi/rules.mk @@ -4,6 +4,8 @@ MODULE := $(LOCAL_DIR) MODULE_INCLUDES += $(LOCAL_DIR)/include +MODULE_DEFINES=MSPACES=1 + MODULE_SRCS += \ $(LOCAL_DIR)/uefi.cpp \ $(LOCAL_DIR)/text_protocol.cpp \ From 1249e8a03b51a8236df8c9be16a00fe15346093f Mon Sep 17 00:00:00 2001 From: Kelvin Zhang Date: Thu, 26 Sep 2024 16:30:23 -0700 Subject: [PATCH 09/11] [app][uefi] Add configuration table --- lib/uefi/configuration_table.cpp | 31 ++++++++++++++++++++++++++ lib/uefi/configuration_table.h | 37 ++++++++++++++++++++++++++++++++ lib/uefi/defer.h | 17 +++++++++++++++ lib/uefi/include/system_table.h | 12 +++++------ lib/uefi/rules.mk | 1 + lib/uefi/uefi.cpp | 31 ++++++++++++++++++++++---- 6 files changed, 119 insertions(+), 10 deletions(-) create mode 100644 lib/uefi/configuration_table.cpp create mode 100644 lib/uefi/configuration_table.h diff --git a/lib/uefi/configuration_table.cpp b/lib/uefi/configuration_table.cpp new file mode 100644 index 000000000..ef9115766 --- /dev/null +++ b/lib/uefi/configuration_table.cpp @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include "configuration_table.h" +#include "boot_service_provider.h" +#include "system_table.h" +#include + +void setup_configuration_table(EfiSystemTable *table) { + auto &rng = table->configuration_table[0]; + rng.vendor_guid = LINUX_EFI_RANDOM_SEED_TABLE_GUID; + rng.vendor_table = alloc_page(PAGE_SIZE); + auto rng_seed = reinterpret_cast(rng.vendor_table); + rng_seed->size = 512; + memset(&rng_seed->bits, 0, rng_seed->size); + table->number_of_table_entries = 1; +} \ No newline at end of file diff --git a/lib/uefi/configuration_table.h b/lib/uefi/configuration_table.h new file mode 100644 index 000000000..348608fd3 --- /dev/null +++ b/lib/uefi/configuration_table.h @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef __CONFIGURATION_TABLE_ +#define __CONFIGURATION_TABLE_ + +#include "system_table.h" +#include "types.h" + +struct linux_efi_random_seed { + uint32_t size; + uint8_t bits[]; +}; + +static constexpr auto LINUX_EFI_RANDOM_SEED_TABLE_GUID = + EfiGuid{0x1ce1e5bc, + 0x7ceb, + 0x42f2, + {0x81, 0xe5, 0x8a, 0xad, 0xf1, 0x80, 0xf5, 0x7b}}; + +void setup_configuration_table(EfiSystemTable *table); + +#endif diff --git a/lib/uefi/defer.h b/lib/uefi/defer.h index 87400e1b8..60692a59f 100644 --- a/lib/uefi/defer.h +++ b/lib/uefi/defer.h @@ -1,3 +1,20 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + #ifndef __DEFER_HEADER_ #define __DEFER_HEADER_ diff --git a/lib/uefi/include/system_table.h b/lib/uefi/include/system_table.h index 9d8fbdd2c..a446969be 100644 --- a/lib/uefi/include/system_table.h +++ b/lib/uefi/include/system_table.h @@ -25,12 +25,12 @@ #include "protocols/simple_text_output_protocol.h" #include "runtime_service.h" -typedef struct { +struct EfiConfigurationTable { EfiGuid vendor_guid; - const void* vendor_table; -} EfiConfigurationTable; + void *vendor_table; +}; -typedef struct EfiSystemTable { +struct EfiSystemTable { EfiTableHeader header; char16_t* firmware_vendor; uint32_t firmware_revision; @@ -43,7 +43,7 @@ typedef struct EfiSystemTable { EfiRuntimeService *runtime_service; EfiBootService* boot_services; size_t number_of_table_entries; - const EfiConfigurationTable* configuration_table; -} EfiSystemTable; + EfiConfigurationTable *configuration_table; +}; #endif // __SYSTEM_TABLE_H__ diff --git a/lib/uefi/rules.mk b/lib/uefi/rules.mk index 6dabf4ab7..97dfbdc14 100644 --- a/lib/uefi/rules.mk +++ b/lib/uefi/rules.mk @@ -12,5 +12,6 @@ MODULE_SRCS += \ $(LOCAL_DIR)/boot_service_provider.cpp \ $(LOCAL_DIR)/runtime_service_provider.cpp \ $(LOCAL_DIR)/switch_stack.S \ + $(LOCAL_DIR)/configuration_table.cpp \ include make/module.mk diff --git a/lib/uefi/uefi.cpp b/lib/uefi/uefi.cpp index a86a50b4c..d943ba195 100644 --- a/lib/uefi/uefi.cpp +++ b/lib/uefi/uefi.cpp @@ -1,3 +1,20 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + #include "arch/mmu.h" #include "boot_service.h" #include "boot_service_provider.h" @@ -11,11 +28,13 @@ #include #include #include +#include #include #include #include #include +#include "configuration_table.h" #include "protocols/simple_text_output_protocol.h" #include "runtime_service.h" #include "runtime_service_provider.h" @@ -53,7 +72,7 @@ static constexpr size_t BIT10 = 1 << 10; @return Immediate address encoded in the instruction **/ -uint16_t ThumbMovtImmediateAddress(uint16_t *Instruction) { +uint16_t ThumbMovtImmediateAddress(const uint16_t *Instruction) { uint32_t Movt; uint16_t Address; @@ -262,7 +281,7 @@ int load_sections_and_execute(bdev_t *dev, image_base + optional_header->AddressOfEntryPoint); printf("Entry function located at %p\n", entry); - EfiSystemTable table{}; + EfiSystemTable &table = *static_cast(alloc_page(PAGE_SIZE)); EfiBootService boot_service{}; EfiRuntimeService runtime_service{}; fill(&runtime_service, 0); @@ -274,8 +293,12 @@ int load_sections_and_execute(bdev_t *dev, table.header.signature = EFI_SYSTEM_TABLE_SIGNATURE; EfiSimpleTextOutputProtocol console_out = get_text_output_protocol(); table.con_out = &console_out; - constexpr size_t kStackSize = 1024ul * 1024; - auto stack = reinterpret_cast(alloc_page(kStackSize, PAGE_SIZE)); + table.configuration_table = + reinterpret_cast(alloc_page(PAGE_SIZE)); + setup_configuration_table(&table); + + constexpr size_t kStackSize = 8 * 1024ul * 1024; + auto stack = reinterpret_cast(alloc_page(kStackSize, 23)); memset(stack, 0, kStackSize); return call_with_stack(stack + kStackSize, entry, image_base, &table); } From 0d8a1ec055df6dd2ff300bcf570bb2314ca5d0e1 Mon Sep 17 00:00:00 2001 From: Kelvin Zhang Date: Tue, 8 Oct 2024 10:40:02 -0700 Subject: [PATCH 10/11] [app][uefi] Make allocate_pool return identity mapped memory --- kernel/include/kernel/vm.h | 1 + kernel/vm/pmm.c | 2 + lib/uefi/boot_service_provider.cpp | 92 ++++++++++++++++++++++++++++-- lib/uefi/boot_service_provider.h | 1 + lib/uefi/include/boot_service.h | 6 ++ lib/uefi/uefi.cpp | 3 - 6 files changed, 97 insertions(+), 8 deletions(-) diff --git a/kernel/include/kernel/vm.h b/kernel/include/kernel/vm.h index c7300527e..fafb8a0ad 100644 --- a/kernel/include/kernel/vm.h +++ b/kernel/include/kernel/vm.h @@ -46,6 +46,7 @@ __BEGIN_CDECLS #define PAGE_ALIGN(x) ALIGN(x, PAGE_SIZE) #define IS_PAGE_ALIGNED(x) IS_ALIGNED(x, PAGE_SIZE) +struct list_node *get_arena_list(void); struct mmu_initial_mapping { paddr_t phys; diff --git a/kernel/vm/pmm.c b/kernel/vm/pmm.c index 7a5673695..67eba84a9 100644 --- a/kernel/vm/pmm.c +++ b/kernel/vm/pmm.c @@ -34,6 +34,8 @@ static mutex_t lock = MUTEX_INITIAL_VALUE(lock); #define ADDRESS_IN_ARENA(address, arena) \ ((address) >= (arena)->base && (address) <= (arena)->base + (arena)->size - 1) +struct list_node *get_arena_list() { return &arena_list; } + static inline bool page_is_free(const vm_page_t *page) { return !(page->flags & VM_PAGE_FLAG_NONFREE); } diff --git a/lib/uefi/boot_service_provider.cpp b/lib/uefi/boot_service_provider.cpp index 551813cf7..3ff9bd4d2 100644 --- a/lib/uefi/boot_service_provider.cpp +++ b/lib/uefi/boot_service_provider.cpp @@ -17,6 +17,7 @@ #include "boot_service_provider.h" #include "boot_service.h" #include "kernel/vm.h" +#include "lib/dlmalloc.h" #include "types.h" #include #include @@ -135,6 +136,23 @@ EfiStatus handle_protocol(EfiHandle handle, const EfiGuid *protocol, return UNSUPPORTED; } +constexpr size_t kHeapSize = 8ul * 1024 * 1024; +void *get_heap() { + static auto heap = alloc_page(kHeapSize); + return heap; +} + +mspace create_mspace_with_base_limit(void *base, size_t capacity, int locked) { + auto space = create_mspace_with_base(get_heap(), kHeapSize, 1); + mspace_set_footprint_limit(space, capacity); + return space; +} + +mspace get_mspace() { + static auto space = create_mspace_with_base_limit(get_heap(), kHeapSize, 1); + return space; +} + EfiStatus allocate_pool(EfiMemoryType pool_type, size_t size, void **buf) { if (buf == nullptr) { return INVALID_PARAMETER; @@ -143,7 +161,7 @@ EfiStatus allocate_pool(EfiMemoryType pool_type, size_t size, void **buf) { *buf = nullptr; return SUCCESS; } - *buf = malloc(size); + *buf = mspace_malloc(get_mspace(), size); if (*buf != nullptr) { return SUCCESS; } @@ -151,7 +169,72 @@ EfiStatus allocate_pool(EfiMemoryType pool_type, size_t size, void **buf) { } EfiStatus free_pool(void *mem) { - free(mem); + mspace_free(get_mspace(), mem); + return SUCCESS; +} + +size_t get_aspace_entry_count(vmm_aspace_t *aspace) { + vmm_region_t *region = nullptr; + size_t num_entries = 0; + list_for_every_entry(&aspace->region_list, region, vmm_region_t, node) { + num_entries++; + } + return num_entries; +} + +void fill_memory_map_entry(vmm_aspace_t *aspace, EfiMemoryDescriptor *entry, + const vmm_region_t *region) { + entry->virtual_start = region->base; + entry->physical_start = entry->virtual_start; + entry->number_of_pages = region->size / PAGE_SIZE; + paddr_t pa{}; + uint flags{}; + status_t err = + arch_mmu_query(&aspace->arch_aspace, region->base, &pa, &flags); + if (err >= 0) { + entry->physical_start = pa; + } + if ((flags & ARCH_MMU_FLAG_CACHE_MASK) == ARCH_MMU_FLAG_CACHED) { + entry->attributes |= EFI_MEMORY_WB | EFI_MEMORY_WC | EFI_MEMORY_WT; + } +} + +EfiStatus get_physical_memory_map(size_t *memory_map_size, + EfiMemoryDescriptor *memory_map, + size_t *map_key, size_t *desc_size, + uint32_t *desc_version) { + if (memory_map_size == nullptr) { + return INVALID_PARAMETER; + } + if (map_key) { + *map_key = 0; + } + if (desc_size) { + *desc_size = sizeof(EfiMemoryDescriptor); + } + if (desc_version) { + *desc_version = 1; + } + pmm_arena_t *a{}; + size_t num_entries = 0; + list_for_every_entry(get_arena_list(), a, pmm_arena_t, node) { + num_entries++; + } + const size_t size_needed = num_entries * sizeof(EfiMemoryDescriptor); + if (*memory_map_size < size_needed) { + *memory_map_size = size_needed; + return BUFFER_TOO_SMALL; + } + *memory_map_size = size_needed; + size_t i = 0; + memset(memory_map, 0, size_needed); + list_for_every_entry(get_arena_list(), a, pmm_arena_t, node) { + memory_map[i].physical_start = a->base; + memory_map[i].number_of_pages = a->size / PAGE_SIZE; + memory_map[i].attributes |= EFI_MEMORY_WB; + memory_map[i].memory_type = LOADER_CODE; + i++; + } return SUCCESS; } @@ -245,8 +328,7 @@ EfiStatus allocate_pages(EfiAllocatorType type, EfiMemoryType memory_type, memory_type, pages, *memory); return UNSUPPORTED; } - *memory = - reinterpret_cast(memalign(PAGE_SIZE, pages * PAGE_SIZE)); + *memory = reinterpret_cast(alloc_page(pages * PAGE_SIZE)); if (*memory == 0) { return OUT_OF_RESOURCES; } @@ -306,7 +388,7 @@ void setup_boot_service_table(EfiBootService *service) { service->handle_protocol = handle_protocol; service->allocate_pool = allocate_pool; service->free_pool = free_pool; - service->get_memory_map = get_memory_map; + service->get_memory_map = get_physical_memory_map; service->register_protocol_notify = register_protocol_notify; service->locate_handle = locate_handle; service->locate_protocol = locate_protocol; diff --git a/lib/uefi/boot_service_provider.h b/lib/uefi/boot_service_provider.h index 22ded7b6d..63354209a 100644 --- a/lib/uefi/boot_service_provider.h +++ b/lib/uefi/boot_service_provider.h @@ -96,5 +96,6 @@ vmm_aspace_t *set_boot_aspace(); void *alloc_page(void *addr, size_t size, size_t align_log2 = PAGE_SIZE_SHIFT); void *alloc_page(size_t size, size_t align_log2 = PAGE_SIZE_SHIFT); +void *identity_map(void *addr, size_t size); #endif diff --git a/lib/uefi/include/boot_service.h b/lib/uefi/include/boot_service.h index 5959f0092..ac510edb3 100644 --- a/lib/uefi/include/boot_service.h +++ b/lib/uefi/include/boot_service.h @@ -49,6 +49,12 @@ typedef enum EFI_OPEN_PROTOCOL_ATTRIBUTE : uint32_t { EXCLUSIVE = 0x00000020, } EfiOpenProtocolAttributes; +#define EFI_MEMORY_UC 0x0000000000000001ULL +#define EFI_MEMORY_WC 0x0000000000000002ULL +#define EFI_MEMORY_WT 0x0000000000000004ULL +#define EFI_MEMORY_WB 0x0000000000000008ULL +#define EFI_MEMORY_UCE 0x0000000000000010ULL + typedef struct { uint32_t memory_type; uint32_t padding; diff --git a/lib/uefi/uefi.cpp b/lib/uefi/uefi.cpp index d943ba195..ed5b3bdf7 100644 --- a/lib/uefi/uefi.cpp +++ b/lib/uefi/uefi.cpp @@ -15,11 +15,9 @@ * */ -#include "arch/mmu.h" #include "boot_service.h" #include "boot_service_provider.h" #include "defer.h" -#include "kernel/vm.h" #include "pe.h" #include @@ -28,7 +26,6 @@ #include #include #include -#include #include #include #include From 649d432b8a355932b946b24a07b6827f4985af98 Mon Sep 17 00:00:00 2001 From: Kelvin Zhang Date: Tue, 5 Nov 2024 18:42:53 -0800 Subject: [PATCH 11/11] Move UEFI header files into private include namespace This help reduce the risk of name collision. As MODULE_DIR/include is automatically added to global includes. --- lib/uefi/{ => local}/include/boot_service.h | 0 lib/uefi/{ => local}/include/efi.h | 0 lib/uefi/{ => local}/include/protocols/android_boot_protocol.h | 0 lib/uefi/{ => local}/include/protocols/block_io_protocol.h | 0 lib/uefi/{ => local}/include/protocols/device_path_protocol.h | 0 lib/uefi/{ => local}/include/protocols/loaded_image_protocol.h | 0 .../{ => local}/include/protocols/riscv_efi_boot_protocol.h | 0 .../{ => local}/include/protocols/simple_network_protocol.h | 0 .../{ => local}/include/protocols/simple_text_input_protocol.h | 0 .../{ => local}/include/protocols/simple_text_output_protocol.h | 0 lib/uefi/{ => local}/include/runtime_service.h | 0 lib/uefi/{ => local}/include/system_table.h | 0 lib/uefi/{ => local}/include/types.h | 0 lib/uefi/rules.mk | 2 +- 14 files changed, 1 insertion(+), 1 deletion(-) rename lib/uefi/{ => local}/include/boot_service.h (100%) rename lib/uefi/{ => local}/include/efi.h (100%) rename lib/uefi/{ => local}/include/protocols/android_boot_protocol.h (100%) rename lib/uefi/{ => local}/include/protocols/block_io_protocol.h (100%) rename lib/uefi/{ => local}/include/protocols/device_path_protocol.h (100%) rename lib/uefi/{ => local}/include/protocols/loaded_image_protocol.h (100%) rename lib/uefi/{ => local}/include/protocols/riscv_efi_boot_protocol.h (100%) rename lib/uefi/{ => local}/include/protocols/simple_network_protocol.h (100%) rename lib/uefi/{ => local}/include/protocols/simple_text_input_protocol.h (100%) rename lib/uefi/{ => local}/include/protocols/simple_text_output_protocol.h (100%) rename lib/uefi/{ => local}/include/runtime_service.h (100%) rename lib/uefi/{ => local}/include/system_table.h (100%) rename lib/uefi/{ => local}/include/types.h (100%) diff --git a/lib/uefi/include/boot_service.h b/lib/uefi/local/include/boot_service.h similarity index 100% rename from lib/uefi/include/boot_service.h rename to lib/uefi/local/include/boot_service.h diff --git a/lib/uefi/include/efi.h b/lib/uefi/local/include/efi.h similarity index 100% rename from lib/uefi/include/efi.h rename to lib/uefi/local/include/efi.h diff --git a/lib/uefi/include/protocols/android_boot_protocol.h b/lib/uefi/local/include/protocols/android_boot_protocol.h similarity index 100% rename from lib/uefi/include/protocols/android_boot_protocol.h rename to lib/uefi/local/include/protocols/android_boot_protocol.h diff --git a/lib/uefi/include/protocols/block_io_protocol.h b/lib/uefi/local/include/protocols/block_io_protocol.h similarity index 100% rename from lib/uefi/include/protocols/block_io_protocol.h rename to lib/uefi/local/include/protocols/block_io_protocol.h diff --git a/lib/uefi/include/protocols/device_path_protocol.h b/lib/uefi/local/include/protocols/device_path_protocol.h similarity index 100% rename from lib/uefi/include/protocols/device_path_protocol.h rename to lib/uefi/local/include/protocols/device_path_protocol.h diff --git a/lib/uefi/include/protocols/loaded_image_protocol.h b/lib/uefi/local/include/protocols/loaded_image_protocol.h similarity index 100% rename from lib/uefi/include/protocols/loaded_image_protocol.h rename to lib/uefi/local/include/protocols/loaded_image_protocol.h diff --git a/lib/uefi/include/protocols/riscv_efi_boot_protocol.h b/lib/uefi/local/include/protocols/riscv_efi_boot_protocol.h similarity index 100% rename from lib/uefi/include/protocols/riscv_efi_boot_protocol.h rename to lib/uefi/local/include/protocols/riscv_efi_boot_protocol.h diff --git a/lib/uefi/include/protocols/simple_network_protocol.h b/lib/uefi/local/include/protocols/simple_network_protocol.h similarity index 100% rename from lib/uefi/include/protocols/simple_network_protocol.h rename to lib/uefi/local/include/protocols/simple_network_protocol.h diff --git a/lib/uefi/include/protocols/simple_text_input_protocol.h b/lib/uefi/local/include/protocols/simple_text_input_protocol.h similarity index 100% rename from lib/uefi/include/protocols/simple_text_input_protocol.h rename to lib/uefi/local/include/protocols/simple_text_input_protocol.h diff --git a/lib/uefi/include/protocols/simple_text_output_protocol.h b/lib/uefi/local/include/protocols/simple_text_output_protocol.h similarity index 100% rename from lib/uefi/include/protocols/simple_text_output_protocol.h rename to lib/uefi/local/include/protocols/simple_text_output_protocol.h diff --git a/lib/uefi/include/runtime_service.h b/lib/uefi/local/include/runtime_service.h similarity index 100% rename from lib/uefi/include/runtime_service.h rename to lib/uefi/local/include/runtime_service.h diff --git a/lib/uefi/include/system_table.h b/lib/uefi/local/include/system_table.h similarity index 100% rename from lib/uefi/include/system_table.h rename to lib/uefi/local/include/system_table.h diff --git a/lib/uefi/include/types.h b/lib/uefi/local/include/types.h similarity index 100% rename from lib/uefi/include/types.h rename to lib/uefi/local/include/types.h diff --git a/lib/uefi/rules.mk b/lib/uefi/rules.mk index 97dfbdc14..f20e082bd 100644 --- a/lib/uefi/rules.mk +++ b/lib/uefi/rules.mk @@ -2,7 +2,7 @@ LOCAL_DIR := $(GET_LOCAL_DIR) MODULE := $(LOCAL_DIR) -MODULE_INCLUDES += $(LOCAL_DIR)/include +MODULE_INCLUDES += $(LOCAL_DIR)/local/include MODULE_DEFINES=MSPACES=1