From e8ca8f298924863f62b929d1c1bc3a93fb6eede0 Mon Sep 17 00:00:00 2001 From: Kelvin Zhang Date: Wed, 18 Dec 2024 14:16:44 -0800 Subject: [PATCH] [app][uefi] Implement GLB fdt fixiup protocol --- lib/uefi/boot_service_provider.cpp | 143 +++++++++++++++++- lib/uefi/boot_service_provider.h | 12 ++ .../include/protocols/dt_fixup_protocol.h | 45 ++++++ .../gbl_efi_os_configuration_protocol.h | 80 ++++++++++ lib/uefi/uefi.cpp | 2 + 5 files changed, 280 insertions(+), 2 deletions(-) create mode 100644 lib/uefi/local/include/protocols/dt_fixup_protocol.h create mode 100644 lib/uefi/local/include/protocols/gbl_efi_os_configuration_protocol.h diff --git a/lib/uefi/boot_service_provider.cpp b/lib/uefi/boot_service_provider.cpp index 7b4193885..ca619ba39 100644 --- a/lib/uefi/boot_service_provider.cpp +++ b/lib/uefi/boot_service_provider.cpp @@ -18,11 +18,15 @@ #include "arch/defines.h" #include "boot_service.h" +#include "defer.h" #include "kernel/thread.h" #include "kernel/vm.h" #include "lib/bio.h" #include "lib/dlmalloc.h" +#include "libfdt.h" #include "protocols/block_io_protocol.h" +#include "protocols/dt_fixup_protocol.h" +#include "protocols/gbl_efi_os_configuration_protocol.h" #include "protocols/loaded_image_protocol.h" #include "switch_stack.h" @@ -142,7 +146,7 @@ EfiStatus handle_protocol(EfiHandle handle, const EfiGuid *protocol, printf("handle_protocol(%p, LOADED_IMAGE_PROTOCOL_GUID, %p);\n", handle, intf); const auto loaded_image = static_cast( - malloc(sizeof(EFI_LOADED_IMAGE_PROTOCOL))); + mspace_malloc(get_mspace(), sizeof(EFI_LOADED_IMAGE_PROTOCOL))); *loaded_image = {}; loaded_image->Revision = EFI_LOADED_IMAGE_PROTOCOL_REVISION; loaded_image->ParentHandle = nullptr; @@ -413,7 +417,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 %ld %ld\n", lba, dev->block_count); + printf("OOB read %llu %u\n", lba, dev->block_count); return END_OF_MEDIA; } @@ -464,6 +468,75 @@ EfiStatus open_block_device(EfiHandle handle, void **intf) { return SUCCESS; } +EFI_STATUS efi_dt_fixup(struct EfiDtFixupProtocol *self, void *fdt, + size_t *buffer_size, uint32_t flags) { + auto offset = fdt_subnode_offset(fdt, 0, "chosen"); + if (offset < 0) { + printf("Failed to find chosen node %d\n", offset); + return SUCCESS; + } + int length = 0; + auto prop = fdt_get_property(fdt, offset, "bootargs", &length); + + if (prop == nullptr) { + printf("Failed to find chosen/bootargs prop\n"); + return SUCCESS; + } + char *new_prop_data = reinterpret_cast(malloc(length)); + DEFER { + free(new_prop_data); + new_prop_data = nullptr; + }; + auto prop_length = strnlen(prop->data, length); + static constexpr auto &&to_add = + "console=ttyAMA0 earlycon=pl011,mmio32,0x9000000 "; + memset(new_prop_data, 0, length); + memcpy(new_prop_data, to_add, sizeof(to_add) - 1); + memcpy(new_prop_data + sizeof(to_add) - 1, prop->data, prop_length); + auto ret = fdt_setprop(fdt, offset, "bootargs", new_prop_data, length); + + printf("chosen/bootargs: %d %d \"%s\"\n", ret, length, new_prop_data); + + return SUCCESS; +} + +// 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) { + printf("%s(0x%lx, \"%s\")\n", __FUNCTION__, self, command_line); + *fixup_buffer_size = 0; + return SUCCESS; +} + +// Generates fixups for the bootconfig built by GBL. +EfiStatus fixup_bootconfig(struct GblEfiOsConfigurationProtocol *self, + const char *bootconfig, size_t size, char *fixup, + size_t *fixup_buffer_size) { + printf("%s(0x%lx, %s, %lu, %lu)\n", __FUNCTION__, self, bootconfig, size, + *fixup_buffer_size); + constexpr auto &&to_add = "\nandroidboot.fstab_suffix=cf.f2fs." + "hctr2\nandroidboot.boot_devices=4010000000.pcie"; + const auto final_len = sizeof(to_add); + if (final_len > *fixup_buffer_size) { + *fixup_buffer_size = final_len; + return OUT_OF_RESOURCES; + } + *fixup_buffer_size = final_len; + memcpy(fixup, to_add, final_len); + + return SUCCESS; +} + +// Selects which device trees and overlays to use from those loaded by GBL. +EfiStatus select_device_trees(struct GblEfiOsConfigurationProtocol *self, + GblEfiVerifiedDeviceTree *device_trees, + size_t num_device_trees) { + printf("%s(0x%lx, %lx, %lu)\n", __FUNCTION__, self, device_trees, + num_device_trees); + return UNSUPPORTED; +} + EfiStatus open_protocol(EfiHandle handle, const EfiGuid *protocol, void **intf, EfiHandle agent_handle, EfiHandle controller_handle, EfiOpenProtocolAttributes attr) { @@ -494,6 +567,39 @@ EfiStatus open_protocol(EfiHandle handle, const EfiGuid *protocol, void **intf, "controller_handle=0x%lx, attr=0x%x)\n", __FUNCTION__, handle, agent_handle, controller_handle, attr); return UNSUPPORTED; + } else if (guid_eq(protocol, EFI_DT_FIXUP_PROTOCOL_GUID)) { + printf("%s(EFI_DT_FIXUP_PROTOCOL_GUID, handle=0x%lx, agent_handle=0x%lx, " + "controller_handle=0x%lx, attr=0x%x)\n", + __FUNCTION__, handle, agent_handle, controller_handle, attr); + if (intf != nullptr) { + EfiDtFixupProtocol *fixup = nullptr; + allocate_pool(BOOT_SERVICES_DATA, sizeof(EfiDtFixupProtocol), + reinterpret_cast(&fixup)); + if (fixup == nullptr) { + return OUT_OF_RESOURCES; + } + fixup->revision = EFI_DT_FIXUP_PROTOCOL_REVISION; + fixup->fixup = efi_dt_fixup; + *intf = reinterpret_cast(fixup); + } + return SUCCESS; + } else if (guid_eq(protocol, EFI_GBL_OS_CONFIGURATION_PROTOCOL_GUID)) { + printf("%s(EFI_GBL_OS_CONFIGURATION_PROTOCOL_GUID, handle=0x%lx, " + "agent_handle=0x%lx, " + "controller_handle=0x%lx, attr=0x%x)\n", + __FUNCTION__, handle, agent_handle, controller_handle, attr); + GblEfiOsConfigurationProtocol *config = nullptr; + allocate_pool(BOOT_SERVICES_DATA, sizeof(*config), + reinterpret_cast(&config)); + if (config == nullptr) { + return OUT_OF_RESOURCES; + } + 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; } printf("%s is unsupported 0x%x 0x%x 0x%x 0x%llx\n", __FUNCTION__, protocol->data1, protocol->data2, protocol->data3, @@ -519,6 +625,11 @@ EfiStatus close_protocol(EfiHandle handle, const EfiGuid *protocol, "controller_handle=0x%lx)\n", __FUNCTION__, handle, agent_handle, controller_handle); return SUCCESS; + } else if (guid_eq(protocol, EFI_DT_FIXUP_PROTOCOL_GUID)) { + printf("%s(EFI_DT_FIXUP_PROTOCOL_GUID, handle=0x%lx, agent_handle=0x%lx, " + "controller_handle=0x%lx)\n", + __FUNCTION__, handle, agent_handle, controller_handle); + return SUCCESS; } printf("%s is called\n", __FUNCTION__); return UNSUPPORTED; @@ -557,6 +668,29 @@ EfiStatus locate_handle_buffer(EfiLocateHandleSearchType search_type, printf("%s(0x%x, EFI_TEXT_INPUT_PROTOCOL_GUID, search_key=0x%lx)\n", __FUNCTION__, search_type, search_key); return NOT_FOUND; + } else if (guid_eq(protocol, EFI_GBL_OS_CONFIGURATION_PROTOCOL_GUID)) { + printf( + "%s(0x%x, EFI_GBL_OS_CONFIGURATION_PROTOCOL_GUID, search_key=0x%lx)\n", + __FUNCTION__, search_type, search_key); + if (num_handles != nullptr) { + *num_handles = 1; + } + if (buf != nullptr) { + *buf = reinterpret_cast( + mspace_malloc(get_mspace(), sizeof(buf))); + } + return SUCCESS; + } else if (guid_eq(protocol, EFI_DT_FIXUP_PROTOCOL_GUID)) { + printf("%s(0x%x, EFI_DT_FIXUP_PROTOCOL_GUID, search_key=0x%lx)\n", + __FUNCTION__, search_type, search_key); + if (num_handles != nullptr) { + *num_handles = 1; + } + if (buf != nullptr) { + *buf = reinterpret_cast( + mspace_malloc(get_mspace(), sizeof(buf))); + } + return SUCCESS; } printf("%s(0x%x, (0x%x 0x%x 0x%x 0x%llx), search_key=0x%lx)\n", __FUNCTION__, search_type, protocol->data1, protocol->data2, protocol->data3, @@ -564,6 +698,10 @@ EfiStatus locate_handle_buffer(EfiLocateHandleSearchType search_type, return UNSUPPORTED; } +EfiStatus wait_for_event(size_t num_events, EfiEvent *event, size_t *index) { + return UNSUPPORTED; +} + } // namespace void setup_boot_service_table(EfiBootService *service) { @@ -589,4 +727,5 @@ 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; } diff --git a/lib/uefi/boot_service_provider.h b/lib/uefi/boot_service_provider.h index 49c710c1e..d89d78f94 100644 --- a/lib/uefi/boot_service_provider.h +++ b/lib/uefi/boot_service_provider.h @@ -76,6 +76,18 @@ static constexpr auto EFI_TEXT_INPUT_PROTOCOL_GUID = 0x11d2, {0x8e, 0x39, 0x00, 0xa0, 0xc9, 0x69, 0x72, 0x3b}}; +static constexpr auto EFI_GBL_OS_CONFIGURATION_PROTOCOL_GUID = + EfiGuid{0xdda0d135, + 0xaa5b, + 0x42ff, + {0x85, 0xac, 0xe3, 0xad, 0x6e, 0xfb, 0x46, 0x19}}; + +static constexpr auto EFI_DT_FIXUP_PROTOCOL_GUID = + EfiGuid{0xe617d64c, + 0xfe08, + 0x46da, + {0xf4, 0xdc, 0xbb, 0xd5, 0x87, 0x0c, 0x73, 0x00}}; + using EFI_IMAGE_UNLOAD = EfiStatus (*)(EfiHandle); //****************************************************** diff --git a/lib/uefi/local/include/protocols/dt_fixup_protocol.h b/lib/uefi/local/include/protocols/dt_fixup_protocol.h new file mode 100644 index 000000000..ccf7a5bc3 --- /dev/null +++ b/lib/uefi/local/include/protocols/dt_fixup_protocol.h @@ -0,0 +1,45 @@ +/* + * 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. + * + */ + +// This is a protocol proposed by U-boot and being used by Kernel UEFI stub. +// https://github.com/U-Boot-EFI/EFI_DT_FIXUP_PROTOCOL +// https://github.com/u-boot/u-boot/blob/master/include/efi_dt_fixup.h + +#ifndef __EFI_DT_FIXUP_PROTOCOL_H__ +#define __EFI_DT_FIXUP_PROTOCOL_H__ + +#include "types.h" + +constexpr uint64_t EFI_DT_FIXUP_PROTOCOL_REVISION = 0x00010000; + +// Add nodes and update properties +constexpr uint32_t EFI_DT_APPLY_FIXUPS = 0x00000001; +// Reserve memory according to the /reserved-memory node and the memory +// reservation block +constexpr uint32_t EFI_DT_RESERVE_MEMORY = 0x00000002; +// Install the device-tree as configuration table +constexpr uint32_t EFI_DT_INSTALL_TABLE = 0x00000004; +constexpr uint32_t EFI_DT_ALL = + EFI_DT_APPLY_FIXUPS | EFI_DT_RESERVE_MEMORY | EFI_DT_INSTALL_TABLE; + +typedef struct EfiDtFixupProtocol { + uint64_t revision; + EfiStatus (*fixup)(struct EfiDtFixupProtocol *self, void *fdt, + size_t *buffer_size, uint32_t flags); +} EfiDtFixupProtocol; + +#endif // __EFI_DT_FIXUP_PROTOCOL_H__ diff --git a/lib/uefi/local/include/protocols/gbl_efi_os_configuration_protocol.h b/lib/uefi/local/include/protocols/gbl_efi_os_configuration_protocol.h new file mode 100644 index 000000000..8cbc6f607 --- /dev/null +++ b/lib/uefi/local/include/protocols/gbl_efi_os_configuration_protocol.h @@ -0,0 +1,80 @@ +/* + * 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. + * + */ + +// This is a custom protocol introduced by GBL. +// See gbl/docs/gbl_efi_os_configuration_protocol.md for details. + +#ifndef __GBL_OS_CONFIGURATION_PROTOCOL_H__ +#define __GBL_OS_CONFIGURATION_PROTOCOL_H__ + +#include "types.h" + +static constexpr size_t GBL_EFI_OS_CONFIGURATION_PROTOCOL_REVISION = 0x00000000; + +typedef enum GBL_EFI_DEVICE_TREE_SOURCE { + BOOT, + VENDOR_BOOT, + DTBO, + DTB +} GblEfiDeviceTreeSource; + +typedef struct { + // GblDeviceTreeSource + uint32_t source; + // 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 { + GblEfiDeviceTreeMetadata metadata; + // Base device tree / overlay buffer (guaranteed to be 8-bytes aligned), + // cannot be NULL. Device tree size can be identified by the header totalsize + // field + const void *device_tree; + // Indicates whether this device tree (or overlay) must be included in the + // final device tree. Set to true by a FW if this component must be used + bool selected; +} GblEfiVerifiedDeviceTree; + +// Warning: API is UNSTABLE +// Documentation: +// https://cs.android.com/android/platform/superproject/main/+/main: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, + char *fixup, size_t *fixup_buffer_size); + + // Selects which device trees and overlays to use from those loaded by GBL. + EfiStatus (*select_device_trees)(struct GblEfiOsConfigurationProtocol *self, + GblEfiVerifiedDeviceTree *device_trees, + size_t num_device_trees); +} GblEfiOsConfigurationProtocol; + +#endif //__GBL_OS_CONFIGURATION_PROTOCOL_H__ \ No newline at end of file diff --git a/lib/uefi/uefi.cpp b/lib/uefi/uefi.cpp index 1728d018a..604a3d1f2 100644 --- a/lib/uefi/uefi.cpp +++ b/lib/uefi/uefi.cpp @@ -299,6 +299,8 @@ int load_sections_and_execute(bdev_t *dev, constexpr size_t kStackSize = 8 * 1024ul * 1024; auto stack = reinterpret_cast(alloc_page(kStackSize, 23)); memset(stack, 0, kStackSize); + printf("Calling kernel with stack [0x%lx, 0x%lx]\n", stack, + stack + kStackSize - 1); return call_with_stack(stack + kStackSize, entry, image_base, &table); }