From b33f350a3fdb597b595b395a5334bed5ad4c6dc4 Mon Sep 17 00:00:00 2001 From: "Ying-Chun Liu (PaulLiu)" Date: Mon, 6 Oct 2025 04:47:56 +0100 Subject: [PATCH] [lib][uefi] implement UEFI HII Database Protocol Shell.efi from EDK2 requires this protocol to be existed. Otherwise it crashes. So we implemented a minimum set of UEFI HII Database Protocol to let the Shell.efi to run. Signed-off-by: Ying-Chun Liu (PaulLiu) --- lib/uefi/boot_service_provider.cpp | 10 + lib/uefi/boot_service_provider.h | 6 + lib/uefi/hii_protocol.cpp | 268 ++++++++++++++++++ .../include/uefi/protocols/hii_protocol.h | 106 +++++++ lib/uefi/rules.mk | 1 + lib/uefi/uefi_platform.h | 3 + 6 files changed, 394 insertions(+) create mode 100644 lib/uefi/hii_protocol.cpp create mode 100644 lib/uefi/include/uefi/protocols/hii_protocol.h diff --git a/lib/uefi/boot_service_provider.cpp b/lib/uefi/boot_service_provider.cpp index c5b435a55e..7d2a6a35c5 100644 --- a/lib/uefi/boot_service_provider.cpp +++ b/lib/uefi/boot_service_provider.cpp @@ -28,6 +28,7 @@ #include #include #include +#include #include #include "blockio2_protocols.h" @@ -104,6 +105,15 @@ EfiStatus locate_protocol(const EfiGuid *protocol, void *registration, printf("%s(EFI_TCG2_PROTOCOL_GUID) is unsupported.\n", __FUNCTION__); return EFI_STATUS_NOT_FOUND; } + if (guid_eq(protocol, EFI_HII_DATABASE_PROTOCOL_GUID)) { + printf("%s(EFI_HII_DATABASE_PROTOCOL_GUID) is supported.\n", + __FUNCTION__); + *intf = open_hii_database_protocol(); + if (*intf == nullptr) { + return EFI_STATUS_OUT_OF_RESOURCES; + } + return EFI_STATUS_SUCCESS; + } printf("%s(%x %x %x %llx) is unsupported\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 94058c20c3..bdfedf883f 100644 --- a/lib/uefi/boot_service_provider.h +++ b/lib/uefi/boot_service_provider.h @@ -134,6 +134,12 @@ static constexpr auto EFI_ERASE_BLOCK_PROTOCOL_GUID = 0x4926, {0xaa, 0xef, 0x99, 0x18, 0xe7, 0x72, 0xd9, 0x87}}; +static constexpr auto EFI_HII_DATABASE_PROTOCOL_GUID = + EfiGuid{0xEF9FC172, + 0xA1B2, + 0x4693, + {0xB3, 0x27, 0x6D, 0x32, 0xFC, 0x41, 0x60, 0x42}}; + // This function would be called from GBL before jumping into android kernel // LK provides a default no-op implementation that is weakly linked, // different platforms can override with their own implementation. diff --git a/lib/uefi/hii_protocol.cpp b/lib/uefi/hii_protocol.cpp new file mode 100644 index 0000000000..d411c0c519 --- /dev/null +++ b/lib/uefi/hii_protocol.cpp @@ -0,0 +1,268 @@ +/* + * 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 +#include +#include + +namespace { + const int EFI_HII_PACKAGE_TYPE_SHIFT = 24; + const uint32_t EFI_HII_PACKAGE_TYPE_MASK = 0xff; + const int EFI_HII_PACKAGE_LEN_SHIFT = 0; + const uint32_t EFI_HII_PACKAGE_LEN_MASK = 0xffffff; + + struct efi_string_info { + uint16_t *string; + }; + + struct efi_string_table { + struct list_node node; + uint16_t language_name; + char *language; + uint32_t nstrings; + struct efi_string_info *strings; + }; + + struct efi_hii_packagelist { + struct list_node node; + EfiHandle driver_handle; + uint32_t max_string_id; + struct list_node string_tables; /* list of efi_string_table */ + }; + + list_node efi_package_lists = LIST_INITIAL_VALUE(efi_package_lists); + + static uint32_t efi_hii_package_type(const EfiHiiPackageHeader *header) { + uint32_t fields; + + fields = header->fields; + + return (fields >> EFI_HII_PACKAGE_TYPE_SHIFT) & EFI_HII_PACKAGE_TYPE_MASK; + } + + static uint32_t efi_hii_package_len(const EfiHiiPackageHeader *header) { + uint32_t fields; + + fields = header->fields; + + return (fields >> EFI_HII_PACKAGE_LEN_SHIFT) & EFI_HII_PACKAGE_LEN_MASK; + } + + struct efi_hii_packagelist *new_packagelist(void) { + struct efi_hii_packagelist *hii; + + hii = reinterpret_cast(malloc(sizeof(struct efi_hii_packagelist))); + if (!hii) + return nullptr; + memset(hii, 0, sizeof(struct efi_hii_packagelist)); + hii->node = LIST_INITIAL_CLEARED_VALUE; + list_add_tail(&efi_package_lists, &hii->node); + hii->string_tables = LIST_INITIAL_VALUE(hii->string_tables); + + return hii; + } + + static void free_strings_table(struct efi_string_table *stbl) { + for (uint32_t i = 0; i < stbl->nstrings; i++) + free(stbl->strings[i].string); + free(stbl->strings); + free(stbl->language); + free(stbl); + } + + static void remove_strings_package(struct efi_hii_packagelist *hii) { + while (true) { + struct efi_string_table *stbl; + + stbl = reinterpret_cast(list_remove_head(&(hii->string_tables))); + if (!stbl) { + break; + } + + free_strings_table(stbl); + } + } + + static void free_packagelist(struct efi_hii_packagelist *hii) { + remove_strings_package(hii); + + list_delete(&hii->node); + free(hii); + } + + EfiStatus add_packages(struct efi_hii_packagelist *hii, + const struct EfiHiiPackageListHeader *package_list) { + struct EfiHiiPackageHeader *package; + void *end; + EfiStatus ret = EFI_STATUS_SUCCESS; + + end = ((char *)package_list) + package_list->package_length; + + package = reinterpret_cast(((char *)package_list) + sizeof(*package_list)); + while ((void *)package < end) { + switch (efi_hii_package_type(package)) { + case EFI_HII_PACKAGE_END: + goto out; + default: + break; + } + + package = reinterpret_cast((char *)package + efi_hii_package_len(package)); + } + out: + return ret; + } + + static bool efi_hii_packagelist_exists(EfiHiiHandle package_list) { + struct efi_hii_packagelist *hii = nullptr; + bool found = false; + list_for_every_entry(&efi_package_lists, hii, struct efi_hii_packagelist, node) { + if (hii == package_list) { + found = true; + break; + } + } + + return found; + } + + EfiStatus new_package_list(EfiHiiDatabaseProtocol *self, + const struct EfiHiiPackageListHeader *package_list, + const EfiHandle driver_handle, + EfiHiiHandle *handle) { + struct efi_hii_packagelist *hii; + EfiStatus ret; + + if (!package_list || !handle) + return EFI_STATUS_INVALID_PARAMETER; + + hii = new_packagelist(); + if (!hii) + return EFI_STATUS_OUT_OF_RESOURCES; + + ret = add_packages(hii, package_list); + if (ret != EFI_STATUS_SUCCESS) { + free_packagelist(hii); + return ret; + } + + hii->driver_handle = driver_handle; + *handle = hii; + + return EFI_STATUS_SUCCESS; + } + + EfiStatus remove_package_list(struct EfiHiiDatabaseProtocol *self, + EfiHiiHandle handle) { + struct efi_hii_packagelist *hii = reinterpret_cast(handle); + + if (!handle || !efi_hii_packagelist_exists(handle)) + return EFI_STATUS_NOT_FOUND; + + free_packagelist(hii); + + return EFI_STATUS_SUCCESS; + } + + EfiStatus update_package_list(struct EfiHiiDatabaseProtocol *self, + EfiHiiHandle handle, + const struct EfiHiiPackageListHeader *package_list) { + printf("UEFI: Hii: update_package_list is not supported\n"); + return EFI_STATUS_UNSUPPORTED; + } + + EfiStatus list_package_lists(struct EfiHiiDatabaseProtocol *self, + uint8_t package_type, + const EfiGuid *package_guid, + size_t *buffer_size, + EfiHiiHandle *handle) { + printf("UEFI: Hii: list_package_lists is not supported\n"); + return EFI_STATUS_UNSUPPORTED; + } + + EfiStatus export_package_lists(struct EfiHiiDatabaseProtocol *self, + EfiHiiHandle handle, + size_t *buffer_size, + struct EfiHiiPackageListHeader *buffer) { + printf("UEFI: Hii: export_package_lists is not supported\n"); + return EFI_STATUS_UNSUPPORTED; + } + + EfiStatus register_package_notify(struct EfiHiiDatabaseProtocol *self, + uint8_t package_type, + const EfiGuid *package_guid, + const void *package_notify_fn, + size_t notify_type, + EfiHandle *notify_handle) { + printf("UEFI: Hii: register_package_notify is not supported\n"); + return EFI_STATUS_UNSUPPORTED; + } + + EfiStatus unregister_package_notify(struct EfiHiiDatabaseProtocol *self, + EfiHandle notification_handle) { + printf("UEFI: Hii: unregister_package_notify is not supported\n"); + return EFI_STATUS_UNSUPPORTED; + } + + EfiStatus find_keyboard_layouts(struct EfiHiiDatabaseProtocol *self, + uint16_t *key_guid_buffer_length, + EfiGuid *key_guid_buffer) { + printf("UEFI: Hii: find_keyboard_layouts is not supported\n"); + return EFI_STATUS_UNSUPPORTED; + } + + EfiStatus get_keyboard_layout(struct EfiHiiDatabaseProtocol *self, + EfiGuid *key_guid, + uint16_t *keyboard_layout_length, + struct EfiHiiKeyboardLayout *keyboard_layout) { + printf("UEFI: Hii: get_keyboard_layout is not supported\n"); + return EFI_STATUS_UNSUPPORTED; + } + + EfiStatus set_keyboard_layout(struct EfiHiiDatabaseProtocol *self, + EfiGuid *key_guid) { + printf("UEFI: Hii: set_keyboard_layout is not supported"); + return EFI_STATUS_UNSUPPORTED; + } + + EfiStatus get_package_list_handle(struct EfiHiiDatabaseProtocol *self, + EfiHiiHandle package_list_handle, + EfiHandle *driver_handle) { + printf("UEFI: Hii: get_package_list_handle is not supported\n"); + return EFI_STATUS_UNSUPPORTED; + } + +} + +__WEAK EfiHiiDatabaseProtocol* open_hii_database_protocol() { + static EfiHiiDatabaseProtocol protocol = { + .new_package_list = new_package_list, + .remove_package_list = remove_package_list, + .update_package_list = update_package_list, + .list_package_lists = list_package_lists, + .export_package_lists = export_package_lists, + .register_package_notify = register_package_notify, + .unregister_package_notify = unregister_package_notify, + .find_keyboard_layouts = find_keyboard_layouts, + .get_keyboard_layout = get_keyboard_layout, + .set_keyboard_layout = set_keyboard_layout, + .get_package_list_handle = get_package_list_handle + }; + return &protocol; +} diff --git a/lib/uefi/include/uefi/protocols/hii_protocol.h b/lib/uefi/include/uefi/protocols/hii_protocol.h new file mode 100644 index 0000000000..8d95ff9f75 --- /dev/null +++ b/lib/uefi/include/uefi/protocols/hii_protocol.h @@ -0,0 +1,106 @@ +/* + * 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. + * + * SPDX-License-Identifier: Apache-2.0 OR BSD-2-Clause-Patent + * + * You may choose to use or redistribute this file under + * (a) the Apache License, Version 2.0, or + * (b) the BSD 2-Clause Patent license. + * + * Unless you expressly elect the BSD-2-Clause-Patent terms, the Apache-2.0 + * terms apply by default. + */ + +#ifndef __HII_PROTOCOL_H__ +#define __HII_PROTOCOL_H__ + +#include +#include +#include + +#define EFI_HII_PACKAGE_TYPE_ALL 0x00 +#define EFI_HII_PACKAGE_TYPE_GUID 0x01 +#define EFI_HII_PACKAGE_FORMS 0x02 +#define EFI_HII_PACKAGE_STRINGS 0x04 +#define EFI_HII_PACKAGE_FONTS 0x05 +#define EFI_HII_PACKAGE_IMAGES 0x06 +#define EFI_HII_PACKAGE_SIMPLE_FONTS 0x07 +#define EFI_HII_PACKAGE_DEVICE_PATH 0x08 +#define EFI_HII_PACKAGE_KEYBOARD_LAYOUT 0x09 +#define EFI_HII_PACKAGE_ANIMATIONS 0x0A +#define EFI_HII_PACKAGE_END 0xDF +#define EFI_HII_PACKAGE_TYPE_SYSTEM_BEGIN 0xE0 +#define EFI_HII_PACKAGE_TYPE_SYSTEM_END 0xFF + +typedef void *EfiHiiHandle; + +struct EfiHiiPackageListHeader { + EfiGuid package_list_guid; + uint32_t package_length; +} __attribute__((packed)); + +struct EfiHiiPackageHeader { + uint32_t fields; +} __attribute__((packed)); + +struct EfiHiiKeyboardLayout { + uint16_t layout_length; + uint8_t guid[16]; + uint32_t layout_descriptor_string_offset; + uint8_t descriptor_count; +} __attribute__((packed)) ; + +typedef struct EfiHiiDatabaseProtocol { + EfiStatus (*new_package_list)(struct EfiHiiDatabaseProtocol *self, + /* in */ const struct EfiHiiPackageListHeader *package_list, + /* in */ const EfiHandle driver_handle, + /* out */ EfiHiiHandle *handle); + EfiStatus (*remove_package_list)(struct EfiHiiDatabaseProtocol *self, + /* in */ EfiHiiHandle handle); + EfiStatus (*update_package_list)(struct EfiHiiDatabaseProtocol *self, + /* in */ EfiHiiHandle handle, + /* in */ const struct EfiHiiPackageListHeader *package_list); + EfiStatus (*list_package_lists)(struct EfiHiiDatabaseProtocol *self, + /* in */ uint8_t package_type, + /* in */ const EfiGuid *package_guid, + /* in/out */ size_t *buffer_size, + /* out */ EfiHiiHandle *handle); + EfiStatus (*export_package_lists)(struct EfiHiiDatabaseProtocol *self, + /* in */ EfiHiiHandle handle, + /* in/out */ size_t *buffer_size, + /* out */ struct EfiHiiPackageListHeader *buffer); + EfiStatus (*register_package_notify)(struct EfiHiiDatabaseProtocol *self, + /* in */ uint8_t package_type, + /* in */ const EfiGuid *package_guid, + /* in */ const void *package_notify_fn, + /* in */ size_t notify_type, + /* out */ EfiHandle *notify_handle); + EfiStatus (*unregister_package_notify)(struct EfiHiiDatabaseProtocol *self, + /* in */ EfiHandle notification_handle); + EfiStatus(*find_keyboard_layouts)(struct EfiHiiDatabaseProtocol *self, + /* in/out */ uint16_t *key_guid_buffer_length, + /* out */ EfiGuid *key_guid_buffer); + EfiStatus(*get_keyboard_layout)(struct EfiHiiDatabaseProtocol *self, + /* in */ EfiGuid *key_guid, + /* in/out */ uint16_t *keyboard_layout_length, + /* out */ struct EfiHiiKeyboardLayout *keyboard_layout); + EfiStatus(*set_keyboard_layout)(struct EfiHiiDatabaseProtocol *self, + /* in */ EfiGuid *key_guid); + EfiStatus(*get_package_list_handle)(struct EfiHiiDatabaseProtocol *self, + /* in */ EfiHiiHandle package_list_handle, + /* out */ EfiHandle *driver_handle); +} EfiHiiDatabaseProtocol; + +#endif // __HII_PROTOCOL_H__ diff --git a/lib/uefi/rules.mk b/lib/uefi/rules.mk index 8ff0ebb3a7..83eb09e3ed 100644 --- a/lib/uefi/rules.mk +++ b/lib/uefi/rules.mk @@ -29,6 +29,7 @@ MODULE_SRCS += \ $(LOCAL_DIR)/debug_support.cpp \ $(LOCAL_DIR)/charset.cpp \ $(LOCAL_DIR)/variable_mem.cpp \ + $(LOCAL_DIR)/hii_protocol.cpp \ include make/module.mk diff --git a/lib/uefi/uefi_platform.h b/lib/uefi/uefi_platform.h index 621c70edcc..6c8370a823 100644 --- a/lib/uefi/uefi_platform.h +++ b/lib/uefi/uefi_platform.h @@ -23,6 +23,7 @@ #include #include #include +#include #include #include @@ -80,4 +81,6 @@ EfiStatus open_efi_erase_block_protocol(EfiHandle handle, void** intf); GblEfiBootMemoryProtocol* open_boot_memory_protocol(); +EfiHiiDatabaseProtocol* open_hii_database_protocol(); + #endif