[U-Boot] [PATCH v1 04/12] efi_loader: start fleshing out HII

Rob Clark
2017-09-10T13:22:25Z

Not complete but enough for Shell.efi and SCT.efi.

Signed-off-by: Rob Clark <robdclark@gmail.com>
---
include/efi_api.h | 68 +++++++++++-
lib/efi_loader/efi_hii.c | 274 ++++++++++++++++++++++++++++++++++++++++-------
2 files changed, 304 insertions(+), 38 deletions(-)

diff --git a/include/efi_api.h b/include/efi_api.h
index 25f774f253..4853b71497 100644
--- a/include/efi_api.h
+++ b/include/efi_api.h
@@ -16,6 +16,7 @@
#define _EFI_API_H

#include <efi.h>
+#include <charset.h>

#ifdef CONFIG_EFI_LOADER
#include <asm/setjmp.h>
@@ -540,6 +541,8 @@ struct efi_device_path_utilities_protocol
EFI_GUID(0x587e72d7, 0xcc50, 0x4f79, \
0x82, 0x09, 0xca, 0x29, 0x1f, 0xc1, 0xa1, 0x0f)

+typedef uint16_t efi_string_id_t;
+
struct efi_hii_config_routing_protocol
{
efi_status_t(EFIAPI *extract_config)(
@@ -630,7 +633,69 @@ struct efi_hii_package_list_header
{
efi_guid_t package_list_guid;
uint32_t package_length;
-};
+} __packed;
+
+struct efi_hii_package_header {
+ uint32_t length : 24;
+ uint32_t type : 8;
+} __packed;
+
+#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
+
+struct efi_hii_strings_package {
+ struct efi_hii_package_header header;
+ uint32_t header_size;
+ uint32_t string_info_offset;
+ uint16_t language_window[16];
+ efi_string_id_t language_name;
+ uint8_t language[];
+} __packed;
+
+struct efi_hii_string_block {
+ uint8_t block_type;
+ /*uint8_t block_body[];*/
+} __packed;
+
+#define EFI_HII_SIBT_END 0x00 // The end of the string information.
+#define EFI_HII_SIBT_STRING_SCSU 0x10 // Single string using default font information.
+#define EFI_HII_SIBT_STRING_SCSU_FONT 0x11 // Single string with font information.
+#define EFI_HII_SIBT_STRINGS_SCSU 0x12 // Multiple strings using default font information.
+#define EFI_HII_SIBT_STRINGS_SCSU_FONT 0x13 // Multiple strings with font information.
+#define EFI_HII_SIBT_STRING_UCS2 0x14 // Single UCS-2 string using default font information.
+#define EFI_HII_SIBT_STRING_UCS2_FONT 0x15 // Single UCS-2 string with font information
+#define EFI_HII_SIBT_STRINGS_UCS2 0x16 // Multiple UCS-2 strings using default font information.
+#define EFI_HII_SIBT_STRINGS_UCS2_FONT 0x17 // Multiple UCS-2 strings with font information.
+#define EFI_HII_SIBT_DUPLICATE 0x20 // Create a duplicate of an existing string.
+#define EFI_HII_SIBT_SKIP2 0x21 // Skip a certain number of string identifiers.
+#define EFI_HII_SIBT_SKIP1 0x22 // Skip a certain number of string identifiers.
+#define EFI_HII_SIBT_EXT1 0x30 // For future expansion (one byte length field)
+#define EFI_HII_SIBT_EXT2 0x31 // For future expansion (two byte length field)
+#define EFI_HII_SIBT_EXT4 0x32 // For future expansion (four byte length field)
+#define EFI_HII_SIBT_FONT 0x40 // Font information.
+
+struct efi_hii_sibt_string_ucs2_block {
+ struct efi_hii_string_block header;
+ uint16_t string_text[];
+} __packed;
+
+static inline struct efi_hii_string_block *efi_hii_sibt_string_ucs2_block_next(
+ struct efi_hii_sibt_string_ucs2_block *blk)
+{
+ return ((void *)blk) + sizeof(*blk) +
+ (utf16_strlen(blk->string_text) + 1) * 2;
+}

typedef void *efi_hii_handle_t;

@@ -693,7 +758,6 @@ struct efi_hii_database_protocol
0xb9, 0xcb, 0x98, 0xd1, 0x77, 0x50, 0x32, 0x2a)

typedef uint32_t efi_hii_font_style_t;
-typedef uint16_t efi_string_id_t;

struct efi_font_info
{
diff --git a/lib/efi_loader/efi_hii.c b/lib/efi_loader/efi_hii.c
index cc68a28d2b..d818f409dc 100644
--- a/lib/efi_loader/efi_hii.c
+++ b/lib/efi_loader/efi_hii.c
@@ -7,6 +7,7 @@
*/

#include <common.h>
+#include <malloc.h>
#include <efi_loader.h>

const efi_guid_t efi_guid_hii_config_routing_protocol =
@@ -14,12 +15,128 @@ const efi_guid_t efi_guid_hii_config_routing_protocol =
const efi_guid_t efi_guid_hii_database_protocol = EFI_HII_DATABASE_PROTOCOL_GUID;
const efi_guid_t efi_guid_hii_string_protocol = EFI_HII_STRING_PROTOCOL_GUID;

+struct hii_package {
+ // TODO should there be an associated efi_object?
+ struct list_head string_tables; /* list of string_table */
+ /* we could also track fonts, images, etc */
+};
+
+struct string_table {
+ struct list_head link;
+ efi_string_id_t language_name;
+ char *language;
+ uint32_t nstrings;
+ /* NOTE: string id starts at 1 so value is stbl->strings[id-1] */
+ struct {
+ efi_string_t string;
+ /* we could also track font info, etc */
+ } strings[];
+};
+
+static void free_strings_table(struct string_table *stbl)
+{
+ for (int i = 0; i < stbl->nstrings; i++)
+ free(stbl->strings[i].string);
+ free(stbl->language);
+ free(stbl);
+}
+
+static struct hii_package *new_package(void)
+{
+ struct hii_package *hii = malloc(sizeof(*hii));
+ INIT_LIST_HEAD(&hii->string_tables);
+ return hii;
+}
+
+static void free_package(struct hii_package *hii)
+{
+
+ while (!list_empty(&hii->string_tables)) {
+ struct string_table *stbl;
+
+ stbl = list_first_entry(&hii->string_tables,
+ struct string_table, link);
+ list_del(&stbl->link);
+ free_strings_table(stbl);
+ }
+
+ free(hii);
+}
+
+static efi_status_t add_strings_package(struct hii_package *hii,
+ struct efi_hii_strings_package *strings_package)
+{
+ struct efi_hii_string_block *block;
+ void *end = ((void *)strings_package) + strings_package->header.length;
+ uint32_t nstrings = 0;
+ unsigned id = 0;
+
+ debug("header_size: %08x\n", strings_package->header_size);
+ debug("string_info_offset: %08x\n", strings_package->string_info_offset);
+ debug("language_name: %u\n", strings_package->language_name);
+ debug("language: %s\n", strings_package->language);
+
+ /* count # of string entries: */
+ block = ((void *)strings_package) + strings_package->string_info_offset;
+ while ((void *)block < end) {
+ switch (block->block_type) {
+ case EFI_HII_SIBT_STRING_UCS2: {
+ struct efi_hii_sibt_string_ucs2_block *ucs2 =
+ (void *)block;
+ nstrings++;
+ block = efi_hii_sibt_string_ucs2_block_next(ucs2);
+ break;
+ }
+ case EFI_HII_SIBT_END:
+ block = end;
+ break;
+ default:
+ debug("unknown HII string block type: %02x\n",
+ block->block_type);
+ return EFI_INVALID_PARAMETER;
+ }
+ }
+
+ struct string_table *stbl = malloc(sizeof(*stbl) +
+ (nstrings * sizeof(stbl->strings[0])));
+ stbl->language_name = strings_package->language_name;
+ stbl->language = strdup((char *)strings_package->language);
+ stbl->nstrings = nstrings;
+
+ list_add(&stbl->link, &hii->string_tables);
+
+ /* and now parse string entries and populate string_table */
+ block = ((void *)strings_package) + strings_package->string_info_offset;
+
+ while ((void *)block < end) {
+ switch (block->block_type) {
+ case EFI_HII_SIBT_STRING_UCS2: {
+ struct efi_hii_sibt_string_ucs2_block *ucs2 =
+ (void *)block;
+ id++;
+ debug("%4u: \"%ls\"\n", id, ucs2->string_text);
+ stbl->strings[id-1].string =
+ utf16_strdup(ucs2->string_text);
+ block = efi_hii_sibt_string_ucs2_block_next(ucs2);
+ break;
+ }
+ case EFI_HII_SIBT_END:
+ return EFI_SUCCESS;
+ default:
+ debug("unknown HII string block type: %02x\n",
+ block->block_type);
+ return EFI_INVALID_PARAMETER;
+ }
+ }
+
+ return EFI_SUCCESS;
+}

/*
* EFI_HII_CONFIG_ROUTING_PROTOCOL
*/

-static efi_status_t extract_config(
+static efi_status_t EFIAPI extract_config(
const struct efi_hii_config_routing_protocol *this,
const efi_string_t request,
efi_string_t *progress,
@@ -29,7 +146,7 @@ static efi_status_t extract_config(
return EFI_EXIT(EFI_OUT_OF_RESOURCES);
}

-static efi_status_t export_config(
+static efi_status_t EFIAPI export_config(
const struct efi_hii_config_routing_protocol *this,
efi_string_t *results)
{
@@ -37,7 +154,7 @@ static efi_status_t export_config(
return EFI_EXIT(EFI_OUT_OF_RESOURCES);
}

-static efi_status_t route_config(
+static efi_status_t EFIAPI route_config(
const struct efi_hii_config_routing_protocol *this,
const efi_string_t configuration,
efi_string_t *progress)
@@ -46,7 +163,7 @@ static efi_status_t route_config(
return EFI_EXIT(EFI_OUT_OF_RESOURCES);
}

-static efi_status_t block_to_config(
+static efi_status_t EFIAPI block_to_config(
const struct efi_hii_config_routing_protocol *this,
const efi_string_t config_request,
const uint8_t *block,
@@ -54,12 +171,12 @@ static efi_status_t block_to_config(
efi_string_t *config,
efi_string_t *progress)
{
- EFI_ENTRY("%p, \"%ls\", %p, %lu, %p, %p", this, config_request, block,
+ EFI_ENTRY("%p, \"%ls\", %p, %zu, %p, %p", this, config_request, block,
block_size, config, progress);
return EFI_EXIT(EFI_OUT_OF_RESOURCES);
}

-static efi_status_t config_to_block(
+static efi_status_t EFIAPI config_to_block(
const struct efi_hii_config_routing_protocol *this,
const efi_string_t config_resp,
const uint8_t *block,
@@ -71,7 +188,7 @@ static efi_status_t config_to_block(
return EFI_EXIT(EFI_OUT_OF_RESOURCES);
}

-static efi_status_t get_alt_config(
+static efi_status_t EFIAPI get_alt_config(
const struct efi_hii_config_routing_protocol *this,
const efi_string_t config_resp,
const efi_guid_t *guid,
@@ -91,28 +208,67 @@ static efi_status_t get_alt_config(
* EFI_HII_DATABASE_PROTOCOL
*/

-static efi_status_t new_package_list(
+static efi_status_t EFIAPI new_package_list(
const struct efi_hii_database_protocol *this,
const struct efi_hii_package_list_header *package_list,
const efi_handle_t driver_handle,
efi_hii_handle_t *handle)
{
- /* Current shell start failure. */
- /* Invoked from MdeModulePkg/Library/UefiHiiLib/HiiLib.c : HiiAddPackages */
- /* (Via autogenerated .c file.) */
+ efi_status_t ret = EFI_SUCCESS;
+
EFI_ENTRY("%p, %p, %p, %p", this, package_list, driver_handle, handle);
- return EFI_EXIT(EFI_OUT_OF_RESOURCES);
+
+ if (!package_list || !driver_handle)
+ return EFI_EXIT(EFI_INVALID_PARAMETER);
+
+ struct hii_package *hii = new_package();
+ struct efi_hii_package_header *package;
+ void *end = ((void *)package_list) + package_list->package_length;
+
+ debug("package_list: %pUl (%u)\n", &package_list->package_list_guid,
+ package_list->package_length);
+
+ package = ((void *)package_list) + sizeof(*package_list);
+ while ((void *)package < end) {
+ debug("package=%p, package type=%x, length=%u\n", package,
+ package->type, package->length);
+ switch (package->type) {
+ case EFI_HII_PACKAGE_STRINGS:
+ ret = add_strings_package(hii,
+ (struct efi_hii_strings_package *)package);
+ break;
+ default:
+ break;
+ }
+
+ if (ret != EFI_SUCCESS)
+ goto error;
+
+ package = ((void *)package) + package->length;
+ }
+
+ // TODO in theory there is some notifications that should be sent..
+
+ *handle = hii;
+
+ return EFI_EXIT(EFI_SUCCESS);
+
+error:
+ free_package(hii);
+ return EFI_EXIT(ret);
}

-static efi_status_t remove_package_list(
+static efi_status_t EFIAPI remove_package_list(
const struct efi_hii_database_protocol *this,
efi_hii_handle_t handle)
{
+ struct hii_package *hii = handle;
EFI_ENTRY("%p, %p", this, handle);
- return EFI_EXIT(EFI_NOT_FOUND);
+ free_package(hii);
+ return EFI_EXIT(EFI_SUCCESS);
}

-static efi_status_t update_package_list(
+static efi_status_t EFIAPI update_package_list(
const struct efi_hii_database_protocol *this,
efi_hii_handle_t handle,
const struct efi_hii_package_list_header *package_list)
@@ -121,7 +277,7 @@ static efi_status_t update_package_list(
return EFI_EXIT(EFI_NOT_FOUND);
}

-static efi_status_t list_package_lists(
+static efi_status_t EFIAPI list_package_lists(
const struct efi_hii_database_protocol *this,
uint8_t package_type,
const efi_guid_t *package_guid,
@@ -133,7 +289,7 @@ static efi_status_t list_package_lists(
return EFI_EXIT(EFI_NOT_FOUND);
}

-static efi_status_t export_package_lists(
+static efi_status_t EFIAPI export_package_lists(
const struct efi_hii_database_protocol *this,
efi_hii_handle_t handle,
UINTN *buffer_size,
@@ -143,7 +299,7 @@ static efi_status_t export_package_lists(
return EFI_EXIT(EFI_NOT_FOUND);
}

-static efi_status_t register_package_notify(
+static efi_status_t EFIAPI register_package_notify(
const struct efi_hii_database_protocol *this,
uint8_t package_type,
const efi_guid_t *package_guid,
@@ -151,13 +307,13 @@ static efi_status_t register_package_notify(
UINTN notify_type,
efi_handle_t *notify_handle)
{
- EFI_ENTRY("%p, %u, %pUl, %p, %lu, %p", this, package_type,
+ EFI_ENTRY("%p, %u, %pUl, %p, %zu, %p", this, package_type,
package_guid, package_notify_fn, notify_type,
notify_handle);
return EFI_EXIT(EFI_OUT_OF_RESOURCES);
}

-static efi_status_t unregister_package_notify(
+static efi_status_t EFIAPI unregister_package_notify(
const struct efi_hii_database_protocol *this,
efi_handle_t notification_handle)
{
@@ -165,7 +321,7 @@ static efi_status_t unregister_package_notify(
return EFI_EXIT(EFI_NOT_FOUND);
}

-static efi_status_t find_keyboard_layouts(
+static efi_status_t EFIAPI find_keyboard_layouts(
const struct efi_hii_database_protocol *this,
uint16_t *key_guid_buffer_length,
efi_guid_t *key_guid_buffer)
@@ -174,7 +330,7 @@ static efi_status_t find_keyboard_layouts(
return EFI_EXIT(EFI_NOT_FOUND); /* Invalid */
}

-static efi_status_t get_keyboard_layout(
+static efi_status_t EFIAPI get_keyboard_layout(
const struct efi_hii_database_protocol *this,
efi_guid_t *key_guid,
uint16_t *keyboard_layout_length,
@@ -185,7 +341,7 @@ static efi_status_t get_keyboard_layout(
return EFI_EXIT(EFI_NOT_FOUND);
}

-static efi_status_t set_keyboard_layout(
+static efi_status_t EFIAPI set_keyboard_layout(
const struct efi_hii_database_protocol *this,
efi_guid_t *key_guid)
{
@@ -193,7 +349,7 @@ static efi_status_t set_keyboard_layout(
return EFI_EXIT(EFI_NOT_FOUND);
}

-static efi_status_t get_package_list_handle(
+static efi_status_t EFIAPI get_package_list_handle(
const struct efi_hii_database_protocol *this,
efi_hii_handle_t package_list_handle,
efi_handle_t *driver_handle)
@@ -207,7 +363,7 @@ static efi_status_t get_package_list_handle(
* EFI_HII_STRING_PROTOCOL
*/

-static efi_status_t new_string(
+static efi_status_t EFIAPI new_string(
const struct efi_hii_string_protocol *this,
efi_hii_handle_t package_list,
efi_string_id_t *string_id,
@@ -216,13 +372,13 @@ static efi_status_t new_string(
const efi_string_t string,
const struct efi_font_info *string_font_info)
{
- EFI_ENTRY("%p, %p, %p, %p, %p, \"%ls\", %p", this, package_list,
+ EFI_ENTRY("%p, %p, %p, \"%s\", %p, \"%ls\", %p", this, package_list,
string_id, language, language_name, string,
string_font_info);
return EFI_EXIT(EFI_NOT_FOUND);
}

-static efi_status_t get_string(
+static efi_status_t EFIAPI get_string(
const struct efi_hii_string_protocol *this,
const uint8_t *language,
efi_hii_handle_t package_list,
@@ -231,13 +387,34 @@ static efi_status_t get_string(
UINTN *string_size,
struct efi_font_info **string_font_info)
{
- EFI_ENTRY("%p, %p, %p, %u, \"%ls\", %p, %p", this, language,
+ struct hii_package *hii = package_list;
+ struct string_table *stbl;
+
+ EFI_ENTRY("%p, \"%s\", %p, %u, %p, %p, %p", this, language,
package_list, string_id, string, string_size,
string_font_info);
+
+ list_for_each_entry(stbl, &hii->string_tables, link) {
+ if (!strcmp((char *)language, (char *)stbl->language)) {
+ unsigned idx = string_id - 1;
+ if (idx > stbl->nstrings)
+ return EFI_EXIT(EFI_NOT_FOUND);
+ efi_string_t str = stbl->strings[idx].string;
+ size_t len = utf16_strlen(str) + 1;
+ if (*string_size < len * 2) {
+ *string_size = len * 2;
+ return EFI_EXIT(EFI_BUFFER_TOO_SMALL);
+ }
+ memcpy(string, str, len * 2);
+ *string_size = len * 2;
+ return EFI_EXIT(EFI_SUCCESS);
+ }
+ }
+
return EFI_EXIT(EFI_NOT_FOUND);
}

-static efi_status_t set_string(
+static efi_status_t EFIAPI set_string(
const struct efi_hii_string_protocol *this,
efi_hii_handle_t package_list,
efi_string_id_t string_id,
@@ -245,31 +422,56 @@ static efi_status_t set_string(
const efi_string_t string,
const struct efi_font_info *string_font_info)
{
- EFI_ENTRY("%p, %p, %u, %p, \"%ls\", %p", this, package_list, string_id,
- language, string, string_font_info);
+ EFI_ENTRY("%p, %p, %u, \"%s\", \"%ls\", %p", this, package_list,
+ string_id, language, string, string_font_info);
return EFI_EXIT(EFI_NOT_FOUND);
}

-static efi_status_t get_languages(
+static efi_status_t EFIAPI get_languages(
const struct efi_hii_string_protocol *this,
efi_hii_handle_t package_list,
uint8_t *languages,
UINTN *languages_size)
{
+ struct hii_package *hii = package_list;
+ struct string_table *stbl;
+ size_t len = 0;
+
EFI_ENTRY("%p, %p, %p, %p", this, package_list, languages,
languages_size);
- return EFI_EXIT(EFI_NOT_FOUND);
+
+ /* figure out required size: */
+ list_for_each_entry(stbl, &hii->string_tables, link) {
+ len += strlen((char *)stbl->language) + 1;
+ }
+
+ if (*languages_size < len) {
+ *languages_size = len;
+ return EFI_EXIT(EFI_BUFFER_TOO_SMALL);
+ }
+
+ char *p = (char *)languages;
+ list_for_each_entry(stbl, &hii->string_tables, link) {
+ if (p != (char *)languages)
+ p += sprintf(p, ";");
+ p += sprintf(p, "%s", stbl->language);
+ }
+
+ debug("languages: %s\n", languages);
+
+ return EFI_EXIT(EFI_SUCCESS);
}

-static efi_status_t get_secondary_languages(
+static efi_status_t EFIAPI get_secondary_languages(
const struct efi_hii_string_protocol *this,
efi_hii_handle_t package_list,
const uint8_t *primary_language,
uint8_t *secondary_languages,
UINTN *secondary_languages_size)
{
- EFI_ENTRY("%p, %p, %p, %p, %p", this, package_list, primary_language,
- secondary_languages, secondary_languages_size);
+ EFI_ENTRY("%p, %p, \"%s\", %p, %p", this, package_list,
+ primary_language, secondary_languages,
+ secondary_languages_size);
return EFI_EXIT(EFI_NOT_FOUND);
}

--
2.13.5