From 62409f55f62cabcc5463751b86a10cc0a0a77687 Mon Sep 17 00:00:00 2001 From: Kelvin Zhang Date: Wed, 11 Jun 2025 10:34:33 -0700 Subject: [PATCH 1/7] [lib][uefi] Add error checking to certain functions --- lib/uefi/blockio_protocols.cpp | 16 ++++++++++++++-- lib/uefi/uefi.cpp | 12 ++++++++---- 2 files changed, 22 insertions(+), 6 deletions(-) diff --git a/lib/uefi/blockio_protocols.cpp b/lib/uefi/blockio_protocols.cpp index 7b534e80..b10435a1 100644 --- a/lib/uefi/blockio_protocols.cpp +++ b/lib/uefi/blockio_protocols.cpp @@ -35,6 +35,10 @@ EfiStatus read_blocks(EfiBlockIoProtocol *self, uint32_t media_id, uint64_t lba, printf("OOB read %llu %u\n", lba, dev->block_count); return END_OF_MEDIA; } + if (interface->io_stack == nullptr) { + printf("No IO stack allocted.\n"); + return OUT_OF_RESOURCES; + } const size_t bytes_read = call_with_stack(interface->io_stack, bio_read_block, dev, buffer, lba, @@ -67,12 +71,20 @@ EfiStatus open_block_device(EfiHandle handle, void **intf) { static constexpr size_t kIoStackSize = 1024ul * 1024 * 64; static void *io_stack = nullptr; if (io_stack == nullptr) { - vmm_alloc(vmm_get_kernel_aspace(), "uefi_io_stack", kIoStackSize, &io_stack, - PAGE_SIZE_SHIFT, 0, 0); + auto status = vmm_alloc(vmm_get_kernel_aspace(), "uefi_io_stack", + kIoStackSize, &io_stack, PAGE_SIZE_SHIFT, 0, 0); + if (io_stack == nullptr) { + printf("Failed to allocated IO stack of size %zu error %d\n", + kIoStackSize, status); + return OUT_OF_RESOURCES; + } } printf("%s(%p)\n", __FUNCTION__, handle); const auto interface = reinterpret_cast( uefi_malloc(sizeof(EfiBlockIoInterface))); + if (interface == nullptr) { + return OUT_OF_RESOURCES; + } memset(interface, 0, sizeof(EfiBlockIoInterface)); auto dev = bio_open(reinterpret_cast(handle)); interface->dev = dev; diff --git a/lib/uefi/uefi.cpp b/lib/uefi/uefi.cpp index 3ab61c7e..821914e1 100644 --- a/lib/uefi/uefi.cpp +++ b/lib/uefi/uefi.cpp @@ -52,7 +52,8 @@ constexpr auto EFI_SYSTEM_TABLE_SIGNATURE = using EfiEntry = int (*)(void *, struct EfiSystemTable *); -template void fill(T *data, size_t skip, uint8_t begin = 0) { +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) { @@ -123,13 +124,16 @@ int load_sections_and_execute(bdev_t *dev, reinterpret_cast(alloc_page(PAGE_SIZE)); memset(table.configuration_table, 0, PAGE_SIZE); setup_configuration_table(&table); - platform_setup_system_table(&table); + auto status = platform_setup_system_table(&table); + if (status != SUCCESS) { + printf("platform_setup_system_table failed: %lu\n", status); + return -static_cast(status); + } constexpr size_t kStackSize = 8 * 1024ul * 1024; auto stack = reinterpret_cast(alloc_page(kStackSize, 23)); memset(stack, 0, kStackSize); - printf("Calling kernel with stack [%p, %p]\n", stack, - stack + kStackSize - 1); + printf("Calling kernel with stack [%p, %p]\n", stack, stack + kStackSize - 1); return call_with_stack(stack + kStackSize, entry, image_base, &table); } From 953b519d6922207697d52c26b47e87ae4ff88c65 Mon Sep 17 00:00:00 2001 From: Kelvin Zhang Date: Wed, 11 Jun 2025 10:34:58 -0700 Subject: [PATCH 2/7] [lib][uefi] Update os configuration protocol Two breaking changes: * fixup_kernel_commandline was removed by upstream * GblEfiDeviceTreeMetadata's reserve field is removed, instead a `type` variable is inserted earlier in the struct. Both changes come from upstream GBL, update LK accordingly --- lib/uefi/boot_service_provider.cpp | 1 - .../gbl_efi_os_configuration_protocol.h | 26 ++++++++++++------- lib/uefi/uefi_platform.cpp | 9 ------- lib/uefi/uefi_platform.h | 4 --- 4 files changed, 16 insertions(+), 24 deletions(-) diff --git a/lib/uefi/boot_service_provider.cpp b/lib/uefi/boot_service_provider.cpp index cf1f99aa..d8421cf2 100644 --- a/lib/uefi/boot_service_provider.cpp +++ b/lib/uefi/boot_service_provider.cpp @@ -233,7 +233,6 @@ EfiStatus open_protocol(EfiHandle handle, const EfiGuid *protocol, void **intf, } config->revision = GBL_EFI_OS_CONFIGURATION_PROTOCOL_REVISION; config->fixup_bootconfig = fixup_bootconfig; - config->fixup_kernel_commandline = fixup_kernel_commandline; config->select_device_trees = select_device_trees; *intf = reinterpret_cast(config); return SUCCESS; diff --git a/lib/uefi/include/uefi/protocols/gbl_efi_os_configuration_protocol.h b/lib/uefi/include/uefi/protocols/gbl_efi_os_configuration_protocol.h index d834f3a1..a0f4476c 100644 --- a/lib/uefi/include/uefi/protocols/gbl_efi_os_configuration_protocol.h +++ b/lib/uefi/include/uefi/protocols/gbl_efi_os_configuration_protocol.h @@ -24,24 +24,35 @@ #include static constexpr size_t GBL_EFI_OS_CONFIGURATION_PROTOCOL_REVISION = 0x00000000; +typedef enum GBL_EFI_DEVICE_TREE_TYPE { + // HLOS device tree. + DEVICE_TREE, + // HLOS device tree overlay. + OVERLAY, + // pVM device assignment overlay. + PVM_DA_OVERLAY, +} GblEfiDeviceTreeType; typedef enum GBL_EFI_DEVICE_TREE_SOURCE { + // Device tree loaded from boot partition. BOOT, + // Device tree loaded from vendor_boot partition. VENDOR_BOOT, + // Device tree loaded from dtbo partition. DTBO, - DTB + // Device tree loaded from dtb partition. + DTB, } GblEfiDeviceTreeSource; typedef struct { // GblDeviceTreeSource uint32_t source; + // GblDeviceTreeType + uint32_t type; // Values are zeroed and must not be used in case of BOOT / VENDOR_BOOT source uint32_t id; uint32_t rev; uint32_t custom[4]; - // Make sure GblDeviceTreeMetadata size is 8-bytes aligned. Also reserved for - // the future cases - uint32_t reserved; } GblEfiDeviceTreeMetadata; typedef struct { @@ -57,15 +68,10 @@ typedef struct { // Warning: API is UNSTABLE // Documentation: -// https://cs.android.com/android/platform/superproject/main/+/main:bootable/libbootloader/gbl/docs/gbl_os_configuration_protocol.md +// https://cs.android.com/android/kernel/superproject/+/common-android-mainline:bootable/libbootloader/gbl/docs/gbl_os_configuration_protocol.md typedef struct GblEfiOsConfigurationProtocol { uint64_t revision; - // Generates fixups for the kernel command line built by GBL. - EfiStatus (*fixup_kernel_commandline)( - struct GblEfiOsConfigurationProtocol *self, const char *command_line, - char *fixup, size_t *fixup_buffer_size); - // Generates fixups for the bootconfig built by GBL. EfiStatus (*fixup_bootconfig)(struct GblEfiOsConfigurationProtocol *self, const char *bootconfig, size_t size, diff --git a/lib/uefi/uefi_platform.cpp b/lib/uefi/uefi_platform.cpp index e582d39c..0cd70b48 100644 --- a/lib/uefi/uefi_platform.cpp +++ b/lib/uefi/uefi_platform.cpp @@ -57,15 +57,6 @@ __WEAK EFI_STATUS efi_dt_fixup(struct EfiDtFixupProtocol *self, void *fdt, return SUCCESS; } -// Generates fixups for the kernel command line built by GBL. -__WEAK EfiStatus fixup_kernel_commandline( - struct GblEfiOsConfigurationProtocol *self, const char *command_line, - char *fixup, size_t *fixup_buffer_size) { - printf("%s(%p, \"%s\")\n", __FUNCTION__, self, command_line); - *fixup_buffer_size = 0; - return SUCCESS; -} - // Generates fixups for the bootconfig built by GBL. __WEAK EfiStatus fixup_bootconfig(struct GblEfiOsConfigurationProtocol *self, const char *bootconfig, size_t size, diff --git a/lib/uefi/uefi_platform.h b/lib/uefi/uefi_platform.h index db6dd30e..eeb7f815 100644 --- a/lib/uefi/uefi_platform.h +++ b/lib/uefi/uefi_platform.h @@ -29,10 +29,6 @@ EFI_STATUS efi_dt_fixup(struct EfiDtFixupProtocol *self, void *fdt, size_t *buffer_size, uint32_t flags); -EfiStatus fixup_kernel_commandline(struct GblEfiOsConfigurationProtocol *self, - const char *command_line, char *fixup, - size_t *fixup_buffer_size); - EfiStatus fixup_bootconfig(struct GblEfiOsConfigurationProtocol *self, const char *bootconfig, size_t size, char *fixup, size_t *fixup_buffer_size); From 2ff6ab3920cc9f35ab92d1665e681e4daef8d55c Mon Sep 17 00:00:00 2001 From: Kelvin Zhang Date: Wed, 11 Jun 2025 15:38:19 -0700 Subject: [PATCH 3/7] [lib][uefi] Implement os loading buffer protocol --- lib/uefi/boot_service_provider.cpp | 62 +++++++++++++++++++ lib/uefi/boot_service_provider.h | 6 ++ .../gbl_efi_image_loading_protocol.h | 62 +++++++++++++++++++ 3 files changed, 130 insertions(+) create mode 100644 lib/uefi/include/uefi/protocols/gbl_efi_image_loading_protocol.h diff --git a/lib/uefi/boot_service_provider.cpp b/lib/uefi/boot_service_provider.cpp index d8421cf2..734802ac 100644 --- a/lib/uefi/boot_service_provider.cpp +++ b/lib/uefi/boot_service_provider.cpp @@ -24,6 +24,7 @@ #include #include #include +#include #include #include #include @@ -173,6 +174,53 @@ EfiTpl raise_tpl(EfiTpl new_tpl) { return APPLICATION; } +const char *GetImageType(const char16_t *ImageType) { + if (memcmp(ImageType, GBL_IMAGE_TYPE_OS_LOAD, + sizeof(GBL_IMAGE_TYPE_OS_LOAD)) == 0) { + return "os_load"; + } else if (memcmp(ImageType, GBL_IMAGE_TYPE_FASTBOOT, + sizeof(GBL_IMAGE_TYPE_FASTBOOT)) == 0) { + return "fastboot"; + } else if (memcmp(ImageType, GBL_IMAGE_TYPE_PVMFW_DATA, + sizeof(GBL_IMAGE_TYPE_PVMFW_DATA)) == 0) { + return "pvmfw_data"; + } + return "unknown"; +} +template +T clamp(T n, T lower, T upper) { + if (n < lower) { + return lower; + } + if (n > upper) { + return upper; + } + return n; +} + +EfiStatus get_buffer(struct GblEfiImageLoadingProtocol *self, + const GblEfiImageInfo *ImageInfo, + GblEfiImageBuffer *Buffer) { + printf("%s(%s, %lu)\n", __FUNCTION__, GetImageType(ImageInfo->ImageType), + ImageInfo->SizeBytes); + + // Allow maximum of 128MB buffer + const size_t buffer_size = + clamp(Buffer->SizeBytes, PAGE_SIZE, 128ul * 1024 * 1024); + Buffer->Memory = alloc_page(buffer_size); + if (Buffer->Memory == nullptr) { + return OUT_OF_RESOURCES; + } + + Buffer->SizeBytes = buffer_size; + return SUCCESS; +} +EfiStatus get_verify_partitions(struct GblEfiImageLoadingProtocol *self, + size_t *NumberOfPartitions, + GblEfiPartitionName *Partitions) { + printf("%s is called\n"); + return UNSUPPORTED; +} EfiStatus open_protocol(EfiHandle handle, const EfiGuid *protocol, void **intf, EfiHandle agent_handle, EfiHandle controller_handle, @@ -236,6 +284,20 @@ EfiStatus open_protocol(EfiHandle handle, const EfiGuid *protocol, void **intf, config->select_device_trees = select_device_trees; *intf = reinterpret_cast(config); return SUCCESS; + } else if (guid_eq(protocol, EFI_GBL_EFI_IMAGE_LOADING_PROTOCOL_GUID)) { + printf( + "%s(EFI_GBL_EFI_IMAGE_LOADING_PROTOCOL_GUID, handle=%p, " + "agent_handle%p, controller_handle=%p, attr=0x%x)\n", + __FUNCTION__, handle, agent_handle, controller_handle, attr); + GblEfiImageLoadingProtocol *image_loading = nullptr; + allocate_pool(BOOT_SERVICES_DATA, sizeof(*image_loading), + reinterpret_cast(&image_loading)); + if (image_loading == nullptr) { + return OUT_OF_RESOURCES; + } + image_loading->revision = GBL_EFI_IMAGE_LOADING_PROTOCOL_REVISION; + *intf = reinterpret_cast(image_loading); + return SUCCESS; } printf("%s is unsupported 0x%x 0x%x 0x%x 0x%llx\n", __FUNCTION__, protocol->data1, protocol->data2, protocol->data3, diff --git a/lib/uefi/boot_service_provider.h b/lib/uefi/boot_service_provider.h index a9f3577e..d27a8b63 100644 --- a/lib/uefi/boot_service_provider.h +++ b/lib/uefi/boot_service_provider.h @@ -80,6 +80,12 @@ static constexpr auto EFI_GBL_OS_CONFIGURATION_PROTOCOL_GUID = 0x42ff, {0x85, 0xac, 0xe3, 0xad, 0x6e, 0xfb, 0x46, 0x19}}; +static constexpr auto EFI_GBL_EFI_IMAGE_LOADING_PROTOCOL_GUID = + EfiGuid{0xdb84b4fa, + 0x53bd, + 0x4436, + {0x98, 0xa7, 0x4e, 0x02, 0x71, 0x42, 0x8b, 0xa8}}; + static constexpr auto EFI_DT_FIXUP_PROTOCOL_GUID = EfiGuid{0xe617d64c, 0xfe08, diff --git a/lib/uefi/include/uefi/protocols/gbl_efi_image_loading_protocol.h b/lib/uefi/include/uefi/protocols/gbl_efi_image_loading_protocol.h new file mode 100644 index 00000000..e891be83 --- /dev/null +++ b/lib/uefi/include/uefi/protocols/gbl_efi_image_loading_protocol.h @@ -0,0 +1,62 @@ +/* + * 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 __GBL_EFI_IMAGE_LOADING_PROTOCOL_H__ +#define __GBL_EFI_IMAGE_LOADING_PROTOCOL_H__ + +#include +#include + +static constexpr uint64_t GBL_EFI_IMAGE_LOADING_PROTOCOL_REVISION = 0x00010000; + +#define PARTITION_NAME_LEN_U16 36 + +//****************************************************** +// GBL reserved image types +//****************************************************** +// Buffer for loading, verifying and fixing up OS images. +#define GBL_IMAGE_TYPE_OS_LOAD L"os_load" +// Buffer for use as fastboot download buffer. +#define GBL_IMAGE_TYPE_FASTBOOT L"fastboot" +// Buffer reserved for pvmfw binary and configuration (must be 4KiB-aligned). +#define GBL_IMAGE_TYPE_PVMFW_DATA L"pvmfw_data" + +typedef struct GblEfiImageInfo { + char16_t ImageType[PARTITION_NAME_LEN_U16]; + size_t SizeBytes; +} GblEfiImageInfo; + +typedef struct GblEfiImageBuffer { + void* Memory; + size_t SizeBytes; +} GblEfiImageBuffer; + +typedef struct GblEfiPartitionName { + char16_t StrUtf16[PARTITION_NAME_LEN_U16]; +} GblEfiPartitionName; + +typedef struct GblEfiImageLoadingProtocol { + uint64_t revision; + EfiStatus (*get_buffer)(struct GblEfiImageLoadingProtocol* self, + const GblEfiImageInfo* ImageInfo, + GblEfiImageBuffer* Buffer); + EfiStatus (*get_verify_partitions)(struct GblEfiImageLoadingProtocol* self, + size_t* NumberOfPartitions, + GblEfiPartitionName* Partitions); +} GblEfiImageLoadingProtocol; + +#endif //__GBL_EFI_IMAGE_LOADING_PROTOCOL_H__ \ No newline at end of file From e8cea74e9b9793d2eb7df743e52af6e50f1bfc2b Mon Sep 17 00:00:00 2001 From: Kelvin Zhang Date: Mon, 14 Jul 2025 12:19:51 -0700 Subject: [PATCH 4/7] [lib][uefi] Implement events API in UEFI LK already has events APIs. To support events protocols in UEFI spec, I created a wrapper object for LK events. This wrapper object keeps additional data that are necessary to support UEFI events spec: * Notification function, or callbacks * Argument for the notification function * Type of this UEFI event (which specifies when the callback should be called) The events API is essential for supporting async I/O in UEFI. Intended use case looks like: * UEFI app creates an event object, attatch callback to this event * UEFI app calls LK for asynchronous read/write on a block device, with the newly created events object * LK executes IO operation in background, after IO finishes, signal the specified event object * UEFI's callback gets executed --- lib/uefi/blockio_protocols.cpp | 37 +++-- lib/uefi/blockio_protocols.h | 2 + lib/uefi/boot_service_provider.cpp | 44 ++---- lib/uefi/configuration_table.cpp | 2 +- lib/uefi/events.cpp | 205 +++++++++++++++++++++++++++ lib/uefi/events.h | 46 ++++++ lib/uefi/include/uefi/boot_service.h | 2 +- lib/uefi/include/uefi/types.h | 4 +- lib/uefi/io_stack.cpp | 34 +++++ lib/uefi/io_stack.h | 65 +++++++++ lib/uefi/memory_protocols.cpp | 3 +- lib/uefi/rules.mk | 3 + lib/uefi/switch_stack.h | 30 +++- 13 files changed, 423 insertions(+), 54 deletions(-) create mode 100644 lib/uefi/events.cpp create mode 100644 lib/uefi/events.h create mode 100644 lib/uefi/io_stack.cpp create mode 100644 lib/uefi/io_stack.h diff --git a/lib/uefi/blockio_protocols.cpp b/lib/uefi/blockio_protocols.cpp index b10435a1..9a289168 100644 --- a/lib/uefi/blockio_protocols.cpp +++ b/lib/uefi/blockio_protocols.cpp @@ -22,6 +22,7 @@ #include #include +#include "io_stack.h" #include "memory_protocols.h" #include "switch_stack.h" @@ -32,7 +33,7 @@ EfiStatus read_blocks(EfiBlockIoProtocol *self, uint32_t media_id, uint64_t lba, auto interface = reinterpret_cast(self); auto dev = reinterpret_cast(interface->dev); if (lba >= dev->block_count) { - printf("OOB read %llu %u\n", lba, dev->block_count); + printf("OOB read %s %llu %u\n", dev->name, lba, dev->block_count); return END_OF_MEDIA; } if (interface->io_stack == nullptr) { @@ -68,18 +69,11 @@ EfiStatus reset(EfiBlockIoProtocol *self, bool extended_verification) { } // namespace EfiStatus open_block_device(EfiHandle handle, void **intf) { - static constexpr size_t kIoStackSize = 1024ul * 1024 * 64; - static void *io_stack = nullptr; - if (io_stack == nullptr) { - auto status = vmm_alloc(vmm_get_kernel_aspace(), "uefi_io_stack", - kIoStackSize, &io_stack, PAGE_SIZE_SHIFT, 0, 0); - if (io_stack == nullptr) { - printf("Failed to allocated IO stack of size %zu error %d\n", - kIoStackSize, status); - return OUT_OF_RESOURCES; - } - } printf("%s(%p)\n", __FUNCTION__, handle); + auto io_stack = get_io_stack(); + if (io_stack == nullptr) { + return OUT_OF_RESOURCES; + } const auto interface = reinterpret_cast( uefi_malloc(sizeof(EfiBlockIoInterface))); if (interface == nullptr) { @@ -100,3 +94,22 @@ EfiStatus open_block_device(EfiHandle handle, void **intf) { *intf = interface; return SUCCESS; } + +EfiStatus list_block_devices(size_t *num_handles, EfiHandle **buf) { + size_t device_count = 0; + bio_iter_devices([&device_count](bdev_t *dev) { + device_count++; + return true; + }); + auto devices = + reinterpret_cast(uefi_malloc(sizeof(char *) * device_count)); + size_t i = 0; + bio_iter_devices([&i, devices, device_count](bdev_t *dev) { + devices[i] = dev->name; + i++; + return i < device_count; + }); + *num_handles = i; + *buf = reinterpret_cast(devices); + return SUCCESS; +} diff --git a/lib/uefi/blockio_protocols.h b/lib/uefi/blockio_protocols.h index b79c034c..cbbb1894 100644 --- a/lib/uefi/blockio_protocols.h +++ b/lib/uefi/blockio_protocols.h @@ -5,4 +5,6 @@ EfiStatus open_block_device(EfiHandle handle, void **intf); +EfiStatus list_block_devices(size_t *num_handles, EfiHandle **buf); + #endif diff --git a/lib/uefi/boot_service_provider.cpp b/lib/uefi/boot_service_provider.cpp index 734802ac..47d4aed4 100644 --- a/lib/uefi/boot_service_provider.cpp +++ b/lib/uefi/boot_service_provider.cpp @@ -31,9 +31,9 @@ #include "arch/defines.h" #include "blockio_protocols.h" -#include "lib/bio.h" +#include "events.h" +#include "io_stack.h" #include "memory_protocols.h" -#include "switch_stack.h" #include "uefi_platform.h" namespace { @@ -215,10 +215,11 @@ EfiStatus get_buffer(struct GblEfiImageLoadingProtocol *self, Buffer->SizeBytes = buffer_size; return SUCCESS; } + EfiStatus get_verify_partitions(struct GblEfiImageLoadingProtocol *self, size_t *NumberOfPartitions, GblEfiPartitionName *Partitions) { - printf("%s is called\n"); + printf("%s is called\n", __FUNCTION__); return UNSUPPORTED; } @@ -333,25 +334,6 @@ EfiStatus close_protocol(EfiHandle handle, const EfiGuid *protocol, return UNSUPPORTED; } -EfiStatus list_block_devices(size_t *num_handles, EfiHandle **buf) { - size_t device_count = 0; - bio_iter_devices([&device_count](bdev_t *dev) { - device_count++; - return true; - }); - auto devices = - reinterpret_cast(uefi_malloc(sizeof(char *) * device_count)); - size_t i = 0; - bio_iter_devices([&i, devices, device_count](bdev_t *dev) { - devices[i] = dev->name; - i++; - return i < device_count; - }); - *num_handles = i; - *buf = reinterpret_cast(devices); - return SUCCESS; -} - EfiStatus locate_handle_buffer(EfiLocateHandleSearchType search_type, const EfiGuid *protocol, void *search_key, size_t *num_handles, EfiHandle **buf) { @@ -394,16 +376,6 @@ EfiStatus locate_handle_buffer(EfiLocateHandleSearchType search_type, return UNSUPPORTED; } -EfiStatus wait_for_event(size_t num_events, EfiEvent *event, size_t *index) { - printf("%s is unsupported\n", __FUNCTION__); - return UNSUPPORTED; -} - -EfiStatus signal_event(EfiEvent event) { - printf("%s is unsupported\n", __FUNCTION__); - return UNSUPPORTED; -} - } // namespace void setup_boot_service_table(EfiBootService *service) { @@ -429,6 +401,10 @@ void setup_boot_service_table(EfiBootService *service) { service->open_protocol = open_protocol; service->locate_handle_buffer = locate_handle_buffer; service->close_protocol = close_protocol; - service->wait_for_event = wait_for_event; - service->signal_event = signal_event; + service->wait_for_event = + switch_stack_wrapper(); + service->signal_event = switch_stack_wrapper(); + service->check_event = switch_stack_wrapper(); + service->create_event = create_event; + service->close_event = close_event; } diff --git a/lib/uefi/configuration_table.cpp b/lib/uefi/configuration_table.cpp index 85f2dcd9..15e4dc53 100644 --- a/lib/uefi/configuration_table.cpp +++ b/lib/uefi/configuration_table.cpp @@ -17,10 +17,10 @@ #include "configuration_table.h" +#include #include #include -#include "libfdt.h" #include "memory_protocols.h" #include "platform.h" diff --git a/lib/uefi/events.cpp b/lib/uefi/events.cpp new file mode 100644 index 00000000..6e2d1603 --- /dev/null +++ b/lib/uefi/events.cpp @@ -0,0 +1,205 @@ +/* + * Copyright (C) 2025 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 "events.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define LOCAL_TRACE 0 + +namespace { +constexpr size_t kMaxEventCount = 16; +struct { + public: + template + void read_access(Func f) const { + AutoLock a(&m); + f(arr, events); + } + + template + void write_access(Func f) { + AutoLock a(&m); + f(arr, events); + } + size_t get_events_count() const { return events; } + + private: + // Cache up to 16 completed events + mutable Mutex m; + EfiEventImpl *volatile arr[kMaxEventCount]; + volatile size_t events = 0; +} completed_events; + +lk_time_t backoff_wait_time = 1; + +} // namespace + +EfiStatus wait_for_event(size_t num_events, EfiEvent *event, size_t *index) { + LTRACEF("waiting for %zu events\n", num_events); + for (size_t i = 0; i < num_events; i++) { + EfiEventImpl *ev = reinterpret_cast(event[i]); + if (ev->ready()) { + *index = i; + return SUCCESS; + } + } + while (true) { + for (size_t i = 0; i < num_events; i++) { + EfiEventImpl *ev = reinterpret_cast(event[i]); + if (ev->ready()) { + *index = i; + return SUCCESS; + } + auto status = event_wait_timeout(&ev->ev, 200); + if (status == ERR_TIMED_OUT) { + continue; + } + return SUCCESS; + } + } + return NOT_READY; +} + +EfiStatus signal_event(EfiEvent event) { + LTRACEF("%s(type=0x%x, ready=%d)\n", __FUNCTION__, event->type, + event->ready()); + if (event->ready()) { + printf("Event %p already signaled\n", event); + return SUCCESS; + } + event_signal(&event->ev, true); + if ((event->type & NOTIFY_SIGNAL) && event->notify_fn != nullptr) { + // If this event is signaled on a different thread, defer + // calling callbacks until the next check_event call. As UEFI apps + // are single threaded, we don't want to call event callbacks from another + // thread + if (event->creator_thread != get_current_thread()) { + LTRACEF( + "Event %p of type 0x%x is signaled from thread %s, defer notify_fn " + "because event is created on thread %s\n", + event, event->type, get_current_thread()->name, + event->creator_thread->name); + bool success = false; + while (!success) { + completed_events.write_access( + [event, &success](auto &&events, auto &i) { + if (i >= kMaxEventCount) { + return; + } + events[i++] = event; + success = true; + }); + if (!success) { + thread_yield(); + } + } + return SUCCESS; + } + event->notify_fn(event, event->notify_ctx); + event->callback_called = true; + } + return SUCCESS; +} + +EfiStatus create_event(EfiEventType type, EfiTpl notify_tpl, + EfiEventNotify notify_fn, void *notify_ctx, + EfiEvent *event) { + if ((type & TIMER) != 0) { + printf("Creating timer event is not supported yet\n"); + return UNSUPPORTED; + } + if ((type & SIGNAL_EXIT_BOOT_SERVICES) == SIGNAL_EXIT_BOOT_SERVICES || + (type & SIGNAL_VIRTUAL_ADDRESS_CHANGE) == SIGNAL_VIRTUAL_ADDRESS_CHANGE) { + printf( + "Creating SIGNAL_EXIT_BOOT_SERVICES or SIGNAL_VIRTUAL_ADDRESS_CHANGE " + "event is not supported yet 0x%x\n", + type); + return UNSUPPORTED; + } + if ((type & NOTIFY_WAIT)) { + printf("Creating NOTIFY_WAIT event is not supported yet\n"); + } + auto ev = reinterpret_cast(malloc(sizeof(EfiEventImpl))); + memset(ev, 0, sizeof(EfiEventImpl)); + ev->type = type; + event_init(&ev->ev, false, 0); + ev->notify_ctx = notify_ctx; + ev->notify_fn = notify_fn; + ev->creator_thread = get_current_thread(); + LTRACEF("Created event 0x%x callback %p %p on thread %s\n", type, notify_fn, + notify_ctx, get_current_thread()->name); + *event = ev; + return SUCCESS; +} + +EfiStatus check_event(EfiEvent event) { + while (completed_events.get_events_count() > 0) { + completed_events.write_access([](auto &&events, auto &count) { + for (size_t i = 0; i < count; i++) { + EfiEventImpl *ev = events[i]; + if (!ev->callback_called) { + // reset exponential backoff timer + LTRACEF("Triggering event %p callback %p %p on thread %s\n", ev, + ev->notify_fn, ev->notify_ctx, get_current_thread()->name); + backoff_wait_time = 0; + ev->notify_fn(ev, ev->notify_ctx); + ev->callback_called = true; + events[i] = nullptr; + } else { + printf("Event %p with callback %p %p already called, unusual\n", ev, + ev->notify_fn, ev->notify_ctx); + } + } + count = 0; + }); + } + // Some UEFI applications repeadtely call check_event(NULL) as a way to poll + // for completed events. To avoid busy waiting, implement an exponential + // backoff strategy if check_event is called repeatdely AND no events in the + // system are completed. + if (event == nullptr) { + if (backoff_wait_time == 0) { + thread_yield(); + backoff_wait_time++; + } else { + thread_sleep(backoff_wait_time); + if (backoff_wait_time < 500) { + backoff_wait_time <<= 1; + } + } + return INVALID_PARAMETER; + } + if (event->ready()) { + return SUCCESS; + } + return NOT_READY; +} + +EfiStatus close_event(EfiEvent event) { + event_destroy(&event->ev); + free(event); + return SUCCESS; +} \ No newline at end of file diff --git a/lib/uefi/events.h b/lib/uefi/events.h new file mode 100644 index 00000000..c5d7c3dc --- /dev/null +++ b/lib/uefi/events.h @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2025 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 __LIB_UEFI_EVENTS_H_ +#define __LIB_UEFI_EVENTS_H_ +#include +#include +#include + +struct EfiEventImpl final { + EfiEventType type; + event_t ev; + EfiEventNotify notify_fn = nullptr; + void *notify_ctx = nullptr; + volatile bool callback_called = false; + thread_t *creator_thread = nullptr; + bool ready() const { return ev.signaled; } +}; + +EfiStatus wait_for_event(size_t num_events, EfiEvent *event, size_t *index); + +EfiStatus signal_event(EfiEvent event); + +EfiStatus check_event(EfiEvent event); + +EfiStatus create_event(EfiEventType type, EfiTpl notify_tpl, + EfiEventNotify notify_fn, void *notify_ctx, + EfiEvent *event); + +EfiStatus close_event(EfiEvent event); + +#endif diff --git a/lib/uefi/include/uefi/boot_service.h b/lib/uefi/include/uefi/boot_service.h index 229dba57..b683b6e5 100644 --- a/lib/uefi/include/uefi/boot_service.h +++ b/lib/uefi/include/uefi/boot_service.h @@ -84,7 +84,7 @@ typedef struct { size_t* desc_size, uint32_t* desc_version); EfiStatus (*allocate_pool)(EfiMemoryType pool_type, size_t size, void** buf); EfiStatus (*free_pool)(void* buf); - EfiStatus (*create_event)(uint32_t type, EfiTpl notify_tpl, + EfiStatus (*create_event)(EfiEventType type, EfiTpl notify_tpl, EfiEventNotify notify_fn, void* notify_ctx, EfiEvent* event); EfiStatus (*set_timer)(EfiEvent event, EfiTimerDelay type, diff --git a/lib/uefi/include/uefi/types.h b/lib/uefi/include/uefi/types.h index 106b8fe4..247e44b7 100644 --- a/lib/uefi/include/uefi/types.h +++ b/lib/uefi/include/uefi/types.h @@ -35,8 +35,10 @@ using EfiGuid = struct EfiGuid { uint8_t data4[8]; }; +struct EfiEventImpl; + using EfiHandle = void *; -using EfiEvent = void *; +using EfiEvent = EfiEventImpl *; using EfiPhysicalAddr = uint64_t; using EfiVirtualAddr = uint64_t; diff --git a/lib/uefi/io_stack.cpp b/lib/uefi/io_stack.cpp new file mode 100644 index 00000000..69c29303 --- /dev/null +++ b/lib/uefi/io_stack.cpp @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2025 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 "io_stack.h" + +#include + +void *get_io_stack() { + static void *io_stack = nullptr; + if (io_stack == nullptr) { + auto status = vmm_alloc(vmm_get_kernel_aspace(), "uefi_io_stack", + kIoStackSize, &io_stack, PAGE_SIZE_SHIFT, 0, 0); + if (io_stack == nullptr) { + printf("Failed to allocated IO stack of size %zu error %d\n", + kIoStackSize, status); + return nullptr; + } + } + return io_stack; +} \ No newline at end of file diff --git a/lib/uefi/io_stack.h b/lib/uefi/io_stack.h new file mode 100644 index 00000000..e535dd57 --- /dev/null +++ b/lib/uefi/io_stack.h @@ -0,0 +1,65 @@ +/* + * Copyright (C) 2025 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 +#include + +#include "switch_stack.h" + +constexpr size_t kIoStackSize = 1024ul * 32; + +void* get_io_stack(); + +template +EfiStatus (*switch_stack_wrapper())(Arg1 arg) { + auto trampoline = [](Arg1 arg) -> EfiStatus { + void* io_stack = reinterpret_cast(get_io_stack()) + kIoStackSize; + return static_cast(call_with_stack(io_stack, Func, arg)); + }; + return trampoline; +} + +template +EfiStatus (*switch_stack_wrapper())(Arg1 arg1, Arg2 arg2) { + auto trampoline = [](Arg1 arg1, Arg2 arg2) -> EfiStatus { + void* io_stack = reinterpret_cast(get_io_stack()) + kIoStackSize; + return static_cast(call_with_stack(io_stack, Func, arg1, arg2)); + }; + return trampoline; +} + +template +EfiStatus (*switch_stack_wrapper())(Arg1 arg1, Arg2 arg2, Arg3 arg3) { + auto trampoline = [](Arg1 arg1, Arg2 arg2, Arg3 arg3) -> EfiStatus { + void* io_stack = reinterpret_cast(get_io_stack()) + kIoStackSize; + return static_cast( + call_with_stack(io_stack, Func, arg1, arg2, arg3)); + }; + return trampoline; +} + +template +EfiStatus (*switch_stack_wrapper())(Arg1, Arg2, Arg3, Arg4) { + auto trampoline = [](Arg1 arg1, Arg2 arg2, Arg3 arg3, + Arg4 arg4) -> EfiStatus { + void* io_stack = reinterpret_cast(get_io_stack()) + kIoStackSize; + return static_cast( + call_with_stack(io_stack, Func, arg1, arg2, arg3, arg4)); + }; + return trampoline; +} diff --git a/lib/uefi/memory_protocols.cpp b/lib/uefi/memory_protocols.cpp index 18090762..b6aa8d57 100644 --- a/lib/uefi/memory_protocols.cpp +++ b/lib/uefi/memory_protocols.cpp @@ -27,7 +27,7 @@ static vmm_aspace_t *old_aspace = nullptr; namespace { -constexpr size_t kHeapSize = 256ul * 1024 * 1024; +constexpr size_t kHeapSize = 300ul * 1024 * 1024; void *get_heap() { static auto heap = alloc_page(kHeapSize); @@ -44,6 +44,7 @@ mspace get_mspace() { static auto space = create_mspace_with_base_limit(get_heap(), kHeapSize, 1); return space; } + } // namespace vmm_aspace_t *set_boot_aspace() { diff --git a/lib/uefi/rules.mk b/lib/uefi/rules.mk index 635eaaaa..d73cf1f3 100644 --- a/lib/uefi/rules.mk +++ b/lib/uefi/rules.mk @@ -17,5 +17,8 @@ MODULE_SRCS += \ $(LOCAL_DIR)/runtime_service_provider.cpp \ $(LOCAL_DIR)/switch_stack.S \ $(LOCAL_DIR)/configuration_table.cpp \ + $(LOCAL_DIR)/events.cpp \ + $(LOCAL_DIR)/io_stack.cpp \ + include make/module.mk diff --git a/lib/uefi/switch_stack.h b/lib/uefi/switch_stack.h index 18df3f74..1d0b6277 100644 --- a/lib/uefi/switch_stack.h +++ b/lib/uefi/switch_stack.h @@ -15,6 +15,9 @@ * */ +#ifndef __LIB_UEFI_SWITCH_STACK_H +#define __LIB_UEFI_SWITCH_STACK_H + #include #include @@ -35,10 +38,29 @@ size_t call_with_stack(void *stack, Function &&fp, P1 &¶m1, P2 &¶m2, reinterpret_cast(param3), reinterpret_cast(param4)); } +template +size_t call_with_stack(void *stack, Function &&fp, P1 &¶m1, P2 &¶m2, + P3 &¶m3) { + return call_with_stack_asm(stack, reinterpret_cast(fp), + reinterpret_cast(param1), + reinterpret_cast(param2), + reinterpret_cast(param3), nullptr); +} + template size_t call_with_stack(void *stack, Function &&fp, P1 &¶m1, P2 &¶m2) { - return call_with_stack_asm( - stack, reinterpret_cast(fp), - reinterpret_cast(param1), reinterpret_cast(param2), 0, 0); + return call_with_stack_asm(stack, reinterpret_cast(fp), + reinterpret_cast(param1), + reinterpret_cast(param2), nullptr, + nullptr); } -#endif \ No newline at end of file + +template +size_t call_with_stack(void *stack, Function &&fp, P1 &¶m1) { + return call_with_stack_asm(stack, reinterpret_cast(fp), + reinterpret_cast(param1), nullptr, nullptr, + nullptr); +} +#endif + +#endif From 76ce54625a2cfedf86230893a0748c1f1334594d Mon Sep 17 00:00:00 2001 From: Kelvin Zhang Date: Tue, 15 Jul 2025 15:03:45 -0700 Subject: [PATCH 5/7] [lib][uefi] Support 5 arguments for switch stack call Some functions have >4 arguments, bump support to 5 arguments --- lib/uefi/switch_stack.S | 15 ++++++++------- lib/uefi/switch_stack.h | 32 ++++++++++++++++++++++---------- 2 files changed, 30 insertions(+), 17 deletions(-) diff --git a/lib/uefi/switch_stack.S b/lib/uefi/switch_stack.S index 15f301ac..f11b3bfc 100644 --- a/lib/uefi/switch_stack.S +++ b/lib/uefi/switch_stack.S @@ -17,23 +17,24 @@ #include -// int call_with_stack_asm(void *stack, int (*fp)(), int arg1, int arg2, int arg3, int arg4); +// int call_with_stack_asm(void *stack, int (*fp)(), int arg1, int arg2, int arg3, int arg4, int arg5); FUNCTION(call_with_stack_asm) stp fp, lr, [sp, #-16]! mov fp, sp sub x0,x0,16 -mov x7,sp -str x7,[x0] +mov x8,sp +str x8,[x0] mov sp,x0 -mov x6,x1 +mov x7,x1 mov x0,x2 mov x1,x3 mov x2,x4 mov x3,x5 -blr x6 -ldr x7,[sp] -mov sp,x7 +mov x4,x6 +blr x7 +ldr x8,[sp] +mov sp,x8 ldp fp, lr, [sp], 16 ret lr diff --git a/lib/uefi/switch_stack.h b/lib/uefi/switch_stack.h index 1d0b6277..53015d85 100644 --- a/lib/uefi/switch_stack.h +++ b/lib/uefi/switch_stack.h @@ -24,34 +24,46 @@ __BEGIN_CDECLS size_t call_with_stack_asm(void *stack, const void *function, void *param1, - void *param2, void *param3, void *param4); + void *param2, void *param3, void *param4, + void *param5); __END_CDECLS #ifdef __cplusplus -template +template size_t call_with_stack(void *stack, Function &&fp, P1 &¶m1, P2 &¶m2, - P3 &¶m3, P4 &¶m4) { + P3 &¶m3, P4 &¶m4, P5 &¶m5) { return call_with_stack_asm( stack, reinterpret_cast(fp), reinterpret_cast(param1), reinterpret_cast(param2), - reinterpret_cast(param3), reinterpret_cast(param4)); + reinterpret_cast(param3), reinterpret_cast(param4), + reinterpret_cast(param5)); +} +template +size_t call_with_stack(void *stack, Function &&fp, P1 &¶m1, P2 &¶m2, + P3 &¶m3, P4 &¶m4) { + return call_with_stack_asm(stack, reinterpret_cast(fp), + reinterpret_cast(param1), + reinterpret_cast(param2), + reinterpret_cast(param3), + reinterpret_cast(param4), nullptr); } template size_t call_with_stack(void *stack, Function &&fp, P1 &¶m1, P2 &¶m2, P3 &¶m3) { - return call_with_stack_asm(stack, reinterpret_cast(fp), - reinterpret_cast(param1), - reinterpret_cast(param2), - reinterpret_cast(param3), nullptr); + return call_with_stack_asm( + stack, reinterpret_cast(fp), + reinterpret_cast(param1), reinterpret_cast(param2), + reinterpret_cast(param3), nullptr, nullptr); } template size_t call_with_stack(void *stack, Function &&fp, P1 &¶m1, P2 &¶m2) { return call_with_stack_asm(stack, reinterpret_cast(fp), reinterpret_cast(param1), - reinterpret_cast(param2), nullptr, + reinterpret_cast(param2), nullptr, nullptr, nullptr); } @@ -59,7 +71,7 @@ template size_t call_with_stack(void *stack, Function &&fp, P1 &¶m1) { return call_with_stack_asm(stack, reinterpret_cast(fp), reinterpret_cast(param1), nullptr, nullptr, - nullptr); + nullptr, nullptr); } #endif From f7d8f58cdc78b0217d883987a461f728d7125b04 Mon Sep 17 00:00:00 2001 From: Kelvin Zhang Date: Tue, 15 Jul 2025 15:05:18 -0700 Subject: [PATCH 6/7] [lib][uefi] Support async blockio --- lib/uefi/blockio2_protocols.cpp | 139 ++++++++++++++++++ lib/uefi/blockio2_protocols.h | 25 ++++ lib/uefi/boot_service_provider.cpp | 3 +- .../uefi/protocols/block_io2_protocol.h | 46 ++++++ lib/uefi/rules.mk | 1 + lib/uefi/thread_utils.h | 28 ++++ 6 files changed, 241 insertions(+), 1 deletion(-) create mode 100644 lib/uefi/blockio2_protocols.cpp create mode 100644 lib/uefi/blockio2_protocols.h create mode 100644 lib/uefi/include/uefi/protocols/block_io2_protocol.h create mode 100644 lib/uefi/thread_utils.h diff --git a/lib/uefi/blockio2_protocols.cpp b/lib/uefi/blockio2_protocols.cpp new file mode 100644 index 00000000..7ee8b937 --- /dev/null +++ b/lib/uefi/blockio2_protocols.cpp @@ -0,0 +1,139 @@ +/* + * Copyright (C) 2025 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 "blockio2_protocols.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "events.h" +#include "io_stack.h" +#include "memory_protocols.h" +#include "switch_stack.h" +#include "thread_utils.h" + +#define LOCAL_TRACE 0 + +namespace { + +struct EfiBlockIo2Interface { + EfiBlockIo2Protocol protocol; + EfiBlockIoMedia media; + void* dev; +}; + +EfiStatus reset(EfiBlockIo2Protocol* self, bool extended_verification) { + return UNSUPPORTED; +} + +// Read from dev, after I/O completes, signal token->event and set +// token->transaction_status +EfiStatus read_blocks_async(bdev_t* dev, uint64_t lba, EfiBlockIo2Token* token, + size_t buffer_size, void* buffer) { + if (lba >= dev->block_count) { + printf("OOB async read %s %llu %u\n", dev->name, lba, dev->block_count); + return END_OF_MEDIA; + } + if (token == nullptr) { + printf("Invalid token %p\n", token); + return INVALID_PARAMETER; + } + // First draft of this API will just use a background thread. + // More efficient version can be implemented once LK's bio layer + // supports async IO + auto thread = thread_create_functor( + "async_bio", + [dev, lba, buffer_size, buffer, token]() { + auto aspace = set_boot_aspace(); + vmm_set_active_aspace(aspace); + auto bytes_read = + bio_read_block(dev, buffer, lba, buffer_size / dev->block_size); + if (static_cast(bytes_read) != buffer_size) { + token->transaction_status = DEVICE_ERROR; + } else { + token->transaction_status = SUCCESS; + } + signal_event(token->event); + return 0; + }, + get_current_thread()->priority, kIoStackSize); + if (thread == nullptr) { + printf("Failed to create thread for IO read\n"); + return DEVICE_ERROR; + } + + auto err = thread_detach_and_resume(thread); + if (err != NO_ERROR) { + printf("Failed to resume thread for IO read %d\n", err); + return DEVICE_ERROR; + } + return SUCCESS; +} + +EfiStatus read_blocks_trampoline(EfiBlockIo2Protocol* self, uint32_t media_id, + uint64_t lba, EfiBlockIo2Token* token, + size_t buffer_size, void* buffer) { + auto interface = reinterpret_cast(self); + auto dev = reinterpret_cast(interface->dev); + void* io_stack = reinterpret_cast(get_io_stack()) + kIoStackSize; + auto ret = call_with_stack(io_stack, read_blocks_async, dev, lba, token, + buffer_size, buffer); + return static_cast(ret); +} + +EfiStatus write_blocks_ex(EfiBlockIo2Protocol* self, uint32_t media_id, + uint64_t lba, EfiBlockIo2Token* token, + size_t buffer_size, const void* buffer) { + printf( + "Writing blocks from UEFI app is currently not supported to protect the " + "device.\n"); + return UNSUPPORTED; +} + +EfiStatus flush_blocks_ex(EfiBlockIo2Protocol* self, EfiBlockIo2Token* token) { + return SUCCESS; +} + +} // namespace + +EfiStatus open_async_block_device(EfiHandle handle, void** intf) { + auto dev = bio_open(reinterpret_cast(handle)); + printf("%s(%s)\n", __FUNCTION__, dev->name); + auto interface = reinterpret_cast( + uefi_malloc(sizeof(EfiBlockIo2Interface))); + auto protocol = &interface->protocol; + auto media = &interface->media; + protocol->media = media; + protocol->reset = reset; + protocol->read_blocks_ex = read_blocks_trampoline; + protocol->write_blocks_ex = write_blocks_ex; + protocol->flush_blocks_ex = flush_blocks_ex; + media->block_size = dev->block_size; + media->io_align = media->block_size; + media->last_block = dev->block_count - 1; + interface->dev = dev; + *intf = interface; + + return SUCCESS; +} \ No newline at end of file diff --git a/lib/uefi/blockio2_protocols.h b/lib/uefi/blockio2_protocols.h new file mode 100644 index 00000000..6717dfba --- /dev/null +++ b/lib/uefi/blockio2_protocols.h @@ -0,0 +1,25 @@ +/* + * Copyright (C) 2025 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 __LIB_UEFI_BLOCKIO2_PROTOCOL_H_ +#define __LIB_UEFI_BLOCKIO2_PROTOCOL_H_ + +#include + +EfiStatus open_async_block_device(EfiHandle handle, void **intf); + +#endif diff --git a/lib/uefi/boot_service_provider.cpp b/lib/uefi/boot_service_provider.cpp index 47d4aed4..df868338 100644 --- a/lib/uefi/boot_service_provider.cpp +++ b/lib/uefi/boot_service_provider.cpp @@ -30,6 +30,7 @@ #include #include "arch/defines.h" +#include "blockio2_protocols.h" #include "blockio_protocols.h" #include "events.h" #include "io_stack.h" @@ -252,7 +253,7 @@ EfiStatus open_protocol(EfiHandle handle, const EfiGuid *protocol, void **intf, printf("%s(EFI_BLOCK_IO2_PROTOCOL_GUID, handle=%p, agent_handle=%p, " "controller_handle=%p, attr=0x%x)\n", __FUNCTION__, handle, agent_handle, controller_handle, attr); - return UNSUPPORTED; + return open_async_block_device(handle, intf); } else if (guid_eq(protocol, EFI_DT_FIXUP_PROTOCOL_GUID)) { printf("%s(EFI_DT_FIXUP_PROTOCOL_GUID, handle=%p, agent_handle=%p, " "controller_handle=%p, attr=0x%x)\n", diff --git a/lib/uefi/include/uefi/protocols/block_io2_protocol.h b/lib/uefi/include/uefi/protocols/block_io2_protocol.h new file mode 100644 index 00000000..2f98709c --- /dev/null +++ b/lib/uefi/include/uefi/protocols/block_io2_protocol.h @@ -0,0 +1,46 @@ +/* + * 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 __BLOCK_IO2_PROTOCOL_H__ +#define __BLOCK_IO2_PROTOCOL_H__ + +#include + +#include "block_io_protocol.h" + +typedef struct EfiBlockIoMedia EfiBlockIoMedia; +typedef struct EfiBlockIo2Protocol EfiBlockIo2Protocol; + +typedef struct { + EfiEvent event; + EfiStatus transaction_status; +} EfiBlockIo2Token; + +struct EfiBlockIo2Protocol { + EfiBlockIoMedia* media; + EfiStatus (*reset)(EfiBlockIo2Protocol* self, bool extended_verification); + EfiStatus (*read_blocks_ex)(EfiBlockIo2Protocol* self, uint32_t media_id, + uint64_t lba, EfiBlockIo2Token* token, + size_t buffer_size, void* buffer); + EfiStatus (*write_blocks_ex)(EfiBlockIo2Protocol* self, uint32_t media_id, + uint64_t lba, EfiBlockIo2Token* token, + size_t buffer_size, const void* buffer); + EfiStatus (*flush_blocks_ex)(EfiBlockIo2Protocol* self, + EfiBlockIo2Token* token); +}; + +#endif //__BLOCK_IO2_PROTOCOL_H__ \ No newline at end of file diff --git a/lib/uefi/rules.mk b/lib/uefi/rules.mk index d73cf1f3..9fb18e7b 100644 --- a/lib/uefi/rules.mk +++ b/lib/uefi/rules.mk @@ -13,6 +13,7 @@ MODULE_SRCS += \ $(LOCAL_DIR)/boot_service_provider.cpp \ $(LOCAL_DIR)/memory_protocols.cpp \ $(LOCAL_DIR)/blockio_protocols.cpp \ + $(LOCAL_DIR)/blockio2_protocols.cpp \ $(LOCAL_DIR)/uefi_platform.cpp \ $(LOCAL_DIR)/runtime_service_provider.cpp \ $(LOCAL_DIR)/switch_stack.S \ diff --git a/lib/uefi/thread_utils.h b/lib/uefi/thread_utils.h new file mode 100644 index 00000000..ca2555aa --- /dev/null +++ b/lib/uefi/thread_utils.h @@ -0,0 +1,28 @@ + +#ifndef __LIB_UEFI_THREAD_UTILS_H_ +#define __LIB_UEFI_THREAD_UTILS_H_ + +#include +#include +#include + +#ifdef __cplusplus + +template +int functor_thread_trampoline(void *f) { + auto callable = reinterpret_cast(f); + int ret = (*callable)(); + delete callable; + return ret; +} + +template +thread_t *thread_create_functor(const char *name, Func f, int priority, + size_t stack_size) { + auto heap_functor = new decltype(f)(f); + return thread_create(name, functor_thread_trampoline, heap_functor, + priority, stack_size); +} +#endif + +#endif \ No newline at end of file From f356261b67ab9ca87db63fa51961a97a252b2330 Mon Sep 17 00:00:00 2001 From: Kelvin Zhang Date: Tue, 15 Jul 2025 15:31:39 -0700 Subject: [PATCH 7/7] [lib][uefi] Allow identity mapped pages to be free'ed Since UEFI requires app to be run in physical address space, we allocate memory that are identity mapped. Previous identity mapping works as follows: 1. call vmm_alloc_contigious to obtain a VA1(virtual address) mapping to PA1(physical address) 2. Use arch_mmu_query to obtain PA1 from VA1 3. arch_mmu_unmap VA1 <-> PA1 4. arch_mmu_map PA1 <-> PA1 Now we can return PA1 as a valid virtual address pointer, which is mapped to physical memory. However, this has problems. LK allocates a vmm_region_t struct for each call to vmm_alloc_* . This struct can never be free'ed, as the associated virtual address VA1 is forever lost. We can't even free PA1, as PA1 is not a valid argument to vmm_free(PA1 does not have associated vmm_region_t struct). The new approach uses pmm_alloc and vmm_reserve_space, allowing vmm_free_region to be called. --- lib/uefi/memory_protocols.cpp | 114 ++++++++++++++++++++-------------- 1 file changed, 67 insertions(+), 47 deletions(-) diff --git a/lib/uefi/memory_protocols.cpp b/lib/uefi/memory_protocols.cpp index b6aa8d57..35a90e55 100644 --- a/lib/uefi/memory_protocols.cpp +++ b/lib/uefi/memory_protocols.cpp @@ -17,16 +17,21 @@ #include "memory_protocols.h" +#include +#include #include #include #include #include +#include #include #include -static vmm_aspace_t *old_aspace = nullptr; +// MACRO list_for_every_entry cast between int/ptr +// NOLINTBEGIN(performance-no-int-to-ptr) namespace { +vmm_aspace_t *old_aspace = nullptr; constexpr size_t kHeapSize = 300ul * 1024 * 1024; void *get_heap() { @@ -45,6 +50,8 @@ mspace get_mspace() { return space; } +void restore_aspace() { vmm_set_active_aspace(old_aspace); } + } // namespace vmm_aspace_t *set_boot_aspace() { @@ -60,63 +67,54 @@ vmm_aspace_t *set_boot_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 size %zu flags 0x%x\n", pa, - size, flags); - - return reinterpret_cast(pa); -} - void *alloc_page(size_t size, size_t align_log2) { + size = ROUNDUP(size, PAGE_SIZE); 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); + paddr_t pa{}; + size_t allocated = + pmm_alloc_contiguous(size / PAGE_SIZE, align_log2, &pa, nullptr); + if (allocated != size / PAGE_SIZE) { + printf("Failed to allocate physical memory size %zu\n", size); return nullptr; } - return identity_map(vptr, size); + int ret = arch_mmu_map(&aspace->arch_aspace, pa, pa, size / PAGE_SIZE, 0); + if (ret != 0) { + printf("Failed to arch_mmu_map(0x%lx)\n", pa); + return nullptr; + } + status_t err{}; + err = vmm_reserve_space(aspace, "uefi_program", size, pa); + if (err) { + printf("Failed to vmm_reserve_space for uefi program %d\n", err); + return nullptr; + } + return reinterpret_cast(pa); } 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) { + auto aspace = set_boot_aspace(); + size = ROUNDUP(size, PAGE_SIZE); + struct list_node list; + size_t allocated = + pmm_alloc_range(reinterpret_cast(addr), size, &list); + if (allocated != size / PAGE_SIZE) { printf( - "Failed to allocate memory for uefi program @ fixed address 0x%p %d , " - "falling back to non-fixed allocation\n", - addr, err); + "Failed to allocate physical memory size %zu at specified address %p " + "fallback to regular allocation\n", + size, addr); return alloc_page(size, align_log2); } - return identity_map(addr, size); + status_t err{}; + err = vmm_reserve_space(aspace, "uefi_program", size, + reinterpret_cast(addr)); + if (err) { + printf("Failed to vmm_reserve_space for uefi program %d\n", err); + return nullptr; + } + return addr; } void *uefi_malloc(size_t size) { return mspace_malloc(get_mspace(), size); } @@ -142,8 +140,28 @@ EfiStatus free_pool(void *mem) { } EfiStatus free_pages(EfiPhysicalAddr memory, size_t pages) { - printf("%s is unsupported\n", __FUNCTION__); - return UNSUPPORTED; + auto pa = reinterpret_cast( + vaddr_to_paddr(reinterpret_cast(memory))); + if (pa != reinterpret_cast(memory)) { + printf( + "WARN: virtual address 0x%llx is not identity mapped, physical addr: " + "%p\n", + memory, pa); + } + status_t err = + vmm_free_region(set_boot_aspace(), static_cast(memory)); + if (err) { + printf("%s err:%d memory [0x%llx] pages:%zu\n", __FUNCTION__, err, memory, + pages); + return DEVICE_ERROR; + } + auto pages_freed = pmm_free_kpages(pa, pages); + if (pages_freed != pages) { + printf("Failed to free physical pages %p %zu, only freed %zu pages\n", pa, + pages, pages_freed); + return DEVICE_ERROR; + } + return SUCCESS; } size_t get_aspace_entry_count(vmm_aspace_t *aspace) { @@ -256,3 +274,5 @@ EfiStatus get_memory_map(size_t *memory_map_size, return SUCCESS; } + +// NOLINTEND(performance-no-int-to-ptr) \ No newline at end of file