* [PATCH v1 01/54] efi: payload: initrd: fix type mismatch on 32-bit
2025-12-18 10:37 [PATCH v1 00/54] efi: implement EFI loader support in barebox Ahmad Fatoum
@ 2025-12-18 10:37 ` Ahmad Fatoum
2025-12-18 10:37 ` [PATCH v1 02/54] efi: loader: switch over event/memory key type to efi_uintn_t Ahmad Fatoum
` (52 subsequent siblings)
53 siblings, 0 replies; 55+ messages in thread
From: Ahmad Fatoum @ 2025-12-18 10:37 UTC (permalink / raw)
To: barebox; +Cc: Ahmad Fatoum
While sizeof(unsigned long) == sizeof(size_t) in barebox, size_t an be
typedeffed as unsigned int on 32-bit, which leads to a mismatch with the
struct efi_load_file_protocol definition we already had.
Switch over to size_t as in the header to fix this.
Signed-off-by: Ahmad Fatoum <a.fatoum@pengutronix.de>
---
efi/initrd.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/efi/initrd.c b/efi/initrd.c
index 7fd8e021f353..642422b38d2a 100644
--- a/efi/initrd.c
+++ b/efi/initrd.c
@@ -17,7 +17,7 @@
static efi_status_t EFIAPI efi_initrd_load_file2(
struct efi_load_file_protocol *this, struct efi_device_path *file_path,
- bool boot_policy, unsigned long *buffer_size, void *buffer);
+ bool boot_policy, size_t *buffer_size, void *buffer);
static const struct {
struct efi_device_path_vendor vendor;
@@ -44,7 +44,7 @@ static struct linux_initrd {
static efi_status_t EFIAPI efi_initrd_load_file2(
struct efi_load_file_protocol *this, struct efi_device_path *file_path,
- bool boot_policy, unsigned long *buffer_size, void *buffer)
+ bool boot_policy, size_t *buffer_size, void *buffer)
{
struct linux_initrd *initrd = to_linux_initrd(this);
--
2.47.3
^ permalink raw reply [flat|nested] 55+ messages in thread* [PATCH v1 02/54] efi: loader: switch over event/memory key type to efi_uintn_t
2025-12-18 10:37 [PATCH v1 00/54] efi: implement EFI loader support in barebox Ahmad Fatoum
2025-12-18 10:37 ` [PATCH v1 01/54] efi: payload: initrd: fix type mismatch on 32-bit Ahmad Fatoum
@ 2025-12-18 10:37 ` Ahmad Fatoum
2025-12-18 10:37 ` [PATCH v1 03/54] lib: vsprintf: print human-readable EFI GUIDs with %pUs Ahmad Fatoum
` (51 subsequent siblings)
53 siblings, 0 replies; 55+ messages in thread
From: Ahmad Fatoum @ 2025-12-18 10:37 UTC (permalink / raw)
To: barebox; +Cc: Ahmad Fatoum
The current unsigned long type has the same size, but switching to
efi_uintn_t makes some code we are going to import from U-Boot easier to
diff.
Signed-off-by: Ahmad Fatoum <a.fatoum@pengutronix.de>
---
include/efi/services.h | 10 +++++-----
1 file changed, 5 insertions(+), 5 deletions(-)
diff --git a/include/efi/services.h b/include/efi/services.h
index eaeaf1a55ee8..c0dbac8743cc 100644
--- a/include/efi/services.h
+++ b/include/efi/services.h
@@ -44,13 +44,13 @@ struct efi_memory_desc;
*/
struct efi_boot_services {
struct efi_table_hdr hdr;
- efi_status_t (EFIAPI *raise_tpl)(unsigned long new_tpl);
- void (EFIAPI *restore_tpl)(unsigned long old_tpl);
+ efi_status_t (EFIAPI *raise_tpl)(efi_uintn_t new_tpl);
+ void (EFIAPI *restore_tpl)(efi_uintn_t old_tpl);
efi_status_t (EFIAPI *allocate_pages)(int, int, size_t,
efi_physical_addr_t *);
efi_status_t (EFIAPI *free_pages)(efi_physical_addr_t, size_t);
efi_status_t (EFIAPI *get_memory_map)(size_t *, struct efi_memory_desc *,
- ulong *, size_t *, u32 *);
+ efi_uintn_t *, size_t *, u32 *);
efi_status_t (EFIAPI *allocate_pool)(int, size_t, void **);
efi_status_t (EFIAPI *free_pool)(void *);
#define EFI_EVT_TIMER 0x80000000
@@ -64,7 +64,7 @@ struct efi_boot_services {
#define EFI_TPL_CALLBACK 8
#define EFI_TPL_NOTIFY 16
#define EFI_TPL_HIGH_LEVEL 31
- efi_status_t(EFIAPI *create_event)(u32 type , unsigned long tpl,
+ efi_status_t(EFIAPI *create_event)(u32 type , efi_uintn_t tpl,
void (EFIAPI *fn) (struct efi_event *event, void *ctx),
void *ctx, struct efi_event **event);
efi_status_t(EFIAPI *set_timer)(struct efi_event *event, enum efi_timer_delay type, uint64_t time);
@@ -99,7 +99,7 @@ struct efi_boot_services {
efi_status_t(EFIAPI *exit)(efi_handle_t handle, efi_status_t exit_status,
size_t exitdata_size, u16 *exitdata);
efi_status_t (EFIAPI *unload_image)(efi_handle_t handle);
- efi_status_t (EFIAPI *exit_boot_services)(efi_handle_t, unsigned long);
+ efi_status_t (EFIAPI *exit_boot_services)(efi_handle_t, efi_uintn_t);
void *get_next_monotonic_count;
efi_status_t (EFIAPI *stall)(unsigned long usecs);
efi_status_t (EFIAPI *set_watchdog_timer)(unsigned long timeout,
--
2.47.3
^ permalink raw reply [flat|nested] 55+ messages in thread* [PATCH v1 03/54] lib: vsprintf: print human-readable EFI GUIDs with %pUs
2025-12-18 10:37 [PATCH v1 00/54] efi: implement EFI loader support in barebox Ahmad Fatoum
2025-12-18 10:37 ` [PATCH v1 01/54] efi: payload: initrd: fix type mismatch on 32-bit Ahmad Fatoum
2025-12-18 10:37 ` [PATCH v1 02/54] efi: loader: switch over event/memory key type to efi_uintn_t Ahmad Fatoum
@ 2025-12-18 10:37 ` Ahmad Fatoum
2025-12-18 10:37 ` [PATCH v1 04/54] fs: fat: don't duplicate dentries when resolving differently cased paths Ahmad Fatoum
` (50 subsequent siblings)
53 siblings, 0 replies; 55+ messages in thread
From: Ahmad Fatoum @ 2025-12-18 10:37 UTC (permalink / raw)
To: barebox; +Cc: Ahmad Fatoum
For use during debugging, let's wire efi_guid_string() into printf.
In future, we may decide to print other UUIDs as well, but before we do
that we'll likely want to make the function more performant as it
currently compares against all GUIDs known to it serially.
Signed-off-by: Ahmad Fatoum <a.fatoum@pengutronix.de>
---
commands/efi_handle_dump.c | 2 +-
drivers/efi/efi-device.c | 2 +-
efi/guid.c | 4 ++--
include/efi/guid.h | 2 +-
lib/vsprintf.c | 23 +++++++++++++++++++++++
test/self/printf.c | 4 ++++
6 files changed, 32 insertions(+), 5 deletions(-)
diff --git a/commands/efi_handle_dump.c b/commands/efi_handle_dump.c
index c82f3e32469a..c5c6b6bbc3bc 100644
--- a/commands/efi_handle_dump.c
+++ b/commands/efi_handle_dump.c
@@ -51,7 +51,7 @@ static void efi_dump(struct efi_boot_services *bs, efi_handle_t *handles, size_t
printf(" Protocols:\n");
for (j = 0; j < num_guids; j++)
printf(" %d: %pUl: %s\n", j, guids[j],
- efi_guid_string(guids[j]));
+ efi_guid_string(guids[j]) ?: "unknown");
efi_devpath(bs, handles[i], &efi_device_path_protocol_guid,
"Devpath");
diff --git a/drivers/efi/efi-device.c b/drivers/efi/efi-device.c
index 470abdee5297..6bf43cebea8d 100644
--- a/drivers/efi/efi-device.c
+++ b/drivers/efi/efi-device.c
@@ -58,7 +58,7 @@ static void efi_devinfo(struct device *dev)
for (i = 0; i < efidev->num_guids; i++)
printf(" %d: %pUl: %s\n", i, &efidev->guids[i],
- efi_guid_string(&efidev->guids[i]));
+ efi_guid_string(&efidev->guids[i]) ?: "unknown");
}
static efi_handle_t efi_find_parent(efi_handle_t handle)
diff --git a/efi/guid.c b/efi/guid.c
index 5b15c472bb85..8853829d216b 100644
--- a/efi/guid.c
+++ b/efi/guid.c
@@ -80,7 +80,7 @@ const efi_guid_t efi_guid_event_group_return_to_efibootmgr =
return long; \
} while(0)
-const char *efi_guid_string(efi_guid_t *g)
+const char *efi_guid_string(const efi_guid_t *g)
{
EFI_GUID_STRING(EFI_NULL_GUID, "NULL", "NULL GUID");
EFI_GUID_STRING(EFI_MPS_TABLE_GUID, "MPS Table", "MPS Table GUID in EFI System Table");
@@ -161,5 +161,5 @@ const char *efi_guid_string(efi_guid_t *g)
/* Ramdisk */
EFI_GUID_STRING(EFI_LINUX_INITRD_MEDIA_GUID, "Initrd Media", "EFI Linux Initrd Media GUID");
- return "unknown";
+ return NULL;
}
diff --git a/include/efi/guid.h b/include/efi/guid.h
index f6018b5f7ef8..202300c74aaa 100644
--- a/include/efi/guid.h
+++ b/include/efi/guid.h
@@ -6,7 +6,7 @@
#include <efi/types.h>
#include <linux/string.h>
-const char *efi_guid_string(efi_guid_t *g);
+const char *efi_guid_string(const efi_guid_t *g);
static inline int
efi_guidcmp (efi_guid_t left, efi_guid_t right)
diff --git a/lib/vsprintf.c b/lib/vsprintf.c
index 622b40ac69e9..5ef4a075d0f6 100644
--- a/lib/vsprintf.c
+++ b/lib/vsprintf.c
@@ -22,6 +22,7 @@
#include <wchar.h>
#include <of.h>
#include <efi/devicepath.h>
+#include <efi/guid.h>
#include <common.h>
#include <pbl.h>
@@ -372,6 +373,22 @@ char *uuid_string(char *buf, const char *end, const u8 *addr, int field_width,
return string(buf, end, uuid, field_width, precision, flags);
}
+static noinline_for_stack
+char *efi_guid_string_format(char *buf, const char *end, const efi_guid_t *addr,
+ int field_width, int precision, int flags, const char *fmt)
+{
+ const char *desc = NULL;
+
+ if (!addr)
+ return string(buf, end, NULL, field_width, precision, flags);
+
+ desc = efi_guid_string(addr);
+ if (desc)
+ return string(buf, end, desc, field_width, precision, flags);
+
+ return uuid_string(buf, end, addr->b, field_width, precision, flags, fmt);
+}
+
static char *device_path_string(char *buf, const char *end, const struct efi_device_path *dp,
int field_width, int precision, int flags)
{
@@ -510,6 +527,8 @@ char *clock(char *buf, const char *end, const struct clk *clk,
* [0][1][2][3]-[4][5]-[6][7]-[8][9]-[10][11][12][13][14][15]
* little endian output byte order is:
* [3][2][1][0]-[5][4]-[7][6]-[8][9]-[10][11][12][13][14][15]
+ * s human readable description of the EFI GUID if possible and plain
+ * GUID otherwise
* - 'V' For a struct va_format which contains a format string * and va_list *,
* call vsnprintf(->format, *->va_list).
* Implements a "recursive vsnprintf".
@@ -534,6 +553,8 @@ char *clock(char *buf, const char *end, const struct clk *clk,
*
* - 'JP' For a JSON path
* - 'D' For EFI device paths
+ * - 'Us' For a 16 byte EFI GUID, it prints the human readable description
+ * if possible or the plain GUID as %pU would otherwise.
*/
static char *pointer(const char *fmt, char *buf, const char *end, const void *ptr,
int field_width, int precision, int flags)
@@ -547,6 +568,8 @@ static char *pointer(const char *fmt, char *buf, const char *end, const void *pt
case 's':
return symbol_string(buf, end, ptr, field_width, precision, flags, with_offset);
case 'U':
+ if (fmt[1] == 's' && IS_ENABLED(CONFIG_EFI_GUID))
+ return efi_guid_string_format(buf, end, ptr, field_width, precision, flags, fmt);
if (IS_ENABLED(CONFIG_PRINTF_UUID))
return uuid_string(buf, end, ptr, field_width, precision, flags, fmt);
break;
diff --git a/test/self/printf.c b/test/self/printf.c
index 3f35b5514705..9d7a936749a1 100644
--- a/test/self/printf.c
+++ b/test/self/printf.c
@@ -13,6 +13,7 @@
#include <stdlib.h>
#include <linux/string.h>
#include <errno.h>
+#include <efi/guid.h>
#include <linux/bitmap.h>
@@ -260,6 +261,7 @@ uuid(void)
{
const char uuid[16] = {0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7,
0x8, 0x9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf};
+ efi_guid_t protocol_guid = EFI_LOAD_FILE2_PROTOCOL_GUID;
if (!IS_ENABLED(CONFIG_PRINTF_UUID)) {
pr_info("skipping UUID tests: disabled in config\n");
@@ -271,6 +273,8 @@ uuid(void)
test("00010203-0405-0607-0809-0A0B0C0D0E0F", "%pUB", uuid);
test("03020100-0504-0706-0809-0a0b0c0d0e0f", "%pUl", uuid);
test("03020100-0504-0706-0809-0A0B0C0D0E0F", "%pUL", uuid);
+ if (IS_ENABLED(CONFIG_EFI_GUID))
+ test("EFI Load File 2 Protocol", "%pUs", &protocol_guid);
}
static void __init
--
2.47.3
^ permalink raw reply [flat|nested] 55+ messages in thread* [PATCH v1 04/54] fs: fat: don't duplicate dentries when resolving differently cased paths
2025-12-18 10:37 [PATCH v1 00/54] efi: implement EFI loader support in barebox Ahmad Fatoum
` (2 preceding siblings ...)
2025-12-18 10:37 ` [PATCH v1 03/54] lib: vsprintf: print human-readable EFI GUIDs with %pUs Ahmad Fatoum
@ 2025-12-18 10:37 ` Ahmad Fatoum
2025-12-18 10:37 ` [PATCH v1 05/54] efi: loader: add memory accounting Ahmad Fatoum
` (49 subsequent siblings)
53 siblings, 0 replies; 55+ messages in thread
From: Ahmad Fatoum @ 2025-12-18 10:37 UTC (permalink / raw)
To: barebox; +Cc: Ahmad Fatoum
With case insensitive file systems, like FAT, we should avoid having
multiple dentry cache entries for the same file differing only in
capitalization. Allow case-insensitive file system to set a casefold
flag that barebox will honour when comparing dentries.
That this patch works can be verified using the debug_fs_dump command,
which will show a dentry for each differently cased path in a VFAT,
but only a single dentry once this patch is applied.
Signed-off-by: Ahmad Fatoum <a.fatoum@pengutronix.de>
---
fs/fat/fat.c | 1 +
fs/fs.c | 3 +++
include/linux/fs.h | 1 +
3 files changed, 5 insertions(+)
diff --git a/fs/fat/fat.c b/fs/fat/fat.c
index cdca666566d2..93d1e08b8456 100644
--- a/fs/fat/fat.c
+++ b/fs/fat/fat.c
@@ -352,6 +352,7 @@ static int fat_probe(struct device *dev)
goto err_open;
priv->cdev = fsdev->cdev;
+ fsdev->sb.s_casefold = true;
priv->fat.userdata = priv;
ret = f_mount(&priv->fat);
diff --git a/fs/fs.c b/fs/fs.c
index cc6f0f7057c3..e997bd5f6a99 100644
--- a/fs/fs.c
+++ b/fs/fs.c
@@ -1562,6 +1562,9 @@ static bool d_same_name(const struct dentry *dentry,
if (dentry->d_name.len != name->len)
return false;
+ if (dentry->d_sb->s_casefold)
+ return strncasecmp(dentry->d_name.name, name->name, name->len) == 0;
+
return strncmp(dentry->d_name.name, name->name, name->len) == 0;
}
diff --git a/include/linux/fs.h b/include/linux/fs.h
index e624b2ce7161..d3813ad9c543 100644
--- a/include/linux/fs.h
+++ b/include/linux/fs.h
@@ -160,6 +160,7 @@ struct super_block {
unsigned long s_blocksize;
unsigned char s_blocksize_bits;
unsigned char s_dirt;
+ bool s_casefold;
unsigned long long s_maxbytes; /* Max file size */
struct file_system_type *s_type;
const struct super_operations *s_op;
--
2.47.3
^ permalink raw reply [flat|nested] 55+ messages in thread* [PATCH v1 05/54] efi: loader: add memory accounting
2025-12-18 10:37 [PATCH v1 00/54] efi: implement EFI loader support in barebox Ahmad Fatoum
` (3 preceding siblings ...)
2025-12-18 10:37 ` [PATCH v1 04/54] fs: fat: don't duplicate dentries when resolving differently cased paths Ahmad Fatoum
@ 2025-12-18 10:37 ` Ahmad Fatoum
2025-12-18 10:37 ` [PATCH v1 06/54] efi: loader: add pool allocator Ahmad Fatoum
` (48 subsequent siblings)
53 siblings, 0 replies; 55+ messages in thread
From: Ahmad Fatoum @ 2025-12-18 10:37 UTC (permalink / raw)
To: barebox; +Cc: Ahmad Fatoum, Ahmad Fatoum
From: Ahmad Fatoum <ahmad@a3f.at>
For use by the EFI runtime, we implement here a naive allocator that
allocates all memory at the highest possible free address.
In future, we may consider switching to Linux' red-black tree based interval
trees, but this is good enough for now.
Signed-off-by: Ahmad Fatoum <a.fatoum@pengutronix.de>
---
arch/Kconfig | 3 +
arch/arm/Kconfig | 1 +
efi/Kconfig | 19 ++
efi/Makefile | 1 +
efi/loader/Kconfig | 3 +
efi/loader/Makefile | 3 +
efi/loader/memory.c | 416 +++++++++++++++++++++++++++++++++++++++++
include/efi/loader.h | 33 ++++
include/efi/types.h | 14 ++
include/linux/ioport.h | 2 +
include/memory.h | 2 +
11 files changed, 497 insertions(+)
create mode 100644 efi/loader/Kconfig
create mode 100644 efi/loader/Makefile
create mode 100644 efi/loader/memory.c
create mode 100644 include/efi/loader.h
diff --git a/arch/Kconfig b/arch/Kconfig
index 5af114a6e9e5..ca9aa25a9c4b 100644
--- a/arch/Kconfig
+++ b/arch/Kconfig
@@ -107,6 +107,9 @@ config HAVE_EFI_PAYLOAD
config HAVE_EFI_STUB
bool
+config HAVE_EFI_LOADER
+ bool
+
config PHYS_ADDR_T_64BIT
bool
diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig
index d2d81a4f90d6..4b7f5b83c67e 100644
--- a/arch/arm/Kconfig
+++ b/arch/arm/Kconfig
@@ -17,6 +17,7 @@ config ARM
select HAVE_ARCH_BOOTM_OFTREE
select HW_HAS_PCI
select ARCH_HAS_DMA_WRITE_COMBINE
+ select HAVE_EFI_LOADER if MMU # for payload unaligned accesses
default y
config ARCH_LINUX_NAME
diff --git a/efi/Kconfig b/efi/Kconfig
index 23ac601af647..766d6b0e3094 100644
--- a/efi/Kconfig
+++ b/efi/Kconfig
@@ -25,6 +25,25 @@ if EFI_PAYLOAD
source "efi/payload/Kconfig"
endif
+config EFI_LOADER
+ bool "barebox as EFI loader (provider)"
+ depends on HAVE_EFI_LOADER
+ select EFI
+ select EFI_GUID
+ select EFI_DEVICEPATH
+ select MEMORY_ATTRIBUTES
+ help
+ Select this option if you want to run UEFI applications (like GNU
+ GRUB or an EFI-stubbed kernel) on top of barebox.
+
+ If this option is enabled, barebox will expose the UEFI API to
+ loaded applications, enabling them to reuse barebox device drivers
+ and file systems.
+
+if EFI_LOADER
+source "efi/loader/Kconfig"
+endif
+
config EFI
bool
diff --git a/efi/Makefile b/efi/Makefile
index 4c35917475c0..e0f6ac549009 100644
--- a/efi/Makefile
+++ b/efi/Makefile
@@ -1,6 +1,7 @@
# SPDX-License-Identifier: GPL-2.0-only
obj-$(CONFIG_EFI_PAYLOAD) += payload/
+obj-$(CONFIG_EFI_LOADER) += loader/
obj-$(CONFIG_EFI_GUID) += guid.o
obj-$(CONFIG_EFI_DEVICEPATH) += devicepath.o
obj-y += errno.o handle.o efivar.o efivar-filename.o
diff --git a/efi/loader/Kconfig b/efi/loader/Kconfig
new file mode 100644
index 000000000000..e7080d8d5093
--- /dev/null
+++ b/efi/loader/Kconfig
@@ -0,0 +1,3 @@
+# SPDX-License-Identifier: GPL-2.0-only
+# SPDX-Comment: Origin-URL: https://github.com/u-boot/u-boot/blob/a0fe8cedcbe8c76403a77e57eac228b8f778a3ae/lib/efi_loader/Kconfig
+
diff --git a/efi/loader/Makefile b/efi/loader/Makefile
new file mode 100644
index 000000000000..dea1e06c18cf
--- /dev/null
+++ b/efi/loader/Makefile
@@ -0,0 +1,3 @@
+# SPDX-License-Identifier: GPL-2.0
+
+obj-y += memory.o
diff --git a/efi/loader/memory.c b/efi/loader/memory.c
new file mode 100644
index 000000000000..075b559367fe
--- /dev/null
+++ b/efi/loader/memory.c
@@ -0,0 +1,416 @@
+// SPDX-License-Identifier: GPL-2.0+
+
+#define pr_fmt(fmt) "efi-loader: memory: " fmt
+
+#include <linux/minmax.h>
+#include <linux/printk.h>
+#include <linux/sprintf.h>
+#include <efi/loader.h>
+#include <efi/error.h>
+#include <init.h>
+#include <memory.h>
+#include <linux/list_sort.h>
+#include <linux/sizes.h>
+#include <dma.h>
+
+efi_uintn_t efi_memory_map_key;
+
+static efi_status_t find_pages_max(struct list_head *banks, size_t npages, size_t *page)
+{
+ struct memory_bank *bank;
+
+ list_for_each_entry_reverse(bank, banks, list) {
+ if ((bank->res->start >> EFI_PAGE_SHIFT) > *page)
+ continue;
+
+ for_each_memory_bank_region_reverse(bank, region) {
+ resource_size_t gap_firstpage, gap_lastpage;
+ resource_size_t candidate_last;
+
+ if (!region_is_gap(region))
+ continue;
+
+ gap_firstpage = EFI_PAGE_ALIGN(region->start) >> EFI_PAGE_SHIFT;
+ gap_lastpage = (EFI_PAGE_ALIGN(region->end + 1) >> EFI_PAGE_SHIFT);
+
+ if (!gap_lastpage || gap_lastpage < gap_firstpage)
+ continue;
+
+ /* make last page inclusive */
+ gap_lastpage--;
+
+ candidate_last = min_t(resource_size_t, gap_lastpage, *page);
+ if (candidate_last < gap_firstpage)
+ continue;
+
+ if (candidate_last - gap_firstpage + 1 < npages)
+ continue;
+
+ *page = candidate_last + 1 - npages;
+ return EFI_SUCCESS;
+ }
+ }
+
+ return EFI_OUT_OF_RESOURCES;
+}
+
+
+static const char *efi_memory_type_tostr(enum efi_memory_type type)
+{
+ switch (type) {
+ case EFI_RESERVED_TYPE:
+ return "res";
+ case EFI_LOADER_CODE:
+ return "ldcode";
+ case EFI_LOADER_DATA:
+ return "lddata";
+ case EFI_BOOT_SERVICES_CODE:
+ return "bscode";
+ case EFI_BOOT_SERVICES_DATA:
+ return "bsdata";
+ case EFI_RUNTIME_SERVICES_CODE:
+ return "rtcode";
+ case EFI_RUNTIME_SERVICES_DATA:
+ return "rtdata";
+ case EFI_CONVENTIONAL_MEMORY:
+ return "memory";
+ case EFI_UNUSABLE_MEMORY:
+ return "unsable";
+ case EFI_ACPI_RECLAIM_MEMORY:
+ return "acpireclaim";
+ case EFI_ACPI_MEMORY_NVS:
+ return "acpinvs";
+ case EFI_MEMORY_MAPPED_IO:
+ return "mmio";
+ case EFI_MEMORY_MAPPED_IO_PORT_SPACE:
+ return "mmioport";
+ case EFI_PAL_CODE:
+ return "pal";
+ case EFI_PERSISTENT_MEMORY_TYPE:
+ return "persistent";
+ case EFI_UNACCEPTED_MEMORY_TYPE:
+ return "unaccepted";
+ default:
+ return "unknown";
+ }
+}
+
+static u64 efi_memory_type_default_attrs(enum efi_memory_type type)
+{
+ switch (type) {
+ case EFI_RESERVED_TYPE:
+ case EFI_MEMORY_MAPPED_IO:
+ case EFI_MEMORY_MAPPED_IO_PORT_SPACE:
+ case EFI_UNACCEPTED_MEMORY_TYPE:
+ return 0;
+ case EFI_BOOT_SERVICES_CODE:
+ case EFI_LOADER_CODE:
+ case EFI_RUNTIME_SERVICES_CODE:
+ case EFI_ACPI_RECLAIM_MEMORY:
+ case EFI_ACPI_MEMORY_NVS:
+ return MEMATTRS_RWX;
+ case EFI_LOADER_DATA:
+ case EFI_BOOT_SERVICES_DATA:
+ case EFI_RUNTIME_SERVICES_DATA:
+ return MEMATTRS_RW;
+ case EFI_CONVENTIONAL_MEMORY:
+ return MEMATTRS_RWX;
+ case EFI_PAL_CODE:
+ return MEMATTRS_FAULT;
+ case EFI_PERSISTENT_MEMORY_TYPE:
+ return MEMATTRS_RW | MEMATTR_SP;
+ case EFI_UNUSABLE_MEMORY:
+ case EFI_MAX_MEMORY_TYPE:
+ pr_warn("Unallocatable type %u\n", type); // FIXME
+ return MEMATTRS_FAULT;
+ }
+
+ return MEMATTRS_RWX;
+}
+
+/**
+ * efi_allocate_pages - allocate memory pages
+ *
+ * @type: type of allocation to be performed
+ * @memory_type: usage type of the allocated memory
+ * @pages: number of pages to be allocated
+ * @memory: allocated memory
+ * @name: name for informational purposes
+ * Return: status code
+ */
+efi_status_t efi_allocate_pages(enum efi_allocate_type type,
+ enum efi_memory_type memory_type,
+ efi_uintn_t npages, uint64_t *memory,
+ const char *name)
+{
+ char namebuf[64];
+ const char *typestr;
+ struct resource *res;
+ uint64_t attrs, new_addr = *memory;
+ size_t new_page;
+ efi_status_t r;
+
+ ++efi_memory_map_key;
+
+ if (npages == 0)
+ return EFI_INVALID_PARAMETER;
+
+ switch (type) {
+ case EFI_ALLOCATE_ANY_PAGES:
+ new_addr = U64_MAX;
+ typestr = "any";
+ fallthrough;
+ case EFI_ALLOCATE_MAX_ADDRESS:
+ new_page = new_addr >> EFI_PAGE_SHIFT;
+ r = find_pages_max(&memory_banks, npages, &new_page);
+ if (r != EFI_SUCCESS)
+ return r;
+ new_addr = new_page << EFI_PAGE_SHIFT;
+ typestr = "max";
+ break;
+ case EFI_ALLOCATE_ADDRESS:
+ typestr = "exact";
+ break;
+ default:
+ /* UEFI doesn't specify other allocation types */
+ return EFI_INVALID_PARAMETER;
+ }
+
+ scnprintf(namebuf, sizeof(namebuf), "efi%zu-%s%c%s", efi_memory_map_key,
+ efi_memory_type_tostr(memory_type),
+ name ? '-' : '\0', name ?: "");
+
+ attrs = efi_memory_type_default_attrs(memory_type);
+ if (!attrs)
+ return EFI_INVALID_PARAMETER;
+
+ res = request_sdram_region(namebuf, new_addr, npages << EFI_PAGE_SHIFT,
+ efi_memory_type_to_resource_type(memory_type),
+ attrs);
+ if (!res) {
+ pr_err("failed to request %s at page 0x%zx+%zu (%s)\n",
+ namebuf, new_page, npages, typestr);
+ dump_stack();
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ if (memory_type == EFI_RUNTIME_SERVICES_CODE ||
+ memory_type == EFI_RUNTIME_SERVICES_DATA)
+ res->runtime = true;
+
+ res->flags |= IORESOURCE_EFI_ALLOC;
+
+ *memory = new_addr;
+ return EFI_SUCCESS;
+}
+
+static int free_efi_only(struct resource *res, void *data)
+{
+ int *nfreed = data;
+
+ if (!(res->flags & IORESOURCE_EFI_ALLOC)) {
+ pr_warn("refusing to free non-EFI allocated resource %s at 0x%llx\n",
+ res->name, res->start);
+ *nfreed = -1;
+ return false;
+ }
+
+ if (nfreed >= 0)
+ ++*nfreed;
+ return true;
+}
+
+// SPDX-SnippetBegin
+// SPDX-License-Identifier: GPL-2.0+
+// SPDX-SnippetCopyrightText: 2016 Alexander Graf
+// SPDX-SnippetComment: Origin-URL: https://github.com/u-boot/u-boot/blob/aa703a816a62deb876a1e77ccff030a7cc60f344/lib/efi_loader/efi_memory.c
+
+/**
+ * efi_alloc_aligned_pages() - allocate aligned memory pages
+ *
+ * @len: len in bytes
+ * @memory_type: usage type of the allocated memory
+ * @align: alignment in bytes
+ * @name: name for informational purposes
+ * Return: aligned memory or NULL
+ */
+void *efi_alloc_aligned_pages(u64 len, int memory_type, size_t align,
+ const char *name)
+{
+ u64 req_pages = efi_size_in_pages(len);
+ u64 true_pages = req_pages + efi_size_in_pages(align) - 1;
+ u64 free_pages;
+ u64 aligned_mem;
+ efi_status_t r;
+ u64 mem;
+
+ /* align must be zero or a power of two */
+ if (align & (align - 1))
+ return NULL;
+
+ /* Check for overflow */
+ if (true_pages < req_pages)
+ return NULL;
+
+ if (align < EFI_PAGE_SIZE) {
+ r = efi_allocate_pages(EFI_ALLOCATE_ANY_PAGES, memory_type,
+ req_pages, &mem, name);
+ return (r == EFI_SUCCESS) ? (void *)(uintptr_t)mem : NULL;
+ }
+
+ r = efi_allocate_pages(EFI_ALLOCATE_ANY_PAGES, memory_type,
+ true_pages, &mem, name);
+ if (r != EFI_SUCCESS)
+ return NULL;
+
+ aligned_mem = ALIGN(mem, align);
+ /* Free pages before alignment */
+ free_pages = efi_size_in_pages(aligned_mem - mem);
+ if (free_pages)
+ efi_free_pages(mem, free_pages);
+
+ /* Free trailing pages */
+ free_pages = true_pages - (req_pages + free_pages);
+ if (free_pages) {
+ mem = aligned_mem + req_pages * EFI_PAGE_SIZE;
+ efi_free_pages(mem, free_pages);
+ }
+
+ return (void *)(uintptr_t)aligned_mem;
+}
+
+// SPDX-SnippetEnd
+
+/**
+ * efi_free_pages() - free memory pages
+ *
+ * @memory: start of the memory area to be freed
+ * @pages: number of pages to be freed
+ * Return: status code
+ */
+efi_status_t efi_free_pages(uint64_t memory, size_t pages)
+{
+ size_t size = pages << EFI_PAGE_SHIFT;
+ struct memory_bank *bank;
+ int nfreed = 0;
+
+ for_each_memory_bank(bank)
+ release_region_range(bank->res, memory, size, free_efi_only, &nfreed);
+
+ if (!nfreed)
+ pr_warn("can't free %llx: not found\n", memory);
+ if (nfreed <= 0)
+ return EFI_INVALID_PARAMETER;
+
+ return EFI_SUCCESS;
+}
+
+static int efi_memory_desc_from_res(const struct resource *region,
+ struct efi_memory_desc *desc)
+{
+ efi_physical_addr_t phys_start = EFI_PAGE_ALIGN(region->start);
+ efi_uintn_t npages = efi_size_in_pages(EFI_PAGE_ALIGN(region->end + 1) - phys_start);
+
+ if (!npages)
+ return 0;
+
+ desc->phys_start = phys_start;
+ desc->virt_start = efi_phys_to_virt(phys_start);
+ desc->npages = npages;
+
+ if (region->flags & IORESOURCE_TYPE_VALID) {
+ desc->type = resource_get_efi_memory_type(region);
+ desc->attrs = resource_get_efi_memory_attrs(region);
+ } else {
+ pr_warn("encountered SDRAM region 0x%pa-0x%pa without valid type\n",
+ ®ion->start, ®ion->end);
+ desc->type = EFI_RESERVED_TYPE;
+ desc->attrs = EFI_MEMORY_WB;
+ }
+
+ return 1;
+}
+
+/**
+ * efi_get_memory_map() - get map describing memory usage.
+ *
+ * @memory_map_size: on entry the size, in bytes, of the memory map buffer,
+ * on exit the size of the copied memory map
+ * @memory_map: buffer to which the memory map is written
+ * @map_key: key for the memory map
+ * @descriptor_size: size of an individual memory descriptor
+ * @descriptor_version: version number of the memory descriptor structure
+ * Return: status code
+ */
+efi_status_t efi_get_memory_map(size_t *memory_map_size,
+ struct efi_memory_desc *memory_map,
+ efi_uintn_t *map_key,
+ size_t *descriptor_size,
+ uint32_t *descriptor_version)
+{
+ size_t map_size = 0;
+ int i = 0, map_entries = 0;
+ size_t provided_map_size;
+ struct memory_bank *bank;
+
+ if (!memory_map_size)
+ return EFI_INVALID_PARAMETER;
+
+ provided_map_size = *memory_map_size;
+
+ for_each_memory_bank(bank) {
+ for_each_memory_bank_region(bank, region) {
+ if (list_empty(®ion->children)) {
+ map_entries++;
+ continue;
+ }
+
+ for_each_resource_region(region, bbregion)
+ map_entries++;
+ }
+ }
+
+ map_size = map_entries * sizeof(struct efi_memory_desc);
+
+ /* Note: some regions may end up being 0-sized after alignment to page
+ * boundaries and those will be skipped later.
+ *
+ * It's fine wrt *memory_map_size though as worst case, this means we
+ * ask the caller to allocate a little more memory than actually needed.
+ *
+ * TODO: Should we rather enforce resource allocations from memory banks
+ * to be page aligned from the outset?
+ */
+ *memory_map_size = map_size;
+
+ if (descriptor_size)
+ *descriptor_size = sizeof(struct efi_memory_desc);
+
+ if (descriptor_version)
+ *descriptor_version = EFI_MEMORY_DESCRIPTOR_VERSION;
+
+ if (provided_map_size < map_size)
+ return EFI_BUFFER_TOO_SMALL;
+
+ if (!memory_map)
+ return EFI_INVALID_PARAMETER;
+
+ for_each_memory_bank(bank) {
+ for_each_memory_bank_region(bank, region) {
+ if (list_empty(®ion->children)) {
+ i += efi_memory_desc_from_res(region, &memory_map[i]);
+ continue;
+ }
+
+ for_each_resource_region(region, bbregion)
+ i += efi_memory_desc_from_res(bbregion, &memory_map[i]);
+ }
+ }
+
+ *memory_map_size = i * sizeof(struct efi_memory_desc);
+
+ if (map_key)
+ *map_key = efi_memory_map_key;
+
+ return EFI_SUCCESS;
+}
diff --git a/include/efi/loader.h b/include/efi/loader.h
new file mode 100644
index 000000000000..a5359f07f125
--- /dev/null
+++ b/include/efi/loader.h
@@ -0,0 +1,33 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/* SPDX-SnippetCopyrightText: 2016 Alexander Graf */
+
+#ifndef _EFI_LOADER_H
+#define _EFI_LOADER_H 1
+
+#include <efi/types.h>
+#include <efi/services.h>
+#include <efi/memory.h>
+
+#define EFI_SPECIFICATION_VERSION (2 << 16 | 80)
+
+/* Key identifying current memory map */
+extern efi_uintn_t efi_memory_map_key;
+
+/* Allocate pages on the specified alignment */
+void *efi_alloc_aligned_pages(u64 len, int memory_type, size_t align,
+ const char *name);
+/* More specific EFI memory allocator, called by EFI payloads */
+efi_status_t efi_allocate_pages(enum efi_allocate_type type,
+ enum efi_memory_type memory_type,
+ efi_uintn_t pages, uint64_t *memory,
+ const char *name);
+/* EFI memory free function. */
+efi_status_t efi_free_pages(uint64_t memory, efi_uintn_t pages);
+/* Returns the EFI memory map */
+efi_status_t efi_get_memory_map(efi_uintn_t *memory_map_size,
+ struct efi_memory_desc *memory_map,
+ efi_uintn_t *map_key,
+ efi_uintn_t *descriptor_size,
+ uint32_t *descriptor_version);
+
+#endif /* _EFI_LOADER_H */
diff --git a/include/efi/types.h b/include/efi/types.h
index f157f7ffe106..e9acc4c497d1 100644
--- a/include/efi/types.h
+++ b/include/efi/types.h
@@ -10,6 +10,7 @@
#include <linux/limits.h>
#include <linux/stddef.h>
#include <linux/compiler.h>
+#include <linux/align.h>
#include <linux/uuid.h>
typedef unsigned long efi_status_t;
@@ -120,6 +121,19 @@ enum efi_allocate_type {
#define EFI_PAGE_SHIFT 12
#define EFI_PAGE_SIZE (1ULL << EFI_PAGE_SHIFT)
#define EFI_PAGE_MASK (EFI_PAGE_SIZE - 1)
+#define EFI_PAGE_ALIGN(val) ALIGN((val), EFI_PAGE_SIZE)
+#define EFI_PAGE_ALIGN_DOWN(val) ALIGN_DOWN((val), EFI_PAGE_SIZE)
+
+/**
+ * efi_size_in_pages() - convert size in bytes to size in pages
+ *
+ * This macro returns the number of EFI memory pages required to hold 'size'
+ * bytes.
+ *
+ * @size: size in bytes
+ * Return: size in pages
+ */
+#define efi_size_in_pages(size) (((size) + EFI_PAGE_MASK) >> EFI_PAGE_SHIFT)
#endif
diff --git a/include/linux/ioport.h b/include/linux/ioport.h
index d57defd20ee1..20c72e1b6cbb 100644
--- a/include/linux/ioport.h
+++ b/include/linux/ioport.h
@@ -58,6 +58,8 @@ struct resource {
#define IORESOURCE_TYPE_VALID 0x00800000 /* type & attrs are valid */
+#define IORESOURCE_EFI_ALLOC 0x02000000 /* Resource allocated by barebox EFI loader */
+
#define IORESOURCE_EXCLUSIVE 0x08000000 /* Userland may not map this resource */
#define IORESOURCE_DISABLED 0x10000000
#define IORESOURCE_UNSET 0x20000000
diff --git a/include/memory.h b/include/memory.h
index 6a6f0e2eb7d3..6189a0f65c50 100644
--- a/include/memory.h
+++ b/include/memory.h
@@ -26,6 +26,8 @@ int barebox_add_memory_bank(const char *name, resource_size_t start,
resource_size_t size);
#define for_each_memory_bank(mem) list_for_each_entry(mem, &memory_banks, list)
+#define for_each_memory_bank_reverse(mem) \
+ list_for_each_entry_reverse(mem, &memory_banks, list)
#define for_each_reserved_region(mem, rsv) \
list_for_each_entry(rsv, &(mem)->res->children, sibling) \
if (!is_reserved_resource(rsv)) {} else
--
2.47.3
^ permalink raw reply [flat|nested] 55+ messages in thread* [PATCH v1 06/54] efi: loader: add pool allocator
2025-12-18 10:37 [PATCH v1 00/54] efi: implement EFI loader support in barebox Ahmad Fatoum
` (4 preceding siblings ...)
2025-12-18 10:37 ` [PATCH v1 05/54] efi: loader: add memory accounting Ahmad Fatoum
@ 2025-12-18 10:37 ` Ahmad Fatoum
2025-12-18 10:37 ` [PATCH v1 07/54] efi: types: add EFI_RUNTIME_SECTION Ahmad Fatoum
` (47 subsequent siblings)
53 siblings, 0 replies; 55+ messages in thread
From: Ahmad Fatoum @ 2025-12-18 10:37 UTC (permalink / raw)
To: barebox; +Cc: Ahmad Fatoum
In addition to the AllocatePages EFI boot service, there is also
AllocatePool, which allows allocating a number of bytes.
Make things easy to us and just map it to AllocatePages with an header
that holds the size to support the API.
Signed-off-by: Ahmad Fatoum <a.fatoum@pengutronix.de>
---
efi/loader/Makefile | 2 +-
efi/loader/pool_alloc.c | 205 ++++++++++++++++++++++++++++++++++++++++
include/efi/loader.h | 10 ++
3 files changed, 216 insertions(+), 1 deletion(-)
create mode 100644 efi/loader/pool_alloc.c
diff --git a/efi/loader/Makefile b/efi/loader/Makefile
index dea1e06c18cf..0475cfc7452f 100644
--- a/efi/loader/Makefile
+++ b/efi/loader/Makefile
@@ -1,3 +1,3 @@
# SPDX-License-Identifier: GPL-2.0
-obj-y += memory.o
+obj-y += memory.o pool_alloc.o
diff --git a/efi/loader/pool_alloc.c b/efi/loader/pool_alloc.c
new file mode 100644
index 000000000000..be10dd77e643
--- /dev/null
+++ b/efi/loader/pool_alloc.c
@@ -0,0 +1,205 @@
+// SPDX-License-Identifier: GPL-2.0+
+// SPDX-FileCopyrightText: 2016 Alexander Graf
+// SPDX-Comment: Origin-URL: https://github.com/u-boot/u-boot/blob/aa703a816a62deb876a1e77ccff030a7cc60f344/lib/efi_loader/efi_memory.c
+
+#define pr_fmt(fmt) "efi-loader: pool: " fmt
+
+#include <efi/memory.h>
+#include <efi/loader.h>
+#include <efi/error.h>
+#include <linux/printk.h>
+#include <linux/minmax.h>
+#include <init.h>
+#include <memory.h>
+#include <linux/list_sort.h>
+#include <linux/sizes.h>
+#include <dma.h>
+
+/* Magic number identifying memory allocated from pool */
+#define EFI_ALLOC_POOL_MAGIC 0x1fe67ddf6491caa2
+
+/**
+ * struct efi_pool_allocation - memory block allocated from pool
+ *
+ * @num_pages: number of pages allocated
+ * @checksum: checksum
+ * @data: allocated pool memory
+ *
+ * Each UEFI AllocatePool() request is serviced as a separate
+ * (multiple) page allocation. We have to track the number of pages
+ * to be able to free the correct amount later.
+ *
+ * The checksum calculated in function checksum() is used in FreePool() to avoid
+ * freeing memory not allocated by AllocatePool() and duplicate freeing.
+ *
+ * EFI requires 8 byte alignment for pool allocations, so we can
+ * prepend each allocation with these header fields.
+ */
+struct efi_pool_allocation {
+ u64 num_pages;
+ u64 checksum;
+ char data[] __aligned(ARCH_DMA_MINALIGN);
+};
+
+/**
+ * checksum() - calculate checksum for memory allocated from pool
+ *
+ * @alloc: allocation header
+ * Return: checksum, always non-zero
+ */
+static u64 checksum(struct efi_pool_allocation *alloc)
+{
+ u64 addr = (uintptr_t)alloc;
+ u64 ret = (addr >> 32) ^ (addr << 32) ^ alloc->num_pages ^
+ EFI_ALLOC_POOL_MAGIC;
+ if (!ret)
+ ++ret;
+ return ret;
+}
+
+/**
+ * efi_allocate_pool - allocate memory from pool
+ *
+ * @pool_type: type of the pool from which memory is to be allocated
+ * @size: number of bytes to be allocated
+ * @buffer: allocated memory
+ * @name: name for informational purposes
+ * Return: status code
+ */
+efi_status_t efi_allocate_pool(enum efi_memory_type pool_type, efi_uintn_t size,
+ void **buffer, const char *name)
+{
+ efi_status_t r;
+ u64 addr;
+ struct efi_pool_allocation *alloc;
+ u64 num_pages = efi_size_in_pages(size +
+ sizeof(struct efi_pool_allocation));
+
+ if (!buffer)
+ return EFI_INVALID_PARAMETER;
+
+ if (size == 0) {
+ *buffer = NULL;
+ return EFI_SUCCESS;
+ }
+
+ r = efi_allocate_pages(EFI_ALLOCATE_ANY_PAGES, pool_type, num_pages,
+ &addr, name);
+ if (r == EFI_SUCCESS) {
+ alloc = (struct efi_pool_allocation *)(uintptr_t)addr;
+ alloc->num_pages = num_pages;
+ alloc->checksum = checksum(alloc);
+ *buffer = alloc->data;
+ }
+
+ return r;
+}
+
+/**
+ * efi_alloc() - allocate boot services data pool memory
+ *
+ * Allocate memory from pool and zero it out.
+ *
+ * @size: number of bytes to allocate
+ * @name: name for informational purposes
+ * Return: pointer to allocated memory or NULL
+ */
+void *efi_alloc(size_t size, const char *name)
+{
+ void *buf;
+
+ if (efi_allocate_pool(EFI_BOOT_SERVICES_DATA, size, &buf, name) !=
+ EFI_SUCCESS) {
+ pr_err("out of memory\n");
+ return NULL;
+ }
+ memset(buf, 0, size);
+
+ return buf;
+}
+
+/**
+ * efi_realloc() - reallocate boot services data pool memory
+ *
+ * Reallocate memory from pool for a new size and copy the data from old one.
+ *
+ * @ptr: pointer to old buffer
+ * @size: number of bytes to allocate
+ * @name: name for informational purposes
+ * Return: EFI status to indicate success or not
+ */
+efi_status_t efi_realloc(void **ptr, size_t size, const char *name)
+{
+ void *new_ptr;
+ struct efi_pool_allocation *alloc;
+ u64 num_pages = efi_size_in_pages(size +
+ sizeof(struct efi_pool_allocation));
+ size_t old_size;
+
+ if (!*ptr) {
+ *ptr = efi_alloc(size, name);
+ if (*ptr)
+ return EFI_SUCCESS;
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ alloc = container_of(*ptr, struct efi_pool_allocation, data);
+
+ /* Check that this memory was allocated by efi_allocate_pool() */
+ if (((uintptr_t)alloc & EFI_PAGE_MASK) ||
+ alloc->checksum != checksum(alloc)) {
+ pr_err("%s: illegal realloc 0x%p\n", __func__, *ptr);
+ return EFI_INVALID_PARAMETER;
+ }
+
+ /* Don't realloc. The actual size in pages is the same. */
+ if (alloc->num_pages == num_pages)
+ return EFI_SUCCESS;
+
+ old_size = alloc->num_pages * EFI_PAGE_SIZE -
+ sizeof(struct efi_pool_allocation);
+
+ new_ptr = efi_alloc(size, name);
+ if (!new_ptr)
+ return EFI_OUT_OF_RESOURCES;
+
+ /* copy old data to new alloced buffer */
+ memcpy(new_ptr, *ptr, min(size, old_size));
+
+ /* free the old buffer */
+ efi_free_pool(*ptr);
+
+ *ptr = new_ptr;
+
+ return EFI_SUCCESS;
+}
+
+/**
+ * efi_free_pool() - free memory from pool
+ *
+ * @buffer: start of memory to be freed
+ * Return: status code
+ */
+efi_status_t efi_free_pool(void *buffer)
+{
+ efi_status_t ret;
+ struct efi_pool_allocation *alloc;
+
+ if (!buffer)
+ return EFI_INVALID_PARAMETER;
+
+ alloc = container_of(buffer, struct efi_pool_allocation, data);
+
+ /* Check that this memory was allocated by efi_allocate_pool() */
+ if (((uintptr_t)alloc & EFI_PAGE_MASK) ||
+ alloc->checksum != checksum(alloc)) {
+ pr_err("%s: illegal free 0x%p\n", __func__, buffer);
+ return EFI_INVALID_PARAMETER;
+ }
+ /* Avoid double free */
+ alloc->checksum = 0;
+
+ ret = efi_free_pages((uintptr_t)alloc, alloc->num_pages);
+
+ return ret;
+}
diff --git a/include/efi/loader.h b/include/efi/loader.h
index a5359f07f125..4a5670bcf672 100644
--- a/include/efi/loader.h
+++ b/include/efi/loader.h
@@ -13,6 +13,10 @@
/* Key identifying current memory map */
extern efi_uintn_t efi_memory_map_key;
+/* Allocate boot service data pool memory */
+void *efi_alloc(size_t len, const char *name);
+/* Reallocate boot service data pool memory */
+efi_status_t efi_realloc(void **ptr, size_t len, const char *name);
/* Allocate pages on the specified alignment */
void *efi_alloc_aligned_pages(u64 len, int memory_type, size_t align,
const char *name);
@@ -23,6 +27,12 @@ efi_status_t efi_allocate_pages(enum efi_allocate_type type,
const char *name);
/* EFI memory free function. */
efi_status_t efi_free_pages(uint64_t memory, efi_uintn_t pages);
+/* EFI memory allocator for small allocations */
+efi_status_t efi_allocate_pool(enum efi_memory_type pool_type,
+ efi_uintn_t size, void **buffer,
+ const char *name);
+/* EFI pool memory free function. */
+efi_status_t efi_free_pool(void *buffer);
/* Returns the EFI memory map */
efi_status_t efi_get_memory_map(efi_uintn_t *memory_map_size,
struct efi_memory_desc *memory_map,
--
2.47.3
^ permalink raw reply [flat|nested] 55+ messages in thread* [PATCH v1 07/54] efi: types: add EFI_RUNTIME_SECTION
2025-12-18 10:37 [PATCH v1 00/54] efi: implement EFI loader support in barebox Ahmad Fatoum
` (5 preceding siblings ...)
2025-12-18 10:37 ` [PATCH v1 06/54] efi: loader: add pool allocator Ahmad Fatoum
@ 2025-12-18 10:37 ` Ahmad Fatoum
2025-12-18 10:37 ` [PATCH v1 08/54] resource: assign memory banks a default type and attr Ahmad Fatoum
` (46 subsequent siblings)
53 siblings, 0 replies; 55+ messages in thread
From: Ahmad Fatoum @ 2025-12-18 10:37 UTC (permalink / raw)
To: barebox; +Cc: Ahmad Fatoum
With barebox acting as EFI loader, parts of it will remain resident in
RAM to provide runtime services. Add the necessary definitions for
annotating runtime data and services.
Signed-off-by: Ahmad Fatoum <a.fatoum@pengutronix.de>
---
include/efi/attributes.h | 84 ++++++++++++++++++++++++++++++++++++++++
1 file changed, 84 insertions(+)
diff --git a/include/efi/attributes.h b/include/efi/attributes.h
index 49d401b8fab1..fe5e5f823ab9 100644
--- a/include/efi/attributes.h
+++ b/include/efi/attributes.h
@@ -2,6 +2,10 @@
#ifndef _EFI_ATTRIBUTES_H_
#define _EFI_ATTRIBUTES_H_
+#ifndef __ASSEMBLY__
+
+#include <linux/compiler.h>
+
#ifdef __x86_64__
#define EFIAPI __attribute__((ms_abi))
#define efi_va_list __builtin_ms_va_list
@@ -18,4 +22,84 @@
#define efi_va_end va_end
#endif /* __x86_64__ */
+#ifdef CONFIG_EFI_RUNTIME
+/**
+ * __efi_runtime_data - declares a non-const variable for EFI runtime section
+ *
+ * This macro indicates that a variable is non-const and should go into the
+ * EFI runtime section, and thus still be available when the OS is running.
+ *
+ * Only use on variables not declared const.
+ *
+ * Example:
+ *
+ * ::
+ *
+ * static __efi_runtime_data my_computed_table[256];
+ */
+#ifndef __efi_runtime_data
+#define __efi_runtime_data __section(.efi_runtime.data)
+#endif
+
+/**
+ * __efi_runtime_rodata - declares a read-only variable for EFI runtime section
+ *
+ * This macro indicates that a variable is read-only (const) and should go into
+ * the EFI runtime section, and thus still be available when the OS is running.
+ *
+ * Only use on variables also declared const.
+ *
+ * Example:
+ *
+ * ::
+ *
+ * static const __efi_runtime_rodata my_const_table[] = { 1, 2, 3 };
+ */
+#ifndef __efi_runtime_rodata
+#define __efi_runtime_rodata __section(.efi_runtime.rodata)
+#endif
+
+/**
+ * __efi_runtime - declares a function for EFI runtime section
+ *
+ * This macro indicates that a function should go into the EFI runtime section,
+ * and thus still be available when the OS is running.
+ *
+ * Example:
+ *
+ * ::
+ *
+ * static __efi_runtime compute_my_table(void);
+ */
+#ifndef __efi_runtime
+#define __efi_runtime __section(.efi_runtime.text) \
+ notrace __no_sanitize_address __no_stack_protector
+#endif
+#endif /* CONFIG_EFI_RUNTIME */
+
+/* We #ifndef beforehand to allow compiler flags to override */
+#ifndef __efi_runtime_data
+#define __efi_runtime_data
+#endif
+
+#ifndef __efi_runtime_rodata
+#define __efi_runtime_rodata
+#endif
+
+#ifndef __efi_runtime
+#define __efi_runtime
+#endif
+
+#else /* __ASSEMBLY__ */
+
+#if defined(CONFIG_EFI_RUNTIME) && !defined(EFI_RUNTIME_SECTION)
+#define EFI_RUNTIME_SECTION(sect) .efi_runtime##sect
+#endif
+
+#ifndef EFI_RUNTIME_SECTION
+#define EFI_RUNTIME_SECTION(sect) sect
+#endif
+
+#endif
+
#endif
--
2.47.3
^ permalink raw reply [flat|nested] 55+ messages in thread* [PATCH v1 08/54] resource: assign memory banks a default type and attr
2025-12-18 10:37 [PATCH v1 00/54] efi: implement EFI loader support in barebox Ahmad Fatoum
` (6 preceding siblings ...)
2025-12-18 10:37 ` [PATCH v1 07/54] efi: types: add EFI_RUNTIME_SECTION Ahmad Fatoum
@ 2025-12-18 10:37 ` Ahmad Fatoum
2025-12-18 10:37 ` [PATCH v1 09/54] ARM: lds: add EFI runtime service section Ahmad Fatoum
` (45 subsequent siblings)
53 siblings, 0 replies; 55+ messages in thread
From: Ahmad Fatoum @ 2025-12-18 10:37 UTC (permalink / raw)
To: barebox; +Cc: Ahmad Fatoum
The resource_iter family of functions iterates over both resources and
the gaps between them and will inherit the parents type and attrs into
the gaps.
Give the SDRAM suitable attributes for the gaps, so users need not
special case the region_is_gap case if they are interested in collecting
all memory type and attributes.
---
common/memory.c | 4 ++++
efi/loader/memory.c | 39 +++++++++++++++++++--------------------
2 files changed, 23 insertions(+), 20 deletions(-)
diff --git a/common/memory.c b/common/memory.c
index 8974482ccb8b..0e07ba432053 100644
--- a/common/memory.c
+++ b/common/memory.c
@@ -237,6 +237,10 @@ int barebox_add_memory_bank(const char *name, resource_size_t start,
res->flags = IORESOURCE_MEM;
+ res->type = MEMTYPE_CONVENTIONAL;
+ res->attrs = MEMATTRS_NORMAL;
+ res->flags |= IORESOURCE_TYPE_VALID;
+
bank = xzalloc(sizeof(*bank));
bank->res = res;
diff --git a/efi/loader/memory.c b/efi/loader/memory.c
index 075b559367fe..6ed7f76714f9 100644
--- a/efi/loader/memory.c
+++ b/efi/loader/memory.c
@@ -121,7 +121,7 @@ static u64 efi_memory_type_default_attrs(enum efi_memory_type type)
return MEMATTRS_RW | MEMATTR_SP;
case EFI_UNUSABLE_MEMORY:
case EFI_MAX_MEMORY_TYPE:
- pr_warn("Unallocatable type %u\n", type); // FIXME
+ pr_warn("Unallocatable type %u\n", type);
return MEMATTRS_FAULT;
}
@@ -188,9 +188,8 @@ efi_status_t efi_allocate_pages(enum efi_allocate_type type,
efi_memory_type_to_resource_type(memory_type),
attrs);
if (!res) {
- pr_err("failed to request %s at page 0x%zx+%zu (%s)\n",
- namebuf, new_page, npages, typestr);
- dump_stack();
+ pr_err("failed to request %s at page 0x%llx+%zx (%s)\n",
+ namebuf, new_addr, npages << EFI_PAGE_SHIFT, typestr);
return EFI_OUT_OF_RESOURCES;
}
@@ -204,22 +203,6 @@ efi_status_t efi_allocate_pages(enum efi_allocate_type type,
return EFI_SUCCESS;
}
-static int free_efi_only(struct resource *res, void *data)
-{
- int *nfreed = data;
-
- if (!(res->flags & IORESOURCE_EFI_ALLOC)) {
- pr_warn("refusing to free non-EFI allocated resource %s at 0x%llx\n",
- res->name, res->start);
- *nfreed = -1;
- return false;
- }
-
- if (nfreed >= 0)
- ++*nfreed;
- return true;
-}
-
// SPDX-SnippetBegin
// SPDX-License-Identifier: GPL-2.0+
// SPDX-SnippetCopyrightText: 2016 Alexander Graf
@@ -281,6 +264,22 @@ void *efi_alloc_aligned_pages(u64 len, int memory_type, size_t align,
// SPDX-SnippetEnd
+static int free_efi_only(struct resource *res, void *data)
+{
+ int *nfreed = data;
+
+ if (!(res->flags & IORESOURCE_EFI_ALLOC)) {
+ pr_err("refusing to free non-EFI allocated resource %s at 0x%pap\n",
+ res->name, &res->start);
+ *nfreed = -1;
+ return false;
+ }
+
+ if (nfreed >= 0)
+ ++*nfreed;
+ return true;
+}
+
/**
* efi_free_pages() - free memory pages
*
--
2.47.3
^ permalink raw reply [flat|nested] 55+ messages in thread* [PATCH v1 09/54] ARM: lds: add EFI runtime service section
2025-12-18 10:37 [PATCH v1 00/54] efi: implement EFI loader support in barebox Ahmad Fatoum
` (7 preceding siblings ...)
2025-12-18 10:37 ` [PATCH v1 08/54] resource: assign memory banks a default type and attr Ahmad Fatoum
@ 2025-12-18 10:37 ` Ahmad Fatoum
2025-12-18 10:37 ` [PATCH v1 10/54] ARM: move needed assembly routines into EFI runtime section Ahmad Fatoum
` (44 subsequent siblings)
53 siblings, 0 replies; 55+ messages in thread
From: Ahmad Fatoum @ 2025-12-18 10:37 UTC (permalink / raw)
To: barebox; +Cc: Ahmad Fatoum
Now that we have __efi_runtime* attributes, let's make sure all data and
code marked as such is consecutive, so the OS can reclaim most of the
barebox memory after ExitBootServices.
It's critical that the EFI runtime section doesn't have any references
to outside itself and the NOCROSSREFS_FROM introduced here helps with
that, but it doesn't catch all issues, e.g. __efi_runtime_data global
variables that are referenced through the GOT.
A workaround for that will be introduced in a later commit.
Signed-off-by: Ahmad Fatoum <a.fatoum@pengutronix.de>
---
arch/arm/lib32/barebox.lds.S | 5 ++++
arch/arm/lib64/barebox.lds.S | 4 ++++
common/memory.c | 36 +++++++++++++++++++++++++++-
include/asm-generic/barebox.lds.h | 40 +++++++++++++++++++++++++++++++
include/asm-generic/sections.h | 10 ++++++++
5 files changed, 94 insertions(+), 1 deletion(-)
diff --git a/arch/arm/lib32/barebox.lds.S b/arch/arm/lib32/barebox.lds.S
index ede6889991f1..2d08b95758b7 100644
--- a/arch/arm/lib32/barebox.lds.S
+++ b/arch/arm/lib32/barebox.lds.S
@@ -68,6 +68,9 @@ SECTIONS
BAREBOX_RELOCATION_TABLE
_edata = .;
+
+ BAREBOX_EFI_RUNTIME
+
.image_end : { *(.__image_end) }
. = ALIGN(4);
@@ -86,3 +89,5 @@ SECTIONS
_end = .;
_barebox_image_size = __bss_start;
}
+
+NOCROSSREFS_FROM(.efi_runtime)
diff --git a/arch/arm/lib64/barebox.lds.S b/arch/arm/lib64/barebox.lds.S
index e2f1164c47f7..5ee5fbc3741e 100644
--- a/arch/arm/lib64/barebox.lds.S
+++ b/arch/arm/lib64/barebox.lds.S
@@ -44,6 +44,8 @@ SECTIONS
_edata = .;
+ BAREBOX_EFI_RUNTIME
+
.image_end : { *(.__image_end) }
. = ALIGN(4);
@@ -53,3 +55,5 @@ SECTIONS
_end = .;
_barebox_image_size = __bss_start;
}
+
+NOCROSSREFS_FROM(.efi_runtime)
diff --git a/common/memory.c b/common/memory.c
index 0e07ba432053..2cd6d29a8f2b 100644
--- a/common/memory.c
+++ b/common/memory.c
@@ -7,6 +7,7 @@
#include <common.h>
#include <memory.h>
+#include <efi/mode.h>
#include <of.h>
#include <init.h>
#include <linux/ioport.h>
@@ -112,6 +113,8 @@ struct resource *request_barebox_region(const char *name,
static int mem_malloc_resource(void)
{
+ __maybe_unused struct resource *res;
+ int ret = 0;
#if !defined __SANDBOX__
/*
* Normally it's a bug when one of these fails,
@@ -138,6 +141,37 @@ static int mem_malloc_resource(void)
(unsigned long)&_edata -
(unsigned long)&_sdata,
MEMATTRS_RW);
+#ifdef CONFIG_EFI_RUNTIME
+ if (!efi_is_payload()) {
+ /* We don't usually have much .rodata here, so we just
+ * fold it into the text area, so we don't waste the
+ * rest of the page
+ */
+ res = request_barebox_region("barebox EFI runtime code",
+ (unsigned long)&__efi_runtime_text_start,
+ (unsigned long)&__efi_runtime_rodata_stop -
+ (unsigned long)&__efi_runtime_text_start,
+ MEMATTRS_RX);
+ if (res) {
+ res->runtime = true;
+ res->type = MEMTYPE_RUNTIME_SERVICES_CODE;
+ } else {
+ ret = -EBUSY;
+ }
+
+ res = request_barebox_region("barebox EFI runtime data",
+ (unsigned long)&__efi_runtime_data_start,
+ (unsigned long)&__efi_runtime_data_stop -
+ (unsigned long)&__efi_runtime_data_start,
+ MEMATTRS_RW);
+ if (res) {
+ res->runtime = true;
+ res->type = MEMTYPE_RUNTIME_SERVICES_DATA;
+ } else {
+ ret = -EBUSY;
+ }
+ }
+#endif
request_barebox_region("barebox bss",
(unsigned long)&__bss_start,
(unsigned long)&__bss_stop -
@@ -149,7 +183,7 @@ static int mem_malloc_resource(void)
MEMTYPE_BOOT_SERVICES_DATA, MEMATTRS_RW);
#endif
- return 0;
+ return ret;
}
coredevice_initcall(mem_malloc_resource);
diff --git a/include/asm-generic/barebox.lds.h b/include/asm-generic/barebox.lds.h
index 2c0f4da9e5d4..092dbc6dffa6 100644
--- a/include/asm-generic/barebox.lds.h
+++ b/include/asm-generic/barebox.lds.h
@@ -155,6 +155,30 @@
BAREBOX_DEEP_PROBE \
BAREBOX_FUZZ_TESTS
+#ifdef CONFIG_EFI_RUNTIME
+#define BAREBOX_EFI_RUNTIME \
+ . = ALIGN(4096); \
+ .efi_runtime : { \
+ __efi_runtime_start = .; \
+ __efi_runtime_text_start = .; \
+ *(.efi_runtime.text*) \
+ __efi_runtime_text_stop = .; \
+ __efi_runtime_rodata_start = .; \
+ *(.efi_runtime.rodata*) \
+ __efi_runtime_rodata_stop = .; \
+ . = ALIGN(4096); \
+ __efi_runtime_data_start = .; \
+ *(.efi_runtime.data*) \
+ *(.efi_runtime.bss*) \
+ __efi_runtime_data_stop = .; \
+ __efi_runtime_stop = .; \
+ } \
+ . = ALIGN(4096);
+#else
+#define BAREBOX_EFI_RUNTIME
+#endif
+
+
#if defined(CONFIG_ARCH_BAREBOX_MAX_BARE_INIT_SIZE) && \
CONFIG_ARCH_BAREBOX_MAX_BARE_INIT_SIZE < CONFIG_BAREBOX_MAX_BARE_INIT_SIZE
#define MAX_BARE_INIT_SIZE CONFIG_ARCH_BAREBOX_MAX_BARE_INIT_SIZE
@@ -187,3 +211,19 @@ CONFIG_ARCH_BAREBOX_MAX_PBL_SIZE < CONFIG_BAREBOX_MAX_PBL_SIZE
#else
#define READONLY
#endif
+
+#ifdef CONFIG_IS_CLANG
+/* binutils implements this since v2.27 */
+#define NOCROSSREFS_TO(...)
+#endif
+
+#define NOCROSSREFS_FROM(sect) \
+ NOCROSSREFS_TO(.text sect) \
+ NOCROSSREFS_TO(.bss sect) \
+ NOCROSSREFS_TO(.rodata sect) \
+ NOCROSSREFS_TO(.data sect) \
+ NOCROSSREFS_TO(.bbenv sect) \
+ NOCROSSREFS_TO(.dynamic sect) \
+ NOCROSSREFS_TO(.got sect) \
+ NOCROSSREFS_TO(.rela sect) \
+ NOCROSSREFS_TO(.dynsym sect)
diff --git a/include/asm-generic/sections.h b/include/asm-generic/sections.h
index 35b0dd8cac1a..816e30341d82 100644
--- a/include/asm-generic/sections.h
+++ b/include/asm-generic/sections.h
@@ -10,6 +10,10 @@ extern char __start_rodata[], __end_rodata[];
extern char __bss_start[], __bss_stop[];
extern char _sdata[], _edata[];
extern char __bare_init_start[], __bare_init_end[];
+extern char __efi_runtime_start[], __efi_runtime_stop[];
+extern char __efi_runtime_text_start[], __efi_runtime_text_stop[];
+extern char __efi_runtime_data_start[], __efi_runtime_data_stop[];
+extern char __efi_runtime_rodata_start[], __efi_runtime_rodata_stop[];
extern char _end[];
extern char __image_start[];
extern char __image_end[];
@@ -39,4 +43,10 @@ static inline bool is_barebox_rodata(unsigned long addr)
addr < (unsigned long)__end_rodata;
}
+static inline bool in_barebox_efi_runtime(unsigned long addr)
+{
+ return addr >= (unsigned long)__efi_runtime_start &&
+ addr < (unsigned long)__efi_runtime_stop;
+}
+
#endif /* _ASM_GENERIC_SECTIONS_H_ */
--
2.47.3
^ permalink raw reply [flat|nested] 55+ messages in thread* [PATCH v1 10/54] ARM: move needed assembly routines into EFI runtime section
2025-12-18 10:37 [PATCH v1 00/54] efi: implement EFI loader support in barebox Ahmad Fatoum
` (8 preceding siblings ...)
2025-12-18 10:37 ` [PATCH v1 09/54] ARM: lds: add EFI runtime service section Ahmad Fatoum
@ 2025-12-18 10:37 ` Ahmad Fatoum
2025-12-18 10:37 ` [PATCH v1 11/54] crypto: crc32: implement position independent CRC32 Ahmad Fatoum
` (43 subsequent siblings)
53 siblings, 0 replies; 55+ messages in thread
From: Ahmad Fatoum @ 2025-12-18 10:37 UTC (permalink / raw)
To: barebox; +Cc: Ahmad Fatoum
We want the usual string operation to be available normally in the
runtime services code, so mark them appropriately.
To implement the runtime services for rebooting and shutting down the
system, move the smccc trampolines into the EFI runtime section as well.
Signed-off-by: Ahmad Fatoum <a.fatoum@pengutronix.de>
---
arch/arm/cpu/smccc-call_32.S | 4 +++-
arch/arm/cpu/smccc-call_64.S | 5 ++++-
arch/arm/lib32/memcpy.S | 3 ++-
arch/arm/lib32/memmove.S | 3 ++-
arch/arm/lib32/memset.S | 3 ++-
arch/arm/lib64/memcpy.S | 3 +++
arch/arm/lib64/memset.S | 3 +++
7 files changed, 19 insertions(+), 5 deletions(-)
diff --git a/arch/arm/cpu/smccc-call_32.S b/arch/arm/cpu/smccc-call_32.S
index 9875e1f94755..02e892be5d8d 100644
--- a/arch/arm/cpu/smccc-call_32.S
+++ b/arch/arm/cpu/smccc-call_32.S
@@ -2,9 +2,11 @@
/* SPDX-FileCopyrightText: 2015 Linaro Limited */
#include <linux/linkage.h>
-
+#include <efi/attributes.h>
#include <asm/unwind.h>
+.section EFI_RUNTIME_SECTION(.text.smccc)
+
.arch_extension sec
.arch_extension virt
.arm
diff --git a/arch/arm/cpu/smccc-call_64.S b/arch/arm/cpu/smccc-call_64.S
index c2959050d2ef..45e334cfbe2c 100644
--- a/arch/arm/cpu/smccc-call_64.S
+++ b/arch/arm/cpu/smccc-call_64.S
@@ -2,9 +2,12 @@
/* SPDX-FileCopyrightText: 2015 Linaro Limited */
#include <linux/linkage.h>
+#include <efi/attributes.h>
#include <linux/arm-smccc.h>
#include <asm/asm-offsets.h>
+.section EFI_RUNTIME_SECTION(.text.smccc)
+
.macro SMCCC instr
.cfi_startproc
\instr #0
@@ -39,4 +42,4 @@ ENDPROC(__arm_smccc_smc)
*/
ENTRY(__arm_smccc_hvc)
SMCCC hvc
-ENDPROC(__arm_smccc_hvc)
\ No newline at end of file
+ENDPROC(__arm_smccc_hvc)
diff --git a/arch/arm/lib32/memcpy.S b/arch/arm/lib32/memcpy.S
index e48e077c5941..a5cc1a6c2f46 100644
--- a/arch/arm/lib32/memcpy.S
+++ b/arch/arm/lib32/memcpy.S
@@ -10,6 +10,7 @@
#include <linux/linkage.h>
#include <asm/assembler.h>
#include <asm/unwind.h>
+#include <efi/attributes.h>
#define LDR1W_SHIFT 0
#define STR1W_SHIFT 0
@@ -51,7 +52,7 @@ UNWIND( .save {r0, \regs} )
ldmfd sp!, {r0, \regs}
.endm
- .text
+.section EFI_RUNTIME_SECTION(.text)
/* Prototype: void *memcpy(void *dest, const void *src, size_t n); */
diff --git a/arch/arm/lib32/memmove.S b/arch/arm/lib32/memmove.S
index 836286bc990f..59b4031b0797 100644
--- a/arch/arm/lib32/memmove.S
+++ b/arch/arm/lib32/memmove.S
@@ -10,8 +10,9 @@
#include <linux/linkage.h>
#include <asm/assembler.h>
#include <asm/unwind.h>
+#include <efi/attributes.h>
- .text
+ .section EFI_RUNTIME_SECTION(.text)
/*
* Prototype: void *memmove(void *dest, const void *src, size_t n);
diff --git a/arch/arm/lib32/memset.S b/arch/arm/lib32/memset.S
index 57dd1aa6daad..f3b0a683157b 100644
--- a/arch/arm/lib32/memset.S
+++ b/arch/arm/lib32/memset.S
@@ -9,8 +9,9 @@
#include <linux/linkage.h>
#include <asm/assembler.h>
#include <asm/unwind.h>
+#include <efi/attributes.h>
- .text
+ .section EFI_RUNTIME_SECTION(.text)
.align 5
ENTRY(__memset)
diff --git a/arch/arm/lib64/memcpy.S b/arch/arm/lib64/memcpy.S
index 98b453d3fd79..a57ebee61aba 100644
--- a/arch/arm/lib64/memcpy.S
+++ b/arch/arm/lib64/memcpy.S
@@ -8,6 +8,7 @@
#include <linux/linkage.h>
#include <asm/assembler.h>
+#include <efi/attributes.h>
/* Assumptions:
*
@@ -15,6 +16,8 @@
*
*/
+.section EFI_RUNTIME_SECTION(.text)
+
#define L(label) .L ## label
#define dstin x0
diff --git a/arch/arm/lib64/memset.S b/arch/arm/lib64/memset.S
index f059203983a2..d0659b424333 100644
--- a/arch/arm/lib64/memset.S
+++ b/arch/arm/lib64/memset.S
@@ -13,6 +13,9 @@
#include <linux/linkage.h>
#include <asm/assembler.h>
#include <asm/cache.h>
+#include <efi/attributes.h>
+
+.section EFI_RUNTIME_SECTION(.text)
/*
* Fill in the buffer with character c (alignment handled by the hardware)
--
2.47.3
^ permalink raw reply [flat|nested] 55+ messages in thread* [PATCH v1 11/54] crypto: crc32: implement position independent CRC32
2025-12-18 10:37 [PATCH v1 00/54] efi: implement EFI loader support in barebox Ahmad Fatoum
` (9 preceding siblings ...)
2025-12-18 10:37 ` [PATCH v1 10/54] ARM: move needed assembly routines into EFI runtime section Ahmad Fatoum
@ 2025-12-18 10:37 ` Ahmad Fatoum
2025-12-18 10:37 ` [PATCH v1 12/54] efi: loader: add support for tracing calls back into UEFI Ahmad Fatoum
` (42 subsequent siblings)
53 siblings, 0 replies; 55+ messages in thread
From: Ahmad Fatoum @ 2025-12-18 10:37 UTC (permalink / raw)
To: barebox; +Cc: Ahmad Fatoum
For use in the barebox EFI runtime, implement a self-contained CRC32
that will be placed into the .efi_runtime section.
The code is in the public domain.
Signed-off-by: Ahmad Fatoum <a.fatoum@pengutronix.de>
---
crypto/crc32.c | 24 ++++++++++++++++++++++++
include/crc.h | 2 ++
2 files changed, 26 insertions(+)
diff --git a/crypto/crc32.c b/crypto/crc32.c
index 5013556c0bea..bcfc8e99ee39 100644
--- a/crypto/crc32.c
+++ b/crypto/crc32.c
@@ -17,9 +17,11 @@
#include <malloc.h>
#include <linux/ctype.h>
#include <errno.h>
+#include <efi/attributes.h>
#define STATIC
#else
#define STATIC static inline
+#define __efi_runtime
#endif
static uint32_t crc_table[sizeof(uint32_t) * 256];
@@ -107,6 +109,28 @@ STATIC uint32_t crc32(uint32_t crc, const void *buf, unsigned int len)
EXPORT_SYMBOL(crc32);
#endif
+/* Taken from Hacker's Delight 2nd Edition by Henry S. Warren */
+STATIC __efi_runtime uint32_t __pi_crc32(uint32_t crc, const void *_buf, unsigned int len)
+{
+ const unsigned char *buf = _buf;
+ uint32_t mask;
+
+ crc = ~crc;
+ for (int i = 0; i < len; i++) {
+ crc = crc ^ buf[i];
+ for (int j = 7; j >= 0; j--) {
+ mask = -(crc & 1);
+ crc = (crc >> 1) ^ (0xEDB88320 & mask);
+ }
+ }
+
+ return ~crc;
+}
+
+#ifdef __BAREBOX__
+EXPORT_SYMBOL(__pi_crc32);
+#endif
+
STATIC uint32_t crc32_be(uint32_t crc, const void *_buf, unsigned int len)
{
const unsigned char *buf = _buf;
diff --git a/include/crc.h b/include/crc.h
index 3eba635dbaa4..c7d52f7cd078 100644
--- a/include/crc.h
+++ b/include/crc.h
@@ -18,4 +18,6 @@ uint32_t crc32_no_comp(uint32_t, const void *, unsigned int);
int file_crc(char *filename, unsigned long start, unsigned long size,
unsigned long *crc, unsigned long *total);
+uint32_t __pi_crc32(uint32_t, const void *, unsigned int);
+
#endif /* __INCLUDE_CRC_H */
--
2.47.3
^ permalink raw reply [flat|nested] 55+ messages in thread* [PATCH v1 12/54] efi: loader: add support for tracing calls back into UEFI
2025-12-18 10:37 [PATCH v1 00/54] efi: implement EFI loader support in barebox Ahmad Fatoum
` (10 preceding siblings ...)
2025-12-18 10:37 ` [PATCH v1 11/54] crypto: crc32: implement position independent CRC32 Ahmad Fatoum
@ 2025-12-18 10:37 ` Ahmad Fatoum
2025-12-18 10:37 ` [PATCH v1 13/54] efi: loader: add table utility functions Ahmad Fatoum
` (41 subsequent siblings)
53 siblings, 0 replies; 55+ messages in thread
From: Ahmad Fatoum @ 2025-12-18 10:37 UTC (permalink / raw)
To: barebox; +Cc: Ahmad Fatoum
This will be very verbose, but when trying to pinpoint what operation
is bothering an EFI payload, it's very useful to know every entry and
exit into barebox-provided services.
Signed-off-by: Ahmad Fatoum <a.fatoum@pengutronix.de>
---
common/Kconfig.debug | 7 +++
efi/loader/Makefile | 1 +
efi/loader/trace.c | 44 +++++++++++++++
include/efi/loader/trace.h | 111 +++++++++++++++++++++++++++++++++++++
4 files changed, 163 insertions(+)
create mode 100644 efi/loader/trace.c
create mode 100644 include/efi/loader/trace.h
diff --git a/common/Kconfig.debug b/common/Kconfig.debug
index 2de885ebb3f8..64127143a5d6 100644
--- a/common/Kconfig.debug
+++ b/common/Kconfig.debug
@@ -77,6 +77,13 @@ config DEBUG_PROBES
Most consoles do not implement a remove callback to remain operable until
the very end. Consoles using DMA, however, must be removed.
+config DEBUG_EFI_LOADER_ENTRY
+ bool "Debug EFI loader entry/exit"
+ depends on EFI_LOADER
+ help
+ If enabled, this will print whenever an EFI payload/app calls
+ into barebox or is returned to before ExitBootServices.
+
config DMA_API_DEBUG
bool "Enable debugging of DMA-API usage"
depends on HAS_DMA
diff --git a/efi/loader/Makefile b/efi/loader/Makefile
index 0475cfc7452f..1f07cf6005b9 100644
--- a/efi/loader/Makefile
+++ b/efi/loader/Makefile
@@ -1,3 +1,4 @@
# SPDX-License-Identifier: GPL-2.0
obj-y += memory.o pool_alloc.o
+obj-y += trace.o
diff --git a/efi/loader/trace.c b/efi/loader/trace.c
new file mode 100644
index 000000000000..9eb22805e12e
--- /dev/null
+++ b/efi/loader/trace.c
@@ -0,0 +1,44 @@
+// SPDX-License-Identifier: GPL-2.0+
+
+#include <efi/loader/trace.h>
+#include <linux/minmax.h>
+
+/* 1 if inside barebox code, 0 if inside EFI payload code */
+static int nesting_level = 1;
+
+/**
+ * indent_string() - returns a string for indenting with two spaces per level
+ * @level: indent level
+ *
+ * A maximum of ten indent levels is supported. Higher indent levels will be
+ * truncated.
+ *
+ * Return: A string for indenting with two spaces per level is
+ * returned.
+ */
+static const char *indent_string(int level)
+{
+ const char *indent = " ";
+ const int max = strlen(indent);
+
+ if (level < 0)
+ return "!!EFI_EXIT called without matching EFI_ENTRY!! ";
+
+ level = min(max, level * 2);
+ return &indent[max - level];
+}
+
+const char *__efi_nesting(void)
+{
+ return indent_string(nesting_level);
+}
+
+const char *__efi_nesting_inc(void)
+{
+ return indent_string(nesting_level++);
+}
+
+const char *__efi_nesting_dec(void)
+{
+ return indent_string(--nesting_level);
+}
diff --git a/include/efi/loader/trace.h b/include/efi/loader/trace.h
new file mode 100644
index 000000000000..93bba7bb88c7
--- /dev/null
+++ b/include/efi/loader/trace.h
@@ -0,0 +1,111 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+#ifndef __EFI_LOADER_TRACE_H__
+#define __EFI_LOADER_TRACE_H__
+
+#include <efi/error.h>
+#include <linux/printk.h>
+
+#ifndef __EFI_PRINT
+#ifdef CONFIG_DEBUG_EFI_LOADER_ENTRY
+#define __EFI_PRINT(...) pr_info(__VA_ARGS__)
+#define __EFI_WARN(...) pr_warning(__VA_ARGS__)
+#else
+#define __EFI_PRINT(...) pr_debug(__VA_ARGS__)
+#define __EFI_WARN(...) pr_debug(__VA_ARGS__)
+#endif
+#endif
+
+const char *__efi_nesting(void);
+const char *__efi_nesting_inc(void);
+const char *__efi_nesting_dec(void);
+
+/*
+ * Enter the barebox world from UEFI:
+ */
+#ifndef EFI_ENTRY
+#define EFI_ENTRY(format, ...) do { \
+ __EFI_PRINT("%sEFI: Entry %s(" format ")\n", __efi_nesting_inc(), \
+ __func__, ##__VA_ARGS__); \
+ } while(0)
+#endif
+
+/*
+ * Exit the barebox world back to UEFI:
+ */
+#ifndef EFI_EXIT
+#define EFI_EXIT(ret) ({ \
+ typeof(ret) _r = ret; \
+ __EFI_PRINT("%sEFI: Exit: %s: %s (%u)\n", __efi_nesting_dec(), \
+ __func__, efi_strerror((uintptr_t)_r), (u32)((uintptr_t) _r & ~EFI_ERROR_MASK)); \
+ _r; \
+ })
+#endif
+
+#ifndef EFI_EXIT2
+#define EFI_EXIT2(ret, val) ({ \
+ typeof(ret) _r = ret; \
+ __EFI_PRINT("%sEFI: Exit: %s: %s (%u) = 0x%llx\n", __efi_nesting_dec(), \
+ __func__, efi_strerror((uintptr_t)_r), (u32)((uintptr_t) _r & ~EFI_ERROR_MASK), \
+ (u64)(uintptr_t)(val)); \
+ _r; \
+ })
+#endif
+
+/*
+ * Call non-void UEFI function from barebox and retrieve return value:
+ */
+#ifndef EFI_CALL
+#define EFI_CALL(exp) ({ \
+ __EFI_PRINT("%sEFI: Call: %s\n", __efi_nesting_inc(), #exp); \
+ typeof(exp) _r = exp; \
+ __EFI_PRINT("%sEFI: %lu returned by %s\n", __efi_nesting_dec(), \
+ (unsigned long)((uintptr_t)_r & ~EFI_ERROR_MASK), #exp); \
+ _r; \
+})
+#endif
+
+/**
+ * define EFI_RETURN() - return from EFI_CALL in efi_start_image()
+ *
+ * @ret: status code
+ */
+#ifndef EFI_RETURN
+#define EFI_RETURN(ret) ({ \
+ typeof(ret) _r = ret; \
+ __EFI_PRINT("%sEFI: %lu returned by started image", __efi_nesting_dec(), \
+ (unsigned long)((uintptr_t)_r & ~EFI_ERROR_MASK)); \
+})
+#endif
+
+/*
+ * Call void UEFI function from barebox:
+ */
+#ifndef EFI_CALL_VOID
+#define EFI_CALL_VOID(exp) do { \
+ __EFI_PRINT("%sEFI: Call: %s\n", __efi_nesting_inc(), #exp); \
+ exp; \
+ __EFI_PRINT("%sEFI: Return From: %s\n", __efi_nesting_dec(), #exp); \
+ } while(0)
+#endif
+
+/*
+ * Write an indented message with EFI prefix
+ */
+#ifndef EFI_PRINT
+#define EFI_PRINT(format, ...) ({ \
+ __EFI_PRINT("%sEFI: " format, __efi_nesting(), \
+ ##__VA_ARGS__); \
+ })
+#endif
+
+/*
+ * Write an indented warning with EFI prefix
+ */
+#ifndef EFI_WARN
+#define EFI_WARN(format, ...) ({ \
+ __EFI_WARN("%sEFI: " format, __efi_nesting(), \
+ ##__VA_ARGS__); \
+ })
+#endif
+
+#endif
--
2.47.3
^ permalink raw reply [flat|nested] 55+ messages in thread* [PATCH v1 13/54] efi: loader: add table utility functions
2025-12-18 10:37 [PATCH v1 00/54] efi: implement EFI loader support in barebox Ahmad Fatoum
` (11 preceding siblings ...)
2025-12-18 10:37 ` [PATCH v1 12/54] efi: loader: add support for tracing calls back into UEFI Ahmad Fatoum
@ 2025-12-18 10:37 ` Ahmad Fatoum
2025-12-18 10:37 ` [PATCH v1 14/54] lib: add charset helpers Ahmad Fatoum
` (40 subsequent siblings)
53 siblings, 0 replies; 55+ messages in thread
From: Ahmad Fatoum @ 2025-12-18 10:37 UTC (permalink / raw)
To: barebox; +Cc: Ahmad Fatoum
Add some common helpers for use in the upcoming EFI boot and runtime
services implementation.
Signed-off-by: Ahmad Fatoum <a.fatoum@pengutronix.de>
---
efi/Kconfig | 1 +
efi/loader/Makefile | 1 +
efi/loader/table.c | 31 +++++++++++++++++++++++++++++++
include/efi/loader.h | 8 ++++++++
4 files changed, 41 insertions(+)
create mode 100644 efi/loader/table.c
diff --git a/efi/Kconfig b/efi/Kconfig
index 766d6b0e3094..590dd7918440 100644
--- a/efi/Kconfig
+++ b/efi/Kconfig
@@ -32,6 +32,7 @@ config EFI_LOADER
select EFI_GUID
select EFI_DEVICEPATH
select MEMORY_ATTRIBUTES
+ select CRC32
help
Select this option if you want to run UEFI applications (like GNU
GRUB or an EFI-stubbed kernel) on top of barebox.
diff --git a/efi/loader/Makefile b/efi/loader/Makefile
index 1f07cf6005b9..b4294d746b8b 100644
--- a/efi/loader/Makefile
+++ b/efi/loader/Makefile
@@ -2,3 +2,4 @@
obj-y += memory.o pool_alloc.o
obj-y += trace.o
+obj-y += table.o
diff --git a/efi/loader/table.c b/efi/loader/table.c
new file mode 100644
index 000000000000..0c200365b835
--- /dev/null
+++ b/efi/loader/table.c
@@ -0,0 +1,31 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#include <efi/services.h>
+#include <efi/loader.h>
+#include <efi/error.h>
+#include <efi/attributes.h>
+#include <crc.h>
+
+/**
+ * efi_update_table_header_crc32() - Update crc32 in table header
+ *
+ * @table: EFI table
+ */
+void __efi_runtime efi_update_table_header_crc32(struct efi_table_hdr *table)
+{
+ table->crc32 = __pi_crc32(0, table, table->headersize);
+}
+
+/**
+ * efi_unimplemented() - replacement function, returns EFI_UNSUPPORTED
+ *
+ * This function is used after SetVirtualAddressMap() is called as replacement
+ * for services that are not available anymore due to constraints of our
+ * implementation.
+ *
+ * Return: EFI_UNSUPPORTED
+ */
+efi_status_t __efi_runtime EFIAPI efi_unimplemented(void)
+{
+ return EFI_UNSUPPORTED;
+}
diff --git a/include/efi/loader.h b/include/efi/loader.h
index 4a5670bcf672..fcca8676479d 100644
--- a/include/efi/loader.h
+++ b/include/efi/loader.h
@@ -8,6 +8,8 @@
#include <efi/services.h>
#include <efi/memory.h>
+struct efi_table_hdr;
+
#define EFI_SPECIFICATION_VERSION (2 << 16 | 80)
/* Key identifying current memory map */
@@ -40,4 +42,10 @@ efi_status_t efi_get_memory_map(efi_uintn_t *memory_map_size,
efi_uintn_t *descriptor_size,
uint32_t *descriptor_version);
+/* Update CRC32 in table header */
+void efi_update_table_header_crc32(struct efi_table_hdr *table);
+
+/* replacement function, returns EFI_UNSUPPORTED */
+efi_status_t __efi_runtime EFIAPI efi_unimplemented(void);
+
#endif /* _EFI_LOADER_H */
--
2.47.3
^ permalink raw reply [flat|nested] 55+ messages in thread* [PATCH v1 14/54] lib: add charset helpers
2025-12-18 10:37 [PATCH v1 00/54] efi: implement EFI loader support in barebox Ahmad Fatoum
` (12 preceding siblings ...)
2025-12-18 10:37 ` [PATCH v1 13/54] efi: loader: add table utility functions Ahmad Fatoum
@ 2025-12-18 10:37 ` Ahmad Fatoum
2025-12-18 10:37 ` [PATCH v1 15/54] efi: loader: add object handling API Ahmad Fatoum
` (39 subsequent siblings)
53 siblings, 0 replies; 55+ messages in thread
From: Ahmad Fatoum @ 2025-12-18 10:37 UTC (permalink / raw)
To: barebox; +Cc: Ahmad Fatoum
For use by the UEFI loader code that is going to be imported, import also
the necessary character set conversion utilities from U-Boot.
Some consolidation with the Linux UCS2 functions may be possible, but
I would leave this clean up to future me.
Signed-off-by: Ahmad Fatoum <a.fatoum@pengutronix.de>
---
efi/Kconfig | 1 +
include/capitalization.h | 2029 ++++++++++++++++++++++++++++++++++++++
include/charset.h | 346 +++++++
lib/Kconfig | 16 +
lib/Makefile | 2 +
lib/charset.c | 583 +++++++++++
lib/cp437.h | 48 +
7 files changed, 3025 insertions(+)
create mode 100644 include/capitalization.h
create mode 100644 include/charset.h
create mode 100644 lib/charset.c
create mode 100644 lib/cp437.h
diff --git a/efi/Kconfig b/efi/Kconfig
index 590dd7918440..31fccaaa8640 100644
--- a/efi/Kconfig
+++ b/efi/Kconfig
@@ -31,6 +31,7 @@ config EFI_LOADER
select EFI
select EFI_GUID
select EFI_DEVICEPATH
+ select CHARSET
select MEMORY_ATTRIBUTES
select CRC32
help
diff --git a/include/capitalization.h b/include/capitalization.h
new file mode 100644
index 000000000000..ee66fd47c553
--- /dev/null
+++ b/include/capitalization.h
@@ -0,0 +1,2029 @@
+/* SPDX-License-Identifier: Unicode-DFS-2016 */
+/* SPDX-Comment: Origin-URL: https://github.com/u-boot/u-boot/blob/5b2118a2ad3bd1b75dd1650db193a2f6182fa3de/include/capitalization.h */
+/*
+ * Capitalization tables
+ */
+
+struct capitalization_table {
+ u16 upper;
+ u16 lower;
+};
+
+/*
+ * Correspondence table for small and capital Unicode letters in the range of
+ * 0x0000 - 0xffff based on http://www.unicode.org/Public/UCA/11.0.0/allkeys.txt
+ */
+#define UNICODE_CAPITALIZATION_TABLE { \
+ { 0x0531, /* ARMENIAN CAPITAL LETTER AYB */ \
+ 0x0561, /* ARMENIAN SMALL LETTER AYB */ }, \
+ { 0x0532, /* ARMENIAN CAPITAL LETTER BEN */ \
+ 0x0562, /* ARMENIAN SMALL LETTER BEN */ }, \
+ { 0x053E, /* ARMENIAN CAPITAL LETTER CA */ \
+ 0x056E, /* ARMENIAN SMALL LETTER CA */ }, \
+ { 0x0549, /* ARMENIAN CAPITAL LETTER CHA */ \
+ 0x0579, /* ARMENIAN SMALL LETTER CHA */ }, \
+ { 0x0543, /* ARMENIAN CAPITAL LETTER CHEH */ \
+ 0x0573, /* ARMENIAN SMALL LETTER CHEH */ }, \
+ { 0x0551, /* ARMENIAN CAPITAL LETTER CO */ \
+ 0x0581, /* ARMENIAN SMALL LETTER CO */ }, \
+ { 0x0534, /* ARMENIAN CAPITAL LETTER DA */ \
+ 0x0564, /* ARMENIAN SMALL LETTER DA */ }, \
+ { 0x0535, /* ARMENIAN CAPITAL LETTER ECH */ \
+ 0x0565, /* ARMENIAN SMALL LETTER ECH */ }, \
+ { 0x0537, /* ARMENIAN CAPITAL LETTER EH */ \
+ 0x0567, /* ARMENIAN SMALL LETTER EH */ }, \
+ { 0x0538, /* ARMENIAN CAPITAL LETTER ET */ \
+ 0x0568, /* ARMENIAN SMALL LETTER ET */ }, \
+ { 0x0556, /* ARMENIAN CAPITAL LETTER FEH */ \
+ 0x0586, /* ARMENIAN SMALL LETTER FEH */ }, \
+ { 0x0542, /* ARMENIAN CAPITAL LETTER GHAD */ \
+ 0x0572, /* ARMENIAN SMALL LETTER GHAD */ }, \
+ { 0x0533, /* ARMENIAN CAPITAL LETTER GIM */ \
+ 0x0563, /* ARMENIAN SMALL LETTER GIM */ }, \
+ { 0x0540, /* ARMENIAN CAPITAL LETTER HO */ \
+ 0x0570, /* ARMENIAN SMALL LETTER HO */ }, \
+ { 0x053B, /* ARMENIAN CAPITAL LETTER INI */ \
+ 0x056B, /* ARMENIAN SMALL LETTER INI */ }, \
+ { 0x0541, /* ARMENIAN CAPITAL LETTER JA */ \
+ 0x0571, /* ARMENIAN SMALL LETTER JA */ }, \
+ { 0x054B, /* ARMENIAN CAPITAL LETTER JHEH */ \
+ 0x057B, /* ARMENIAN SMALL LETTER JHEH */ }, \
+ { 0x0554, /* ARMENIAN CAPITAL LETTER KEH */ \
+ 0x0584, /* ARMENIAN SMALL LETTER KEH */ }, \
+ { 0x053F, /* ARMENIAN CAPITAL LETTER KEN */ \
+ 0x056F, /* ARMENIAN SMALL LETTER KEN */ }, \
+ { 0x053C, /* ARMENIAN CAPITAL LETTER LIWN */ \
+ 0x056C, /* ARMENIAN SMALL LETTER LIWN */ }, \
+ { 0x0544, /* ARMENIAN CAPITAL LETTER MEN */ \
+ 0x0574, /* ARMENIAN SMALL LETTER MEN */ }, \
+ { 0x0546, /* ARMENIAN CAPITAL LETTER NOW */ \
+ 0x0576, /* ARMENIAN SMALL LETTER NOW */ }, \
+ { 0x0555, /* ARMENIAN CAPITAL LETTER OH */ \
+ 0x0585, /* ARMENIAN SMALL LETTER OH */ }, \
+ { 0x054A, /* ARMENIAN CAPITAL LETTER PEH */ \
+ 0x057A, /* ARMENIAN SMALL LETTER PEH */ }, \
+ { 0x0553, /* ARMENIAN CAPITAL LETTER PIWR */ \
+ 0x0583, /* ARMENIAN SMALL LETTER PIWR */ }, \
+ { 0x054C, /* ARMENIAN CAPITAL LETTER RA */ \
+ 0x057C, /* ARMENIAN SMALL LETTER RA */ }, \
+ { 0x0550, /* ARMENIAN CAPITAL LETTER REH */ \
+ 0x0580, /* ARMENIAN SMALL LETTER REH */ }, \
+ { 0x054D, /* ARMENIAN CAPITAL LETTER SEH */ \
+ 0x057D, /* ARMENIAN SMALL LETTER SEH */ }, \
+ { 0x0547, /* ARMENIAN CAPITAL LETTER SHA */ \
+ 0x0577, /* ARMENIAN SMALL LETTER SHA */ }, \
+ { 0x054F, /* ARMENIAN CAPITAL LETTER TIWN */ \
+ 0x057F, /* ARMENIAN SMALL LETTER TIWN */ }, \
+ { 0x0539, /* ARMENIAN CAPITAL LETTER TO */ \
+ 0x0569, /* ARMENIAN SMALL LETTER TO */ }, \
+ { 0x054E, /* ARMENIAN CAPITAL LETTER VEW */ \
+ 0x057E, /* ARMENIAN SMALL LETTER VEW */ }, \
+ { 0x0548, /* ARMENIAN CAPITAL LETTER VO */ \
+ 0x0578, /* ARMENIAN SMALL LETTER VO */ }, \
+ { 0x053D, /* ARMENIAN CAPITAL LETTER XEH */ \
+ 0x056D, /* ARMENIAN SMALL LETTER XEH */ }, \
+ { 0x0545, /* ARMENIAN CAPITAL LETTER YI */ \
+ 0x0575, /* ARMENIAN SMALL LETTER YI */ }, \
+ { 0x0552, /* ARMENIAN CAPITAL LETTER YIWN */ \
+ 0x0582, /* ARMENIAN SMALL LETTER YIWN */ }, \
+ { 0x0536, /* ARMENIAN CAPITAL LETTER ZA */ \
+ 0x0566, /* ARMENIAN SMALL LETTER ZA */ }, \
+ { 0x053A, /* ARMENIAN CAPITAL LETTER ZHE */ \
+ 0x056A, /* ARMENIAN SMALL LETTER ZHE */ }, \
+ { 0x24B6, /* CIRCLED LATIN CAPITAL LETTER A */ \
+ 0x24D0, /* CIRCLED LATIN SMALL LETTER A */ }, \
+ { 0x24B7, /* CIRCLED LATIN CAPITAL LETTER B */ \
+ 0x24D1, /* CIRCLED LATIN SMALL LETTER B */ }, \
+ { 0x24B8, /* CIRCLED LATIN CAPITAL LETTER C */ \
+ 0x24D2, /* CIRCLED LATIN SMALL LETTER C */ }, \
+ { 0x24B9, /* CIRCLED LATIN CAPITAL LETTER D */ \
+ 0x24D3, /* CIRCLED LATIN SMALL LETTER D */ }, \
+ { 0x24BA, /* CIRCLED LATIN CAPITAL LETTER E */ \
+ 0x24D4, /* CIRCLED LATIN SMALL LETTER E */ }, \
+ { 0x24BB, /* CIRCLED LATIN CAPITAL LETTER F */ \
+ 0x24D5, /* CIRCLED LATIN SMALL LETTER F */ }, \
+ { 0x24BC, /* CIRCLED LATIN CAPITAL LETTER G */ \
+ 0x24D6, /* CIRCLED LATIN SMALL LETTER G */ }, \
+ { 0x24BD, /* CIRCLED LATIN CAPITAL LETTER H */ \
+ 0x24D7, /* CIRCLED LATIN SMALL LETTER H */ }, \
+ { 0x24BE, /* CIRCLED LATIN CAPITAL LETTER I */ \
+ 0x24D8, /* CIRCLED LATIN SMALL LETTER I */ }, \
+ { 0x24BF, /* CIRCLED LATIN CAPITAL LETTER J */ \
+ 0x24D9, /* CIRCLED LATIN SMALL LETTER J */ }, \
+ { 0x24C0, /* CIRCLED LATIN CAPITAL LETTER K */ \
+ 0x24DA, /* CIRCLED LATIN SMALL LETTER K */ }, \
+ { 0x24C1, /* CIRCLED LATIN CAPITAL LETTER L */ \
+ 0x24DB, /* CIRCLED LATIN SMALL LETTER L */ }, \
+ { 0x24C2, /* CIRCLED LATIN CAPITAL LETTER M */ \
+ 0x24DC, /* CIRCLED LATIN SMALL LETTER M */ }, \
+ { 0x24C3, /* CIRCLED LATIN CAPITAL LETTER N */ \
+ 0x24DD, /* CIRCLED LATIN SMALL LETTER N */ }, \
+ { 0x24C4, /* CIRCLED LATIN CAPITAL LETTER O */ \
+ 0x24DE, /* CIRCLED LATIN SMALL LETTER O */ }, \
+ { 0x24C5, /* CIRCLED LATIN CAPITAL LETTER P */ \
+ 0x24DF, /* CIRCLED LATIN SMALL LETTER P */ }, \
+ { 0x24C6, /* CIRCLED LATIN CAPITAL LETTER Q */ \
+ 0x24E0, /* CIRCLED LATIN SMALL LETTER Q */ }, \
+ { 0x24C7, /* CIRCLED LATIN CAPITAL LETTER R */ \
+ 0x24E1, /* CIRCLED LATIN SMALL LETTER R */ }, \
+ { 0x24C8, /* CIRCLED LATIN CAPITAL LETTER S */ \
+ 0x24E2, /* CIRCLED LATIN SMALL LETTER S */ }, \
+ { 0x24C9, /* CIRCLED LATIN CAPITAL LETTER T */ \
+ 0x24E3, /* CIRCLED LATIN SMALL LETTER T */ }, \
+ { 0x24CA, /* CIRCLED LATIN CAPITAL LETTER U */ \
+ 0x24E4, /* CIRCLED LATIN SMALL LETTER U */ }, \
+ { 0x24CB, /* CIRCLED LATIN CAPITAL LETTER V */ \
+ 0x24E5, /* CIRCLED LATIN SMALL LETTER V */ }, \
+ { 0x24CC, /* CIRCLED LATIN CAPITAL LETTER W */ \
+ 0x24E6, /* CIRCLED LATIN SMALL LETTER W */ }, \
+ { 0x24CD, /* CIRCLED LATIN CAPITAL LETTER X */ \
+ 0x24E7, /* CIRCLED LATIN SMALL LETTER X */ }, \
+ { 0x24CE, /* CIRCLED LATIN CAPITAL LETTER Y */ \
+ 0x24E8, /* CIRCLED LATIN SMALL LETTER Y */ }, \
+ { 0x24CF, /* CIRCLED LATIN CAPITAL LETTER Z */ \
+ 0x24E9, /* CIRCLED LATIN SMALL LETTER Z */ }, \
+ { 0x2CC8, /* COPTIC CAPITAL LETTER AKHMIMIC KHEI */ \
+ 0x2CC9, /* COPTIC SMALL LETTER AKHMIMIC KHEI */ }, \
+ { 0x2C80, /* COPTIC CAPITAL LETTER ALFA */ \
+ 0x2C81, /* COPTIC SMALL LETTER ALFA */ }, \
+ { 0x2CF2, /* COPTIC CAPITAL LETTER BOHAIRIC KHEI */ \
+ 0x2CF3, /* COPTIC SMALL LETTER BOHAIRIC KHEI */ }, \
+ { 0x2CC2, /* COPTIC CAPITAL LETTER CROSSED SHEI */ \
+ 0x2CC3, /* COPTIC SMALL LETTER CROSSED SHEI */ }, \
+ { 0x2CB6, /* COPTIC CAPITAL LETTER CRYPTOGRAMMIC EIE */ \
+ 0x2CB7, /* COPTIC SMALL LETTER CRYPTOGRAMMIC EIE */ }, \
+ { 0x2CED, /* COPTIC CAPITAL LETTER CRYPTOGRAMMIC GANGIA */ \
+ 0x2CEE, /* COPTIC SMALL LETTER CRYPTOGRAMMIC GANGIA */ }, \
+ { 0x2CBC, /* COPTIC CAPITAL LETTER CRYPTOGRAMMIC NI */ \
+ 0x2CBD, /* COPTIC SMALL LETTER CRYPTOGRAMMIC NI */ }, \
+ { 0x2CEB, /* COPTIC CAPITAL LETTER CRYPTOGRAMMIC SHEI */ \
+ 0x2CEC, /* COPTIC SMALL LETTER CRYPTOGRAMMIC SHEI */ }, \
+ { 0x2C86, /* COPTIC CAPITAL LETTER DALDA */ \
+ 0x2C87, /* COPTIC SMALL LETTER DALDA */ }, \
+ { 0x03EE, /* COPTIC CAPITAL LETTER DEI */ \
+ 0x03EF, /* COPTIC SMALL LETTER DEI */ }, \
+ { 0x2CB2, /* COPTIC CAPITAL LETTER DIALECT-P ALEF */ \
+ 0x2CB3, /* COPTIC SMALL LETTER DIALECT-P ALEF */ }, \
+ { 0x2CCA, /* COPTIC CAPITAL LETTER DIALECT-P HORI */ \
+ 0x2CCB, /* COPTIC SMALL LETTER DIALECT-P HORI */ }, \
+ { 0x2CB8, /* COPTIC CAPITAL LETTER DIALECT-P KAPA */ \
+ 0x2CB9, /* COPTIC SMALL LETTER DIALECT-P KAPA */ }, \
+ { 0x2CBA, /* COPTIC CAPITAL LETTER DIALECT-P NI */ \
+ 0x2CBB, /* COPTIC SMALL LETTER DIALECT-P NI */ }, \
+ { 0x2C88, /* COPTIC CAPITAL LETTER EIE */ \
+ 0x2C89, /* COPTIC SMALL LETTER EIE */ }, \
+ { 0x03E4, /* COPTIC CAPITAL LETTER FEI */ \
+ 0x03E5, /* COPTIC SMALL LETTER FEI */ }, \
+ { 0x2CAA, /* COPTIC CAPITAL LETTER FI */ \
+ 0x2CAB, /* COPTIC SMALL LETTER FI */ }, \
+ { 0x2C84, /* COPTIC CAPITAL LETTER GAMMA */ \
+ 0x2C85, /* COPTIC SMALL LETTER GAMMA */ }, \
+ { 0x03EA, /* COPTIC CAPITAL LETTER GANGIA */ \
+ 0x03EB, /* COPTIC SMALL LETTER GANGIA */ }, \
+ { 0x2C8E, /* COPTIC CAPITAL LETTER HATE */ \
+ 0x2C8F, /* COPTIC SMALL LETTER HATE */ }, \
+ { 0x03E8, /* COPTIC CAPITAL LETTER HORI */ \
+ 0x03E9, /* COPTIC SMALL LETTER HORI */ }, \
+ { 0x2C92, /* COPTIC CAPITAL LETTER IAUDA */ \
+ 0x2C93, /* COPTIC SMALL LETTER IAUDA */ }, \
+ { 0x2C94, /* COPTIC CAPITAL LETTER KAPA */ \
+ 0x2C95, /* COPTIC SMALL LETTER KAPA */ }, \
+ { 0x03E6, /* COPTIC CAPITAL LETTER KHEI */ \
+ 0x03E7, /* COPTIC SMALL LETTER KHEI */ }, \
+ { 0x2CAC, /* COPTIC CAPITAL LETTER KHI */ \
+ 0x2CAD, /* COPTIC SMALL LETTER KHI */ }, \
+ { 0x2C9C, /* COPTIC CAPITAL LETTER KSI */ \
+ 0x2C9D, /* COPTIC SMALL LETTER KSI */ }, \
+ { 0x2C96, /* COPTIC CAPITAL LETTER LAULA */ \
+ 0x2C97, /* COPTIC SMALL LETTER LAULA */ }, \
+ { 0x2CD0, /* COPTIC CAPITAL LETTER L-SHAPED HA */ \
+ 0x2CD1, /* COPTIC SMALL LETTER L-SHAPED HA */ }, \
+ { 0x2C98, /* COPTIC CAPITAL LETTER MI */ \
+ 0x2C99, /* COPTIC SMALL LETTER MI */ }, \
+ { 0x2C9A, /* COPTIC CAPITAL LETTER NI */ \
+ 0x2C9B, /* COPTIC SMALL LETTER NI */ }, \
+ { 0x2C9E, /* COPTIC CAPITAL LETTER O */ \
+ 0x2C9F, /* COPTIC SMALL LETTER O */ }, \
+ { 0x2CB4, /* COPTIC CAPITAL LETTER OLD COPTIC AIN */ \
+ 0x2CB5, /* COPTIC SMALL LETTER OLD COPTIC AIN */ }, \
+ { 0x2CD8, /* COPTIC CAPITAL LETTER OLD COPTIC DJA */ \
+ 0x2CD9, /* COPTIC SMALL LETTER OLD COPTIC DJA */ }, \
+ { 0x2CC6, /* COPTIC CAPITAL LETTER OLD COPTIC ESH */ \
+ 0x2CC7, /* COPTIC SMALL LETTER OLD COPTIC ESH */ }, \
+ { 0x2CD6, /* COPTIC CAPITAL LETTER OLD COPTIC GANGIA */ \
+ 0x2CD7, /* COPTIC SMALL LETTER OLD COPTIC GANGIA */ }, \
+ { 0x2CCE, /* COPTIC CAPITAL LETTER OLD COPTIC HA */ \
+ 0x2CCF, /* COPTIC SMALL LETTER OLD COPTIC HA */ }, \
+ { 0x2CD4, /* COPTIC CAPITAL LETTER OLD COPTIC HAT */ \
+ 0x2CD5, /* COPTIC SMALL LETTER OLD COPTIC HAT */ }, \
+ { 0x2CD2, /* COPTIC CAPITAL LETTER OLD COPTIC HEI */ \
+ 0x2CD3, /* COPTIC SMALL LETTER OLD COPTIC HEI */ }, \
+ { 0x2CCC, /* COPTIC CAPITAL LETTER OLD COPTIC HORI */ \
+ 0x2CCD, /* COPTIC SMALL LETTER OLD COPTIC HORI */ }, \
+ { 0x2CBE, /* COPTIC CAPITAL LETTER OLD COPTIC OOU */ \
+ 0x2CBF, /* COPTIC SMALL LETTER OLD COPTIC OOU */ }, \
+ { 0x2CC4, /* COPTIC CAPITAL LETTER OLD COPTIC SHEI */ \
+ 0x2CC5, /* COPTIC SMALL LETTER OLD COPTIC SHEI */ }, \
+ { 0x2CDA, /* COPTIC CAPITAL LETTER OLD COPTIC SHIMA */ \
+ 0x2CDB, /* COPTIC SMALL LETTER OLD COPTIC SHIMA */ }, \
+ { 0x2CDE, /* COPTIC CAPITAL LETTER OLD NUBIAN NGI */ \
+ 0x2CDF, /* COPTIC SMALL LETTER OLD NUBIAN NGI */ }, \
+ { 0x2CE0, /* COPTIC CAPITAL LETTER OLD NUBIAN NYI */ \
+ 0x2CE1, /* COPTIC SMALL LETTER OLD NUBIAN NYI */ }, \
+ { 0x2CDC, /* COPTIC CAPITAL LETTER OLD NUBIAN SHIMA */ \
+ 0x2CDD, /* COPTIC SMALL LETTER OLD NUBIAN SHIMA */ }, \
+ { 0x2CE2, /* COPTIC CAPITAL LETTER OLD NUBIAN WAU */ \
+ 0x2CE3, /* COPTIC SMALL LETTER OLD NUBIAN WAU */ }, \
+ { 0x2CB0, /* COPTIC CAPITAL LETTER OOU */ \
+ 0x2CB1, /* COPTIC SMALL LETTER OOU */ }, \
+ { 0x2CA0, /* COPTIC CAPITAL LETTER PI */ \
+ 0x2CA1, /* COPTIC SMALL LETTER PI */ }, \
+ { 0x2CAE, /* COPTIC CAPITAL LETTER PSI */ \
+ 0x2CAF, /* COPTIC SMALL LETTER PSI */ }, \
+ { 0x2CA2, /* COPTIC CAPITAL LETTER RO */ \
+ 0x2CA3, /* COPTIC SMALL LETTER RO */ }, \
+ { 0x2CC0, /* COPTIC CAPITAL LETTER SAMPI */ \
+ 0x2CC1, /* COPTIC SMALL LETTER SAMPI */ }, \
+ { 0x03E2, /* COPTIC CAPITAL LETTER SHEI */ \
+ 0x03E3, /* COPTIC SMALL LETTER SHEI */ }, \
+ { 0x03EC, /* COPTIC CAPITAL LETTER SHIMA */ \
+ 0x03ED, /* COPTIC SMALL LETTER SHIMA */ }, \
+ { 0x2CA4, /* COPTIC CAPITAL LETTER SIMA */ \
+ 0x2CA5, /* COPTIC SMALL LETTER SIMA */ }, \
+ { 0x2C8A, /* COPTIC CAPITAL LETTER SOU */ \
+ 0x2C8B, /* COPTIC SMALL LETTER SOU */ }, \
+ { 0x2CA6, /* COPTIC CAPITAL LETTER TAU */ \
+ 0x2CA7, /* COPTIC SMALL LETTER TAU */ }, \
+ { 0x2C90, /* COPTIC CAPITAL LETTER THETHE */ \
+ 0x2C91, /* COPTIC SMALL LETTER THETHE */ }, \
+ { 0x2CA8, /* COPTIC CAPITAL LETTER UA */ \
+ 0x2CA9, /* COPTIC SMALL LETTER UA */ }, \
+ { 0x2C82, /* COPTIC CAPITAL LETTER VIDA */ \
+ 0x2C83, /* COPTIC SMALL LETTER VIDA */ }, \
+ { 0x2C8C, /* COPTIC CAPITAL LETTER ZATA */ \
+ 0x2C8D, /* COPTIC SMALL LETTER ZATA */ }, \
+ { 0x0410, /* CYRILLIC CAPITAL LETTER A */ \
+ 0x0430, /* CYRILLIC SMALL LETTER A */ }, \
+ { 0x04D0, /* CYRILLIC CAPITAL LETTER A WITH BREVE */ \
+ 0x04D1, /* CYRILLIC SMALL LETTER A WITH BREVE */ }, \
+ { 0x04D2, /* CYRILLIC CAPITAL LETTER A WITH DIAERESIS */ \
+ 0x04D3, /* CYRILLIC SMALL LETTER A WITH DIAERESIS */ }, \
+ { 0x04BC, /* CYRILLIC CAPITAL LETTER ABKHASIAN CHE */ \
+ 0x04BD, /* CYRILLIC SMALL LETTER ABKHASIAN CHE */ }, \
+ { 0x04BE, /* CYRILLIC CAPITAL LETTER ABKHASIAN CHE WITH DESCENDER */ \
+ 0x04BF, /* CYRILLIC SMALL LETTER ABKHASIAN CHE WITH DESCENDER */ }, \
+ { 0x04E0, /* CYRILLIC CAPITAL LETTER ABKHASIAN DZE */ \
+ 0x04E1, /* CYRILLIC SMALL LETTER ABKHASIAN DZE */ }, \
+ { 0x04A8, /* CYRILLIC CAPITAL LETTER ABKHASIAN HA */ \
+ 0x04A9, /* CYRILLIC SMALL LETTER ABKHASIAN HA */ }, \
+ { 0x051E, /* CYRILLIC CAPITAL LETTER ALEUT KA */ \
+ 0x051F, /* CYRILLIC SMALL LETTER ALEUT KA */ }, \
+ { 0x04E8, /* CYRILLIC CAPITAL LETTER BARRED O */ \
+ 0x04E9, /* CYRILLIC SMALL LETTER BARRED O */ }, \
+ { 0x04EA, /* CYRILLIC CAPITAL LETTER BARRED O WITH DIAERESIS */ \
+ 0x04EB, /* CYRILLIC SMALL LETTER BARRED O WITH DIAERESIS */ }, \
+ { 0x04A0, /* CYRILLIC CAPITAL LETTER BASHKIR KA */ \
+ 0x04A1, /* CYRILLIC SMALL LETTER BASHKIR KA */ }, \
+ { 0x0411, /* CYRILLIC CAPITAL LETTER BE */ \
+ 0x0431, /* CYRILLIC SMALL LETTER BE */ }, \
+ { 0x046A, /* CYRILLIC CAPITAL LETTER BIG YUS */ \
+ 0x046B, /* CYRILLIC SMALL LETTER BIG YUS */ }, \
+ { 0xA66A, /* CYRILLIC CAPITAL LETTER BINOCULAR O */ \
+ 0xA66B, /* CYRILLIC SMALL LETTER BINOCULAR O */ }, \
+ { 0xA65A, /* CYRILLIC CAPITAL LETTER BLENDED YUS */ \
+ 0xA65B, /* CYRILLIC SMALL LETTER BLENDED YUS */ }, \
+ { 0xA64C, /* CYRILLIC CAPITAL LETTER BROAD OMEGA */ \
+ 0xA64D, /* CYRILLIC SMALL LETTER BROAD OMEGA */ }, \
+ { 0x0406, /* CYRILLIC CAPITAL LETTER BYELORUSSIAN-UKRAINIAN I */ \
+ 0x0456, /* CYRILLIC SMALL LETTER BYELORUSSIAN-UKRAINIAN I */ }, \
+ { 0xA686, /* CYRILLIC CAPITAL LETTER CCHE */ \
+ 0xA687, /* CYRILLIC SMALL LETTER CCHE */ }, \
+ { 0x0427, /* CYRILLIC CAPITAL LETTER CHE */ \
+ 0x0447, /* CYRILLIC SMALL LETTER CHE */ }, \
+ { 0x04B6, /* CYRILLIC CAPITAL LETTER CHE WITH DESCENDER */ \
+ 0x04B7, /* CYRILLIC SMALL LETTER CHE WITH DESCENDER */ }, \
+ { 0x04F4, /* CYRILLIC CAPITAL LETTER CHE WITH DIAERESIS */ \
+ 0x04F5, /* CYRILLIC SMALL LETTER CHE WITH DIAERESIS */ }, \
+ { 0x04B8, /* CYRILLIC CAPITAL LETTER CHE WITH VERTICAL STROKE */ \
+ 0x04B9, /* CYRILLIC SMALL LETTER CHE WITH VERTICAL STROKE */ }, \
+ { 0xA658, /* CYRILLIC CAPITAL LETTER CLOSED LITTLE YUS */ \
+ 0xA659, /* CYRILLIC SMALL LETTER CLOSED LITTLE YUS */ }, \
+ { 0xA69A, /* CYRILLIC CAPITAL LETTER CROSSED O */ \
+ 0xA69B, /* CYRILLIC SMALL LETTER CROSSED O */ }, \
+ { 0x052C, /* CYRILLIC CAPITAL LETTER DCHE */ \
+ 0x052D, /* CYRILLIC SMALL LETTER DCHE */ }, \
+ { 0x0414, /* CYRILLIC CAPITAL LETTER DE */ \
+ 0x0434, /* CYRILLIC SMALL LETTER DE */ }, \
+ { 0x0402, /* CYRILLIC CAPITAL LETTER DJE */ \
+ 0x0452, /* CYRILLIC SMALL LETTER DJE */ }, \
+ { 0xA648, /* CYRILLIC CAPITAL LETTER DJERV */ \
+ 0xA649, /* CYRILLIC SMALL LETTER DJERV */ }, \
+ { 0xA66C, /* CYRILLIC CAPITAL LETTER DOUBLE MONOCULAR O */ \
+ 0xA66D, /* CYRILLIC SMALL LETTER DOUBLE MONOCULAR O */ }, \
+ { 0xA698, /* CYRILLIC CAPITAL LETTER DOUBLE O */ \
+ 0xA699, /* CYRILLIC SMALL LETTER DOUBLE O */ }, \
+ { 0xA680, /* CYRILLIC CAPITAL LETTER DWE */ \
+ 0xA681, /* CYRILLIC SMALL LETTER DWE */ }, \
+ { 0x0405, /* CYRILLIC CAPITAL LETTER DZE */ \
+ 0x0455, /* CYRILLIC SMALL LETTER DZE */ }, \
+ { 0xA642, /* CYRILLIC CAPITAL LETTER DZELO */ \
+ 0xA643, /* CYRILLIC SMALL LETTER DZELO */ }, \
+ { 0x040F, /* CYRILLIC CAPITAL LETTER DZHE */ \
+ 0x045F, /* CYRILLIC SMALL LETTER DZHE */ }, \
+ { 0xA682, /* CYRILLIC CAPITAL LETTER DZWE */ \
+ 0xA683, /* CYRILLIC SMALL LETTER DZWE */ }, \
+ { 0xA688, /* CYRILLIC CAPITAL LETTER DZZE */ \
+ 0xA689, /* CYRILLIC SMALL LETTER DZZE */ }, \
+ { 0x052A, /* CYRILLIC CAPITAL LETTER DZZHE */ \
+ 0x052B, /* CYRILLIC SMALL LETTER DZZHE */ }, \
+ { 0x042D, /* CYRILLIC CAPITAL LETTER E */ \
+ 0x044D, /* CYRILLIC SMALL LETTER E */ }, \
+ { 0x04EC, /* CYRILLIC CAPITAL LETTER E WITH DIAERESIS */ \
+ 0x04ED, /* CYRILLIC SMALL LETTER E WITH DIAERESIS */ }, \
+ { 0x0424, /* CYRILLIC CAPITAL LETTER EF */ \
+ 0x0444, /* CYRILLIC SMALL LETTER EF */ }, \
+ { 0x041B, /* CYRILLIC CAPITAL LETTER EL */ \
+ 0x043B, /* CYRILLIC SMALL LETTER EL */ }, \
+ { 0x052E, /* CYRILLIC CAPITAL LETTER EL WITH DESCENDER */ \
+ 0x052F, /* CYRILLIC SMALL LETTER EL WITH DESCENDER */ }, \
+ { 0x0512, /* CYRILLIC CAPITAL LETTER EL WITH HOOK */ \
+ 0x0513, /* CYRILLIC SMALL LETTER EL WITH HOOK */ }, \
+ { 0x0520, /* CYRILLIC CAPITAL LETTER EL WITH MIDDLE HOOK */ \
+ 0x0521, /* CYRILLIC SMALL LETTER EL WITH MIDDLE HOOK */ }, \
+ { 0x04C5, /* CYRILLIC CAPITAL LETTER EL WITH TAIL */ \
+ 0x04C6, /* CYRILLIC SMALL LETTER EL WITH TAIL */ }, \
+ { 0x041C, /* CYRILLIC CAPITAL LETTER EM */ \
+ 0x043C, /* CYRILLIC SMALL LETTER EM */ }, \
+ { 0x04CD, /* CYRILLIC CAPITAL LETTER EM WITH TAIL */ \
+ 0x04CE, /* CYRILLIC SMALL LETTER EM WITH TAIL */ }, \
+ { 0x041D, /* CYRILLIC CAPITAL LETTER EN */ \
+ 0x043D, /* CYRILLIC SMALL LETTER EN */ }, \
+ { 0x04A2, /* CYRILLIC CAPITAL LETTER EN WITH DESCENDER */ \
+ 0x04A3, /* CYRILLIC SMALL LETTER EN WITH DESCENDER */ }, \
+ { 0x04C7, /* CYRILLIC CAPITAL LETTER EN WITH HOOK */ \
+ 0x04C8, /* CYRILLIC SMALL LETTER EN WITH HOOK */ }, \
+ { 0x0528, /* CYRILLIC CAPITAL LETTER EN WITH LEFT HOOK */ \
+ 0x0529, /* CYRILLIC SMALL LETTER EN WITH LEFT HOOK */ }, \
+ { 0x0522, /* CYRILLIC CAPITAL LETTER EN WITH MIDDLE HOOK */ \
+ 0x0523, /* CYRILLIC SMALL LETTER EN WITH MIDDLE HOOK */ }, \
+ { 0x04C9, /* CYRILLIC CAPITAL LETTER EN WITH TAIL */ \
+ 0x04CA, /* CYRILLIC SMALL LETTER EN WITH TAIL */ }, \
+ { 0x0420, /* CYRILLIC CAPITAL LETTER ER */ \
+ 0x0440, /* CYRILLIC SMALL LETTER ER */ }, \
+ { 0x048E, /* CYRILLIC CAPITAL LETTER ER WITH TICK */ \
+ 0x048F, /* CYRILLIC SMALL LETTER ER WITH TICK */ }, \
+ { 0x0421, /* CYRILLIC CAPITAL LETTER ES */ \
+ 0x0441, /* CYRILLIC SMALL LETTER ES */ }, \
+ { 0x04AA, /* CYRILLIC CAPITAL LETTER ES WITH DESCENDER */ \
+ 0x04AB, /* CYRILLIC SMALL LETTER ES WITH DESCENDER */ }, \
+ { 0x0472, /* CYRILLIC CAPITAL LETTER FITA */ \
+ 0x0473, /* CYRILLIC SMALL LETTER FITA */ }, \
+ { 0x0413, /* CYRILLIC CAPITAL LETTER GHE */ \
+ 0x0433, /* CYRILLIC SMALL LETTER GHE */ }, \
+ { 0x04F6, /* CYRILLIC CAPITAL LETTER GHE WITH DESCENDER */ \
+ 0x04F7, /* CYRILLIC SMALL LETTER GHE WITH DESCENDER */ }, \
+ { 0x0494, /* CYRILLIC CAPITAL LETTER GHE WITH MIDDLE HOOK */ \
+ 0x0495, /* CYRILLIC SMALL LETTER GHE WITH MIDDLE HOOK */ }, \
+ { 0x0492, /* CYRILLIC CAPITAL LETTER GHE WITH STROKE */ \
+ 0x0493, /* CYRILLIC SMALL LETTER GHE WITH STROKE */ }, \
+ { 0x04FA, /* CYRILLIC CAPITAL LETTER GHE WITH STROKE AND HOOK */ \
+ 0x04FB, /* CYRILLIC SMALL LETTER GHE WITH STROKE AND HOOK */ }, \
+ { 0x0490, /* CYRILLIC CAPITAL LETTER GHE WITH UPTURN */ \
+ 0x0491, /* CYRILLIC SMALL LETTER GHE WITH UPTURN */ }, \
+ { 0x0403, /* CYRILLIC CAPITAL LETTER GJE */ \
+ 0x0453, /* CYRILLIC SMALL LETTER GJE */ }, \
+ { 0x0425, /* CYRILLIC CAPITAL LETTER HA */ \
+ 0x0445, /* CYRILLIC SMALL LETTER HA */ }, \
+ { 0x04B2, /* CYRILLIC CAPITAL LETTER HA WITH DESCENDER */ \
+ 0x04B3, /* CYRILLIC SMALL LETTER HA WITH DESCENDER */ }, \
+ { 0x04FC, /* CYRILLIC CAPITAL LETTER HA WITH HOOK */ \
+ 0x04FD, /* CYRILLIC SMALL LETTER HA WITH HOOK */ }, \
+ { 0x04FE, /* CYRILLIC CAPITAL LETTER HA WITH STROKE */ \
+ 0x04FF, /* CYRILLIC SMALL LETTER HA WITH STROKE */ }, \
+ { 0x042A, /* CYRILLIC CAPITAL LETTER HARD SIGN */ \
+ 0x044A, /* CYRILLIC SMALL LETTER HARD SIGN */ }, \
+ { 0xA694, /* CYRILLIC CAPITAL LETTER HWE */ \
+ 0xA695, /* CYRILLIC SMALL LETTER HWE */ }, \
+ { 0x0418, /* CYRILLIC CAPITAL LETTER I */ \
+ 0x0438, /* CYRILLIC SMALL LETTER I */ }, \
+ { 0x04E4, /* CYRILLIC CAPITAL LETTER I WITH DIAERESIS */ \
+ 0x04E5, /* CYRILLIC SMALL LETTER I WITH DIAERESIS */ }, \
+ { 0x040D, /* CYRILLIC CAPITAL LETTER I WITH GRAVE */ \
+ 0x045D, /* CYRILLIC SMALL LETTER I WITH GRAVE */ }, \
+ { 0x04E2, /* CYRILLIC CAPITAL LETTER I WITH MACRON */ \
+ 0x04E3, /* CYRILLIC SMALL LETTER I WITH MACRON */ }, \
+ { 0x0415, /* CYRILLIC CAPITAL LETTER IE */ \
+ 0x0435, /* CYRILLIC SMALL LETTER IE */ }, \
+ { 0x04D6, /* CYRILLIC CAPITAL LETTER IE WITH BREVE */ \
+ 0x04D7, /* CYRILLIC SMALL LETTER IE WITH BREVE */ }, \
+ { 0x0400, /* CYRILLIC CAPITAL LETTER IE WITH GRAVE */ \
+ 0x0450, /* CYRILLIC SMALL LETTER IE WITH GRAVE */ }, \
+ { 0x0401, /* CYRILLIC CAPITAL LETTER IO */ \
+ 0x0451, /* CYRILLIC SMALL LETTER IO */ }, \
+ { 0xA646, /* CYRILLIC CAPITAL LETTER IOTA */ \
+ 0xA647, /* CYRILLIC SMALL LETTER IOTA */ }, \
+ { 0xA656, /* CYRILLIC CAPITAL LETTER IOTIFIED A */ \
+ 0xA657, /* CYRILLIC SMALL LETTER IOTIFIED A */ }, \
+ { 0x046C, /* CYRILLIC CAPITAL LETTER IOTIFIED BIG YUS */ \
+ 0x046D, /* CYRILLIC SMALL LETTER IOTIFIED BIG YUS */ }, \
+ { 0xA65C, /* CYRILLIC CAPITAL LETTER IOTIFIED CLOSED LITTLE YUS */ \
+ 0xA65D, /* CYRILLIC SMALL LETTER IOTIFIED CLOSED LITTLE YUS */ }, \
+ { 0x0464, /* CYRILLIC CAPITAL LETTER IOTIFIED E */ \
+ 0x0465, /* CYRILLIC SMALL LETTER IOTIFIED E */ }, \
+ { 0x0468, /* CYRILLIC CAPITAL LETTER IOTIFIED LITTLE YUS */ \
+ 0x0469, /* CYRILLIC SMALL LETTER IOTIFIED LITTLE YUS */ }, \
+ { 0xA652, /* CYRILLIC CAPITAL LETTER IOTIFIED YAT */ \
+ 0xA653, /* CYRILLIC SMALL LETTER IOTIFIED YAT */ }, \
+ { 0x0474, /* CYRILLIC CAPITAL LETTER IZHITSA */ \
+ 0x0475, /* CYRILLIC SMALL LETTER IZHITSA */ }, \
+ { 0x0476, /* CYRILLIC CAPITAL LETTER IZHITSA WITH DOUBLE GRAVE ACCENT */ \
+ 0x0477, /* CYRILLIC SMALL LETTER IZHITSA WITH DOUBLE GRAVE ACCENT */ }, \
+ { 0x0408, /* CYRILLIC CAPITAL LETTER JE */ \
+ 0x0458, /* CYRILLIC SMALL LETTER JE */ }, \
+ { 0x041A, /* CYRILLIC CAPITAL LETTER KA */ \
+ 0x043A, /* CYRILLIC SMALL LETTER KA */ }, \
+ { 0x049A, /* CYRILLIC CAPITAL LETTER KA WITH DESCENDER */ \
+ 0x049B, /* CYRILLIC SMALL LETTER KA WITH DESCENDER */ }, \
+ { 0x04C3, /* CYRILLIC CAPITAL LETTER KA WITH HOOK */ \
+ 0x04C4, /* CYRILLIC SMALL LETTER KA WITH HOOK */ }, \
+ { 0x049E, /* CYRILLIC CAPITAL LETTER KA WITH STROKE */ \
+ 0x049F, /* CYRILLIC SMALL LETTER KA WITH STROKE */ }, \
+ { 0x049C, /* CYRILLIC CAPITAL LETTER KA WITH VERTICAL STROKE */ \
+ 0x049D, /* CYRILLIC SMALL LETTER KA WITH VERTICAL STROKE */ }, \
+ { 0x04CB, /* CYRILLIC CAPITAL LETTER KHAKASSIAN CHE */ \
+ 0x04CC, /* CYRILLIC SMALL LETTER KHAKASSIAN CHE */ }, \
+ { 0x040C, /* CYRILLIC CAPITAL LETTER KJE */ \
+ 0x045C, /* CYRILLIC SMALL LETTER KJE */ }, \
+ { 0x0500, /* CYRILLIC CAPITAL LETTER KOMI DE */ \
+ 0x0501, /* CYRILLIC SMALL LETTER KOMI DE */ }, \
+ { 0x0502, /* CYRILLIC CAPITAL LETTER KOMI DJE */ \
+ 0x0503, /* CYRILLIC SMALL LETTER KOMI DJE */ }, \
+ { 0x0506, /* CYRILLIC CAPITAL LETTER KOMI DZJE */ \
+ 0x0507, /* CYRILLIC SMALL LETTER KOMI DZJE */ }, \
+ { 0x0508, /* CYRILLIC CAPITAL LETTER KOMI LJE */ \
+ 0x0509, /* CYRILLIC SMALL LETTER KOMI LJE */ }, \
+ { 0x050A, /* CYRILLIC CAPITAL LETTER KOMI NJE */ \
+ 0x050B, /* CYRILLIC SMALL LETTER KOMI NJE */ }, \
+ { 0x050C, /* CYRILLIC CAPITAL LETTER KOMI SJE */ \
+ 0x050D, /* CYRILLIC SMALL LETTER KOMI SJE */ }, \
+ { 0x050E, /* CYRILLIC CAPITAL LETTER KOMI TJE */ \
+ 0x050F, /* CYRILLIC SMALL LETTER KOMI TJE */ }, \
+ { 0x0504, /* CYRILLIC CAPITAL LETTER KOMI ZJE */ \
+ 0x0505, /* CYRILLIC SMALL LETTER KOMI ZJE */ }, \
+ { 0x0480, /* CYRILLIC CAPITAL LETTER KOPPA */ \
+ 0x0481, /* CYRILLIC SMALL LETTER KOPPA */ }, \
+ { 0x046E, /* CYRILLIC CAPITAL LETTER KSI */ \
+ 0x046F, /* CYRILLIC SMALL LETTER KSI */ }, \
+ { 0x0514, /* CYRILLIC CAPITAL LETTER LHA */ \
+ 0x0515, /* CYRILLIC SMALL LETTER LHA */ }, \
+ { 0x0466, /* CYRILLIC CAPITAL LETTER LITTLE YUS */ \
+ 0x0467, /* CYRILLIC SMALL LETTER LITTLE YUS */ }, \
+ { 0x0409, /* CYRILLIC CAPITAL LETTER LJE */ \
+ 0x0459, /* CYRILLIC SMALL LETTER LJE */ }, \
+ { 0xA668, /* CYRILLIC CAPITAL LETTER MONOCULAR O */ \
+ 0xA669, /* CYRILLIC SMALL LETTER MONOCULAR O */ }, \
+ { 0xA64A, /* CYRILLIC CAPITAL LETTER MONOGRAPH UK */ \
+ 0xA64B, /* CYRILLIC SMALL LETTER MONOGRAPH UK */ }, \
+ { 0xA64E, /* CYRILLIC CAPITAL LETTER NEUTRAL YER */ \
+ 0xA64F, /* CYRILLIC SMALL LETTER NEUTRAL YER */ }, \
+ { 0x040A, /* CYRILLIC CAPITAL LETTER NJE */ \
+ 0x045A, /* CYRILLIC SMALL LETTER NJE */ }, \
+ { 0x041E, /* CYRILLIC CAPITAL LETTER O */ \
+ 0x043E, /* CYRILLIC SMALL LETTER O */ }, \
+ { 0x04E6, /* CYRILLIC CAPITAL LETTER O WITH DIAERESIS */ \
+ 0x04E7, /* CYRILLIC SMALL LETTER O WITH DIAERESIS */ }, \
+ { 0x0460, /* CYRILLIC CAPITAL LETTER OMEGA */ \
+ 0x0461, /* CYRILLIC SMALL LETTER OMEGA */ }, \
+ { 0x047C, /* CYRILLIC CAPITAL LETTER OMEGA WITH TITLO */ \
+ 0x047D, /* CYRILLIC SMALL LETTER OMEGA WITH TITLO */ }, \
+ { 0x047E, /* CYRILLIC CAPITAL LETTER OT */ \
+ 0x047F, /* CYRILLIC SMALL LETTER OT */ }, \
+ { 0x041F, /* CYRILLIC CAPITAL LETTER PE */ \
+ 0x043F, /* CYRILLIC SMALL LETTER PE */ }, \
+ { 0x0524, /* CYRILLIC CAPITAL LETTER PE WITH DESCENDER */ \
+ 0x0525, /* CYRILLIC SMALL LETTER PE WITH DESCENDER */ }, \
+ { 0x04A6, /* CYRILLIC CAPITAL LETTER PE WITH MIDDLE HOOK */ \
+ 0x04A7, /* CYRILLIC SMALL LETTER PE WITH MIDDLE HOOK */ }, \
+ { 0x0470, /* CYRILLIC CAPITAL LETTER PSI */ \
+ 0x0471, /* CYRILLIC SMALL LETTER PSI */ }, \
+ { 0x051A, /* CYRILLIC CAPITAL LETTER QA */ \
+ 0x051B, /* CYRILLIC SMALL LETTER QA */ }, \
+ { 0xA644, /* CYRILLIC CAPITAL LETTER REVERSED DZE */ \
+ 0xA645, /* CYRILLIC SMALL LETTER REVERSED DZE */ }, \
+ { 0xA660, /* CYRILLIC CAPITAL LETTER REVERSED TSE */ \
+ 0xA661, /* CYRILLIC SMALL LETTER REVERSED TSE */ }, \
+ { 0xA654, /* CYRILLIC CAPITAL LETTER REVERSED YU */ \
+ 0xA655, /* CYRILLIC SMALL LETTER REVERSED YU */ }, \
+ { 0x0510, /* CYRILLIC CAPITAL LETTER REVERSED ZE */ \
+ 0x0511, /* CYRILLIC SMALL LETTER REVERSED ZE */ }, \
+ { 0x0516, /* CYRILLIC CAPITAL LETTER RHA */ \
+ 0x0517, /* CYRILLIC SMALL LETTER RHA */ }, \
+ { 0x047A, /* CYRILLIC CAPITAL LETTER ROUND OMEGA */ \
+ 0x047B, /* CYRILLIC SMALL LETTER ROUND OMEGA */ }, \
+ { 0x04D8, /* CYRILLIC CAPITAL LETTER SCHWA */ \
+ 0x04D9, /* CYRILLIC SMALL LETTER SCHWA */ }, \
+ { 0x04DA, /* CYRILLIC CAPITAL LETTER SCHWA WITH DIAERESIS */ \
+ 0x04DB, /* CYRILLIC SMALL LETTER SCHWA WITH DIAERESIS */ }, \
+ { 0x048C, /* CYRILLIC CAPITAL LETTER SEMISOFT SIGN */ \
+ 0x048D, /* CYRILLIC SMALL LETTER SEMISOFT SIGN */ }, \
+ { 0x0428, /* CYRILLIC CAPITAL LETTER SHA */ \
+ 0x0448, /* CYRILLIC SMALL LETTER SHA */ }, \
+ { 0x0429, /* CYRILLIC CAPITAL LETTER SHCHA */ \
+ 0x0449, /* CYRILLIC SMALL LETTER SHCHA */ }, \
+ { 0x04BA, /* CYRILLIC CAPITAL LETTER SHHA */ \
+ 0x04BB, /* CYRILLIC SMALL LETTER SHHA */ }, \
+ { 0x0526, /* CYRILLIC CAPITAL LETTER SHHA WITH DESCENDER */ \
+ 0x0527, /* CYRILLIC SMALL LETTER SHHA WITH DESCENDER */ }, \
+ { 0x048A, /* CYRILLIC CAPITAL LETTER SHORT I WITH TAIL */ \
+ 0x048B, /* CYRILLIC SMALL LETTER SHORT I WITH TAIL */ }, \
+ { 0x040E, /* CYRILLIC CAPITAL LETTER SHORT U */ \
+ 0x045E, /* CYRILLIC SMALL LETTER SHORT U */ }, \
+ { 0xA696, /* CYRILLIC CAPITAL LETTER SHWE */ \
+ 0xA697, /* CYRILLIC SMALL LETTER SHWE */ }, \
+ { 0xA662, /* CYRILLIC CAPITAL LETTER SOFT DE */ \
+ 0xA663, /* CYRILLIC SMALL LETTER SOFT DE */ }, \
+ { 0xA664, /* CYRILLIC CAPITAL LETTER SOFT EL */ \
+ 0xA665, /* CYRILLIC SMALL LETTER SOFT EL */ }, \
+ { 0xA666, /* CYRILLIC CAPITAL LETTER SOFT EM */ \
+ 0xA667, /* CYRILLIC SMALL LETTER SOFT EM */ }, \
+ { 0x042C, /* CYRILLIC CAPITAL LETTER SOFT SIGN */ \
+ 0x044C, /* CYRILLIC SMALL LETTER SOFT SIGN */ }, \
+ { 0x04AE, /* CYRILLIC CAPITAL LETTER STRAIGHT U */ \
+ 0x04AF, /* CYRILLIC SMALL LETTER STRAIGHT U */ }, \
+ { 0x04B0, /* CYRILLIC CAPITAL LETTER STRAIGHT U WITH STROKE */ \
+ 0x04B1, /* CYRILLIC SMALL LETTER STRAIGHT U WITH STROKE */ }, \
+ { 0xA692, /* CYRILLIC CAPITAL LETTER TCHE */ \
+ 0xA693, /* CYRILLIC SMALL LETTER TCHE */ }, \
+ { 0x0422, /* CYRILLIC CAPITAL LETTER TE */ \
+ 0x0442, /* CYRILLIC SMALL LETTER TE */ }, \
+ { 0x04AC, /* CYRILLIC CAPITAL LETTER TE WITH DESCENDER */ \
+ 0x04AD, /* CYRILLIC SMALL LETTER TE WITH DESCENDER */ }, \
+ { 0xA68A, /* CYRILLIC CAPITAL LETTER TE WITH MIDDLE HOOK */ \
+ 0xA68B, /* CYRILLIC SMALL LETTER TE WITH MIDDLE HOOK */ }, \
+ { 0x0426, /* CYRILLIC CAPITAL LETTER TSE */ \
+ 0x0446, /* CYRILLIC SMALL LETTER TSE */ }, \
+ { 0x040B, /* CYRILLIC CAPITAL LETTER TSHE */ \
+ 0x045B, /* CYRILLIC SMALL LETTER TSHE */ }, \
+ { 0xA690, /* CYRILLIC CAPITAL LETTER TSSE */ \
+ 0xA691, /* CYRILLIC SMALL LETTER TSSE */ }, \
+ { 0xA68E, /* CYRILLIC CAPITAL LETTER TSWE */ \
+ 0xA68F, /* CYRILLIC SMALL LETTER TSWE */ }, \
+ { 0xA68C, /* CYRILLIC CAPITAL LETTER TWE */ \
+ 0xA68D, /* CYRILLIC SMALL LETTER TWE */ }, \
+ { 0x0423, /* CYRILLIC CAPITAL LETTER U */ \
+ 0x0443, /* CYRILLIC SMALL LETTER U */ }, \
+ { 0x04F0, /* CYRILLIC CAPITAL LETTER U WITH DIAERESIS */ \
+ 0x04F1, /* CYRILLIC SMALL LETTER U WITH DIAERESIS */ }, \
+ { 0x04F2, /* CYRILLIC CAPITAL LETTER U WITH DOUBLE ACUTE */ \
+ 0x04F3, /* CYRILLIC SMALL LETTER U WITH DOUBLE ACUTE */ }, \
+ { 0x04EE, /* CYRILLIC CAPITAL LETTER U WITH MACRON */ \
+ 0x04EF, /* CYRILLIC SMALL LETTER U WITH MACRON */ }, \
+ { 0x0478, /* CYRILLIC CAPITAL LETTER UK */ \
+ 0x0479, /* CYRILLIC SMALL LETTER UK */ }, \
+ { 0x0404, /* CYRILLIC CAPITAL LETTER UKRAINIAN IE */ \
+ 0x0454, /* CYRILLIC SMALL LETTER UKRAINIAN IE */ }, \
+ { 0x0412, /* CYRILLIC CAPITAL LETTER VE */ \
+ 0x0432, /* CYRILLIC SMALL LETTER VE */ }, \
+ { 0x051C, /* CYRILLIC CAPITAL LETTER WE */ \
+ 0x051D, /* CYRILLIC SMALL LETTER WE */ }, \
+ { 0x042F, /* CYRILLIC CAPITAL LETTER YA */ \
+ 0x044F, /* CYRILLIC SMALL LETTER YA */ }, \
+ { 0x0518, /* CYRILLIC CAPITAL LETTER YAE */ \
+ 0x0519, /* CYRILLIC SMALL LETTER YAE */ }, \
+ { 0x0462, /* CYRILLIC CAPITAL LETTER YAT */ \
+ 0x0463, /* CYRILLIC SMALL LETTER YAT */ }, \
+ { 0x042B, /* CYRILLIC CAPITAL LETTER YERU */ \
+ 0x044B, /* CYRILLIC SMALL LETTER YERU */ }, \
+ { 0xA650, /* CYRILLIC CAPITAL LETTER YERU WITH BACK YER */ \
+ 0xA651, /* CYRILLIC SMALL LETTER YERU WITH BACK YER */ }, \
+ { 0x04F8, /* CYRILLIC CAPITAL LETTER YERU WITH DIAERESIS */ \
+ 0x04F9, /* CYRILLIC SMALL LETTER YERU WITH DIAERESIS */ }, \
+ { 0x0407, /* CYRILLIC CAPITAL LETTER YI */ \
+ 0x0457, /* CYRILLIC SMALL LETTER YI */ }, \
+ { 0xA65E, /* CYRILLIC CAPITAL LETTER YN */ \
+ 0xA65F, /* CYRILLIC SMALL LETTER YN */ }, \
+ { 0x042E, /* CYRILLIC CAPITAL LETTER YU */ \
+ 0x044E, /* CYRILLIC SMALL LETTER YU */ }, \
+ { 0x0417, /* CYRILLIC CAPITAL LETTER ZE */ \
+ 0x0437, /* CYRILLIC SMALL LETTER ZE */ }, \
+ { 0x0498, /* CYRILLIC CAPITAL LETTER ZE WITH DESCENDER */ \
+ 0x0499, /* CYRILLIC SMALL LETTER ZE WITH DESCENDER */ }, \
+ { 0x04DE, /* CYRILLIC CAPITAL LETTER ZE WITH DIAERESIS */ \
+ 0x04DF, /* CYRILLIC SMALL LETTER ZE WITH DIAERESIS */ }, \
+ { 0xA640, /* CYRILLIC CAPITAL LETTER ZEMLYA */ \
+ 0xA641, /* CYRILLIC SMALL LETTER ZEMLYA */ }, \
+ { 0x0416, /* CYRILLIC CAPITAL LETTER ZHE */ \
+ 0x0436, /* CYRILLIC SMALL LETTER ZHE */ }, \
+ { 0x04C1, /* CYRILLIC CAPITAL LETTER ZHE WITH BREVE */ \
+ 0x04C2, /* CYRILLIC SMALL LETTER ZHE WITH BREVE */ }, \
+ { 0x0496, /* CYRILLIC CAPITAL LETTER ZHE WITH DESCENDER */ \
+ 0x0497, /* CYRILLIC SMALL LETTER ZHE WITH DESCENDER */ }, \
+ { 0x04DC, /* CYRILLIC CAPITAL LETTER ZHE WITH DIAERESIS */ \
+ 0x04DD, /* CYRILLIC SMALL LETTER ZHE WITH DIAERESIS */ }, \
+ { 0xA684, /* CYRILLIC CAPITAL LETTER ZHWE */ \
+ 0xA685, /* CYRILLIC SMALL LETTER ZHWE */ }, \
+ { 0xFF21, /* FULLWIDTH LATIN CAPITAL LETTER A */ \
+ 0xFF41, /* FULLWIDTH LATIN SMALL LETTER A */ }, \
+ { 0xFF22, /* FULLWIDTH LATIN CAPITAL LETTER B */ \
+ 0xFF42, /* FULLWIDTH LATIN SMALL LETTER B */ }, \
+ { 0xFF23, /* FULLWIDTH LATIN CAPITAL LETTER C */ \
+ 0xFF43, /* FULLWIDTH LATIN SMALL LETTER C */ }, \
+ { 0xFF24, /* FULLWIDTH LATIN CAPITAL LETTER D */ \
+ 0xFF44, /* FULLWIDTH LATIN SMALL LETTER D */ }, \
+ { 0xFF25, /* FULLWIDTH LATIN CAPITAL LETTER E */ \
+ 0xFF45, /* FULLWIDTH LATIN SMALL LETTER E */ }, \
+ { 0xFF26, /* FULLWIDTH LATIN CAPITAL LETTER F */ \
+ 0xFF46, /* FULLWIDTH LATIN SMALL LETTER F */ }, \
+ { 0xFF27, /* FULLWIDTH LATIN CAPITAL LETTER G */ \
+ 0xFF47, /* FULLWIDTH LATIN SMALL LETTER G */ }, \
+ { 0xFF28, /* FULLWIDTH LATIN CAPITAL LETTER H */ \
+ 0xFF48, /* FULLWIDTH LATIN SMALL LETTER H */ }, \
+ { 0xFF29, /* FULLWIDTH LATIN CAPITAL LETTER I */ \
+ 0xFF49, /* FULLWIDTH LATIN SMALL LETTER I */ }, \
+ { 0xFF2A, /* FULLWIDTH LATIN CAPITAL LETTER J */ \
+ 0xFF4A, /* FULLWIDTH LATIN SMALL LETTER J */ }, \
+ { 0xFF2B, /* FULLWIDTH LATIN CAPITAL LETTER K */ \
+ 0xFF4B, /* FULLWIDTH LATIN SMALL LETTER K */ }, \
+ { 0xFF2C, /* FULLWIDTH LATIN CAPITAL LETTER L */ \
+ 0xFF4C, /* FULLWIDTH LATIN SMALL LETTER L */ }, \
+ { 0xFF2D, /* FULLWIDTH LATIN CAPITAL LETTER M */ \
+ 0xFF4D, /* FULLWIDTH LATIN SMALL LETTER M */ }, \
+ { 0xFF2E, /* FULLWIDTH LATIN CAPITAL LETTER N */ \
+ 0xFF4E, /* FULLWIDTH LATIN SMALL LETTER N */ }, \
+ { 0xFF2F, /* FULLWIDTH LATIN CAPITAL LETTER O */ \
+ 0xFF4F, /* FULLWIDTH LATIN SMALL LETTER O */ }, \
+ { 0xFF30, /* FULLWIDTH LATIN CAPITAL LETTER P */ \
+ 0xFF50, /* FULLWIDTH LATIN SMALL LETTER P */ }, \
+ { 0xFF31, /* FULLWIDTH LATIN CAPITAL LETTER Q */ \
+ 0xFF51, /* FULLWIDTH LATIN SMALL LETTER Q */ }, \
+ { 0xFF32, /* FULLWIDTH LATIN CAPITAL LETTER R */ \
+ 0xFF52, /* FULLWIDTH LATIN SMALL LETTER R */ }, \
+ { 0xFF33, /* FULLWIDTH LATIN CAPITAL LETTER S */ \
+ 0xFF53, /* FULLWIDTH LATIN SMALL LETTER S */ }, \
+ { 0xFF34, /* FULLWIDTH LATIN CAPITAL LETTER T */ \
+ 0xFF54, /* FULLWIDTH LATIN SMALL LETTER T */ }, \
+ { 0xFF35, /* FULLWIDTH LATIN CAPITAL LETTER U */ \
+ 0xFF55, /* FULLWIDTH LATIN SMALL LETTER U */ }, \
+ { 0xFF36, /* FULLWIDTH LATIN CAPITAL LETTER V */ \
+ 0xFF56, /* FULLWIDTH LATIN SMALL LETTER V */ }, \
+ { 0xFF37, /* FULLWIDTH LATIN CAPITAL LETTER W */ \
+ 0xFF57, /* FULLWIDTH LATIN SMALL LETTER W */ }, \
+ { 0xFF38, /* FULLWIDTH LATIN CAPITAL LETTER X */ \
+ 0xFF58, /* FULLWIDTH LATIN SMALL LETTER X */ }, \
+ { 0xFF39, /* FULLWIDTH LATIN CAPITAL LETTER Y */ \
+ 0xFF59, /* FULLWIDTH LATIN SMALL LETTER Y */ }, \
+ { 0xFF3A, /* FULLWIDTH LATIN CAPITAL LETTER Z */ \
+ 0xFF5A, /* FULLWIDTH LATIN SMALL LETTER Z */ }, \
+ { 0x10CD, /* GEORGIAN CAPITAL LETTER AEN */ \
+ 0x2D2D, /* GEORGIAN SMALL LETTER AEN */ }, \
+ { 0x10A0, /* GEORGIAN CAPITAL LETTER AN */ \
+ 0x2D00, /* GEORGIAN SMALL LETTER AN */ }, \
+ { 0x10A1, /* GEORGIAN CAPITAL LETTER BAN */ \
+ 0x2D01, /* GEORGIAN SMALL LETTER BAN */ }, \
+ { 0x10BA, /* GEORGIAN CAPITAL LETTER CAN */ \
+ 0x2D1A, /* GEORGIAN SMALL LETTER CAN */ }, \
+ { 0x10BD, /* GEORGIAN CAPITAL LETTER CHAR */ \
+ 0x2D1D, /* GEORGIAN SMALL LETTER CHAR */ }, \
+ { 0x10B9, /* GEORGIAN CAPITAL LETTER CHIN */ \
+ 0x2D19, /* GEORGIAN SMALL LETTER CHIN */ }, \
+ { 0x10BC, /* GEORGIAN CAPITAL LETTER CIL */ \
+ 0x2D1C, /* GEORGIAN SMALL LETTER CIL */ }, \
+ { 0x10A3, /* GEORGIAN CAPITAL LETTER DON */ \
+ 0x2D03, /* GEORGIAN SMALL LETTER DON */ }, \
+ { 0x10A4, /* GEORGIAN CAPITAL LETTER EN */ \
+ 0x2D04, /* GEORGIAN SMALL LETTER EN */ }, \
+ { 0x10A2, /* GEORGIAN CAPITAL LETTER GAN */ \
+ 0x2D02, /* GEORGIAN SMALL LETTER GAN */ }, \
+ { 0x10B6, /* GEORGIAN CAPITAL LETTER GHAN */ \
+ 0x2D16, /* GEORGIAN SMALL LETTER GHAN */ }, \
+ { 0x10C0, /* GEORGIAN CAPITAL LETTER HAE */ \
+ 0x2D20, /* GEORGIAN SMALL LETTER HAE */ }, \
+ { 0x10C4, /* GEORGIAN CAPITAL LETTER HAR */ \
+ 0x2D24, /* GEORGIAN SMALL LETTER HAR */ }, \
+ { 0x10C1, /* GEORGIAN CAPITAL LETTER HE */ \
+ 0x2D21, /* GEORGIAN SMALL LETTER HE */ }, \
+ { 0x10C2, /* GEORGIAN CAPITAL LETTER HIE */ \
+ 0x2D22, /* GEORGIAN SMALL LETTER HIE */ }, \
+ { 0x10C5, /* GEORGIAN CAPITAL LETTER HOE */ \
+ 0x2D25, /* GEORGIAN SMALL LETTER HOE */ }, \
+ { 0x10A8, /* GEORGIAN CAPITAL LETTER IN */ \
+ 0x2D08, /* GEORGIAN SMALL LETTER IN */ }, \
+ { 0x10BF, /* GEORGIAN CAPITAL LETTER JHAN */ \
+ 0x2D1F, /* GEORGIAN SMALL LETTER JHAN */ }, \
+ { 0x10BB, /* GEORGIAN CAPITAL LETTER JIL */ \
+ 0x2D1B, /* GEORGIAN SMALL LETTER JIL */ }, \
+ { 0x10A9, /* GEORGIAN CAPITAL LETTER KAN */ \
+ 0x2D09, /* GEORGIAN SMALL LETTER KAN */ }, \
+ { 0x10B5, /* GEORGIAN CAPITAL LETTER KHAR */ \
+ 0x2D15, /* GEORGIAN SMALL LETTER KHAR */ }, \
+ { 0x10AA, /* GEORGIAN CAPITAL LETTER LAS */ \
+ 0x2D0A, /* GEORGIAN SMALL LETTER LAS */ }, \
+ { 0x10AB, /* GEORGIAN CAPITAL LETTER MAN */ \
+ 0x2D0B, /* GEORGIAN SMALL LETTER MAN */ }, \
+ { 0x10AC, /* GEORGIAN CAPITAL LETTER NAR */ \
+ 0x2D0C, /* GEORGIAN SMALL LETTER NAR */ }, \
+ { 0x10AD, /* GEORGIAN CAPITAL LETTER ON */ \
+ 0x2D0D, /* GEORGIAN SMALL LETTER ON */ }, \
+ { 0x10AE, /* GEORGIAN CAPITAL LETTER PAR */ \
+ 0x2D0E, /* GEORGIAN SMALL LETTER PAR */ }, \
+ { 0x10B4, /* GEORGIAN CAPITAL LETTER PHAR */ \
+ 0x2D14, /* GEORGIAN SMALL LETTER PHAR */ }, \
+ { 0x10B7, /* GEORGIAN CAPITAL LETTER QAR */ \
+ 0x2D17, /* GEORGIAN SMALL LETTER QAR */ }, \
+ { 0x10B0, /* GEORGIAN CAPITAL LETTER RAE */ \
+ 0x2D10, /* GEORGIAN SMALL LETTER RAE */ }, \
+ { 0x10B1, /* GEORGIAN CAPITAL LETTER SAN */ \
+ 0x2D11, /* GEORGIAN SMALL LETTER SAN */ }, \
+ { 0x10B8, /* GEORGIAN CAPITAL LETTER SHIN */ \
+ 0x2D18, /* GEORGIAN SMALL LETTER SHIN */ }, \
+ { 0x10A7, /* GEORGIAN CAPITAL LETTER TAN */ \
+ 0x2D07, /* GEORGIAN SMALL LETTER TAN */ }, \
+ { 0x10B2, /* GEORGIAN CAPITAL LETTER TAR */ \
+ 0x2D12, /* GEORGIAN SMALL LETTER TAR */ }, \
+ { 0x10B3, /* GEORGIAN CAPITAL LETTER UN */ \
+ 0x2D13, /* GEORGIAN SMALL LETTER UN */ }, \
+ { 0x10A5, /* GEORGIAN CAPITAL LETTER VIN */ \
+ 0x2D05, /* GEORGIAN SMALL LETTER VIN */ }, \
+ { 0x10C3, /* GEORGIAN CAPITAL LETTER WE */ \
+ 0x2D23, /* GEORGIAN SMALL LETTER WE */ }, \
+ { 0x10BE, /* GEORGIAN CAPITAL LETTER XAN */ \
+ 0x2D1E, /* GEORGIAN SMALL LETTER XAN */ }, \
+ { 0x10C7, /* GEORGIAN CAPITAL LETTER YN */ \
+ 0x2D27, /* GEORGIAN SMALL LETTER YN */ }, \
+ { 0x10A6, /* GEORGIAN CAPITAL LETTER ZEN */ \
+ 0x2D06, /* GEORGIAN SMALL LETTER ZEN */ }, \
+ { 0x10AF, /* GEORGIAN CAPITAL LETTER ZHAR */ \
+ 0x2D0F, /* GEORGIAN SMALL LETTER ZHAR */ }, \
+ { 0x2C00, /* GLAGOLITIC CAPITAL LETTER AZU */ \
+ 0x2C30, /* GLAGOLITIC SMALL LETTER AZU */ }, \
+ { 0x2C28, /* GLAGOLITIC CAPITAL LETTER BIG YUS */ \
+ 0x2C58, /* GLAGOLITIC SMALL LETTER BIG YUS */ }, \
+ { 0x2C01, /* GLAGOLITIC CAPITAL LETTER BUKY */ \
+ 0x2C31, /* GLAGOLITIC SMALL LETTER BUKY */ }, \
+ { 0x2C1D, /* GLAGOLITIC CAPITAL LETTER CHRIVI */ \
+ 0x2C4D, /* GLAGOLITIC SMALL LETTER CHRIVI */ }, \
+ { 0x2C0C, /* GLAGOLITIC CAPITAL LETTER DJERVI */ \
+ 0x2C3C, /* GLAGOLITIC SMALL LETTER DJERVI */ }, \
+ { 0x2C04, /* GLAGOLITIC CAPITAL LETTER DOBRO */ \
+ 0x2C34, /* GLAGOLITIC SMALL LETTER DOBRO */ }, \
+ { 0x2C07, /* GLAGOLITIC CAPITAL LETTER DZELO */ \
+ 0x2C37, /* GLAGOLITIC SMALL LETTER DZELO */ }, \
+ { 0x2C2A, /* GLAGOLITIC CAPITAL LETTER FITA */ \
+ 0x2C5A, /* GLAGOLITIC SMALL LETTER FITA */ }, \
+ { 0x2C17, /* GLAGOLITIC CAPITAL LETTER FRITU */ \
+ 0x2C47, /* GLAGOLITIC SMALL LETTER FRITU */ }, \
+ { 0x2C03, /* GLAGOLITIC CAPITAL LETTER GLAGOLI */ \
+ 0x2C33, /* GLAGOLITIC SMALL LETTER GLAGOLI */ }, \
+ { 0x2C18, /* GLAGOLITIC CAPITAL LETTER HERU */ \
+ 0x2C48, /* GLAGOLITIC SMALL LETTER HERU */ }, \
+ { 0x2C0B, /* GLAGOLITIC CAPITAL LETTER I */ \
+ 0x2C3B, /* GLAGOLITIC SMALL LETTER I */ }, \
+ { 0x2C0A, /* GLAGOLITIC CAPITAL LETTER INITIAL IZHE */ \
+ 0x2C3A, /* GLAGOLITIC SMALL LETTER INITIAL IZHE */ }, \
+ { 0x2C29, /* GLAGOLITIC CAPITAL LETTER IOTATED BIG YUS */ \
+ 0x2C59, /* GLAGOLITIC SMALL LETTER IOTATED BIG YUS */ }, \
+ { 0x2C27, /* GLAGOLITIC CAPITAL LETTER IOTATED SMALL YUS */ \
+ 0x2C57, /* GLAGOLITIC SMALL LETTER IOTATED SMALL YUS */ }, \
+ { 0x2C09, /* GLAGOLITIC CAPITAL LETTER IZHE */ \
+ 0x2C39, /* GLAGOLITIC SMALL LETTER IZHE */ }, \
+ { 0x2C2B, /* GLAGOLITIC CAPITAL LETTER IZHITSA */ \
+ 0x2C5B, /* GLAGOLITIC SMALL LETTER IZHITSA */ }, \
+ { 0x2C0D, /* GLAGOLITIC CAPITAL LETTER KAKO */ \
+ 0x2C3D, /* GLAGOLITIC SMALL LETTER KAKO */ }, \
+ { 0x2C2E, /* GLAGOLITIC CAPITAL LETTER LATINATE MYSLITE */ \
+ 0x2C5E, /* GLAGOLITIC SMALL LETTER LATINATE MYSLITE */ }, \
+ { 0x2C0E, /* GLAGOLITIC CAPITAL LETTER LJUDIJE */ \
+ 0x2C3E, /* GLAGOLITIC SMALL LETTER LJUDIJE */ }, \
+ { 0x2C0F, /* GLAGOLITIC CAPITAL LETTER MYSLITE */ \
+ 0x2C3F, /* GLAGOLITIC SMALL LETTER MYSLITE */ }, \
+ { 0x2C10, /* GLAGOLITIC CAPITAL LETTER NASHI */ \
+ 0x2C40, /* GLAGOLITIC SMALL LETTER NASHI */ }, \
+ { 0x2C11, /* GLAGOLITIC CAPITAL LETTER ONU */ \
+ 0x2C41, /* GLAGOLITIC SMALL LETTER ONU */ }, \
+ { 0x2C19, /* GLAGOLITIC CAPITAL LETTER OTU */ \
+ 0x2C49, /* GLAGOLITIC SMALL LETTER OTU */ }, \
+ { 0x2C1A, /* GLAGOLITIC CAPITAL LETTER PE */ \
+ 0x2C4A, /* GLAGOLITIC SMALL LETTER PE */ }, \
+ { 0x2C12, /* GLAGOLITIC CAPITAL LETTER POKOJI */ \
+ 0x2C42, /* GLAGOLITIC SMALL LETTER POKOJI */ }, \
+ { 0x2C13, /* GLAGOLITIC CAPITAL LETTER RITSI */ \
+ 0x2C43, /* GLAGOLITIC SMALL LETTER RITSI */ }, \
+ { 0x2C1E, /* GLAGOLITIC CAPITAL LETTER SHA */ \
+ 0x2C4E, /* GLAGOLITIC SMALL LETTER SHA */ }, \
+ { 0x2C1B, /* GLAGOLITIC CAPITAL LETTER SHTA */ \
+ 0x2C4B, /* GLAGOLITIC SMALL LETTER SHTA */ }, \
+ { 0x2C2C, /* GLAGOLITIC CAPITAL LETTER SHTAPIC */ \
+ 0x2C5C, /* GLAGOLITIC SMALL LETTER SHTAPIC */ }, \
+ { 0x2C14, /* GLAGOLITIC CAPITAL LETTER SLOVO */ \
+ 0x2C44, /* GLAGOLITIC SMALL LETTER SLOVO */ }, \
+ { 0x2C24, /* GLAGOLITIC CAPITAL LETTER SMALL YUS */ \
+ 0x2C54, /* GLAGOLITIC SMALL LETTER SMALL YUS */ }, \
+ { 0x2C25, /* GLAGOLITIC CAPITAL LETTER SMALL YUS WITH TAIL */ \
+ 0x2C55, /* GLAGOLITIC SMALL LETTER SMALL YUS WITH TAIL */ }, \
+ { 0x2C22, /* GLAGOLITIC CAPITAL LETTER SPIDERY HA */ \
+ 0x2C52, /* GLAGOLITIC SMALL LETTER SPIDERY HA */ }, \
+ { 0x2C2D, /* GLAGOLITIC CAPITAL LETTER TROKUTASTI A */ \
+ 0x2C5D, /* GLAGOLITIC SMALL LETTER TROKUTASTI A */ }, \
+ { 0x2C1C, /* GLAGOLITIC CAPITAL LETTER TSI */ \
+ 0x2C4C, /* GLAGOLITIC SMALL LETTER TSI */ }, \
+ { 0x2C15, /* GLAGOLITIC CAPITAL LETTER TVRIDO */ \
+ 0x2C45, /* GLAGOLITIC SMALL LETTER TVRIDO */ }, \
+ { 0x2C16, /* GLAGOLITIC CAPITAL LETTER UKU */ \
+ 0x2C46, /* GLAGOLITIC SMALL LETTER UKU */ }, \
+ { 0x2C02, /* GLAGOLITIC CAPITAL LETTER VEDE */ \
+ 0x2C32, /* GLAGOLITIC SMALL LETTER VEDE */ }, \
+ { 0x2C21, /* GLAGOLITIC CAPITAL LETTER YATI */ \
+ 0x2C51, /* GLAGOLITIC SMALL LETTER YATI */ }, \
+ { 0x2C20, /* GLAGOLITIC CAPITAL LETTER YERI */ \
+ 0x2C50, /* GLAGOLITIC SMALL LETTER YERI */ }, \
+ { 0x2C1F, /* GLAGOLITIC CAPITAL LETTER YERU */ \
+ 0x2C4F, /* GLAGOLITIC SMALL LETTER YERU */ }, \
+ { 0x2C05, /* GLAGOLITIC CAPITAL LETTER YESTU */ \
+ 0x2C35, /* GLAGOLITIC SMALL LETTER YESTU */ }, \
+ { 0x2C26, /* GLAGOLITIC CAPITAL LETTER YO */ \
+ 0x2C56, /* GLAGOLITIC SMALL LETTER YO */ }, \
+ { 0x2C23, /* GLAGOLITIC CAPITAL LETTER YU */ \
+ 0x2C53, /* GLAGOLITIC SMALL LETTER YU */ }, \
+ { 0x2C08, /* GLAGOLITIC CAPITAL LETTER ZEMLJA */ \
+ 0x2C38, /* GLAGOLITIC SMALL LETTER ZEMLJA */ }, \
+ { 0x2C06, /* GLAGOLITIC CAPITAL LETTER ZHIVETE */ \
+ 0x2C36, /* GLAGOLITIC SMALL LETTER ZHIVETE */ }, \
+ { 0x0391, /* GREEK CAPITAL LETTER ALPHA */ \
+ 0x03B1, /* GREEK SMALL LETTER ALPHA */ }, \
+ { 0x1F09, /* GREEK CAPITAL LETTER ALPHA WITH DASIA */ \
+ 0x1F01, /* GREEK SMALL LETTER ALPHA WITH DASIA */ }, \
+ { 0x1F0D, /* GREEK CAPITAL LETTER ALPHA WITH DASIA AND OXIA */ \
+ 0x1F05, /* GREEK SMALL LETTER ALPHA WITH DASIA AND OXIA */ }, \
+ { 0x1F0F, /* GREEK CAPITAL LETTER ALPHA WITH DASIA AND PERISPOMENI */ \
+ 0x1F07, /* GREEK SMALL LETTER ALPHA WITH DASIA AND PERISPOMENI */ }, \
+ { 0x1F0B, /* GREEK CAPITAL LETTER ALPHA WITH DASIA AND VARIA */ \
+ 0x1F03, /* GREEK SMALL LETTER ALPHA WITH DASIA AND VARIA */ }, \
+ { 0x1FB9, /* GREEK CAPITAL LETTER ALPHA WITH MACRON */ \
+ 0x1FB1, /* GREEK SMALL LETTER ALPHA WITH MACRON */ }, \
+ { 0x1FBB, /* GREEK CAPITAL LETTER ALPHA WITH OXIA */ \
+ 0x1F71, /* GREEK SMALL LETTER ALPHA WITH OXIA */ }, \
+ { 0x1F08, /* GREEK CAPITAL LETTER ALPHA WITH PSILI */ \
+ 0x1F00, /* GREEK SMALL LETTER ALPHA WITH PSILI */ }, \
+ { 0x1F0C, /* GREEK CAPITAL LETTER ALPHA WITH PSILI AND OXIA */ \
+ 0x1F04, /* GREEK SMALL LETTER ALPHA WITH PSILI AND OXIA */ }, \
+ { 0x1F0E, /* GREEK CAPITAL LETTER ALPHA WITH PSILI AND PERISPOMENI */ \
+ 0x1F06, /* GREEK SMALL LETTER ALPHA WITH PSILI AND PERISPOMENI */ }, \
+ { 0x1F0A, /* GREEK CAPITAL LETTER ALPHA WITH PSILI AND VARIA */ \
+ 0x1F02, /* GREEK SMALL LETTER ALPHA WITH PSILI AND VARIA */ }, \
+ { 0x0386, /* GREEK CAPITAL LETTER ALPHA WITH TONOS */ \
+ 0x03AC, /* GREEK SMALL LETTER ALPHA WITH TONOS */ }, \
+ { 0x1FBA, /* GREEK CAPITAL LETTER ALPHA WITH VARIA */ \
+ 0x1F70, /* GREEK SMALL LETTER ALPHA WITH VARIA */ }, \
+ { 0x1FB8, /* GREEK CAPITAL LETTER ALPHA WITH VRACHY */ \
+ 0x1FB0, /* GREEK SMALL LETTER ALPHA WITH VRACHY */ }, \
+ { 0x0372, /* GREEK CAPITAL LETTER ARCHAIC SAMPI */ \
+ 0x0373, /* GREEK SMALL LETTER ARCHAIC SAMPI */ }, \
+ { 0x0392, /* GREEK CAPITAL LETTER BETA */ \
+ 0x03B2, /* GREEK SMALL LETTER BETA */ }, \
+ { 0x03A7, /* GREEK CAPITAL LETTER CHI */ \
+ 0x03C7, /* GREEK SMALL LETTER CHI */ }, \
+ { 0x0394, /* GREEK CAPITAL LETTER DELTA */ \
+ 0x03B4, /* GREEK SMALL LETTER DELTA */ }, \
+ { 0x0395, /* GREEK CAPITAL LETTER EPSILON */ \
+ 0x03B5, /* GREEK SMALL LETTER EPSILON */ }, \
+ { 0x1F19, /* GREEK CAPITAL LETTER EPSILON WITH DASIA */ \
+ 0x1F11, /* GREEK SMALL LETTER EPSILON WITH DASIA */ }, \
+ { 0x1F1D, /* GREEK CAPITAL LETTER EPSILON WITH DASIA AND OXIA */ \
+ 0x1F15, /* GREEK SMALL LETTER EPSILON WITH DASIA AND OXIA */ }, \
+ { 0x1F1B, /* GREEK CAPITAL LETTER EPSILON WITH DASIA AND VARIA */ \
+ 0x1F13, /* GREEK SMALL LETTER EPSILON WITH DASIA AND VARIA */ }, \
+ { 0x1FC9, /* GREEK CAPITAL LETTER EPSILON WITH OXIA */ \
+ 0x1F73, /* GREEK SMALL LETTER EPSILON WITH OXIA */ }, \
+ { 0x1F18, /* GREEK CAPITAL LETTER EPSILON WITH PSILI */ \
+ 0x1F10, /* GREEK SMALL LETTER EPSILON WITH PSILI */ }, \
+ { 0x1F1C, /* GREEK CAPITAL LETTER EPSILON WITH PSILI AND OXIA */ \
+ 0x1F14, /* GREEK SMALL LETTER EPSILON WITH PSILI AND OXIA */ }, \
+ { 0x1F1A, /* GREEK CAPITAL LETTER EPSILON WITH PSILI AND VARIA */ \
+ 0x1F12, /* GREEK SMALL LETTER EPSILON WITH PSILI AND VARIA */ }, \
+ { 0x0388, /* GREEK CAPITAL LETTER EPSILON WITH TONOS */ \
+ 0x03AD, /* GREEK SMALL LETTER EPSILON WITH TONOS */ }, \
+ { 0x1FC8, /* GREEK CAPITAL LETTER EPSILON WITH VARIA */ \
+ 0x1F72, /* GREEK SMALL LETTER EPSILON WITH VARIA */ }, \
+ { 0x0397, /* GREEK CAPITAL LETTER ETA */ \
+ 0x03B7, /* GREEK SMALL LETTER ETA */ }, \
+ { 0x1F29, /* GREEK CAPITAL LETTER ETA WITH DASIA */ \
+ 0x1F21, /* GREEK SMALL LETTER ETA WITH DASIA */ }, \
+ { 0x1F2D, /* GREEK CAPITAL LETTER ETA WITH DASIA AND OXIA */ \
+ 0x1F25, /* GREEK SMALL LETTER ETA WITH DASIA AND OXIA */ }, \
+ { 0x1F2F, /* GREEK CAPITAL LETTER ETA WITH DASIA AND PERISPOMENI */ \
+ 0x1F27, /* GREEK SMALL LETTER ETA WITH DASIA AND PERISPOMENI */ }, \
+ { 0x1F2B, /* GREEK CAPITAL LETTER ETA WITH DASIA AND VARIA */ \
+ 0x1F23, /* GREEK SMALL LETTER ETA WITH DASIA AND VARIA */ }, \
+ { 0x1FCB, /* GREEK CAPITAL LETTER ETA WITH OXIA */ \
+ 0x1F75, /* GREEK SMALL LETTER ETA WITH OXIA */ }, \
+ { 0x1F28, /* GREEK CAPITAL LETTER ETA WITH PSILI */ \
+ 0x1F20, /* GREEK SMALL LETTER ETA WITH PSILI */ }, \
+ { 0x1F2C, /* GREEK CAPITAL LETTER ETA WITH PSILI AND OXIA */ \
+ 0x1F24, /* GREEK SMALL LETTER ETA WITH PSILI AND OXIA */ }, \
+ { 0x1F2E, /* GREEK CAPITAL LETTER ETA WITH PSILI AND PERISPOMENI */ \
+ 0x1F26, /* GREEK SMALL LETTER ETA WITH PSILI AND PERISPOMENI */ }, \
+ { 0x1F2A, /* GREEK CAPITAL LETTER ETA WITH PSILI AND VARIA */ \
+ 0x1F22, /* GREEK SMALL LETTER ETA WITH PSILI AND VARIA */ }, \
+ { 0x0389, /* GREEK CAPITAL LETTER ETA WITH TONOS */ \
+ 0x03AE, /* GREEK SMALL LETTER ETA WITH TONOS */ }, \
+ { 0x1FCA, /* GREEK CAPITAL LETTER ETA WITH VARIA */ \
+ 0x1F74, /* GREEK SMALL LETTER ETA WITH VARIA */ }, \
+ { 0x0393, /* GREEK CAPITAL LETTER GAMMA */ \
+ 0x03B3, /* GREEK SMALL LETTER GAMMA */ }, \
+ { 0x0370, /* GREEK CAPITAL LETTER HETA */ \
+ 0x0371, /* GREEK SMALL LETTER HETA */ }, \
+ { 0x0399, /* GREEK CAPITAL LETTER IOTA */ \
+ 0x03B9, /* GREEK SMALL LETTER IOTA */ }, \
+ { 0x1F39, /* GREEK CAPITAL LETTER IOTA WITH DASIA */ \
+ 0x1F31, /* GREEK SMALL LETTER IOTA WITH DASIA */ }, \
+ { 0x1F3D, /* GREEK CAPITAL LETTER IOTA WITH DASIA AND OXIA */ \
+ 0x1F35, /* GREEK SMALL LETTER IOTA WITH DASIA AND OXIA */ }, \
+ { 0x1F3F, /* GREEK CAPITAL LETTER IOTA WITH DASIA AND PERISPOMENI */ \
+ 0x1F37, /* GREEK SMALL LETTER IOTA WITH DASIA AND PERISPOMENI */ }, \
+ { 0x1F3B, /* GREEK CAPITAL LETTER IOTA WITH DASIA AND VARIA */ \
+ 0x1F33, /* GREEK SMALL LETTER IOTA WITH DASIA AND VARIA */ }, \
+ { 0x03AA, /* GREEK CAPITAL LETTER IOTA WITH DIALYTIKA */ \
+ 0x03CA, /* GREEK SMALL LETTER IOTA WITH DIALYTIKA */ }, \
+ { 0x1FD9, /* GREEK CAPITAL LETTER IOTA WITH MACRON */ \
+ 0x1FD1, /* GREEK SMALL LETTER IOTA WITH MACRON */ }, \
+ { 0x1FDB, /* GREEK CAPITAL LETTER IOTA WITH OXIA */ \
+ 0x1F77, /* GREEK SMALL LETTER IOTA WITH OXIA */ }, \
+ { 0x1F38, /* GREEK CAPITAL LETTER IOTA WITH PSILI */ \
+ 0x1F30, /* GREEK SMALL LETTER IOTA WITH PSILI */ }, \
+ { 0x1F3C, /* GREEK CAPITAL LETTER IOTA WITH PSILI AND OXIA */ \
+ 0x1F34, /* GREEK SMALL LETTER IOTA WITH PSILI AND OXIA */ }, \
+ { 0x1F3E, /* GREEK CAPITAL LETTER IOTA WITH PSILI AND PERISPOMENI */ \
+ 0x1F36, /* GREEK SMALL LETTER IOTA WITH PSILI AND PERISPOMENI */ }, \
+ { 0x1F3A, /* GREEK CAPITAL LETTER IOTA WITH PSILI AND VARIA */ \
+ 0x1F32, /* GREEK SMALL LETTER IOTA WITH PSILI AND VARIA */ }, \
+ { 0x038A, /* GREEK CAPITAL LETTER IOTA WITH TONOS */ \
+ 0x03AF, /* GREEK SMALL LETTER IOTA WITH TONOS */ }, \
+ { 0x1FDA, /* GREEK CAPITAL LETTER IOTA WITH VARIA */ \
+ 0x1F76, /* GREEK SMALL LETTER IOTA WITH VARIA */ }, \
+ { 0x1FD8, /* GREEK CAPITAL LETTER IOTA WITH VRACHY */ \
+ 0x1FD0, /* GREEK SMALL LETTER IOTA WITH VRACHY */ }, \
+ { 0x039A, /* GREEK CAPITAL LETTER KAPPA */ \
+ 0x03BA, /* GREEK SMALL LETTER KAPPA */ }, \
+ { 0x039B, /* GREEK CAPITAL LETTER LAMDA */ \
+ 0x03BB, /* GREEK SMALL LETTER LAMDA */ }, \
+ { 0x039C, /* GREEK CAPITAL LETTER MU */ \
+ 0x03BC, /* GREEK SMALL LETTER MU */ }, \
+ { 0x039D, /* GREEK CAPITAL LETTER NU */ \
+ 0x03BD, /* GREEK SMALL LETTER NU */ }, \
+ { 0x03A9, /* GREEK CAPITAL LETTER OMEGA */ \
+ 0x03C9, /* GREEK SMALL LETTER OMEGA */ }, \
+ { 0x1F69, /* GREEK CAPITAL LETTER OMEGA WITH DASIA */ \
+ 0x1F61, /* GREEK SMALL LETTER OMEGA WITH DASIA */ }, \
+ { 0x1F6D, /* GREEK CAPITAL LETTER OMEGA WITH DASIA AND OXIA */ \
+ 0x1F65, /* GREEK SMALL LETTER OMEGA WITH DASIA AND OXIA */ }, \
+ { 0x1F6F, /* GREEK CAPITAL LETTER OMEGA WITH DASIA AND PERISPOMENI */ \
+ 0x1F67, /* GREEK SMALL LETTER OMEGA WITH DASIA AND PERISPOMENI */ }, \
+ { 0x1F6B, /* GREEK CAPITAL LETTER OMEGA WITH DASIA AND VARIA */ \
+ 0x1F63, /* GREEK SMALL LETTER OMEGA WITH DASIA AND VARIA */ }, \
+ { 0x1FFB, /* GREEK CAPITAL LETTER OMEGA WITH OXIA */ \
+ 0x1F7D, /* GREEK SMALL LETTER OMEGA WITH OXIA */ }, \
+ { 0x1F68, /* GREEK CAPITAL LETTER OMEGA WITH PSILI */ \
+ 0x1F60, /* GREEK SMALL LETTER OMEGA WITH PSILI */ }, \
+ { 0x1F6C, /* GREEK CAPITAL LETTER OMEGA WITH PSILI AND OXIA */ \
+ 0x1F64, /* GREEK SMALL LETTER OMEGA WITH PSILI AND OXIA */ }, \
+ { 0x1F6E, /* GREEK CAPITAL LETTER OMEGA WITH PSILI AND PERISPOMENI */ \
+ 0x1F66, /* GREEK SMALL LETTER OMEGA WITH PSILI AND PERISPOMENI */ }, \
+ { 0x1F6A, /* GREEK CAPITAL LETTER OMEGA WITH PSILI AND VARIA */ \
+ 0x1F62, /* GREEK SMALL LETTER OMEGA WITH PSILI AND VARIA */ }, \
+ { 0x038F, /* GREEK CAPITAL LETTER OMEGA WITH TONOS */ \
+ 0x03CE, /* GREEK SMALL LETTER OMEGA WITH TONOS */ }, \
+ { 0x1FFA, /* GREEK CAPITAL LETTER OMEGA WITH VARIA */ \
+ 0x1F7C, /* GREEK SMALL LETTER OMEGA WITH VARIA */ }, \
+ { 0x039F, /* GREEK CAPITAL LETTER OMICRON */ \
+ 0x03BF, /* GREEK SMALL LETTER OMICRON */ }, \
+ { 0x1F49, /* GREEK CAPITAL LETTER OMICRON WITH DASIA */ \
+ 0x1F41, /* GREEK SMALL LETTER OMICRON WITH DASIA */ }, \
+ { 0x1F4D, /* GREEK CAPITAL LETTER OMICRON WITH DASIA AND OXIA */ \
+ 0x1F45, /* GREEK SMALL LETTER OMICRON WITH DASIA AND OXIA */ }, \
+ { 0x1F4B, /* GREEK CAPITAL LETTER OMICRON WITH DASIA AND VARIA */ \
+ 0x1F43, /* GREEK SMALL LETTER OMICRON WITH DASIA AND VARIA */ }, \
+ { 0x1FF9, /* GREEK CAPITAL LETTER OMICRON WITH OXIA */ \
+ 0x1F79, /* GREEK SMALL LETTER OMICRON WITH OXIA */ }, \
+ { 0x1F48, /* GREEK CAPITAL LETTER OMICRON WITH PSILI */ \
+ 0x1F40, /* GREEK SMALL LETTER OMICRON WITH PSILI */ }, \
+ { 0x1F4C, /* GREEK CAPITAL LETTER OMICRON WITH PSILI AND OXIA */ \
+ 0x1F44, /* GREEK SMALL LETTER OMICRON WITH PSILI AND OXIA */ }, \
+ { 0x1F4A, /* GREEK CAPITAL LETTER OMICRON WITH PSILI AND VARIA */ \
+ 0x1F42, /* GREEK SMALL LETTER OMICRON WITH PSILI AND VARIA */ }, \
+ { 0x038C, /* GREEK CAPITAL LETTER OMICRON WITH TONOS */ \
+ 0x03CC, /* GREEK SMALL LETTER OMICRON WITH TONOS */ }, \
+ { 0x1FF8, /* GREEK CAPITAL LETTER OMICRON WITH VARIA */ \
+ 0x1F78, /* GREEK SMALL LETTER OMICRON WITH VARIA */ }, \
+ { 0x0376, /* GREEK CAPITAL LETTER PAMPHYLIAN DIGAMMA */ \
+ 0x0377, /* GREEK SMALL LETTER PAMPHYLIAN DIGAMMA */ }, \
+ { 0x03A6, /* GREEK CAPITAL LETTER PHI */ \
+ 0x03C6, /* GREEK SMALL LETTER PHI */ }, \
+ { 0x03A0, /* GREEK CAPITAL LETTER PI */ \
+ 0x03C0, /* GREEK SMALL LETTER PI */ }, \
+ { 0x03A8, /* GREEK CAPITAL LETTER PSI */ \
+ 0x03C8, /* GREEK SMALL LETTER PSI */ }, \
+ { 0x03A1, /* GREEK CAPITAL LETTER RHO */ \
+ 0x03C1, /* GREEK SMALL LETTER RHO */ }, \
+ { 0x1FEC, /* GREEK CAPITAL LETTER RHO WITH DASIA */ \
+ 0x1FE5, /* GREEK SMALL LETTER RHO WITH DASIA */ }, \
+ { 0x03FA, /* GREEK CAPITAL LETTER SAN */ \
+ 0x03FB, /* GREEK SMALL LETTER SAN */ }, \
+ { 0x03F7, /* GREEK CAPITAL LETTER SHO */ \
+ 0x03F8, /* GREEK SMALL LETTER SHO */ }, \
+ { 0x03A3, /* GREEK CAPITAL LETTER SIGMA */ \
+ 0x03C3, /* GREEK SMALL LETTER SIGMA */ }, \
+ { 0x03A4, /* GREEK CAPITAL LETTER TAU */ \
+ 0x03C4, /* GREEK SMALL LETTER TAU */ }, \
+ { 0x0398, /* GREEK CAPITAL LETTER THETA */ \
+ 0x03B8, /* GREEK SMALL LETTER THETA */ }, \
+ { 0x03A5, /* GREEK CAPITAL LETTER UPSILON */ \
+ 0x03C5, /* GREEK SMALL LETTER UPSILON */ }, \
+ { 0x1F59, /* GREEK CAPITAL LETTER UPSILON WITH DASIA */ \
+ 0x1F51, /* GREEK SMALL LETTER UPSILON WITH DASIA */ }, \
+ { 0x1F5D, /* GREEK CAPITAL LETTER UPSILON WITH DASIA AND OXIA */ \
+ 0x1F55, /* GREEK SMALL LETTER UPSILON WITH DASIA AND OXIA */ }, \
+ { 0x1F5F, /* GREEK CAPITAL LETTER UPSILON WITH DASIA AND PERISPOMENI */ \
+ 0x1F57, /* GREEK SMALL LETTER UPSILON WITH DASIA AND PERISPOMENI */ }, \
+ { 0x1F5B, /* GREEK CAPITAL LETTER UPSILON WITH DASIA AND VARIA */ \
+ 0x1F53, /* GREEK SMALL LETTER UPSILON WITH DASIA AND VARIA */ }, \
+ { 0x03AB, /* GREEK CAPITAL LETTER UPSILON WITH DIALYTIKA */ \
+ 0x03CB, /* GREEK SMALL LETTER UPSILON WITH DIALYTIKA */ }, \
+ { 0x1FE9, /* GREEK CAPITAL LETTER UPSILON WITH MACRON */ \
+ 0x1FE1, /* GREEK SMALL LETTER UPSILON WITH MACRON */ }, \
+ { 0x1FEB, /* GREEK CAPITAL LETTER UPSILON WITH OXIA */ \
+ 0x1F7B, /* GREEK SMALL LETTER UPSILON WITH OXIA */ }, \
+ { 0x038E, /* GREEK CAPITAL LETTER UPSILON WITH TONOS */ \
+ 0x03CD, /* GREEK SMALL LETTER UPSILON WITH TONOS */ }, \
+ { 0x1FEA, /* GREEK CAPITAL LETTER UPSILON WITH VARIA */ \
+ 0x1F7A, /* GREEK SMALL LETTER UPSILON WITH VARIA */ }, \
+ { 0x1FE8, /* GREEK CAPITAL LETTER UPSILON WITH VRACHY */ \
+ 0x1FE0, /* GREEK SMALL LETTER UPSILON WITH VRACHY */ }, \
+ { 0x039E, /* GREEK CAPITAL LETTER XI */ \
+ 0x03BE, /* GREEK SMALL LETTER XI */ }, \
+ { 0x0396, /* GREEK CAPITAL LETTER ZETA */ \
+ 0x03B6, /* GREEK SMALL LETTER ZETA */ }, \
+ { 0x0041, /* LATIN CAPITAL LETTER A */ \
+ 0x0061, /* LATIN SMALL LETTER A */ }, \
+ { 0x00C1, /* LATIN CAPITAL LETTER A WITH ACUTE */ \
+ 0x00E1, /* LATIN SMALL LETTER A WITH ACUTE */ }, \
+ { 0x0102, /* LATIN CAPITAL LETTER A WITH BREVE */ \
+ 0x0103, /* LATIN SMALL LETTER A WITH BREVE */ }, \
+ { 0x1EAE, /* LATIN CAPITAL LETTER A WITH BREVE AND ACUTE */ \
+ 0x1EAF, /* LATIN SMALL LETTER A WITH BREVE AND ACUTE */ }, \
+ { 0x1EB6, /* LATIN CAPITAL LETTER A WITH BREVE AND DOT BELOW */ \
+ 0x1EB7, /* LATIN SMALL LETTER A WITH BREVE AND DOT BELOW */ }, \
+ { 0x1EB0, /* LATIN CAPITAL LETTER A WITH BREVE AND GRAVE */ \
+ 0x1EB1, /* LATIN SMALL LETTER A WITH BREVE AND GRAVE */ }, \
+ { 0x1EB2, /* LATIN CAPITAL LETTER A WITH BREVE AND HOOK ABOVE */ \
+ 0x1EB3, /* LATIN SMALL LETTER A WITH BREVE AND HOOK ABOVE */ }, \
+ { 0x1EB4, /* LATIN CAPITAL LETTER A WITH BREVE AND TILDE */ \
+ 0x1EB5, /* LATIN SMALL LETTER A WITH BREVE AND TILDE */ }, \
+ { 0x01CD, /* LATIN CAPITAL LETTER A WITH CARON */ \
+ 0x01CE, /* LATIN SMALL LETTER A WITH CARON */ }, \
+ { 0x00C2, /* LATIN CAPITAL LETTER A WITH CIRCUMFLEX */ \
+ 0x00E2, /* LATIN SMALL LETTER A WITH CIRCUMFLEX */ }, \
+ { 0x1EA4, /* LATIN CAPITAL LETTER A WITH CIRCUMFLEX AND ACUTE */ \
+ 0x1EA5, /* LATIN SMALL LETTER A WITH CIRCUMFLEX AND ACUTE */ }, \
+ { 0x1EAC, /* LATIN CAPITAL LETTER A WITH CIRCUMFLEX AND DOT BELOW */ \
+ 0x1EAD, /* LATIN SMALL LETTER A WITH CIRCUMFLEX AND DOT BELOW */ }, \
+ { 0x1EA6, /* LATIN CAPITAL LETTER A WITH CIRCUMFLEX AND GRAVE */ \
+ 0x1EA7, /* LATIN SMALL LETTER A WITH CIRCUMFLEX AND GRAVE */ }, \
+ { 0x1EA8, /* LATIN CAPITAL LETTER A WITH CIRCUMFLEX AND HOOK ABOVE */ \
+ 0x1EA9, /* LATIN SMALL LETTER A WITH CIRCUMFLEX AND HOOK ABOVE */ }, \
+ { 0x1EAA, /* LATIN CAPITAL LETTER A WITH CIRCUMFLEX AND TILDE */ \
+ 0x1EAB, /* LATIN SMALL LETTER A WITH CIRCUMFLEX AND TILDE */ }, \
+ { 0x00C4, /* LATIN CAPITAL LETTER A WITH DIAERESIS */ \
+ 0x00E4, /* LATIN SMALL LETTER A WITH DIAERESIS */ }, \
+ { 0x01DE, /* LATIN CAPITAL LETTER A WITH DIAERESIS AND MACRON */ \
+ 0x01DF, /* LATIN SMALL LETTER A WITH DIAERESIS AND MACRON */ }, \
+ { 0x0226, /* LATIN CAPITAL LETTER A WITH DOT ABOVE */ \
+ 0x0227, /* LATIN SMALL LETTER A WITH DOT ABOVE */ }, \
+ { 0x01E0, /* LATIN CAPITAL LETTER A WITH DOT ABOVE AND MACRON */ \
+ 0x01E1, /* LATIN SMALL LETTER A WITH DOT ABOVE AND MACRON */ }, \
+ { 0x1EA0, /* LATIN CAPITAL LETTER A WITH DOT BELOW */ \
+ 0x1EA1, /* LATIN SMALL LETTER A WITH DOT BELOW */ }, \
+ { 0x0200, /* LATIN CAPITAL LETTER A WITH DOUBLE GRAVE */ \
+ 0x0201, /* LATIN SMALL LETTER A WITH DOUBLE GRAVE */ }, \
+ { 0x00C0, /* LATIN CAPITAL LETTER A WITH GRAVE */ \
+ 0x00E0, /* LATIN SMALL LETTER A WITH GRAVE */ }, \
+ { 0x1EA2, /* LATIN CAPITAL LETTER A WITH HOOK ABOVE */ \
+ 0x1EA3, /* LATIN SMALL LETTER A WITH HOOK ABOVE */ }, \
+ { 0x0202, /* LATIN CAPITAL LETTER A WITH INVERTED BREVE */ \
+ 0x0203, /* LATIN SMALL LETTER A WITH INVERTED BREVE */ }, \
+ { 0x0100, /* LATIN CAPITAL LETTER A WITH MACRON */ \
+ 0x0101, /* LATIN SMALL LETTER A WITH MACRON */ }, \
+ { 0x0104, /* LATIN CAPITAL LETTER A WITH OGONEK */ \
+ 0x0105, /* LATIN SMALL LETTER A WITH OGONEK */ }, \
+ { 0x00C5, /* LATIN CAPITAL LETTER A WITH RING ABOVE */ \
+ 0x00E5, /* LATIN SMALL LETTER A WITH RING ABOVE */ }, \
+ { 0x01FA, /* LATIN CAPITAL LETTER A WITH RING ABOVE AND ACUTE */ \
+ 0x01FB, /* LATIN SMALL LETTER A WITH RING ABOVE AND ACUTE */ }, \
+ { 0x1E00, /* LATIN CAPITAL LETTER A WITH RING BELOW */ \
+ 0x1E01, /* LATIN SMALL LETTER A WITH RING BELOW */ }, \
+ { 0x023A, /* LATIN CAPITAL LETTER A WITH STROKE */ \
+ 0x2C65, /* LATIN SMALL LETTER A WITH STROKE */ }, \
+ { 0x00C3, /* LATIN CAPITAL LETTER A WITH TILDE */ \
+ 0x00E3, /* LATIN SMALL LETTER A WITH TILDE */ }, \
+ { 0xA732, /* LATIN CAPITAL LETTER AA */ \
+ 0xA733, /* LATIN SMALL LETTER AA */ }, \
+ { 0x00C6, /* LATIN CAPITAL LETTER AE */ \
+ 0x00E6, /* LATIN SMALL LETTER AE */ }, \
+ { 0x01FC, /* LATIN CAPITAL LETTER AE WITH ACUTE */ \
+ 0x01FD, /* LATIN SMALL LETTER AE WITH ACUTE */ }, \
+ { 0x01E2, /* LATIN CAPITAL LETTER AE WITH MACRON */ \
+ 0x01E3, /* LATIN SMALL LETTER AE WITH MACRON */ }, \
+ { 0x2C6D, /* LATIN CAPITAL LETTER ALPHA */ \
+ 0x0251, /* LATIN SMALL LETTER ALPHA */ }, \
+ { 0xA734, /* LATIN CAPITAL LETTER AO */ \
+ 0xA735, /* LATIN SMALL LETTER AO */ }, \
+ { 0xA736, /* LATIN CAPITAL LETTER AU */ \
+ 0xA737, /* LATIN SMALL LETTER AU */ }, \
+ { 0xA738, /* LATIN CAPITAL LETTER AV */ \
+ 0xA739, /* LATIN SMALL LETTER AV */ }, \
+ { 0xA73A, /* LATIN CAPITAL LETTER AV WITH HORIZONTAL BAR */ \
+ 0xA73B, /* LATIN SMALL LETTER AV WITH HORIZONTAL BAR */ }, \
+ { 0xA73C, /* LATIN CAPITAL LETTER AY */ \
+ 0xA73D, /* LATIN SMALL LETTER AY */ }, \
+ { 0x0042, /* LATIN CAPITAL LETTER B */ \
+ 0x0062, /* LATIN SMALL LETTER B */ }, \
+ { 0x1E02, /* LATIN CAPITAL LETTER B WITH DOT ABOVE */ \
+ 0x1E03, /* LATIN SMALL LETTER B WITH DOT ABOVE */ }, \
+ { 0x1E04, /* LATIN CAPITAL LETTER B WITH DOT BELOW */ \
+ 0x1E05, /* LATIN SMALL LETTER B WITH DOT BELOW */ }, \
+ { 0xA796, /* LATIN CAPITAL LETTER B WITH FLOURISH */ \
+ 0xA797, /* LATIN SMALL LETTER B WITH FLOURISH */ }, \
+ { 0x0181, /* LATIN CAPITAL LETTER B WITH HOOK */ \
+ 0x0253, /* LATIN SMALL LETTER B WITH HOOK */ }, \
+ { 0x1E06, /* LATIN CAPITAL LETTER B WITH LINE BELOW */ \
+ 0x1E07, /* LATIN SMALL LETTER B WITH LINE BELOW */ }, \
+ { 0x0243, /* LATIN CAPITAL LETTER B WITH STROKE */ \
+ 0x0180, /* LATIN SMALL LETTER B WITH STROKE */ }, \
+ { 0x0182, /* LATIN CAPITAL LETTER B WITH TOPBAR */ \
+ 0x0183, /* LATIN SMALL LETTER B WITH TOPBAR */ }, \
+ { 0xA7B4, /* LATIN CAPITAL LETTER BETA */ \
+ 0xA7B5, /* LATIN SMALL LETTER BETA */ }, \
+ { 0xA746, /* LATIN CAPITAL LETTER BROKEN L */ \
+ 0xA747, /* LATIN SMALL LETTER BROKEN L */ }, \
+ { 0x0043, /* LATIN CAPITAL LETTER C */ \
+ 0x0063, /* LATIN SMALL LETTER C */ }, \
+ { 0x0106, /* LATIN CAPITAL LETTER C WITH ACUTE */ \
+ 0x0107, /* LATIN SMALL LETTER C WITH ACUTE */ }, \
+ { 0xA792, /* LATIN CAPITAL LETTER C WITH BAR */ \
+ 0xA793, /* LATIN SMALL LETTER C WITH BAR */ }, \
+ { 0x010C, /* LATIN CAPITAL LETTER C WITH CARON */ \
+ 0x010D, /* LATIN SMALL LETTER C WITH CARON */ }, \
+ { 0x00C7, /* LATIN CAPITAL LETTER C WITH CEDILLA */ \
+ 0x00E7, /* LATIN SMALL LETTER C WITH CEDILLA */ }, \
+ { 0x1E08, /* LATIN CAPITAL LETTER C WITH CEDILLA AND ACUTE */ \
+ 0x1E09, /* LATIN SMALL LETTER C WITH CEDILLA AND ACUTE */ }, \
+ { 0x0108, /* LATIN CAPITAL LETTER C WITH CIRCUMFLEX */ \
+ 0x0109, /* LATIN SMALL LETTER C WITH CIRCUMFLEX */ }, \
+ { 0x010A, /* LATIN CAPITAL LETTER C WITH DOT ABOVE */ \
+ 0x010B, /* LATIN SMALL LETTER C WITH DOT ABOVE */ }, \
+ { 0x0187, /* LATIN CAPITAL LETTER C WITH HOOK */ \
+ 0x0188, /* LATIN SMALL LETTER C WITH HOOK */ }, \
+ { 0x023B, /* LATIN CAPITAL LETTER C WITH STROKE */ \
+ 0x023C, /* LATIN SMALL LETTER C WITH STROKE */ }, \
+ { 0xA7B3, /* LATIN CAPITAL LETTER CHI */ \
+ 0xAB53, /* LATIN SMALL LETTER CHI */ }, \
+ { 0xA76E, /* LATIN CAPITAL LETTER CON */ \
+ 0xA76F, /* LATIN SMALL LETTER CON */ }, \
+ { 0xA72C, /* LATIN CAPITAL LETTER CUATRILLO */ \
+ 0xA72D, /* LATIN SMALL LETTER CUATRILLO */ }, \
+ { 0xA72E, /* LATIN CAPITAL LETTER CUATRILLO WITH COMMA */ \
+ 0xA72F, /* LATIN SMALL LETTER CUATRILLO WITH COMMA */ }, \
+ { 0x0044, /* LATIN CAPITAL LETTER D */ \
+ 0x0064, /* LATIN SMALL LETTER D */ }, \
+ { 0x010E, /* LATIN CAPITAL LETTER D WITH CARON */ \
+ 0x010F, /* LATIN SMALL LETTER D WITH CARON */ }, \
+ { 0x1E10, /* LATIN CAPITAL LETTER D WITH CEDILLA */ \
+ 0x1E11, /* LATIN SMALL LETTER D WITH CEDILLA */ }, \
+ { 0x1E12, /* LATIN CAPITAL LETTER D WITH CIRCUMFLEX BELOW */ \
+ 0x1E13, /* LATIN SMALL LETTER D WITH CIRCUMFLEX BELOW */ }, \
+ { 0x1E0A, /* LATIN CAPITAL LETTER D WITH DOT ABOVE */ \
+ 0x1E0B, /* LATIN SMALL LETTER D WITH DOT ABOVE */ }, \
+ { 0x1E0C, /* LATIN CAPITAL LETTER D WITH DOT BELOW */ \
+ 0x1E0D, /* LATIN SMALL LETTER D WITH DOT BELOW */ }, \
+ { 0x018A, /* LATIN CAPITAL LETTER D WITH HOOK */ \
+ 0x0257, /* LATIN SMALL LETTER D WITH HOOK */ }, \
+ { 0x1E0E, /* LATIN CAPITAL LETTER D WITH LINE BELOW */ \
+ 0x1E0F, /* LATIN SMALL LETTER D WITH LINE BELOW */ }, \
+ { 0x0110, /* LATIN CAPITAL LETTER D WITH STROKE */ \
+ 0x0111, /* LATIN SMALL LETTER D WITH STROKE */ }, \
+ { 0x018B, /* LATIN CAPITAL LETTER D WITH TOPBAR */ \
+ 0x018C, /* LATIN SMALL LETTER D WITH TOPBAR */ }, \
+ { 0x01F1, /* LATIN CAPITAL LETTER DZ */ \
+ 0x01F3, /* LATIN SMALL LETTER DZ */ }, \
+ { 0x01C4, /* LATIN CAPITAL LETTER DZ WITH CARON */ \
+ 0x01C6, /* LATIN SMALL LETTER DZ WITH CARON */ }, \
+ { 0x0045, /* LATIN CAPITAL LETTER E */ \
+ 0x0065, /* LATIN SMALL LETTER E */ }, \
+ { 0x00C9, /* LATIN CAPITAL LETTER E WITH ACUTE */ \
+ 0x00E9, /* LATIN SMALL LETTER E WITH ACUTE */ }, \
+ { 0x0114, /* LATIN CAPITAL LETTER E WITH BREVE */ \
+ 0x0115, /* LATIN SMALL LETTER E WITH BREVE */ }, \
+ { 0x011A, /* LATIN CAPITAL LETTER E WITH CARON */ \
+ 0x011B, /* LATIN SMALL LETTER E WITH CARON */ }, \
+ { 0x0228, /* LATIN CAPITAL LETTER E WITH CEDILLA */ \
+ 0x0229, /* LATIN SMALL LETTER E WITH CEDILLA */ }, \
+ { 0x1E1C, /* LATIN CAPITAL LETTER E WITH CEDILLA AND BREVE */ \
+ 0x1E1D, /* LATIN SMALL LETTER E WITH CEDILLA AND BREVE */ }, \
+ { 0x00CA, /* LATIN CAPITAL LETTER E WITH CIRCUMFLEX */ \
+ 0x00EA, /* LATIN SMALL LETTER E WITH CIRCUMFLEX */ }, \
+ { 0x1EBE, /* LATIN CAPITAL LETTER E WITH CIRCUMFLEX AND ACUTE */ \
+ 0x1EBF, /* LATIN SMALL LETTER E WITH CIRCUMFLEX AND ACUTE */ }, \
+ { 0x1EC6, /* LATIN CAPITAL LETTER E WITH CIRCUMFLEX AND DOT BELOW */ \
+ 0x1EC7, /* LATIN SMALL LETTER E WITH CIRCUMFLEX AND DOT BELOW */ }, \
+ { 0x1EC0, /* LATIN CAPITAL LETTER E WITH CIRCUMFLEX AND GRAVE */ \
+ 0x1EC1, /* LATIN SMALL LETTER E WITH CIRCUMFLEX AND GRAVE */ }, \
+ { 0x1EC2, /* LATIN CAPITAL LETTER E WITH CIRCUMFLEX AND HOOK ABOVE */ \
+ 0x1EC3, /* LATIN SMALL LETTER E WITH CIRCUMFLEX AND HOOK ABOVE */ }, \
+ { 0x1EC4, /* LATIN CAPITAL LETTER E WITH CIRCUMFLEX AND TILDE */ \
+ 0x1EC5, /* LATIN SMALL LETTER E WITH CIRCUMFLEX AND TILDE */ }, \
+ { 0x1E18, /* LATIN CAPITAL LETTER E WITH CIRCUMFLEX BELOW */ \
+ 0x1E19, /* LATIN SMALL LETTER E WITH CIRCUMFLEX BELOW */ }, \
+ { 0x00CB, /* LATIN CAPITAL LETTER E WITH DIAERESIS */ \
+ 0x00EB, /* LATIN SMALL LETTER E WITH DIAERESIS */ }, \
+ { 0x0116, /* LATIN CAPITAL LETTER E WITH DOT ABOVE */ \
+ 0x0117, /* LATIN SMALL LETTER E WITH DOT ABOVE */ }, \
+ { 0x1EB8, /* LATIN CAPITAL LETTER E WITH DOT BELOW */ \
+ 0x1EB9, /* LATIN SMALL LETTER E WITH DOT BELOW */ }, \
+ { 0x0204, /* LATIN CAPITAL LETTER E WITH DOUBLE GRAVE */ \
+ 0x0205, /* LATIN SMALL LETTER E WITH DOUBLE GRAVE */ }, \
+ { 0x00C8, /* LATIN CAPITAL LETTER E WITH GRAVE */ \
+ 0x00E8, /* LATIN SMALL LETTER E WITH GRAVE */ }, \
+ { 0x1EBA, /* LATIN CAPITAL LETTER E WITH HOOK ABOVE */ \
+ 0x1EBB, /* LATIN SMALL LETTER E WITH HOOK ABOVE */ }, \
+ { 0x0206, /* LATIN CAPITAL LETTER E WITH INVERTED BREVE */ \
+ 0x0207, /* LATIN SMALL LETTER E WITH INVERTED BREVE */ }, \
+ { 0x0112, /* LATIN CAPITAL LETTER E WITH MACRON */ \
+ 0x0113, /* LATIN SMALL LETTER E WITH MACRON */ }, \
+ { 0x1E16, /* LATIN CAPITAL LETTER E WITH MACRON AND ACUTE */ \
+ 0x1E17, /* LATIN SMALL LETTER E WITH MACRON AND ACUTE */ }, \
+ { 0x1E14, /* LATIN CAPITAL LETTER E WITH MACRON AND GRAVE */ \
+ 0x1E15, /* LATIN SMALL LETTER E WITH MACRON AND GRAVE */ }, \
+ { 0x0118, /* LATIN CAPITAL LETTER E WITH OGONEK */ \
+ 0x0119, /* LATIN SMALL LETTER E WITH OGONEK */ }, \
+ { 0x0246, /* LATIN CAPITAL LETTER E WITH STROKE */ \
+ 0x0247, /* LATIN SMALL LETTER E WITH STROKE */ }, \
+ { 0x1EBC, /* LATIN CAPITAL LETTER E WITH TILDE */ \
+ 0x1EBD, /* LATIN SMALL LETTER E WITH TILDE */ }, \
+ { 0x1E1A, /* LATIN CAPITAL LETTER E WITH TILDE BELOW */ \
+ 0x1E1B, /* LATIN SMALL LETTER E WITH TILDE BELOW */ }, \
+ { 0xA724, /* LATIN CAPITAL LETTER EGYPTOLOGICAL AIN */ \
+ 0xA725, /* LATIN SMALL LETTER EGYPTOLOGICAL AIN */ }, \
+ { 0xA722, /* LATIN CAPITAL LETTER EGYPTOLOGICAL ALEF */ \
+ 0xA723, /* LATIN SMALL LETTER EGYPTOLOGICAL ALEF */ }, \
+ { 0x014A, /* LATIN CAPITAL LETTER ENG */ \
+ 0x014B, /* LATIN SMALL LETTER ENG */ }, \
+ { 0x01A9, /* LATIN CAPITAL LETTER ESH */ \
+ 0x0283, /* LATIN SMALL LETTER ESH */ }, \
+ { 0xA76A, /* LATIN CAPITAL LETTER ET */ \
+ 0xA76B, /* LATIN SMALL LETTER ET */ }, \
+ { 0x00D0, /* LATIN CAPITAL LETTER ETH */ \
+ 0x00F0, /* LATIN SMALL LETTER ETH */ }, \
+ { 0x01B7, /* LATIN CAPITAL LETTER EZH */ \
+ 0x0292, /* LATIN SMALL LETTER EZH */ }, \
+ { 0x01B8, /* LATIN CAPITAL LETTER EZH REVERSED */ \
+ 0x01B9, /* LATIN SMALL LETTER EZH REVERSED */ }, \
+ { 0x01EE, /* LATIN CAPITAL LETTER EZH WITH CARON */ \
+ 0x01EF, /* LATIN SMALL LETTER EZH WITH CARON */ }, \
+ { 0x0046, /* LATIN CAPITAL LETTER F */ \
+ 0x0066, /* LATIN SMALL LETTER F */ }, \
+ { 0x1E1E, /* LATIN CAPITAL LETTER F WITH DOT ABOVE */ \
+ 0x1E1F, /* LATIN SMALL LETTER F WITH DOT ABOVE */ }, \
+ { 0x0191, /* LATIN CAPITAL LETTER F WITH HOOK */ \
+ 0x0192, /* LATIN SMALL LETTER F WITH HOOK */ }, \
+ { 0xA798, /* LATIN CAPITAL LETTER F WITH STROKE */ \
+ 0xA799, /* LATIN SMALL LETTER F WITH STROKE */ }, \
+ { 0x0047, /* LATIN CAPITAL LETTER G */ \
+ 0x0067, /* LATIN SMALL LETTER G */ }, \
+ { 0x01F4, /* LATIN CAPITAL LETTER G WITH ACUTE */ \
+ 0x01F5, /* LATIN SMALL LETTER G WITH ACUTE */ }, \
+ { 0x011E, /* LATIN CAPITAL LETTER G WITH BREVE */ \
+ 0x011F, /* LATIN SMALL LETTER G WITH BREVE */ }, \
+ { 0x01E6, /* LATIN CAPITAL LETTER G WITH CARON */ \
+ 0x01E7, /* LATIN SMALL LETTER G WITH CARON */ }, \
+ { 0x0122, /* LATIN CAPITAL LETTER G WITH CEDILLA */ \
+ 0x0123, /* LATIN SMALL LETTER G WITH CEDILLA */ }, \
+ { 0x011C, /* LATIN CAPITAL LETTER G WITH CIRCUMFLEX */ \
+ 0x011D, /* LATIN SMALL LETTER G WITH CIRCUMFLEX */ }, \
+ { 0x0120, /* LATIN CAPITAL LETTER G WITH DOT ABOVE */ \
+ 0x0121, /* LATIN SMALL LETTER G WITH DOT ABOVE */ }, \
+ { 0x0193, /* LATIN CAPITAL LETTER G WITH HOOK */ \
+ 0x0260, /* LATIN SMALL LETTER G WITH HOOK */ }, \
+ { 0x1E20, /* LATIN CAPITAL LETTER G WITH MACRON */ \
+ 0x1E21, /* LATIN SMALL LETTER G WITH MACRON */ }, \
+ { 0xA7A0, /* LATIN CAPITAL LETTER G WITH OBLIQUE STROKE */ \
+ 0xA7A1, /* LATIN SMALL LETTER G WITH OBLIQUE STROKE */ }, \
+ { 0x01E4, /* LATIN CAPITAL LETTER G WITH STROKE */ \
+ 0x01E5, /* LATIN SMALL LETTER G WITH STROKE */ }, \
+ { 0x0194, /* LATIN CAPITAL LETTER GAMMA */ \
+ 0x0263, /* LATIN SMALL LETTER GAMMA */ }, \
+ { 0x0241, /* LATIN CAPITAL LETTER GLOTTAL STOP */ \
+ 0x0242, /* LATIN SMALL LETTER GLOTTAL STOP */ }, \
+ { 0x0048, /* LATIN CAPITAL LETTER H */ \
+ 0x0068, /* LATIN SMALL LETTER H */ }, \
+ { 0x1E2A, /* LATIN CAPITAL LETTER H WITH BREVE BELOW */ \
+ 0x1E2B, /* LATIN SMALL LETTER H WITH BREVE BELOW */ }, \
+ { 0x021E, /* LATIN CAPITAL LETTER H WITH CARON */ \
+ 0x021F, /* LATIN SMALL LETTER H WITH CARON */ }, \
+ { 0x1E28, /* LATIN CAPITAL LETTER H WITH CEDILLA */ \
+ 0x1E29, /* LATIN SMALL LETTER H WITH CEDILLA */ }, \
+ { 0x0124, /* LATIN CAPITAL LETTER H WITH CIRCUMFLEX */ \
+ 0x0125, /* LATIN SMALL LETTER H WITH CIRCUMFLEX */ }, \
+ { 0x2C67, /* LATIN CAPITAL LETTER H WITH DESCENDER */ \
+ 0x2C68, /* LATIN SMALL LETTER H WITH DESCENDER */ }, \
+ { 0x1E26, /* LATIN CAPITAL LETTER H WITH DIAERESIS */ \
+ 0x1E27, /* LATIN SMALL LETTER H WITH DIAERESIS */ }, \
+ { 0x1E22, /* LATIN CAPITAL LETTER H WITH DOT ABOVE */ \
+ 0x1E23, /* LATIN SMALL LETTER H WITH DOT ABOVE */ }, \
+ { 0x1E24, /* LATIN CAPITAL LETTER H WITH DOT BELOW */ \
+ 0x1E25, /* LATIN SMALL LETTER H WITH DOT BELOW */ }, \
+ { 0xA7AA, /* LATIN CAPITAL LETTER H WITH HOOK */ \
+ 0x0266, /* LATIN SMALL LETTER H WITH HOOK */ }, \
+ { 0x0126, /* LATIN CAPITAL LETTER H WITH STROKE */ \
+ 0x0127, /* LATIN SMALL LETTER H WITH STROKE */ }, \
+ { 0x2C75, /* LATIN CAPITAL LETTER HALF H */ \
+ 0x2C76, /* LATIN SMALL LETTER HALF H */ }, \
+ { 0xA726, /* LATIN CAPITAL LETTER HENG */ \
+ 0xA727, /* LATIN SMALL LETTER HENG */ }, \
+ { 0x0049, /* LATIN CAPITAL LETTER I */ \
+ 0x0069, /* LATIN SMALL LETTER I */ }, \
+ { 0x00CD, /* LATIN CAPITAL LETTER I WITH ACUTE */ \
+ 0x00ED, /* LATIN SMALL LETTER I WITH ACUTE */ }, \
+ { 0x012C, /* LATIN CAPITAL LETTER I WITH BREVE */ \
+ 0x012D, /* LATIN SMALL LETTER I WITH BREVE */ }, \
+ { 0x01CF, /* LATIN CAPITAL LETTER I WITH CARON */ \
+ 0x01D0, /* LATIN SMALL LETTER I WITH CARON */ }, \
+ { 0x00CE, /* LATIN CAPITAL LETTER I WITH CIRCUMFLEX */ \
+ 0x00EE, /* LATIN SMALL LETTER I WITH CIRCUMFLEX */ }, \
+ { 0x00CF, /* LATIN CAPITAL LETTER I WITH DIAERESIS */ \
+ 0x00EF, /* LATIN SMALL LETTER I WITH DIAERESIS */ }, \
+ { 0x1E2E, /* LATIN CAPITAL LETTER I WITH DIAERESIS AND ACUTE */ \
+ 0x1E2F, /* LATIN SMALL LETTER I WITH DIAERESIS AND ACUTE */ }, \
+ { 0x1ECA, /* LATIN CAPITAL LETTER I WITH DOT BELOW */ \
+ 0x1ECB, /* LATIN SMALL LETTER I WITH DOT BELOW */ }, \
+ { 0x0208, /* LATIN CAPITAL LETTER I WITH DOUBLE GRAVE */ \
+ 0x0209, /* LATIN SMALL LETTER I WITH DOUBLE GRAVE */ }, \
+ { 0x00CC, /* LATIN CAPITAL LETTER I WITH GRAVE */ \
+ 0x00EC, /* LATIN SMALL LETTER I WITH GRAVE */ }, \
+ { 0x1EC8, /* LATIN CAPITAL LETTER I WITH HOOK ABOVE */ \
+ 0x1EC9, /* LATIN SMALL LETTER I WITH HOOK ABOVE */ }, \
+ { 0x020A, /* LATIN CAPITAL LETTER I WITH INVERTED BREVE */ \
+ 0x020B, /* LATIN SMALL LETTER I WITH INVERTED BREVE */ }, \
+ { 0x012A, /* LATIN CAPITAL LETTER I WITH MACRON */ \
+ 0x012B, /* LATIN SMALL LETTER I WITH MACRON */ }, \
+ { 0x012E, /* LATIN CAPITAL LETTER I WITH OGONEK */ \
+ 0x012F, /* LATIN SMALL LETTER I WITH OGONEK */ }, \
+ { 0x0197, /* LATIN CAPITAL LETTER I WITH STROKE */ \
+ 0x0268, /* LATIN SMALL LETTER I WITH STROKE */ }, \
+ { 0x0128, /* LATIN CAPITAL LETTER I WITH TILDE */ \
+ 0x0129, /* LATIN SMALL LETTER I WITH TILDE */ }, \
+ { 0x1E2C, /* LATIN CAPITAL LETTER I WITH TILDE BELOW */ \
+ 0x1E2D, /* LATIN SMALL LETTER I WITH TILDE BELOW */ }, \
+ { 0xA779, /* LATIN CAPITAL LETTER INSULAR D */ \
+ 0xA77A, /* LATIN SMALL LETTER INSULAR D */ }, \
+ { 0xA77B, /* LATIN CAPITAL LETTER INSULAR F */ \
+ 0xA77C, /* LATIN SMALL LETTER INSULAR F */ }, \
+ { 0xA77D, /* LATIN CAPITAL LETTER INSULAR G */ \
+ 0x1D79, /* LATIN SMALL LETTER INSULAR G */ }, \
+ { 0xA782, /* LATIN CAPITAL LETTER INSULAR R */ \
+ 0xA783, /* LATIN SMALL LETTER INSULAR R */ }, \
+ { 0xA784, /* LATIN CAPITAL LETTER INSULAR S */ \
+ 0xA785, /* LATIN SMALL LETTER INSULAR S */ }, \
+ { 0xA786, /* LATIN CAPITAL LETTER INSULAR T */ \
+ 0xA787, /* LATIN SMALL LETTER INSULAR T */ }, \
+ { 0x0196, /* LATIN CAPITAL LETTER IOTA */ \
+ 0x0269, /* LATIN SMALL LETTER IOTA */ }, \
+ { 0xA76C, /* LATIN CAPITAL LETTER IS */ \
+ 0xA76D, /* LATIN SMALL LETTER IS */ }, \
+ { 0x004A, /* LATIN CAPITAL LETTER J */ \
+ 0x006A, /* LATIN SMALL LETTER J */ }, \
+ { 0x0134, /* LATIN CAPITAL LETTER J WITH CIRCUMFLEX */ \
+ 0x0135, /* LATIN SMALL LETTER J WITH CIRCUMFLEX */ }, \
+ { 0xA7B2, /* LATIN CAPITAL LETTER J WITH CROSSED-TAIL */ \
+ 0x029D, /* LATIN SMALL LETTER J WITH CROSSED-TAIL */ }, \
+ { 0x0248, /* LATIN CAPITAL LETTER J WITH STROKE */ \
+ 0x0249, /* LATIN SMALL LETTER J WITH STROKE */ }, \
+ { 0x004B, /* LATIN CAPITAL LETTER K */ \
+ 0x006B, /* LATIN SMALL LETTER K */ }, \
+ { 0x1E30, /* LATIN CAPITAL LETTER K WITH ACUTE */ \
+ 0x1E31, /* LATIN SMALL LETTER K WITH ACUTE */ }, \
+ { 0x01E8, /* LATIN CAPITAL LETTER K WITH CARON */ \
+ 0x01E9, /* LATIN SMALL LETTER K WITH CARON */ }, \
+ { 0x0136, /* LATIN CAPITAL LETTER K WITH CEDILLA */ \
+ 0x0137, /* LATIN SMALL LETTER K WITH CEDILLA */ }, \
+ { 0x2C69, /* LATIN CAPITAL LETTER K WITH DESCENDER */ \
+ 0x2C6A, /* LATIN SMALL LETTER K WITH DESCENDER */ }, \
+ { 0xA742, /* LATIN CAPITAL LETTER K WITH DIAGONAL STROKE */ \
+ 0xA743, /* LATIN SMALL LETTER K WITH DIAGONAL STROKE */ }, \
+ { 0x1E32, /* LATIN CAPITAL LETTER K WITH DOT BELOW */ \
+ 0x1E33, /* LATIN SMALL LETTER K WITH DOT BELOW */ }, \
+ { 0x0198, /* LATIN CAPITAL LETTER K WITH HOOK */ \
+ 0x0199, /* LATIN SMALL LETTER K WITH HOOK */ }, \
+ { 0x1E34, /* LATIN CAPITAL LETTER K WITH LINE BELOW */ \
+ 0x1E35, /* LATIN SMALL LETTER K WITH LINE BELOW */ }, \
+ { 0xA7A2, /* LATIN CAPITAL LETTER K WITH OBLIQUE STROKE */ \
+ 0xA7A3, /* LATIN SMALL LETTER K WITH OBLIQUE STROKE */ }, \
+ { 0xA740, /* LATIN CAPITAL LETTER K WITH STROKE */ \
+ 0xA741, /* LATIN SMALL LETTER K WITH STROKE */ }, \
+ { 0xA744, /* LATIN CAPITAL LETTER K WITH STROKE AND DIAGONAL STROKE */ \
+ 0xA745, /* LATIN SMALL LETTER K WITH STROKE AND DIAGONAL STROKE */ }, \
+ { 0x004C, /* LATIN CAPITAL LETTER L */ \
+ 0x006C, /* LATIN SMALL LETTER L */ }, \
+ { 0x0139, /* LATIN CAPITAL LETTER L WITH ACUTE */ \
+ 0x013A, /* LATIN SMALL LETTER L WITH ACUTE */ }, \
+ { 0x023D, /* LATIN CAPITAL LETTER L WITH BAR */ \
+ 0x019A, /* LATIN SMALL LETTER L WITH BAR */ }, \
+ { 0xA7AD, /* LATIN CAPITAL LETTER L WITH BELT */ \
+ 0x026C, /* LATIN SMALL LETTER L WITH BELT */ }, \
+ { 0x013D, /* LATIN CAPITAL LETTER L WITH CARON */ \
+ 0x013E, /* LATIN SMALL LETTER L WITH CARON */ }, \
+ { 0x013B, /* LATIN CAPITAL LETTER L WITH CEDILLA */ \
+ 0x013C, /* LATIN SMALL LETTER L WITH CEDILLA */ }, \
+ { 0x1E3C, /* LATIN CAPITAL LETTER L WITH CIRCUMFLEX BELOW */ \
+ 0x1E3D, /* LATIN SMALL LETTER L WITH CIRCUMFLEX BELOW */ }, \
+ { 0x1E36, /* LATIN CAPITAL LETTER L WITH DOT BELOW */ \
+ 0x1E37, /* LATIN SMALL LETTER L WITH DOT BELOW */ }, \
+ { 0x1E38, /* LATIN CAPITAL LETTER L WITH DOT BELOW AND MACRON */ \
+ 0x1E39, /* LATIN SMALL LETTER L WITH DOT BELOW AND MACRON */ }, \
+ { 0x2C60, /* LATIN CAPITAL LETTER L WITH DOUBLE BAR */ \
+ 0x2C61, /* LATIN SMALL LETTER L WITH DOUBLE BAR */ }, \
+ { 0xA748, /* LATIN CAPITAL LETTER L WITH HIGH STROKE */ \
+ 0xA749, /* LATIN SMALL LETTER L WITH HIGH STROKE */ }, \
+ { 0x1E3A, /* LATIN CAPITAL LETTER L WITH LINE BELOW */ \
+ 0x1E3B, /* LATIN SMALL LETTER L WITH LINE BELOW */ }, \
+ { 0x013F, /* LATIN CAPITAL LETTER L WITH MIDDLE DOT */ \
+ 0x0140, /* LATIN SMALL LETTER L WITH MIDDLE DOT */ }, \
+ { 0x2C62, /* LATIN CAPITAL LETTER L WITH MIDDLE TILDE */ \
+ 0x026B, /* LATIN SMALL LETTER L WITH MIDDLE TILDE */ }, \
+ { 0x0141, /* LATIN CAPITAL LETTER L WITH STROKE */ \
+ 0x0142, /* LATIN SMALL LETTER L WITH STROKE */ }, \
+ { 0x01C7, /* LATIN CAPITAL LETTER LJ */ \
+ 0x01C9, /* LATIN SMALL LETTER LJ */ }, \
+ { 0x004D, /* LATIN CAPITAL LETTER M */ \
+ 0x006D, /* LATIN SMALL LETTER M */ }, \
+ { 0x1E3E, /* LATIN CAPITAL LETTER M WITH ACUTE */ \
+ 0x1E3F, /* LATIN SMALL LETTER M WITH ACUTE */ }, \
+ { 0x1E40, /* LATIN CAPITAL LETTER M WITH DOT ABOVE */ \
+ 0x1E41, /* LATIN SMALL LETTER M WITH DOT ABOVE */ }, \
+ { 0x1E42, /* LATIN CAPITAL LETTER M WITH DOT BELOW */ \
+ 0x1E43, /* LATIN SMALL LETTER M WITH DOT BELOW */ }, \
+ { 0x2C6E, /* LATIN CAPITAL LETTER M WITH HOOK */ \
+ 0x0271, /* LATIN SMALL LETTER M WITH HOOK */ }, \
+ { 0x1EFA, /* LATIN CAPITAL LETTER MIDDLE-WELSH LL */ \
+ 0x1EFB, /* LATIN SMALL LETTER MIDDLE-WELSH LL */ }, \
+ { 0x1EFC, /* LATIN CAPITAL LETTER MIDDLE-WELSH V */ \
+ 0x1EFD, /* LATIN SMALL LETTER MIDDLE-WELSH V */ }, \
+ { 0x004E, /* LATIN CAPITAL LETTER N */ \
+ 0x006E, /* LATIN SMALL LETTER N */ }, \
+ { 0x0143, /* LATIN CAPITAL LETTER N WITH ACUTE */ \
+ 0x0144, /* LATIN SMALL LETTER N WITH ACUTE */ }, \
+ { 0x0147, /* LATIN CAPITAL LETTER N WITH CARON */ \
+ 0x0148, /* LATIN SMALL LETTER N WITH CARON */ }, \
+ { 0x0145, /* LATIN CAPITAL LETTER N WITH CEDILLA */ \
+ 0x0146, /* LATIN SMALL LETTER N WITH CEDILLA */ }, \
+ { 0x1E4A, /* LATIN CAPITAL LETTER N WITH CIRCUMFLEX BELOW */ \
+ 0x1E4B, /* LATIN SMALL LETTER N WITH CIRCUMFLEX BELOW */ }, \
+ { 0xA790, /* LATIN CAPITAL LETTER N WITH DESCENDER */ \
+ 0xA791, /* LATIN SMALL LETTER N WITH DESCENDER */ }, \
+ { 0x1E44, /* LATIN CAPITAL LETTER N WITH DOT ABOVE */ \
+ 0x1E45, /* LATIN SMALL LETTER N WITH DOT ABOVE */ }, \
+ { 0x1E46, /* LATIN CAPITAL LETTER N WITH DOT BELOW */ \
+ 0x1E47, /* LATIN SMALL LETTER N WITH DOT BELOW */ }, \
+ { 0x01F8, /* LATIN CAPITAL LETTER N WITH GRAVE */ \
+ 0x01F9, /* LATIN SMALL LETTER N WITH GRAVE */ }, \
+ { 0x019D, /* LATIN CAPITAL LETTER N WITH LEFT HOOK */ \
+ 0x0272, /* LATIN SMALL LETTER N WITH LEFT HOOK */ }, \
+ { 0x1E48, /* LATIN CAPITAL LETTER N WITH LINE BELOW */ \
+ 0x1E49, /* LATIN SMALL LETTER N WITH LINE BELOW */ }, \
+ { 0x0220, /* LATIN CAPITAL LETTER N WITH LONG RIGHT LEG */ \
+ 0x019E, /* LATIN SMALL LETTER N WITH LONG RIGHT LEG */ }, \
+ { 0xA7A4, /* LATIN CAPITAL LETTER N WITH OBLIQUE STROKE */ \
+ 0xA7A5, /* LATIN SMALL LETTER N WITH OBLIQUE STROKE */ }, \
+ { 0x00D1, /* LATIN CAPITAL LETTER N WITH TILDE */ \
+ 0x00F1, /* LATIN SMALL LETTER N WITH TILDE */ }, \
+ { 0x01CA, /* LATIN CAPITAL LETTER NJ */ \
+ 0x01CC, /* LATIN SMALL LETTER NJ */ }, \
+ { 0x004F, /* LATIN CAPITAL LETTER O */ \
+ 0x006F, /* LATIN SMALL LETTER O */ }, \
+ { 0x00D3, /* LATIN CAPITAL LETTER O WITH ACUTE */ \
+ 0x00F3, /* LATIN SMALL LETTER O WITH ACUTE */ }, \
+ { 0x014E, /* LATIN CAPITAL LETTER O WITH BREVE */ \
+ 0x014F, /* LATIN SMALL LETTER O WITH BREVE */ }, \
+ { 0x01D1, /* LATIN CAPITAL LETTER O WITH CARON */ \
+ 0x01D2, /* LATIN SMALL LETTER O WITH CARON */ }, \
+ { 0x00D4, /* LATIN CAPITAL LETTER O WITH CIRCUMFLEX */ \
+ 0x00F4, /* LATIN SMALL LETTER O WITH CIRCUMFLEX */ }, \
+ { 0x1ED0, /* LATIN CAPITAL LETTER O WITH CIRCUMFLEX AND ACUTE */ \
+ 0x1ED1, /* LATIN SMALL LETTER O WITH CIRCUMFLEX AND ACUTE */ }, \
+ { 0x1ED8, /* LATIN CAPITAL LETTER O WITH CIRCUMFLEX AND DOT BELOW */ \
+ 0x1ED9, /* LATIN SMALL LETTER O WITH CIRCUMFLEX AND DOT BELOW */ }, \
+ { 0x1ED2, /* LATIN CAPITAL LETTER O WITH CIRCUMFLEX AND GRAVE */ \
+ 0x1ED3, /* LATIN SMALL LETTER O WITH CIRCUMFLEX AND GRAVE */ }, \
+ { 0x1ED4, /* LATIN CAPITAL LETTER O WITH CIRCUMFLEX AND HOOK ABOVE */ \
+ 0x1ED5, /* LATIN SMALL LETTER O WITH CIRCUMFLEX AND HOOK ABOVE */ }, \
+ { 0x1ED6, /* LATIN CAPITAL LETTER O WITH CIRCUMFLEX AND TILDE */ \
+ 0x1ED7, /* LATIN SMALL LETTER O WITH CIRCUMFLEX AND TILDE */ }, \
+ { 0x00D6, /* LATIN CAPITAL LETTER O WITH DIAERESIS */ \
+ 0x00F6, /* LATIN SMALL LETTER O WITH DIAERESIS */ }, \
+ { 0x022A, /* LATIN CAPITAL LETTER O WITH DIAERESIS AND MACRON */ \
+ 0x022B, /* LATIN SMALL LETTER O WITH DIAERESIS AND MACRON */ }, \
+ { 0x022E, /* LATIN CAPITAL LETTER O WITH DOT ABOVE */ \
+ 0x022F, /* LATIN SMALL LETTER O WITH DOT ABOVE */ }, \
+ { 0x0230, /* LATIN CAPITAL LETTER O WITH DOT ABOVE AND MACRON */ \
+ 0x0231, /* LATIN SMALL LETTER O WITH DOT ABOVE AND MACRON */ }, \
+ { 0x1ECC, /* LATIN CAPITAL LETTER O WITH DOT BELOW */ \
+ 0x1ECD, /* LATIN SMALL LETTER O WITH DOT BELOW */ }, \
+ { 0x0150, /* LATIN CAPITAL LETTER O WITH DOUBLE ACUTE */ \
+ 0x0151, /* LATIN SMALL LETTER O WITH DOUBLE ACUTE */ }, \
+ { 0x020C, /* LATIN CAPITAL LETTER O WITH DOUBLE GRAVE */ \
+ 0x020D, /* LATIN SMALL LETTER O WITH DOUBLE GRAVE */ }, \
+ { 0x00D2, /* LATIN CAPITAL LETTER O WITH GRAVE */ \
+ 0x00F2, /* LATIN SMALL LETTER O WITH GRAVE */ }, \
+ { 0x1ECE, /* LATIN CAPITAL LETTER O WITH HOOK ABOVE */ \
+ 0x1ECF, /* LATIN SMALL LETTER O WITH HOOK ABOVE */ }, \
+ { 0x01A0, /* LATIN CAPITAL LETTER O WITH HORN */ \
+ 0x01A1, /* LATIN SMALL LETTER O WITH HORN */ }, \
+ { 0x1EDA, /* LATIN CAPITAL LETTER O WITH HORN AND ACUTE */ \
+ 0x1EDB, /* LATIN SMALL LETTER O WITH HORN AND ACUTE */ }, \
+ { 0x1EE2, /* LATIN CAPITAL LETTER O WITH HORN AND DOT BELOW */ \
+ 0x1EE3, /* LATIN SMALL LETTER O WITH HORN AND DOT BELOW */ }, \
+ { 0x1EDC, /* LATIN CAPITAL LETTER O WITH HORN AND GRAVE */ \
+ 0x1EDD, /* LATIN SMALL LETTER O WITH HORN AND GRAVE */ }, \
+ { 0x1EDE, /* LATIN CAPITAL LETTER O WITH HORN AND HOOK ABOVE */ \
+ 0x1EDF, /* LATIN SMALL LETTER O WITH HORN AND HOOK ABOVE */ }, \
+ { 0x1EE0, /* LATIN CAPITAL LETTER O WITH HORN AND TILDE */ \
+ 0x1EE1, /* LATIN SMALL LETTER O WITH HORN AND TILDE */ }, \
+ { 0x020E, /* LATIN CAPITAL LETTER O WITH INVERTED BREVE */ \
+ 0x020F, /* LATIN SMALL LETTER O WITH INVERTED BREVE */ }, \
+ { 0xA74A, /* LATIN CAPITAL LETTER O WITH LONG STROKE OVERLAY */ \
+ 0xA74B, /* LATIN SMALL LETTER O WITH LONG STROKE OVERLAY */ }, \
+ { 0xA74C, /* LATIN CAPITAL LETTER O WITH LOOP */ \
+ 0xA74D, /* LATIN SMALL LETTER O WITH LOOP */ }, \
+ { 0x014C, /* LATIN CAPITAL LETTER O WITH MACRON */ \
+ 0x014D, /* LATIN SMALL LETTER O WITH MACRON */ }, \
+ { 0x1E52, /* LATIN CAPITAL LETTER O WITH MACRON AND ACUTE */ \
+ 0x1E53, /* LATIN SMALL LETTER O WITH MACRON AND ACUTE */ }, \
+ { 0x1E50, /* LATIN CAPITAL LETTER O WITH MACRON AND GRAVE */ \
+ 0x1E51, /* LATIN SMALL LETTER O WITH MACRON AND GRAVE */ }, \
+ { 0x01EA, /* LATIN CAPITAL LETTER O WITH OGONEK */ \
+ 0x01EB, /* LATIN SMALL LETTER O WITH OGONEK */ }, \
+ { 0x01EC, /* LATIN CAPITAL LETTER O WITH OGONEK AND MACRON */ \
+ 0x01ED, /* LATIN SMALL LETTER O WITH OGONEK AND MACRON */ }, \
+ { 0x00D8, /* LATIN CAPITAL LETTER O WITH STROKE */ \
+ 0x00F8, /* LATIN SMALL LETTER O WITH STROKE */ }, \
+ { 0x01FE, /* LATIN CAPITAL LETTER O WITH STROKE AND ACUTE */ \
+ 0x01FF, /* LATIN SMALL LETTER O WITH STROKE AND ACUTE */ }, \
+ { 0x00D5, /* LATIN CAPITAL LETTER O WITH TILDE */ \
+ 0x00F5, /* LATIN SMALL LETTER O WITH TILDE */ }, \
+ { 0x1E4C, /* LATIN CAPITAL LETTER O WITH TILDE AND ACUTE */ \
+ 0x1E4D, /* LATIN SMALL LETTER O WITH TILDE AND ACUTE */ }, \
+ { 0x1E4E, /* LATIN CAPITAL LETTER O WITH TILDE AND DIAERESIS */ \
+ 0x1E4F, /* LATIN SMALL LETTER O WITH TILDE AND DIAERESIS */ }, \
+ { 0x022C, /* LATIN CAPITAL LETTER O WITH TILDE AND MACRON */ \
+ 0x022D, /* LATIN SMALL LETTER O WITH TILDE AND MACRON */ }, \
+ { 0x01A2, /* LATIN CAPITAL LETTER OI */ \
+ 0x01A3, /* LATIN SMALL LETTER OI */ }, \
+ { 0xA7B6, /* LATIN CAPITAL LETTER OMEGA */ \
+ 0xA7B7, /* LATIN SMALL LETTER OMEGA */ }, \
+ { 0xA74E, /* LATIN CAPITAL LETTER OO */ \
+ 0xA74F, /* LATIN SMALL LETTER OO */ }, \
+ { 0x0190, /* LATIN CAPITAL LETTER OPEN E */ \
+ 0x025B, /* LATIN SMALL LETTER OPEN E */ }, \
+ { 0x0186, /* LATIN CAPITAL LETTER OPEN O */ \
+ 0x0254, /* LATIN SMALL LETTER OPEN O */ }, \
+ { 0x0222, /* LATIN CAPITAL LETTER OU */ \
+ 0x0223, /* LATIN SMALL LETTER OU */ }, \
+ { 0x0050, /* LATIN CAPITAL LETTER P */ \
+ 0x0070, /* LATIN SMALL LETTER P */ }, \
+ { 0x1E54, /* LATIN CAPITAL LETTER P WITH ACUTE */ \
+ 0x1E55, /* LATIN SMALL LETTER P WITH ACUTE */ }, \
+ { 0x1E56, /* LATIN CAPITAL LETTER P WITH DOT ABOVE */ \
+ 0x1E57, /* LATIN SMALL LETTER P WITH DOT ABOVE */ }, \
+ { 0xA752, /* LATIN CAPITAL LETTER P WITH FLOURISH */ \
+ 0xA753, /* LATIN SMALL LETTER P WITH FLOURISH */ }, \
+ { 0x01A4, /* LATIN CAPITAL LETTER P WITH HOOK */ \
+ 0x01A5, /* LATIN SMALL LETTER P WITH HOOK */ }, \
+ { 0xA754, /* LATIN CAPITAL LETTER P WITH SQUIRREL TAIL */ \
+ 0xA755, /* LATIN SMALL LETTER P WITH SQUIRREL TAIL */ }, \
+ { 0x2C63, /* LATIN CAPITAL LETTER P WITH STROKE */ \
+ 0x1D7D, /* LATIN SMALL LETTER P WITH STROKE */ }, \
+ { 0xA750, /* LATIN CAPITAL LETTER P WITH STROKE THROUGH DESCENDER */ \
+ 0xA751, /* LATIN SMALL LETTER P WITH STROKE THROUGH DESCENDER */ }, \
+ { 0x0051, /* LATIN CAPITAL LETTER Q */ \
+ 0x0071, /* LATIN SMALL LETTER Q */ }, \
+ { 0xA758, /* LATIN CAPITAL LETTER Q WITH DIAGONAL STROKE */ \
+ 0xA759, /* LATIN SMALL LETTER Q WITH DIAGONAL STROKE */ }, \
+ { 0xA756, /* LATIN CAPITAL LETTER Q WITH STROKE THROUGH DESCENDER */ \
+ 0xA757, /* LATIN SMALL LETTER Q WITH STROKE THROUGH DESCENDER */ }, \
+ { 0x0052, /* LATIN CAPITAL LETTER R */ \
+ 0x0072, /* LATIN SMALL LETTER R */ }, \
+ { 0xA75A, /* LATIN CAPITAL LETTER R ROTUNDA */ \
+ 0xA75B, /* LATIN SMALL LETTER R ROTUNDA */ }, \
+ { 0x0154, /* LATIN CAPITAL LETTER R WITH ACUTE */ \
+ 0x0155, /* LATIN SMALL LETTER R WITH ACUTE */ }, \
+ { 0x0158, /* LATIN CAPITAL LETTER R WITH CARON */ \
+ 0x0159, /* LATIN SMALL LETTER R WITH CARON */ }, \
+ { 0x0156, /* LATIN CAPITAL LETTER R WITH CEDILLA */ \
+ 0x0157, /* LATIN SMALL LETTER R WITH CEDILLA */ }, \
+ { 0x1E58, /* LATIN CAPITAL LETTER R WITH DOT ABOVE */ \
+ 0x1E59, /* LATIN SMALL LETTER R WITH DOT ABOVE */ }, \
+ { 0x1E5A, /* LATIN CAPITAL LETTER R WITH DOT BELOW */ \
+ 0x1E5B, /* LATIN SMALL LETTER R WITH DOT BELOW */ }, \
+ { 0x1E5C, /* LATIN CAPITAL LETTER R WITH DOT BELOW AND MACRON */ \
+ 0x1E5D, /* LATIN SMALL LETTER R WITH DOT BELOW AND MACRON */ }, \
+ { 0x0210, /* LATIN CAPITAL LETTER R WITH DOUBLE GRAVE */ \
+ 0x0211, /* LATIN SMALL LETTER R WITH DOUBLE GRAVE */ }, \
+ { 0x0212, /* LATIN CAPITAL LETTER R WITH INVERTED BREVE */ \
+ 0x0213, /* LATIN SMALL LETTER R WITH INVERTED BREVE */ }, \
+ { 0x1E5E, /* LATIN CAPITAL LETTER R WITH LINE BELOW */ \
+ 0x1E5F, /* LATIN SMALL LETTER R WITH LINE BELOW */ }, \
+ { 0xA7A6, /* LATIN CAPITAL LETTER R WITH OBLIQUE STROKE */ \
+ 0xA7A7, /* LATIN SMALL LETTER R WITH OBLIQUE STROKE */ }, \
+ { 0x024C, /* LATIN CAPITAL LETTER R WITH STROKE */ \
+ 0x024D, /* LATIN SMALL LETTER R WITH STROKE */ }, \
+ { 0x2C64, /* LATIN CAPITAL LETTER R WITH TAIL */ \
+ 0x027D, /* LATIN SMALL LETTER R WITH TAIL */ }, \
+ { 0xA73E, /* LATIN CAPITAL LETTER REVERSED C WITH DOT */ \
+ 0xA73F, /* LATIN SMALL LETTER REVERSED C WITH DOT */ }, \
+ { 0x018E, /* LATIN CAPITAL LETTER REVERSED E */ \
+ 0x0258, /* LATIN SMALL LETTER REVERSED E */ }, \
+ { 0xA7AB, /* LATIN CAPITAL LETTER REVERSED OPEN E */ \
+ 0x025C, /* LATIN SMALL LETTER REVERSED OPEN E */ }, \
+ { 0xA75C, /* LATIN CAPITAL LETTER RUM ROTUNDA */ \
+ 0xA75D, /* LATIN SMALL LETTER RUM ROTUNDA */ }, \
+ { 0x0053, /* LATIN CAPITAL LETTER S */ \
+ 0x0073, /* LATIN SMALL LETTER S */ }, \
+ { 0x015A, /* LATIN CAPITAL LETTER S WITH ACUTE */ \
+ 0x015B, /* LATIN SMALL LETTER S WITH ACUTE */ }, \
+ { 0x1E64, /* LATIN CAPITAL LETTER S WITH ACUTE AND DOT ABOVE */ \
+ 0x1E65, /* LATIN SMALL LETTER S WITH ACUTE AND DOT ABOVE */ }, \
+ { 0x0160, /* LATIN CAPITAL LETTER S WITH CARON */ \
+ 0x0161, /* LATIN SMALL LETTER S WITH CARON */ }, \
+ { 0x1E66, /* LATIN CAPITAL LETTER S WITH CARON AND DOT ABOVE */ \
+ 0x1E67, /* LATIN SMALL LETTER S WITH CARON AND DOT ABOVE */ }, \
+ { 0x015E, /* LATIN CAPITAL LETTER S WITH CEDILLA */ \
+ 0x015F, /* LATIN SMALL LETTER S WITH CEDILLA */ }, \
+ { 0x015C, /* LATIN CAPITAL LETTER S WITH CIRCUMFLEX */ \
+ 0x015D, /* LATIN SMALL LETTER S WITH CIRCUMFLEX */ }, \
+ { 0x0218, /* LATIN CAPITAL LETTER S WITH COMMA BELOW */ \
+ 0x0219, /* LATIN SMALL LETTER S WITH COMMA BELOW */ }, \
+ { 0x1E60, /* LATIN CAPITAL LETTER S WITH DOT ABOVE */ \
+ 0x1E61, /* LATIN SMALL LETTER S WITH DOT ABOVE */ }, \
+ { 0x1E62, /* LATIN CAPITAL LETTER S WITH DOT BELOW */ \
+ 0x1E63, /* LATIN SMALL LETTER S WITH DOT BELOW */ }, \
+ { 0x1E68, /* LATIN CAPITAL LETTER S WITH DOT BELOW AND DOT ABOVE */ \
+ 0x1E69, /* LATIN SMALL LETTER S WITH DOT BELOW AND DOT ABOVE */ }, \
+ { 0xA7A8, /* LATIN CAPITAL LETTER S WITH OBLIQUE STROKE */ \
+ 0xA7A9, /* LATIN SMALL LETTER S WITH OBLIQUE STROKE */ }, \
+ { 0x2C7E, /* LATIN CAPITAL LETTER S WITH SWASH TAIL */ \
+ 0x023F, /* LATIN SMALL LETTER S WITH SWASH TAIL */ }, \
+ { 0xA78B, /* LATIN CAPITAL LETTER SALTILLO */ \
+ 0xA78C, /* LATIN SMALL LETTER SALTILLO */ }, \
+ { 0x018F, /* LATIN CAPITAL LETTER SCHWA */ \
+ 0x0259, /* LATIN SMALL LETTER SCHWA */ }, \
+ { 0xA7AC, /* LATIN CAPITAL LETTER SCRIPT G */ \
+ 0x0261, /* LATIN SMALL LETTER SCRIPT G */ }, \
+ { 0x1E9E, /* LATIN CAPITAL LETTER SHARP S */ \
+ 0x00DF, /* LATIN SMALL LETTER SHARP S */ }, \
+ { 0x0054, /* LATIN CAPITAL LETTER T */ \
+ 0x0074, /* LATIN SMALL LETTER T */ }, \
+ { 0x0164, /* LATIN CAPITAL LETTER T WITH CARON */ \
+ 0x0165, /* LATIN SMALL LETTER T WITH CARON */ }, \
+ { 0x0162, /* LATIN CAPITAL LETTER T WITH CEDILLA */ \
+ 0x0163, /* LATIN SMALL LETTER T WITH CEDILLA */ }, \
+ { 0x1E70, /* LATIN CAPITAL LETTER T WITH CIRCUMFLEX BELOW */ \
+ 0x1E71, /* LATIN SMALL LETTER T WITH CIRCUMFLEX BELOW */ }, \
+ { 0x021A, /* LATIN CAPITAL LETTER T WITH COMMA BELOW */ \
+ 0x021B, /* LATIN SMALL LETTER T WITH COMMA BELOW */ }, \
+ { 0x023E, /* LATIN CAPITAL LETTER T WITH DIAGONAL STROKE */ \
+ 0x2C66, /* LATIN SMALL LETTER T WITH DIAGONAL STROKE */ }, \
+ { 0x1E6A, /* LATIN CAPITAL LETTER T WITH DOT ABOVE */ \
+ 0x1E6B, /* LATIN SMALL LETTER T WITH DOT ABOVE */ }, \
+ { 0x1E6C, /* LATIN CAPITAL LETTER T WITH DOT BELOW */ \
+ 0x1E6D, /* LATIN SMALL LETTER T WITH DOT BELOW */ }, \
+ { 0x01AC, /* LATIN CAPITAL LETTER T WITH HOOK */ \
+ 0x01AD, /* LATIN SMALL LETTER T WITH HOOK */ }, \
+ { 0x1E6E, /* LATIN CAPITAL LETTER T WITH LINE BELOW */ \
+ 0x1E6F, /* LATIN SMALL LETTER T WITH LINE BELOW */ }, \
+ { 0x01AE, /* LATIN CAPITAL LETTER T WITH RETROFLEX HOOK */ \
+ 0x0288, /* LATIN SMALL LETTER T WITH RETROFLEX HOOK */ }, \
+ { 0x0166, /* LATIN CAPITAL LETTER T WITH STROKE */ \
+ 0x0167, /* LATIN SMALL LETTER T WITH STROKE */ }, \
+ { 0x00DE, /* LATIN CAPITAL LETTER THORN */ \
+ 0x00FE, /* LATIN SMALL LETTER THORN */ }, \
+ { 0xA764, /* LATIN CAPITAL LETTER THORN WITH STROKE */ \
+ 0xA765, /* LATIN SMALL LETTER THORN WITH STROKE */ }, \
+ { 0xA766, /* LATIN CAPITAL LETTER THORN WITH STROKE THROUGH DESCENDER */ \
+ 0xA767, /* LATIN SMALL LETTER THORN WITH STROKE THROUGH DESCENDER */ }, \
+ { 0x01BC, /* LATIN CAPITAL LETTER TONE FIVE */ \
+ 0x01BD, /* LATIN SMALL LETTER TONE FIVE */ }, \
+ { 0x0184, /* LATIN CAPITAL LETTER TONE SIX */ \
+ 0x0185, /* LATIN SMALL LETTER TONE SIX */ }, \
+ { 0x01A7, /* LATIN CAPITAL LETTER TONE TWO */ \
+ 0x01A8, /* LATIN SMALL LETTER TONE TWO */ }, \
+ { 0xA72A, /* LATIN CAPITAL LETTER TRESILLO */ \
+ 0xA72B, /* LATIN SMALL LETTER TRESILLO */ }, \
+ { 0x2C6F, /* LATIN CAPITAL LETTER TURNED A */ \
+ 0x0250, /* LATIN SMALL LETTER TURNED A */ }, \
+ { 0x2C70, /* LATIN CAPITAL LETTER TURNED ALPHA */ \
+ 0x0252, /* LATIN SMALL LETTER TURNED ALPHA */ }, \
+ { 0xA78D, /* LATIN CAPITAL LETTER TURNED H */ \
+ 0x0265, /* LATIN SMALL LETTER TURNED H */ }, \
+ { 0xA77E, /* LATIN CAPITAL LETTER TURNED INSULAR G */ \
+ 0xA77F, /* LATIN SMALL LETTER TURNED INSULAR G */ }, \
+ { 0xA7B0, /* LATIN CAPITAL LETTER TURNED K */ \
+ 0x029E, /* LATIN SMALL LETTER TURNED K */ }, \
+ { 0xA780, /* LATIN CAPITAL LETTER TURNED L */ \
+ 0xA781, /* LATIN SMALL LETTER TURNED L */ }, \
+ { 0x019C, /* LATIN CAPITAL LETTER TURNED M */ \
+ 0x026F, /* LATIN SMALL LETTER TURNED M */ }, \
+ { 0xA7B1, /* LATIN CAPITAL LETTER TURNED T */ \
+ 0x0287, /* LATIN SMALL LETTER TURNED T */ }, \
+ { 0x0245, /* LATIN CAPITAL LETTER TURNED V */ \
+ 0x028C, /* LATIN SMALL LETTER TURNED V */ }, \
+ { 0xA728, /* LATIN CAPITAL LETTER TZ */ \
+ 0xA729, /* LATIN SMALL LETTER TZ */ }, \
+ { 0x0055, /* LATIN CAPITAL LETTER U */ \
+ 0x0075, /* LATIN SMALL LETTER U */ }, \
+ { 0x0244, /* LATIN CAPITAL LETTER U BAR */ \
+ 0x0289, /* LATIN SMALL LETTER U BAR */ }, \
+ { 0x00DA, /* LATIN CAPITAL LETTER U WITH ACUTE */ \
+ 0x00FA, /* LATIN SMALL LETTER U WITH ACUTE */ }, \
+ { 0x016C, /* LATIN CAPITAL LETTER U WITH BREVE */ \
+ 0x016D, /* LATIN SMALL LETTER U WITH BREVE */ }, \
+ { 0x01D3, /* LATIN CAPITAL LETTER U WITH CARON */ \
+ 0x01D4, /* LATIN SMALL LETTER U WITH CARON */ }, \
+ { 0x00DB, /* LATIN CAPITAL LETTER U WITH CIRCUMFLEX */ \
+ 0x00FB, /* LATIN SMALL LETTER U WITH CIRCUMFLEX */ }, \
+ { 0x1E76, /* LATIN CAPITAL LETTER U WITH CIRCUMFLEX BELOW */ \
+ 0x1E77, /* LATIN SMALL LETTER U WITH CIRCUMFLEX BELOW */ }, \
+ { 0x00DC, /* LATIN CAPITAL LETTER U WITH DIAERESIS */ \
+ 0x00FC, /* LATIN SMALL LETTER U WITH DIAERESIS */ }, \
+ { 0x01D7, /* LATIN CAPITAL LETTER U WITH DIAERESIS AND ACUTE */ \
+ 0x01D8, /* LATIN SMALL LETTER U WITH DIAERESIS AND ACUTE */ }, \
+ { 0x01D9, /* LATIN CAPITAL LETTER U WITH DIAERESIS AND CARON */ \
+ 0x01DA, /* LATIN SMALL LETTER U WITH DIAERESIS AND CARON */ }, \
+ { 0x01DB, /* LATIN CAPITAL LETTER U WITH DIAERESIS AND GRAVE */ \
+ 0x01DC, /* LATIN SMALL LETTER U WITH DIAERESIS AND GRAVE */ }, \
+ { 0x01D5, /* LATIN CAPITAL LETTER U WITH DIAERESIS AND MACRON */ \
+ 0x01D6, /* LATIN SMALL LETTER U WITH DIAERESIS AND MACRON */ }, \
+ { 0x1E72, /* LATIN CAPITAL LETTER U WITH DIAERESIS BELOW */ \
+ 0x1E73, /* LATIN SMALL LETTER U WITH DIAERESIS BELOW */ }, \
+ { 0x1EE4, /* LATIN CAPITAL LETTER U WITH DOT BELOW */ \
+ 0x1EE5, /* LATIN SMALL LETTER U WITH DOT BELOW */ }, \
+ { 0x0170, /* LATIN CAPITAL LETTER U WITH DOUBLE ACUTE */ \
+ 0x0171, /* LATIN SMALL LETTER U WITH DOUBLE ACUTE */ }, \
+ { 0x0214, /* LATIN CAPITAL LETTER U WITH DOUBLE GRAVE */ \
+ 0x0215, /* LATIN SMALL LETTER U WITH DOUBLE GRAVE */ }, \
+ { 0x00D9, /* LATIN CAPITAL LETTER U WITH GRAVE */ \
+ 0x00F9, /* LATIN SMALL LETTER U WITH GRAVE */ }, \
+ { 0x1EE6, /* LATIN CAPITAL LETTER U WITH HOOK ABOVE */ \
+ 0x1EE7, /* LATIN SMALL LETTER U WITH HOOK ABOVE */ }, \
+ { 0x01AF, /* LATIN CAPITAL LETTER U WITH HORN */ \
+ 0x01B0, /* LATIN SMALL LETTER U WITH HORN */ }, \
+ { 0x1EE8, /* LATIN CAPITAL LETTER U WITH HORN AND ACUTE */ \
+ 0x1EE9, /* LATIN SMALL LETTER U WITH HORN AND ACUTE */ }, \
+ { 0x1EF0, /* LATIN CAPITAL LETTER U WITH HORN AND DOT BELOW */ \
+ 0x1EF1, /* LATIN SMALL LETTER U WITH HORN AND DOT BELOW */ }, \
+ { 0x1EEA, /* LATIN CAPITAL LETTER U WITH HORN AND GRAVE */ \
+ 0x1EEB, /* LATIN SMALL LETTER U WITH HORN AND GRAVE */ }, \
+ { 0x1EEC, /* LATIN CAPITAL LETTER U WITH HORN AND HOOK ABOVE */ \
+ 0x1EED, /* LATIN SMALL LETTER U WITH HORN AND HOOK ABOVE */ }, \
+ { 0x1EEE, /* LATIN CAPITAL LETTER U WITH HORN AND TILDE */ \
+ 0x1EEF, /* LATIN SMALL LETTER U WITH HORN AND TILDE */ }, \
+ { 0x0216, /* LATIN CAPITAL LETTER U WITH INVERTED BREVE */ \
+ 0x0217, /* LATIN SMALL LETTER U WITH INVERTED BREVE */ }, \
+ { 0x016A, /* LATIN CAPITAL LETTER U WITH MACRON */ \
+ 0x016B, /* LATIN SMALL LETTER U WITH MACRON */ }, \
+ { 0x1E7A, /* LATIN CAPITAL LETTER U WITH MACRON AND DIAERESIS */ \
+ 0x1E7B, /* LATIN SMALL LETTER U WITH MACRON AND DIAERESIS */ }, \
+ { 0x0172, /* LATIN CAPITAL LETTER U WITH OGONEK */ \
+ 0x0173, /* LATIN SMALL LETTER U WITH OGONEK */ }, \
+ { 0x016E, /* LATIN CAPITAL LETTER U WITH RING ABOVE */ \
+ 0x016F, /* LATIN SMALL LETTER U WITH RING ABOVE */ }, \
+ { 0xA7B8, /* LATIN CAPITAL LETTER U WITH STROKE */ \
+ 0xA7B9, /* LATIN SMALL LETTER U WITH STROKE */ }, \
+ { 0x0168, /* LATIN CAPITAL LETTER U WITH TILDE */ \
+ 0x0169, /* LATIN SMALL LETTER U WITH TILDE */ }, \
+ { 0x1E78, /* LATIN CAPITAL LETTER U WITH TILDE AND ACUTE */ \
+ 0x1E79, /* LATIN SMALL LETTER U WITH TILDE AND ACUTE */ }, \
+ { 0x1E74, /* LATIN CAPITAL LETTER U WITH TILDE BELOW */ \
+ 0x1E75, /* LATIN SMALL LETTER U WITH TILDE BELOW */ }, \
+ { 0x01B1, /* LATIN CAPITAL LETTER UPSILON */ \
+ 0x028A, /* LATIN SMALL LETTER UPSILON */ }, \
+ { 0x0056, /* LATIN CAPITAL LETTER V */ \
+ 0x0076, /* LATIN SMALL LETTER V */ }, \
+ { 0xA75E, /* LATIN CAPITAL LETTER V WITH DIAGONAL STROKE */ \
+ 0xA75F, /* LATIN SMALL LETTER V WITH DIAGONAL STROKE */ }, \
+ { 0x1E7E, /* LATIN CAPITAL LETTER V WITH DOT BELOW */ \
+ 0x1E7F, /* LATIN SMALL LETTER V WITH DOT BELOW */ }, \
+ { 0x01B2, /* LATIN CAPITAL LETTER V WITH HOOK */ \
+ 0x028B, /* LATIN SMALL LETTER V WITH HOOK */ }, \
+ { 0x1E7C, /* LATIN CAPITAL LETTER V WITH TILDE */ \
+ 0x1E7D, /* LATIN SMALL LETTER V WITH TILDE */ }, \
+ { 0xA768, /* LATIN CAPITAL LETTER VEND */ \
+ 0xA769, /* LATIN SMALL LETTER VEND */ }, \
+ { 0xA762, /* LATIN CAPITAL LETTER VISIGOTHIC Z */ \
+ 0xA763, /* LATIN SMALL LETTER VISIGOTHIC Z */ }, \
+ { 0xA79A, /* LATIN CAPITAL LETTER VOLAPUK AE */ \
+ 0xA79B, /* LATIN SMALL LETTER VOLAPUK AE */ }, \
+ { 0xA79C, /* LATIN CAPITAL LETTER VOLAPUK OE */ \
+ 0xA79D, /* LATIN SMALL LETTER VOLAPUK OE */ }, \
+ { 0xA79E, /* LATIN CAPITAL LETTER VOLAPUK UE */ \
+ 0xA79F, /* LATIN SMALL LETTER VOLAPUK UE */ }, \
+ { 0xA760, /* LATIN CAPITAL LETTER VY */ \
+ 0xA761, /* LATIN SMALL LETTER VY */ }, \
+ { 0x0057, /* LATIN CAPITAL LETTER W */ \
+ 0x0077, /* LATIN SMALL LETTER W */ }, \
+ { 0x1E82, /* LATIN CAPITAL LETTER W WITH ACUTE */ \
+ 0x1E83, /* LATIN SMALL LETTER W WITH ACUTE */ }, \
+ { 0x0174, /* LATIN CAPITAL LETTER W WITH CIRCUMFLEX */ \
+ 0x0175, /* LATIN SMALL LETTER W WITH CIRCUMFLEX */ }, \
+ { 0x1E84, /* LATIN CAPITAL LETTER W WITH DIAERESIS */ \
+ 0x1E85, /* LATIN SMALL LETTER W WITH DIAERESIS */ }, \
+ { 0x1E86, /* LATIN CAPITAL LETTER W WITH DOT ABOVE */ \
+ 0x1E87, /* LATIN SMALL LETTER W WITH DOT ABOVE */ }, \
+ { 0x1E88, /* LATIN CAPITAL LETTER W WITH DOT BELOW */ \
+ 0x1E89, /* LATIN SMALL LETTER W WITH DOT BELOW */ }, \
+ { 0x1E80, /* LATIN CAPITAL LETTER W WITH GRAVE */ \
+ 0x1E81, /* LATIN SMALL LETTER W WITH GRAVE */ }, \
+ { 0x2C72, /* LATIN CAPITAL LETTER W WITH HOOK */ \
+ 0x2C73, /* LATIN SMALL LETTER W WITH HOOK */ }, \
+ { 0x0058, /* LATIN CAPITAL LETTER X */ \
+ 0x0078, /* LATIN SMALL LETTER X */ }, \
+ { 0x1E8C, /* LATIN CAPITAL LETTER X WITH DIAERESIS */ \
+ 0x1E8D, /* LATIN SMALL LETTER X WITH DIAERESIS */ }, \
+ { 0x1E8A, /* LATIN CAPITAL LETTER X WITH DOT ABOVE */ \
+ 0x1E8B, /* LATIN SMALL LETTER X WITH DOT ABOVE */ }, \
+ { 0x0059, /* LATIN CAPITAL LETTER Y */ \
+ 0x0079, /* LATIN SMALL LETTER Y */ }, \
+ { 0x00DD, /* LATIN CAPITAL LETTER Y WITH ACUTE */ \
+ 0x00FD, /* LATIN SMALL LETTER Y WITH ACUTE */ }, \
+ { 0x0176, /* LATIN CAPITAL LETTER Y WITH CIRCUMFLEX */ \
+ 0x0177, /* LATIN SMALL LETTER Y WITH CIRCUMFLEX */ }, \
+ { 0x0178, /* LATIN CAPITAL LETTER Y WITH DIAERESIS */ \
+ 0x00FF, /* LATIN SMALL LETTER Y WITH DIAERESIS */ }, \
+ { 0x1E8E, /* LATIN CAPITAL LETTER Y WITH DOT ABOVE */ \
+ 0x1E8F, /* LATIN SMALL LETTER Y WITH DOT ABOVE */ }, \
+ { 0x1EF4, /* LATIN CAPITAL LETTER Y WITH DOT BELOW */ \
+ 0x1EF5, /* LATIN SMALL LETTER Y WITH DOT BELOW */ }, \
+ { 0x1EF2, /* LATIN CAPITAL LETTER Y WITH GRAVE */ \
+ 0x1EF3, /* LATIN SMALL LETTER Y WITH GRAVE */ }, \
+ { 0x01B3, /* LATIN CAPITAL LETTER Y WITH HOOK */ \
+ 0x01B4, /* LATIN SMALL LETTER Y WITH HOOK */ }, \
+ { 0x1EF6, /* LATIN CAPITAL LETTER Y WITH HOOK ABOVE */ \
+ 0x1EF7, /* LATIN SMALL LETTER Y WITH HOOK ABOVE */ }, \
+ { 0x1EFE, /* LATIN CAPITAL LETTER Y WITH LOOP */ \
+ 0x1EFF, /* LATIN SMALL LETTER Y WITH LOOP */ }, \
+ { 0x0232, /* LATIN CAPITAL LETTER Y WITH MACRON */ \
+ 0x0233, /* LATIN SMALL LETTER Y WITH MACRON */ }, \
+ { 0x024E, /* LATIN CAPITAL LETTER Y WITH STROKE */ \
+ 0x024F, /* LATIN SMALL LETTER Y WITH STROKE */ }, \
+ { 0x1EF8, /* LATIN CAPITAL LETTER Y WITH TILDE */ \
+ 0x1EF9, /* LATIN SMALL LETTER Y WITH TILDE */ }, \
+ { 0x021C, /* LATIN CAPITAL LETTER YOGH */ \
+ 0x021D, /* LATIN SMALL LETTER YOGH */ }, \
+ { 0x005A, /* LATIN CAPITAL LETTER Z */ \
+ 0x007A, /* LATIN SMALL LETTER Z */ }, \
+ { 0x0179, /* LATIN CAPITAL LETTER Z WITH ACUTE */ \
+ 0x017A, /* LATIN SMALL LETTER Z WITH ACUTE */ }, \
+ { 0x017D, /* LATIN CAPITAL LETTER Z WITH CARON */ \
+ 0x017E, /* LATIN SMALL LETTER Z WITH CARON */ }, \
+ { 0x1E90, /* LATIN CAPITAL LETTER Z WITH CIRCUMFLEX */ \
+ 0x1E91, /* LATIN SMALL LETTER Z WITH CIRCUMFLEX */ }, \
+ { 0x2C6B, /* LATIN CAPITAL LETTER Z WITH DESCENDER */ \
+ 0x2C6C, /* LATIN SMALL LETTER Z WITH DESCENDER */ }, \
+ { 0x017B, /* LATIN CAPITAL LETTER Z WITH DOT ABOVE */ \
+ 0x017C, /* LATIN SMALL LETTER Z WITH DOT ABOVE */ }, \
+ { 0x1E92, /* LATIN CAPITAL LETTER Z WITH DOT BELOW */ \
+ 0x1E93, /* LATIN SMALL LETTER Z WITH DOT BELOW */ }, \
+ { 0x0224, /* LATIN CAPITAL LETTER Z WITH HOOK */ \
+ 0x0225, /* LATIN SMALL LETTER Z WITH HOOK */ }, \
+ { 0x1E94, /* LATIN CAPITAL LETTER Z WITH LINE BELOW */ \
+ 0x1E95, /* LATIN SMALL LETTER Z WITH LINE BELOW */ }, \
+ { 0x01B5, /* LATIN CAPITAL LETTER Z WITH STROKE */ \
+ 0x01B6, /* LATIN SMALL LETTER Z WITH STROKE */ }, \
+ { 0x2C7F, /* LATIN CAPITAL LETTER Z WITH SWASH TAIL */ \
+ 0x0240, /* LATIN SMALL LETTER Z WITH SWASH TAIL */ }, \
+ { 0x0000, /* END OF LIST CAPITAL LETTERS */ \
+ 0x0000, /* END OF LIST SMALL LETTERS */ }, \
+}
+
+/*
+ * Correspondence table for small and capital letters of codepage 437.
+ * Letters A-Z are handled in code.
+ */
+#define CP437_CAPITALIZATION_TABLE { \
+ { 0x00C4, /* LATIN CAPITAL LETTER A WITH DIAERESIS */ \
+ 0x00E4, /* LATIN SMALL LETTER A WITH DIAERESIS */ }, \
+ { 0x00C5, /* LATIN CAPITAL LETTER A WITH RING ABOVE */ \
+ 0x00E5, /* LATIN SMALL LETTER A WITH RING ABOVE */ }, \
+ { 0x00C6, /* LATIN CAPITAL LETTER AE */ \
+ 0x00E6, /* LATIN SMALL LETTER AE */ }, \
+ { 0x00C7, /* LATIN CAPITAL LETTER C WITH CEDILLA */ \
+ 0x00E7, /* LATIN SMALL LETTER C WITH CEDILLA */ }, \
+ { 0x00C9, /* LATIN CAPITAL LETTER E WITH ACUTE */ \
+ 0x00E9, /* LATIN SMALL LETTER E WITH ACUTE */ }, \
+ { 0x00D1, /* LATIN CAPITAL LETTER N WITH TILDE */ \
+ 0x00F1, /* LATIN SMALL LETTER N WITH TILDE */ }, \
+ { 0x00D6, /* LATIN CAPITAL LETTER O WITH DIAERESIS */ \
+ 0x00F6, /* LATIN SMALL LETTER O WITH DIAERESIS */ }, \
+ { 0x00DC, /* LATIN CAPITAL LETTER U WITH DIAERESIS */ \
+ 0x00FC, /* LATIN SMALL LETTER U WITH DIAERESIS */ }, \
+ { 0x03A3, /* GREEK CAPITAL LETTER SIGMA */ \
+ 0x03C3, /* GREEK SMALL LETTER SIGMA */ }, \
+ { 0x03A6, /* GREEK CAPITAL LETTER PHI */ \
+ 0x03C6, /* GREEK SMALL LETTER PHI */ }, \
+ { 0x0000, 0x0000, }, \
+}
+
+/*
+ * Correspondence table for small and capital letters of codepage 1250.
+ * Letters A-Z are handled in code.
+ */
+#define CP1250_CAPITALIZATION_TABLE { \
+ { 0x00C1, /* LATIN CAPITAL LETTER A WITH ACUTE */ \
+ 0x00E1, /* LATIN SMALL LETTER A WITH ACUTE */ }, \
+ { 0x00C2, /* LATIN CAPITAL LETTER A WITH CIRCUMFLEX */ \
+ 0x00E2, /* LATIN SMALL LETTER A WITH CIRCUMFLEX */ }, \
+ { 0x00C4, /* LATIN CAPITAL LETTER A WITH DIAERESIS */ \
+ 0x00E4, /* LATIN SMALL LETTER A WITH DIAERESIS */ }, \
+ { 0x00C7, /* LATIN CAPITAL LETTER C WITH CEDILLA */ \
+ 0x00E7, /* LATIN SMALL LETTER C WITH CEDILLA */ }, \
+ { 0x00C9, /* LATIN CAPITAL LETTER E WITH ACUTE */ \
+ 0x00E9, /* LATIN SMALL LETTER E WITH ACUTE */ }, \
+ { 0x00CB, /* LATIN CAPITAL LETTER E WITH DIAERESIS */ \
+ 0x00EB, /* LATIN SMALL LETTER E WITH DIAERESIS */ }, \
+ { 0x00CD, /* LATIN CAPITAL LETTER I WITH ACUTE */ \
+ 0x00ED, /* LATIN SMALL LETTER I WITH ACUTE */ }, \
+ { 0x00CE, /* LATIN CAPITAL LETTER I WITH CIRCUMFLEX */ \
+ 0x00EE, /* LATIN SMALL LETTER I WITH CIRCUMFLEX */ }, \
+ { 0x00D3, /* LATIN CAPITAL LETTER O WITH ACUTE */ \
+ 0x00F3, /* LATIN SMALL LETTER O WITH ACUTE */ }, \
+ { 0x00D4, /* LATIN CAPITAL LETTER O WITH CIRCUMFLEX */ \
+ 0x00F4, /* LATIN SMALL LETTER O WITH CIRCUMFLEX */ }, \
+ { 0x00D6, /* LATIN CAPITAL LETTER O WITH DIAERESIS */ \
+ 0x00F6, /* LATIN SMALL LETTER O WITH DIAERESIS */ }, \
+ { 0x00DA, /* LATIN CAPITAL LETTER U WITH ACUTE */ \
+ 0x00FA, /* LATIN SMALL LETTER U WITH ACUTE */ }, \
+ { 0x00DC, /* LATIN CAPITAL LETTER U WITH DIAERESIS */ \
+ 0x00FC, /* LATIN SMALL LETTER U WITH DIAERESIS */ }, \
+ { 0x00DD, /* LATIN CAPITAL LETTER Y WITH ACUTE */ \
+ 0x00FD, /* LATIN SMALL LETTER Y WITH ACUTE */ }, \
+ { 0x0102, /* LATIN CAPITAL LETTER A WITH BREVE */ \
+ 0x0103, /* LATIN SMALL LETTER A WITH BREVE */ }, \
+ { 0x0104, /* LATIN CAPITAL LETTER A WITH OGONEK */ \
+ 0x0105, /* LATIN SMALL LETTER A WITH OGONEK */ }, \
+ { 0x0106, /* LATIN CAPITAL LETTER C WITH ACUTE */ \
+ 0x0107, /* LATIN SMALL LETTER C WITH ACUTE */ }, \
+ { 0x010C, /* LATIN CAPITAL LETTER C WITH CARON */ \
+ 0x010D, /* LATIN SMALL LETTER C WITH CARON */ }, \
+ { 0x010E, /* LATIN CAPITAL LETTER D WITH CARON */ \
+ 0x010F, /* LATIN SMALL LETTER D WITH CARON */ }, \
+ { 0x0110, /* LATIN CAPITAL LETTER D WITH STROKE */ \
+ 0x0111, /* LATIN SMALL LETTER D WITH STROKE */ }, \
+ { 0x0118, /* LATIN CAPITAL LETTER E WITH OGONEK */ \
+ 0x0119, /* LATIN SMALL LETTER E WITH OGONEK */ }, \
+ { 0x011A, /* LATIN CAPITAL LETTER E WITH CARON */ \
+ 0x011B, /* LATIN SMALL LETTER E WITH CARON */ }, \
+ { 0x0139, /* LATIN CAPITAL LETTER L WITH ACUTE */ \
+ 0x013A, /* LATIN SMALL LETTER L WITH ACUTE */ }, \
+ { 0x013D, /* LATIN CAPITAL LETTER L WITH CARON */ \
+ 0x013E, /* LATIN SMALL LETTER L WITH CARON */ }, \
+ { 0x0141, /* LATIN CAPITAL LETTER L WITH STROKE */ \
+ 0x0142, /* LATIN SMALL LETTER L WITH STROKE */ }, \
+ { 0x0143, /* LATIN CAPITAL LETTER N WITH ACUTE */ \
+ 0x0144, /* LATIN SMALL LETTER N WITH ACUTE */ }, \
+ { 0x0147, /* LATIN CAPITAL LETTER N WITH CARON */ \
+ 0x0148, /* LATIN SMALL LETTER N WITH CARON */ }, \
+ { 0x0150, /* LATIN CAPITAL LETTER O WITH DOUBLE ACUTE */ \
+ 0x0151, /* LATIN SMALL LETTER O WITH DOUBLE ACUTE */ }, \
+ { 0x0154, /* LATIN CAPITAL LETTER R WITH ACUTE */ \
+ 0x0155, /* LATIN SMALL LETTER R WITH ACUTE */ }, \
+ { 0x0158, /* LATIN CAPITAL LETTER R WITH CARON */ \
+ 0x0159, /* LATIN SMALL LETTER R WITH CARON */ }, \
+ { 0x015A, /* LATIN CAPITAL LETTER S WITH ACUTE */ \
+ 0x015B, /* LATIN SMALL LETTER S WITH ACUTE */ }, \
+ { 0x015E, /* LATIN CAPITAL LETTER S WITH CEDILLA */ \
+ 0x015F, /* LATIN SMALL LETTER S WITH CEDILLA */ }, \
+ { 0x0160, /* LATIN CAPITAL LETTER S WITH CARON */ \
+ 0x0161, /* LATIN SMALL LETTER S WITH CARON */ }, \
+ { 0x0162, /* LATIN CAPITAL LETTER T WITH CEDILLA */ \
+ 0x0163, /* LATIN SMALL LETTER T WITH CEDILLA */ }, \
+ { 0x0164, /* LATIN CAPITAL LETTER T WITH CARON */ \
+ 0x0165, /* LATIN SMALL LETTER T WITH CARON */ }, \
+ { 0x016E, /* LATIN CAPITAL LETTER U WITH RING ABOVE */ \
+ 0x016F, /* LATIN SMALL LETTER U WITH RING ABOVE */ }, \
+ { 0x0170, /* LATIN CAPITAL LETTER U WITH DOUBLE ACUTE */ \
+ 0x0171, /* LATIN SMALL LETTER U WITH DOUBLE ACUTE */ }, \
+ { 0x0179, /* LATIN CAPITAL LETTER Z WITH ACUTE */ \
+ 0x017A, /* LATIN SMALL LETTER Z WITH ACUTE */ }, \
+ { 0x017B, /* LATIN CAPITAL LETTER Z WITH DOT ABOVE */ \
+ 0x017C, /* LATIN SMALL LETTER Z WITH DOT ABOVE */ }, \
+ { 0x017D, /* LATIN CAPITAL LETTER Z WITH CARON */ \
+ 0x017E, /* LATIN SMALL LETTER Z WITH CARON */ }, \
+ { 0x0000, 0x0000, }, \
+}
diff --git a/include/charset.h b/include/charset.h
new file mode 100644
index 000000000000..8cf27d91daa2
--- /dev/null
+++ b/include/charset.h
@@ -0,0 +1,346 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/* SPDX-Comment: Origin-URL: https://github.com/u-boot/u-boot/blob/ac72d17fd8fadd62df71547ef2446ef54e3c8ee5/include/charset.h */
+/*
+ * charset conversion utils
+ *
+ * Copyright (c) 2017 Rob Clark
+ */
+
+#ifndef __CHARSET_H_
+#define __CHARSET_H_
+
+#include <linux/kernel.h>
+#include <linux/types.h>
+
+#define MAX_UTF8_PER_UTF16 3
+
+/*
+ * codepage_437 - Unicode to codepage 437 translation table
+ */
+extern const u16 codepage_437[160];
+
+/**
+ * console_read_unicode() - read Unicode code point from console
+ *
+ * @code: pointer to store Unicode code point
+ * Return: 0 = success
+ */
+int console_read_unicode(s32 *code);
+
+/**
+ * utf8_get() - get next UTF-8 code point from buffer
+ *
+ * @src: pointer to current byte, updated to point to next byte
+ * Return: code point, or 0 for end of string, or -1 if no legal
+ * code point is found. In case of an error src points to
+ * the incorrect byte.
+ */
+s32 utf8_get(const char **src);
+
+/**
+ * utf8_put() - write UTF-8 code point to buffer
+ *
+ * @code: code point
+ * @dst: pointer to destination buffer, updated to next position
+ * Return: -1 if the input parameters are invalid
+ */
+int utf8_put(s32 code, char **dst);
+
+/**
+ * utf8_utf16_strnlen() - length of a truncated utf-8 string after conversion
+ * to utf-16
+ *
+ * @src: utf-8 string
+ * @count: maximum number of code points to convert
+ * Return: length in u16 after conversion to utf-16 without the
+ * trailing \0. If an invalid UTF-8 sequence is hit one
+ * u16 will be reserved for a replacement character.
+ */
+size_t utf8_utf16_strnlen(const char *src, size_t count);
+
+/**
+ * utf8_utf16_strlen() - length of a utf-8 string after conversion to utf-16
+ *
+ * @a: utf-8 string
+ * Return: length in u16 after conversion to utf-16 without the
+ * trailing \0. If an invalid UTF-8 sequence is hit one
+ * u16 will be reserved for a replacement character.
+ */
+#define utf8_utf16_strlen(a) utf8_utf16_strnlen((a), SIZE_MAX)
+
+/**
+ * utf8_utf16_strncpy() - copy utf-8 string to utf-16 string
+ *
+ * @dst: destination buffer
+ * @src: source buffer
+ * @count: maximum number of code points to copy
+ * Return: -1 if the input parameters are invalid
+ */
+int utf8_utf16_strncpy(u16 **dst, const char *src, size_t count);
+
+/**
+ * utf8_utf16_strcpy() - copy utf-8 string to utf-16 string
+ *
+ * @d: destination buffer
+ * @s: source buffer
+ * Return: -1 if the input parameters are invalid
+ */
+#define utf8_utf16_strcpy(d, s) utf8_utf16_strncpy((d), (s), SIZE_MAX)
+
+/**
+ * utf16_get() - get next UTF-16 code point from buffer
+ *
+ * @src: pointer to current word, updated to point to next word
+ * Return: code point, or 0 for end of string, or -1 if no legal
+ * code point is found. In case of an error src points to
+ * the incorrect word.
+ */
+s32 utf16_get(const u16 **src);
+
+/**
+ * utf16_put() - write UTF-16 code point to buffer
+ *
+ * @code: code point
+ * @dst: pointer to destination buffer, updated to next position
+ * Return: -1 if the input parameters are invalid
+ */
+int utf16_put(s32 code, u16 **dst);
+
+/**
+ * utf16_strnlen() - length of a truncated utf-16 string
+ *
+ * @src: utf-16 string
+ * @count: maximum number of code points to convert
+ * Return: length in code points. If an invalid UTF-16 sequence is
+ * hit one position will be reserved for a replacement
+ * character.
+ */
+size_t utf16_strnlen(const u16 *src, size_t count);
+
+/**
+ * utf16_utf8_strnlen() - length of a truncated utf-16 string after conversion
+ * to utf-8
+ *
+ * @src: utf-16 string
+ * @count: maximum number of code points to convert
+ * Return: length in bytes after conversion to utf-8 without the
+ * trailing \0. If an invalid UTF-16 sequence is hit one
+ * byte will be reserved for a replacement character.
+ */
+size_t utf16_utf8_strnlen(const u16 *src, size_t count);
+
+/**
+ * utf16_utf8_strlen() - length of a utf-16 string after conversion to utf-8
+ *
+ * @a: utf-16 string
+ * Return: length in bytes after conversion to utf-8 without the
+ * trailing \0. If an invalid UTF-16 sequence is hit one
+ * byte will be reserved for a replacement character.
+ */
+#define utf16_utf8_strlen(a) utf16_utf8_strnlen((a), SIZE_MAX)
+
+/**
+ * utf16_utf8_strncpy() - copy utf-16 string to utf-8 string
+ *
+ * @dst: destination buffer
+ * @src: source buffer
+ * @count: maximum number of code points to copy
+ * Return: -1 if the input parameters are invalid
+ */
+int utf16_utf8_strncpy(char **dst, const u16 *src, size_t count);
+
+/**
+ * utf16_utf8_strcpy() - copy utf-16 string to utf-8 string
+ *
+ * @d: destination buffer
+ * @s: source buffer
+ * Return: -1 if the input parameters are invalid
+ */
+#define utf16_utf8_strcpy(d, s) utf16_utf8_strncpy((d), (s), SIZE_MAX)
+
+/**
+ * utf_to_lower() - convert a Unicode letter to lower case
+ *
+ * @code: letter to convert
+ * Return: lower case letter or unchanged letter
+ */
+s32 utf_to_lower(const s32 code);
+
+/**
+ * utf_to_upper() - convert a Unicode letter to upper case
+ *
+ * @code: letter to convert
+ * Return: upper case letter or unchanged letter
+ */
+s32 utf_to_upper(const s32 code);
+
+/**
+ * u16_strcasecmp() - compare two u16 strings case insensitively
+ *
+ * @s1: first string to compare
+ * @s2: second string to compare
+ * Return: 0 if the first n u16 are the same in s1 and s2
+ * < 0 if the first different u16 in s1 is less than the
+ * corresponding u16 in s2
+ * > 0 if the first different u16 in s1 is greater than the
+ */
+int u16_strcasecmp(const u16 *s1, const u16 *s2);
+
+/**
+ * u16_strncmp() - compare two u16 string
+ *
+ * @s1: first string to compare
+ * @s2: second string to compare
+ * @n: maximum number of u16 to compare
+ * Return: 0 if the first n u16 are the same in s1 and s2
+ * < 0 if the first different u16 in s1 is less than the
+ * corresponding u16 in s2
+ * > 0 if the first different u16 in s1 is greater than the
+ * corresponding u16 in s2
+ */
+int u16_strncmp(const u16 *s1, const u16 *s2, size_t n);
+
+/**
+ * u16_strcmp() - compare two u16 string
+ *
+ * @s1: first string to compare
+ * @s2: second string to compare
+ * Return: 0 if the first n u16 are the same in s1 and s2
+ * < 0 if the first different u16 in s1 is less than the
+ * corresponding u16 in s2
+ * > 0 if the first different u16 in s1 is greater than the
+ * corresponding u16 in s2
+ */
+#define u16_strcmp(s1, s2) u16_strncmp((s1), (s2), SIZE_MAX)
+
+/**
+ * u16_strsize() - count size of u16 string in bytes including the null
+ * character
+ *
+ * Counts the number of bytes occupied by a u16 string
+ *
+ * @in: null terminated u16 string
+ * Return: bytes in a u16 string
+ */
+size_t u16_strsize(const void *in);
+
+/**
+ * u16_strnlen() - count non-zero words
+ *
+ * This function matches wscnlen_s() if the -fshort-wchar compiler flag is set.
+ * In the EFI context we explicitly need a function handling u16 strings.
+ *
+ * @in: null terminated u16 string
+ * @count: maximum number of words to count
+ * Return: number of non-zero words.
+ * This is not the number of utf-16 letters!
+ */
+size_t u16_strnlen(const u16 *in, size_t count);
+
+/**
+ * u16_strlen - count non-zero words
+ *
+ * This function matches wsclen() if the -fshort-wchar compiler flag is set.
+ * In the EFI context we explicitly need a function handling u16 strings.
+ *
+ * @in: null terminated u16 string
+ * Return: number of non-zero words.
+ * This is not the number of utf-16 letters!
+ */
+size_t u16_strlen(const void *in);
+
+#define u16_strlen(in) u16_strnlen(in, SIZE_MAX)
+
+/**
+ * u16_strcpy() - copy u16 string
+ *
+ * Copy u16 string pointed to by src, including terminating null word, to
+ * the buffer pointed to by dest.
+ *
+ * @dest: destination buffer
+ * @src: source buffer (null terminated)
+ * Return: 'dest' address
+ */
+u16 *u16_strcpy(u16 *dest, const u16 *src);
+
+/**
+ * u16_strdup() - duplicate u16 string
+ *
+ * Copy u16 string pointed to by src, including terminating null word, to a
+ * newly allocated buffer.
+ *
+ * @src: source buffer (null terminated)
+ * Return: allocated new buffer on success, NULL on failure
+ */
+u16 *u16_strdup(const void *src);
+
+/**
+ * u16_strlcat() - Append a length-limited, %NUL-terminated string to another
+ *
+ * Append the source string @src to the destination string @dest, overwriting
+ * null word at the end of @dest adding a terminating null word.
+ *
+ * @dest: zero terminated u16 destination string
+ * @src: zero terminated u16 source string
+ * @count: size of buffer in u16 words including taling 0x0000
+ * Return: required size including trailing 0x0000 in u16 words
+ * If return value >= count, truncation occurred.
+ */
+size_t u16_strlcat(u16 *dest, const u16 *src, size_t count);
+
+/**
+ * utf16_to_utf8() - Convert an utf16 string to utf8
+ *
+ * Converts 'size' characters of the utf16 string 'src' to utf8
+ * written to the 'dest' buffer.
+ *
+ * NOTE that a single utf16 character can generate up to 3 utf8
+ * characters. See MAX_UTF8_PER_UTF16.
+ *
+ * @dest: the destination buffer to write the utf8 characters
+ * @src: the source utf16 string
+ * @size: the number of utf16 characters to convert
+ * Return: the pointer to the first unwritten byte in 'dest'
+ */
+uint8_t *utf16_to_utf8(uint8_t *dest, const uint16_t *src, size_t size);
+
+/**
+ * utf_to_cp() - translate Unicode code point to 8bit codepage
+ *
+ * Codepoints that do not exist in the codepage are rendered as question mark.
+ *
+ * @c: pointer to Unicode code point to be translated
+ * @codepage: Unicode to codepage translation table
+ * Return: 0 on success, -ENOENT if codepoint cannot be translated
+ */
+int utf_to_cp(s32 *c, const u16 *codepage);
+
+/**
+ * utf8_to_cp437_stream() - convert UTF-8 stream to codepage 437
+ *
+ * @c: next UTF-8 character to convert
+ * @buffer: buffer, at least 5 characters
+ * Return: next codepage 437 character or 0
+ */
+int utf8_to_cp437_stream(u8 c, char *buffer);
+
+/**
+ * utf8_to_utf32_stream() - convert UTF-8 byte stream to Unicode code points
+ *
+ * The function is called for each byte @c in a UTF-8 stream. The byte is
+ * appended to the temporary storage @buffer until the UTF-8 stream in
+ * @buffer describes a Unicode code point.
+ *
+ * When a new code point has been decoded it is returned and buffer[0] is
+ * set to '\0', otherwise the return value is 0.
+ *
+ * The buffer must be at least 5 characters long. Before the first function
+ * invocation buffer[0] must be set to '\0'."
+ *
+ * @c: next UTF-8 character to convert
+ * @buffer: buffer, at least 5 characters
+ * Return: Unicode code point or 0
+ */
+int utf8_to_utf32_stream(u8 c, char *buffer);
+
+#endif /* __CHARSET_H_ */
diff --git a/lib/Kconfig b/lib/Kconfig
index 3d4221246ea2..58993bc5d3c9 100644
--- a/lib/Kconfig
+++ b/lib/Kconfig
@@ -222,6 +222,22 @@ config GENERIC_LIB_MULDI3
config NLS
bool "Native language support"
+config CHARSET
+ bool "Enable character set conversion routines" if COMPILE_TEST
+ help
+ Enables support for various conversions between different
+ character sets, such as between unicode representations and
+ different 'code pages'.
+
+config UNICODE_CAPITALIZATION
+ bool "Support Unicode capitalization" if COMPILE_TEST
+ depends on CHARSET
+ help
+ Enable correct handling of the capitalization of Unicode codepoints
+ in the range 0x0000-0xffff. If this option is not set, only the
+ the correct handling of the letters of the codepage used by the
+ FAT file system is ensured.
+
config GENERIC_ALLOCATOR
bool
help
diff --git a/lib/Makefile b/lib/Makefile
index b9b0a59ebbee..6d259dd94e16 100644
--- a/lib/Makefile
+++ b/lib/Makefile
@@ -89,6 +89,8 @@ obj-$(CONFIG_CRC_CCITT) += crc-ccitt.o
obj-$(CONFIG_CRC8) += crc8.o
obj-$(CONFIG_CRC16) += crc16.o
obj-$(CONFIG_NLS) += nls_base.o
+obj-$(CONFIG_CHARSET) += charset.o
+
obj-$(CONFIG_FSL_QE_FIRMWARE) += fsl-qe-firmware.o
obj-$(CONFIG_UBSAN_STANDALONE) += ubsan.o
obj-$(CONFIG_GENERIC_ALLOCATOR) += genalloc.o
diff --git a/lib/charset.c b/lib/charset.c
new file mode 100644
index 000000000000..b104177fc65c
--- /dev/null
+++ b/lib/charset.c
@@ -0,0 +1,583 @@
+// SPDX-License-Identifier: GPL-2.0+
+// SPDX-Comment: Origin-URL: https://github.com/u-boot/u-boot/blob/00da8d65a3baea8c3745367bea99b1d76f8f129c/lib/charset.c
+/*
+ * charset conversion utils
+ *
+ * Copyright (c) 2017 Rob Clark
+ */
+
+#include <charset.h>
+#include <stdio.h>
+#include <capitalization.h>
+#include <efi/types.h>
+#include <errno.h>
+#include <malloc.h>
+
+#include "cp437.h"
+
+/**
+ * codepage_437 - Unicode to codepage 437 translation table
+ */
+const u16 codepage_437[160] = CP437;
+
+static struct capitalization_table capitalization_table[] =
+#ifdef CONFIG_UNICODE_CAPITALIZATION
+ UNICODE_CAPITALIZATION_TABLE;
+#else
+ CP437_CAPITALIZATION_TABLE;
+#endif
+
+/**
+ * get_code() - read Unicode code point from UTF-8 stream
+ *
+ * @read_u8: - stream reader
+ * @src: - string buffer passed to stream reader, optional
+ * Return: - Unicode code point, or -1
+ */
+static int get_code(u8 (*read_u8)(void *data), void *data)
+{
+ s32 ch = 0;
+
+ ch = read_u8(data);
+ if (!ch)
+ return 0;
+ if (ch >= 0xc2 && ch <= 0xf4) {
+ int code = 0;
+
+ if (ch >= 0xe0) {
+ if (ch >= 0xf0) {
+ /* 0xf0 - 0xf4 */
+ ch &= 0x07;
+ code = ch << 18;
+ ch = read_u8(data);
+ if (ch < 0x80 || ch > 0xbf)
+ goto error;
+ ch &= 0x3f;
+ } else {
+ /* 0xe0 - 0xef */
+ ch &= 0x0f;
+ }
+ code += ch << 12;
+ if ((code >= 0xD800 && code <= 0xDFFF) ||
+ code >= 0x110000)
+ goto error;
+ ch = read_u8(data);
+ if (ch < 0x80 || ch > 0xbf)
+ goto error;
+ }
+ /* 0xc0 - 0xdf or continuation byte (0x80 - 0xbf) */
+ ch &= 0x3f;
+ code += ch << 6;
+ ch = read_u8(data);
+ if (ch < 0x80 || ch > 0xbf)
+ goto error;
+ ch &= 0x3f;
+ ch += code;
+ } else if (ch >= 0x80) {
+ goto error;
+ }
+ return ch;
+error:
+ return -1;
+}
+
+/**
+ * read_string() - read byte from character string
+ *
+ * @data: - pointer to string
+ * Return: - byte read
+ *
+ * The string pointer is incremented if it does not point to '\0'.
+ */
+static u8 read_string(void *data)
+
+{
+ const char **src = (const char **)data;
+ u8 c;
+
+ if (!src || !*src || !**src)
+ return 0;
+ c = **src;
+ ++*src;
+ return c;
+}
+
+/**
+ * read_console() - read byte from console
+ *
+ * @data - not used, needed to match interface
+ * Return: - byte read or 0 on error
+ */
+static u8 read_console(void *data)
+{
+ int ch;
+
+ ch = getchar();
+ if (ch < 0)
+ ch = 0;
+ return ch;
+}
+
+int console_read_unicode(s32 *code)
+{
+ for (;;) {
+ s32 c;
+
+ if (!tstc()) {
+ /* No input available */
+ return 1;
+ }
+
+ /* Read Unicode code */
+ c = get_code(read_console, NULL);
+ if (c > 0) {
+ *code = c;
+ return 0;
+ }
+ }
+}
+
+s32 utf8_get(const char **src)
+{
+ return get_code(read_string, src);
+}
+
+int utf8_put(s32 code, char **dst)
+{
+ if (!dst || !*dst)
+ return -1;
+ if ((code >= 0xD800 && code <= 0xDFFF) || code >= 0x110000)
+ return -1;
+ if (code <= 0x007F) {
+ **dst = code;
+ } else {
+ if (code <= 0x07FF) {
+ **dst = code >> 6 | 0xC0;
+ } else {
+ if (code < 0x10000) {
+ **dst = code >> 12 | 0xE0;
+ } else {
+ **dst = code >> 18 | 0xF0;
+ ++*dst;
+ **dst = (code >> 12 & 0x3F) | 0x80;
+ }
+ ++*dst;
+ **dst = (code >> 6 & 0x3F) | 0x80;
+ }
+ ++*dst;
+ **dst = (code & 0x3F) | 0x80;
+ }
+ ++*dst;
+ return 0;
+}
+
+size_t utf8_utf16_strnlen(const char *src, size_t count)
+{
+ size_t len = 0;
+
+ for (; *src && count; --count) {
+ s32 code = utf8_get(&src);
+
+ if (!code)
+ break;
+ if (code < 0) {
+ /* Reserve space for a replacement character */
+ len += 1;
+ } else if (code < 0x10000) {
+ len += 1;
+ } else {
+ len += 2;
+ }
+ }
+ return len;
+}
+
+int utf8_utf16_strncpy(u16 **dst, const char *src, size_t count)
+{
+ if (!src || !dst || !*dst)
+ return -1;
+
+ for (; count && *src; --count) {
+ s32 code = utf8_get(&src);
+
+ if (code < 0)
+ code = '?';
+ utf16_put(code, dst);
+ }
+ **dst = 0;
+ return 0;
+}
+
+s32 utf16_get(const u16 **src)
+{
+ s32 code, code2;
+
+ if (!src || !*src)
+ return -1;
+ if (!**src)
+ return 0;
+ code = **src;
+ ++*src;
+ if (code >= 0xDC00 && code <= 0xDFFF)
+ return -1;
+ if (code >= 0xD800 && code <= 0xDBFF) {
+ if (!**src)
+ return -1;
+ code &= 0x3ff;
+ code <<= 10;
+ code += 0x10000;
+ code2 = **src;
+ ++*src;
+ if (code2 <= 0xDC00 || code2 >= 0xDFFF)
+ return -1;
+ code2 &= 0x3ff;
+ code += code2;
+ }
+ return code;
+}
+
+int utf16_put(s32 code, u16 **dst)
+{
+ if (!dst || !*dst)
+ return -1;
+ if ((code >= 0xD800 && code <= 0xDFFF) || code >= 0x110000)
+ return -1;
+ if (code < 0x10000) {
+ **dst = code;
+ } else {
+ code -= 0x10000;
+ **dst = code >> 10 | 0xD800;
+ ++*dst;
+ **dst = (code & 0x3ff) | 0xDC00;
+ }
+ ++*dst;
+ return 0;
+}
+
+size_t utf16_strnlen(const u16 *src, size_t count)
+{
+ size_t len = 0;
+
+ for (; *src && count; --count) {
+ s32 code = utf16_get(&src);
+
+ if (!code)
+ break;
+ /*
+ * In case of an illegal sequence still reserve space for a
+ * replacement character.
+ */
+ ++len;
+ }
+ return len;
+}
+
+size_t utf16_utf8_strnlen(const u16 *src, size_t count)
+{
+ size_t len = 0;
+
+ for (; *src && count; --count) {
+ s32 code = utf16_get(&src);
+
+ if (!code)
+ break;
+ if (code < 0)
+ /* Reserve space for a replacement character */
+ len += 1;
+ else if (code < 0x80)
+ len += 1;
+ else if (code < 0x800)
+ len += 2;
+ else if (code < 0x10000)
+ len += 3;
+ else
+ len += 4;
+ }
+ return len;
+}
+
+int utf16_utf8_strncpy(char **dst, const u16 *src, size_t count)
+{
+ if (!src || !dst || !*dst)
+ return -1;
+
+ for (; count && *src; --count) {
+ s32 code = utf16_get(&src);
+
+ if (code < 0)
+ code = '?';
+ utf8_put(code, dst);
+ }
+ **dst = 0;
+ return 0;
+}
+
+s32 utf_to_lower(const s32 code)
+{
+ struct capitalization_table *pos = capitalization_table;
+ s32 ret = code;
+
+ if (code <= 0x7f) {
+ if (code >= 'A' && code <= 'Z')
+ ret += 0x20;
+ return ret;
+ }
+ for (; pos->upper; ++pos) {
+ if (pos->upper == code) {
+ ret = pos->lower;
+ break;
+ }
+ }
+ return ret;
+}
+
+s32 utf_to_upper(const s32 code)
+{
+ struct capitalization_table *pos = capitalization_table;
+ s32 ret = code;
+
+ if (code <= 0x7f) {
+ if (code >= 'a' && code <= 'z')
+ ret -= 0x20;
+ return ret;
+ }
+ for (; pos->lower; ++pos) {
+ if (pos->lower == code) {
+ ret = pos->upper;
+ break;
+ }
+ }
+ return ret;
+}
+
+/*
+ * u16_strcasecmp() - compare two u16 strings case insensitively
+ *
+ * @s1: first string to compare
+ * @s2: second string to compare
+ * @n: maximum number of u16 to compare
+ * Return: 0 if the first n u16 are the same in s1 and s2
+ * < 0 if the first different u16 in s1 is less than the
+ * corresponding u16 in s2
+ * > 0 if the first different u16 in s1 is greater than the
+ */
+int u16_strcasecmp(const u16 *s1, const u16 *s2)
+{
+ int ret = 0;
+ s32 c1, c2;
+
+ for (;;) {
+ c1 = utf_to_upper(utf16_get(&s1));
+ c2 = utf_to_upper(utf16_get(&s2));
+ ret = c1 - c2;
+ if (ret || !c1 || c1 == -1 || c2 == -1)
+ break;
+ }
+ return ret;
+}
+
+/*
+ * u16_strncmp() - compare two u16 string
+ *
+ * @s1: first string to compare
+ * @s2: second string to compare
+ * @n: maximum number of u16 to compare
+ * Return: 0 if the first n u16 are the same in s1 and s2
+ * < 0 if the first different u16 in s1 is less than the
+ * corresponding u16 in s2
+ * > 0 if the first different u16 in s1 is greater than the
+ * corresponding u16 in s2
+ */
+int __efi_runtime u16_strncmp(const u16 *s1, const u16 *s2, size_t n)
+{
+ int ret = 0;
+
+ for (; n; --n, ++s1, ++s2) {
+ ret = *s1 - *s2;
+ if (ret || !*s1)
+ break;
+ }
+
+ return ret;
+}
+
+size_t __efi_runtime u16_strnlen(const u16 *in, size_t count)
+{
+ size_t i;
+ for (i = 0; count-- && in[i]; i++);
+ return i;
+}
+
+size_t u16_strsize(const void *in)
+{
+ return (u16_strlen(in) + 1) * sizeof(u16);
+}
+
+u16 *u16_strcpy(u16 *dest, const u16 *src)
+{
+ u16 *tmp = dest;
+
+ for (;; dest++, src++) {
+ *dest = *src;
+ if (!*src)
+ break;
+ }
+
+ return tmp;
+}
+
+u16 *u16_strdup(const void *src)
+{
+ u16 *new;
+ size_t len;
+
+ if (!src)
+ return NULL;
+ len = u16_strsize(src);
+ new = malloc(len);
+ if (!new)
+ return NULL;
+ memcpy(new, src, len);
+
+ return new;
+}
+
+size_t u16_strlcat(u16 *dest, const u16 *src, size_t count)
+{
+ size_t destlen = u16_strnlen(dest, count);
+ size_t srclen = u16_strlen(src);
+ size_t ret = destlen + srclen;
+
+ if (destlen >= count)
+ return ret;
+ if (ret >= count)
+ srclen -= (ret - count + 1);
+ memcpy(&dest[destlen], src, 2 * srclen);
+ dest[destlen + srclen] = 0x0000;
+
+ return ret;
+}
+
+/* Convert UTF-16 to UTF-8. */
+uint8_t *utf16_to_utf8(uint8_t *dest, const uint16_t *src, size_t size)
+{
+ uint32_t code_high = 0;
+
+ while (size--) {
+ uint32_t code = *src++;
+
+ if (code_high) {
+ if (code >= 0xDC00 && code <= 0xDFFF) {
+ /* Surrogate pair. */
+ code = ((code_high - 0xD800) << 10) + (code - 0xDC00) + 0x10000;
+
+ *dest++ = (code >> 18) | 0xF0;
+ *dest++ = ((code >> 12) & 0x3F) | 0x80;
+ *dest++ = ((code >> 6) & 0x3F) | 0x80;
+ *dest++ = (code & 0x3F) | 0x80;
+ } else {
+ /* Error... */
+ *dest++ = '?';
+ /* *src may be valid. Don't eat it. */
+ src--;
+ }
+
+ code_high = 0;
+ } else {
+ if (code <= 0x007F) {
+ *dest++ = code;
+ } else if (code <= 0x07FF) {
+ *dest++ = (code >> 6) | 0xC0;
+ *dest++ = (code & 0x3F) | 0x80;
+ } else if (code >= 0xD800 && code <= 0xDBFF) {
+ code_high = code;
+ continue;
+ } else if (code >= 0xDC00 && code <= 0xDFFF) {
+ /* Error... */
+ *dest++ = '?';
+ } else if (code < 0x10000) {
+ *dest++ = (code >> 12) | 0xE0;
+ *dest++ = ((code >> 6) & 0x3F) | 0x80;
+ *dest++ = (code & 0x3F) | 0x80;
+ } else {
+ *dest++ = (code >> 18) | 0xF0;
+ *dest++ = ((code >> 12) & 0x3F) | 0x80;
+ *dest++ = ((code >> 6) & 0x3F) | 0x80;
+ *dest++ = (code & 0x3F) | 0x80;
+ }
+ }
+ }
+
+ return dest;
+}
+
+int utf_to_cp(s32 *c, const u16 *codepage)
+{
+ if (*c >= 0x80) {
+ int j;
+
+ /* Look up codepage translation */
+ for (j = 0; j < 0xA0; ++j) {
+ if (*c == codepage[j]) {
+ if (j < 0x20)
+ *c = j;
+ else
+ *c = j + 0x60;
+ return 0;
+ }
+ }
+ *c = '?';
+ return -ENOENT;
+ }
+ return 0;
+}
+
+int utf8_to_cp437_stream(u8 c, char *buffer)
+{
+ char *end;
+ const char *pos;
+ s32 s;
+ int ret;
+
+ for (;;) {
+ pos = buffer;
+ end = buffer + strlen(buffer);
+ *end++ = c;
+ *end = 0;
+ s = utf8_get(&pos);
+ if (s > 0) {
+ *buffer = 0;
+ ret = utf_to_cp(&s, codepage_437);
+ return s;
+ }
+ if (pos == end)
+ return 0;
+ *buffer = 0;
+ }
+}
+
+int utf8_to_utf32_stream(u8 c, char *buffer)
+{
+ char *end;
+ const char *pos;
+ s32 s;
+
+ for (;;) {
+ pos = buffer;
+ end = buffer + strlen(buffer);
+ *end++ = c;
+ *end = 0;
+ s = utf8_get(&pos);
+ if (s > 0) {
+ *buffer = 0;
+ return s;
+ }
+ if (pos == end)
+ return 0;
+ /*
+ * Appending the byte lead to an invalid UTF-8 byte sequence.
+ * Consider it as the start of a new code sequence.
+ */
+ *buffer = 0;
+ }
+}
diff --git a/lib/cp437.h b/lib/cp437.h
new file mode 100644
index 000000000000..5093130f5ed3
--- /dev/null
+++ b/lib/cp437.h
@@ -0,0 +1,48 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+
+/*
+ * Constant CP437 contains the Unicode code points for characters 0x00 - 0x1f
+ * and 0x80 - 0xff of the code page 437.
+ */
+#define CP437 { \
+ 0x0000, 0x263a, 0x263b, 0x2665, \
+ 0x2666, 0x2663, 0x2660, 0x2022, \
+ 0x25d8, 0x25cb, 0x25d9, 0x2642, \
+ 0x2640, 0x266a, 0x266b, 0x263c, \
+ 0x25ba, 0x25c4, 0x2195, 0x203c, \
+ 0x00b6, 0x00a7, 0x25ac, 0x21a8, \
+ 0x2191, 0x2193, 0x2192, 0x2190, \
+ 0x221f, 0x2194, 0x25b2, 0x25bc, \
+ 0x00c7, 0x00fc, 0x00e9, 0x00e2, \
+ 0x00e4, 0x00e0, 0x00e5, 0x00e7, \
+ 0x00ea, 0x00eb, 0x00e8, 0x00ef, \
+ 0x00ee, 0x00ec, 0x00c4, 0x00c5, \
+ 0x00c9, 0x00e6, 0x00c6, 0x00f4, \
+ 0x00f6, 0x00f2, 0x00fb, 0x00f9, \
+ 0x00ff, 0x00d6, 0x00dc, 0x00a2, \
+ 0x00a3, 0x00a5, 0x20a7, 0x0192, \
+ 0x00e1, 0x00ed, 0x00f3, 0x00fa, \
+ 0x00f1, 0x00d1, 0x00aa, 0x00ba, \
+ 0x00bf, 0x2310, 0x00ac, 0x00bd, \
+ 0x00bc, 0x00a1, 0x00ab, 0x00bb, \
+ 0x2591, 0x2592, 0x2593, 0x2502, \
+ 0x2524, 0x2561, 0x2562, 0x2556, \
+ 0x2555, 0x2563, 0x2551, 0x2557, \
+ 0x255d, 0x255c, 0x255b, 0x2510, \
+ 0x2514, 0x2534, 0x252c, 0x251c, \
+ 0x2500, 0x253c, 0x255e, 0x255f, \
+ 0x255a, 0x2554, 0x2569, 0x2566, \
+ 0x2560, 0x2550, 0x256c, 0x2567, \
+ 0x2568, 0x2564, 0x2565, 0x2559, \
+ 0x2558, 0x2552, 0x2553, 0x256b, \
+ 0x256a, 0x2518, 0x250c, 0x2588, \
+ 0x2584, 0x258c, 0x2590, 0x2580, \
+ 0x03b1, 0x00df, 0x0393, 0x03c0, \
+ 0x03a3, 0x03c3, 0x00b5, 0x03c4, \
+ 0x03a6, 0x0398, 0x03a9, 0x03b4, \
+ 0x221e, 0x03c6, 0x03b5, 0x2229, \
+ 0x2261, 0x00b1, 0x2265, 0x2264, \
+ 0x2320, 0x2321, 0x00f7, 0x2248, \
+ 0x00b0, 0x2219, 0x00b7, 0x221a, \
+ 0x207f, 0x00b2, 0x25a0, 0x00a0, \
+}
--
2.47.3
^ permalink raw reply [flat|nested] 55+ messages in thread* [PATCH v1 15/54] efi: loader: add object handling API
2025-12-18 10:37 [PATCH v1 00/54] efi: implement EFI loader support in barebox Ahmad Fatoum
` (13 preceding siblings ...)
2025-12-18 10:37 ` [PATCH v1 14/54] lib: add charset helpers Ahmad Fatoum
@ 2025-12-18 10:37 ` Ahmad Fatoum
2025-12-18 10:37 ` [PATCH v1 16/54] efi: loader: add devicepath support Ahmad Fatoum
` (38 subsequent siblings)
53 siblings, 0 replies; 55+ messages in thread
From: Ahmad Fatoum @ 2025-12-18 10:37 UTC (permalink / raw)
To: barebox; +Cc: Ahmad Fatoum
Add definitions and prototypes for the EFI object model.
The implementations will follow later, but we add this first here, so we
do not have to add everything in one big commit and can split it out
into separate parts.
The code using it can still be compiled, but it will only be linkable
with linker garbage collection enabled.
Signed-off-by: Ahmad Fatoum <a.fatoum@pengutronix.de>
---
include/efi/loader/object.h | 137 ++++++++++++++++++++++++++++++++++++
1 file changed, 137 insertions(+)
create mode 100644 include/efi/loader/object.h
diff --git a/include/efi/loader/object.h b/include/efi/loader/object.h
new file mode 100644
index 000000000000..ba294d8ff4e3
--- /dev/null
+++ b/include/efi/loader/object.h
@@ -0,0 +1,137 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * EFI application loader
+ *
+ * Copyright (c) 2016 Alexander Graf
+ */
+
+#ifndef _EFI_LOADER_OBJECT_H
+#define _EFI_LOADER_OBJECT_H
+
+#include <efi/types.h>
+#include <efi/services.h>
+
+/**
+ * enum efi_object_type - type of EFI object
+ *
+ * In UnloadImage we must be able to identify if the handle relates to a
+ * started image.
+ */
+enum efi_object_type {
+ /** @EFI_OBJECT_TYPE_UNDEFINED: undefined image type */
+ EFI_OBJECT_TYPE_UNDEFINED = 0,
+ /** @EFI_OBJECT_TYPE_BAREBOX_FIRMWARE: barebox firmware */
+ EFI_OBJECT_TYPE_BAREBOX_FIRMWARE,
+ /** @EFI_OBJECT_TYPE_LOADED_IMAGE: loaded image (not started) */
+ EFI_OBJECT_TYPE_LOADED_IMAGE,
+ /** @EFI_OBJECT_TYPE_STARTED_IMAGE: started image */
+ EFI_OBJECT_TYPE_STARTED_IMAGE,
+};
+
+/**
+ * struct efi_object - dereferenced EFI handle
+ *
+ * @link: pointers to put the handle into a linked list
+ * @protocols: linked list with the protocol interfaces installed on this
+ * handle
+ * @type: image type if the handle relates to an image
+ *
+ * UEFI offers a flexible and expandable object model. The objects in the UEFI
+ * API are devices, drivers, and loaded images. struct efi_object is our storage
+ * structure for these objects.
+ *
+ * When including this structure into a larger structure always put it first so
+ * that when deleting a handle the whole encompassing structure can be freed.
+ *
+ * A pointer to this structure is referred to as a handle. Typedef efi_handle_t
+ * has been created for such pointers.
+ */
+struct efi_object {
+ /* Every UEFI object is part of a global object list */
+ struct list_head link;
+ /* The list of protocols */
+ struct list_head protocols;
+ enum efi_object_type type;
+};
+
+/**
+ * struct efi_open_protocol_info_item - open protocol info item
+ *
+ * When a protocol is opened a open protocol info entry is created.
+ * These are maintained in a list.
+ *
+ * @link: link to the list of open protocol info entries of a protocol
+ * @info: information about the opening of a protocol
+ */
+struct efi_open_protocol_info_item {
+ struct list_head link;
+ struct efi_open_protocol_information_entry info;
+};
+
+/**
+ * struct efi_handler - single protocol interface of a handle
+ *
+ * When the UEFI payload wants to open a protocol on an object to get its
+ * interface (usually a struct with callback functions), this struct maps the
+ * protocol GUID to the respective protocol interface
+ *
+ * @link: link to the list of protocols of a handle
+ * @guid: GUID of the protocol
+ * @protocol_interface: protocol interface
+ * @open_infos: link to the list of open protocol info items
+ */
+struct efi_handler {
+ struct list_head link;
+ efi_guid_t guid;
+ void *protocol_interface;
+ struct list_head open_infos;
+};
+
+/* This list contains all UEFI objects we know of */
+extern struct list_head efi_obj_list;
+
+/* Root node */
+extern efi_handle_t efi_root;
+
+/* Initialize efi execution environment */
+efi_status_t efi_init_obj_list(void);
+
+/* Add a new object to the object list. */
+void efi_add_handle(efi_handle_t obj);
+/* Create handle */
+efi_status_t efi_create_handle(efi_handle_t *handle);
+/* Delete handle */
+efi_status_t efi_delete_handle(efi_handle_t obj);
+/* Call this to validate a handle and find the EFI object for it */
+struct efi_object *efi_search_obj(const efi_handle_t handle);
+
+/* Find a protocol on a handle */
+efi_status_t efi_search_protocol(const efi_handle_t handle,
+ const efi_guid_t *protocol_guid,
+ struct efi_handler **handler);
+/* Install new protocol on a handle */
+efi_status_t efi_add_protocol(const efi_handle_t handle,
+ const efi_guid_t *protocol,
+ const void *protocol_interface);
+/* Open protocol */
+efi_status_t efi_protocol_open(struct efi_handler *handler,
+ void **protocol_interface, void *agent_handle,
+ void *controller_handle, uint32_t attributes);
+
+/* Install multiple protocol interfaces */
+efi_status_t EFIAPI
+efi_install_multiple_protocol_interfaces(efi_handle_t *handle, ...) __attribute__((sentinel));
+efi_status_t EFIAPI
+efi_uninstall_multiple_protocol_interfaces(efi_handle_t handle, ...) __attribute__((sentinel));
+/* Get handles that support a given protocol */
+efi_status_t EFIAPI efi_locate_handle_buffer(
+ enum efi_locate_search_type search_type,
+ const efi_guid_t *protocol, void *search_key,
+ size_t *no_handles, efi_handle_t **buffer);
+/* Close an previously opened protocol interface */
+efi_status_t EFIAPI efi_close_protocol(efi_handle_t handle,
+ const efi_guid_t *protocol,
+ efi_handle_t agent_handle,
+ efi_handle_t controller_handle);
+
+#endif /* _EFI_LOADER_OBJECT_H */
--
2.47.3
^ permalink raw reply [flat|nested] 55+ messages in thread* [PATCH v1 16/54] efi: loader: add devicepath support
2025-12-18 10:37 [PATCH v1 00/54] efi: implement EFI loader support in barebox Ahmad Fatoum
` (14 preceding siblings ...)
2025-12-18 10:37 ` [PATCH v1 15/54] efi: loader: add object handling API Ahmad Fatoum
@ 2025-12-18 10:37 ` Ahmad Fatoum
2025-12-18 10:37 ` [PATCH v1 17/54] efi: loader: add debug support Ahmad Fatoum
` (37 subsequent siblings)
53 siblings, 0 replies; 55+ messages in thread
From: Ahmad Fatoum @ 2025-12-18 10:37 UTC (permalink / raw)
To: barebox; +Cc: Ahmad Fatoum
A proper device path would walk the chain of barebox devices as
determined from the OF device tree and assemble a path like:
MMIO -> PCI root -> USB controller -> USB Mass storage -> Block device
This can still happen in future, but for now we just use following
simplification that allows us to represent every VFS path in barebox as
EFI device path:
- A Bbs device node for the device component:
* The cdev of the device the file resides in
* The mount path followed by / if no cdev backs the FS device
- A FilePath device node for the file path, relative to the mount point
BBS is the BIOS Boot Specification, which seems long obsolete, but it lends
itself nicely to our purposes of wrapping barebox VFS paths. There's already
a FilePath device node sub type for media devices, but it's usually at the
end of the device path and buggy EFI payload may be not search beyond the
first file path they encounter. All other device path nodes are documented
as formatting their argument as hexadecimal with BBS being the only exception,
which takes a non-UTF-8 string.
Signed-off-by: Ahmad Fatoum <a.fatoum@pengutronix.de>
---
efi/loader/Makefile | 1 +
efi/loader/devicepath.c | 1073 +++++++++++++++++++++++++++++++
fs/fs.c | 7 +
include/efi/loader.h | 4 +
include/efi/loader/devicepath.h | 66 ++
5 files changed, 1151 insertions(+)
create mode 100644 efi/loader/devicepath.c
create mode 100644 include/efi/loader/devicepath.h
diff --git a/efi/loader/Makefile b/efi/loader/Makefile
index b4294d746b8b..c635b5b6a533 100644
--- a/efi/loader/Makefile
+++ b/efi/loader/Makefile
@@ -3,3 +3,4 @@
obj-y += memory.o pool_alloc.o
obj-y += trace.o
obj-y += table.o
+obj-y += devicepath.o
diff --git a/efi/loader/devicepath.c b/efi/loader/devicepath.c
new file mode 100644
index 000000000000..cd783477ef3e
--- /dev/null
+++ b/efi/loader/devicepath.c
@@ -0,0 +1,1073 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+// SPDX-Comment: Origin-URL: https://github.com/u-boot/u-boot/blob/d786c6b69f537ed2a64950028d270d064970d29f/lib/efi_loader/efi_device_path.c
+
+#define pr_fmt(fmt) "efi-loader: devicepath: " fmt
+
+#include <efi/loader.h>
+#include <efi/guid.h>
+#include <efi/loader/object.h>
+#include <efi/devicepath.h>
+#include <efi/loader/devicepath.h>
+#include <efi/error.h>
+#include <string.h>
+#include <xfuncs.h>
+#include <driver.h>
+#include <charset.h>
+#include <block.h>
+#include <asm/unaligned.h>
+#include <linux/overflow.h>
+#include <fs.h>
+
+/*
+ * END - Template end node for EFI device paths.
+ *
+ * Represents the terminating node of an EFI device path.
+ * It has a type of DEVICE_PATH_TYPE_END and sub_type DEVICE_PATH_SUB_TYPE_END
+ */
+static const struct efi_device_path END = {
+ .type = DEVICE_PATH_TYPE_END,
+ .sub_type = DEVICE_PATH_SUB_TYPE_END,
+ .length = sizeof(END),
+};
+
+/* template ROOT node: */
+static const struct efi_device_path_vendor ROOT = {
+ .header = {
+ .type = DEVICE_PATH_TYPE_HARDWARE_DEVICE,
+ .sub_type = DEVICE_PATH_SUB_TYPE_VENDOR,
+ .length = sizeof(ROOT),
+ },
+ .Guid = BAREBOX_GUID,
+};
+
+static void *dp_alloc(size_t sz)
+{
+ return efi_alloc(sz, "DP");
+}
+
+/**
+ * efi_dp_next() - Iterate to next block in device-path
+ *
+ * Advance to the next node in an EFI device path.
+ *
+ * @dp: Pointer to the current device path node.
+ * Return: Pointer to the next device path node, or NULL if at the end
+ * or if input is NULL.
+ */
+struct efi_device_path *efi_dp_next(const struct efi_device_path *dp)
+{
+ if (dp == NULL)
+ return NULL;
+ if (dp->type == DEVICE_PATH_TYPE_END)
+ return NULL;
+ dp = ((void *)dp) + dp->length;
+ if (dp->type == DEVICE_PATH_TYPE_END)
+ return NULL;
+ return (struct efi_device_path *)dp;
+}
+
+/**
+ * efi_dp_match() - Compare two device-paths
+ *
+ * Compare two device paths node by node. The comparison stops when an End
+ * node is reached in the shorter of the two paths. This is useful, for example,
+ * to compare a device-path representing a device with one representing a file
+ * on that device, or a device with a parent device.
+ *
+ * @a: Pointer to the first device path.
+ * @b: Pointer to the second device path.
+ * Return: An integer less than, equal to, or greater than zero if the first
+ * differing node in 'a' is found, respectively, to be less than,
+ * to match, or be greater than the corresponding node in 'b'. Returns 0
+ * if they match up to the end of the shorter path. Compares length first,
+ * then content.
+ */
+int efi_dp_match(const struct efi_device_path *a,
+ const struct efi_device_path *b)
+{
+ while (1) {
+ int ret;
+
+ ret = memcmp(&a->length, &b->length, sizeof(a->length));
+ if (ret)
+ return ret;
+
+ ret = memcmp(a, b, a->length);
+ if (ret)
+ return ret;
+
+ a = efi_dp_next(a);
+ b = efi_dp_next(b);
+
+ if (!a || !b)
+ return 0;
+ }
+}
+
+/**
+ * efi_dp_shorten() - shorten device-path
+ *
+ * When creating a short-boot option we want to use a device-path that is
+ * independent of the location where the block device is plugged in.
+ *
+ * UsbWwi() nodes contain a serial number, hard drive paths a partition
+ * UUID. Both should be unique.
+ *
+ * See UEFI spec, section 3.1.2 for "short-form device path".
+ *
+ * @dp: original device-path
+ * Return: shortened device-path or NULL
+ */
+static struct efi_device_path *efi_dp_shorten(struct efi_device_path *dp)
+{
+ while (dp) {
+ if (EFI_DP_TYPE(dp, MEDIA_DEVICE, HARD_DRIVE_PATH) ||
+ EFI_DP_TYPE(dp, MEDIA_DEVICE, FILE_PATH))
+ return dp;
+
+ dp = efi_dp_next(dp);
+ }
+
+ return dp;
+}
+
+/**
+ * find_handle() - find handle by device path and installed protocol
+ *
+ * If @rem is provided, the handle with the longest partial match is returned.
+ *
+ * @dp: device path to search
+ * @guid: GUID of protocol that must be installed on path or NULL
+ * @short_path: use short form device path for matching
+ * @rem: pointer to receive remaining device path
+ * Return: matching handle
+ */
+static efi_handle_t find_handle(struct efi_device_path *dp,
+ const efi_guid_t *guid, bool short_path,
+ struct efi_device_path **rem)
+{
+ efi_handle_t handle, best_handle = NULL;
+ efi_uintn_t len, best_len = 0;
+
+ len = efi_dp_instance_size(dp);
+
+ list_for_each_entry(handle, &efi_obj_list, link) {
+ struct efi_handler *handler;
+ struct efi_device_path *dp_current;
+ efi_uintn_t len_current;
+ efi_status_t ret;
+
+ if (guid) {
+ ret = efi_search_protocol(handle, guid, &handler);
+ if (ret != EFI_SUCCESS)
+ continue;
+ }
+ ret = efi_search_protocol(handle, &efi_device_path_protocol_guid,
+ &handler);
+ if (ret != EFI_SUCCESS)
+ continue;
+ dp_current = handler->protocol_interface;
+ if (short_path) {
+ dp_current = efi_dp_shorten(dp_current);
+ if (!dp_current)
+ continue;
+ }
+ len_current = efi_dp_instance_size(dp_current);
+ if (rem) {
+ if (len_current > len)
+ continue;
+ } else {
+ if (len_current != len)
+ continue;
+ }
+ if (memcmp(dp_current, dp, len_current))
+ continue;
+ if (!rem)
+ return handle;
+ if (len_current > best_len) {
+ best_len = len_current;
+ best_handle = handle;
+ *rem = (void*)((u8 *)dp + len_current);
+ }
+ }
+ return best_handle;
+}
+
+/**
+ * efi_dp_find_obj() - find handle by device path
+ *
+ * If @rem is provided, the handle with the longest partial match is returned.
+ *
+ * @dp: device path to search
+ * @guid: GUID of protocol that must be installed on path or NULL
+ * @rem: pointer to receive remaining device path
+ * Return: matching handle
+ */
+efi_handle_t efi_dp_find_obj(struct efi_device_path *dp,
+ const efi_guid_t *guid,
+ struct efi_device_path **rem)
+{
+ efi_handle_t handle;
+
+ handle = find_handle(dp, guid, false, rem);
+ if (!handle)
+ /* Match short form device path */
+ handle = find_handle(dp, guid, true, rem);
+
+ return handle;
+}
+
+/**
+ * efi_dp_last_node() - Determine the last device path node before the end node
+ *
+ * Iterate through the device path to find the very last node before
+ * the terminating EFI_DP_END node.
+ *
+ * @dp: Pointer to the device path.
+ * Return: Pointer to the last actual data node before the end node if it exists
+ * otherwise NULL (e.g., if dp is NULL or only an EFI_DP_END node).
+ */
+const struct efi_device_path *efi_dp_last_node(const struct efi_device_path *dp)
+{
+ struct efi_device_path *ret;
+
+ if (!dp || dp->type == DEVICE_PATH_TYPE_END)
+ return NULL;
+ while (dp) {
+ ret = (struct efi_device_path *)dp;
+ dp = efi_dp_next(dp);
+ }
+ return ret;
+}
+
+/**
+ * efi_dp_instance_size() - Get size of the first device path instance
+ *
+ * Calculate the total length of all nodes in the first instance of a
+ * (potentially multi-instance) device path. The size of the instance-specific
+ * end node (if any) or the final device path. The end node is not included.
+ *
+ * @dp: Pointer to the device path.
+ * Return: Size in bytes of the first instance, or 0 if dp is NULL or an
+ * EFI_DP_END node
+ */
+efi_uintn_t efi_dp_instance_size(const struct efi_device_path *dp)
+{
+ efi_uintn_t sz = 0;
+
+ if (!dp || dp->type == DEVICE_PATH_TYPE_END)
+ return 0;
+ while (dp) {
+ sz += dp->length;
+ dp = efi_dp_next(dp);
+ }
+
+ return sz;
+}
+
+/**
+ * efi_dp_size() - Get size of multi-instance device path excluding end node
+ *
+ * Calculate the total size of the entire device path structure, traversing
+ * through all instances, up to but not including the final
+ * END_ENTIRE_DEVICE_PATH node.
+ *
+ * @dp: Pointer to the device path.
+ * Return: Total size in bytes of all nodes in the device path (excluding the
+ * final EFI_DP_END node), or 0 if dp is NULL.
+ */
+efi_uintn_t efi_dp_size(const struct efi_device_path *dp)
+{
+ const struct efi_device_path *p = dp;
+
+ if (!p)
+ return 0;
+ while (p->type != DEVICE_PATH_TYPE_END ||
+ p->sub_type != DEVICE_PATH_SUB_TYPE_END)
+ p = (void *)p + p->length;
+
+ return (void *)p - (void *)dp;
+}
+
+/**
+ * efi_dp_dup() - Copy multi-instance device path
+ *
+ * Duplicate the given device path, including its end node(s).
+ * The caller is responsible for freeing the allocated memory (e.g.,
+ * using efi_free()).
+ *
+ * @dp: Pointer to the device path to duplicate.
+ * Return: Pointer to the newly allocated and copied device path, or NULL on
+ * allocation failure or if dp is NULL.
+ */
+struct efi_device_path *efi_dp_dup(const struct efi_device_path *dp)
+{
+ struct efi_device_path *ndp;
+ size_t sz = efi_dp_size(dp) + sizeof(END);
+
+ if (!dp)
+ return NULL;
+
+ ndp = dp_alloc(sz);
+ if (!ndp)
+ return NULL;
+ memcpy(ndp, dp, sz);
+
+ return ndp;
+}
+
+/**
+ * efi_dp_concat() - Concatenate two device paths and terminate the result
+ *
+ * @dp1: First device path
+ * @dp2: Second device path
+ * @split_end_node:
+ * - 0 to concatenate (dp1 is assumed not to have an end node or it's ignored,
+ * dp2 is appended, then one EFI_DP_END node)
+ * - 1 to concatenate with end node added as separator (dp1, END_THIS_INSTANCE,
+ * dp2, END_ENTIRE)
+ *
+ * Size of dp1 excluding last end node to concatenate with end node as
+ * separator in case dp1 contains an end node (dp1 (partial), END_THIS_INSTANCE,
+ * dp2, END_ENTIRE)
+ *
+ * Return:
+ * concatenated device path or NULL. Caller must free the returned value.
+ */
+struct
+efi_device_path *efi_dp_concat(const struct efi_device_path *dp1,
+ const struct efi_device_path *dp2,
+ size_t split_end_node)
+{
+ struct efi_device_path *ret;
+ size_t end_size;
+
+ if (!dp1 && !dp2) {
+ /* return an end node */
+ ret = efi_dp_dup(&END);
+ } else if (!dp1) {
+ ret = efi_dp_dup(dp2);
+ } else if (!dp2) {
+ ret = efi_dp_dup(dp1);
+ } else {
+ /* both dp1 and dp2 are non-null */
+ size_t sz1;
+ size_t sz2 = efi_dp_size(dp2);
+ void *p;
+
+ if (split_end_node < sizeof(struct efi_device_path))
+ sz1 = efi_dp_size(dp1);
+ else
+ sz1 = split_end_node;
+
+ if (split_end_node)
+ end_size = 2 * sizeof(END);
+ else
+ end_size = sizeof(END);
+ p = dp_alloc(sz1 + sz2 + end_size);
+ if (!p)
+ return NULL;
+ ret = p;
+ memcpy(p, dp1, sz1);
+ p += sz1;
+
+ if (split_end_node) {
+ memcpy(p, &END, sizeof(END));
+ p += sizeof(END);
+ }
+
+ /* the end node of the second device path has to be retained */
+ memcpy(p, dp2, sz2);
+ p += sz2;
+ memcpy(p, &END, sizeof(END));
+ }
+
+ return ret;
+}
+
+/**
+ * efi_dp_append_node() - Append a single node to a device path
+ *
+ * Create a new device path by appending a given node to an existing
+ * device path.
+ * If the original device path @dp is NULL, a new path is created
+ * with the given @node followed by an EFI_DP_END node.
+ * If the @node is NULL and @dp is not NULL, the original path @dp is
+ * duplicated.
+ * If both @dp and @node are NULL, a path with only an EFI_DP_END node is
+ * returned.
+ * The caller must free the returned path (e.g., using efi_free()).
+ *
+ * @dp: Original device path (can be NULL).
+ * @node: Node to append (can be NULL).
+ * Return: New device path with the node appended, or NULL on allocation
+ * failure.
+ */
+struct efi_device_path *efi_dp_append_node(const struct efi_device_path *dp,
+ const struct efi_device_path *node)
+{
+ struct efi_device_path *ret;
+
+ if (!node && !dp) {
+ ret = efi_dp_dup(&END);
+ } else if (!node) {
+ ret = efi_dp_dup(dp);
+ } else if (!dp) {
+ size_t sz = node->length;
+ void *p = dp_alloc(sz + sizeof(END));
+ if (!p)
+ return NULL;
+ memcpy(p, node, sz);
+ memcpy(p + sz, &END, sizeof(END));
+ ret = p;
+ } else {
+ /* both dp and node are non-null */
+ size_t sz = efi_dp_size(dp);
+ void *p = dp_alloc(sz + node->length + sizeof(END));
+ if (!p)
+ return NULL;
+ memcpy(p, dp, sz);
+ memcpy(p + sz, node, node->length);
+ memcpy(p + sz + node->length, &END, sizeof(END));
+ ret = p;
+ }
+
+ return ret;
+}
+
+/**
+ * efi_dp_split_file_path() - split of relative file path from device path
+ *
+ * Given a device path indicating a file on a device, separate the device
+ * path in two: the device path of the actual device and the file path
+ * relative to this device.
+ *
+ * @full_path: device path including device and file path
+ * @device_path: path of the device
+ * @file_path: relative path of the file or NULL if there is none
+ * Return: status code
+ */
+efi_status_t efi_dp_split_file_path(struct efi_device_path *full_path,
+ struct efi_device_path **device_path,
+ struct efi_device_path **file_path)
+{
+ struct efi_device_path *p, *dp, *fp = NULL;
+
+ *device_path = NULL;
+ *file_path = NULL;
+ dp = efi_dp_dup(full_path);
+ if (!dp)
+ return EFI_OUT_OF_RESOURCES;
+ p = dp;
+ while (!EFI_DP_TYPE(p, MEDIA_DEVICE, FILE_PATH)) {
+ p = efi_dp_next(p);
+ if (!p)
+ goto out;
+ }
+ fp = efi_dp_dup(p);
+ if (!fp)
+ return EFI_OUT_OF_RESOURCES;
+ p->type = DEVICE_PATH_TYPE_END;
+ p->sub_type = DEVICE_PATH_SUB_TYPE_END;
+ p->length = sizeof(*p);
+
+out:
+ *device_path = dp;
+ *file_path = fp;
+ return EFI_SUCCESS;
+}
+
+static void efi_dp_end(struct efi_device_path *dp)
+{
+ *dp = END;
+}
+
+/**
+ * efi_dp_from_mem() - Construct a device-path for a memory-mapped region
+ *
+ * Create an EFI device path representing a specific memory region, defined
+ * by its type, start address, and size.
+ * The caller is responsible for freeing the allocated memory (e.g.,
+ * using efi_free()).
+ *
+ * @memory_type: EFI memory type (e.g., EFI_RESERVED_MEMORY_TYPE).
+ * @start_address: Starting address of the memory region.
+ * @size: Size of the memory region in bytes.
+ * Return: Pointer to the new memory device path, or NULL on allocation failure
+ */
+struct efi_device_path *efi_dp_from_mem(uint32_t memory_type,
+ uint64_t start_address,
+ uint64_t end_address)
+{
+ struct efi_device_path_memory *mdp;
+ void *buf, *start;
+
+ start = buf = dp_alloc(sizeof(*mdp) + sizeof(END));
+ if (!buf)
+ return NULL;
+
+ mdp = buf;
+ mdp->header.type = DEVICE_PATH_TYPE_HARDWARE_DEVICE;
+ mdp->header.sub_type = DEVICE_PATH_SUB_TYPE_MEMORY;
+ mdp->header.length = sizeof(*mdp);
+ mdp->memory_type = memory_type;
+ mdp->starting_address = start_address;
+ mdp->ending_address = end_address;
+
+ efi_dp_end(&mdp[1].header);
+
+ return start;
+}
+
+/**
+ * efi_dp_create_device_node() - Create a new device path node
+ *
+ * Allocate and initialise the header of a new EFI device path node with the
+ * given type, sub-type, and length. The content of the node beyond the basic
+ * efi_device_path header is zeroed by efi_alloc.
+ *
+ * @type: Device path type.
+ * @sub_type: Device path sub-type.
+ * @length: Length of the node (must be >= sizeof(struct efi_device_path)).
+ * Return: Pointer to the new device path node, or NULL on allocation failure
+ * or if length is invalid.
+ */
+struct efi_device_path *efi_dp_create_device_node(const u8 type,
+ const u8 sub_type,
+ const u16 length)
+{
+ struct efi_device_path *ret;
+
+ if (length < sizeof(struct efi_device_path))
+ return NULL;
+
+ ret = dp_alloc(length);
+ if (!ret)
+ return ret;
+ ret->type = type;
+ ret->sub_type = sub_type;
+ ret->length = length;
+ return ret;
+}
+
+/**
+ * efi_dp_append_instance() - Append a device path instance to another
+ *
+ * Concatenate two device paths, treating the second path (@dpi) as a new
+ * instance appended to the first path (@dp). An END_THIS_INSTANCE node is
+ * inserted between @dp and @dpi if @dp is not NULL.
+ * If @dp is NULL, @dpi is duplicated (and terminated appropriately).
+ * @dpi must not be NULL.
+ * The caller is responsible for freeing the returned path (e.g., using
+ * efi_free()).
+ *
+ * @dp: The base device path. If NULL, @dpi is duplicated.
+ * @dpi: The device path instance to append. Must not be NULL.
+ * Return: A new device path with @dpi appended as a new instance, or NULL on
+ * error (e.g. allocation failure, @dpi is NULL).
+ */
+struct efi_device_path *efi_dp_append_instance(
+ const struct efi_device_path *dp,
+ const struct efi_device_path *dpi)
+{
+ size_t sz, szi;
+ struct efi_device_path *p, *ret;
+
+ if (!dpi)
+ return NULL;
+ if (!dp)
+ return efi_dp_dup(dpi);
+ sz = efi_dp_size(dp);
+ szi = efi_dp_instance_size(dpi);
+ p = dp_alloc(sz + szi + 2 * sizeof(END));
+ if (!p)
+ return NULL;
+ ret = p;
+ memcpy(p, dp, sz + sizeof(END));
+ p = (void *)p + sz;
+ p->sub_type = DEVICE_PATH_SUB_TYPE_INSTANCE_END;
+ p = (void *)p + sizeof(END);
+ memcpy(p, dpi, szi);
+ p = (void *)p + szi;
+ memcpy(p, &END, sizeof(END));
+ return ret;
+}
+
+/**
+ * efi_dp_get_next_instance() - Extract the next dp instance
+ *
+ * Given a pointer to a pointer to a device path (@dp), this function extracts
+ * the first instance from the path. It allocates a new path for this extracted
+ * instance (including its instance-specific EFI_DP_END node). The input pointer
+ * (*@dp) is then updated to point to the start of the next instance in the
+ * original path, or set to NULL if no more instances remain.
+ * The caller is responsible for freeing the returned instance path (e.g.,
+ * using efi_free()).
+ *
+ * @dp: On input, a pointer to a pointer to the multi-instance device path.
+ * On output, *@dp is updated to point to the start of the next instance,
+ * or NULL if no more instances.
+ * @size: Optional pointer to an efi_uintn_t variable that will receive the size
+ * of the extracted instance path (including its EFI_DP_END node).
+ * Return: Pointer to a newly allocated device path for the extracted instance,
+ * or NULL if no instance could be extracted or an error occurred (e.g.,
+ * allocation failure).
+ */
+struct efi_device_path *efi_dp_get_next_instance(struct efi_device_path **dp,
+ efi_uintn_t *size)
+{
+ size_t sz;
+ struct efi_device_path *p;
+
+ if (size)
+ *size = 0;
+ if (!dp || !*dp)
+ return NULL;
+ sz = efi_dp_instance_size(*dp);
+ p = dp_alloc(sz + sizeof(END));
+ if (!p)
+ return NULL;
+ memcpy(p, *dp, sz + sizeof(END));
+ *dp = (void *)*dp + sz;
+ if ((*dp)->sub_type == DEVICE_PATH_SUB_TYPE_INSTANCE_END)
+ *dp = (void *)*dp + sizeof(END);
+ else
+ *dp = NULL;
+ if (size)
+ *size = sz + sizeof(END);
+ return p;
+}
+
+/**
+ * efi_dp_is_multi_instance() - Check if a device path is multi-instance
+ *
+ * Traverse the device path to its end. It is considered multi-instance if an
+ * END_THIS_INSTANCE_DEVICE_PATH node (type DEVICE_PATH_TYPE_END, sub-type
+ * DEVICE_PATH_SUB_TYPE_INSTANCE_END) is encountered before the final
+ * END_ENTIRE_DEVICE_PATH node.
+ *
+ * @dp: The device path to check.
+ * Return: True if the device path contains multiple instances, false otherwise
+ * (including if @dp is NULL).
+ */
+bool efi_dp_is_multi_instance(const struct efi_device_path *dp)
+{
+ const struct efi_device_path *p = dp;
+
+ if (!p)
+ return false;
+ while (p->type != DEVICE_PATH_TYPE_END)
+ p = (void *)p + p->length;
+ return p->sub_type == DEVICE_PATH_SUB_TYPE_INSTANCE_END;
+}
+
+/**
+ * path_to_uefi() - convert UTF-8 path to an UEFI style path
+ *
+ * Convert UTF-8 path to a UEFI style path (i.e. with backslashes as path
+ * separators and UTF-16).
+ *
+ * @src: source buffer
+ * @uefi: target buffer, possibly unaligned
+ * Return: number of wide characters, including NUL terminator
+ */
+static size_t path_to_uefi(wchar_t *uefi, const char *src)
+{
+ wchar_t *pos = uefi;
+
+ while (*src) {
+ s32 code = utf8_get(&src);
+
+ if (code < 0)
+ code = '?';
+ else if (code == '/')
+ code = '\\';
+ utf16_put(code, &pos);
+ }
+ *pos++ = 0;
+
+ return pos - uefi;
+}
+
+/**
+ * efi_fp_size() - Calculate file path EFI device node size
+ *
+ * Calculate the final size the node would take, so space can be allocated.
+ *
+ * @path: file path inside device
+ * Return: Size needed for the efi_device_path_file_path or -EOVERFLOW on error
+ */
+static ssize_t efi_fp_size(const char *path)
+{
+ size_t fpsize;
+
+ fpsize = struct_size_t(struct efi_device_path_file_path, path_name,
+ utf8_utf16_strlen(path) + 1);
+ if (fpsize > U16_MAX)
+ return -EOVERFLOW;
+
+ return fpsize;
+}
+
+/**
+ * efi_fp_fill() - Fill file path EFI device node
+ *
+ * Initialize the node and fill out its path including NUL termination.
+ *
+ * @fp: efi_device_path_file_path to be filled
+ * @path: file path inside device
+ * Return: Pointer to next node after advancing by fp + copied path
+ */
+static struct efi_device_path *efi_fp_fill(struct efi_device_path_file_path *fp, const char *path)
+{
+ fp->header.type = DEVICE_PATH_TYPE_MEDIA_DEVICE;
+ fp->header.sub_type = DEVICE_PATH_SUB_TYPE_FILE_PATH;
+ fp->header.length = struct_size(fp, path_name, path_to_uefi(fp->path_name, path));
+
+ return efi_dp_next(&fp->header);
+}
+
+/**
+ * efi_bbs_size() - Calculate BiosBootSpecification EFI device node size
+ *
+ * Calculate the final size node would take, so space can be allocated.
+ * To simplify things for users within this file that will want to prepend
+ * and append strings, this is directly handled in the API.
+ *
+ * @prefix: prefix to prepend in front of path, may be NULL
+ * @path: The string argument for the BBS node
+ * @suffix: suffix to append after path, may be NULL
+ * Return: Size needed for the efi_device_path_bbs_bbs or -EOVERFLOW on error
+ */
+static ssize_t efi_bbs_size(const char *prefix, const char *path, const char *suffix)
+{
+ size_t fpsize;
+ size_t len = 0;
+
+ len += strlen(prefix ?: "");
+ len += strlen(path);
+ len += strlen(suffix ?: "");
+ len += 1;
+
+ fpsize = struct_size_t(struct efi_device_path_bbs_bbs, String, len);
+ if (fpsize > U16_MAX)
+ return -EOVERFLOW;
+
+ return fpsize;
+}
+
+/**
+ * efi_bbs_fill() - Fill BiosBootSpecification EFI device node
+ *
+ * Initialize the node and fill out its path including NUL termination.
+ * To simplify things for users within this file that will want to prepend
+ * and append strings, this is directly handled in the API.
+ *
+ * @bp: efi_device_path_file_path to be filled
+ * @prefix: prefix to prepend in front of path, may be NULL
+ * @path: The string argument for the BBS node
+ * @suffix: suffix to append after path, may be NULL
+ * Return: Pointer to next node after advancing by bp + copied path
+ */
+static struct efi_device_path *efi_bbs_fill(struct efi_device_path_bbs_bbs *bp,
+ const char *prefix,
+ const char *path,
+ const char *suffix)
+{
+ s8 *end = bp->String;
+
+ bp->header.type = DEVICE_PATH_TYPE_BBS_DEVICE;
+ bp->header.sub_type = 0x01;
+ bp->device_type = BBS_TYPE_DEV;
+ bp->status_flag = 0;
+
+ end = stpcpy(end, prefix ?: "");
+ end = stpcpy(end, path);
+ end = stpcpy(end, suffix ?: "");
+ end++;
+
+ bp->header.length = struct_size(bp, String, end - bp->String);
+
+ return efi_dp_next(&bp->header);
+}
+
+/**
+ * __efi_dp_cdev_table_part() - Fill hard drive EFI device node from cdev
+ *
+ * Generates an EFI device node from a barebox cdev partition. The result
+ * looks like this:
+ *
+ * HD(Part2,Sig7963f4e7-618a-3b49-ac6b-10821a9385cc).
+ *
+ * @hddp: The EFI device path node
+ * @cdev: The partition's cdev
+ * Return: Pointer to next node after advancing by sizeof(*hddp)
+ */
+static struct efi_device_path *
+__efi_dp_cdev_table_part(struct efi_device_path_hard_drive_path *hddp,
+ const struct cdev *cdev)
+{
+ struct cdev *master = cdev->master;
+ struct block_device *blk = cdev_get_block_device(master);
+ int ret;
+
+ if (!(master->flags & (DEVFS_IS_MBR_PARTITIONED | DEVFS_IS_GPT_PARTITIONED))) {
+ pr_debug("%s: cdev outside partition table\n", cdev->name);
+ return NULL;
+ }
+
+ /*
+ * UEFI v2.10 documents Signature as u32 for MBR and GUID for GPT.
+ * As signature for partition on MBR ist ${DISKSIG}-${PARTID}, this
+ * would imply that GUID is also DISKUUID and not PARTUUID. EDK-II
+ * and U-Boot use partition GUID though, which is kind of redundant
+ * as there's already a partition number... We follow suit, even
+ * if doesn't make perfect sense.
+ */
+ if (cdev_is_gpt_partitioned(master)) {
+ guid_t sig;
+
+ ret = guid_parse(cdev->partuuid, &sig);
+ if (ret) {
+ pr_debug("%s: failed to parse GPT uuid\n", cdev->name);
+ return NULL;
+ }
+
+ hddp->mbr_type = MBR_TYPE_EFI_PARTITION_TABLE_HEADER;
+ hddp->signature_type = SIGNATURE_TYPE_GUID;
+ export_guid(hddp->signature, &sig);
+ } else if (cdev_is_mbr_partitioned(master)) {
+ u32 sig;
+
+ // TODO Extended partitions should be children of the
+ // extended raw partition, which should've an index of 0
+
+ ret = kstrtou32(master->diskuuid, 16, &sig);
+ if (ret) {
+ pr_debug("%s: failed to parse MBR signature\n", cdev->name);
+ return NULL;
+ }
+
+ hddp->mbr_type = MBR_TYPE_PCAT;
+ hddp->signature_type = SIGNATURE_TYPE_MBR;
+ put_unaligned_le32(sig, hddp->signature);
+ memset(&hddp->signature[4], 0, 12);
+ }
+
+ hddp->header.type = DEVICE_PATH_TYPE_MEDIA_DEVICE;
+ hddp->header.sub_type = DEVICE_PATH_SUB_TYPE_HARD_DRIVE_PATH;
+ hddp->header.length = sizeof(*hddp);
+ /* barebox starts counting partitions at 1 */
+ hddp->partition_number = cdev->partition_table_index + 1;
+
+ hddp->partition_start = cdev->offset >> blk->blockbits;
+ hddp->partition_size = cdev->size >> blk->blockbits;
+
+ return efi_dp_next(&hddp->header);
+}
+
+/**
+ * efi_dp_from_cdev() - Generate EFI device node from any cdev
+ *
+ * A barebox cdev is represented as a BBS path, optionally followed
+ * by a hard drive path specifying the partition.
+ *
+ * BBS is the BIOS Boot Specification, which seems long obsolete, but it lends
+ * itself nicely to our purposes of wrapping barebox VFS paths. There's already
+ * a FilePath device node sub type for media devices, but it's usually at the
+ * end of the device path and buggy EFI payload may be not search beyond the
+ * first file path they encounter. All other device path nodes are documented
+ * as formatting their argument as hexadecimal with BBS being the only exception,
+ * which takes a non-UTF-8 string. Our convention is:
+ *
+ * virtioblk2 -> BBS(Dev,/dev/virtioblk2) FIXME
+ *
+ * @cdev: A barebox cdev
+ * @full: return full path, including ROOT and END
+ * Return: Pointer to efi_alloc()ated node or NULL on error
+ */
+struct efi_device_path *efi_dp_from_cdev(struct cdev *cdev, bool full)
+{
+ struct efi_device_path *dp = NULL;
+ void *buf = NULL;
+ struct cdev *cdevm = full ? cdev->master : NULL;
+ ssize_t bbs_size;
+ size_t dp_size = 0;
+
+ bbs_size = efi_bbs_size("/dev/", cdev->name, NULL);
+ if (bbs_size < 0)
+ return NULL;
+
+ if (full)
+ dp_size += sizeof(ROOT) + sizeof(END);
+ if (cdevm)
+ dp_size += bbs_size;
+
+ dp_size += max_t(size_t,
+ sizeof(struct efi_device_path_hard_drive_path), bbs_size);
+
+ dp = buf = dp_alloc(dp_size);
+ if (!dp)
+ return NULL;
+
+ if (full)
+ buf = mempcpy(buf, &ROOT, sizeof(ROOT));
+
+ if (cdevm)
+ buf = efi_bbs_fill(buf, "/dev/", cdevm->name, NULL);
+
+ if (cdev->flags & DEVFS_PARTITION_FROM_TABLE) {
+ void *oldbuf = buf;
+
+ buf = __efi_dp_cdev_table_part(buf, cdev);
+ if (buf)
+ goto out;
+
+ buf = oldbuf;
+ }
+
+ buf = efi_bbs_fill(buf, "/dev/", cdev->name, NULL);
+
+out:
+ if (full)
+ efi_dp_end(buf);
+ return dp;
+}
+
+/**
+ * efi_dp_from_fsdev() - Generate EFI device node from non-cdev-backed FS dev
+ *
+ * For file system devices with no backing cdev, this function returns
+ * a BBS path wrapping the mount point with a trailing slash as to
+ * differentiate from cdevs, which have no trailing /. Example:
+ *
+ * tftpfs0 -> BBS(Dev,/mnt/tftp/)
+ *
+ * @fsdev: File system device
+ * Return: Pointer to efi_alloc()ated node or NULL on error
+ */
+static struct efi_device_path *efi_dp_from_fsdev(struct fs_device *fsdev)
+{
+ struct efi_device_path *dp;
+ ssize_t bbs_size;
+ const char *slash = "/";
+ void *buf;
+
+ if (*fsdev->path && fsdev->path[strlen(fsdev->path) - 1] == '/')
+ slash = NULL;
+
+ bbs_size = efi_bbs_size(NULL, fsdev->path, slash);
+ if (bbs_size < 0)
+ return NULL;
+
+ dp = buf = dp_alloc(sizeof(ROOT) + bbs_size + sizeof(END));
+ if (!dp)
+ return NULL;
+
+ buf = mempcpy(buf, &ROOT, sizeof(ROOT));
+ buf = efi_bbs_fill(buf, NULL, fsdev->path, slash);
+ efi_dp_end(buf);
+
+ return dp;
+}
+
+/**
+ * efi_dp_from_fsdev() - Generate EFI device node from barebox VFS path
+ *
+ * This function can map any barebox VFS path to an EFI device path.
+ * It's not pretty and in future it's meant to be only the fallback,
+ * with proper hierarchy, e.g.
+ * MMIO -> PCI root -> USB controller -> USB Mass storage -> Block device,
+ * taking its place.
+ *
+ * For now though, it's enough to allow Grub to find its config and to
+ * represent arbitrary VFS paths.
+ *
+ * Examples (All start with VenHw(b9731de6-84a3-cc4a-aeab-82e828f362bb)):
+ *
+ * /dev/hwrng0
+ * -> devfs0/hwrng0
+ * /mnt/virtioblk0.14/
+ * -> BBS(Dev,/dev/virtioblk0)/HD(Part15,Sig26e17b2d-45f0-4c67-9d42-0a5c4a9587fc)/\
+ * /mnt/virtioblk0.14/barebox.efivars
+ * ->
+ * BBS(Dev,/dev/virtioblk0)/HD(Part15,Sig26e17b2d-45f0-4c67-9d42-0a5c4a9587fc)/\barebox.efivars
+ * /dev/hwrng0
+ * -> BBS(Dev,/dev/hwrng0)
+ * /mnt/9p/
+ * -> BBS(Dev,/)/\mnt\9p
+ * /mnt/9p/fs0
+ * -> BBS(Dev,/mnt/9p/fs0/)/\
+ * /mnt/9p/fs0/
+ * -> BBS(Dev,/mnt/9p/fs0/)/\
+ *
+ * @dirfd: directory fd to lookup a relative path in
+ * @path: relative or absolute VFS path
+ * Return: Pointer to efi_alloc()ated node or NULL on error
+ */
+struct efi_device_path *efi_dp_from_file(int dirfd, const char *path)
+{
+ struct fs_device *fsdev;
+ void *buf;
+ ssize_t dp_size;
+ struct efi_device_path *prefix_dp, *dp = NULL;
+ struct cdev *cdev;
+ char *filepath;
+
+ cdev = cdev_by_name(devpath_to_name(path));
+ if (cdev)
+ return efi_dp_from_cdev(cdev, true);
+
+ fsdev = resolve_fsdevice_path(dirfd, path, &filepath);
+ if (!fsdev)
+ return NULL;
+
+ if (fsdev->cdev)
+ prefix_dp = efi_dp_from_cdev(fsdev->cdev, true);
+ else
+ prefix_dp = efi_dp_from_fsdev(fsdev);
+
+ if (!prefix_dp)
+ goto out;
+
+ /* both dp and node are non-null */
+ dp_size = efi_dp_size(prefix_dp);
+ buf = dp = dp_alloc(dp_size + efi_fp_size(filepath) + sizeof(END));
+ if (!dp)
+ goto out;
+
+ buf = mempcpy(buf, prefix_dp, dp_size);
+ buf = efi_fp_fill(buf, filepath);
+ efi_dp_end(buf);
+
+out:
+ efi_free_pool(prefix_dp);
+ free(filepath);
+
+ return dp;
+}
+
+/**
+ * efi_dp_from_file_tostr() - format barebox VFS path as EFI device path string
+ *
+ * Convert any barebox virtual file system path to an UEFI device path
+ * and then return its full string representation.
+ *
+ * @dirfd: directory fd
+ * @path: barebox virtual file system path
+ * Return: string representation of the UEFI device path to be freed with
+ * free() or NULL otherwise.
+ */
+char *efi_dp_from_file_tostr(int dirfd, const char *path)
+{
+ struct efi_device_path *dp;
+ char *dpstr = NULL;
+
+ dp = efi_dp_from_file(dirfd, path);
+ if (dp)
+ dpstr = device_path_to_str(dp);
+
+ efi_free_pool(dp);
+
+ return dpstr;
+}
diff --git a/fs/fs.c b/fs/fs.c
index e997bd5f6a99..75fd3a3ef962 100644
--- a/fs/fs.c
+++ b/fs/fs.c
@@ -37,6 +37,7 @@
#include <parseopt.h>
#include <linux/namei.h>
#include <security/config.h>
+#include <efi/loader/devicepath.h>
char *mkmodestr(unsigned long mode, char *str)
{
@@ -197,6 +198,12 @@ void stat_print(int dirfd, const char *filename, const struct stat *st)
if (cdev)
cdev_print(cdev);
+
+ if (IS_ENABLED(CONFIG_EFI_LOADER)) {
+ char *dpstr = efi_dp_from_file_tostr(dirfd, filename);
+ printf("EFI device path: %s\n", dpstr);
+ free(dpstr);
+ }
}
EXPORT_SYMBOL(stat_print);
diff --git a/include/efi/loader.h b/include/efi/loader.h
index fcca8676479d..47fdcf5c5114 100644
--- a/include/efi/loader.h
+++ b/include/efi/loader.h
@@ -12,6 +12,10 @@ struct efi_table_hdr;
#define EFI_SPECIFICATION_VERSION (2 << 16 | 80)
+/* GUID used by the root node. */
+#define BAREBOX_GUID \
+ EFI_GUID(0x0f38fd98, 0x6788, 0x5379, 0x9b, 0xae, 0x43, 0x05, 0xb3, 0x58, 0x4f, 0x82)
+
/* Key identifying current memory map */
extern efi_uintn_t efi_memory_map_key;
diff --git a/include/efi/loader/devicepath.h b/include/efi/loader/devicepath.h
new file mode 100644
index 000000000000..f199dab8e58c
--- /dev/null
+++ b/include/efi/loader/devicepath.h
@@ -0,0 +1,66 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+#ifndef __EFI_LOADER_DEVICE_PATH_H
+#define __EFI_LOADER_DEVICE_PATH_H
+
+#include <efi/types.h>
+#include <linux/compiler.h>
+
+struct efi_device_path *efi_dp_next(const struct efi_device_path *dp);
+
+int efi_dp_match(const struct efi_device_path *a,
+ const struct efi_device_path *b);
+
+efi_handle_t efi_dp_find_obj(struct efi_device_path *dp,
+ const efi_guid_t *guid,
+ struct efi_device_path **rem);
+
+size_t efi_dp_instance_size(const struct efi_device_path *dp);
+
+size_t efi_dp_size(const struct efi_device_path *dp);
+
+struct efi_device_path *efi_dp_dup(const struct efi_device_path *dp);
+
+struct efi_device_path *efi_dp_concat(const struct efi_device_path *dp1,
+ const struct efi_device_path *dp2,
+ size_t split_end_node);
+
+struct efi_device_path *efi_dp_append_node(const struct efi_device_path *dp,
+ const struct efi_device_path *node);
+
+struct efi_device_path *efi_dp_create_device_node(const u8 type,
+ const u8 sub_type,
+ const u16 length);
+struct efi_device_path *efi_dp_append_instance(
+ const struct efi_device_path *dp,
+ const struct efi_device_path *dpi);
+
+struct efi_device_path *efi_dp_get_next_instance(struct efi_device_path **dp,
+ size_t *size);
+
+bool efi_dp_is_multi_instance(const struct efi_device_path *dp);
+
+struct efi_device_path *efi_dp_from_file(int dirfd, const char *);
+
+struct cdev;
+
+struct efi_device_path *efi_dp_from_cdev(struct cdev *cdev, bool full);
+
+efi_status_t efi_dp_split_file_path(struct efi_device_path *full_path,
+ struct efi_device_path **device_path,
+ struct efi_device_path **file_path);
+
+struct efi_device_path *efi_dp_from_mem(uint32_t mem_type,
+ uint64_t start_address,
+ uint64_t end_address);
+
+const struct efi_device_path *efi_dp_last_node(
+ const struct efi_device_path *dp);
+
+#define EFI_DP_TYPE(_dp, _type, _subtype) \
+ (((_dp)->type == DEVICE_PATH_TYPE_##_type) && \
+ ((_dp)->sub_type == DEVICE_PATH_SUB_TYPE_##_subtype))
+
+
+char *efi_dp_from_file_tostr(int dirfd, const char *path);
+
+#endif /* __EFI_LOADER_DEVICE_PATH_H */
--
2.47.3
^ permalink raw reply [flat|nested] 55+ messages in thread* [PATCH v1 17/54] efi: loader: add debug support
2025-12-18 10:37 [PATCH v1 00/54] efi: implement EFI loader support in barebox Ahmad Fatoum
` (15 preceding siblings ...)
2025-12-18 10:37 ` [PATCH v1 16/54] efi: loader: add devicepath support Ahmad Fatoum
@ 2025-12-18 10:37 ` Ahmad Fatoum
2025-12-18 10:37 ` [PATCH v1 18/54] efi: loader: add boot services support Ahmad Fatoum
` (36 subsequent siblings)
53 siblings, 0 replies; 55+ messages in thread
From: Ahmad Fatoum @ 2025-12-18 10:37 UTC (permalink / raw)
To: barebox; +Cc: Ahmad Fatoum
The EFI Debug Support Table provides an external debugger enough information
to determine loaded image information in a quiescent manner.
Add the necessary support to barebox, so the EFI boot time which will be
added in the follow-up commit can make use of it.
Signed-off-by: Ahmad Fatoum <a.fatoum@pengutronix.de>
---
efi/loader/Kconfig | 8 ++
efi/loader/Makefile | 1 +
efi/loader/debug_support.c | 192 +++++++++++++++++++++++++++++++++++++
include/efi/loader.h | 2 +
include/efi/loader/debug.h | 73 ++++++++++++++
include/efi/services.h | 16 ++++
6 files changed, 292 insertions(+)
create mode 100644 efi/loader/debug_support.c
create mode 100644 include/efi/loader/debug.h
diff --git a/efi/loader/Kconfig b/efi/loader/Kconfig
index e7080d8d5093..43a80869cf8b 100644
--- a/efi/loader/Kconfig
+++ b/efi/loader/Kconfig
@@ -1,3 +1,11 @@
# SPDX-License-Identifier: GPL-2.0-only
# SPDX-Comment: Origin-URL: https://github.com/u-boot/u-boot/blob/a0fe8cedcbe8c76403a77e57eac228b8f778a3ae/lib/efi_loader/Kconfig
+config EFI_LOADER_DEBUG_SUPPORT
+ bool "EFI Debug Support"
+ default y
+ help
+ Select this option if you want to setup the EFI Debug Support
+ Table and the EFI_SYSTEM_TABLE_POINTER which is used by the debug
+ agent or an external debugger to determine loaded image information
+ in a quiescent manner.
diff --git a/efi/loader/Makefile b/efi/loader/Makefile
index c635b5b6a533..593d456f0d39 100644
--- a/efi/loader/Makefile
+++ b/efi/loader/Makefile
@@ -4,3 +4,4 @@ obj-y += memory.o pool_alloc.o
obj-y += trace.o
obj-y += table.o
obj-y += devicepath.o
+obj-y += debug_support.o
diff --git a/efi/loader/debug_support.c b/efi/loader/debug_support.c
new file mode 100644
index 000000000000..8b5ee574bab6
--- /dev/null
+++ b/efi/loader/debug_support.c
@@ -0,0 +1,192 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+// SPDX-Comment: Origin-URL: https://github.com/u-boot/u-boot/blob/7e2506e3a247c787a9ad4b4db00bc8e6a348df90/lib/efi_loader/efi_debug_support.c
+/*
+ * EFI debug support
+ *
+ * Copyright (c) 2025 Ying-Chun Liu, Linaro Ltd. <paul.liu@linaro.org>
+ */
+
+#define pr_fmt(fmt) "efi-loader: debug: " fmt
+
+#include <linux/printk.h>
+#include <efi/loader.h>
+#include <efi/loader/debug.h>
+#include <efi/services.h>
+#include <efi/error.h>
+#include <linux/sizes.h>
+#include <crc.h>
+
+struct efi_system_table_pointer __efi_runtime_data *systab_pointer = NULL;
+
+struct efi_debug_image_info_table_header efi_m_debug_info_table_header = {
+ 0,
+ 0,
+ NULL
+};
+
+/* efi_m_max_table_entries is the maximum entries allocated for
+ * the efi_m_debug_info_table_header.efi_debug_image_info_table.
+ */
+static u32 efi_m_max_table_entries;
+
+#define EFI_DEBUG_TABLE_ENTRY_SIZE (sizeof(union efi_debug_image_info))
+
+/**
+ * efi_initialize_system_table_pointer() - Initialize system table pointer
+ *
+ * Return: status code
+ */
+efi_status_t efi_initialize_system_table_pointer(void)
+{
+ /* Allocate efi_system_table_pointer structure with 4MB alignment. */
+ systab_pointer = efi_alloc_aligned_pages(sizeof(struct efi_system_table_pointer),
+ EFI_RUNTIME_SERVICES_DATA,
+ SZ_4M, "debug");
+
+ if (!systab_pointer) {
+ pr_err("Installing EFI system table pointer failed\n");
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ systab_pointer->crc32 = 0;
+
+ systab_pointer->signature = EFI_SYSTEM_TABLE_SIGNATURE;
+ systab_pointer->efi_system_table_base = (uintptr_t)&systab;
+ systab_pointer->crc32 = crc32(0,
+ (const unsigned char *)systab_pointer,
+ sizeof(struct efi_system_table_pointer));
+
+ return EFI_SUCCESS;
+}
+
+/**
+ * efi_core_new_debug_image_info_entry() - Add a new efi_loaded_image structure to the
+ * efi_debug_image_info table.
+ *
+ * @image_info_type: type of debug image information
+ * @loaded_image: pointer to the loaded image protocol for the image
+ * being loaded
+ * @image_handle: image handle for the image being loaded
+ *
+ * Re-Allocates the table if it's not large enough to accommodate another
+ * entry.
+ *
+ * Return: status code
+ **/
+efi_status_t efi_core_new_debug_image_info_entry(u32 image_info_type,
+ struct efi_loaded_image *loaded_image,
+ efi_handle_t image_handle)
+{
+ union efi_debug_image_info **table;
+ u32 index;
+ u32 table_size;
+ efi_status_t ret;
+
+ /* Set the flag indicating that we're in the process of updating
+ * the table.
+ */
+ efi_m_debug_info_table_header.update_status |=
+ EFI_DEBUG_IMAGE_INFO_UPDATE_IN_PROGRESS;
+
+ table = &efi_m_debug_info_table_header.efi_debug_image_info_table;
+
+ if (efi_m_debug_info_table_header.table_size >= efi_m_max_table_entries) {
+ /* table is full, re-allocate the buffer increasing the size
+ * by 4 KiB.
+ */
+ table_size = efi_m_max_table_entries * EFI_DEBUG_TABLE_ENTRY_SIZE;
+
+ ret = efi_realloc((void **)table, table_size + EFI_PAGE_SIZE,
+ "debug_image");
+
+ if (ret != EFI_SUCCESS) {
+ efi_m_debug_info_table_header.update_status &=
+ ~EFI_DEBUG_IMAGE_INFO_UPDATE_IN_PROGRESS;
+ return ret;
+ }
+
+ /* Enlarge the max table entries and set the first empty
+ * entry index to be the original max table entries.
+ */
+ efi_m_max_table_entries +=
+ EFI_PAGE_SIZE / EFI_DEBUG_TABLE_ENTRY_SIZE;
+ }
+
+ /* We always put the next entry at the end of the currently consumed
+ * table (i.e. first free entry)
+ */
+ index = efi_m_debug_info_table_header.table_size;
+
+ /* Allocate data for new entry. */
+ ret = efi_allocate_pool(EFI_BOOT_SERVICES_DATA,
+ sizeof(union efi_debug_image_info),
+ (void **)(&(*table)[index].normal_image),
+ "debug_image");
+ if (ret == EFI_SUCCESS && (*table)[index].normal_image) {
+ /* Update the entry. */
+ (*table)[index].normal_image->image_info_type = image_info_type;
+ (*table)[index].normal_image->loaded_image_protocol_instance =
+ loaded_image;
+ (*table)[index].normal_image->image_handle = image_handle;
+
+ /* Increase the number of EFI_DEBUG_IMAGE_INFO elements and
+ * set the efi_m_debug_info_table_header in modified status.
+ */
+ efi_m_debug_info_table_header.table_size++;
+ efi_m_debug_info_table_header.update_status |=
+ EFI_DEBUG_IMAGE_INFO_TABLE_MODIFIED;
+ } else {
+ pr_err("Adding new efi_debug_image_info failed\n");
+ return ret;
+ }
+
+ efi_m_debug_info_table_header.update_status &=
+ ~EFI_DEBUG_IMAGE_INFO_UPDATE_IN_PROGRESS;
+
+ return EFI_SUCCESS;
+}
+
+/**
+ * efi_core_remove_debug_image_info_entry() - Remove an efi_debug_image_info entry.
+ *
+ * @image_handle: image handle for the image being removed
+ **/
+void efi_core_remove_debug_image_info_entry(efi_handle_t image_handle)
+{
+ union efi_debug_image_info *table;
+ u32 index;
+
+ efi_m_debug_info_table_header.update_status |=
+ EFI_DEBUG_IMAGE_INFO_UPDATE_IN_PROGRESS;
+
+ table = efi_m_debug_info_table_header.efi_debug_image_info_table;
+
+ for (index = 0; index < efi_m_max_table_entries; index++) {
+ if (table[index].normal_image &&
+ table[index].normal_image->image_handle == image_handle) {
+ /* Found a match. Free up the table entry.
+ * Move the tail of the table one slot to the front.
+ */
+ efi_free_pool(table[index].normal_image);
+
+ memmove(&table[index],
+ &table[index + 1],
+ (efi_m_debug_info_table_header.table_size -
+ index - 1) * EFI_DEBUG_TABLE_ENTRY_SIZE);
+
+ /* Decrease the number of EFI_DEBUG_IMAGE_INFO
+ * elements and set the efi_m_debug_info_table_header
+ * in modified status.
+ */
+ efi_m_debug_info_table_header.table_size--;
+ table[efi_m_debug_info_table_header.table_size].normal_image =
+ NULL;
+ efi_m_debug_info_table_header.update_status |=
+ EFI_DEBUG_IMAGE_INFO_TABLE_MODIFIED;
+ break;
+ }
+ }
+
+ efi_m_debug_info_table_header.update_status &=
+ ~EFI_DEBUG_IMAGE_INFO_UPDATE_IN_PROGRESS;
+}
diff --git a/include/efi/loader.h b/include/efi/loader.h
index 47fdcf5c5114..f2ca32c578a2 100644
--- a/include/efi/loader.h
+++ b/include/efi/loader.h
@@ -19,6 +19,8 @@ struct efi_table_hdr;
/* Key identifying current memory map */
extern efi_uintn_t efi_memory_map_key;
+extern struct efi_system_table systab;
+
/* Allocate boot service data pool memory */
void *efi_alloc(size_t len, const char *name);
/* Reallocate boot service data pool memory */
diff --git a/include/efi/loader/debug.h b/include/efi/loader/debug.h
new file mode 100644
index 000000000000..7b810c2218a7
--- /dev/null
+++ b/include/efi/loader/debug.h
@@ -0,0 +1,73 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/* SPDX-SnippetCopyrightText: 2016 Alexander Graf */
+
+#ifndef _EFI_LOADER_DEBUG_H
+#define _EFI_LOADER_DEBUG_H
+
+#include <efi/types.h>
+
+struct efi_loaded_image;
+
+/* Called by bootefi to initialize debug */
+efi_status_t efi_initialize_system_table_pointer(void);
+/* Called by efi_load_image for register debug info */
+efi_status_t efi_core_new_debug_image_info_entry(u32 image_info_type,
+ struct efi_loaded_image *loaded_image,
+ efi_handle_t image_handle);
+void efi_core_remove_debug_image_info_entry(efi_handle_t image_handle);
+
+#define EFI_DEBUG_IMAGE_INFO_UPDATE_IN_PROGRESS 0x01
+#define EFI_DEBUG_IMAGE_INFO_TABLE_MODIFIED 0x02
+
+#define EFI_DEBUG_IMAGE_INFO_TYPE_NORMAL 0x01
+
+/**
+ * struct efi_debug_image_info_normal - Store Debug Information for normal
+ * image.
+ * @image_info_type: the type of image info.
+ * @loaded_image_protocol_instance: the pointer to struct efi_loaded_image.
+ * @image_handle: the EFI handle of the image.
+ *
+ * This struct is created by efi_load_image() and store the information
+ * for debugging an normal image.
+ */
+struct efi_debug_image_info_normal {
+ u32 image_info_type;
+ struct efi_loaded_image *loaded_image_protocol_instance;
+ efi_handle_t image_handle;
+};
+
+/**
+ * union efi_debug_image_info - The union to store a pointer for EFI
+ * DEBUG IMAGE INFO.
+ * @image_info_type: the type of the image_info if it is not a normal image.
+ * @normal_image: The pointer to a normal image.
+ *
+ * This union is for a pointer that can point to the struct of normal_image.
+ * Or it points to an image_info_type.
+ */
+union efi_debug_image_info {
+ u32 *image_info_type;
+ struct efi_debug_image_info_normal *normal_image;
+};
+
+/**
+ * struct efi_debug_image_info_table_header - store the array of
+ * struct efi_debug_image_info.
+ * @update_status: Status to notify this struct is ready to use or not.
+ * @table_size: The number of elements of efi_debug_image_info_table.
+ * @efi_debug_image_info_table: The array of efi_debug_image_info.
+ *
+ * This struct stores the array of efi_debug_image_info. The
+ * number of elements is table_size.
+ */
+struct efi_debug_image_info_table_header {
+ volatile u32 update_status;
+ u32 table_size;
+ union efi_debug_image_info *efi_debug_image_info_table;
+};
+
+/* structure for EFI_DEBUG_SUPPORT_PROTOCOL */
+extern struct efi_debug_image_info_table_header efi_m_debug_info_table_header;
+
+#endif
diff --git a/include/efi/services.h b/include/efi/services.h
index c0dbac8743cc..f6fd268b2923 100644
--- a/include/efi/services.h
+++ b/include/efi/services.h
@@ -259,6 +259,22 @@ struct efi_loaded_image {
efi_status_t (EFIAPI *unload)(efi_handle_t image_handle);
};
+/**
+ * struct efi_system_table_pointer - struct to store the pointer of system
+ * table.
+ * @signature: The signature of this struct.
+ * @efi_system_table_base: The physical address of System Table.
+ * @crc32: CRC32 checksum
+ *
+ * This struct is design for hardware debugger to search through memory to
+ * get the address of EFI System Table.
+ */
+struct efi_system_table_pointer {
+ u64 signature;
+ efi_physical_addr_t efi_system_table_base;
+ u32 crc32;
+};
+
enum efi_locate_search_type;
int __efi_locate_handle(struct efi_boot_services *bs,
--
2.47.3
^ permalink raw reply [flat|nested] 55+ messages in thread* [PATCH v1 18/54] efi: loader: add boot services support
2025-12-18 10:37 [PATCH v1 00/54] efi: implement EFI loader support in barebox Ahmad Fatoum
` (16 preceding siblings ...)
2025-12-18 10:37 ` [PATCH v1 17/54] efi: loader: add debug support Ahmad Fatoum
@ 2025-12-18 10:37 ` Ahmad Fatoum
2025-12-18 10:37 ` [PATCH v1 19/54] efi: loader: add support for runtime services before ExitBootServices Ahmad Fatoum
` (35 subsequent siblings)
53 siblings, 0 replies; 55+ messages in thread
From: Ahmad Fatoum @ 2025-12-18 10:37 UTC (permalink / raw)
To: barebox; +Cc: Ahmad Fatoum
Provide implementation for the EFI object API introduced a few commits
back and wire it along with other boot time services as first step
towards barebox functioning as EFI firmware provider.
Signed-off-by: Ahmad Fatoum <a.fatoum@pengutronix.de>
---
efi/loader/Makefile | 1 +
efi/loader/boot.c | 3256 ++++++++++++++++++++++++++++++++++++
include/efi/loader.h | 4 +
include/efi/loader/event.h | 93 +
include/efi/loader/image.h | 18 +
include/efi/loader/table.h | 11 +
include/efi/services.h | 2 +
7 files changed, 3385 insertions(+)
create mode 100644 efi/loader/boot.c
create mode 100644 include/efi/loader/event.h
create mode 100644 include/efi/loader/image.h
create mode 100644 include/efi/loader/table.h
diff --git a/efi/loader/Makefile b/efi/loader/Makefile
index 593d456f0d39..a25960b477db 100644
--- a/efi/loader/Makefile
+++ b/efi/loader/Makefile
@@ -5,3 +5,4 @@ obj-y += trace.o
obj-y += table.o
obj-y += devicepath.o
obj-y += debug_support.o
+obj-y += boot.o
diff --git a/efi/loader/boot.c b/efi/loader/boot.c
new file mode 100644
index 000000000000..7d8e2914c3a0
--- /dev/null
+++ b/efi/loader/boot.c
@@ -0,0 +1,3256 @@
+// SPDX-License-Identifier: GPL-2.0+
+// SPDX-Comment: Origin-URL: https://github.com/u-boot/u-boot/blob/146546138af5966c97619797dc7f879c4857b00d/lib/efi_loader/efi_boottime.c
+/*
+ * EFI application boot time services
+ *
+ * Copyright (c) 2016 Alexander Graf
+ */
+
+#define pr_fmt(fmt) "efi-loader: boot: " fmt
+
+#include <bootm.h>
+#include <mmu.h>
+#include <memory.h>
+#include <barebox.h>
+#include <linux/math64.h>
+#include <efi/loader.h>
+#include <efi/loader/event.h>
+#include <efi/loader/object.h>
+#include <efi/loader/table.h>
+#include <efi/loader/devicepath.h>
+#include <efi/loader/debug.h>
+#include <efi/loader/image.h>
+#include <efi/protocol/file.h>
+#include <efi/guid.h>
+#include <efi/error.h>
+#include <efi/variable.h>
+#include <efi/devicepath.h>
+#include <efi/loader/trace.h>
+#include <malloc.h>
+#include <pe.h>
+#include <asm/cache.h>
+#include <clock.h>
+#include <asm/setjmp.h>
+#include <sched.h>
+#include <linux/list.h>
+#include <linux/ktime.h>
+#include <generated/version.h>
+#include <crc.h>
+
+#define EFI_LOADED_IMAGE_PROTOCOL_REVISION 0x1000
+#define EFI_MAX_CONFIGURATION_TABLES 16
+
+/* Task priority level */
+static efi_uintn_t efi_tpl = EFI_TPL_APPLICATION;
+
+/* This list contains all the EFI objects our payload has access to */
+LIST_HEAD(efi_obj_list);
+
+/* List of all events */
+LIST_HEAD(efi_events);
+
+/* List of queued events */
+static LIST_HEAD(efi_event_queue);
+
+/* Flag to disable timer activity in ExitBootServices() */
+static bool timers_enabled = true;
+
+/* List of all events registered by RegisterProtocolNotify() */
+static LIST_HEAD(efi_register_notify_events);
+
+efi_status_t efi_uninstall_protocol
+ (efi_handle_t handle, const efi_guid_t *protocol,
+ void *protocol_interface, bool preserve);
+
+efi_status_t EFIAPI efi_disconnect_controller(
+ efi_handle_t controller_handle,
+ efi_handle_t driver_image_handle,
+ efi_handle_t child_handle);
+
+efi_status_t EFIAPI efi_connect_controller(efi_handle_t controller_handle,
+ efi_handle_t *driver_image_handle,
+ struct efi_device_path *remain_device_path,
+ bool recursive);
+
+/**
+ * efi_event_is_queued() - check if an event is queued
+ *
+ * @event: event
+ * Return: true if event is queued
+ */
+static bool efi_event_is_queued(struct efi_event *event)
+{
+ return !!event->queue_link.next;
+}
+
+/**
+ * efi_purge_handle() - Clean the deleted handle from the various lists
+ * @handle: handle to remove
+ *
+ * Return: status code
+ */
+static efi_status_t efi_purge_handle(efi_handle_t handle)
+{
+ struct efi_register_notify_event *item;
+
+ if (!list_empty(&handle->protocols))
+ return EFI_ACCESS_DENIED;
+ /* The handle is about to be freed. Remove it from events */
+ list_for_each_entry(item, &efi_register_notify_events, link) {
+ struct efi_protocol_notification *hitem, *hnext;
+
+ list_for_each_entry_safe(hitem, hnext, &item->handles, link) {
+ if (handle == hitem->handle) {
+ list_del(&hitem->link);
+ free(hitem);
+ }
+ }
+ }
+ /* The last protocol has been removed, delete the handle. */
+ list_del(&handle->link);
+ free(handle);
+
+ return EFI_SUCCESS;
+}
+
+/**
+ * efi_process_event_queue() - process event queue
+ */
+static void efi_process_event_queue(void)
+{
+ while (!list_empty(&efi_event_queue)) {
+ struct efi_event *event;
+ efi_uintn_t old_tpl;
+
+ event = list_first_entry(&efi_event_queue, struct efi_event,
+ queue_link);
+ if (efi_tpl >= event->notify_tpl)
+ return;
+ list_del(&event->queue_link);
+ event->queue_link.next = NULL;
+ event->queue_link.prev = NULL;
+ /* Events must be executed at the event's TPL */
+ old_tpl = efi_tpl;
+ efi_tpl = event->notify_tpl;
+ event->notify_function(event,
+ event->notify_context);
+ efi_tpl = old_tpl;
+ if (event->type == EFI_EVT_NOTIFY_SIGNAL)
+ event->is_signaled = 0;
+ }
+}
+
+/**
+ * efi_queue_event() - queue an EFI event
+ * @event: event to signal
+ *
+ * This function queues the notification function of the event for future
+ * execution.
+ *
+ */
+static void efi_queue_event(struct efi_event *event)
+{
+ struct efi_event *item;
+
+ if (!event->notify_function)
+ return;
+
+ if (!efi_event_is_queued(event)) {
+ /*
+ * Events must be notified in order of decreasing task priority
+ * level. Insert the new event accordingly.
+ */
+ list_for_each_entry(item, &efi_event_queue, queue_link) {
+ if (item->notify_tpl < event->notify_tpl) {
+ list_add_tail(&event->queue_link,
+ &item->queue_link);
+ event = NULL;
+ break;
+ }
+ }
+ if (event)
+ list_add_tail(&event->queue_link, &efi_event_queue);
+ efi_process_event_queue();
+ }
+}
+
+/**
+ * is_valid_tpl() - check if the task priority level is valid
+ *
+ * @tpl: TPL level to check
+ * Return: status code
+ */
+static efi_status_t is_valid_tpl(efi_uintn_t tpl)
+{
+ switch (tpl) {
+ case EFI_TPL_APPLICATION:
+ case EFI_TPL_CALLBACK:
+ case EFI_TPL_NOTIFY:
+ return EFI_SUCCESS;
+ default:
+ return EFI_INVALID_PARAMETER;
+ }
+}
+
+/**
+ * efi_signal_event() - signal an EFI event
+ * @event: event to signal
+ *
+ * This function signals an event. If the event belongs to an event group, all
+ * events of the group are signaled. If they are of type EFI_EVT_NOTIFY_SIGNAL,
+ * their notification function is queued.
+ *
+ * For the SignalEvent service see efi_signal_event_ext.
+ */
+void efi_signal_event(struct efi_event *event)
+{
+ if (event->is_signaled)
+ return;
+ if (event->group) {
+ struct efi_event *evt;
+
+ /*
+ * The signaled state has to set before executing any
+ * notification function
+ */
+ list_for_each_entry(evt, &efi_events, link) {
+ if (!evt->group || efi_guidcmp(*evt->group, *event->group))
+ continue;
+ if (evt->is_signaled)
+ continue;
+ evt->is_signaled = true;
+ }
+ list_for_each_entry(evt, &efi_events, link) {
+ if (!evt->group || efi_guidcmp(*evt->group, *event->group))
+ continue;
+ efi_queue_event(evt);
+ }
+ } else {
+ event->is_signaled = true;
+ efi_queue_event(event);
+ }
+}
+
+/**
+ * efi_raise_tpl() - raise the task priority level
+ * @new_tpl: new value of the task priority level
+ *
+ * This function implements the RaiseTpl service.
+ *
+ * See the Unified Extensible Firmware Interface (UEFI) specification for
+ * details.
+ *
+ * Return: old value of the task priority level
+ */
+static unsigned long EFIAPI efi_raise_tpl(efi_uintn_t new_tpl)
+{
+ efi_uintn_t old_tpl = efi_tpl;
+
+ EFI_ENTRY("0x%zx", new_tpl);
+
+ if (new_tpl < efi_tpl)
+ EFI_WARN("new_tpl < current_tpl in %s\n", __func__);
+ efi_tpl = new_tpl;
+ if (efi_tpl > EFI_TPL_HIGH_LEVEL)
+ efi_tpl = EFI_TPL_HIGH_LEVEL;
+
+ EFI_EXIT(EFI_SUCCESS);
+ return old_tpl;
+}
+
+/**
+ * efi_restore_tpl() - lower the task priority level
+ * @old_tpl: value of the task priority level to be restored
+ *
+ * This function implements the RestoreTpl service.
+ *
+ * See the Unified Extensible Firmware Interface (UEFI) specification for
+ * details.
+ */
+static void EFIAPI efi_restore_tpl(efi_uintn_t old_tpl)
+{
+ EFI_ENTRY("0x%zx", old_tpl);
+
+ if (old_tpl > efi_tpl)
+ EFI_WARN("old_tpl > current_tpl in %s\n", __func__);
+ efi_tpl = old_tpl;
+ if (efi_tpl > EFI_TPL_HIGH_LEVEL)
+ efi_tpl = EFI_TPL_HIGH_LEVEL;
+
+ /*
+ * Lowering the TPL may have made queued events eligible for execution.
+ */
+ efi_timer_check();
+
+ EFI_EXIT(EFI_SUCCESS);
+}
+
+/**
+ * efi_allocate_pages_ext() - allocate memory pages
+ * @type: type of allocation to be performed
+ * @memory_type: usage type of the allocated memory
+ * @pages: number of pages to be allocated
+ * @memory: allocated memory
+ *
+ * This function implements the AllocatePages service.
+ *
+ * See the Unified Extensible Firmware Interface (UEFI) specification for
+ * details.
+ *
+ * Return: status code
+ */
+static efi_status_t EFIAPI efi_allocate_pages_ext(int type, int memory_type,
+ efi_uintn_t pages,
+ uint64_t *memory)
+{
+ efi_status_t r;
+
+ EFI_ENTRY("%d, %d, 0x%zx, %llx", type, memory_type, pages, *memory);
+ r = efi_allocate_pages(type, memory_type, pages, memory, "API");
+ return EFI_EXIT2(r, *memory);
+}
+
+/**
+ * efi_free_pages_ext() - Free memory pages.
+ * @memory: start of the memory area to be freed
+ * @pages: number of pages to be freed
+ *
+ * This function implements the FreePages service.
+ *
+ * See the Unified Extensible Firmware Interface (UEFI) specification for
+ * details.
+ *
+ * Return: status code
+ */
+static efi_status_t EFIAPI efi_free_pages_ext(uint64_t memory,
+ efi_uintn_t pages)
+{
+ efi_status_t r;
+
+ EFI_ENTRY("%llx, 0x%zx", memory, pages);
+ r = efi_free_pages(memory, pages);
+ return EFI_EXIT(r);
+}
+
+/**
+ * efi_get_memory_map_ext() - get map describing memory usage
+ * @memory_map_size: on entry the size, in bytes, of the memory map buffer,
+ * on exit the size of the copied memory map
+ * @memory_map: buffer to which the memory map is written
+ * @map_key: key for the memory map
+ * @descriptor_size: size of an individual memory descriptor
+ * @descriptor_version: version number of the memory descriptor structure
+ *
+ * This function implements the GetMemoryMap service.
+ *
+ * See the Unified Extensible Firmware Interface (UEFI) specification for
+ * details.
+ *
+ * Return: status code
+ */
+static efi_status_t EFIAPI efi_get_memory_map_ext(
+ efi_uintn_t *memory_map_size,
+ struct efi_memory_desc *memory_map,
+ efi_uintn_t *map_key,
+ efi_uintn_t *descriptor_size,
+ uint32_t *descriptor_version)
+{
+ efi_status_t r;
+
+ EFI_ENTRY("%zu@%p, %p, %p, %p, %p",
+ *memory_map_size, memory_map_size, memory_map,
+ map_key, descriptor_size, descriptor_version);
+ r = efi_get_memory_map(memory_map_size, memory_map, map_key,
+ descriptor_size, descriptor_version);
+
+ __EFI_PRINT("%sEFI: Exit: %s(%zu@%p, %p, 0x%lx@%p, %zu@%p, %u@%p): %u\n",
+ __efi_nesting_dec(), __func__,
+ *memory_map_size, memory_map_size, memory_map,
+ *map_key, map_key, *descriptor_size, descriptor_size,
+ *descriptor_version, descriptor_version,
+ (u32)((uintptr_t) r & ~EFI_ERROR_MASK));
+
+ return r;
+}
+
+/**
+ * efi_allocate_pool_ext() - allocate memory from pool
+ * @pool_type: type of the pool from which memory is to be allocated
+ * @size: number of bytes to be allocated
+ * @buffer: allocated memory
+ *
+ * This function implements the AllocatePool service.
+ *
+ * See the Unified Extensible Firmware Interface (UEFI) specification for
+ * details.
+ *
+ * Return: status code
+ */
+static efi_status_t EFIAPI efi_allocate_pool_ext(int pool_type,
+ efi_uintn_t size,
+ void **buffer)
+{
+ efi_status_t r;
+
+ EFI_ENTRY("%d, %zd, %p", pool_type, size, buffer);
+ r = efi_allocate_pool(pool_type, size, buffer, "API");
+ return EFI_EXIT2(r, *buffer);
+}
+
+/**
+ * efi_free_pool_ext() - free memory from pool
+ * @buffer: start of memory to be freed
+ *
+ * This function implements the FreePool service.
+ *
+ * See the Unified Extensible Firmware Interface (UEFI) specification for
+ * details.
+ *
+ * Return: status code
+ */
+static efi_status_t EFIAPI efi_free_pool_ext(void *buffer)
+{
+ efi_status_t r;
+
+ EFI_ENTRY("%p", buffer);
+ r = efi_free_pool(buffer);
+ return EFI_EXIT(r);
+}
+
+/**
+ * efi_add_handle() - add a new handle to the object list
+ *
+ * @handle: handle to be added
+ *
+ * The protocols list is initialized. The handle is added to the list of known
+ * UEFI objects.
+ */
+void efi_add_handle(efi_handle_t handle)
+{
+ if (!handle)
+ return;
+ INIT_LIST_HEAD(&handle->protocols);
+ list_add_tail(&handle->link, &efi_obj_list);
+}
+
+/**
+ * efi_create_handle() - create handle
+ * @handle: new handle
+ *
+ * Return: status code
+ */
+efi_status_t efi_create_handle(efi_handle_t *handle)
+{
+ struct efi_object *obj;
+
+ obj = calloc(1, sizeof(struct efi_object));
+ if (!obj)
+ return EFI_OUT_OF_RESOURCES;
+
+ efi_add_handle(obj);
+ *handle = obj;
+
+ return EFI_SUCCESS;
+}
+
+/**
+ * efi_search_protocol() - find a protocol on a handle.
+ * @handle: handle
+ * @protocol_guid: GUID of the protocol
+ * @handler: reference to the protocol
+ *
+ * Return: status code
+ */
+efi_status_t efi_search_protocol(const efi_handle_t handle,
+ const efi_guid_t *protocol_guid,
+ struct efi_handler **handler)
+{
+ struct efi_object *efiobj;
+ struct list_head *lhandle;
+
+ if (!handle || !protocol_guid)
+ return EFI_INVALID_PARAMETER;
+ efiobj = efi_search_obj(handle);
+ if (!efiobj)
+ return EFI_INVALID_PARAMETER;
+ list_for_each(lhandle, &efiobj->protocols) {
+ struct efi_handler *protocol;
+
+ protocol = list_entry(lhandle, struct efi_handler, link);
+ if (!efi_guidcmp(protocol->guid, *protocol_guid)) {
+ if (handler)
+ *handler = protocol;
+ return EFI_SUCCESS;
+ }
+ }
+ return EFI_NOT_FOUND;
+}
+
+/**
+ * efi_remove_protocol() - delete protocol from a handle
+ * @handle: handle from which the protocol shall be deleted
+ * @protocol: GUID of the protocol to be deleted
+ * @protocol_interface: interface of the protocol implementation
+ *
+ * Return: status code
+ */
+static efi_status_t efi_remove_protocol(const efi_handle_t handle,
+ const efi_guid_t *protocol,
+ void *protocol_interface)
+{
+ struct efi_handler *handler;
+ efi_status_t ret;
+
+ ret = efi_search_protocol(handle, protocol, &handler);
+ if (ret != EFI_SUCCESS)
+ return ret;
+ if (handler->protocol_interface != protocol_interface)
+ return EFI_NOT_FOUND;
+ list_del(&handler->link);
+ free(handler);
+ return EFI_SUCCESS;
+}
+
+/**
+ * efi_remove_all_protocols() - delete all protocols from a handle
+ * @handle: handle from which the protocols shall be deleted
+ *
+ * Return: status code
+ */
+static efi_status_t efi_remove_all_protocols(const efi_handle_t handle)
+{
+ struct efi_object *efiobj;
+ struct efi_handler *protocol;
+ struct efi_handler *pos;
+
+ efiobj = efi_search_obj(handle);
+ if (!efiobj)
+ return EFI_INVALID_PARAMETER;
+ list_for_each_entry_safe(protocol, pos, &efiobj->protocols, link) {
+ efi_status_t ret;
+
+ ret = efi_uninstall_protocol(handle, &protocol->guid,
+ protocol->protocol_interface, true);
+ if (ret != EFI_SUCCESS)
+ return ret;
+ }
+ return EFI_SUCCESS;
+}
+
+/**
+ * efi_delete_handle() - delete handle
+ *
+ * @handle: handle to delete
+ *
+ * Return: status code
+ */
+efi_status_t efi_delete_handle(efi_handle_t handle)
+{
+ efi_status_t ret;
+
+ ret = efi_remove_all_protocols(handle);
+ if (ret != EFI_SUCCESS) {
+ pr_err("Handle %p has protocols installed. Unable to delete\n", handle);
+ return ret;
+ }
+
+ return efi_purge_handle(handle);
+}
+
+/**
+ * efi_is_event() - check if a pointer is a valid event
+ * @event: pointer to check
+ *
+ * Return: status code
+ */
+static efi_status_t efi_is_event(const struct efi_event *event)
+{
+ const struct efi_event *evt;
+
+ if (!event)
+ return EFI_INVALID_PARAMETER;
+ list_for_each_entry(evt, &efi_events, link) {
+ if (evt == event)
+ return EFI_SUCCESS;
+ }
+ return EFI_INVALID_PARAMETER;
+}
+
+/**
+ * efi_create_event() - create an event
+ *
+ * @type: type of the event to create
+ * @notify_tpl: task priority level of the event
+ * @notify_function: notification function of the event
+ * @notify_context: pointer passed to the notification function
+ * @group: event group
+ * @event: created event
+ *
+ * This function is used inside barebox code to create an event.
+ *
+ * For the API function implementing the CreateEvent service see
+ * efi_create_event_ext.
+ *
+ * Return: status code
+ */
+efi_status_t efi_create_event(uint32_t type, efi_uintn_t notify_tpl,
+ void (EFIAPI *notify_function) (
+ struct efi_event *event,
+ void *context),
+ void *notify_context, const efi_guid_t *group,
+ struct efi_event **event)
+{
+ struct efi_event *evt;
+ efi_status_t ret;
+ int pool_type;
+
+ if (event == NULL)
+ return EFI_INVALID_PARAMETER;
+
+ switch (type) {
+ case 0:
+ case EFI_EVT_TIMER:
+ case EFI_EVT_NOTIFY_SIGNAL:
+ case EFI_EVT_TIMER | EFI_EVT_NOTIFY_SIGNAL:
+ case EFI_EVT_NOTIFY_WAIT:
+ case EFI_EVT_TIMER | EFI_EVT_NOTIFY_WAIT:
+ case EFI_EVT_SIGNAL_EXIT_BOOT_SERVICES:
+ pool_type = EFI_BOOT_SERVICES_DATA;
+ break;
+ case EFI_EVT_SIGNAL_VIRTUAL_ADDRESS_CHANGE:
+ pool_type = EFI_RUNTIME_SERVICES_DATA;
+ break;
+ default:
+ return EFI_INVALID_PARAMETER;
+ }
+
+ /*
+ * The UEFI specification requires event notification levels to be
+ * > EFI_TPL_APPLICATION and <= EFI_TPL_HIGH_LEVEL.
+ *
+ * Parameter NotifyTpl should not be checked if it is not used.
+ */
+ if ((type & (EFI_EVT_NOTIFY_WAIT | EFI_EVT_NOTIFY_SIGNAL)) &&
+ (!notify_function || is_valid_tpl(notify_tpl) != EFI_SUCCESS ||
+ notify_tpl == EFI_TPL_APPLICATION))
+ return EFI_INVALID_PARAMETER;
+
+ ret = efi_allocate_pool(pool_type, sizeof(struct efi_event),
+ (void **)&evt, "Event");
+ if (ret != EFI_SUCCESS)
+ return ret;
+ memset(evt, 0, sizeof(struct efi_event));
+ evt->type = type;
+ evt->notify_tpl = notify_tpl;
+ evt->notify_function = notify_function;
+ evt->notify_context = notify_context;
+ evt->group = group;
+ /* Disable timers on boot up */
+ evt->trigger_next = -1ULL;
+ list_add_tail(&evt->link, &efi_events);
+ *event = evt;
+ return EFI_SUCCESS;
+}
+
+/*
+ * efi_create_event_ex() - create an event in a group
+ *
+ * @type: type of the event to create
+ * @notify_tpl: task priority level of the event
+ * @notify_function: notification function of the event
+ * @notify_context: pointer passed to the notification function
+ * @event: created event
+ * @event_group: event group
+ *
+ * This function implements the CreateEventEx service.
+ *
+ * See the Unified Extensible Firmware Interface (UEFI) specification for
+ * details.
+ *
+ * Return: status code
+ */
+static
+efi_status_t EFIAPI efi_create_event_ex(uint32_t type, efi_uintn_t notify_tpl,
+ void (EFIAPI *notify_function) (
+ struct efi_event *event,
+ void *context),
+ void *notify_context,
+ efi_guid_t *event_group,
+ struct efi_event **event)
+{
+ efi_status_t ret;
+
+ EFI_ENTRY("%d, 0x%zx, %p, %p, %pUs", type, notify_tpl, notify_function,
+ notify_context, event_group);
+
+ /*
+ * The allowable input parameters are the same as in CreateEvent()
+ * except for the following two disallowed event types.
+ */
+ switch (type) {
+ case EFI_EVT_SIGNAL_EXIT_BOOT_SERVICES:
+ case EFI_EVT_SIGNAL_VIRTUAL_ADDRESS_CHANGE:
+ ret = EFI_INVALID_PARAMETER;
+ goto out;
+ }
+
+ ret = efi_create_event(type, notify_tpl, notify_function,
+ notify_context, event_group, event);
+out:
+ return EFI_EXIT2(ret, *event);
+}
+
+/**
+ * efi_create_event_ext() - create an event
+ * @type: type of the event to create
+ * @notify_tpl: task priority level of the event
+ * @notify_function: notification function of the event
+ * @notify_context: pointer passed to the notification function
+ * @event: created event
+ *
+ * This function implements the CreateEvent service.
+ *
+ * See the Unified Extensible Firmware Interface (UEFI) specification for
+ * details.
+ *
+ * Return: status code
+ */
+static efi_status_t EFIAPI efi_create_event_ext(
+ uint32_t type, efi_uintn_t notify_tpl,
+ void (EFIAPI *notify_function) (
+ struct efi_event *event,
+ void *context),
+ void *notify_context, struct efi_event **event)
+{
+ EFI_ENTRY("%d, 0x%lu, %p, %p", type, notify_tpl, notify_function,
+ notify_context);
+ return EFI_EXIT2(efi_create_event(type, notify_tpl, notify_function,
+ notify_context, NULL, event), *event);
+}
+
+/**
+ * efi_timer_check() - check if a timer event has occurred
+ *
+ * Check if a timer event has occurred or a queued notification function should
+ * be called.
+ *
+ * Our timers have to work without interrupts, so we check whenever keyboard
+ * input or disk accesses happen if enough time elapsed for them to fire.
+ * TODO: run in delay loops?
+ */
+void efi_timer_check(void)
+{
+ struct efi_event *evt;
+ u64 now = get_time_ns();
+
+ list_for_each_entry(evt, &efi_events, link) {
+ if (!timers_enabled)
+ continue;
+ if (!(evt->type & EFI_EVT_TIMER) || now < evt->trigger_next)
+ continue;
+ switch (evt->trigger_type) {
+ case EFI_TIMER_RELATIVE:
+ evt->trigger_type = EFI_TIMER_CANCEL;
+ break;
+ case EFI_TIMER_PERIODIC:
+ evt->trigger_next += evt->trigger_time;
+ break;
+ default:
+ continue;
+ }
+ evt->is_signaled = false;
+ efi_signal_event(evt);
+ }
+ efi_process_event_queue();
+ resched();
+}
+
+/**
+ * efi_set_timer() - set the trigger time for a timer event or stop the event
+ * @event: event for which the timer is set
+ * @type: type of the timer
+ * @trigger_time: trigger period in multiples of 100 ns
+ *
+ * This is the function for internal usage in barebox. For the API function
+ * implementing the SetTimer service see efi_set_timer_ext.
+ *
+ * Return: status code
+ */
+efi_status_t efi_set_timer(struct efi_event *event, enum efi_timer_delay type,
+ uint64_t trigger_time)
+{
+ /* Check that the event is valid */
+ if (efi_is_event(event) != EFI_SUCCESS || !(event->type & EFI_EVT_TIMER))
+ return EFI_INVALID_PARAMETER;
+
+ /*
+ * The parameter defines a multiple of 100 ns.
+ * We use a granularity of 1 ns. So multiply by 100.
+ */
+ trigger_time *= 100;
+
+ switch (type) {
+ case EFI_TIMER_CANCEL:
+ event->trigger_next = -1ULL;
+ break;
+ case EFI_TIMER_PERIODIC:
+ case EFI_TIMER_RELATIVE:
+ event->trigger_next = get_time_ns() + trigger_time;
+ break;
+ default:
+ return EFI_INVALID_PARAMETER;
+ }
+ event->trigger_type = type;
+ event->trigger_time = trigger_time;
+ event->is_signaled = false;
+ return EFI_SUCCESS;
+}
+
+/**
+ * efi_set_timer_ext() - Set the trigger time for a timer event or stop the
+ * event
+ * @event: event for which the timer is set
+ * @type: type of the timer
+ * @trigger_time: trigger period in multiples of 100 ns
+ *
+ * This function implements the SetTimer service.
+ *
+ * See the Unified Extensible Firmware Interface (UEFI) specification for
+ * details.
+ *
+ *
+ * Return: status code
+ */
+static efi_status_t EFIAPI efi_set_timer_ext(struct efi_event *event,
+ enum efi_timer_delay type,
+ uint64_t trigger_time)
+{
+ EFI_ENTRY("%p, %d, %llx", event, type, trigger_time);
+ return EFI_EXIT(efi_set_timer(event, type, trigger_time));
+}
+
+/**
+ * efi_wait_for_event() - wait for events to be signaled
+ * @num_events: number of events to be waited for
+ * @event: events to be waited for
+ * @index: index of the event that was signaled
+ *
+ * This function implements the WaitForEvent service.
+ *
+ * See the Unified Extensible Firmware Interface (UEFI) specification for
+ * details.
+ *
+ * Return: status code
+ */
+static efi_status_t EFIAPI efi_wait_for_event(efi_uintn_t num_events,
+ struct efi_event **event,
+ efi_uintn_t *index)
+{
+ int i;
+
+ EFI_ENTRY("%zu, %p, %p", num_events, event, index);
+
+ /* Check parameters */
+ if (!num_events || !event)
+ return EFI_EXIT(EFI_INVALID_PARAMETER);
+ /* Check TPL */
+ if (efi_tpl != EFI_TPL_APPLICATION)
+ return EFI_EXIT(EFI_UNSUPPORTED);
+ for (i = 0; i < num_events; ++i) {
+ if (efi_is_event(event[i]) != EFI_SUCCESS)
+ return EFI_EXIT(EFI_INVALID_PARAMETER);
+ if (!event[i]->type || event[i]->type & EFI_EVT_NOTIFY_SIGNAL)
+ return EFI_EXIT(EFI_INVALID_PARAMETER);
+ if (!event[i]->is_signaled)
+ efi_queue_event(event[i]);
+ }
+
+ /* Wait for signal */
+ for (;;) {
+ for (i = 0; i < num_events; ++i) {
+ if (event[i]->is_signaled)
+ goto out;
+ }
+ /* Allow events to occur. */
+ efi_timer_check();
+ }
+
+out:
+ /*
+ * Reset the signal which is passed to the caller to allow periodic
+ * events to occur.
+ */
+ event[i]->is_signaled = false;
+ if (index)
+ *index = i;
+
+ return EFI_EXIT2(EFI_SUCCESS, *index);
+}
+
+/**
+ * efi_signal_event_ext() - signal an EFI event
+ * @event: event to signal
+ *
+ * This function implements the SignalEvent service.
+ *
+ * See the Unified Extensible Firmware Interface (UEFI) specification for
+ * details.
+ *
+ * This functions sets the signaled state of the event and queues the
+ * notification function for execution.
+ *
+ * Return: status code
+ */
+static efi_status_t EFIAPI efi_signal_event_ext(struct efi_event *event)
+{
+ EFI_ENTRY("%p", event);
+ if (efi_is_event(event) != EFI_SUCCESS)
+ return EFI_EXIT(EFI_INVALID_PARAMETER);
+ efi_signal_event(event);
+ return EFI_EXIT(EFI_SUCCESS);
+}
+
+/**
+ * efi_close_event() - close an EFI event
+ * @event: event to close
+ *
+ * This function implements the CloseEvent service.
+ *
+ * See the Unified Extensible Firmware Interface (UEFI) specification for
+ * details.
+ *
+ * Return: status code
+ */
+efi_status_t EFIAPI efi_close_event(struct efi_event *event)
+{
+ struct efi_register_notify_event *item, *next;
+
+ EFI_ENTRY("%p", event);
+ if (efi_is_event(event) != EFI_SUCCESS)
+ return EFI_EXIT(EFI_INVALID_PARAMETER);
+
+ /* Remove protocol notify registrations for the event */
+ list_for_each_entry_safe(item, next, &efi_register_notify_events,
+ link) {
+ if (event == item->event) {
+ struct efi_protocol_notification *hitem, *hnext;
+
+ /* Remove signaled handles */
+ list_for_each_entry_safe(hitem, hnext, &item->handles,
+ link) {
+ list_del(&hitem->link);
+ free(hitem);
+ }
+ list_del(&item->link);
+ free(item);
+ }
+ }
+ /* Remove event from queue */
+ if (efi_event_is_queued(event))
+ list_del(&event->queue_link);
+
+ list_del(&event->link);
+ efi_free_pool(event);
+ return EFI_EXIT(EFI_SUCCESS);
+}
+
+/**
+ * efi_check_event() - check if an event is signaled
+ * @event: event to check
+ *
+ * This function implements the CheckEvent service.
+ *
+ * See the Unified Extensible Firmware Interface (UEFI) specification for
+ * details.
+ *
+ * If an event is not signaled yet, the notification function is queued. The
+ * signaled state is cleared.
+ *
+ * Return: status code
+ */
+static efi_status_t EFIAPI efi_check_event(struct efi_event *event)
+{
+ EFI_ENTRY("%p", event);
+ efi_timer_check();
+ if (efi_is_event(event) != EFI_SUCCESS ||
+ event->type & EFI_EVT_NOTIFY_SIGNAL)
+ return EFI_EXIT(EFI_INVALID_PARAMETER);
+ if (!event->is_signaled)
+ efi_queue_event(event);
+ if (event->is_signaled) {
+ event->is_signaled = false;
+ return EFI_EXIT(EFI_SUCCESS);
+ }
+ return EFI_EXIT(EFI_NOT_READY);
+}
+
+/**
+ * efi_search_obj() - find the internal EFI object for a handle
+ * @handle: handle to find
+ *
+ * Return: EFI object
+ */
+struct efi_object *efi_search_obj(const efi_handle_t handle)
+{
+ struct efi_object *efiobj;
+
+ if (!handle)
+ return NULL;
+
+ list_for_each_entry(efiobj, &efi_obj_list, link) {
+ if (efiobj == handle)
+ return efiobj;
+ }
+ return NULL;
+}
+
+/**
+ * efi_open_protocol_info_entry() - create open protocol info entry and add it
+ * to a protocol
+ * @handler: handler of a protocol
+ *
+ * Return: open protocol info entry
+ */
+static struct efi_open_protocol_information_entry *efi_create_open_info(
+ struct efi_handler *handler)
+{
+ struct efi_open_protocol_info_item *item;
+
+ item = calloc(1, sizeof(struct efi_open_protocol_info_item));
+ if (!item)
+ return NULL;
+ /* Append the item to the open protocol info list. */
+ list_add_tail(&item->link, &handler->open_infos);
+
+ return &item->info;
+}
+
+/**
+ * efi_delete_open_info() - remove an open protocol info entry from a protocol
+ * @item: open protocol info entry to delete
+ *
+ * Return: status code
+ */
+static efi_status_t efi_delete_open_info(
+ struct efi_open_protocol_info_item *item)
+{
+ list_del(&item->link);
+ free(item);
+ return EFI_SUCCESS;
+}
+
+/**
+ * efi_add_protocol() - install new protocol on a handle
+ * @handle: handle on which the protocol shall be installed
+ * @protocol: GUID of the protocol to be installed
+ * @protocol_interface: interface of the protocol implementation
+ *
+ * Return: status code
+ */
+efi_status_t efi_add_protocol(const efi_handle_t handle,
+ const efi_guid_t *protocol,
+ const void *protocol_interface)
+{
+ struct efi_object *efiobj;
+ struct efi_handler *handler;
+ efi_status_t ret;
+ struct efi_register_notify_event *event;
+
+ efiobj = efi_search_obj(handle);
+ if (!efiobj)
+ return EFI_INVALID_PARAMETER;
+ ret = efi_search_protocol(handle, protocol, NULL);
+ if (ret != EFI_NOT_FOUND)
+ return EFI_INVALID_PARAMETER;
+ handler = calloc(1, sizeof(struct efi_handler));
+ if (!handler)
+ return EFI_OUT_OF_RESOURCES;
+
+ handler->guid = *protocol;
+ handler->protocol_interface = (void *)protocol_interface;
+ INIT_LIST_HEAD(&handler->open_infos);
+ list_add_tail(&handler->link, &efiobj->protocols);
+
+ /* Notify registered events */
+ list_for_each_entry(event, &efi_register_notify_events, link) {
+ if (!efi_guidcmp(*protocol, event->protocol)) {
+ struct efi_protocol_notification *notif;
+
+ notif = calloc(1, sizeof(*notif));
+ if (!notif) {
+ list_del(&handler->link);
+ free(handler);
+ return EFI_OUT_OF_RESOURCES;
+ }
+ notif->handle = handle;
+ list_add_tail(¬if->link, &event->handles);
+ event->event->is_signaled = false;
+ efi_signal_event(event->event);
+ }
+ }
+
+ if (!efi_guidcmp(efi_device_path_protocol_guid, *protocol))
+ EFI_PRINT("installed device path '%pD'\n", protocol_interface);
+ return EFI_SUCCESS;
+}
+
+/**
+ * efi_install_protocol_interface() - install protocol interface
+ * @handle: handle on which the protocol shall be installed
+ * @protocol: GUID of the protocol to be installed
+ * @protocol_interface_type: type of the interface to be installed,
+ * always EFI_NATIVE_INTERFACE
+ * @protocol_interface: interface of the protocol implementation
+ *
+ * This function implements the InstallProtocolInterface service.
+ *
+ * See the Unified Extensible Firmware Interface (UEFI) specification for
+ * details.
+ *
+ * Return: status code
+ */
+static efi_status_t EFIAPI efi_install_protocol_interface(
+ efi_handle_t *handle, const efi_guid_t *protocol,
+ int protocol_interface_type, void *protocol_interface)
+{
+ efi_status_t r;
+
+ EFI_ENTRY("%p, %pUs, %d, %p", handle, protocol, protocol_interface_type,
+ protocol_interface);
+
+ if (!handle || !protocol ||
+ protocol_interface_type != EFI_NATIVE_INTERFACE) {
+ r = EFI_INVALID_PARAMETER;
+ goto out;
+ }
+
+ /* Create new handle if requested. */
+ if (!*handle) {
+ r = efi_create_handle(handle);
+ if (r != EFI_SUCCESS)
+ goto out;
+ EFI_PRINT("new handle %p\n", *handle);
+ } else {
+ EFI_PRINT("handle %p\n", *handle);
+ }
+ /* Add new protocol */
+ r = efi_add_protocol(*handle, protocol, protocol_interface);
+out:
+ return EFI_EXIT(r);
+}
+
+/**
+ * efi_get_drivers() - get all drivers associated to a controller
+ * @handle: handle of the controller
+ * @protocol: protocol GUID (optional)
+ * @number_of_drivers: number of child controllers
+ * @driver_handle_buffer: handles of the the drivers
+ *
+ * The allocated buffer has to be freed with free().
+ *
+ * Return: status code
+ */
+static efi_status_t efi_get_drivers(efi_handle_t handle,
+ const efi_guid_t *protocol,
+ efi_uintn_t *number_of_drivers,
+ efi_handle_t **driver_handle_buffer)
+{
+ struct efi_handler *handler;
+ struct efi_open_protocol_info_item *item;
+ efi_uintn_t count = 0, i;
+ bool duplicate;
+
+ /* Count all driver associations */
+ list_for_each_entry(handler, &handle->protocols, link) {
+ if (protocol && efi_guidcmp(handler->guid, *protocol))
+ continue;
+ list_for_each_entry(item, &handler->open_infos, link) {
+ if (item->info.attributes &
+ EFI_OPEN_PROTOCOL_BY_DRIVER)
+ ++count;
+ }
+ }
+ *number_of_drivers = 0;
+ if (!count) {
+ *driver_handle_buffer = NULL;
+ return EFI_SUCCESS;
+ }
+ /*
+ * Create buffer. In case of duplicate driver assignments the buffer
+ * will be too large. But that does not harm.
+ */
+ *driver_handle_buffer = calloc(count, sizeof(efi_handle_t));
+ if (!*driver_handle_buffer)
+ return EFI_OUT_OF_RESOURCES;
+ /* Collect unique driver handles */
+ list_for_each_entry(handler, &handle->protocols, link) {
+ if (protocol && efi_guidcmp(handler->guid, *protocol))
+ continue;
+ list_for_each_entry(item, &handler->open_infos, link) {
+ if (item->info.attributes &
+ EFI_OPEN_PROTOCOL_BY_DRIVER) {
+ /* Check this is a new driver */
+ duplicate = false;
+ for (i = 0; i < *number_of_drivers; ++i) {
+ if ((*driver_handle_buffer)[i] ==
+ item->info.agent_handle)
+ duplicate = true;
+ }
+ /* Copy handle to buffer */
+ if (!duplicate) {
+ i = (*number_of_drivers)++;
+ (*driver_handle_buffer)[i] =
+ item->info.agent_handle;
+ }
+ }
+ }
+ }
+ return EFI_SUCCESS;
+}
+
+/**
+ * efi_disconnect_all_drivers() - disconnect all drivers from a controller
+ * @handle: handle of the controller
+ * @protocol: protocol GUID (optional)
+ * @child_handle: handle of the child to destroy
+ *
+ * This function implements the DisconnectController service.
+ *
+ * See the Unified Extensible Firmware Interface (UEFI) specification for
+ * details.
+ *
+ * Return: status code
+ */
+static efi_status_t efi_disconnect_all_drivers
+ (efi_handle_t handle,
+ const efi_guid_t *protocol,
+ efi_handle_t child_handle)
+{
+ efi_uintn_t number_of_drivers;
+ efi_handle_t *driver_handle_buffer;
+ efi_status_t r, ret;
+
+ ret = efi_get_drivers(handle, protocol, &number_of_drivers,
+ &driver_handle_buffer);
+ if (ret != EFI_SUCCESS)
+ return ret;
+ if (!number_of_drivers)
+ return EFI_SUCCESS;
+
+ while (number_of_drivers) {
+ r = EFI_CALL(efi_disconnect_controller(
+ handle,
+ driver_handle_buffer[--number_of_drivers],
+ child_handle));
+ if (r != EFI_SUCCESS)
+ ret = r;
+ }
+
+ free(driver_handle_buffer);
+ return ret;
+}
+
+/**
+ * efi_uninstall_protocol() - uninstall protocol interface
+ *
+ * @handle: handle from which the protocol shall be removed
+ * @protocol: GUID of the protocol to be removed
+ * @protocol_interface: interface to be removed
+ * @preserve: preserve or delete the handle and remove it from any
+ * list it participates if no protocols remain
+ *
+ * This function DOES NOT delete a handle without installed protocol.
+ *
+ * Return: status code
+ */
+efi_status_t efi_uninstall_protocol
+ (efi_handle_t handle, const efi_guid_t *protocol,
+ void *protocol_interface, bool preserve)
+{
+ struct efi_handler *handler;
+ struct efi_open_protocol_info_item *item;
+ struct efi_open_protocol_info_item *pos;
+ efi_status_t r;
+
+ /* Find the protocol on the handle */
+ r = efi_search_protocol(handle, protocol, &handler);
+ if (r != EFI_SUCCESS)
+ goto out;
+ if (handler->protocol_interface != protocol_interface)
+ return EFI_NOT_FOUND;
+ /* Disconnect controllers */
+ r = efi_disconnect_all_drivers(handle, protocol, NULL);
+ if (r != EFI_SUCCESS) {
+ r = EFI_ACCESS_DENIED;
+ /*
+ * This will reconnect all controllers of the handle, even ones
+ * that were not connected before. This can be done better
+ * but we are following the EDKII implementation on this for
+ * now
+ */
+ EFI_CALL(efi_connect_controller(handle, NULL, NULL, true));
+ goto out;
+ }
+ /* Close protocol */
+ list_for_each_entry_safe(item, pos, &handler->open_infos, link) {
+ if (item->info.attributes ==
+ EFI_OPEN_PROTOCOL_BY_HANDLE_PROTOCOL ||
+ item->info.attributes == EFI_OPEN_PROTOCOL_GET_PROTOCOL ||
+ item->info.attributes == EFI_OPEN_PROTOCOL_TEST_PROTOCOL)
+ efi_delete_open_info(item);
+ }
+ /* if agents didn't close the protocols properly */
+ if (!list_empty(&handler->open_infos)) {
+ r = EFI_ACCESS_DENIED;
+ EFI_CALL(efi_connect_controller(handle, NULL, NULL, true));
+ goto out;
+ }
+ r = efi_remove_protocol(handle, protocol, protocol_interface);
+ if (r != EFI_SUCCESS)
+ return r;
+ /*
+ * We don't care about the return value here since the
+ * handle might have more protocols installed
+ */
+ if (!preserve)
+ efi_purge_handle(handle);
+out:
+ return r;
+}
+
+/**
+ * efi_uninstall_protocol_interface() - uninstall protocol interface
+ * @handle: handle from which the protocol shall be removed
+ * @protocol: GUID of the protocol to be removed
+ * @protocol_interface: interface to be removed
+ *
+ * This function implements the UninstallProtocolInterface service.
+ *
+ * See the Unified Extensible Firmware Interface (UEFI) specification for
+ * details.
+ *
+ * Return: status code
+ */
+static efi_status_t EFIAPI efi_uninstall_protocol_interface
+ (efi_handle_t handle, const efi_guid_t *protocol,
+ void *protocol_interface)
+{
+ efi_status_t ret;
+
+ EFI_ENTRY("%p, %pUs, %p", handle, protocol, protocol_interface);
+
+ ret = efi_uninstall_protocol(handle, protocol, protocol_interface, false);
+ if (ret != EFI_SUCCESS)
+ goto out;
+
+out:
+ return EFI_EXIT(ret);
+}
+
+/**
+ * efi_register_protocol_notify() - register an event for notification when a
+ * protocol is installed.
+ * @protocol: GUID of the protocol whose installation shall be notified
+ * @event: event to be signaled upon installation of the protocol
+ * @registration: key for retrieving the registration information
+ *
+ * This function implements the RegisterProtocolNotify service.
+ * See the Unified Extensible Firmware Interface (UEFI) specification
+ * for details.
+ *
+ * Return: status code
+ */
+static efi_status_t EFIAPI efi_register_protocol_notify(const efi_guid_t *protocol,
+ struct efi_event *event,
+ void **registration)
+{
+ struct efi_register_notify_event *item;
+ efi_status_t ret = EFI_SUCCESS;
+
+ EFI_ENTRY("%pUs, %p, %p", protocol, event, registration);
+
+ if (!protocol || !event || !registration) {
+ ret = EFI_INVALID_PARAMETER;
+ goto out;
+ }
+
+ item = calloc(1, sizeof(struct efi_register_notify_event));
+ if (!item) {
+ ret = EFI_OUT_OF_RESOURCES;
+ goto out;
+ }
+
+ item->event = event;
+ item->protocol = *protocol;
+ INIT_LIST_HEAD(&item->handles);
+
+ list_add_tail(&item->link, &efi_register_notify_events);
+
+ *registration = item;
+out:
+ return EFI_EXIT2(ret, *registration);
+}
+
+/**
+ * efi_search() - determine if an EFI handle implements a protocol
+ *
+ * @search_type: selection criterion
+ * @protocol: GUID of the protocol
+ * @handle: handle
+ *
+ * See the documentation of the LocateHandle service in the UEFI specification.
+ *
+ * Return: 0 if the handle implements the protocol
+ */
+static int efi_search(enum efi_locate_search_type search_type,
+ const efi_guid_t *protocol, efi_handle_t handle)
+{
+ efi_status_t ret;
+
+ switch (search_type) {
+ case ALL_HANDLES:
+ return 0;
+ case BY_PROTOCOL:
+ ret = efi_search_protocol(handle, protocol, NULL);
+ return (ret != EFI_SUCCESS);
+ default:
+ /* Invalid search type */
+ return -1;
+ }
+}
+
+/**
+ * efi_check_register_notify_event() - check if registration key is valid
+ *
+ * Check that a pointer is a valid registration key as returned by
+ * RegisterProtocolNotify().
+ *
+ * @key: registration key
+ * Return: valid registration key or NULL
+ */
+static struct efi_register_notify_event *efi_check_register_notify_event
+ (void *key)
+{
+ struct efi_register_notify_event *event;
+
+ list_for_each_entry(event, &efi_register_notify_events, link) {
+ if (event == (struct efi_register_notify_event *)key)
+ return event;
+ }
+ return NULL;
+}
+
+/**
+ * efi_locate_handle() - locate handles implementing a protocol
+ *
+ * @search_type: selection criterion
+ * @protocol: GUID of the protocol
+ * @search_key: registration key
+ * @buffer_size: size of the buffer to receive the handles in bytes
+ * @buffer: buffer to receive the relevant handles
+ *
+ * This function is meant for barebox internal calls. For the API implementation
+ * of the LocateHandle service see efi_locate_handle_ext.
+ *
+ * Return: status code
+ */
+static efi_status_t efi_locate_handle(
+ enum efi_locate_search_type search_type,
+ const efi_guid_t *protocol, void *search_key,
+ efi_uintn_t *buffer_size, efi_handle_t *buffer)
+{
+ struct efi_object *efiobj;
+ efi_uintn_t size = 0;
+ struct efi_register_notify_event *event;
+ struct efi_protocol_notification *handle = NULL;
+
+ /* Check parameters */
+ switch (search_type) {
+ case ALL_HANDLES:
+ break;
+ case BY_REGISTER_NOTIFY:
+ if (!search_key)
+ return EFI_INVALID_PARAMETER;
+ /* Check that the registration key is valid */
+ event = efi_check_register_notify_event(search_key);
+ if (!event)
+ return EFI_INVALID_PARAMETER;
+ break;
+ case BY_PROTOCOL:
+ if (!protocol)
+ return EFI_INVALID_PARAMETER;
+ break;
+ default:
+ return EFI_INVALID_PARAMETER;
+ }
+
+ /* Count how much space we need */
+ if (search_type == BY_REGISTER_NOTIFY) {
+ if (list_empty(&event->handles))
+ return EFI_NOT_FOUND;
+ handle = list_first_entry(&event->handles,
+ struct efi_protocol_notification,
+ link);
+ efiobj = handle->handle;
+ size += sizeof(void *);
+ } else {
+ list_for_each_entry(efiobj, &efi_obj_list, link) {
+ if (!efi_search(search_type, protocol, efiobj))
+ size += sizeof(void *);
+ }
+ if (size == 0)
+ return EFI_NOT_FOUND;
+ }
+
+ if (!buffer_size)
+ return EFI_INVALID_PARAMETER;
+
+ if (*buffer_size < size) {
+ *buffer_size = size;
+ return EFI_BUFFER_TOO_SMALL;
+ }
+
+ *buffer_size = size;
+
+ /* The buffer size is sufficient but there is no buffer */
+ if (!buffer)
+ return EFI_INVALID_PARAMETER;
+
+ /* Then fill the array */
+ if (search_type == BY_REGISTER_NOTIFY) {
+ *buffer = efiobj;
+ list_del(&handle->link);
+ } else {
+ list_for_each_entry(efiobj, &efi_obj_list, link) {
+ if (!efi_search(search_type, protocol, efiobj))
+ *buffer++ = efiobj;
+ }
+ }
+
+ return EFI_SUCCESS;
+}
+
+/**
+ * efi_locate_handle_ext() - locate handles implementing a protocol.
+ * @search_type: selection criterion
+ * @protocol: GUID of the protocol
+ * @search_key: registration key
+ * @buffer_size: size of the buffer to receive the handles in bytes
+ * @buffer: buffer to receive the relevant handles
+ *
+ * This function implements the LocateHandle service.
+ *
+ * See the Unified Extensible Firmware Interface (UEFI) specification for
+ * details.
+ *
+ * Return: 0 if the handle implements the protocol
+ */
+static efi_status_t EFIAPI efi_locate_handle_ext(
+ enum efi_locate_search_type search_type,
+ const efi_guid_t *protocol, void *search_key,
+ efi_uintn_t *buffer_size, efi_handle_t *buffer)
+{
+ EFI_ENTRY("%d, %pUs, %p, %p, %p", search_type, protocol, search_key,
+ buffer_size, buffer);
+
+ return EFI_EXIT2(efi_locate_handle(search_type, protocol, search_key,
+ buffer_size, buffer), *buffer_size);
+}
+
+/**
+ * efi_remove_configuration_table() - collapses configuration table entries,
+ * removing index i
+ *
+ * @i: index of the table entry to be removed
+ */
+static void efi_remove_configuration_table(int i)
+{
+ struct efi_config_table *this = &systab.tables[i];
+ struct efi_config_table *next = &systab.tables[i + 1];
+ struct efi_config_table *end = &systab.tables[systab.nr_tables];
+
+ memmove(this, next, (ulong)end - (ulong)next);
+ systab.nr_tables--;
+}
+
+/**
+ * efi_install_configuration_table() - adds, updates, or removes a
+ * configuration table
+ * @guid: GUID of the installed table
+ * @table: table to be installed
+ *
+ * This function is used for internal calls. For the API implementation of the
+ * InstallConfigurationTable service see efi_install_configuration_table_ext.
+ *
+ * Return: status code
+ */
+efi_status_t efi_install_configuration_table(const efi_guid_t *guid,
+ void *table)
+{
+ struct efi_event *evt;
+ int i;
+
+ if (!guid)
+ return EFI_INVALID_PARAMETER;
+
+ /* Check for GUID override */
+ for (i = 0; i < systab.nr_tables; i++) {
+ if (!efi_guidcmp(*guid, systab.tables[i].guid)) {
+ if (table)
+ systab.tables[i].table = table;
+ else
+ efi_remove_configuration_table(i);
+ goto out;
+ }
+ }
+
+ if (!table)
+ return EFI_NOT_FOUND;
+
+ /* No override, check for overflow */
+ if (i >= EFI_MAX_CONFIGURATION_TABLES)
+ return EFI_OUT_OF_RESOURCES;
+
+ /* Add a new entry */
+ systab.tables[i].guid = *guid;
+ systab.tables[i].table = table;
+ systab.nr_tables = i + 1;
+
+out:
+ /* systab.nr_tables may have changed. So we need to update the CRC32 */
+ efi_update_table_header_crc32(&systab.hdr);
+
+ /* Notify that the configuration table was changed */
+ list_for_each_entry(evt, &efi_events, link) {
+ if (evt->group && !efi_guidcmp(*evt->group, *guid)) {
+ efi_signal_event(evt);
+ break;
+ }
+ }
+
+ return EFI_SUCCESS;
+}
+
+/**
+ * efi_install_configuration_table_ex() - Adds, updates, or removes a
+ * configuration table.
+ * @guid: GUID of the installed table
+ * @table: table to be installed
+ *
+ * This function implements the InstallConfigurationTable service.
+ *
+ * See the Unified Extensible Firmware Interface (UEFI) specification for
+ * details.
+ *
+ * Return: status code
+ */
+static efi_status_t
+EFIAPI efi_install_configuration_table_ext(const efi_guid_t *guid,
+ void *table)
+{
+ EFI_ENTRY("%pUs, %p", guid, table);
+ return EFI_EXIT(efi_install_configuration_table(guid, table));
+}
+
+/**
+ * efi_locate_device_path() - Get the device path and handle of an device
+ * implementing a protocol
+ * @protocol: GUID of the protocol
+ * @device_path: device path
+ * @device: handle of the device
+ *
+ * This function implements the LocateDevicePath service.
+ *
+ * See the Unified Extensible Firmware Interface (UEFI) specification for
+ * details.
+ *
+ * Return: status code
+ */
+static efi_status_t EFIAPI efi_locate_device_path(const efi_guid_t *protocol,
+ struct efi_device_path **device_path,
+ efi_handle_t *device)
+{
+ struct efi_device_path *dp;
+ size_t i;
+ struct efi_handler *handler;
+ efi_handle_t *handles;
+ size_t len, len_dp;
+ size_t len_best = 0;
+ efi_uintn_t no_handles;
+ u8 *remainder;
+ efi_status_t ret;
+
+ EFI_ENTRY("%pUs, %p, %p", protocol, device_path, device);
+
+ if (!protocol || !device_path || !*device_path) {
+ ret = EFI_INVALID_PARAMETER;
+ goto out;
+ }
+
+ /* Find end of device path */
+ len = efi_dp_instance_size(*device_path);
+
+ /* Get all handles implementing the protocol */
+ ret = EFI_CALL(efi_locate_handle_buffer(BY_PROTOCOL, protocol, NULL,
+ &no_handles, &handles));
+ if (ret != EFI_SUCCESS)
+ goto out;
+
+ for (i = 0; i < no_handles; ++i) {
+ /* Find the device path protocol */
+ ret = efi_search_protocol(handles[i], &efi_device_path_protocol_guid,
+ &handler);
+ if (ret != EFI_SUCCESS)
+ continue;
+ dp = (struct efi_device_path *)handler->protocol_interface;
+ len_dp = efi_dp_instance_size(dp);
+ /*
+ * This handle can only be a better fit
+ * if its device path length is longer than the best fit and
+ * if its device path length is shorter of equal the searched
+ * device path.
+ */
+ if (len_dp <= len_best || len_dp > len)
+ continue;
+ /* Check if dp is a subpath of device_path */
+ if (memcmp(*device_path, dp, len_dp))
+ continue;
+ if (!device) {
+ ret = EFI_INVALID_PARAMETER;
+ goto out;
+ }
+ *device = handles[i];
+ len_best = len_dp;
+ }
+ if (len_best) {
+ remainder = (u8 *)*device_path + len_best;
+ *device_path = (struct efi_device_path *)remainder;
+ ret = EFI_SUCCESS;
+ } else {
+ ret = EFI_NOT_FOUND;
+ }
+out:
+ return EFI_EXIT2(ret, *device_path);
+}
+
+/**
+ * efi_exit_boot_services() - stop all boot services
+ * @image_handle: handle of the loaded image
+ * @map_key: key of the memory map
+ *
+ * This function implements the ExitBootServices service.
+ *
+ * See the Unified Extensible Firmware Interface (UEFI) specification
+ * for details.
+ *
+ * All timer events are disabled. For exit boot services events the
+ * notification function is called. The boot services are disabled in the
+ * system table.
+ *
+ * Return: status code
+ */
+static efi_status_t EFIAPI efi_exit_boot_services(efi_handle_t image_handle,
+ efi_uintn_t map_key)
+{
+ EFI_ENTRY("%p, %zx", image_handle, map_key);
+
+ return EFI_EXIT(EFI_UNSUPPORTED);
+}
+
+/**
+ * efi_get_next_monotonic_count() - get next value of the counter
+ * @count: returned value of the counter
+ *
+ * This function implements the NextMonotonicCount service.
+ *
+ * See the Unified Extensible Firmware Interface (UEFI) specification for
+ * details.
+ *
+ * Return: status code
+ */
+static efi_status_t EFIAPI efi_get_next_monotonic_count(uint64_t *count)
+{
+ static uint64_t mono;
+ efi_status_t ret;
+
+ EFI_ENTRY("%p", count);
+ if (!count) {
+ ret = EFI_INVALID_PARAMETER;
+ goto out;
+ }
+ *count = mono++;
+ ret = EFI_SUCCESS;
+out:
+ return EFI_EXIT(ret);
+}
+
+/**
+ * efi_stall() - sleep
+ * @microseconds: period to sleep in microseconds
+ *
+ * This function implements the Stall service.
+ *
+ * See the Unified Extensible Firmware Interface (UEFI) specification for
+ * details.
+ *
+ * Return: status code
+ */
+static efi_status_t EFIAPI efi_stall(unsigned long microseconds)
+{
+ u64 start;
+
+ EFI_ENTRY("%ld", microseconds);
+
+ // TODO move efi_timer_check into poller?
+
+ start = get_time_ns();
+ while (!is_timeout(start, (u64)microseconds * NSEC_PER_USEC))
+ efi_timer_check();
+
+ return EFI_EXIT(EFI_SUCCESS);
+}
+
+/**
+ * efi_set_watchdog_timer() - reset the watchdog timer
+ * @timeout: seconds before reset by watchdog
+ * @watchdog_code: code to be logged when resetting
+ * @data_size: size of buffer in bytes
+ * @watchdog_data: buffer with data describing the reset reason
+ *
+ * This function implements the SetWatchdogTimer service.
+ *
+ * See the Unified Extensible Firmware Interface (UEFI) specification for
+ * details.
+ *
+ * Return: status code
+ */
+static efi_status_t EFIAPI efi_set_watchdog_timer(unsigned long timeout,
+ uint64_t watchdog_code,
+ size_t data_size,
+ uint16_t *watchdog_data)
+{
+ EFI_ENTRY("%ld, 0x%llx, %zu, %p", timeout, watchdog_code,
+ data_size, watchdog_data);
+ return EFI_EXIT(EFI_UNSUPPORTED);
+}
+
+/**
+ * efi_close_protocol() - close a protocol
+ * @handle: handle on which the protocol shall be closed
+ * @protocol: GUID of the protocol to close
+ * @agent_handle: handle of the driver
+ * @controller_handle: handle of the controller
+ *
+ * This is the function implementing the CloseProtocol service is for internal
+ * usage in barebox. For API usage wrapper efi_close_protocol_ext() is provided.
+ *
+ * See the Unified Extensible Firmware Interface (UEFI) specification for
+ * details.
+ *
+ * Return: status code
+ */
+efi_status_t EFIAPI efi_close_protocol(efi_handle_t handle, const efi_guid_t *protocol,
+ efi_handle_t agent_handle,
+ efi_handle_t controller_handle)
+{
+ struct efi_handler *handler;
+ struct efi_open_protocol_info_item *item;
+ struct efi_open_protocol_info_item *pos;
+ efi_status_t ret;
+
+ if (!efi_search_obj(agent_handle) ||
+ (controller_handle && !efi_search_obj(controller_handle)))
+ return EFI_INVALID_PARAMETER;
+ ret = efi_search_protocol(handle, protocol, &handler);
+ if (ret != EFI_SUCCESS)
+ return ret;
+
+ ret = EFI_NOT_FOUND;
+ list_for_each_entry_safe(item, pos, &handler->open_infos, link) {
+ if (item->info.agent_handle == agent_handle &&
+ item->info.controller_handle == controller_handle) {
+ efi_delete_open_info(item);
+ ret = EFI_SUCCESS;
+ }
+ }
+
+ return ret;
+}
+
+/**
+ * efi_close_protocol_ext() - close a protocol
+ * @handle: handle on which the protocol shall be closed
+ * @protocol: GUID of the protocol to close
+ * @agent_handle: handle of the driver
+ * @controller_handle: handle of the controller
+ *
+ * This function implements the CloseProtocol service.
+ *
+ * See the Unified Extensible Firmware Interface (UEFI) specification for
+ * details.
+ *
+ * Return: status code
+ */
+static efi_status_t EFIAPI
+efi_close_protocol_ext(efi_handle_t handle, const efi_guid_t *protocol,
+ efi_handle_t agent_handle,
+ efi_handle_t controller_handle)
+{
+ efi_status_t ret;
+
+ EFI_ENTRY("%p, %pUs, %p, %p", handle, protocol, agent_handle,
+ controller_handle);
+
+ ret = efi_close_protocol(handle, protocol,
+ agent_handle, controller_handle);
+
+ return EFI_EXIT(ret);
+}
+
+/**
+ * efi_open_protocol_information() - provide information about then open status
+ * of a protocol on a handle
+ * @handle: handle for which the information shall be retrieved
+ * @protocol: GUID of the protocol
+ * @entry_buffer: buffer to receive the open protocol information
+ * @entry_count: number of entries available in the buffer
+ *
+ * This function implements the OpenProtocolInformation service.
+ *
+ * See the Unified Extensible Firmware Interface (UEFI) specification for
+ * details.
+ *
+ * Return: status code
+ */
+static efi_status_t EFIAPI efi_open_protocol_information(
+ efi_handle_t handle, const efi_guid_t *protocol,
+ struct efi_open_protocol_information_entry **entry_buffer,
+ size_t *entry_count)
+{
+ unsigned long buffer_size;
+ unsigned long count;
+ struct efi_handler *handler;
+ struct efi_open_protocol_info_item *item;
+ efi_status_t r;
+
+ EFI_ENTRY("%p, %pUs, %p, %p", handle, protocol, entry_buffer,
+ entry_count);
+
+ /* Check parameters */
+ if (!entry_buffer) {
+ r = EFI_INVALID_PARAMETER;
+ goto out;
+ }
+ r = efi_search_protocol(handle, protocol, &handler);
+ if (r != EFI_SUCCESS)
+ goto out;
+
+ /* Count entries */
+ count = 0;
+ list_for_each_entry(item, &handler->open_infos, link) {
+ if (item->info.open_count)
+ ++count;
+ }
+ *entry_count = count;
+ *entry_buffer = NULL;
+ if (!count) {
+ r = EFI_SUCCESS;
+ goto out;
+ }
+
+ /* Copy entries */
+ buffer_size = count * sizeof(struct efi_open_protocol_information_entry);
+ r = efi_allocate_pool(EFI_BOOT_SERVICES_DATA, buffer_size,
+ (void **)entry_buffer, "openproto");
+ if (r != EFI_SUCCESS)
+ goto out;
+ list_for_each_entry_reverse(item, &handler->open_infos, link) {
+ if (item->info.open_count)
+ (*entry_buffer)[--count] = item->info;
+ }
+out:
+ return EFI_EXIT2(r, *entry_buffer);
+}
+
+/**
+ * efi_protocols_per_handle() - get protocols installed on a handle
+ * @handle: handle for which the information is retrieved
+ * @protocol_buffer: buffer with protocol GUIDs
+ * @protocol_buffer_count: number of entries in the buffer
+ *
+ * This function implements the ProtocolsPerHandleService.
+ *
+ * See the Unified Extensible Firmware Interface (UEFI) specification for
+ * details.
+ *
+ * Return: status code
+ */
+static efi_status_t EFIAPI efi_protocols_per_handle(
+ efi_handle_t handle, efi_guid_t ***protocol_buffer,
+ efi_uintn_t *protocol_buffer_count)
+{
+ unsigned long buffer_size;
+ struct efi_object *efiobj;
+ struct list_head *protocol_handle;
+ efi_status_t r;
+
+ EFI_ENTRY("%p, %p, %p", handle, protocol_buffer,
+ protocol_buffer_count);
+
+ if (!handle || !protocol_buffer || !protocol_buffer_count)
+ return EFI_EXIT(EFI_INVALID_PARAMETER);
+
+ *protocol_buffer = NULL;
+
+ efiobj = efi_search_obj(handle);
+ if (!efiobj)
+ return EFI_EXIT(EFI_INVALID_PARAMETER);
+
+ *protocol_buffer_count = list_count_nodes(&efiobj->protocols);
+
+ /* Copy GUIDs */
+ if (*protocol_buffer_count) {
+ size_t j = 0;
+
+ buffer_size = sizeof(efi_guid_t *) * *protocol_buffer_count;
+ r = efi_allocate_pool(EFI_BOOT_SERVICES_DATA, buffer_size,
+ (void **)protocol_buffer, "protocols_per_handle");
+ if (r != EFI_SUCCESS)
+ return EFI_EXIT(r);
+ list_for_each(protocol_handle, &efiobj->protocols) {
+ struct efi_handler *protocol;
+
+ protocol = list_entry(protocol_handle,
+ struct efi_handler, link);
+ (*protocol_buffer)[j] = &protocol->guid;
+ ++j;
+ }
+ }
+
+ return EFI_EXIT2(EFI_SUCCESS, *protocol_buffer);
+}
+
+static efi_status_t efi_locate_handle_buffer_int(enum efi_locate_search_type search_type,
+ const efi_guid_t *protocol, void *search_key,
+ efi_uintn_t *no_handles, efi_handle_t **buffer)
+{
+ efi_status_t r;
+ efi_uintn_t buffer_size = 0;
+
+ if (!no_handles || !buffer) {
+ r = EFI_INVALID_PARAMETER;
+ goto out;
+ }
+ *no_handles = 0;
+ *buffer = NULL;
+ r = efi_locate_handle(search_type, protocol, search_key, &buffer_size,
+ *buffer);
+ if (r != EFI_BUFFER_TOO_SMALL)
+ goto out;
+ r = efi_allocate_pool(EFI_BOOT_SERVICES_DATA, buffer_size,
+ (void **)buffer, "locate_handle");
+ if (r != EFI_SUCCESS)
+ goto out;
+ r = efi_locate_handle(search_type, protocol, search_key, &buffer_size,
+ *buffer);
+ if (r == EFI_SUCCESS)
+ *no_handles = buffer_size / sizeof(efi_handle_t);
+out:
+ return r;
+}
+
+/**
+ * efi_locate_handle_buffer() - locate handles implementing a protocol
+ * @search_type: selection criterion
+ * @protocol: GUID of the protocol
+ * @search_key: registration key
+ * @no_handles: number of returned handles
+ * @buffer: buffer with the returned handles
+ *
+ * This function implements the LocateHandleBuffer service.
+ *
+ * See the Unified Extensible Firmware Interface (UEFI) specification for
+ * details.
+ *
+ * Return: status code
+ */
+efi_status_t EFIAPI efi_locate_handle_buffer(
+ enum efi_locate_search_type search_type,
+ const efi_guid_t *protocol, void *search_key,
+ efi_uintn_t *no_handles, efi_handle_t **buffer)
+{
+ efi_status_t r;
+
+ EFI_ENTRY("%d, %pUs, %p, %p, %p", search_type, protocol, search_key,
+ no_handles, buffer);
+
+ r = efi_locate_handle_buffer_int(search_type, protocol, search_key,
+ no_handles, buffer);
+
+ return EFI_EXIT(r);
+}
+
+/**
+ * efi_locate_protocol() - find an interface implementing a protocol
+ * @protocol: GUID of the protocol
+ * @registration: registration key passed to the notification function
+ * @protocol_interface: interface implementing the protocol
+ *
+ * This function implements the LocateProtocol service.
+ *
+ * See the Unified Extensible Firmware Interface (UEFI) specification for
+ * details.
+ *
+ * Return: status code
+ */
+static efi_status_t EFIAPI efi_locate_protocol(const efi_guid_t *protocol,
+ void *registration,
+ void **protocol_interface)
+{
+ struct efi_handler *handler;
+ efi_status_t ret;
+ struct efi_object *efiobj;
+
+ EFI_ENTRY("%pUs, %p, %p", protocol, registration, protocol_interface);
+
+ /*
+ * The UEFI spec explicitly requires a protocol even if a registration
+ * key is provided. This differs from the logic in LocateHandle().
+ */
+ if (!protocol || !protocol_interface)
+ return EFI_EXIT(EFI_INVALID_PARAMETER);
+
+ if (registration) {
+ struct efi_register_notify_event *event;
+ struct efi_protocol_notification *handle;
+
+ event = efi_check_register_notify_event(registration);
+ if (!event)
+ return EFI_EXIT(EFI_INVALID_PARAMETER);
+ /*
+ * The UEFI spec requires to return EFI_NOT_FOUND if no
+ * protocol instance matches protocol and registration.
+ * So let's do the same for a mismatch between protocol and
+ * registration.
+ */
+ if (efi_guidcmp(event->protocol, *protocol))
+ goto not_found;
+ if (list_empty(&event->handles))
+ goto not_found;
+ handle = list_first_entry(&event->handles,
+ struct efi_protocol_notification,
+ link);
+ efiobj = handle->handle;
+ list_del(&handle->link);
+ free(handle);
+ ret = efi_search_protocol(efiobj, protocol, &handler);
+ if (ret == EFI_SUCCESS)
+ goto found;
+ } else {
+ list_for_each_entry(efiobj, &efi_obj_list, link) {
+ ret = efi_search_protocol(efiobj, protocol, &handler);
+ if (ret == EFI_SUCCESS)
+ goto found;
+ }
+ }
+not_found:
+ *protocol_interface = NULL;
+ return EFI_EXIT(EFI_NOT_FOUND);
+found:
+ *protocol_interface = handler->protocol_interface;
+ return EFI_EXIT2(EFI_SUCCESS, *protocol_interface);
+}
+
+/**
+ * efi_install_multiple_protocol_interfaces_int() - Install multiple protocol
+ * interfaces
+ * @handle: handle on which the protocol interfaces shall be installed
+ * @argptr: va_list of args
+ *
+ * Core functionality of efi_install_multiple_protocol_interfaces
+ * Must not be called directly
+ *
+ * Return: status code
+ */
+static efi_status_t EFIAPI
+efi_install_multiple_protocol_interfaces_int(efi_handle_t *handle,
+ efi_va_list argptr)
+{
+ const efi_guid_t *protocol;
+ void *protocol_interface;
+ efi_handle_t old_handle;
+ efi_status_t ret = EFI_SUCCESS;
+ int i = 0;
+ efi_va_list argptr_copy;
+
+ if (!handle)
+ return EFI_INVALID_PARAMETER;
+
+ efi_va_copy(argptr_copy, argptr);
+ for (;;) {
+ protocol = efi_va_arg(argptr, efi_guid_t*);
+ if (!protocol)
+ break;
+ protocol_interface = efi_va_arg(argptr, void*);
+ /* Check that a device path has not been installed before */
+ if (!efi_guidcmp(*protocol, efi_device_path_protocol_guid)) {
+ struct efi_device_path *dp = protocol_interface;
+
+ ret = EFI_CALL(efi_locate_device_path(protocol, &dp,
+ &old_handle));
+ if (ret == EFI_SUCCESS &&
+ dp->type == DEVICE_PATH_TYPE_END) {
+ EFI_PRINT("Path %pD already installed\n",
+ protocol_interface);
+ ret = EFI_ALREADY_STARTED;
+ break;
+ }
+ }
+ ret = EFI_CALL(efi_install_protocol_interface(handle, protocol,
+ EFI_NATIVE_INTERFACE,
+ protocol_interface));
+ if (ret != EFI_SUCCESS)
+ break;
+ i++;
+ }
+ if (ret == EFI_SUCCESS)
+ goto out;
+
+ /* If an error occurred undo all changes. */
+ for (; i; --i) {
+ protocol = efi_va_arg(argptr_copy, efi_guid_t*);
+ protocol_interface = efi_va_arg(argptr_copy, void*);
+ EFI_CALL(efi_uninstall_protocol_interface(*handle, protocol,
+ protocol_interface));
+ }
+
+out:
+ efi_va_end(argptr_copy);
+ return ret;
+
+}
+
+/**
+ * efi_install_multiple_protocol_interfaces() - Install multiple protocol
+ * interfaces
+ * @handle: handle on which the protocol interfaces shall be installed
+ * @...: NULL terminated argument list with pairs of protocol GUIDS and
+ * interfaces
+ *
+ *
+ * This is the function for internal usage in barebox. For the API function
+ * implementing the InstallMultipleProtocol service see
+ * efi_install_multiple_protocol_interfaces_ext()
+ *
+ * Return: status code
+ */
+efi_status_t EFIAPI
+efi_install_multiple_protocol_interfaces(efi_handle_t *handle, ...)
+{
+ efi_status_t ret;
+ efi_va_list argptr;
+
+ efi_va_start(argptr, handle);
+ ret = efi_install_multiple_protocol_interfaces_int(handle, argptr);
+ efi_va_end(argptr);
+ return ret;
+}
+
+/**
+ * efi_install_multiple_protocol_interfaces_ext() - Install multiple protocol
+ * interfaces
+ * @handle: handle on which the protocol interfaces shall be installed
+ * @...: NULL terminated argument list with pairs of protocol GUIDS and
+ * interfaces
+ *
+ * This function implements the MultipleProtocolInterfaces service.
+ *
+ * See the Unified Extensible Firmware Interface (UEFI) specification for
+ * details.
+ *
+ * Return: status code
+ */
+static efi_status_t EFIAPI
+efi_install_multiple_protocol_interfaces_ext(efi_handle_t *handle, ...)
+{
+ EFI_ENTRY("%p", handle);
+ efi_status_t ret;
+ efi_va_list argptr;
+
+ efi_va_start(argptr, handle);
+ ret = efi_install_multiple_protocol_interfaces_int(handle, argptr);
+ efi_va_end(argptr);
+ return EFI_EXIT(ret);
+}
+
+/**
+ * efi_uninstall_multiple_protocol_interfaces_int() - wrapper for uninstall
+ * multiple protocol
+ * interfaces
+ * @handle: handle from which the protocol interfaces shall be removed
+ * @argptr: va_list of args
+ *
+ * Core functionality of efi_uninstall_multiple_protocol_interfaces
+ * Must not be called directly
+ *
+ * Return: status code
+ */
+static efi_status_t EFIAPI
+efi_uninstall_multiple_protocol_interfaces_int(efi_handle_t handle,
+ efi_va_list argptr)
+{
+ const efi_guid_t *protocol, *next_protocol;
+ void *protocol_interface;
+ efi_status_t ret = EFI_SUCCESS;
+ size_t i = 0;
+ efi_va_list argptr_copy;
+
+ if (!handle)
+ return EFI_INVALID_PARAMETER;
+
+ efi_va_copy(argptr_copy, argptr);
+ protocol = efi_va_arg(argptr, efi_guid_t*);
+ for (;;) {
+ /*
+ * If efi_uninstall_protocol() fails we need to be able to
+ * reinstall the previously uninstalled protocols on the same
+ * handle.
+ * Instead of calling efi_uninstall_protocol(...,..., false)
+ * and potentially removing the handle, only allow the handle
+ * removal on the last protocol that we requested to uninstall.
+ * That way we can preserve the handle in case the latter fails
+ */
+ bool preserve = true;
+
+ if (!protocol)
+ break;
+ protocol_interface = efi_va_arg(argptr, void*);
+ next_protocol = efi_va_arg(argptr, efi_guid_t*);
+ if (!next_protocol)
+ preserve = false;
+ ret = efi_uninstall_protocol(handle, protocol,
+ protocol_interface, preserve);
+ if (ret != EFI_SUCCESS)
+ break;
+ i++;
+ protocol = next_protocol;
+ }
+ if (ret == EFI_SUCCESS)
+ goto out;
+
+ /* If an error occurred undo all changes. */
+ for (; i; --i) {
+ protocol = efi_va_arg(argptr_copy, efi_guid_t*);
+ protocol_interface = efi_va_arg(argptr_copy, void*);
+ EFI_CALL(efi_install_protocol_interface(&handle, protocol,
+ EFI_NATIVE_INTERFACE,
+ protocol_interface));
+ }
+ /*
+ * If any errors are generated while the protocol interfaces are being
+ * uninstalled, then the protocols uninstalled prior to the error will
+ * be reinstalled using InstallProtocolInterface() and the status code
+ * EFI_INVALID_PARAMETER is returned.
+ */
+ ret = EFI_INVALID_PARAMETER;
+
+out:
+ efi_va_end(argptr_copy);
+ return ret;
+}
+
+/**
+ * efi_uninstall_multiple_protocol_interfaces() - uninstall multiple protocol
+ * interfaces
+ * @handle: handle from which the protocol interfaces shall be removed
+ * @...: NULL terminated argument list with pairs of protocol GUIDS and
+ * interfaces
+ *
+ * This function implements the UninstallMultipleProtocolInterfaces service.
+ *
+ * This is the function for internal usage in barebox. For the API function
+ * implementing the UninstallMultipleProtocolInterfaces service see
+ * efi_uninstall_multiple_protocol_interfaces_ext()
+ *
+ * Return: status code
+ */
+efi_status_t EFIAPI
+efi_uninstall_multiple_protocol_interfaces(efi_handle_t handle, ...)
+{
+ efi_status_t ret;
+ efi_va_list argptr;
+
+ efi_va_start(argptr, handle);
+ ret = efi_uninstall_multiple_protocol_interfaces_int(handle, argptr);
+ efi_va_end(argptr);
+ return ret;
+}
+
+/**
+ * efi_uninstall_multiple_protocol_interfaces_ext() - uninstall multiple protocol
+ * interfaces
+ * @handle: handle from which the protocol interfaces shall be removed
+ * @...: NULL terminated argument list with pairs of protocol GUIDS and
+ * interfaces
+ *
+ * This function implements the UninstallMultipleProtocolInterfaces service.
+ *
+ * See the Unified Extensible Firmware Interface (UEFI) specification for
+ * details.
+ *
+ * Return: status code
+ */
+static efi_status_t EFIAPI
+efi_uninstall_multiple_protocol_interfaces_ext(efi_handle_t handle, ...)
+{
+ EFI_ENTRY("%p", handle);
+ efi_status_t ret;
+ efi_va_list argptr;
+
+ efi_va_start(argptr, handle);
+ ret = efi_uninstall_multiple_protocol_interfaces_int(handle, argptr);
+ efi_va_end(argptr);
+ return EFI_EXIT(ret);
+}
+
+/**
+ * efi_calculate_crc32() - calculate cyclic redundancy code
+ * @data: buffer with data
+ * @data_size: size of buffer in bytes
+ * @crc32_p: cyclic redundancy code
+ *
+ * This function implements the CalculateCrc32 service.
+ *
+ * See the Unified Extensible Firmware Interface (UEFI) specification for
+ * details.
+ *
+ * Return: status code
+ */
+static efi_status_t EFIAPI efi_calculate_crc32(const void *data,
+ efi_uintn_t data_size,
+ u32 *crc32_p)
+{
+ efi_status_t ret = EFI_SUCCESS;
+
+ EFI_ENTRY("%p, %zu", data, data_size);
+ if (!data || !data_size || !crc32_p) {
+ ret = EFI_INVALID_PARAMETER;
+ goto out;
+ }
+ *crc32_p = crc32(0, data, data_size);
+out:
+ return EFI_EXIT2(ret, *crc32_p);
+}
+
+/**
+ * efi_copy_mem() - copy memory
+ * @destination: destination of the copy operation
+ * @source: source of the copy operation
+ * @length: number of bytes to copy
+ *
+ * This function implements the CopyMem service.
+ *
+ * See the Unified Extensible Firmware Interface (UEFI) specification for
+ * details.
+ */
+static void EFIAPI efi_copy_mem(void *destination, const void *source,
+ size_t length)
+{
+ EFI_ENTRY("%p, %p, %ld", destination, source, (unsigned long)length);
+ memmove(destination, source, length);
+ EFI_EXIT(EFI_SUCCESS);
+}
+
+/**
+ * efi_set_mem() - Fill memory with a byte value.
+ * @buffer: buffer to fill
+ * @size: size of buffer in bytes
+ * @value: byte to copy to the buffer
+ *
+ * This function implements the SetMem service.
+ *
+ * See the Unified Extensible Firmware Interface (UEFI) specification for
+ * details.
+ */
+static void EFIAPI efi_set_mem(void *buffer, size_t size, uint8_t value)
+{
+ EFI_ENTRY("%p, %ld, 0x%x", buffer, (unsigned long)size, value);
+ memset(buffer, value, size);
+ EFI_EXIT(EFI_SUCCESS);
+}
+
+/**
+ * efi_protocol_open() - open protocol interface on a handle
+ * @handler: handler of a protocol
+ * @protocol_interface: interface implementing the protocol
+ * @agent_handle: handle of the driver
+ * @controller_handle: handle of the controller
+ * @attributes: attributes indicating how to open the protocol
+ *
+ * Return: status code
+ */
+efi_status_t efi_protocol_open(
+ struct efi_handler *handler,
+ void **protocol_interface, void *agent_handle,
+ void *controller_handle, uint32_t attributes)
+{
+ struct efi_open_protocol_info_item *item;
+ struct efi_open_protocol_information_entry *match = NULL;
+ bool opened_by_driver = false;
+ bool opened_exclusive = false;
+
+ /* If there is no agent, only return the interface */
+ if (!agent_handle)
+ goto out;
+
+ /* For TEST_PROTOCOL ignore interface attribute */
+ if (attributes != EFI_OPEN_PROTOCOL_TEST_PROTOCOL)
+ *protocol_interface = NULL;
+
+ /*
+ * Check if the protocol is already opened by a driver with the same
+ * attributes or opened exclusively
+ */
+ list_for_each_entry(item, &handler->open_infos, link) {
+ if (item->info.agent_handle == agent_handle) {
+ if ((attributes & EFI_OPEN_PROTOCOL_BY_DRIVER) &&
+ (item->info.attributes == attributes))
+ return EFI_ALREADY_STARTED;
+ } else {
+ if (item->info.attributes &
+ EFI_OPEN_PROTOCOL_BY_DRIVER)
+ opened_by_driver = true;
+ }
+ if (item->info.attributes & EFI_OPEN_PROTOCOL_EXCLUSIVE)
+ opened_exclusive = true;
+ }
+
+ /* Only one controller can open the protocol exclusively */
+ if (attributes & EFI_OPEN_PROTOCOL_EXCLUSIVE) {
+ if (opened_exclusive)
+ return EFI_ACCESS_DENIED;
+ } else if (attributes & EFI_OPEN_PROTOCOL_BY_DRIVER) {
+ if (opened_exclusive || opened_by_driver)
+ return EFI_ACCESS_DENIED;
+ }
+
+ /* Prepare exclusive opening */
+ if (attributes & EFI_OPEN_PROTOCOL_EXCLUSIVE) {
+ /* Try to disconnect controllers */
+disconnect_next:
+ opened_by_driver = false;
+ list_for_each_entry(item, &handler->open_infos, link) {
+ efi_status_t ret;
+
+ if (item->info.attributes ==
+ EFI_OPEN_PROTOCOL_BY_DRIVER) {
+ ret = EFI_CALL(efi_disconnect_controller(
+ item->info.controller_handle,
+ item->info.agent_handle,
+ NULL));
+ if (ret == EFI_SUCCESS)
+ /*
+ * Child controllers may have been
+ * removed from the open_infos list. So
+ * let's restart the loop.
+ */
+ goto disconnect_next;
+ else
+ opened_by_driver = true;
+ }
+ }
+ /* Only one driver can be connected */
+ if (opened_by_driver)
+ return EFI_ACCESS_DENIED;
+ }
+
+ /* Find existing entry */
+ list_for_each_entry(item, &handler->open_infos, link) {
+ if (item->info.agent_handle == agent_handle &&
+ item->info.controller_handle == controller_handle &&
+ item->info.attributes == attributes)
+ match = &item->info;
+ }
+ /* None found, create one */
+ if (!match) {
+ match = efi_create_open_info(handler);
+ if (!match)
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ match->agent_handle = agent_handle;
+ match->controller_handle = controller_handle;
+ match->attributes = attributes;
+ match->open_count++;
+
+out:
+ /* For TEST_PROTOCOL ignore interface attribute. */
+ if (attributes != EFI_OPEN_PROTOCOL_TEST_PROTOCOL)
+ *protocol_interface = handler->protocol_interface;
+
+ return EFI_SUCCESS;
+}
+
+/**
+ * efi_open_protocol() - open protocol interface on a handle
+ * @handle: handle on which the protocol shall be opened
+ * @protocol: GUID of the protocol
+ * @protocol_interface: interface implementing the protocol
+ * @agent_handle: handle of the driver
+ * @controller_handle: handle of the controller
+ * @attributes: attributes indicating how to open the protocol
+ *
+ * This function implements the OpenProtocol interface.
+ *
+ * See the Unified Extensible Firmware Interface (UEFI) specification for
+ * details.
+ *
+ * Return: status code
+ */
+static efi_status_t EFIAPI efi_open_protocol
+ (efi_handle_t handle, const efi_guid_t *protocol,
+ void **protocol_interface, efi_handle_t agent_handle,
+ efi_handle_t controller_handle, uint32_t attributes)
+{
+ struct efi_handler *handler;
+ efi_status_t r = EFI_INVALID_PARAMETER;
+
+ EFI_ENTRY("%p, %pUs, %p, %p, %p, 0x%x", handle, protocol,
+ protocol_interface, agent_handle, controller_handle,
+ attributes);
+
+ if (!handle || !protocol ||
+ (!protocol_interface && attributes !=
+ EFI_OPEN_PROTOCOL_TEST_PROTOCOL)) {
+ goto out;
+ }
+
+ switch (attributes) {
+ case EFI_OPEN_PROTOCOL_BY_HANDLE_PROTOCOL:
+ case EFI_OPEN_PROTOCOL_GET_PROTOCOL:
+ case EFI_OPEN_PROTOCOL_TEST_PROTOCOL:
+ break;
+ case EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER:
+ if (controller_handle == handle)
+ goto out;
+ /* fall-through */
+ case EFI_OPEN_PROTOCOL_BY_DRIVER:
+ case EFI_OPEN_PROTOCOL_BY_DRIVER | EFI_OPEN_PROTOCOL_EXCLUSIVE:
+ /* Check that the controller handle is valid */
+ if (!efi_search_obj(controller_handle))
+ goto out;
+ /* fall-through */
+ case EFI_OPEN_PROTOCOL_EXCLUSIVE:
+ /* Check that the agent handle is valid */
+ if (!efi_search_obj(agent_handle))
+ goto out;
+ break;
+ default:
+ goto out;
+ }
+
+ r = efi_search_protocol(handle, protocol, &handler);
+ switch (r) {
+ case EFI_SUCCESS:
+ break;
+ case EFI_NOT_FOUND:
+ r = EFI_UNSUPPORTED;
+ goto out;
+ default:
+ goto out;
+ }
+
+ r = efi_protocol_open(handler, protocol_interface, agent_handle,
+ controller_handle, attributes);
+out:
+ return EFI_EXIT(r);
+}
+
+/**
+ * efi_start_image() - call the entry point of an image
+ * @image_handle: handle of the image
+ * @exit_data_size: size of the buffer
+ * @exit_data: buffer to receive the exit data of the called image
+ *
+ * This function implements the StartImage service.
+ *
+ * See the Unified Extensible Firmware Interface (UEFI) specification for
+ * details.
+ *
+ * Return: status code
+ */
+static efi_status_t EFIAPI efi_start_image(efi_handle_t image_handle,
+ efi_uintn_t *exit_data_size,
+ u16 **exit_data)
+{
+ EFI_ENTRY("%p, %p, %p", image_handle, exit_data_size, exit_data);
+
+ return EFI_EXIT(EFI_UNSUPPORTED);
+}
+
+/**
+ * efi_unload_image() - unload an EFI image
+ * @image_handle: handle of the image to be unloaded
+ *
+ * This function implements the UnloadImage service.
+ *
+ * See the Unified Extensible Firmware Interface (UEFI) specification for
+ * details.
+ *
+ * Return: status code
+ */
+static efi_status_t EFIAPI efi_unload_image(efi_handle_t image_handle)
+{
+ return EFI_EXIT(EFI_UNSUPPORTED);
+}
+
+/**
+ * efi_exit() - leave an EFI application or driver
+ * @image_handle: handle of the application or driver that is exiting
+ * @exit_status: status code
+ * @exit_data_size: size of the buffer in bytes
+ * @exit_data: buffer with data describing an error
+ *
+ * This function implements the Exit service.
+ *
+ * See the Unified Extensible Firmware Interface (UEFI) specification for
+ * details.
+ *
+ * Return: status code
+ */
+static efi_status_t EFIAPI efi_exit(efi_handle_t image_handle,
+ efi_status_t exit_status,
+ efi_uintn_t exit_data_size,
+ u16 *exit_data)
+{
+ EFI_ENTRY("%p, %ld, %zu, %p", image_handle, exit_status,
+ exit_data_size, exit_data);
+
+ return EFI_EXIT(EFI_UNSUPPORTED);
+}
+
+/**
+ * efi_handle_protocol() - get interface of a protocol on a handle
+ * @handle: handle on which the protocol shall be opened
+ * @protocol: GUID of the protocol
+ * @protocol_interface: interface implementing the protocol
+ *
+ * This function implements the HandleProtocol service.
+ *
+ * See the Unified Extensible Firmware Interface (UEFI) specification for
+ * details.
+ *
+ * Return: status code
+ */
+static efi_status_t EFIAPI efi_handle_protocol(efi_handle_t handle,
+ const efi_guid_t *protocol,
+ void **protocol_interface)
+{
+ return efi_open_protocol(handle, protocol, protocol_interface, efi_root,
+ NULL, EFI_OPEN_PROTOCOL_BY_HANDLE_PROTOCOL);
+}
+
+/**
+ * efi_bind_controller() - bind a single driver to a controller
+ * @controller_handle: controller handle
+ * @driver_image_handle: driver handle
+ * @remain_device_path: remaining path
+ *
+ * Return: status code
+ */
+static efi_status_t efi_bind_controller(
+ efi_handle_t controller_handle,
+ efi_handle_t driver_image_handle,
+ struct efi_device_path *remain_device_path)
+{
+ struct efi_driver_binding_protocol *binding_protocol;
+ efi_status_t r;
+
+ r = EFI_CALL(efi_open_protocol(driver_image_handle,
+ &efi_driver_binding_protocol_guid,
+ (void **)&binding_protocol,
+ driver_image_handle, NULL,
+ EFI_OPEN_PROTOCOL_GET_PROTOCOL));
+ if (r != EFI_SUCCESS)
+ return r;
+ r = EFI_CALL(binding_protocol->supported(binding_protocol,
+ controller_handle,
+ remain_device_path));
+ if (r == EFI_SUCCESS)
+ r = EFI_CALL(binding_protocol->start(binding_protocol,
+ controller_handle,
+ remain_device_path));
+ efi_close_protocol(driver_image_handle,
+ &efi_driver_binding_protocol_guid,
+ driver_image_handle, NULL);
+ return r;
+}
+
+/**
+ * efi_connect_single_controller() - connect a single driver to a controller
+ * @controller_handle: controller
+ * @driver_image_handle: driver
+ * @remain_device_path: remaining path
+ *
+ * Return: status code
+ */
+static efi_status_t efi_connect_single_controller(
+ efi_handle_t controller_handle,
+ efi_handle_t *driver_image_handle,
+ struct efi_device_path *remain_device_path)
+{
+ efi_handle_t *buffer;
+ size_t count;
+ size_t i;
+ efi_status_t r;
+ size_t connected = 0;
+
+ /* Get buffer with all handles with driver binding protocol */
+ r = EFI_CALL(efi_locate_handle_buffer(BY_PROTOCOL,
+ &efi_driver_binding_protocol_guid,
+ NULL, &count, &buffer));
+ if (r != EFI_SUCCESS)
+ return r;
+
+ /* Context Override */
+ if (driver_image_handle) {
+ for (; *driver_image_handle; ++driver_image_handle) {
+ for (i = 0; i < count; ++i) {
+ if (buffer[i] == *driver_image_handle) {
+ buffer[i] = NULL;
+ r = efi_bind_controller(
+ controller_handle,
+ *driver_image_handle,
+ remain_device_path);
+ /*
+ * For drivers that do not support the
+ * controller or are already connected
+ * we receive an error code here.
+ */
+ if (r == EFI_SUCCESS)
+ ++connected;
+ }
+ }
+ }
+ }
+
+ /*
+ * TODO: Some overrides are not yet implemented:
+ * - Platform Driver Override
+ * - Driver Family Override Search
+ * - Bus Specific Driver Override
+ */
+
+ /* Driver Binding Search */
+ for (i = 0; i < count; ++i) {
+ if (buffer[i]) {
+ r = efi_bind_controller(controller_handle,
+ buffer[i],
+ remain_device_path);
+ if (r == EFI_SUCCESS)
+ ++connected;
+ }
+ }
+
+ efi_free_pool(buffer);
+ if (!connected)
+ return EFI_NOT_FOUND;
+ return EFI_SUCCESS;
+}
+
+/**
+ * efi_connect_controller() - connect a controller to a driver
+ * @controller_handle: handle of the controller
+ * @driver_image_handle: handle of the driver
+ * @remain_device_path: device path of a child controller
+ * @recursive: true to connect all child controllers
+ *
+ * This function implements the ConnectController service.
+ *
+ * See the Unified Extensible Firmware Interface (UEFI) specification for
+ * details.
+ *
+ * First all driver binding protocol handles are tried for binding drivers.
+ * Afterwards all handles that have opened a protocol of the controller
+ * with EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER are connected to drivers.
+ *
+ * Return: status code
+ */
+efi_status_t EFIAPI efi_connect_controller(
+ efi_handle_t controller_handle,
+ efi_handle_t *driver_image_handle,
+ struct efi_device_path *remain_device_path,
+ bool recursive)
+{
+ efi_status_t r;
+ efi_status_t ret = EFI_NOT_FOUND;
+ struct efi_object *efiobj;
+
+ EFI_ENTRY("%p, %p, %pD, %d", controller_handle, driver_image_handle,
+ remain_device_path, recursive);
+
+ efiobj = efi_search_obj(controller_handle);
+ if (!efiobj) {
+ ret = EFI_INVALID_PARAMETER;
+ goto out;
+ }
+
+ r = efi_connect_single_controller(controller_handle,
+ driver_image_handle,
+ remain_device_path);
+ if (r == EFI_SUCCESS)
+ ret = EFI_SUCCESS;
+ if (recursive) {
+ struct efi_handler *handler;
+ struct efi_open_protocol_info_item *item;
+
+ list_for_each_entry(handler, &efiobj->protocols, link) {
+ list_for_each_entry(item, &handler->open_infos, link) {
+ if (item->info.attributes &
+ EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER) {
+ r = EFI_CALL(efi_connect_controller(
+ item->info.controller_handle,
+ driver_image_handle,
+ remain_device_path,
+ recursive));
+ if (r == EFI_SUCCESS)
+ ret = EFI_SUCCESS;
+ }
+ }
+ }
+ }
+ /* Check for child controller specified by end node */
+ if (ret != EFI_SUCCESS && remain_device_path &&
+ remain_device_path->type == DEVICE_PATH_TYPE_END)
+ ret = EFI_SUCCESS;
+out:
+ return EFI_EXIT(ret);
+}
+
+/**
+ * efi_reinstall_protocol_interface() - reinstall protocol interface
+ * @handle: handle on which the protocol shall be reinstalled
+ * @protocol: GUID of the protocol to be installed
+ * @old_interface: interface to be removed
+ * @new_interface: interface to be installed
+ *
+ * This function implements the ReinstallProtocolInterface service.
+ *
+ * See the Unified Extensible Firmware Interface (UEFI) specification for
+ * details.
+ *
+ * The old interface is uninstalled. The new interface is installed.
+ * Drivers are connected.
+ *
+ * Return: status code
+ */
+static efi_status_t EFIAPI efi_reinstall_protocol_interface(
+ efi_handle_t handle, const efi_guid_t *protocol,
+ void *old_interface, void *new_interface)
+{
+ efi_status_t ret;
+
+ EFI_ENTRY("%p, %pUs, %p, %p", handle, protocol, old_interface,
+ new_interface);
+
+ /* Uninstall protocol but do not delete handle */
+ ret = efi_uninstall_protocol(handle, protocol, old_interface, true);
+ if (ret != EFI_SUCCESS)
+ goto out;
+
+ /* Install the new protocol */
+ ret = efi_add_protocol(handle, protocol, new_interface);
+ /*
+ * The UEFI spec does not specify what should happen to the handle
+ * if in case of an error no protocol interface remains on the handle.
+ * So let's do nothing here.
+ */
+ if (ret != EFI_SUCCESS)
+ goto out;
+ /*
+ * The returned status code has to be ignored.
+ * Do not create an error if no suitable driver for the handle exists.
+ */
+ EFI_CALL(efi_connect_controller(handle, NULL, NULL, true));
+out:
+ return EFI_EXIT(ret);
+}
+
+/**
+ * efi_get_child_controllers() - get all child controllers associated to a driver
+ * @efiobj: handle of the controller
+ * @driver_handle: handle of the driver
+ * @number_of_children: number of child controllers
+ * @child_handle_buffer: handles of the the child controllers
+ *
+ * The allocated buffer has to be freed with free().
+ *
+ * Return: status code
+ */
+static efi_status_t efi_get_child_controllers(
+ struct efi_object *efiobj,
+ efi_handle_t driver_handle,
+ efi_uintn_t *number_of_children,
+ efi_handle_t **child_handle_buffer)
+{
+ struct efi_handler *handler;
+ struct efi_open_protocol_info_item *item;
+ efi_uintn_t count = 0, i;
+ bool duplicate;
+
+ /* Count all child controller associations */
+ list_for_each_entry(handler, &efiobj->protocols, link) {
+ list_for_each_entry(item, &handler->open_infos, link) {
+ if (item->info.agent_handle == driver_handle &&
+ item->info.attributes &
+ EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER)
+ ++count;
+ }
+ }
+ /*
+ * Create buffer. In case of duplicate child controller assignments
+ * the buffer will be too large. But that does not harm.
+ */
+ *number_of_children = 0;
+ if (!count)
+ return EFI_SUCCESS;
+ *child_handle_buffer = calloc(count, sizeof(efi_handle_t));
+ if (!*child_handle_buffer)
+ return EFI_OUT_OF_RESOURCES;
+ /* Copy unique child handles */
+ list_for_each_entry(handler, &efiobj->protocols, link) {
+ list_for_each_entry(item, &handler->open_infos, link) {
+ if (item->info.agent_handle == driver_handle &&
+ item->info.attributes &
+ EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER) {
+ /* Check this is a new child controller */
+ duplicate = false;
+ for (i = 0; i < *number_of_children; ++i) {
+ if ((*child_handle_buffer)[i] ==
+ item->info.controller_handle)
+ duplicate = true;
+ }
+ /* Copy handle to buffer */
+ if (!duplicate) {
+ i = (*number_of_children)++;
+ (*child_handle_buffer)[i] =
+ item->info.controller_handle;
+ }
+ }
+ }
+ }
+ return EFI_SUCCESS;
+}
+
+/**
+ * efi_disconnect_controller() - disconnect a controller from a driver
+ * @controller_handle: handle of the controller
+ * @driver_image_handle: handle of the driver
+ * @child_handle: handle of the child to destroy
+ *
+ * This function implements the DisconnectController service.
+ *
+ * See the Unified Extensible Firmware Interface (UEFI) specification for
+ * details.
+ *
+ * Return: status code
+ */
+efi_status_t EFIAPI efi_disconnect_controller(
+ efi_handle_t controller_handle,
+ efi_handle_t driver_image_handle,
+ efi_handle_t child_handle)
+{
+ struct efi_driver_binding_protocol *binding_protocol;
+ efi_handle_t *child_handle_buffer = NULL;
+ size_t number_of_children = 0;
+ efi_status_t r;
+ struct efi_object *efiobj;
+ bool sole_child;
+
+ EFI_ENTRY("%p, %p, %p", controller_handle, driver_image_handle,
+ child_handle);
+
+ efiobj = efi_search_obj(controller_handle);
+ if (!efiobj) {
+ r = EFI_INVALID_PARAMETER;
+ goto out;
+ }
+
+ if (child_handle && !efi_search_obj(child_handle)) {
+ r = EFI_INVALID_PARAMETER;
+ goto out;
+ }
+
+ /* If no driver handle is supplied, disconnect all drivers */
+ if (!driver_image_handle) {
+ r = efi_disconnect_all_drivers(efiobj, NULL, child_handle);
+ goto out;
+ }
+
+ /* Create list of child handles */
+ r = efi_get_child_controllers(efiobj,
+ driver_image_handle,
+ &number_of_children,
+ &child_handle_buffer);
+ if (r != EFI_SUCCESS)
+ return r;
+ sole_child = (number_of_children == 1);
+
+ if (child_handle) {
+ number_of_children = 1;
+ free(child_handle_buffer);
+ child_handle_buffer = &child_handle;
+ }
+
+ /* Get the driver binding protocol */
+ r = EFI_CALL(efi_open_protocol(driver_image_handle,
+ &efi_driver_binding_protocol_guid,
+ (void **)&binding_protocol,
+ driver_image_handle, NULL,
+ EFI_OPEN_PROTOCOL_GET_PROTOCOL));
+ if (r != EFI_SUCCESS) {
+ r = EFI_INVALID_PARAMETER;
+ goto out;
+ }
+ /* Remove the children */
+ if (number_of_children) {
+ r = EFI_CALL(binding_protocol->stop(binding_protocol,
+ controller_handle,
+ number_of_children,
+ child_handle_buffer));
+ if (r != EFI_SUCCESS) {
+ r = EFI_DEVICE_ERROR;
+ goto out;
+ }
+ }
+ /* Remove the driver */
+ if (!child_handle || sole_child) {
+ r = EFI_CALL(binding_protocol->stop(binding_protocol,
+ controller_handle,
+ 0, NULL));
+ if (r != EFI_SUCCESS) {
+ r = EFI_DEVICE_ERROR;
+ goto out;
+ }
+ }
+ efi_close_protocol(driver_image_handle,
+ &efi_driver_binding_protocol_guid,
+ driver_image_handle, NULL);
+ r = EFI_SUCCESS;
+out:
+ if (!child_handle)
+ free(child_handle_buffer);
+ return EFI_EXIT(r);
+}
+
+static struct efi_boot_services efi_boot_services = {
+ .hdr = {
+ .signature = EFI_BOOT_SERVICES_SIGNATURE,
+ .revision = EFI_SPECIFICATION_VERSION,
+ .headersize = sizeof(struct efi_boot_services),
+ },
+ .raise_tpl = efi_raise_tpl,
+ .restore_tpl = efi_restore_tpl,
+ .allocate_pages = efi_allocate_pages_ext,
+ .free_pages = efi_free_pages_ext,
+ .get_memory_map = efi_get_memory_map_ext,
+ .allocate_pool = efi_allocate_pool_ext,
+ .free_pool = efi_free_pool_ext,
+ .create_event = efi_create_event_ext,
+ .set_timer = efi_set_timer_ext,
+ .wait_for_event = efi_wait_for_event,
+ .signal_event = efi_signal_event_ext,
+ .close_event = efi_close_event,
+ .check_event = efi_check_event,
+ .install_protocol_interface = efi_install_protocol_interface,
+ .reinstall_protocol_interface = efi_reinstall_protocol_interface,
+ .uninstall_protocol_interface = efi_uninstall_protocol_interface,
+ .handle_protocol = efi_handle_protocol,
+ .register_protocol_notify = efi_register_protocol_notify,
+ .locate_handle = efi_locate_handle_ext,
+ .locate_device_path = efi_locate_device_path,
+ .install_configuration_table = efi_install_configuration_table_ext,
+ .load_image = (void *)efi_unimplemented,
+ .start_image = efi_start_image,
+ .exit = efi_exit,
+ .unload_image = efi_unload_image,
+ .exit_boot_services = efi_exit_boot_services,
+ .get_next_monotonic_count = efi_get_next_monotonic_count,
+ .stall = efi_stall,
+ .set_watchdog_timer = efi_set_watchdog_timer,
+ .connect_controller = efi_connect_controller,
+ .disconnect_controller = efi_disconnect_controller,
+ .open_protocol = efi_open_protocol,
+ .close_protocol = efi_close_protocol_ext,
+ .open_protocol_information = efi_open_protocol_information,
+ .protocols_per_handle = efi_protocols_per_handle,
+ .locate_handle_buffer = efi_locate_handle_buffer,
+ .locate_protocol = efi_locate_protocol,
+ .install_multiple_protocol_interfaces =
+ efi_install_multiple_protocol_interfaces_ext,
+ .uninstall_multiple_protocol_interfaces =
+ efi_uninstall_multiple_protocol_interfaces_ext,
+ .calculate_crc32 = efi_calculate_crc32,
+ .copy_mem = efi_copy_mem,
+ .set_mem = efi_set_mem,
+ .create_event_ex = efi_create_event_ex,
+};
+
+static __efi_runtime_data u16 firmware_vendor[] = L"barebox";
+
+__efi_runtime_data struct efi_system_table systab = {
+ .hdr = {
+ .signature = EFI_SYSTEM_TABLE_SIGNATURE,
+ .revision = EFI_SPECIFICATION_VERSION,
+ .headersize = sizeof(struct efi_system_table),
+ },
+ .fw_vendor = firmware_vendor,
+ .fw_revision = LINUX_VERSION_CODE, /* barebox version, formatted as 0xYYYYMMPP */
+ .nr_tables = 0,
+ .tables = NULL,
+};
+
+/**
+ * efi_alloc_system_table() - Allocate system table
+ *
+ * Return: status code
+ */
+efi_status_t efi_alloc_system_table(void)
+{
+ /* Allocate configuration table array */
+ return efi_allocate_pool(EFI_RUNTIME_SERVICES_DATA,
+ EFI_MAX_CONFIGURATION_TABLES *
+ sizeof(struct efi_config_table),
+ (void **)&systab.tables, "systab");
+}
+
+/**
+ * efi_initialize_system_table() - Initialize system table
+ *
+ * Return: status code
+ */
+efi_status_t efi_initialize_system_table(void)
+{
+ systab.boottime = &efi_boot_services;
+
+ /* Set CRC32 field in table headers */
+ efi_update_table_header_crc32(&systab.hdr);
+ efi_update_table_header_crc32(&efi_boot_services.hdr);
+
+ if (IS_ENABLED(CONFIG_EFI_LOADER_DEBUG_SUPPORT)) {
+ efi_guid_t debug_image_info_table_guid =
+ efi_debug_image_info_table_guid;
+ efi_status_t ret;
+
+ ret = efi_initialize_system_table_pointer();
+ if (ret != EFI_SUCCESS)
+ return ret;
+
+ ret = efi_install_configuration_table(&debug_image_info_table_guid,
+ &efi_m_debug_info_table_header);
+ if (ret != EFI_SUCCESS)
+ return ret;
+ }
+
+ return EFI_SUCCESS;
+}
diff --git a/include/efi/loader.h b/include/efi/loader.h
index f2ca32c578a2..5b6b7d996de0 100644
--- a/include/efi/loader.h
+++ b/include/efi/loader.h
@@ -21,6 +21,10 @@ extern efi_uintn_t efi_memory_map_key;
extern struct efi_system_table systab;
+/* Called by bootefi to initialize runtime */
+efi_status_t efi_alloc_system_table(void);
+efi_status_t efi_initialize_system_table(void);
+
/* Allocate boot service data pool memory */
void *efi_alloc(size_t len, const char *name);
/* Reallocate boot service data pool memory */
diff --git a/include/efi/loader/event.h b/include/efi/loader/event.h
new file mode 100644
index 000000000000..72fca88db77d
--- /dev/null
+++ b/include/efi/loader/event.h
@@ -0,0 +1,93 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+#ifndef __EFI_LOADER_EVENT_H_
+#define __EFI_LOADER_EVENT_H_
+
+#include <efi/types.h>
+#include <efi/services.h>
+#include <linux/list.h>
+
+/**
+ * struct efi_event
+ *
+ * @link: Link to list of all events
+ * @queue_link: Link to the list of queued events
+ * @type: Type of event, see efi_create_event
+ * @notify_tpl: Task priority level of notifications
+ * @notify_function: Function to call when the event is triggered
+ * @notify_context: Data to be passed to the notify function
+ * @group: Event group
+ * @trigger_time: Period of the timer
+ * @trigger_next: Next time to trigger the timer
+ * @trigger_type: Type of timer, see efi_set_timer
+ * @is_signaled: The event occurred. The event is in the signaled state.
+ */
+struct efi_event {
+ struct list_head link;
+ struct list_head queue_link;
+ uint32_t type;
+ efi_uintn_t notify_tpl;
+ void (EFIAPI *notify_function)(struct efi_event *event, void *context);
+ void *notify_context;
+ const efi_guid_t *group;
+ u64 trigger_next;
+ u64 trigger_time;
+ enum efi_timer_delay trigger_type;
+ bool is_signaled;
+};
+
+/* List of all events */
+extern struct list_head efi_events;
+
+/**
+ * struct efi_protocol_notification - handle for notified protocol
+ *
+ * When a protocol interface is installed for which an event was registered with
+ * the RegisterProtocolNotify() service this structure is used to hold the
+ * handle on which the protocol interface was installed.
+ *
+ * @link: link to list of all handles notified for this event
+ * @handle: handle on which the notified protocol interface was installed
+ */
+struct efi_protocol_notification {
+ struct list_head link;
+ efi_handle_t handle;
+};
+
+/**
+ * struct efi_register_notify_event - event registered by
+ * RegisterProtocolNotify()
+ *
+ * The address of this structure serves as registration value.
+ *
+ * @link: link to list of all registered events
+ * @event: registered event. The same event may registered for multiple
+ * GUIDs.
+ * @protocol: protocol for which the event is registered
+ * @handles: linked list of all handles on which the notified protocol was
+ * installed
+ */
+struct efi_register_notify_event {
+ struct list_head link;
+ struct efi_event *event;
+ efi_guid_t protocol;
+ struct list_head handles;
+};
+
+/* Call this to create an event */
+efi_status_t efi_create_event(uint32_t type, efi_uintn_t notify_tpl,
+ void (EFIAPI *notify_function) (
+ struct efi_event *event,
+ void *context),
+ void *notify_context, const efi_guid_t *group,
+ struct efi_event **event);
+/* Call this to close an event */
+efi_status_t EFIAPI efi_close_event(struct efi_event *event);
+/* Call this to set a timer */
+efi_status_t efi_set_timer(struct efi_event *event, enum efi_timer_delay type,
+ uint64_t trigger_time);
+/* Call this to signal an event */
+void efi_signal_event(struct efi_event *event);
+/* Called from places to check whether a timer expired */
+void efi_timer_check(void);
+
+#endif
diff --git a/include/efi/loader/image.h b/include/efi/loader/image.h
new file mode 100644
index 000000000000..d434844186e7
--- /dev/null
+++ b/include/efi/loader/image.h
@@ -0,0 +1,18 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+#ifndef _EFI_LOADER_IMAGE_H
+#define _EFI_LOADER_IMAGE_H
+
+#include <efi/types.h>
+
+#define EFI_DRYRUN 1
+#define EFI_VERBOSE_RUN 2
+
+/* Load image */
+efi_status_t EFIAPI efiloader_load_image(bool boot_policy,
+ efi_handle_t parent_image,
+ struct efi_device_path *file_path,
+ void *source_buffer,
+ efi_uintn_t source_size,
+ efi_handle_t *image_handle);
+
+#endif
diff --git a/include/efi/loader/table.h b/include/efi/loader/table.h
new file mode 100644
index 000000000000..7481c1a30cb1
--- /dev/null
+++ b/include/efi/loader/table.h
@@ -0,0 +1,11 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+
+#ifndef __EFI_LOADER_TABLE_H
+#define __EFI_LOADER_TABLE_H
+
+#include <efi/types.h>
+
+/* Adds new or overrides configuration table entry to the system table */
+efi_status_t efi_install_configuration_table(const efi_guid_t *guid, void *table);
+
+#endif
diff --git a/include/efi/services.h b/include/efi/services.h
index f6fd268b2923..3b9217a3bf22 100644
--- a/include/efi/services.h
+++ b/include/efi/services.h
@@ -39,6 +39,8 @@ enum efi_timer_delay {
struct efi_event;
struct efi_memory_desc;
+#define EFI_BOOT_SERVICES_SIGNATURE ((u64)0x56524553544f4f42ULL)
+
/*
* EFI Boot Services table
*/
--
2.47.3
^ permalink raw reply [flat|nested] 55+ messages in thread* [PATCH v1 19/54] efi: loader: add support for runtime services before ExitBootServices
2025-12-18 10:37 [PATCH v1 00/54] efi: implement EFI loader support in barebox Ahmad Fatoum
` (17 preceding siblings ...)
2025-12-18 10:37 ` [PATCH v1 18/54] efi: loader: add boot services support Ahmad Fatoum
@ 2025-12-18 10:37 ` Ahmad Fatoum
2025-12-18 10:37 ` [PATCH v1 20/54] efi: loader: setup root node Ahmad Fatoum
` (34 subsequent siblings)
53 siblings, 0 replies; 55+ messages in thread
From: Ahmad Fatoum @ 2025-12-18 10:37 UTC (permalink / raw)
To: barebox; +Cc: Ahmad Fatoum
Before ExitBootServices, barebox is the main firmware running and
shutdown_barebox has not been called, so we can easily implement
services using normal functionality. Start by implementing the low
hanging fruits.
This will be further fleshed out in later commits.
Signed-off-by: Ahmad Fatoum <a.fatoum@pengutronix.de>
---
efi/Kconfig | 8 +
efi/loader/Kconfig | 20 ++
efi/loader/Makefile | 1 +
efi/loader/boot.c | 6 +
efi/loader/runtime.c | 308 ++++++++++++++++++++++++++++++
efi/runtime/Kconfig | 44 +++++
include/efi/loader.h | 4 +
include/efi/table/rt_properties.h | 30 +++
8 files changed, 421 insertions(+)
create mode 100644 efi/loader/runtime.c
create mode 100644 efi/runtime/Kconfig
create mode 100644 include/efi/table/rt_properties.h
diff --git a/efi/Kconfig b/efi/Kconfig
index 31fccaaa8640..792073a9ef24 100644
--- a/efi/Kconfig
+++ b/efi/Kconfig
@@ -29,6 +29,7 @@ config EFI_LOADER
bool "barebox as EFI loader (provider)"
depends on HAVE_EFI_LOADER
select EFI
+ select EFI_RUNTIME
select EFI_GUID
select EFI_DEVICEPATH
select CHARSET
@@ -46,6 +47,13 @@ if EFI_LOADER
source "efi/loader/Kconfig"
endif
+config EFI_RUNTIME
+ bool
+
+if EFI_RUNTIME
+source "efi/runtime/Kconfig"
+endif
+
config EFI
bool
diff --git a/efi/loader/Kconfig b/efi/loader/Kconfig
index 43a80869cf8b..eaa5087b9ab8 100644
--- a/efi/loader/Kconfig
+++ b/efi/loader/Kconfig
@@ -9,3 +9,23 @@ config EFI_LOADER_DEBUG_SUPPORT
Table and the EFI_SYSTEM_TABLE_POINTER which is used by the debug
agent or an external debugger to determine loaded image information
in a quiescent manner.
+
+menu "UEFI services"
+
+config EFI_LOADER_GET_TIME
+ bool "GetTime() runtime service"
+ depends on RTC_CLASS
+ default y
+ help
+ Provide the GetTime() runtime service at boottime. This service
+ can be used by an EFI application to read the real time clock.
+
+config EFI_LOADER_SET_TIME
+ bool "SetTime() runtime service"
+ depends on RTC_CLASS
+ default y
+ help
+ Provide the SetTime() runtime service at boottime. This service
+ can be used by an EFI application to adjust the real time clock.
+
+endmenu
diff --git a/efi/loader/Makefile b/efi/loader/Makefile
index a25960b477db..92e4b51eb665 100644
--- a/efi/loader/Makefile
+++ b/efi/loader/Makefile
@@ -6,3 +6,4 @@ obj-y += table.o
obj-y += devicepath.o
obj-y += debug_support.o
obj-y += boot.o
+obj-y += runtime.o
diff --git a/efi/loader/boot.c b/efi/loader/boot.c
index 7d8e2914c3a0..1e2496f1ec53 100644
--- a/efi/loader/boot.c
+++ b/efi/loader/boot.c
@@ -3233,8 +3233,14 @@ efi_status_t efi_initialize_system_table(void)
{
systab.boottime = &efi_boot_services;
+ /*
+ * This will be overwritten dynamically at detach time.
+ */
+ systab.runtime = &efi_runtime_services;
+
/* Set CRC32 field in table headers */
efi_update_table_header_crc32(&systab.hdr);
+ efi_update_table_header_crc32(&efi_runtime_services.hdr);
efi_update_table_header_crc32(&efi_boot_services.hdr);
if (IS_ENABLED(CONFIG_EFI_LOADER_DEBUG_SUPPORT)) {
diff --git a/efi/loader/runtime.c b/efi/loader/runtime.c
new file mode 100644
index 000000000000..76b9bc101a06
--- /dev/null
+++ b/efi/loader/runtime.c
@@ -0,0 +1,308 @@
+// SPDX-License-Identifier: GPL-2.0+
+// SPDX-Comment: Origin-URL: https://github.com/u-boot/u-boot/blob/e9c34fab18a9a0022b36729afd8e262e062764e2/lib/efi_loader/efi_runtime.c
+/*
+ * EFI application runtime services
+ *
+ * Copyright (c) 2016 Alexander Graf
+ */
+
+#define pr_fmt(fmt) "efi-loader: runtime: " fmt
+
+#include <efi/loader.h>
+#include <efi/guid.h>
+#include <efi/error.h>
+#include <linux/printk.h>
+#include <barebox.h>
+#include <efi/loader/table.h>
+#include <efi/loader/trace.h>
+#include <efi/loader/event.h>
+#include <efi/mode.h>
+#include <efi/table/rt_properties.h>
+#include <poweroff.h>
+#include <restart.h>
+#include <linux/rtc.h>
+
+
+/*
+ * EFI runtime code lives in two stages. In the first stage, EFI loader and an
+ * EFI payload are running concurrently at the same time. In this mode, we can
+ * handle a good number of runtime callbacks
+ */
+
+#define CHECK_RT_FLAG(call) \
+ (IS_ENABLED(CONFIG_EFI_RUNTIME_##call) ? EFI_RT_SUPPORTED_##call : 0)
+
+/**
+ * efi_init_runtime_supported() - create runtime properties table
+ *
+ * Create a configuration table specifying which services are available at
+ * runtime.
+ *
+ * Return: status code
+ */
+efi_status_t efi_init_runtime_supported(void)
+{
+ efi_status_t ret;
+ struct efi_rt_properties_table *rt_table;
+
+ ret = efi_allocate_pool(EFI_RUNTIME_SERVICES_DATA,
+ sizeof(struct efi_rt_properties_table),
+ (void **)&rt_table, "RtTable");
+ if (ret != EFI_SUCCESS)
+ return ret;
+
+ rt_table->version = EFI_RT_PROPERTIES_TABLE_VERSION;
+ rt_table->length = sizeof(struct efi_rt_properties_table);
+ rt_table->runtime_services_supported =
+ CHECK_RT_FLAG(GET_TIME) |
+ CHECK_RT_FLAG(SET_TIME) |
+ CHECK_RT_FLAG(GET_WAKEUP_TIME) |
+ CHECK_RT_FLAG(SET_WAKEUP_TIME) |
+ CHECK_RT_FLAG(GET_VARIABLE) |
+ CHECK_RT_FLAG(GET_NEXT_VARIABLE_NAME) |
+ CHECK_RT_FLAG(SET_VARIABLE) |
+ CHECK_RT_FLAG(SET_VIRTUAL_ADDRESS_MAP) |
+ CHECK_RT_FLAG(CONVERT_POINTER) |
+ CHECK_RT_FLAG(GET_NEXT_HIGH_MONOTONIC_COUNT) |
+ CHECK_RT_FLAG(RESET_SYSTEM) |
+ CHECK_RT_FLAG(UPDATE_CAPSULE) |
+ CHECK_RT_FLAG(QUERY_CAPSULE_CAPABILITIES) |
+ CHECK_RT_FLAG(QUERY_VARIABLE_INFO);
+
+ return efi_install_configuration_table(&efi_rt_properties_table_guid, rt_table);
+}
+
+/**
+ * efi_reset_system_boottime() - reset system at boot time
+ *
+ * This function implements the ResetSystem() runtime service before
+ * SetVirtualAddressMap() is called.
+ *
+ * See the Unified Extensible Firmware Interface (UEFI) specification for
+ * details.
+ *
+ * @reset_type: type of reset to perform
+ * @reset_status: status code for the reset
+ * @data_size: size of reset_data
+ * @reset_data: information about the reset
+ */
+static void EFIAPI efi_reset_system_boottime(
+ enum efi_reset_type reset_type,
+ efi_status_t reset_status,
+ size_t data_size, void *reset_data)
+{
+ struct efi_event *evt;
+
+ EFI_ENTRY("%d %lx %zx %p", reset_type, reset_status, data_size,
+ reset_data);
+
+ /* Notify reset */
+ list_for_each_entry(evt, &efi_events, link) {
+ if (evt->group &&
+ !efi_guidcmp(*evt->group,
+ efi_guid_event_group_reset_system)) {
+ efi_signal_event(evt);
+ break;
+ }
+ }
+
+ switch (reset_type) {
+ case EFI_RESET_WARM:
+ restart_machine(RESTART_WARM);
+ break;
+ case EFI_RESET_COLD:
+ case EFI_RESET_PLATFORM_SPECIFIC:
+ restart_machine(0);
+ break;
+ case EFI_RESET_SHUTDOWN:
+ poweroff_machine(0);
+ break;
+ }
+
+ hang();
+}
+
+/**
+ * efi_get_time_boottime() - get current time at boot time
+ *
+ * This function implements the GetTime runtime service before
+ * SetVirtualAddressMap() is called.
+ *
+ * See the Unified Extensible Firmware Interface (UEFI) specification
+ * for details.
+ *
+ * @time: pointer to structure to receive current time
+ * @capabilities: pointer to structure to receive RTC properties
+ * Returns: status code
+ */
+static efi_status_t EFIAPI efi_get_time_boottime(
+ struct efi_time *time,
+ struct efi_time_cap *capabilities)
+{
+ efi_status_t ret;
+ struct rtc_time tm;
+ struct rtc_device *rtc;
+
+ EFI_ENTRY("%p %p", time, capabilities);
+
+ if (!IS_ENABLED(CONFIG_EFI_LOADER_GET_TIME)) {
+ ret = EFI_UNSUPPORTED;
+ goto out;
+ }
+
+ if (!time) {
+ ret = EFI_INVALID_PARAMETER;
+ goto out;
+ }
+
+ rtc = rtc_lookup("rtc0");
+ if (IS_ERR(rtc)) {
+ ret = EFI_UNSUPPORTED;
+ goto out;
+ }
+
+ if (rtc_read_time(rtc, &tm)) {
+ ret = EFI_DEVICE_ERROR;
+ goto out;
+ }
+
+ memset(time, 0, sizeof(*time));
+ time->year = tm.tm_year;
+ time->month = tm.tm_mon;
+ time->day = tm.tm_mday;
+ time->hour = tm.tm_hour;
+ time->minute = tm.tm_min;
+ time->second = tm.tm_sec;
+
+ if (tm.tm_isdst > 0)
+ time->daylight =
+ EFI_TIME_ADJUST_DAYLIGHT | EFI_TIME_IN_DAYLIGHT;
+ else if (!tm.tm_isdst)
+ time->daylight = EFI_TIME_ADJUST_DAYLIGHT;
+ else
+ time->daylight = 0;
+ time->timezone = EFI_UNSPECIFIED_TIMEZONE;
+
+ if (capabilities) {
+ /* Set reasonable dummy values */
+ capabilities->resolution = 1; /* 1 Hz */
+ capabilities->accuracy = 100000000; /* 100 ppm */
+ capabilities->sets_to_zero = false;
+ }
+
+ ret = EFI_SUCCESS;
+out:
+ return EFI_EXIT(ret);
+}
+
+
+/**
+ * efi_validate_time() - checks if timestamp is valid
+ *
+ * @time: timestamp to validate
+ * Returns: 0 if timestamp is valid, 1 otherwise
+ */
+static int efi_validate_time(struct efi_time *time)
+{
+ return (!time ||
+ time->year < 1900 || time->year > 9999 ||
+ !time->month || time->month > 12 || !time->day ||
+ time->day > rtc_month_days(time->month - 1, time->year) ||
+ time->hour > 23 || time->minute > 59 || time->second > 59 ||
+ time->nanosecond > 999999999 ||
+ time->daylight &
+ ~(EFI_TIME_IN_DAYLIGHT | EFI_TIME_ADJUST_DAYLIGHT) ||
+ ((time->timezone < -1440 || time->timezone > 1440) &&
+ time->timezone != EFI_UNSPECIFIED_TIMEZONE));
+}
+
+/**
+ * efi_set_time_boottime() - set current time
+ *
+ * This function implements the SetTime() runtime service before
+ * SetVirtualAddressMap() is called.
+ *
+ * See the Unified Extensible Firmware Interface (UEFI) specification
+ * for details.
+ *
+ * @time: pointer to structure to with current time
+ * Returns: status code
+ */
+static efi_status_t EFIAPI efi_set_time_boottime(struct efi_time *time)
+{
+ efi_status_t ret = EFI_SUCCESS;
+ struct rtc_time tm;
+ struct rtc_device *rtc;
+
+ if (!IS_ENABLED(CONFIG_EFI_LOADER_SET_TIME))
+ return EFI_EXIT(EFI_UNSUPPORTED);
+
+ EFI_ENTRY("%p", time);
+
+ if (efi_validate_time(time)) {
+ ret = EFI_INVALID_PARAMETER;
+ goto out;
+ }
+
+ rtc = rtc_lookup("rtc0");
+ if (IS_ERR(rtc)) {
+ ret = EFI_UNSUPPORTED;
+ goto out;
+ }
+
+ memset(&tm, 0, sizeof(tm));
+ tm.tm_year = time->year;
+ tm.tm_mon = time->month;
+ tm.tm_mday = time->day;
+ tm.tm_hour = time->hour;
+ tm.tm_min = time->minute;
+ tm.tm_sec = time->second;
+
+ switch (time->daylight) {
+ case EFI_TIME_ADJUST_DAYLIGHT:
+ tm.tm_isdst = 0;
+ break;
+ case EFI_TIME_ADJUST_DAYLIGHT | EFI_TIME_IN_DAYLIGHT:
+ tm.tm_isdst = 1;
+ break;
+ default:
+ tm.tm_isdst = -1;
+ break;
+ }
+
+ /* Calculate day of week */
+ rtc_calc_weekday(&tm);
+
+ if (rtc_set_time(rtc, &tm))
+ ret = EFI_DEVICE_ERROR;
+out:
+ return EFI_EXIT(ret);
+}
+
+/*
+ * The implementation of these services depends on barebox still running,
+ * i.e. BS->ExitBootServices() was not yet called. When it's called,
+ * the runtime services will be replaced by the detached implementation,
+ * so we need not take any special consideration here.
+ */
+struct efi_runtime_services efi_runtime_services = {
+ .hdr = {
+ .signature = EFI_RUNTIME_SERVICES_SIGNATURE,
+ .revision = EFI_SPECIFICATION_VERSION,
+ .headersize = sizeof(struct efi_runtime_services),
+ },
+ .get_time = &efi_get_time_boottime,
+ .set_time = &efi_set_time_boottime,
+ .get_wakeup_time = (void *)efi_unimplemented,
+ .set_wakeup_time = (void *)efi_unimplemented,
+ .set_virtual_address_map = (void *)efi_unimplemented,
+ .convert_pointer = (void *)efi_unimplemented,
+ .get_variable = (void *)efi_unimplemented,
+ .get_next_variable = (void *)efi_unimplemented,
+ .set_variable = (void *)efi_unimplemented,
+ .get_next_high_mono_count = (void *)efi_unimplemented,
+ .reset_system = &efi_reset_system_boottime,
+ .update_capsule = (void *)efi_unimplemented,
+ .query_capsule_caps = (void *)efi_unimplemented,
+ .query_variable_info = (void *)efi_unimplemented,
+};
diff --git a/efi/runtime/Kconfig b/efi/runtime/Kconfig
new file mode 100644
index 000000000000..4506322305b9
--- /dev/null
+++ b/efi/runtime/Kconfig
@@ -0,0 +1,44 @@
+# SPDX-License-Identifier: GPL-2.0
+
+config EFI_RUNTIME_GET_TIME
+ bool
+
+config EFI_RUNTIME_SET_TIME
+ bool
+
+config EFI_RUNTIME_GET_WAKEUP_TIME
+ bool
+
+config EFI_RUNTIME_SET_WAKEUP_TIME
+ bool
+
+config EFI_RUNTIME_GET_VARIABLE
+ bool
+
+config EFI_RUNTIME_GET_NEXT_VARIABLE_NAME
+ bool
+
+config EFI_RUNTIME_SET_VARIABLE
+ bool
+
+config EFI_RUNTIME_SET_VIRTUAL_ADDRESS_MAP
+ bool
+
+config EFI_RUNTIME_CONVERT_POINTER
+ bool
+
+config EFI_RUNTIME_GET_NEXT_HIGH_MONOTONIC_COUNT
+ bool
+
+config EFI_RUNTIME_RESET_SYSTEM
+ def_bool y
+
+config EFI_RUNTIME_UPDATE_CAPSULE
+ bool
+
+config EFI_RUNTIME_QUERY_CAPSULE_CAPABILITIES
+ bool
+
+config EFI_RUNTIME_QUERY_VARIABLE_INFO
+ bool
+
diff --git a/include/efi/loader.h b/include/efi/loader.h
index 5b6b7d996de0..8a51cde0ee25 100644
--- a/include/efi/loader.h
+++ b/include/efi/loader.h
@@ -19,6 +19,7 @@ struct efi_table_hdr;
/* Key identifying current memory map */
extern efi_uintn_t efi_memory_map_key;
+extern struct efi_runtime_services efi_runtime_services;
extern struct efi_system_table systab;
/* Called by bootefi to initialize runtime */
@@ -52,6 +53,9 @@ efi_status_t efi_get_memory_map(efi_uintn_t *memory_map_size,
efi_uintn_t *descriptor_size,
uint32_t *descriptor_version);
+/* Indicate supported runtime services */
+efi_status_t efi_init_runtime_supported(void);
+
/* Update CRC32 in table header */
void efi_update_table_header_crc32(struct efi_table_hdr *table);
diff --git a/include/efi/table/rt_properties.h b/include/efi/table/rt_properties.h
new file mode 100644
index 000000000000..aec6777dbc45
--- /dev/null
+++ b/include/efi/table/rt_properties.h
@@ -0,0 +1,30 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+#ifndef __EF_TABLE_RT_PROPERTIES_H_
+#define __EF_TABLE_RT_PROPERTIES_H_
+
+#include <linux/types.h>
+
+#define EFI_RT_SUPPORTED_GET_TIME 0x0001
+#define EFI_RT_SUPPORTED_SET_TIME 0x0002
+#define EFI_RT_SUPPORTED_GET_WAKEUP_TIME 0x0004
+#define EFI_RT_SUPPORTED_SET_WAKEUP_TIME 0x0008
+#define EFI_RT_SUPPORTED_GET_VARIABLE 0x0010
+#define EFI_RT_SUPPORTED_GET_NEXT_VARIABLE_NAME 0x0020
+#define EFI_RT_SUPPORTED_SET_VARIABLE 0x0040
+#define EFI_RT_SUPPORTED_SET_VIRTUAL_ADDRESS_MAP 0x0080
+#define EFI_RT_SUPPORTED_CONVERT_POINTER 0x0100
+#define EFI_RT_SUPPORTED_GET_NEXT_HIGH_MONOTONIC_COUNT 0x0200
+#define EFI_RT_SUPPORTED_RESET_SYSTEM 0x0400
+#define EFI_RT_SUPPORTED_UPDATE_CAPSULE 0x0800
+#define EFI_RT_SUPPORTED_QUERY_CAPSULE_CAPABILITIES 0x1000
+#define EFI_RT_SUPPORTED_QUERY_VARIABLE_INFO 0x2000
+
+#define EFI_RT_PROPERTIES_TABLE_VERSION 0x1
+
+struct efi_rt_properties_table {
+ u16 version;
+ u16 length;
+ u32 runtime_services_supported;
+};
+
+#endif
--
2.47.3
^ permalink raw reply [flat|nested] 55+ messages in thread* [PATCH v1 20/54] efi: loader: setup root node
2025-12-18 10:37 [PATCH v1 00/54] efi: implement EFI loader support in barebox Ahmad Fatoum
` (18 preceding siblings ...)
2025-12-18 10:37 ` [PATCH v1 19/54] efi: loader: add support for runtime services before ExitBootServices Ahmad Fatoum
@ 2025-12-18 10:37 ` Ahmad Fatoum
2025-12-18 10:37 ` [PATCH v1 21/54] efi: loader: add watchdog support Ahmad Fatoum
` (33 subsequent siblings)
53 siblings, 0 replies; 55+ messages in thread
From: Ahmad Fatoum @ 2025-12-18 10:37 UTC (permalink / raw)
To: barebox; +Cc: Ahmad Fatoum
The root of the EFI object model is the EFI root node. Register it and
add infrastructure for protocols and tables to register themselves in
initcalls.
Signed-off-by: Ahmad Fatoum <a.fatoum@pengutronix.de>
---
efi/loader/Makefile | 1 +
efi/loader/setup.c | 232 +++++++++++++++++++++++++++++++++++++++++++
include/efi/loader.h | 9 ++
include/efi/mode.h | 23 ++++-
4 files changed, 261 insertions(+), 4 deletions(-)
create mode 100644 efi/loader/setup.c
diff --git a/efi/loader/Makefile b/efi/loader/Makefile
index 92e4b51eb665..bb739210ac1d 100644
--- a/efi/loader/Makefile
+++ b/efi/loader/Makefile
@@ -7,3 +7,4 @@ obj-y += devicepath.o
obj-y += debug_support.o
obj-y += boot.o
obj-y += runtime.o
+obj-y += setup.o
diff --git a/efi/loader/setup.c b/efi/loader/setup.c
new file mode 100644
index 000000000000..87b7d3a68dc8
--- /dev/null
+++ b/efi/loader/setup.c
@@ -0,0 +1,232 @@
+// SPDX-License-Identifier: GPL-2.0+
+// SPDX-Comment: Origin-URL: https://github.com/u-boot/u-boot/blob/e7a85ec651ed5794eb9a837e1073f6b3146af501/lib/efi_loader/efi_setup.c
+// SPDX-Comment: Origin-URL: https://github.com/u-boot/u-boot/blob/ac040ae58dba316d86edc788416fae58a5f44e3f/lib/efi_loader/efi_root_node.c
+/*
+ * EFI setup code
+ *
+ * Copyright (c) 2016-2018 Alexander Graf et al.
+ */
+
+#define pr_fmt(fmt) "efi-loader: setup: " fmt
+
+#include <linux/printk.h>
+#include <linux/array_size.h>
+#include <driver.h>
+#include <string.h>
+#include <magicvar.h>
+#include <efi/loader.h>
+#include <efi/mode.h>
+#include <efi/guid.h>
+#include <efi/devicepath.h>
+#include <efi/loader/object.h>
+#include <efi/error.h>
+#include <xfuncs.h>
+
+struct efi_boot_services *loaderBS;
+struct efi_runtime_services *loaderRT;
+
+const efi_guid_t efi_barebox_guid = BAREBOX_GUID;
+
+efi_handle_t efi_root = NULL;
+
+struct efi_root_dp {
+ struct efi_device_path_vendor vendor;
+ struct efi_device_path end;
+} __packed;
+
+/**
+ * efi_root_node_register() - create root node
+ *
+ * Create the root node on which we install all protocols that are
+ * not related to a loaded image or a driver.
+ *
+ * Return: status code
+ */
+static efi_status_t efi_root_node_register(void)
+{
+ efi_status_t ret;
+ struct efi_root_dp *dp;
+
+ /* Create device path protocol */
+ dp = calloc(1, sizeof(*dp));
+ if (!dp)
+ return EFI_OUT_OF_RESOURCES;
+
+ /* Fill vendor node */
+ dp->vendor.header.type = DEVICE_PATH_TYPE_HARDWARE_DEVICE;
+ dp->vendor.header.sub_type = DEVICE_PATH_SUB_TYPE_VENDOR;
+ dp->vendor.header.length = sizeof(struct efi_device_path_vendor);
+ dp->vendor.Guid = efi_barebox_guid;
+
+ /* Fill end node */
+ dp->end.type = DEVICE_PATH_TYPE_END;
+ dp->end.sub_type = DEVICE_PATH_SUB_TYPE_END;
+ dp->end.length = sizeof(struct efi_device_path);
+
+ /* Create root node and install protocols */
+ ret = efi_install_multiple_protocol_interfaces(&efi_root,
+ /* Device path protocol */
+ &efi_device_path_protocol_guid, dp,
+ /* Device path to text protocol */
+ NULL);
+
+ efi_root->type = EFI_OBJECT_TYPE_BAREBOX_FIRMWARE;
+
+ return ret;
+}
+
+struct efi_deferred_cb {
+ efi_status_t (*cb)(void *);
+ void *data;
+ struct list_head list;
+};
+
+struct efi_deferred_add_protocol_ctx {
+ efi_handle_t handle;
+ const efi_guid_t *protocol;
+ const void *interface;
+};
+
+struct efi_deferred_add_protocol {
+ struct efi_deferred_cb base;
+ struct efi_deferred_add_protocol_ctx ctx;
+};
+
+static LIST_HEAD(efi_deferred_cbs);
+
+void efi_register_deferred_init(efi_status_t (*init)(void *), void *data)
+{
+ struct efi_deferred_cb *deferred = xzalloc(sizeof(*deferred));
+
+ deferred->cb = init;
+ deferred->data = data;
+ list_add_tail(&deferred->list, &efi_deferred_cbs);
+}
+
+static efi_status_t add_protocol(void *_ctx)
+{
+ struct efi_deferred_add_protocol_ctx *ctx = _ctx;
+
+ return efi_add_protocol(ctx->handle ?: efi_root, ctx->protocol, ctx->interface);
+}
+
+void efi_add_root_node_protocol_deferred(const efi_guid_t *protocol, const void *interface)
+{
+ struct efi_deferred_add_protocol *deferred = xzalloc(sizeof(*deferred));
+
+ deferred->base.cb = add_protocol;
+ deferred->base.data = &deferred->ctx;
+
+ list_add_tail(&deferred->base.list, &efi_deferred_cbs);
+}
+
+/**
+ * efi_init_obj_list() - Initialize and populate EFI object list
+ *
+ * Return: status code
+ */
+efi_status_t efi_init_obj_list(void)
+{
+ struct efi_deferred_cb *deferred;
+ efi_status_t ret;
+
+ /* Initialize once only */
+ if (loaderBS)
+ return 0;
+
+ efi_loader_set_state(EFI_LOADER_BOOT);
+
+ /* Initialize root node */
+ ret = efi_root_node_register();
+ if (ret != EFI_SUCCESS)
+ goto err;
+
+ ret = efi_alloc_system_table();
+ if (ret != EFI_SUCCESS)
+ goto err;
+
+ list_for_each_entry(deferred, &efi_deferred_cbs, list)
+ deferred->cb(deferred->data);
+
+ /* Initialize system table */
+ ret = efi_initialize_system_table();
+ if (ret != EFI_SUCCESS)
+ goto err;
+
+ /* Indicate supported runtime services */
+ ret = efi_init_runtime_supported();
+ if (ret != EFI_SUCCESS)
+ goto err;
+
+ loaderBS = systab.boottime;
+ loaderRT = systab.runtime;
+
+ return 0;
+
+err:
+ efi_loader_set_state(EFI_LOADER_INACTIVE);
+ return ret;
+
+}
+
+struct device efidev = {
+ .name = "efi",
+ .id = DEVICE_ID_SINGLE,
+};
+
+static int loader_state;
+
+static const char *loader_state_names[] = {
+ [EFI_LOADER_INACTIVE] = "inactive",
+ [EFI_LOADER_BOOT] = "boot",
+ [EFI_LOADER_RUNTIME] = "runtime",
+};
+
+static int loader_state_set(struct param_d *param, void *data)
+{
+ switch (loader_state) {
+ case EFI_LOADER_INACTIVE:
+ return -EINVAL;
+ case EFI_LOADER_BOOT:
+ return -efi_errno(efi_init_obj_list());
+ case EFI_LOADER_RUNTIME:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int efi_init_params(void)
+{
+ int ret;
+
+ if (efi_is_payload())
+ return 0;
+
+ ret = platform_device_register(&efidev);
+ if (ret)
+ return ret;
+
+ dev_add_param_enum(&efidev, "loader",
+ loader_state_set, NULL, &loader_state,
+ loader_state_names, ARRAY_SIZE(loader_state_names),
+ NULL);
+
+ dev_add_param_fixed(&efidev, "payload_default_path",
+ CONFIG_EFI_PAYLOAD_DEFAULT_PATH);
+
+ return 0;
+}
+late_initcall(efi_init_params);
+
+BAREBOX_MAGICVAR(efi.loader, "EFI loader state (inactive, boot, runtime)");
+BAREBOX_MAGICVAR(efi.payload_default_path, "The arch-specific removable media path");
+
+void efi_loader_set_state(enum efi_loader_state state)
+{
+ loader_state = state;
+}
+
+enum efi_loader_state efi_is_loader(void)
+{
+ return loader_state;
+}
diff --git a/include/efi/loader.h b/include/efi/loader.h
index 8a51cde0ee25..e1a3c1e298c5 100644
--- a/include/efi/loader.h
+++ b/include/efi/loader.h
@@ -62,4 +62,13 @@ void efi_update_table_header_crc32(struct efi_table_hdr *table);
/* replacement function, returns EFI_UNSUPPORTED */
efi_status_t __efi_runtime EFIAPI efi_unimplemented(void);
+enum efi_loader_state;
+
+void efi_loader_set_state(enum efi_loader_state);
+
+extern struct device efidev;
+
+void efi_register_deferred_init(efi_status_t (*init)(void *), void *);
+void efi_add_root_node_protocol_deferred(const efi_guid_t *protocol, const void *interface);
+
#endif /* _EFI_LOADER_H */
diff --git a/include/efi/mode.h b/include/efi/mode.h
index f398d80d932f..d63c47e88dbb 100644
--- a/include/efi/mode.h
+++ b/include/efi/mode.h
@@ -9,23 +9,36 @@
struct efi_boot_services;
struct efi_runtime_services;
-extern struct efi_boot_services *BS;
-extern struct efi_runtime_services *RT;
+extern struct efi_boot_services *BS, *loaderBS;
+extern struct efi_runtime_services *RT, *loaderRT;
+
+enum efi_loader_state {
+ EFI_LOADER_INACTIVE = 0,
+ EFI_LOADER_BOOT,
+ EFI_LOADER_RUNTIME,
+};
static inline bool efi_is_payload(void)
{
return IS_ENABLED(CONFIG_EFI_PAYLOAD) && BS;
}
-static inline bool efi_is_loader(void)
+#ifdef CONFIG_EFI_LOADER
+enum efi_loader_state efi_is_loader(void);
+#else
+static inline enum efi_loader_state efi_is_loader(void)
{
- return false;
+ return EFI_LOADER_INACTIVE;
}
+#endif
+
static inline struct efi_boot_services *efi_get_boot_services(void)
{
if (efi_is_payload())
return BS;
+ if (efi_is_loader())
+ return loaderBS;
return NULL;
}
@@ -34,6 +47,8 @@ static inline struct efi_runtime_services *efi_get_runtime_services(void)
{
if (efi_is_payload())
return RT;
+ if (efi_is_loader())
+ return loaderRT;
return NULL;
}
--
2.47.3
^ permalink raw reply [flat|nested] 55+ messages in thread* [PATCH v1 21/54] efi: loader: add watchdog support
2025-12-18 10:37 [PATCH v1 00/54] efi: implement EFI loader support in barebox Ahmad Fatoum
` (19 preceding siblings ...)
2025-12-18 10:37 ` [PATCH v1 20/54] efi: loader: setup root node Ahmad Fatoum
@ 2025-12-18 10:37 ` Ahmad Fatoum
2025-12-18 10:37 ` [PATCH v1 22/54] efi: loader: move PE implementation out of common code Ahmad Fatoum
` (32 subsequent siblings)
53 siblings, 0 replies; 55+ messages in thread
From: Ahmad Fatoum @ 2025-12-18 10:37 UTC (permalink / raw)
To: barebox; +Cc: Ahmad Fatoum
The UEFI standard requires that the watchdog timer is set to five
minutes when invoking an EFI boot option.
Add the necessary support, so we can start configuring the watchdog
where appropriate.
Signed-off-by: Ahmad Fatoum <a.fatoum@pengutronix.de>
---
efi/loader/Makefile | 1 +
efi/loader/boot.c | 2 +-
efi/loader/watchdog.c | 94 +++++++++++++++++++++++++++++++++++++++++++
include/efi/loader.h | 3 ++
4 files changed, 99 insertions(+), 1 deletion(-)
create mode 100644 efi/loader/watchdog.c
diff --git a/efi/loader/Makefile b/efi/loader/Makefile
index bb739210ac1d..640c9707060c 100644
--- a/efi/loader/Makefile
+++ b/efi/loader/Makefile
@@ -8,3 +8,4 @@ obj-y += debug_support.o
obj-y += boot.o
obj-y += runtime.o
obj-y += setup.o
+obj-y += watchdog.o
diff --git a/efi/loader/boot.c b/efi/loader/boot.c
index 1e2496f1ec53..263165135944 100644
--- a/efi/loader/boot.c
+++ b/efi/loader/boot.c
@@ -1831,7 +1831,7 @@ static efi_status_t EFIAPI efi_set_watchdog_timer(unsigned long timeout,
{
EFI_ENTRY("%ld, 0x%llx, %zu, %p", timeout, watchdog_code,
data_size, watchdog_data);
- return EFI_EXIT(EFI_UNSUPPORTED);
+ return EFI_EXIT(efi_set_watchdog(timeout));
}
/**
diff --git a/efi/loader/watchdog.c b/efi/loader/watchdog.c
new file mode 100644
index 000000000000..a4715c149add
--- /dev/null
+++ b/efi/loader/watchdog.c
@@ -0,0 +1,94 @@
+// SPDX-License-Identifier: GPL-2.0+
+// SPDX-Comment: Origin-URL: https://github.com/u-boot/u-boot/blob/e9c34fab18a9a0022b36729afd8e262e062764e2/lib/efi_loader/efi_watchdog.c
+/*
+ * EFI watchdog
+ *
+ * Copyright (c) 2017 Heinrich Schuchardt
+ */
+
+#define pr_fmt(fmt) "efi-loader: watchdog: " fmt
+
+#include <linux/printk.h>
+#include <efi/loader.h>
+#include <efi/loader/event.h>
+#include <efi/loader/trace.h>
+#include <efi/services.h>
+#include <efi/error.h>
+#include <init.h>
+
+/* Conversion factor from seconds to multiples of 100ns */
+#define EFI_SECONDS_TO_100NS 10000000ULL
+
+static struct efi_event *watchdog_timer_event;
+
+/**
+ * efi_watchdog_timer_notify() - resets system upon watchdog event
+ *
+ * Reset the system when the watchdog event is notified.
+ *
+ * @event: the watchdog event
+ * @context: not used
+ */
+static void EFIAPI efi_watchdog_timer_notify(struct efi_event *event,
+ void *context)
+{
+ EFI_ENTRY("%p, %p", event, context);
+
+ pr_emerg("\nEFI: Watchdog timeout\n");
+ EFI_CALL_VOID(efi_runtime_services.reset_system(EFI_RESET_COLD,
+ EFI_SUCCESS, 0, NULL));
+
+ EFI_EXIT(EFI_UNSUPPORTED);
+}
+
+/**
+ * efi_set_watchdog() - resets the watchdog timer
+ *
+ * This function is used by the SetWatchdogTimer service.
+ *
+ * @timeout: seconds before reset by watchdog
+ * Return: status code
+ */
+efi_status_t efi_set_watchdog(unsigned long timeout)
+{
+ efi_status_t r;
+
+ if (timeout)
+ /* Reset watchdog */
+ r = efi_set_timer(watchdog_timer_event, EFI_TIMER_RELATIVE,
+ EFI_SECONDS_TO_100NS * timeout);
+ else
+ /* Deactivate watchdog */
+ r = efi_set_timer(watchdog_timer_event, EFI_TIMER_CANCEL, 0);
+ return r;
+}
+
+/**
+ * efi_watchdog_register() - initializes the EFI watchdog
+ *
+ * Return: status code
+ */
+static efi_status_t efi_watchdog_register(void *data)
+{
+ efi_status_t r;
+
+ /*
+ * Create a timer event.
+ */
+ r = efi_create_event(EFI_EVT_TIMER | EFI_EVT_NOTIFY_SIGNAL, EFI_TPL_CALLBACK,
+ efi_watchdog_timer_notify, NULL, NULL,
+ &watchdog_timer_event);
+ if (r) {
+ pr_err("Failed to register watchdog event\n");
+ return r;
+ }
+
+ return EFI_SUCCESS;
+}
+
+static int efi_watchdog_init(void)
+{
+ efi_register_deferred_init(efi_watchdog_register, NULL);
+ return 0;
+}
+late_initcall(efi_watchdog_init);
diff --git a/include/efi/loader.h b/include/efi/loader.h
index e1a3c1e298c5..ef82a2f5214e 100644
--- a/include/efi/loader.h
+++ b/include/efi/loader.h
@@ -26,6 +26,9 @@ extern struct efi_system_table systab;
efi_status_t efi_alloc_system_table(void);
efi_status_t efi_initialize_system_table(void);
+/* Called by efi_set_watchdog_timer to reset the timer */
+efi_status_t efi_set_watchdog(unsigned long timeout);
+
/* Allocate boot service data pool memory */
void *efi_alloc(size_t len, const char *name);
/* Reallocate boot service data pool memory */
--
2.47.3
^ permalink raw reply [flat|nested] 55+ messages in thread* [PATCH v1 22/54] efi: loader: move PE implementation out of common code
2025-12-18 10:37 [PATCH v1 00/54] efi: implement EFI loader support in barebox Ahmad Fatoum
` (20 preceding siblings ...)
2025-12-18 10:37 ` [PATCH v1 21/54] efi: loader: add watchdog support Ahmad Fatoum
@ 2025-12-18 10:37 ` Ahmad Fatoum
2025-12-18 10:37 ` [PATCH v1 23/54] efi: loader: protocol: add file protocol support Ahmad Fatoum
` (31 subsequent siblings)
53 siblings, 0 replies; 55+ messages in thread
From: Ahmad Fatoum @ 2025-12-18 10:37 UTC (permalink / raw)
To: barebox; +Cc: Ahmad Fatoum
The PE code was initially added to a common location to mimic the ELF
code (which is used across architectures and also for remoteproc).
Since then ELF handling has been moved out of common/bootm.c and I
reached the conclusion that there is little benefit of supporting
running PE executables without UEFI.
Therefore move the implementation to efi/loader/ instead and give it an
API suitable for use from the EFI loader.
The code was not in use before, so we just use the occasion to sync with
U-Boot.
Signed-off-by: Ahmad Fatoum <a.fatoum@pengutronix.de>
---
common/Kconfig | 3 -
common/Makefile | 1 -
common/pe.c | 382 ---------------------
efi/loader/Makefile | 1 +
efi/loader/pe.c | 727 ++++++++++++++++++++++++++++++++++++++++
include/efi/loader/pe.h | 83 +++++
include/pe.h | 47 +--
7 files changed, 826 insertions(+), 418 deletions(-)
delete mode 100644 common/pe.c
create mode 100644 efi/loader/pe.c
create mode 100644 include/efi/loader/pe.h
diff --git a/common/Kconfig b/common/Kconfig
index b765953ee32d..b61a5bc40a1f 100644
--- a/common/Kconfig
+++ b/common/Kconfig
@@ -648,9 +648,6 @@ config BOOTM_AIMAGE
help
Support using Android Images.
-config PE
- bool "PE/COFF Support" if COMPILE_TEST
-
config ELF
bool "ELF Support" if COMPILE_TEST
diff --git a/common/Makefile b/common/Makefile
index 8769d04d04e7..36dee5f7a98a 100644
--- a/common/Makefile
+++ b/common/Makefile
@@ -15,7 +15,6 @@ obj-pbl-y += memsize.o
obj-y += resource.o
obj-pbl-y += bootsource.o
obj-$(CONFIG_ELF) += elf.o
-obj-$(CONFIG_PE) += pe.o
obj-y += restart.o
obj-y += poweroff.o
obj-y += slice.o
diff --git a/common/pe.c b/common/pe.c
deleted file mode 100644
index 0508670a5264..000000000000
--- a/common/pe.c
+++ /dev/null
@@ -1,382 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0+
-/*
- * PE image loader
- *
- * based partly on wine code
- *
- * Copyright (c) 2016 Alexander Graf
- */
-
-#define pr_fmt(fmt) "pe: " fmt
-
-#include <common.h>
-#include <pe.h>
-#include <linux/sizes.h>
-#include <libfile.h>
-#include <memory.h>
-#include <linux/err.h>
-
-static int machines[] = {
-#if defined(__aarch64__)
- IMAGE_FILE_MACHINE_ARM64,
-#elif defined(__arm__)
- IMAGE_FILE_MACHINE_ARM,
- IMAGE_FILE_MACHINE_THUMB,
- IMAGE_FILE_MACHINE_ARMV7,
-#endif
-
-#if defined(__x86_64__)
- IMAGE_FILE_MACHINE_AMD64,
-#elif defined(__i386__)
- IMAGE_FILE_MACHINE_I386,
-#endif
-
-#if defined(__riscv) && (__riscv_xlen == 32)
- IMAGE_FILE_MACHINE_RISCV32,
-#endif
-
-#if defined(__riscv) && (__riscv_xlen == 64)
- IMAGE_FILE_MACHINE_RISCV64,
-#endif
- 0 };
-
-/**
- * pe_loader_relocate() - relocate PE binary
- *
- * @rel: pointer to the relocation table
- * @rel_size: size of the relocation table in bytes
- * @pe_reloc: actual load address of the image
- * @pref_address: preferred load address of the image
- * Return: status code
- */
-static int pe_loader_relocate(const IMAGE_BASE_RELOCATION *rel,
- unsigned long rel_size, void *pe_reloc,
- unsigned long pref_address)
-{
- unsigned long delta = (unsigned long)pe_reloc - pref_address;
- const IMAGE_BASE_RELOCATION *end;
- int i;
-
- if (delta == 0)
- return 0;
-
- end = (const IMAGE_BASE_RELOCATION *)((const char *)rel + rel_size);
- while (rel + 1 < end && rel->SizeOfBlock) {
- const uint16_t *relocs = (const uint16_t *)(rel + 1);
- i = (rel->SizeOfBlock - sizeof(*rel)) / sizeof(uint16_t);
- while (i--) {
- uint32_t offset = (uint32_t)(*relocs & 0xfff) +
- rel->VirtualAddress;
- int type = *relocs >> PAGE_SHIFT;
- uint64_t *x64 = pe_reloc + offset;
- uint32_t *x32 = pe_reloc + offset;
- uint16_t *x16 = pe_reloc + offset;
-
- switch (type) {
- case IMAGE_REL_BASED_ABSOLUTE:
- break;
- case IMAGE_REL_BASED_HIGH:
- *x16 += ((uint32_t)delta) >> 16;
- break;
- case IMAGE_REL_BASED_LOW:
- *x16 += (uint16_t)delta;
- break;
- case IMAGE_REL_BASED_HIGHLOW:
- *x32 += (uint32_t)delta;
- break;
- case IMAGE_REL_BASED_DIR64:
- *x64 += (uint64_t)delta;
- break;
-#ifdef __riscv
- case IMAGE_REL_BASED_RISCV_HI20:
- *x32 = ((*x32 & 0xfffff000) + (uint32_t)delta) |
- (*x32 & 0x00000fff);
- break;
- case IMAGE_REL_BASED_RISCV_LOW12I:
- case IMAGE_REL_BASED_RISCV_LOW12S:
- /* We know that we're 4k aligned */
- if (delta & 0xfff) {
- pr_err("Unsupported reloc offset\n");
- return -ENOEXEC;
- }
- break;
-#endif
- default:
- pr_err("Unknown Relocation off %x type %x\n",
- offset, type);
- return -ENOEXEC;
- }
- relocs++;
- }
- rel = (const IMAGE_BASE_RELOCATION *)relocs;
- }
- return 0;
-}
-
-/**
- * pe_check_header() - check if a memory buffer contains a PE-COFF image
- *
- * @buffer: buffer to check
- * @size: size of buffer
- * @nt_header: on return pointer to NT header of PE-COFF image
- * Return: 0 if the buffer contains a PE-COFF image
- */
-static int pe_check_header(void *buffer, size_t size, void **nt_header)
-{
- struct mz_hdr *dos = buffer;
- IMAGE_NT_HEADERS32 *nt;
-
- if (size < sizeof(*dos))
- return -EINVAL;
-
- /* Check for DOS magix */
- if (dos->magic != MZ_MAGIC)
- return -EINVAL;
-
- /*
- * Check if the image section header fits into the file. Knowing that at
- * least one section header follows we only need to check for the length
- * of the 64bit header which is longer than the 32bit header.
- */
- if (size < dos->peaddr + sizeof(IMAGE_NT_HEADERS32))
- return -EINVAL;
- nt = (IMAGE_NT_HEADERS32 *)((u8 *)buffer + dos->peaddr);
-
- /* Check for PE-COFF magic */
- if (nt->FileHeader.magic != PE_MAGIC)
- return -EINVAL;
-
- if (nt_header)
- *nt_header = nt;
-
- return 0;
-}
-
-/**
- * section_size() - determine size of section
- *
- * The size of a section in memory if normally given by VirtualSize.
- * If VirtualSize is not provided, use SizeOfRawData.
- *
- * @sec: section header
- * Return: size of section in memory
- */
-static u32 section_size(IMAGE_SECTION_HEADER *sec)
-{
- if (sec->Misc.VirtualSize)
- return sec->Misc.VirtualSize;
- else
- return sec->SizeOfRawData;
-}
-
-struct pe_image *pe_open_buf(void *bin, size_t pe_size)
-{
- struct pe_image *pe;
- int i;
- int supported = 0;
- int ret;
-
- pe = calloc(1, sizeof(*pe));
- if (!pe)
- return ERR_PTR(-ENOMEM);
-
- ret = pe_check_header(bin, pe_size, (void **)&pe->nt);
- if (ret) {
- pr_err("Not a PE-COFF file\n");
- ret = -ENOEXEC;
- goto err;
- }
-
- for (i = 0; machines[i]; i++)
- if (machines[i] == pe->nt->FileHeader.machine) {
- supported = 1;
- break;
- }
-
- if (!supported) {
- pr_err("Machine type 0x%04x is not supported\n",
- pe->nt->FileHeader.machine);
- ret = -ENOEXEC;
- goto err;
- }
-
- pe->num_sections = pe->nt->FileHeader.sections;
- pe->sections = (void *)&pe->nt->OptionalHeader +
- pe->nt->FileHeader.opt_hdr_size;
-
- if (pe_size < ((void *)pe->sections + sizeof(pe->sections[0]) * pe->num_sections - bin)) {
- pr_err("Invalid number of sections: %d\n", pe->num_sections);
- ret = -ENOEXEC;
- goto err;
- }
-
- pe->bin = bin;
-
- return pe;
-err:
- return ERR_PTR(ret);
-}
-
-struct pe_image *pe_open(const char *filename)
-{
- struct pe_image *pe;
- size_t size;
- void *bin;
-
- bin = read_file(filename, &size);
- if (!bin)
- return ERR_PTR(-errno);
-
- pe = pe_open_buf(bin, size);
- if (IS_ERR(pe))
- free(bin);
-
- return pe;
-}
-
-static struct resource *pe_alloc(size_t virt_size)
-{
- resource_size_t start, end;
- int ret;
-
- ret = memory_bank_first_find_space(&start, &end);
- if (ret)
- return NULL;
-
- start = ALIGN(start, SZ_64K);
-
- if (start + virt_size > end)
- return NULL;
-
- return request_sdram_region("pe-code", start, virt_size,
- MEMTYPE_LOADER_CODE,
- MEMATTRS_RWX);
-}
-
-unsigned long pe_get_mem_size(struct pe_image *pe)
-{
- unsigned long virt_size = 0;
- int i;
-
- /* Calculate upper virtual address boundary */
- for (i = pe->num_sections - 1; i >= 0; i--) {
- IMAGE_SECTION_HEADER *sec = &pe->sections[i];
-
- virt_size = max_t(unsigned long, virt_size,
- sec->VirtualAddress + section_size(sec));
- }
-
- /* Read 32/64bit specific header bits */
- if (pe->nt->OptionalHeader.magic == PE_OPT_MAGIC_PE32PLUS) {
- IMAGE_NT_HEADERS64 *nt64 = (void *)pe->nt;
- struct pe32plus_opt_hdr *opt = &nt64->OptionalHeader;
-
- virt_size = ALIGN(virt_size, opt->section_align);
- } else if (pe->nt->OptionalHeader.magic == PE_OPT_MAGIC_PE32) {
- struct pe32_opt_hdr *opt = &pe->nt->OptionalHeader;
-
- virt_size = ALIGN(virt_size, opt->section_align);
- } else {
- pr_err("Invalid optional header magic %x\n",
- pe->nt->OptionalHeader.magic);
- return 0;
- }
-
- return virt_size;
-}
-
-int pe_load(struct pe_image *pe)
-{
- int rel_idx = IMAGE_DIRECTORY_ENTRY_BASERELOC;
- uint64_t image_base;
- unsigned long virt_size;
- unsigned long rel_size;
- const IMAGE_BASE_RELOCATION *rel;
- struct mz_hdr *dos;
- struct resource *code;
- void *pe_reloc;
- int i;
-
- virt_size = pe_get_mem_size(pe);
- if (!virt_size)
- return -ENOEXEC;
-
- /* Read 32/64bit specific header bits */
- if (pe->nt->OptionalHeader.magic == PE_OPT_MAGIC_PE32PLUS) {
- IMAGE_NT_HEADERS64 *nt64 = (void *)pe->nt;
- struct pe32plus_opt_hdr *opt = &nt64->OptionalHeader;
- image_base = opt->image_base;
- pe->image_type = opt->subsys;
-
- code = pe_alloc(virt_size);
- if (!code)
- return -ENOMEM;
-
- pe_reloc = (void *)code->start;
-
- pe->entry = code->start + opt->entry_point;
- rel_size = nt64->DataDirectory[rel_idx].size;
- rel = pe_reloc + nt64->DataDirectory[rel_idx].virtual_address;
- } else if (pe->nt->OptionalHeader.magic == PE_OPT_MAGIC_PE32) {
- struct pe32_opt_hdr *opt = &pe->nt->OptionalHeader;
- image_base = opt->image_base;
- pe->image_type = opt->subsys;
- virt_size = ALIGN(virt_size, opt->section_align);
-
- code = pe_alloc(virt_size);
- if (!code)
- return -ENOMEM;
-
- pe_reloc = (void *)code->start;
-
- pe->entry = code->start + opt->entry_point;
- rel_size = pe->nt->DataDirectory[rel_idx].size;
- rel = pe_reloc + pe->nt->DataDirectory[rel_idx].virtual_address;
- } else {
- pr_err("Invalid optional header magic %x\n",
- pe->nt->OptionalHeader.magic);
- return -ENOEXEC;
- }
-
- /* Copy PE headers */
- memcpy(pe_reloc, pe->bin,
- sizeof(*dos)
- + sizeof(*pe->nt)
- + pe->nt->FileHeader.opt_hdr_size
- + pe->num_sections * sizeof(IMAGE_SECTION_HEADER));
-
- /* Load sections into RAM */
- for (i = pe->num_sections - 1; i >= 0; i--) {
- IMAGE_SECTION_HEADER *sec = &pe->sections[i];
- u32 copy_size = section_size(sec);
-
- if (copy_size > sec->SizeOfRawData) {
- copy_size = sec->SizeOfRawData;
- memset(pe_reloc + sec->VirtualAddress, 0,
- sec->Misc.VirtualSize);
- }
-
- memcpy(pe_reloc + sec->VirtualAddress,
- pe->bin + sec->PointerToRawData,
- copy_size);
- }
-
- /* Run through relocations */
- if (pe_loader_relocate(rel, rel_size, pe_reloc,
- (unsigned long)image_base) != 0) {
- release_sdram_region(code);
- return -EINVAL;
- }
-
- pe->code = code;
-
- return 0;
-}
-
-void pe_close(struct pe_image *pe)
-{
- if (pe->code)
- release_sdram_region(pe->code);
- free(pe->bin);
- free(pe);
-}
diff --git a/efi/loader/Makefile b/efi/loader/Makefile
index 640c9707060c..e1c9765581cb 100644
--- a/efi/loader/Makefile
+++ b/efi/loader/Makefile
@@ -9,3 +9,4 @@ obj-y += boot.o
obj-y += runtime.o
obj-y += setup.o
obj-y += watchdog.o
+obj-y += pe.o
diff --git a/efi/loader/pe.c b/efi/loader/pe.c
new file mode 100644
index 000000000000..3c30211f1074
--- /dev/null
+++ b/efi/loader/pe.c
@@ -0,0 +1,727 @@
+// SPDX-License-Identifier: GPL-2.0+
+// SPDX-Comment: Origin-URL: https://github.com/u-boot/u-boot/blob/463e4e6476299b32452a8a9e57374241cca26292/lib/efi_loader/efi_image_loader.c
+/*
+ * EFI image loader
+ *
+ * based partly on wine code
+ *
+ * Copyright (c) 2016 Alexander Graf
+ */
+
+#define pr_fmt(fmt) "efi-loader: pe: " fmt
+
+#include <stdio.h>
+#include <memory.h>
+#include <linux/align.h>
+#include <linux/sizes.h>
+#include <efi/services.h>
+#include <efi/memory.h>
+#include <efi/loader.h>
+#include <efi/loader/pe.h>
+#include <efi/guid.h>
+#include <efi/error.h>
+#include <pe.h>
+#include <qsort.h>
+#include <linux/err.h>
+
+static int machines[] = {
+#if defined(__aarch64__)
+ IMAGE_FILE_MACHINE_ARM64,
+#elif defined(__arm__)
+ IMAGE_FILE_MACHINE_ARM,
+ IMAGE_FILE_MACHINE_THUMB,
+ IMAGE_FILE_MACHINE_ARMV7,
+#endif
+
+#if defined(__x86_64__)
+ IMAGE_FILE_MACHINE_AMD64,
+#elif defined(__i386__)
+ IMAGE_FILE_MACHINE_I386,
+#endif
+
+#if defined(__riscv) && (__riscv_xlen == 32)
+ IMAGE_FILE_MACHINE_RISCV32,
+#endif
+
+#if defined(__riscv) && (__riscv_xlen == 64)
+ IMAGE_FILE_MACHINE_RISCV64,
+#endif
+ 0 };
+
+/**
+ * efi_print_image_info() - print information about a loaded image
+ *
+ * If the program counter is located within the image the offset to the base
+ * address is shown.
+ *
+ * @obj: EFI object
+ * @image: loaded image
+ * @pc: program counter (use NULL to suppress offset output)
+ * Return: status code
+ */
+static efi_status_t efi_print_image_info(struct efi_loaded_image_obj *obj,
+ struct efi_loaded_image *image,
+ void *pc)
+{
+ printf("UEFI image");
+ printf(" [0x%p:0x%p]",
+ image->image_base, image->image_base + image->image_size - 1);
+ if (pc && pc >= image->image_base &&
+ pc < image->image_base + image->image_size)
+ printf(" pc=0x%zx", pc - image->image_base);
+ if (image->file_path)
+ printf(" '%pD'", image->file_path);
+ printf("\n");
+ return EFI_SUCCESS;
+}
+
+/**
+ * efi_print_image_infos() - print information about all loaded images
+ *
+ * @pc: program counter (use NULL to suppress offset output)
+ */
+void efi_print_image_infos(void *pc)
+{
+ struct efi_object *efiobj;
+ struct efi_handler *handler;
+
+ list_for_each_entry(efiobj, &efi_obj_list, link) {
+ list_for_each_entry(handler, &efiobj->protocols, link) {
+ if (!efi_guidcmp(handler->guid, efi_loaded_image_protocol_guid)) {
+ efi_print_image_info(
+ (struct efi_loaded_image_obj *)efiobj,
+ handler->protocol_interface, pc);
+ }
+ }
+ }
+}
+
+/**
+ * efi_loader_relocate() - relocate UEFI binary
+ *
+ * @rel: pointer to the relocation table
+ * @rel_size: size of the relocation table in bytes
+ * @efi_reloc: actual load address of the image
+ * @pref_address: preferred load address of the image
+ * Return: status code
+ */
+static efi_status_t efi_loader_relocate(const IMAGE_BASE_RELOCATION *rel,
+ unsigned long rel_size, void *efi_reloc,
+ unsigned long pref_address)
+{
+ unsigned long delta = (unsigned long)efi_reloc - pref_address;
+ const IMAGE_BASE_RELOCATION *end;
+ int i;
+
+ if (delta == 0)
+ return EFI_SUCCESS;
+
+ end = (const IMAGE_BASE_RELOCATION *)((const char *)rel + rel_size);
+ while (rel + 1 < end && rel->SizeOfBlock) {
+ const uint16_t *relocs = (const uint16_t *)(rel + 1);
+ i = (rel->SizeOfBlock - sizeof(*rel)) / sizeof(uint16_t);
+ while (i--) {
+ uint32_t offset = (uint32_t)(*relocs & 0xfff) +
+ rel->VirtualAddress;
+ int type = *relocs >> EFI_PAGE_SHIFT;
+ uint64_t *x64 = efi_reloc + offset;
+ uint32_t *x32 = efi_reloc + offset;
+ uint16_t *x16 = efi_reloc + offset;
+
+ switch (type) {
+ case IMAGE_REL_BASED_ABSOLUTE:
+ break;
+ case IMAGE_REL_BASED_HIGH:
+ *x16 += ((uint32_t)delta) >> 16;
+ break;
+ case IMAGE_REL_BASED_LOW:
+ *x16 += (uint16_t)delta;
+ break;
+ case IMAGE_REL_BASED_HIGHLOW:
+ *x32 += (uint32_t)delta;
+ break;
+ case IMAGE_REL_BASED_DIR64:
+ *x64 += (uint64_t)delta;
+ break;
+#ifdef __riscv
+ case IMAGE_REL_BASED_RISCV_HI20:
+ *x32 = ((*x32 & 0xfffff000) + (uint32_t)delta) |
+ (*x32 & 0x00000fff);
+ break;
+ case IMAGE_REL_BASED_RISCV_LOW12I:
+ case IMAGE_REL_BASED_RISCV_LOW12S:
+ /* We know that we're 4k aligned */
+ if (delta & 0xfff) {
+ pr_err("Unsupported reloc offset\n");
+ return EFI_LOAD_ERROR;
+ }
+ break;
+#endif
+ default:
+ pr_err("Unknown Relocation off %x type %x\n",
+ offset, type);
+ return EFI_LOAD_ERROR;
+ }
+ relocs++;
+ }
+ rel = (const IMAGE_BASE_RELOCATION *)relocs;
+ }
+ return EFI_SUCCESS;
+}
+
+/**
+ * efi_set_code_and_data_type() - determine the memory types to be used for code
+ * and data.
+ *
+ * @loaded_image_info: image descriptor
+ * @image_type: field Subsystem of the optional header for
+ * Windows specific field
+ */
+static void efi_set_code_and_data_type(
+ struct efi_loaded_image *loaded_image_info,
+ uint16_t image_type)
+{
+ switch (image_type) {
+ case IMAGE_SUBSYSTEM_EFI_APPLICATION:
+ loaded_image_info->image_code_type = EFI_LOADER_CODE;
+ loaded_image_info->image_data_type = EFI_LOADER_DATA;
+ break;
+ case IMAGE_SUBSYSTEM_EFI_BOOT_SERVICE_DRIVER:
+ loaded_image_info->image_code_type = EFI_BOOT_SERVICES_CODE;
+ loaded_image_info->image_data_type = EFI_BOOT_SERVICES_DATA;
+ break;
+ case IMAGE_SUBSYSTEM_EFI_RUNTIME_DRIVER:
+ case IMAGE_SUBSYSTEM_EFI_ROM_IMAGE:
+ loaded_image_info->image_code_type = EFI_RUNTIME_SERVICES_CODE;
+ loaded_image_info->image_data_type = EFI_RUNTIME_SERVICES_DATA;
+ break;
+ default:
+ pr_err("invalid image type: %u\n", image_type);
+ /* Let's assume it is an application */
+ loaded_image_info->image_code_type = EFI_LOADER_CODE;
+ loaded_image_info->image_data_type = EFI_LOADER_DATA;
+ break;
+ }
+}
+
+/**
+ * efi_image_region_add() - add an entry of region
+ * @regs: Pointer to array of regions
+ * @start: Start address of region (included)
+ * @end: End address of region (excluded)
+ * @nocheck: flag against overlapped regions
+ *
+ * Take one entry of region \[@start, @end\[ and insert it into the list.
+ *
+ * * If @nocheck is false, the list will be sorted ascending by address.
+ * Overlapping entries will not be allowed.
+ *
+ * * If @nocheck is true, the list will be sorted ascending by sequence
+ * of adding the entries. Overlapping is allowed.
+ *
+ * Return: status code
+ */
+efi_status_t efi_image_region_add(struct efi_image_regions *regs,
+ const void *start, const void *end,
+ int nocheck)
+{
+ struct image_region *reg;
+ int i, j;
+
+ if (regs->num >= regs->max) {
+ pr_err("%s: no more room for regions\n", __func__);
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ if (end < start)
+ return EFI_INVALID_PARAMETER;
+
+ for (i = 0; i < regs->num; i++) {
+ reg = ®s->reg[i];
+ if (nocheck)
+ continue;
+
+ /* new data after registered region */
+ if (start >= reg->data + reg->size)
+ continue;
+
+ /* new data preceding registered region */
+ if (end <= reg->data) {
+ for (j = regs->num - 1; j >= i; j--)
+ memcpy(®s->reg[j + 1], ®s->reg[j],
+ sizeof(*reg));
+ break;
+ }
+
+ /* new data overlapping registered region */
+ pr_err("%s: new region already part of another\n", __func__);
+ return EFI_INVALID_PARAMETER;
+ }
+
+ reg = ®s->reg[i];
+ reg->data = start;
+ reg->size = end - start;
+ regs->num++;
+
+ return EFI_SUCCESS;
+}
+
+/**
+ * cmp_pe_section() - compare virtual addresses of two PE image sections
+ * @arg1: pointer to pointer to first section header
+ * @arg2: pointer to pointer to second section header
+ *
+ * Compare the virtual addresses of two sections of an portable executable.
+ * The arguments are defined as const void * to allow usage with qsort().
+ *
+ * Return: -1 if the virtual address of arg1 is less than that of arg2,
+ * 0 if the virtual addresses are equal, 1 if the virtual address
+ * of arg1 is greater than that of arg2.
+ */
+static int cmp_pe_section(const void *arg1, const void *arg2)
+{
+ const IMAGE_SECTION_HEADER *section1, *section2;
+
+ section1 = *((const IMAGE_SECTION_HEADER **)arg1);
+ section2 = *((const IMAGE_SECTION_HEADER **)arg2);
+
+ if (section1->VirtualAddress < section2->VirtualAddress)
+ return -1;
+ else if (section1->VirtualAddress == section2->VirtualAddress)
+ return 0;
+ else
+ return 1;
+}
+
+/**
+ * efi_prepare_aligned_image() - prepare 8-byte aligned image
+ * @efi: pointer to the EFI binary
+ * @efi_size: size of @efi binary
+ *
+ * If @efi is not 8-byte aligned, this function newly allocates
+ * the image buffer.
+ *
+ * Return: valid pointer to a image, return NULL if allocation fails.
+ */
+void *efi_prepare_aligned_image(void *efi, u64 *efi_size)
+{
+ size_t new_efi_size;
+ void *new_efi;
+
+ /*
+ * Size must be 8-byte aligned and the trailing bytes must be
+ * zero'ed. Otherwise hash value may be incorrect.
+ */
+ if (!IS_ALIGNED(*efi_size, 8)) {
+ new_efi_size = ALIGN(*efi_size, 8);
+ new_efi = calloc(new_efi_size, 1);
+ if (!new_efi)
+ return NULL;
+ memcpy(new_efi, efi, *efi_size);
+ *efi_size = new_efi_size;
+ return new_efi;
+ } else {
+ return efi;
+ }
+}
+
+/**
+ * efi_image_parse() - parse a PE image
+ * @efi: Pointer to image
+ * @len: Size of @efi
+ * @regp: Pointer to a list of regions
+ * @auth: Pointer to a pointer to authentication data in PE
+ * @auth_len: Size of @auth
+ *
+ * Parse image binary in PE32(+) format, assuming that sanity of PE image
+ * has been checked by a caller.
+ * On success, an address of authentication data in @efi and its size will
+ * be returned in @auth and @auth_len, respectively.
+ *
+ * Return: true on success, false on error
+ */
+bool efi_image_parse(void *efi, size_t len, struct efi_image_regions **regp,
+ WIN_CERTIFICATE **auth, size_t *auth_len)
+{
+ struct efi_image_regions *regs;
+ struct mz_hdr *dos;
+ IMAGE_NT_HEADERS32 *nt;
+ IMAGE_SECTION_HEADER *sections, **sorted;
+ int num_regions, num_sections, i;
+ int ctidx = IMAGE_DIRECTORY_ENTRY_SECURITY;
+ u32 align, size, authsz, authoff;
+ size_t bytes_hashed;
+
+ dos = (void *)efi;
+ nt = (void *)(efi + dos->peaddr);
+ authoff = 0;
+ authsz = 0;
+
+ /*
+ * Count maximum number of regions to be digested.
+ * We don't have to have an exact number here.
+ * See efi_image_region_add()'s in parsing below.
+ */
+ num_regions = 3; /* for header */
+ num_regions += nt->FileHeader.sections;
+ num_regions++; /* for extra */
+
+ regs = calloc(sizeof(*regs) + sizeof(struct image_region) * num_regions,
+ 1);
+ if (!regs)
+ goto err;
+ regs->max = num_regions;
+
+ /*
+ * Collect data regions for hash calculation
+ * 1. File headers
+ */
+ if (nt->OptionalHeader.magic == PE_OPT_MAGIC_PE32PLUS) {
+ IMAGE_NT_HEADERS64 *nt64 = (void *)nt;
+ struct pe32plus_opt_hdr *opt = &nt64->OptionalHeader;
+
+ /* Skip CheckSum */
+ efi_image_region_add(regs, efi, &opt->csum, 0);
+ if (nt64->OptionalHeader.data_dirs <= ctidx) {
+ efi_image_region_add(regs,
+ &opt->subsys,
+ efi + opt->header_size, 0);
+ } else {
+ /* Skip Certificates Table */
+ efi_image_region_add(regs,
+ &opt->subsys,
+ &nt64->DataDirectory[ctidx], 0);
+ efi_image_region_add(regs,
+ &nt64->DataDirectory[ctidx] + 1,
+ efi + opt->header_size, 0);
+
+ authoff = nt64->DataDirectory[ctidx].virtual_address;
+ authsz = nt64->DataDirectory[ctidx].size;
+ }
+
+ bytes_hashed = opt->header_size;
+ align = opt->file_align;
+ } else if (nt->OptionalHeader.magic == PE_OPT_MAGIC_PE32) {
+ struct pe32_opt_hdr *opt = &nt->OptionalHeader;
+
+ /* Skip CheckSum */
+ efi_image_region_add(regs, efi, &opt->csum, 0);
+ if (nt->OptionalHeader.data_dirs <= ctidx) {
+ efi_image_region_add(regs,
+ &opt->subsys,
+ efi + opt->header_size, 0);
+ } else {
+ /* Skip Certificates Table */
+ efi_image_region_add(regs, &opt->subsys,
+ &nt->DataDirectory[ctidx], 0);
+ efi_image_region_add(regs,
+ &nt->DataDirectory[ctidx] + 1,
+ efi + opt->header_size, 0);
+
+ authoff = nt->DataDirectory[ctidx].virtual_address;
+ authsz = nt->DataDirectory[ctidx].size;
+ }
+
+ bytes_hashed = opt->header_size;
+ align = opt->file_align;
+ } else {
+ pr_err("%s: Invalid optional header magic %x\n", __func__,
+ nt->OptionalHeader.magic);
+ goto err;
+ }
+
+ /* 2. Sections */
+ num_sections = nt->FileHeader.sections;
+ sections = (void *)((uint8_t *)&nt->OptionalHeader +
+ nt->FileHeader.opt_hdr_size);
+ sorted = calloc(sizeof(IMAGE_SECTION_HEADER *), num_sections);
+ if (!sorted) {
+ pr_err("%s: Out of memory\n", __func__);
+ goto err;
+ }
+
+ /*
+ * Make sure the section list is in ascending order.
+ */
+ for (i = 0; i < num_sections; i++)
+ sorted[i] = §ions[i];
+ qsort(sorted, num_sections, sizeof(sorted[0]), cmp_pe_section);
+
+ for (i = 0; i < num_sections; i++) {
+ if (!sorted[i]->SizeOfRawData)
+ continue;
+
+ size = (sorted[i]->SizeOfRawData + align - 1) & ~(align - 1);
+ efi_image_region_add(regs, efi + sorted[i]->PointerToRawData,
+ efi + sorted[i]->PointerToRawData + size,
+ 0);
+ pr_debug("section[%d](%s): raw: 0x%x-0x%x, virt: %x-%x\n",
+ i, sorted[i]->Name,
+ sorted[i]->PointerToRawData,
+ sorted[i]->PointerToRawData + size,
+ sorted[i]->VirtualAddress,
+ sorted[i]->VirtualAddress
+ + sorted[i]->Misc.VirtualSize);
+
+ bytes_hashed += size;
+ }
+ free(sorted);
+
+ /* 3. Extra data excluding Certificates Table */
+ if (bytes_hashed + authsz < len) {
+ pr_debug("extra data for hash: %zu\n",
+ len - (bytes_hashed + authsz));
+ efi_image_region_add(regs, efi + bytes_hashed,
+ efi + len - authsz, 0);
+ }
+
+ /* Return Certificates Table */
+ if (authsz) {
+ if (len < authoff + authsz) {
+ pr_err("%s: Size for auth too large: %u >= %zu\n",
+ __func__, authsz, len - authoff);
+ goto err;
+ }
+ if (authsz < sizeof(*auth)) {
+ pr_err("%s: Size for auth too small: %u < %zu\n",
+ __func__, authsz, sizeof(*auth));
+ goto err;
+ }
+ *auth = efi + authoff;
+ *auth_len = authsz;
+ pr_debug("WIN_CERTIFICATE: 0x%x, size: 0x%x\n", authoff,
+ authsz);
+ } else {
+ *auth = NULL;
+ *auth_len = 0;
+ }
+
+ *regp = regs;
+
+ return true;
+
+err:
+ free(regs);
+
+ return false;
+}
+
+static bool efi_image_authenticate(void *efi, size_t efi_size)
+{
+ return true;
+}
+
+/**
+ * efi_check_pe() - check if a memory buffer contains a PE-COFF image
+ *
+ * @buffer: buffer to check
+ * @size: size of buffer
+ * @nt_header: on return pointer to NT header of PE-COFF image
+ * Return: EFI_SUCCESS if the buffer contains a PE-COFF image
+ */
+efi_status_t efi_check_pe(void *buffer, size_t size, void **nt_header)
+{
+ struct mz_hdr *dos = buffer;
+ IMAGE_NT_HEADERS32 *nt;
+
+ if (size < sizeof(*dos))
+ return EFI_INVALID_PARAMETER;
+
+ /* Check for DOS magix */
+ if (dos->magic != MZ_MAGIC)
+ return EFI_INVALID_PARAMETER;
+
+ /*
+ * Check if the image section header fits into the file. Knowing that at
+ * least one section header follows we only need to check for the length
+ * of the 64bit header which is longer than the 32bit header.
+ */
+ if (size < dos->peaddr + sizeof(IMAGE_NT_HEADERS32))
+ return EFI_INVALID_PARAMETER;
+ nt = (IMAGE_NT_HEADERS32 *)((u8 *)buffer + dos->peaddr);
+
+ /* Check for PE-COFF magic */
+ if (nt->FileHeader.magic != PE_MAGIC)
+ return EFI_INVALID_PARAMETER;
+
+ if (nt_header)
+ *nt_header = nt;
+
+ return EFI_SUCCESS;
+}
+
+/**
+ * section_size() - determine size of section
+ *
+ * The size of a section in memory if normally given by VirtualSize.
+ * If VirtualSize is not provided, use SizeOfRawData.
+ *
+ * @sec: section header
+ * Return: size of section in memory
+ */
+static u32 section_size(IMAGE_SECTION_HEADER *sec)
+{
+ if (sec->Misc.VirtualSize)
+ return sec->Misc.VirtualSize;
+ else
+ return sec->SizeOfRawData;
+}
+
+/**
+ * efi_load_pe() - relocate EFI binary
+ *
+ * This function loads all sections from a PE binary into a newly reserved
+ * piece of memory. On success the entry point is returned as handle->entry.
+ *
+ * @handle: loaded image handle
+ * @efi: pointer to the EFI binary
+ * @efi_size: size of @efi binary
+ * @loaded_image_info: loaded image protocol
+ * Return: status code
+ */
+efi_status_t efi_load_pe(struct efi_loaded_image_obj *handle,
+ void *efi, size_t efi_size,
+ struct efi_loaded_image *loaded_image_info)
+{
+ IMAGE_NT_HEADERS32 *nt;
+ struct mz_hdr *dos;
+ IMAGE_SECTION_HEADER *sections;
+ int num_sections;
+ void *efi_reloc;
+ int i;
+ const IMAGE_BASE_RELOCATION *rel;
+ unsigned long rel_size;
+ int rel_idx = IMAGE_DIRECTORY_ENTRY_BASERELOC;
+ uint64_t image_base;
+ unsigned long virt_size = 0;
+ int supported = 0;
+ efi_status_t ret;
+
+ ret = efi_check_pe(efi, efi_size, (void **)&nt);
+ if (ret != EFI_SUCCESS) {
+ pr_err("Not a PE-COFF file\n");
+ return EFI_LOAD_ERROR;
+ }
+
+ for (i = 0; machines[i]; i++)
+ if (machines[i] == nt->FileHeader.machine) {
+ supported = 1;
+ break;
+ }
+
+ if (!supported) {
+ pr_err("Machine type 0x%04x is not supported\n",
+ nt->FileHeader.machine);
+ return EFI_LOAD_ERROR;
+ }
+
+ num_sections = nt->FileHeader.sections;
+ sections = (void *)&nt->OptionalHeader +
+ nt->FileHeader.opt_hdr_size;
+
+ if (efi_size < ((void *)sections + sizeof(sections[0]) * num_sections
+ - efi)) {
+ pr_err("Invalid number of sections: %d\n", num_sections);
+ return EFI_LOAD_ERROR;
+ }
+
+ /* Authenticate an image */
+ if (efi_image_authenticate(efi, efi_size)) {
+ handle->auth_status = EFI_IMAGE_AUTH_PASSED;
+ } else {
+ handle->auth_status = EFI_IMAGE_AUTH_FAILED;
+ pr_err("Image not authenticated\n");
+ }
+
+ /* Calculate upper virtual address boundary */
+ for (i = num_sections - 1; i >= 0; i--) {
+ IMAGE_SECTION_HEADER *sec = §ions[i];
+
+ virt_size = max_t(unsigned long, virt_size,
+ sec->VirtualAddress + section_size(sec));
+ }
+
+ /* Read 32/64bit specific header bits */
+ if (nt->OptionalHeader.magic == PE_OPT_MAGIC_PE32PLUS) {
+ IMAGE_NT_HEADERS64 *nt64 = (void *)nt;
+ struct pe32plus_opt_hdr *opt = &nt64->OptionalHeader;
+ image_base = opt->image_base;
+ efi_set_code_and_data_type(loaded_image_info, opt->subsys);
+ handle->image_type = opt->subsys;
+ efi_reloc = efi_alloc_aligned_pages(virt_size,
+ loaded_image_info->image_code_type,
+ opt->section_align, "pe64");
+ if (!efi_reloc) {
+ pr_err("Out of memory\n");
+ ret = EFI_OUT_OF_RESOURCES;
+ goto err;
+ }
+ handle->entry = efi_reloc + opt->entry_point;
+ rel_size = nt64->DataDirectory[rel_idx].size;
+ rel = efi_reloc + nt64->DataDirectory[rel_idx].virtual_address;
+ } else if (nt->OptionalHeader.magic == PE_OPT_MAGIC_PE32) {
+ struct pe32_opt_hdr *opt = &nt->OptionalHeader;
+ image_base = opt->image_base;
+ efi_set_code_and_data_type(loaded_image_info, opt->subsys);
+ handle->image_type = opt->subsys;
+ efi_reloc = efi_alloc_aligned_pages(virt_size,
+ loaded_image_info->image_code_type,
+ opt->section_align, "pe32");
+ if (!efi_reloc) {
+ pr_err("Out of memory\n");
+ ret = EFI_OUT_OF_RESOURCES;
+ goto err;
+ }
+ handle->entry = efi_reloc + opt->entry_point;
+ rel_size = nt->DataDirectory[rel_idx].size;
+ rel = efi_reloc + nt->DataDirectory[rel_idx].virtual_address;
+ } else {
+ pr_err("Invalid optional header magic %x\n",
+ nt->OptionalHeader.magic);
+ ret = EFI_LOAD_ERROR;
+ goto err;
+ }
+
+ /* Copy PE headers */
+ memcpy(efi_reloc, efi,
+ sizeof(*dos)
+ + sizeof(*nt)
+ + nt->FileHeader.opt_hdr_size
+ + num_sections * sizeof(IMAGE_SECTION_HEADER));
+
+ /* Load sections into RAM */
+ for (i = num_sections - 1; i >= 0; i--) {
+ IMAGE_SECTION_HEADER *sec = §ions[i];
+ u32 copy_size = section_size(sec);
+
+ if (copy_size > sec->SizeOfRawData) {
+ copy_size = sec->SizeOfRawData;
+ memset(efi_reloc + sec->VirtualAddress, 0,
+ sec->Misc.VirtualSize);
+ }
+ memcpy(efi_reloc + sec->VirtualAddress,
+ efi + sec->PointerToRawData,
+ copy_size);
+ }
+
+ /* Run through relocations */
+ if (efi_loader_relocate(rel, rel_size, efi_reloc,
+ (unsigned long)image_base) != EFI_SUCCESS) {
+ efi_free_pages((uintptr_t) efi_reloc,
+ (virt_size + EFI_PAGE_MASK) >> EFI_PAGE_SHIFT);
+ ret = EFI_LOAD_ERROR;
+ goto err;
+ }
+
+ /* Populate the loaded image interface bits */
+ loaded_image_info->image_base = efi_reloc;
+ loaded_image_info->image_size = virt_size;
+
+ if (handle->auth_status == EFI_IMAGE_AUTH_PASSED)
+ return EFI_SUCCESS;
+ else
+ return EFI_SECURITY_VIOLATION;
+
+err:
+ return ret;
+}
diff --git a/include/efi/loader/pe.h b/include/efi/loader/pe.h
new file mode 100644
index 000000000000..b99f517cbfe3
--- /dev/null
+++ b/include/efi/loader/pe.h
@@ -0,0 +1,83 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+#ifndef __EFI_LOADER_PE_H_
+#define __EFI_LOADER_PE_H_
+
+#include <efi/types.h>
+#include <efi/loader/object.h>
+#include <asm/setjmp.h>
+#include <asm/setjmp.h>
+
+struct efi_loaded_image;
+struct efi_system_table;
+struct _WIN_CERTIFICATE;
+
+enum efi_image_auth_status {
+ EFI_IMAGE_AUTH_FAILED = 0,
+ EFI_IMAGE_AUTH_PASSED,
+};
+
+/**
+ * struct efi_loaded_image_obj - handle of a loaded image
+ *
+ * @header: EFI object header
+ * @exit_status: exit status passed to Exit()
+ * @exit_data_size: exit data size passed to Exit()
+ * @exit_data: exit data passed to Exit()
+ * @exit_jmp: long jump buffer for returning from started image
+ * @entry: entry address of the relocated image
+ * @image_type: indicates if the image is an applicition or a driver
+ * @auth_status: indicates if the image is authenticated
+ */
+struct efi_loaded_image_obj {
+ struct efi_object header;
+ efi_status_t *exit_status;
+ efi_uintn_t *exit_data_size;
+ u16 **exit_data;
+ struct jmp_buf_data *exit_jmp;
+ EFIAPI efi_status_t (*entry)(efi_handle_t image_handle,
+ struct efi_system_table *st);
+ u16 image_type;
+ enum efi_image_auth_status auth_status;
+};
+
+/* A part of an image, used for hashing */
+struct image_region {
+ const void *data;
+ int size;
+};
+
+/**
+ * struct efi_image_regions - A list of memory regions
+ *
+ * @max: Maximum number of regions
+ * @num: Number of regions
+ * @reg: array of regions
+ */
+struct efi_image_regions {
+ int max;
+ int num;
+ struct image_region reg[];
+};
+
+struct efi_system_table;
+
+/* Print information about all loaded images */
+void efi_print_image_infos(void *pc);
+
+efi_status_t efi_image_region_add(struct efi_image_regions *regs,
+ const void *start, const void *end,
+ int nocheck);
+
+void *efi_prepare_aligned_image(void *efi, u64 *efi_size);
+
+bool efi_image_parse(void *efi, size_t len, struct efi_image_regions **regp,
+ struct _WIN_CERTIFICATE **auth, size_t *auth_len);
+
+/* Check if a buffer contains a PE-COFF image */
+efi_status_t efi_check_pe(void *buffer, size_t size, void **nt_header);
+/* PE loader implementation */
+efi_status_t efi_load_pe(struct efi_loaded_image_obj *handle,
+ void *efi, size_t efi_size,
+ struct efi_loaded_image *loaded_image_info);
+
+#endif
diff --git a/include/pe.h b/include/pe.h
index 742dd8235a24..a3253d75f352 100644
--- a/include/pe.h
+++ b/include/pe.h
@@ -78,6 +78,21 @@ typedef struct _IMAGE_BASE_RELOCATION
#define IMAGE_REL_BASED_DIR64 10
#define IMAGE_REL_BASED_HIGH3ADJ 11
+/* certificate appended to PE image */
+typedef struct _WIN_CERTIFICATE {
+ uint32_t dwLength;
+ uint16_t wRevision;
+ uint16_t wCertificateType;
+} WIN_CERTIFICATE, *LPWIN_CERTIFICATE;
+
+/* Definitions for the contents of the certs data block */
+#define WIN_CERT_TYPE_PKCS_SIGNED_DATA 0x0002
+#define WIN_CERT_TYPE_EFI_OKCS115 0x0EF0
+#define WIN_CERT_TYPE_EFI_GUID 0x0EF1
+
+#define WIN_CERT_REVISION_1_0 0x0100
+#define WIN_CERT_REVISION_2_0 0x0200
+
struct pe_image {
u64 entry;
struct resource *code;
@@ -89,36 +104,4 @@ struct pe_image {
int num_sections;
};
-#ifdef CONFIG_PE
-struct pe_image *pe_open(const char *filename);
-unsigned long pe_get_mem_size(struct pe_image *pe);
-struct pe_image *pe_open_buf(void *bin, size_t pe_size);
-int pe_load(struct pe_image *pe);
-void pe_close(struct pe_image *pe);
-#else
-static inline struct pe_image *pe_open(const char *filename)
-{
- return ERR_PTR(-ENOSYS);
-}
-
-static inline unsigned long pe_get_mem_size(struct pe_image *pe)
-{
- return 0;
-}
-
-static inline struct pe_image *pe_open_buf(void *bin, size_t pe_size)
-{
- return ERR_PTR(-ENOSYS);
-}
-
-static inline int pe_load(struct pe_image *pe)
-{
- return -ENOSYS;
-}
-
-static inline void pe_close(struct pe_image *pe)
-{
-}
-#endif
-
#endif /* _PE_H */
--
2.47.3
^ permalink raw reply [flat|nested] 55+ messages in thread* [PATCH v1 23/54] efi: loader: protocol: add file protocol support
2025-12-18 10:37 [PATCH v1 00/54] efi: implement EFI loader support in barebox Ahmad Fatoum
` (21 preceding siblings ...)
2025-12-18 10:37 ` [PATCH v1 22/54] efi: loader: move PE implementation out of common code Ahmad Fatoum
@ 2025-12-18 10:37 ` Ahmad Fatoum
2025-12-18 10:37 ` [PATCH v1 24/54] efi: loader: protocol: add Block IO support Ahmad Fatoum
` (30 subsequent siblings)
53 siblings, 0 replies; 55+ messages in thread
From: Ahmad Fatoum @ 2025-12-18 10:37 UTC (permalink / raw)
To: barebox; +Cc: Ahmad Fatoum
EFI firmware doesn't necessarily bing its own file system
implementations, so we need to support the Simple File System Protocol
to enable started EFI applications to reuse the barebox file systems.
Signed-off-by: Ahmad Fatoum <a.fatoum@pengutronix.de>
---
efi/loader/Makefile | 1 +
efi/loader/protocols/Makefile | 3 +
efi/loader/protocols/file.c | 977 ++++++++++++++++++++++++++++++++++
include/efi/loader/file.h | 30 ++
4 files changed, 1011 insertions(+)
create mode 100644 efi/loader/protocols/Makefile
create mode 100644 efi/loader/protocols/file.c
create mode 100644 include/efi/loader/file.h
diff --git a/efi/loader/Makefile b/efi/loader/Makefile
index e1c9765581cb..bb6aa05c3748 100644
--- a/efi/loader/Makefile
+++ b/efi/loader/Makefile
@@ -1,5 +1,6 @@
# SPDX-License-Identifier: GPL-2.0
+obj-y += protocols/
obj-y += memory.o pool_alloc.o
obj-y += trace.o
obj-y += table.o
diff --git a/efi/loader/protocols/Makefile b/efi/loader/protocols/Makefile
new file mode 100644
index 000000000000..7cd92eb0bb54
--- /dev/null
+++ b/efi/loader/protocols/Makefile
@@ -0,0 +1,3 @@
+# SPDX-License-Identifier: GPL-2.0
+
+obj-$(CONFIG_FS) += file.o
diff --git a/efi/loader/protocols/file.c b/efi/loader/protocols/file.c
new file mode 100644
index 000000000000..3858cd3e1682
--- /dev/null
+++ b/efi/loader/protocols/file.c
@@ -0,0 +1,977 @@
+// SPDX-License-Identifier: GPL-2.0+
+// SPDX-Comment: Origin-URL: https://github.com/u-boot/u-boot/blob/9d95a35715fcb8e81ee423e31273489a47ed1563/lib/efi_loader/efi_file.c
+/*
+ * EFI_FILE_PROTOCOL
+ *
+ * Copyright (c) 2017 Rob Clark
+ */
+
+#define pr_fmt(fmt) "efi-loader: file: " fmt
+
+#include <errno.h>
+#include <linux/nls.h>
+#include <linux/overflow.h>
+#include <sys/stat.h>
+#include <efi/loader.h>
+#include <efi/error.h>
+#include <efi/guid.h>
+#include <efi/devicepath.h>
+#include <efi/loader/devicepath.h>
+#include <efi/loader/trace.h>
+#include <efi/loader/event.h>
+#include <efi/loader/file.h>
+#include <efi/protocol/file.h>
+#include <wchar.h>
+#include <string.h>
+#include <libfile.h>
+#include <disks.h>
+#include <charset.h>
+#include <malloc.h>
+#include <dirent.h>
+#include <fs.h>
+
+#define MAX_UTF8_PER_UTF16 3
+
+struct file_system {
+ struct efi_simple_file_system_protocol base;
+ struct efi_device_path *dp;
+ struct cdev *cdev;
+ struct file_handle *fh;
+};
+#define to_fs(x) container_of(x, struct file_system, base)
+
+struct file_handle {
+ struct efi_file_handle base;
+ struct file_handle *parent;
+ struct file_system *fs;
+ int fd;
+ u64 open_mode;
+
+ /* for reading a directory: */
+ DIR *dir;
+
+ char path[];
+};
+#define to_fh(x) container_of(x, struct file_handle, base)
+
+static const struct efi_file_handle efi_file_handle_protocol;
+
+static int efi_opendir(int dirfd, const char *path, struct file_handle *fh)
+{
+ fh->fd = openat(dirfd, path, O_DIRECTORY | O_CHROOT);
+ if (fh->fd < 0) {
+ pr_err("openat(%d, \"%s\") failed: %m\n", dirfd, path);
+ return fh->fd;
+ }
+
+ fh->dir = fdopendir(fh->fd);
+ if (!fh->dir) {
+ close(fh->fd);
+ return -errno;
+ }
+
+ return 0;
+}
+
+/**
+ * file_open() - open a file handle
+ *
+ * @parent: directory relative to which the file is to be opened
+ * @file_name: path of the file to be opened. '\', '.', or '..' may
+ * be used as modifiers. A leading backslash indicates an
+ * absolute path.
+ * @open_mode: bit mask indicating the access mode (read, write,
+ * create)
+ * @attributes: attributes for newly created file
+ * Returns: handle to the opened file or NULL
+ */
+static struct efi_file_handle *file_open(struct file_handle *parent,
+ efi_char16_t *file_name, u64 open_mode,
+ u64 attributes)
+{
+ struct file_handle *fh;
+ int dirfd;
+ bool is_dir = false;
+ struct stat s;
+ int flags = 0;
+ char *p;
+ int flen;
+
+ flen = wcslen(file_name);
+
+ fh = xzalloc(struct_size(fh, path, (flen * MAX_UTF8_PER_UTF16) + 2));
+
+ fh->open_mode = open_mode;
+ fh->base = efi_file_handle_protocol;
+ fh->parent = parent;
+ fh->fs = parent->fs;
+
+ p = fh->path;
+ utf16_utf8_strcpy(&p, file_name);
+
+ strreplace(fh->path, '\\', '/');
+
+ dirfd = parent->dir ? parent->fd : parent->parent->fd;
+
+ if (open_mode & (EFI_FILE_MODE_READ | EFI_FILE_MODE_WRITE))
+ flags |= O_RDWR;
+ else
+ flags |= O_RDONLY;
+
+ if (open_mode & EFI_FILE_MODE_CREATE) {
+ flags |= O_CREAT;
+
+ if (attributes & EFI_FILE_DIRECTORY)
+ is_dir = true;
+ }
+
+ if (is_dir || (!statat(dirfd, fh->path, &s) && S_ISDIR(s.st_mode))) {
+ if (flags & O_CREAT)
+ mkdirat(dirfd, fh->path, 0);
+
+ if (efi_opendir(dirfd, fh->path, fh))
+ goto error;
+ } else {
+ fh->fd = openat(dirfd, fh->path, flags);
+ if (fh->fd < 0)
+ goto error;
+ }
+
+ return &fh->base;
+
+error:
+ free(fh);
+ return NULL;
+}
+
+static efi_status_t efi_file_open_int(struct efi_file_handle *this,
+ struct efi_file_handle **new_handle,
+ efi_char16_t *file_name, u64 open_mode,
+ u64 attributes)
+{
+ struct file_handle *fh = to_fh(this);
+ efi_status_t efiret;
+
+ /* Check parameters */
+ if (!this || !new_handle || !file_name) {
+ efiret = EFI_INVALID_PARAMETER;
+ goto out;
+ }
+ if (open_mode != EFI_FILE_MODE_READ &&
+ open_mode != (EFI_FILE_MODE_READ | EFI_FILE_MODE_WRITE) &&
+ open_mode != (EFI_FILE_MODE_READ | EFI_FILE_MODE_WRITE |
+ EFI_FILE_MODE_CREATE)) {
+ efiret = EFI_INVALID_PARAMETER;
+ goto out;
+ }
+ /*
+ * The UEFI spec requires that attributes are only set in create mode.
+ * The SCT does not care about this and sets EFI_FILE_DIRECTORY in
+ * read mode. EDK2 does not check that attributes are zero if not in
+ * create mode.
+ *
+ * So here we only check attributes in create mode and do not check
+ * that they are zero otherwise.
+ */
+ if ((open_mode & EFI_FILE_MODE_CREATE) &&
+ (attributes & (EFI_FILE_READ_ONLY | ~EFI_FILE_VALID_ATTR))) {
+ efiret = EFI_INVALID_PARAMETER;
+ goto out;
+ }
+
+ /* Open file */
+ *new_handle = file_open(fh, file_name, open_mode, attributes);
+ if (*new_handle)
+ efiret = EFI_SUCCESS;
+ else
+ efiret = EFI_NOT_FOUND;
+out:
+ return efiret;
+}
+
+/**
+ * efi_file_open_()
+ *
+ * This function implements the Open service of the File Protocol.
+ * See the UEFI spec for details.
+ *
+ * @this: EFI_FILE_PROTOCOL instance
+ * @new_handle: on return pointer to file handle
+ * @file_name: file name
+ * @open_mode: mode to open the file (read, read/write, create/read/write)
+ * @attributes: attributes for newly created file
+ */
+static efi_status_t EFIAPI efi_file_open(struct efi_file_handle *this,
+ struct efi_file_handle **new_handle,
+ efi_char16_t *file_name, u64 open_mode,
+ u64 attributes)
+{
+ efi_status_t efiret;
+
+ EFI_ENTRY("%p, %p, \"%ls\", %llx, %llu", this, new_handle,
+ file_name, open_mode, attributes);
+
+ efiret = efi_file_open_int(this, new_handle, file_name, open_mode,
+ attributes);
+
+ return EFI_EXIT2(efiret, *new_handle);
+}
+
+/**
+ * efi_file_open_ex() - open file asynchronously
+ *
+ * This function implements the OpenEx service of the File Protocol.
+ * See the UEFI spec for details.
+ *
+ * @this: EFI_FILE_PROTOCOL instance
+ * @new_handle: on return pointer to file handle
+ * @file_name: file name
+ * @open_mode: mode to open the file (read, read/write, create/read/write)
+ * @attributes: attributes for newly created file
+ * @token: transaction token
+ */
+static efi_status_t EFIAPI efi_file_open_ex(struct efi_file_handle *this,
+ struct efi_file_handle **new_handle,
+ efi_char16_t *file_name, u64 open_mode,
+ u64 attributes,
+ struct efi_file_io_token *token)
+{
+ efi_status_t efiret;
+
+ EFI_ENTRY("%p, %p, \"%ls\", %llx, %llu, %p", this, new_handle,
+ file_name, open_mode, attributes, token);
+
+ if (!token) {
+ efiret = EFI_INVALID_PARAMETER;
+ goto out;
+ }
+
+ efiret = efi_file_open_int(this, new_handle, file_name, open_mode,
+ attributes);
+
+ if (efiret == EFI_SUCCESS && token->event) {
+ token->status = EFI_SUCCESS;
+ efi_signal_event(token->event);
+ }
+
+out:
+ return EFI_EXIT2(efiret, *new_handle);
+}
+
+static void file_close(struct file_handle *fh)
+{
+ if (fh->dir)
+ closedir(fh->dir);
+ else
+ close(fh->fd);
+}
+
+static efi_status_t EFIAPI efi_file_close(struct efi_file_handle *file)
+{
+ struct file_handle *fh = to_fh(file);
+ EFI_ENTRY("%p", file);
+ file_close(fh);
+ free(fh);
+ return EFI_EXIT(EFI_SUCCESS);
+}
+
+static efi_status_t EFIAPI efi_file_delete(struct efi_file_handle *file)
+{
+ struct file_handle *fh = to_fh(file);
+ int flags = fh->dir ? AT_REMOVEDIR : 0;
+ int ret;
+
+ EFI_ENTRY("%p", file);
+
+ file_close(fh);
+
+ ret = unlinkat(fh->parent->fd, fh->path, flags);
+
+ free(fh);
+
+ return EFI_EXIT(ret ? EFI_WARN_DELETE_FAILURE : EFI_SUCCESS);
+}
+
+/**
+ * efi_get_file_size() - determine the size of a file
+ *
+ * @fh: file handle
+ * @file_size: pointer to receive file size
+ * Return: status code
+ */
+static efi_status_t efi_get_file_size(struct file_handle *fh,
+ loff_t *file_size)
+{
+ struct stat s;
+ int ret;
+
+ if (fh->dir) {
+ *file_size = 0;
+ return EFI_SUCCESS;
+ }
+
+ ret = fstat(fh->fd, &s);
+ if (ret)
+ return EFI_DEVICE_ERROR;
+
+ *file_size = s.st_size;
+
+ return EFI_SUCCESS;
+}
+
+/**
+ * efi_file_size() - Get the size of a file using an EFI file handle
+ *
+ * @fh: EFI file handle
+ * @size: buffer to fill in the discovered size
+ *
+ * Return: size of the file
+ */
+efi_status_t efi_file_size(struct efi_file_handle *fh, size_t *size)
+{
+ struct efi_file_info *info = NULL;
+ size_t bs = 0;
+ efi_status_t efiret;
+
+ *size = 0;
+ efiret = fh->get_info(fh, (efi_guid_t *)&efi_file_info_id, &bs, info);
+ if (efiret != EFI_BUFFER_TOO_SMALL) {
+ efiret = EFI_DEVICE_ERROR;
+ goto out;
+ }
+
+ info = malloc(bs);
+ if (!info) {
+ efiret = EFI_OUT_OF_RESOURCES;
+ goto out;
+ }
+ efiret = fh->get_info(fh, (efi_guid_t *)&efi_file_info_id, &bs, info);
+ if (efiret != EFI_SUCCESS)
+ goto out;
+
+ *size = info->FileSize;
+
+out:
+ free(info);
+ return efiret;
+}
+
+static efi_status_t file_read(struct file_handle *fh, u64 *buffer_size,
+ void *buffer)
+{
+ ssize_t nbytes;
+
+ if (!buffer)
+ return EFI_INVALID_PARAMETER;
+
+ nbytes = read_full(fh->fd, buffer, *buffer_size);
+ if (nbytes < 0)
+ return EFI_DEVICE_ERROR;
+
+ *buffer_size = nbytes;
+
+ return EFI_SUCCESS;
+}
+
+static efi_status_t dir_read(struct file_handle *fh, u64 *buffer_size,
+ void *buffer)
+{
+ struct efi_file_info *info = buffer;
+ struct dirent *d;
+ u64 required_size;
+ struct stat s;
+ efi_char16_t *dst;
+ int ret;
+
+ do {
+ d = readdir(fh->dir);
+ } while (d && (!strcmp(d->d_name, ".") || !strcmp(d->d_name, "..")));
+
+ if (!d) {
+ /* no more files in directory */
+ *buffer_size = 0;
+ return EFI_SUCCESS;
+ }
+
+ /* check buffer size: */
+ required_size = struct_size(info, FileName, utf8_utf16_strlen(d->d_name) + 1);
+ if (*buffer_size < required_size) {
+ *buffer_size = required_size;
+ unreaddir(fh->dir, d);
+ return EFI_BUFFER_TOO_SMALL;
+ }
+ if (!buffer)
+ return EFI_INVALID_PARAMETER;
+
+ ret = statat(fh->fd, d->d_name, &s);
+ if (ret)
+ return EFI_DEVICE_ERROR;
+
+ *buffer_size = required_size;
+ memset(info, 0, required_size);
+
+ dst = info->FileName;
+ utf8_utf16_strcpy(&dst, d->d_name);
+
+ info->Size = required_size;
+ info->FileSize = s.st_size;
+ info->PhysicalSize = s.st_size;
+ info->Attribute = 0;
+ info->CreateTime = (struct efi_time) {0}; // TODO
+ info->ModificationTime = (struct efi_time) {0};
+ info->LastAccessTime = (struct efi_time) {0};
+
+ if (S_ISDIR(s.st_mode))
+ info->Attribute |= EFI_FILE_DIRECTORY;
+
+ return EFI_SUCCESS;
+}
+
+static efi_status_t efi_file_read_int(struct efi_file_handle *this,
+ size_t *buffer_size, void *buffer)
+{
+ struct file_handle *fh = to_fh(this);
+ efi_status_t efiret = EFI_SUCCESS;
+ u64 bs;
+
+ if (!this || !buffer_size)
+ return EFI_INVALID_PARAMETER;
+
+ bs = *buffer_size;
+ if (fh->dir)
+ efiret = dir_read(fh, &bs, buffer);
+ else
+ efiret = file_read(fh, &bs, buffer);
+ if (bs <= SIZE_MAX)
+ *buffer_size = bs;
+ else
+ *buffer_size = SIZE_MAX;
+
+ return efiret;
+}
+
+/**
+ * efi_file_read() - read file
+ *
+ * This function implements the Read() service of the EFI_FILE_PROTOCOL.
+ *
+ * See the Unified Extensible Firmware Interface (UEFI) specification for
+ * details.
+ *
+ * @this: file protocol instance
+ * @buffer_size: number of bytes to read
+ * @buffer: read buffer
+ * Return: status code
+ */
+static efi_status_t EFIAPI efi_file_read(struct efi_file_handle *this,
+ size_t *buffer_size, void *buffer)
+{
+ efi_status_t efiret;
+
+ EFI_ENTRY("%p, *%p(%zu), %p", this, buffer_size,
+ buffer_size ? *buffer_size : 0, buffer);
+
+ efiret = efi_file_read_int(this, buffer_size, buffer);
+
+ return EFI_EXIT2(efiret, *buffer_size);
+}
+
+/**
+ * efi_file_read_ex() - read file asynchonously
+ *
+ * This function implements the ReadEx() service of the EFI_FILE_PROTOCOL.
+ *
+ * See the Unified Extensible Firmware Interface (UEFI) specification for
+ * details.
+ *
+ * @this: file protocol instance
+ * @token: transaction token
+ * Return: status code
+ */
+static efi_status_t EFIAPI efi_file_read_ex(struct efi_file_handle *this,
+ struct efi_file_io_token *token)
+{
+ efi_status_t efiret;
+
+ EFI_ENTRY("%p, %p", this, token);
+
+ if (!token) {
+ efiret = EFI_INVALID_PARAMETER;
+ goto out;
+ }
+
+ efiret = efi_file_read_int(this, &token->buffer_size, token->buffer);
+
+ if (efiret == EFI_SUCCESS && token->event) {
+ token->status = EFI_SUCCESS;
+ efi_signal_event(token->event);
+ }
+
+out:
+ return EFI_EXIT(efiret);
+}
+
+static efi_status_t efi_file_write_int(struct efi_file_handle *this,
+ size_t *buffer_size, void *buffer)
+{
+ struct file_handle *fh = to_fh(this);
+ efi_status_t efiret = EFI_SUCCESS;
+ ssize_t nbytes;
+
+ if (!this || !buffer_size || !buffer) {
+ efiret = EFI_INVALID_PARAMETER;
+ goto out;
+ }
+ if (fh->dir) {
+ efiret = EFI_UNSUPPORTED;
+ goto out;
+ }
+ if (!(fh->open_mode & EFI_FILE_MODE_WRITE)) {
+ efiret = EFI_ACCESS_DENIED;
+ goto out;
+ }
+
+ if (!*buffer_size)
+ goto out;
+
+ nbytes = write_full(fh->fd, buffer, *buffer_size);
+ if (nbytes < 0) {
+ efiret = errno == ENOSPC ? EFI_VOLUME_FULL : EFI_DEVICE_ERROR;
+ goto out;
+ }
+
+ *buffer_size = nbytes;
+
+out:
+ return efiret;
+}
+
+/**
+ * efi_file_write() - write to file
+ *
+ * This function implements the Write() service of the EFI_FILE_PROTOCOL.
+ *
+ * See the Unified Extensible Firmware Interface (UEFI) specification for
+ * details.
+ *
+ * @this: file protocol instance
+ * @buffer_size: number of bytes to write
+ * @buffer: buffer with the bytes to write
+ * Return: status code
+ */
+static efi_status_t EFIAPI efi_file_write(struct efi_file_handle *this,
+ size_t *buffer_size,
+ void *buffer)
+{
+ efi_status_t efiret;
+
+ EFI_ENTRY("%p, %p, %p", this, buffer_size, buffer);
+
+ efiret = efi_file_write_int(this, buffer_size, buffer);
+
+ return EFI_EXIT2(efiret, *buffer_size);
+}
+
+/**
+ * efi_file_write_ex() - write to file
+ *
+ * This function implements the WriteEx() service of the EFI_FILE_PROTOCOL.
+ *
+ * See the Unified Extensible Firmware Interface (UEFI) specification for
+ * details.
+ *
+ * @this: file protocol instance
+ * @token: transaction token
+ * Return: status code
+ */
+static efi_status_t EFIAPI efi_file_write_ex(struct efi_file_handle *this,
+ struct efi_file_io_token *token)
+{
+ efi_status_t efiret;
+
+ EFI_ENTRY("%p, %p", this, token);
+
+ if (!token) {
+ efiret = EFI_INVALID_PARAMETER;
+ goto out;
+ }
+
+ efiret = efi_file_write_int(this, &token->buffer_size, token->buffer);
+
+ if (efiret == EFI_SUCCESS && token->event) {
+ token->status = EFI_SUCCESS;
+ efi_signal_event(token->event);
+ }
+
+out:
+ return EFI_EXIT(efiret);
+}
+
+/**
+ * efi_file_getpos() - get current position in file
+ *
+ * This function implements the GetPosition service of the EFI file protocol.
+ * See the UEFI spec for details.
+ *
+ * @file: file handle
+ * @pos: pointer to file position
+ * Return: status code
+ */
+static efi_status_t EFIAPI efi_file_getpos(struct efi_file_handle *file,
+ u64 *pos)
+{
+ struct file_handle *fh = to_fh(file);
+ loff_t ret;
+
+ EFI_ENTRY("%p, %p", file, pos);
+
+ if (fh->dir)
+ return EFI_EXIT(EFI_UNSUPPORTED);
+
+ ret = lseek(fh->fd, 0, SEEK_CUR);
+ if (ret < 0)
+ return EFI_EXIT(EFI_DEVICE_ERROR);
+
+ *pos = ret;
+
+ return EFI_EXIT2(EFI_SUCCESS, pos);
+}
+
+/**
+ * efi_file_setpos() - set current position in file
+ *
+ * This function implements the SetPosition service of the EFI file protocol.
+ * See the UEFI spec for details.
+ *
+ * @file: file handle
+ * @pos: new file position
+ * Return: status code
+ */
+static efi_status_t EFIAPI efi_file_setpos(struct efi_file_handle *file,
+ u64 pos)
+{
+ struct file_handle *fh = to_fh(file);
+ loff_t ret;
+
+ EFI_ENTRY("%p, %llu", file, pos);
+
+ if (fh->dir) {
+ if (pos)
+ return EFI_EXIT(EFI_UNSUPPORTED);
+ rewinddir(fh->dir);
+ return EFI_EXIT(EFI_SUCCESS);
+ }
+
+ if (pos == ~0ULL)
+ ret = lseek(fh->fd, 0, SEEK_END);
+ else
+ ret = lseek(fh->fd, pos, SEEK_SET);
+
+ return EFI_EXIT(ret < 0 ? EFI_DEVICE_ERROR : EFI_SUCCESS);
+}
+
+static efi_status_t EFIAPI efi_file_get_info(struct efi_file_handle *file,
+ const efi_guid_t *info_type,
+ size_t *buffer_size,
+ void *buffer)
+{
+ struct file_handle *fh = to_fh(file);
+ efi_status_t efiret = EFI_SUCCESS;
+
+ EFI_ENTRY("%p, %pUs, %p, %p", file, info_type, buffer_size, buffer);
+
+ if (!file || !info_type || !buffer_size ||
+ (*buffer_size && !buffer)) {
+ efiret = EFI_INVALID_PARAMETER;
+ goto error;
+ }
+
+ if (!efi_guidcmp(*info_type, efi_file_info_id)) {
+ struct efi_file_info *info = buffer;
+ const char *filename = kbasename(fh->path);
+ unsigned int required_size;
+ efi_char16_t *dst;
+ loff_t file_size;
+
+ /* check buffer size: */
+ required_size = sizeof(*info) +
+ 2 * (utf8_utf16_strlen(filename) + 1);
+ if (*buffer_size < required_size) {
+ *buffer_size = required_size;
+ efiret = EFI_BUFFER_TOO_SMALL;
+ goto error;
+ }
+
+ efiret = efi_get_file_size(fh, &file_size);
+ if (efiret != EFI_SUCCESS)
+ goto error;
+
+ memset(info, 0, required_size);
+
+ info->Size = required_size;
+ info->FileSize = file_size;
+ info->PhysicalSize = file_size;
+
+ if (fh->dir)
+ info->Attribute |= EFI_FILE_DIRECTORY;
+
+ dst = info->FileName;
+ utf8_utf16_strcpy(&dst, filename);
+ } else if (!efi_guidcmp(*info_type, efi_file_system_info_guid)) {
+ struct efi_file_system_info *info = buffer;
+ size_t required_size;
+
+ required_size = sizeof(*info) + 2;
+ if (*buffer_size < required_size) {
+ *buffer_size = required_size;
+ efiret = EFI_BUFFER_TOO_SMALL;
+ goto error;
+ }
+
+ memset(info, 0, required_size);
+
+ info->size = required_size;
+ info->read_only = fh->fs->cdev->flags & DEVFS_PARTITION_READONLY;
+ info->volume_size = fh->fs->cdev->size;
+ /*
+ * TODO: We currently have no function to determine the free
+ * space. The volume size is the best upper bound we have.
+ */
+ info->free_space = info->volume_size;
+ info->block_size = SECTOR_SIZE;
+ /*
+ * TODO: The volume label is not available in barebox.
+ */
+ info->volume_label[0] = 0;
+ } else if (!efi_guidcmp(*info_type, efi_system_volume_label_id)) {
+ if (*buffer_size < 2) {
+ *buffer_size = 2;
+ efiret = EFI_BUFFER_TOO_SMALL;
+ goto error;
+ }
+ *(efi_char16_t *)buffer = 0;
+ } else {
+ efiret = EFI_UNSUPPORTED;
+ }
+
+error:
+ return EFI_EXIT2(efiret, *buffer_size);
+}
+
+static efi_status_t EFIAPI efi_file_set_info(struct efi_file_handle *file,
+ const efi_guid_t *info_type,
+ size_t buffer_size,
+ void *buffer)
+{
+ struct file_handle *fh = to_fh(file);
+ efi_status_t efiret = EFI_UNSUPPORTED;
+
+ EFI_ENTRY("%p, %pUs, %zu, %p", file, info_type, buffer_size, buffer);
+
+ if (!efi_guidcmp(*info_type, efi_file_info_id)) {
+ struct efi_file_info *info = (struct efi_file_info *)buffer;
+ const char *filename = kbasename(fh->path);
+ char *new_file_name, *pos;
+ loff_t file_size;
+
+ /* The buffer will always contain a file name. */
+ if (buffer_size < sizeof(struct efi_file_info) + 2 ||
+ buffer_size < info->Size) {
+ efiret = EFI_BAD_BUFFER_SIZE;
+ goto out;
+ }
+ /* We cannot change the directory attribute */
+ if (!!fh->dir != !!(info->Attribute & EFI_FILE_DIRECTORY)) {
+ efiret = EFI_ACCESS_DENIED;
+ goto out;
+ }
+ /* Check for renaming */
+ new_file_name = malloc(utf16_utf8_strlen(info->FileName) + 1);
+ if (!new_file_name) {
+ efiret = EFI_OUT_OF_RESOURCES;
+ goto out;
+ }
+ pos = new_file_name;
+ utf16_utf8_strcpy(&pos, info->FileName);
+ if (strcmp(new_file_name, filename)) {
+ /* TODO: we do not support renaming */
+ EFI_PRINT("Renaming not supported\n");
+ free(new_file_name);
+ efiret = EFI_ACCESS_DENIED;
+ goto out;
+ }
+ free(new_file_name);
+ /* Check for truncation */
+ efiret = efi_get_file_size(fh, &file_size);
+ if (efiret != EFI_SUCCESS)
+ goto out;
+ if (file_size != info->FileSize) {
+ /* TODO: we do not support truncation */
+ EFI_PRINT("Truncation not supported\n");
+ efiret = EFI_ACCESS_DENIED;
+ goto out;
+ }
+ /*
+ * We do not care for the other attributes
+ * TODO: Support read only
+ */
+ efiret = EFI_SUCCESS;
+ } else {
+ /* TODO: We do not support changing the volume label */
+ efiret = EFI_UNSUPPORTED;
+ }
+out:
+ return EFI_EXIT(efiret);
+}
+
+/**
+ * efi_file_flush_int() - flush file
+ *
+ * This is the internal implementation of the Flush() and FlushEx() services of
+ * the EFI_FILE_PROTOCOL.
+ *
+ * @this: file protocol instance
+ * Return: status code
+ */
+static efi_status_t efi_file_flush_int(struct efi_file_handle *this)
+{
+ struct file_handle *fh = to_fh(this);
+
+ if (!this)
+ return EFI_INVALID_PARAMETER;
+
+ if (!(fh->open_mode & EFI_FILE_MODE_WRITE))
+ return EFI_ACCESS_DENIED;
+
+ /* TODO: flush for file position after end of file */
+ return EFI_SUCCESS;
+}
+
+/**
+ * efi_file_flush() - flush file
+ *
+ * This function implements the Flush() service of the EFI_FILE_PROTOCOL.
+ *
+ * See the Unified Extensible Firmware Interface (UEFI) specification for
+ * details.
+ *
+ * @this: file protocol instance
+ * Return: status code
+ */
+static efi_status_t EFIAPI efi_file_flush(struct efi_file_handle *this)
+{
+ efi_status_t efiret;
+
+ EFI_ENTRY("%p", this);
+
+ efiret = efi_file_flush_int(this);
+
+ return EFI_EXIT(efiret);
+}
+
+/**
+ * efi_file_flush_ex() - flush file
+ *
+ * This function implements the FlushEx() service of the EFI_FILE_PROTOCOL.
+ *
+ * See the Unified Extensible Firmware Interface (UEFI) specification for
+ * details.
+ *
+ * @this: file protocol instance
+ * @token: transaction token
+ * Return: status code
+ */
+static efi_status_t EFIAPI efi_file_flush_ex(struct efi_file_handle *this,
+ struct efi_file_io_token *token)
+{
+ efi_status_t efiret;
+
+ EFI_ENTRY("%p, %p", this, token);
+
+ if (!token) {
+ efiret = EFI_INVALID_PARAMETER;
+ goto out;
+ }
+
+ efiret = efi_file_flush_int(this);
+
+ if (efiret == EFI_SUCCESS && token->event) {
+ token->status = EFI_SUCCESS;
+ efi_signal_event(token->event);
+ }
+
+out:
+ return EFI_EXIT(efiret);
+}
+
+static const struct efi_file_handle efi_file_handle_protocol = {
+ .Revision = EFI_FILE_HANDLE_REVISION2,
+ .open = efi_file_open,
+ .close = efi_file_close,
+ .delete = efi_file_delete,
+ .read = efi_file_read,
+ .write = efi_file_write,
+ .get_position = efi_file_getpos,
+ .set_position = efi_file_setpos,
+ .get_info = efi_file_get_info,
+ .set_info = efi_file_set_info,
+ .flush = efi_file_flush,
+ .open_ex = efi_file_open_ex,
+ .read_ex = efi_file_read_ex,
+ .write_ex = efi_file_write_ex,
+ .flush_ex = efi_file_flush_ex,
+};
+
+static efi_status_t EFIAPI
+efi_open_volume(struct efi_simple_file_system_protocol *this,
+ struct efi_file_handle **root)
+{
+ struct file_system *fs = to_fs(this);
+ const char rootpath[] = "/";
+ struct file_handle *fh;
+ const char *path;
+
+ EFI_ENTRY("%p, %p", this, root);
+
+ path = cdev_mount(fs->cdev);
+ if (IS_ERR(path))
+ return EFI_EXIT(EFI_DEVICE_ERROR);
+
+ fh = xzalloc(struct_size(fh, path, ARRAY_SIZE(rootpath)));
+
+ fh->open_mode = EFI_FILE_MODE_READ;
+ fh->base = efi_file_handle_protocol;
+ fh->fs = fs;
+
+ fs->fh = fh;
+
+ memcpy(fh->path, rootpath, sizeof(rootpath));
+
+ EFI_PRINT("Open volume %s\n", path);
+
+ if (efi_opendir(AT_FDCWD, path, fh) < 0)
+ goto error;
+
+ *root = &fh->base;
+
+ return EFI_EXIT2(EFI_SUCCESS, *root);
+
+error:
+ free(fh);
+ return EFI_EXIT(EFI_DEVICE_ERROR);
+}
+
+struct efi_simple_file_system_protocol *
+efi_simple_file_system(struct cdev *cdev,
+ struct efi_device_path *dp)
+{
+ struct file_system *fs;
+
+ fs = xzalloc(sizeof(*fs));
+ fs->base.Revision = EFI_SIMPLE_FILE_SYSTEM_PROTOCOL_REVISION;
+ fs->base.open_volume = efi_open_volume;
+ fs->cdev = cdev;
+ fs->dp = dp;
+
+ return &fs->base;
+}
diff --git a/include/efi/loader/file.h b/include/efi/loader/file.h
new file mode 100644
index 000000000000..ce52cc5df8aa
--- /dev/null
+++ b/include/efi/loader/file.h
@@ -0,0 +1,30 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+#ifndef __EFI_LOADER_FILE_H_
+#define __EFI_LOADER_FILE_H_
+
+#include <efi/types.h>
+
+struct cdev;
+struct efi_device_path;
+struct efi_simple_file_system_protocol;
+struct efi_file_handle;
+
+struct efi_simple_file_system_protocol *
+efi_fs_from_path(struct efi_device_path *fp);
+
+efi_status_t efi_file_size(struct efi_file_handle *fh, efi_uintn_t *size);
+
+/**
+ * efi_simple_file_system() - create simple file system protocol
+ *
+ * Create a simple file system protocol for a partition.
+ *
+ * @cdev: barebox cdev
+ * @dp: device path
+ * Return: status code
+ */
+struct efi_simple_file_system_protocol *
+efi_simple_file_system(struct cdev *cdev,
+ struct efi_device_path *dp);
+
+#endif
--
2.47.3
^ permalink raw reply [flat|nested] 55+ messages in thread* [PATCH v1 24/54] efi: loader: protocol: add Block IO support
2025-12-18 10:37 [PATCH v1 00/54] efi: implement EFI loader support in barebox Ahmad Fatoum
` (22 preceding siblings ...)
2025-12-18 10:37 ` [PATCH v1 23/54] efi: loader: protocol: add file protocol support Ahmad Fatoum
@ 2025-12-18 10:37 ` Ahmad Fatoum
2025-12-18 10:37 ` [PATCH v1 25/54] efi: loader: protocol: implement efi_file_from_path Ahmad Fatoum
` (29 subsequent siblings)
53 siblings, 0 replies; 55+ messages in thread
From: Ahmad Fatoum @ 2025-12-18 10:37 UTC (permalink / raw)
To: barebox; +Cc: Ahmad Fatoum
EFI firmware does usually not bring its own support for block devices,
so we need to support the Block IO protocol and also use that as hook to
register EFI file systems.
Signed-off-by: Ahmad Fatoum <a.fatoum@pengutronix.de>
---
efi/Kconfig | 2 +
efi/loader/protocols/Makefile | 1 +
efi/loader/protocols/disk.c | 401 ++++++++++++++++++++++++++++++++++
include/efi/loader.h | 2 +
4 files changed, 406 insertions(+)
create mode 100644 efi/loader/protocols/disk.c
diff --git a/efi/Kconfig b/efi/Kconfig
index 792073a9ef24..9c559207e95c 100644
--- a/efi/Kconfig
+++ b/efi/Kconfig
@@ -33,6 +33,8 @@ config EFI_LOADER
select EFI_GUID
select EFI_DEVICEPATH
select CHARSET
+ select BLOCK
+ select PARTITION_DISK
select MEMORY_ATTRIBUTES
select CRC32
help
diff --git a/efi/loader/protocols/Makefile b/efi/loader/protocols/Makefile
index 7cd92eb0bb54..91bea23677e9 100644
--- a/efi/loader/protocols/Makefile
+++ b/efi/loader/protocols/Makefile
@@ -1,3 +1,4 @@
# SPDX-License-Identifier: GPL-2.0
obj-$(CONFIG_FS) += file.o
+obj-$(CONFIG_DISK) += disk.o
diff --git a/efi/loader/protocols/disk.c b/efi/loader/protocols/disk.c
new file mode 100644
index 000000000000..cb1771e0cf99
--- /dev/null
+++ b/efi/loader/protocols/disk.c
@@ -0,0 +1,401 @@
+// SPDX-License-Identifier: GPL-2.0+
+// SPDX-Comment: Origin-URL: https://github.com/u-boot/u-boot/blob/2fdfb802e30a1fbd65a830a283d7bd87631f08c0/lib/efi_loader/efi_disk.c
+/*
+ * EFI application disk support
+ *
+ * Copyright (c) 2016 Alexander Graf
+ */
+
+#define pr_fmt(fmt) "efi-loader: disk: " fmt
+
+#include <efi/loader.h>
+#include <efi/devicepath.h>
+#include <efi/guid.h>
+#include <efi/error.h>
+#include <efi/protocol/block.h>
+#include <efi/loader/object.h>
+#include <efi/loader/devicepath.h>
+#include <efi/loader/file.h>
+#include <efi/loader/trace.h>
+#include <efi/partition.h>
+#include <malloc.h>
+#include <bootsource.h>
+#include <block.h>
+#include <fs.h>
+
+const efi_guid_t efi_system_partition_guid = PARTITION_SYSTEM_GUID;
+
+static struct cdev *chosen_esp_cdev;
+static int chosen_esp_dirfd = -1;
+static int max_esp_score;
+
+int efiloader_esp_mount_dir(void)
+{
+ return chosen_esp_dirfd;
+}
+
+/**
+ * struct efi_disk_obj - EFI disk object
+ *
+ * @header: EFI object header
+ * @ops: EFI disk I/O protocol interface
+ * @dp: device path to the block device
+ * @bdev: internal block device descriptor
+ * @blockbits of underlying block device
+ */
+struct efi_disk_obj {
+ struct efi_object header;
+ struct efi_block_io_protocol ops;
+ struct efi_block_io_media media;
+ struct efi_device_path *dp;
+ struct cdev *cdev;
+ struct efi_simple_file_system_protocol *volume;
+ u8 blockbits;
+};
+
+#define to_efi_disk_obj(this) container_of(this, struct efi_disk_obj, ops)
+
+/**
+ * efi_disk_reset() - reset block device
+ *
+ * This function implements the Reset service of the EFI_BLOCK_IO_PROTOCOL.
+ *
+ * As barebox block devices do not have a reset function simply return
+ * EFI_SUCCESS.
+ *
+ * See the Unified Extensible Firmware Interface (UEFI) specification for
+ * details.
+ *
+ * @this: pointer to the BLOCK_IO_PROTOCOL
+ * @extended_verification: extended verification
+ * Return: status code
+ */
+static efi_status_t EFIAPI efi_disk_reset(struct efi_block_io_protocol *this,
+ bool extended_verification)
+{
+ EFI_ENTRY("%p, %x", this, extended_verification);
+ return EFI_EXIT(EFI_SUCCESS);
+}
+
+static efi_status_t EFIAPI efi_disk_read(struct efi_block_io_protocol *this,
+ u32 media_id, u64 lba,
+ size_t buffer_size, void *buffer)
+{
+ struct efi_disk_obj *disk = to_efi_disk_obj(this);
+ ssize_t ret;
+
+ ret = cdev_read(disk->cdev, buffer, buffer_size, lba << disk->blockbits, 0);
+
+ return ret < 0 ? EFI_DEVICE_ERROR : EFI_SUCCESS;
+}
+
+static efi_status_t EFIAPI efi_disk_write(struct efi_block_io_protocol *this,
+ u32 media_id, u64 lba,
+ size_t buffer_size, void *buffer)
+{
+ struct efi_disk_obj *disk = to_efi_disk_obj(this);
+ ssize_t ret;
+
+ ret = cdev_write(disk->cdev, buffer, buffer_size, lba << disk->blockbits, 0);
+
+ return ret ? EFI_DEVICE_ERROR : EFI_SUCCESS;
+}
+
+static efi_status_t EFIAPI efi_disk_flush(struct efi_block_io_protocol *this)
+{
+ return cdev_flush(to_efi_disk_obj(this)->cdev) ? EFI_DEVICE_ERROR : EFI_SUCCESS;
+}
+
+static const struct efi_block_io_protocol block_io_disk_template = {
+ .reset = efi_disk_reset,
+ .read = efi_disk_read,
+ .write = efi_disk_write,
+ .flush = efi_disk_flush,
+};
+
+/**
+ * efi_fs_from_path() - retrieve simple file system protocol
+ *
+ * Gets the simple file system protocol for a file device path.
+ *
+ * The full path provided is split into device part and into a file
+ * part. The device part is used to find the handle on which the
+ * simple file system protocol is installed.
+ *
+ * @full_path: device path including device and file
+ * Return: simple file system protocol
+ */
+struct efi_simple_file_system_protocol *
+efi_fs_from_path(struct efi_device_path *full_path)
+{
+ struct efi_object *efiobj;
+ struct efi_handler *handler;
+ struct efi_device_path *device_path;
+ struct efi_device_path *file_path;
+ efi_status_t ret;
+
+ /* Split the path into a device part and a file part */
+ ret = efi_dp_split_file_path(full_path, &device_path, &file_path);
+ if (ret != EFI_SUCCESS)
+ return NULL;
+ efi_free_pool(file_path);
+
+ /* Get the EFI object for the partition */
+ efiobj = efi_dp_find_obj(device_path, NULL, NULL);
+ efi_free_pool(device_path);
+ if (!efiobj)
+ return NULL;
+
+ /* Find the simple file system protocol */
+ ret = efi_search_protocol(efiobj, &efi_simple_file_system_protocol_guid,
+ &handler);
+ if (ret != EFI_SUCCESS)
+ return NULL;
+
+ /* Return the simple file system protocol for the partition */
+ return handler->protocol_interface;
+}
+
+static bool efi_fs_exists(struct cdev *cdev)
+{
+ struct driver *drv;
+
+ bus_for_each_driver(&fs_bus, drv) {
+ struct fs_driver *fdrv;
+ enum filetype filetype;
+
+ fdrv = drv_to_fs_driver(drv);
+
+ if (cdev_detect_type(cdev, &filetype) == 0 &&
+ filetype == fdrv->type)
+ return true;
+ }
+
+ return false;
+}
+
+/**
+ * efi_disk_add_dev() - create a handle for a partition or disk
+ *
+ * @parent: parent handle
+ * @dp_parent: parent device path
+ * @cdev: cdev of block device or partition
+ * @disk: pointer to receive the created handle
+ * @removable: whether part of a removable block device
+ * Return: disk object
+ */
+static efi_status_t efi_disk_add_cdev(efi_handle_t parent,
+ struct efi_device_path *dp_parent,
+ struct cdev *cdev,
+ struct efi_disk_obj **disk,
+ bool removable)
+{
+ struct efi_disk_obj *diskobj;
+ struct efi_object *handle;
+ const efi_guid_t *esp_guid = NULL;
+ int score = 0;
+ efi_status_t ret;
+
+ diskobj = calloc(1, sizeof(*diskobj));
+ if (!diskobj)
+ return EFI_OUT_OF_RESOURCES;
+
+ /* Hook up to the device list */
+ efi_add_handle(&diskobj->header);
+
+ /* Fill in object data */
+ if (cdev->flags & DEVFS_PARTITION_FROM_TABLE) {
+ struct efi_device_path *node;
+ struct efi_handler *handler;
+ void *protocol_interface;
+
+ /* Parent must expose EFI_BLOCK_IO_PROTOCOL */
+ ret = efi_search_protocol(parent, &efi_block_io_protocol_guid, &handler);
+ if (ret != EFI_SUCCESS)
+ goto error;
+
+ /*
+ * Link the partition (child controller) to the block device
+ * (controller).
+ */
+ ret = efi_protocol_open(handler, &protocol_interface, NULL,
+ &diskobj->header,
+ EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER);
+ if (ret != EFI_SUCCESS)
+ goto error;
+
+ node = efi_dp_from_cdev(cdev, false);
+ diskobj->dp = efi_dp_append_node(dp_parent, node);
+ efi_free_pool(node);
+ if (cdev->flags & DEVFS_PARTITION_BOOTABLE_ESP) {
+ struct cdev *bootcdev;
+
+ esp_guid = &efi_system_partition_guid;
+
+ bootcdev = bootsource_of_cdev_find();
+
+ /* Prefer eMMC over SD if neither is the boot medium */
+ if (removable)
+ score += 1;
+ else
+ score += 2;
+
+ if (bootcdev && bootcdev == cdev)
+ score += 4;
+ }
+ } else {
+ diskobj->dp = efi_dp_from_cdev(cdev, true);
+ }
+
+ /*
+ * Install the device path and the block IO protocol.
+ *
+ * InstallMultipleProtocolInterfaces() checks if the device path is
+ * already installed on an other handle and returns EFI_ALREADY_STARTED
+ * in this case.
+ */
+ handle = &diskobj->header;
+ ret = efi_install_multiple_protocol_interfaces(&handle,
+ &efi_device_path_protocol_guid, diskobj->dp,
+ &efi_block_io_protocol_guid, &diskobj->ops,
+ esp_guid, NULL, NULL);
+ if (ret != EFI_SUCCESS)
+ return ret;
+
+ if ((cdev_is_partition(cdev)|| list_empty(&cdev->partitions)) && efi_fs_exists(cdev)) {
+ diskobj->volume = efi_simple_file_system(cdev, diskobj->dp);
+ ret = efi_add_protocol(&diskobj->header,
+ &efi_simple_file_system_protocol_guid,
+ diskobj->volume);
+ if (ret != EFI_SUCCESS)
+ return ret;
+ }
+
+ diskobj->media.removable_media = removable;
+ diskobj->media.media_present = true;
+ diskobj->media.read_only = cdev->flags & DEVFS_PARTITION_READONLY;
+ diskobj->media.block_size = 512;
+ diskobj->media.io_align = 512;
+ diskobj->media.last_block = cdev->size / diskobj->media.block_size - 1;
+
+ diskobj->ops = block_io_disk_template;
+ diskobj->ops.media = &diskobj->media;
+
+ diskobj->cdev = cdev;
+
+ if (disk)
+ *disk = diskobj;
+
+
+ if (score && score > max_esp_score) {
+ max_esp_score = score;
+ chosen_esp_cdev = cdev;
+ }
+
+ return EFI_SUCCESS;
+error:
+ efi_delete_handle(&diskobj->header);
+ return ret;
+}
+
+/**
+ * efi_disk_create_partitions() - create handles and protocols for partitions
+ *
+ * Create handles and protocols for the partitions of a block device.
+ *
+ * @parent: handle of the parent disk
+ * @bdev: block device
+ * Return: number of partitions created
+ */
+static int efi_disk_create_partitions(efi_handle_t parent, struct block_device *bdev)
+{
+ struct cdev *partcdev;
+ int npartitions = 0;
+ struct efi_device_path *dp = NULL;
+ efi_status_t ret;
+ struct efi_handler *handler;
+
+ /* Get the device path of the parent */
+ ret = efi_search_protocol(parent, &efi_device_path_protocol_guid, &handler);
+ if (ret == EFI_SUCCESS)
+ dp = handler->protocol_interface;
+
+ /* Add devices for each partition */
+ for_each_cdev_partition(partcdev, &bdev->cdev) {
+ ret = efi_disk_add_cdev(parent, dp, partcdev, NULL, bdev->removable);
+ if (ret != EFI_SUCCESS) {
+ pr_err("Adding partition %s failed\n", partcdev->name);
+ continue;
+ }
+
+ npartitions++;
+ }
+
+ return npartitions;
+}
+
+static void symlink_esp(void)
+{
+ const char *mntpath;
+
+ if (!chosen_esp_cdev)
+ return;
+
+ mntpath = cdev_mount(chosen_esp_cdev);
+ if (IS_ERR(mntpath)) {
+ pr_warn("Failed to mount %s as /esp: %pe\n",
+ chosen_esp_cdev->name, mntpath);
+ return;
+ }
+
+ chosen_esp_dirfd = open(mntpath, O_PATH | O_DIRECTORY);
+ if (chosen_esp_dirfd >= 0)
+ symlink(mntpath, "/esp");
+
+ pr_info("%s symlinked as /esp\n", mntpath);
+}
+
+/**
+ * efi_disk_register() - register block devices
+ *
+ * This function is called in efi_init_obj_list().
+ *
+ * Return: status code
+ */
+static efi_status_t efi_disk_register(void *data)
+{
+ struct block_device *bdev;
+ struct efi_disk_obj *disk;
+ unsigned ndisks = 0;
+ efi_status_t ret;
+
+ for_each_block_device(bdev) {
+ /* Add block device for the full device */
+ pr_debug("Scanning disk %s...\n", bdev->cdev.name);
+ ret = efi_disk_add_cdev(NULL, NULL, &bdev->cdev, &disk, bdev->removable);
+ if (ret) {
+ pr_err("failure to add disk device %s, r = %lu\n",
+ bdev->cdev.name, ret & ~EFI_ERROR_MASK);
+ return ret;
+ }
+
+ disk->blockbits = bdev->blockbits;
+ ndisks++;
+
+ /* Partitions show up as block devices in EFI */
+ ndisks += efi_disk_create_partitions(&disk->header, bdev);
+ }
+
+ pr_info("exporting %d disks\n", ndisks);
+
+ symlink_esp();
+
+ return EFI_SUCCESS;
+}
+
+static int efi_disk_init(void)
+{
+ efi_register_deferred_init(efi_disk_register, NULL);
+ return 0;
+}
+coredevice_initcall(efi_disk_init);
diff --git a/include/efi/loader.h b/include/efi/loader.h
index ef82a2f5214e..466541c1e2b5 100644
--- a/include/efi/loader.h
+++ b/include/efi/loader.h
@@ -74,4 +74,6 @@ extern struct device efidev;
void efi_register_deferred_init(efi_status_t (*init)(void *), void *);
void efi_add_root_node_protocol_deferred(const efi_guid_t *protocol, const void *interface);
+int efiloader_esp_mount_dir(void);
+
#endif /* _EFI_LOADER_H */
--
2.47.3
^ permalink raw reply [flat|nested] 55+ messages in thread* [PATCH v1 25/54] efi: loader: protocol: implement efi_file_from_path
2025-12-18 10:37 [PATCH v1 00/54] efi: implement EFI loader support in barebox Ahmad Fatoum
` (23 preceding siblings ...)
2025-12-18 10:37 ` [PATCH v1 24/54] efi: loader: protocol: add Block IO support Ahmad Fatoum
@ 2025-12-18 10:37 ` Ahmad Fatoum
2025-12-18 10:37 ` [PATCH v1 26/54] efi: loader: boot: implement LoadImage BootService Ahmad Fatoum
` (28 subsequent siblings)
53 siblings, 0 replies; 55+ messages in thread
From: Ahmad Fatoum @ 2025-12-18 10:37 UTC (permalink / raw)
To: barebox; +Cc: Ahmad Fatoum
For use by the EFI LoadImage boot service, implement support of opening
a file within an EFI simple file system by its barebox-generated EFI
device path.
Signed-off-by: Ahmad Fatoum <a.fatoum@pengutronix.de>
---
efi/loader/protocols/file.c | 67 +++++++++++++++++++++++++++++++++++++
include/efi/loader/file.h | 3 ++
2 files changed, 70 insertions(+)
diff --git a/efi/loader/protocols/file.c b/efi/loader/protocols/file.c
index 3858cd3e1682..03d2dc3a0d38 100644
--- a/efi/loader/protocols/file.c
+++ b/efi/loader/protocols/file.c
@@ -922,6 +922,73 @@ static const struct efi_file_handle efi_file_handle_protocol = {
.flush_ex = efi_file_flush_ex,
};
+/**
+ * efi_file_from_path() - open file via device path
+ *
+ * @fp: device path
+ * Return: EFI_FILE_PROTOCOL for the file or NULL
+ */
+struct efi_file_handle *efi_file_from_path(struct efi_device_path *fp)
+{
+ struct efi_simple_file_system_protocol *v;
+ struct efi_file_handle *f;
+ efi_status_t efiret;
+
+ v = efi_fs_from_path(fp);
+ if (!v)
+ return NULL;
+
+ efiret = v->open_volume(v, &f);
+ if (efiret != EFI_SUCCESS)
+ return NULL;
+
+ /* Skip over device-path nodes before the file path. */
+ while (fp && !EFI_DP_TYPE(fp, MEDIA_DEVICE, FILE_PATH))
+ fp = efi_dp_next(fp);
+
+ /*
+ * Step through the nodes of the directory path until the actual file
+ * node is reached which is the final node in the device path.
+ */
+ while (fp) {
+ struct efi_device_path_file_path *fdp =
+ container_of(fp, struct efi_device_path_file_path, header);
+ struct efi_file_handle *f2;
+ efi_char16_t *filename;
+ size_t filename_sz;
+
+ if (!EFI_DP_TYPE(fp, MEDIA_DEVICE, FILE_PATH)) {
+ pr_warn("bad file path!\n");
+ f->close(f);
+ return NULL;
+ }
+
+ /*
+ * UEFI specification requires pointers that are passed to
+ * protocol member functions to be aligned. So memcpy it
+ * unconditionally
+ */
+ if (fdp->header.length <= offsetof(struct efi_device_path_file_path, path_name))
+ return NULL;
+ filename_sz = fdp->header.length -
+ offsetof(struct efi_device_path_file_path, path_name);
+ filename = memdup(fdp->path_name, filename_sz);
+ if (!filename)
+ return NULL;
+ efiret = f->open(f, &f2, filename, EFI_FILE_MODE_READ, 0);
+ free(filename);
+ if (efiret != EFI_SUCCESS)
+ return NULL;
+
+ fp = efi_dp_next(fp);
+
+ f->close(f);
+ f = f2;
+ }
+
+ return f;
+}
+
static efi_status_t EFIAPI
efi_open_volume(struct efi_simple_file_system_protocol *this,
struct efi_file_handle **root)
diff --git a/include/efi/loader/file.h b/include/efi/loader/file.h
index ce52cc5df8aa..a4f071f97817 100644
--- a/include/efi/loader/file.h
+++ b/include/efi/loader/file.h
@@ -12,6 +12,9 @@ struct efi_file_handle;
struct efi_simple_file_system_protocol *
efi_fs_from_path(struct efi_device_path *fp);
+/* open file from device-path: */
+struct efi_file_handle *efi_file_from_path(struct efi_device_path *fp);
+
efi_status_t efi_file_size(struct efi_file_handle *fh, efi_uintn_t *size);
/**
--
2.47.3
^ permalink raw reply [flat|nested] 55+ messages in thread* [PATCH v1 26/54] efi: loader: boot: implement LoadImage BootService
2025-12-18 10:37 [PATCH v1 00/54] efi: implement EFI loader support in barebox Ahmad Fatoum
` (24 preceding siblings ...)
2025-12-18 10:37 ` [PATCH v1 25/54] efi: loader: protocol: implement efi_file_from_path Ahmad Fatoum
@ 2025-12-18 10:37 ` Ahmad Fatoum
2025-12-18 10:37 ` [PATCH v1 27/54] efi: loader: add EFI load option handling Ahmad Fatoum
` (27 subsequent siblings)
53 siblings, 0 replies; 55+ messages in thread
From: Ahmad Fatoum @ 2025-12-18 10:37 UTC (permalink / raw)
To: barebox; +Cc: Ahmad Fatoum
We now have everything in place, so we can actually start images like
the barebox EFI payload itself, even if ExitBootServices is not yet
supported.
Signed-off-by: Ahmad Fatoum <a.fatoum@pengutronix.de>
---
efi/loader/boot.c | 591 ++++++++++++++++++++++++++++++++++++++++++-
include/efi/loader.h | 4 +
2 files changed, 591 insertions(+), 4 deletions(-)
diff --git a/efi/loader/boot.c b/efi/loader/boot.c
index 263165135944..2d208c4cafbd 100644
--- a/efi/loader/boot.c
+++ b/efi/loader/boot.c
@@ -20,6 +20,8 @@
#include <efi/loader/devicepath.h>
#include <efi/loader/debug.h>
#include <efi/loader/image.h>
+#include <efi/loader/file.h>
+#include <efi/loader/pe.h>
#include <efi/protocol/file.h>
#include <efi/guid.h>
#include <efi/error.h>
@@ -58,6 +60,9 @@ static bool timers_enabled = true;
/* List of all events registered by RegisterProtocolNotify() */
static LIST_HEAD(efi_register_notify_events);
+/* Handle of the currently executing image */
+static efi_handle_t current_image;
+
efi_status_t efi_uninstall_protocol
(efi_handle_t handle, const efi_guid_t *protocol,
void *protocol_interface, bool preserve);
@@ -1652,6 +1657,88 @@ EFIAPI efi_install_configuration_table_ext(const efi_guid_t *guid,
return EFI_EXIT(efi_install_configuration_table(guid, table));
}
+/**
+ * efi_setup_loaded_image() - initialize a loaded image
+ *
+ * Initialize a loaded_image_info and loaded_image_info object with correct
+ * protocols, boot-device, etc.
+ *
+ * In case of an error \*handle_ptr and \*info_ptr are set to NULL and an error
+ * code is returned.
+ *
+ * @device_path: device path of the loaded image
+ * @file_path: file path of the loaded image
+ * @handle_ptr: handle of the loaded image
+ * @info_ptr: loaded image protocol
+ * Return: status code
+ */
+static efi_status_t efi_setup_loaded_image(struct efi_device_path *device_path,
+ struct efi_device_path *file_path,
+ struct efi_loaded_image_obj **handle_ptr,
+ struct efi_loaded_image **info_ptr)
+{
+ efi_status_t ret;
+ struct efi_loaded_image *info = NULL;
+ struct efi_loaded_image_obj *obj = NULL;
+ struct efi_device_path *dp;
+
+ /* In case of EFI_OUT_OF_RESOURCES avoid illegal free by caller. */
+ *handle_ptr = NULL;
+ *info_ptr = NULL;
+
+ info = calloc(1, sizeof(*info));
+ if (!info)
+ return EFI_OUT_OF_RESOURCES;
+ obj = calloc(1, sizeof(*obj));
+ if (!obj) {
+ free(info);
+ return EFI_OUT_OF_RESOURCES;
+ }
+ obj->header.type = EFI_OBJECT_TYPE_LOADED_IMAGE;
+
+ /* Add internal object to object list */
+ efi_add_handle(&obj->header);
+
+ info->revision = EFI_LOADED_IMAGE_PROTOCOL_REVISION;
+ info->file_path = file_path;
+ info->system_table = &systab;
+
+ if (device_path) {
+ info->device_handle = efi_dp_find_obj(device_path, NULL, NULL);
+
+ dp = efi_dp_concat(device_path, file_path, 0);
+ if (!dp) {
+ ret = EFI_OUT_OF_RESOURCES;
+ goto failure;
+ }
+ } else {
+ dp = NULL;
+ }
+ ret = efi_add_protocol(&obj->header,
+ &efi_loaded_image_device_path_guid, dp);
+ if (ret != EFI_SUCCESS)
+ goto failure;
+
+ /*
+ * When asking for the loaded_image interface, just
+ * return handle which points to loaded_image_info
+ */
+ ret = efi_add_protocol(&obj->header,
+ &efi_loaded_image_protocol_guid, info);
+ if (ret != EFI_SUCCESS)
+ goto failure;
+
+ *info_ptr = info;
+ *handle_ptr = obj;
+
+ return ret;
+failure:
+ pr_err("Failure to install protocols for loaded image\n");
+ efi_delete_handle(&obj->header);
+ free(info);
+ return ret;
+}
+
/**
* efi_locate_device_path() - Get the device path and handle of an device
* implementing a protocol
@@ -1733,6 +1820,216 @@ static efi_status_t EFIAPI efi_locate_device_path(const efi_guid_t *protocol,
return EFI_EXIT2(ret, *device_path);
}
+/**
+ * efi_load_image_from_file() - load an image from file system
+ *
+ * Read a file into a buffer allocated as EFI_BOOT_SERVICES_DATA. It is the
+ * callers obligation to update the memory type as needed.
+ *
+ * @file_path: the path of the image to load
+ * @buffer: buffer containing the loaded image
+ * @size: size of the loaded image
+ * Return: status code
+ */
+static
+efi_status_t efi_load_image_from_file(struct efi_device_path *file_path,
+ void **buffer, efi_uintn_t *size)
+{
+ struct efi_file_handle *f;
+ efi_status_t ret;
+ u64 addr;
+ efi_uintn_t bs;
+
+ /* Open file */
+ f = efi_file_from_path(file_path);
+ if (!f)
+ return EFI_NOT_FOUND;
+
+ ret = efi_file_size(f, &bs);
+ if (ret != EFI_SUCCESS)
+ goto error;
+
+ /*
+ * When reading the file we do not yet know if it contains an
+ * application, a boottime driver, or a runtime driver. So here we
+ * allocate a buffer as EFI_BOOT_SERVICES_DATA. The caller has to
+ * update the reservation according to the image type.
+ */
+ ret = efi_allocate_pages(EFI_ALLOCATE_ANY_PAGES,
+ EFI_BOOT_SERVICES_DATA,
+ efi_size_in_pages(bs), &addr,
+ "loadimage");
+ if (ret != EFI_SUCCESS) {
+ ret = EFI_OUT_OF_RESOURCES;
+ goto error;
+ }
+
+ /* Read file */
+ EFI_CALL(ret = f->read(f, &bs, (void *)(uintptr_t)addr));
+ if (ret != EFI_SUCCESS)
+ efi_free_pages(addr, efi_size_in_pages(bs));
+ *buffer = (void *)(uintptr_t)addr;
+ *size = bs;
+error:
+ EFI_CALL(f->close(f));
+ return ret;
+}
+
+/**
+ * efi_load_image_from_path() - load an image using a file path
+ *
+ * Read a file into a buffer allocated as EFI_BOOT_SERVICES_DATA. It is the
+ * callers obligation to update the memory type as needed.
+ *
+ * @boot_policy: true for request originating from the boot manager
+ * @file_path: the path of the image to load
+ * @buffer: buffer containing the loaded image
+ * @size: size of the loaded image
+ * Return: status code
+ */
+static
+efi_status_t efi_load_image_from_path(bool boot_policy,
+ struct efi_device_path *file_path,
+ void **buffer, efi_uintn_t *size)
+{
+ efi_handle_t device;
+ efi_status_t ret;
+ struct efi_device_path *dp, *rem;
+ struct efi_load_file_protocol *load_file_protocol = NULL;
+ efi_uintn_t buffer_size;
+ uint64_t addr, pages;
+ const efi_guid_t *guid;
+ struct efi_handler *handler;
+
+ /* In case of failure nothing is returned */
+ *buffer = NULL;
+ *size = 0;
+
+ dp = file_path;
+ device = efi_dp_find_obj(dp, NULL, &rem);
+ ret = efi_search_protocol(device, &efi_simple_file_system_protocol_guid,
+ NULL);
+ if (ret == EFI_SUCCESS)
+ return efi_load_image_from_file(file_path, buffer, size);
+
+ ret = efi_search_protocol(device, &efi_load_file_protocol_guid, NULL);
+ if (ret == EFI_SUCCESS) {
+ guid = &efi_load_file_protocol_guid;
+ } else if (!boot_policy) {
+ guid = &efi_load_file2_protocol_guid;
+ ret = efi_search_protocol(device, guid, NULL);
+ }
+ if (ret != EFI_SUCCESS)
+ return EFI_NOT_FOUND;
+ ret = efi_search_protocol(device, guid, &handler);
+ if (ret != EFI_SUCCESS)
+ return EFI_NOT_FOUND;
+ buffer_size = 0;
+ load_file_protocol = handler->protocol_interface;
+ ret = EFI_CALL(load_file_protocol->load_file(
+ load_file_protocol, rem, boot_policy,
+ &buffer_size, NULL));
+ if (ret != EFI_BUFFER_TOO_SMALL)
+ goto out;
+ pages = efi_size_in_pages(buffer_size);
+ ret = efi_allocate_pages(EFI_ALLOCATE_ANY_PAGES, EFI_BOOT_SERVICES_DATA,
+ pages, &addr, "loadimage");
+ if (ret != EFI_SUCCESS) {
+ ret = EFI_OUT_OF_RESOURCES;
+ goto out;
+ }
+ ret = EFI_CALL(load_file_protocol->load_file(
+ load_file_protocol, rem, boot_policy,
+ &buffer_size, (void *)(uintptr_t)addr));
+ if (ret != EFI_SUCCESS)
+ efi_free_pages(addr, pages);
+out:
+ efi_close_protocol(device, guid, efi_root, NULL);
+ if (ret == EFI_SUCCESS) {
+ *buffer = (void *)(uintptr_t)addr;
+ *size = buffer_size;
+ }
+
+ return ret;
+}
+
+/**
+ * efiloader_load_image() - load an EFI image into memory
+ * @boot_policy: true for request originating from the boot manager
+ * @parent_image: the caller's image handle
+ * @file_path: the path of the image to load
+ * @source_buffer: memory location from which the image is installed
+ * @source_size: size of the memory area from which the image is installed
+ * @image_handle: handle for the newly installed image
+ *
+ * This function implements the LoadImage service.
+ *
+ * See the Unified Extensible Firmware Interface (UEFI) specification
+ * for details.
+ *
+ * Return: status code
+ */
+efi_status_t EFIAPI efiloader_load_image(bool boot_policy,
+ efi_handle_t parent_image,
+ struct efi_device_path *file_path,
+ void *source_buffer,
+ efi_uintn_t source_size,
+ efi_handle_t *image_handle)
+{
+ struct efi_device_path *dp, *fp;
+ struct efi_loaded_image *info = NULL;
+ struct efi_loaded_image_obj **image_obj =
+ (struct efi_loaded_image_obj **)image_handle;
+ efi_status_t ret;
+ void *dest_buffer;
+
+ EFI_ENTRY("%d, %p, %pD, %p, %zu, %p", boot_policy, parent_image,
+ file_path, source_buffer, source_size, image_handle);
+
+ if (!image_handle || (!source_buffer && !file_path) ||
+ !efi_search_obj(parent_image) ||
+ /* The parent image handle must refer to a loaded image */
+ !parent_image->type) {
+ ret = EFI_INVALID_PARAMETER;
+ goto error;
+ }
+
+ if (!source_buffer) {
+ ret = efi_load_image_from_path(boot_policy, file_path,
+ &dest_buffer, &source_size);
+ if (ret != EFI_SUCCESS)
+ goto error;
+ } else {
+ dest_buffer = source_buffer;
+ }
+ /* split file_path which contains both the device and file parts */
+ efi_dp_split_file_path(file_path, &dp, &fp);
+ ret = efi_setup_loaded_image(dp, fp, image_obj, &info);
+ if (ret == EFI_SUCCESS)
+ ret = efi_load_pe(*image_obj, dest_buffer, source_size, info);
+ if (!source_buffer)
+ /* Release buffer to which file was loaded */
+ efi_free_pages((uintptr_t)dest_buffer,
+ efi_size_in_pages(source_size));
+ if (ret == EFI_SUCCESS || ret == EFI_SECURITY_VIOLATION) {
+ info->system_table = &systab;
+ info->parent_handle = parent_image;
+ } else {
+ /* The image is invalid. Release all associated resources. */
+ efi_delete_handle(*image_handle);
+ *image_handle = NULL;
+ free(info);
+ }
+
+ if (IS_ENABLED(CONFIG_EFI_LOADER_DEBUG_SUPPORT) && *image_handle)
+ efi_core_new_debug_image_info_entry(EFI_DEBUG_IMAGE_INFO_TYPE_NORMAL,
+ info,
+ *image_handle);
+
+error:
+ return EFI_EXIT2(ret, *image_handle);
+}
+
/**
* efi_exit_boot_services() - stop all boot services
* @image_handle: handle of the loaded image
@@ -2666,6 +2963,104 @@ static efi_status_t EFIAPI efi_open_protocol
return EFI_EXIT(r);
}
+/**
+ * __efi_start_image() - call the entry point of an image
+ * @image_handle: handle of the image
+ * @exit_data_size: size of the buffer
+ * @exit_data: buffer to receive the exit data of the called image
+ * @flags: boot flags
+ *
+ * This function implements the StartImage service.
+ *
+ * See the Unified Extensible Firmware Interface (UEFI) specification for
+ * details.
+ *
+ * Return: status code
+ */
+efi_status_t __efi_start_image(efi_handle_t image_handle,
+ efi_uintn_t *exit_data_size,
+ u16 **exit_data, int flags)
+{
+ struct efi_loaded_image_obj *image_obj =
+ (struct efi_loaded_image_obj *)image_handle;
+ efi_status_t ret;
+ struct efi_loaded_image *info;
+ efi_handle_t parent_image = current_image;
+ efi_status_t exit_status;
+ struct jmp_buf_data exit_jmp;
+ EFIAPI efi_status_t (*entry)(efi_handle_t image_handle,
+ struct efi_system_table *st);
+
+ if (!efi_search_obj(image_handle))
+ return EFI_EXIT(EFI_INVALID_PARAMETER);
+
+ /* Check parameters */
+ if (image_obj->header.type != EFI_OBJECT_TYPE_LOADED_IMAGE)
+ return EFI_EXIT(EFI_INVALID_PARAMETER);
+
+ ret = EFI_CALL(efi_open_protocol(image_handle, &efi_loaded_image_protocol_guid,
+ (void **)&info, NULL, NULL,
+ EFI_OPEN_PROTOCOL_GET_PROTOCOL));
+ if (ret != EFI_SUCCESS)
+ return EFI_EXIT(EFI_INVALID_PARAMETER);
+
+ image_obj->exit_data_size = exit_data_size;
+ image_obj->exit_data = exit_data;
+ image_obj->exit_status = &exit_status;
+ image_obj->exit_jmp = &exit_jmp;
+
+ /* call the image! */
+ if (setjmp(&exit_jmp)) {
+ /*
+ * We called the entry point of the child image with EFI_CALL
+ * in the lines below. The child image called the Exit() boot
+ * service efi_exit() which executed the long jump that brought
+ * us to the current line. This implies that the second half
+ * of the EFI_CALL macro has not been executed.
+ */
+ /*
+ * To get ready to call EFI_EXIT below we have to execute the
+ * missed out steps of EFI_CALL.
+ */
+ EFI_RETURN(exit_status);
+
+ current_image = parent_image;
+
+ return EFI_EXIT(exit_status);
+ }
+
+ current_image = image_handle;
+ image_obj->header.type = EFI_OBJECT_TYPE_STARTED_IMAGE;
+
+ if (flags & EFI_DRYRUN) {
+ pr_info("Dryrun. Aborted\n");
+ ret = EFI_SUCCESS;
+ goto exit;
+ }
+
+ entry = image_obj->entry;
+
+ pr_info("Starting EFI payload at %p\n", entry);
+
+ // FIXME: we need the below if we enable CONFIG_ARM_MMU_PERMISSIONS...
+ // remap_range(info->image_base, info->image_size, MAP_ARCH(2));
+ sync_caches_for_execution();
+
+ efi_set_variable_usec("LoaderTimeExecUSec", &efi_systemd_vendor_guid,
+ ktime_to_us(ktime_get()));
+
+ ret = EFI_CALL(entry(image_handle, &systab));
+
+exit:
+ /*
+ * Control is returned from a started UEFI image either by calling
+ * Exit() (where exit data can be provided) or by simply returning from
+ * the entry point. In the latter case call Exit() on behalf of the
+ * image.
+ */
+ return EFI_CALL(systab.boottime->exit(image_handle, ret, 0, NULL));
+}
+
/**
* efi_start_image() - call the entry point of an image
* @image_handle: handle of the image
@@ -2685,7 +3080,54 @@ static efi_status_t EFIAPI efi_start_image(efi_handle_t image_handle,
{
EFI_ENTRY("%p, %p, %p", image_handle, exit_data_size, exit_data);
- return EFI_EXIT(EFI_UNSUPPORTED);
+ return __efi_start_image(image_handle, exit_data_size, exit_data, 0);
+}
+
+/**
+ * efi_delete_image() - delete loaded image from memory)
+ *
+ * @image_obj: handle of the loaded image
+ * @loaded_image_protocol: loaded image protocol
+ */
+static efi_status_t efi_delete_image
+ (struct efi_loaded_image_obj *image_obj,
+ struct efi_loaded_image *loaded_image_protocol)
+{
+ struct efi_object *efiobj;
+ efi_status_t r, ret = EFI_SUCCESS;
+
+close_next:
+ list_for_each_entry(efiobj, &efi_obj_list, link) {
+ struct efi_handler *protocol;
+
+ list_for_each_entry(protocol, &efiobj->protocols, link) {
+ struct efi_open_protocol_info_item *info;
+
+ list_for_each_entry(info, &protocol->open_infos, link) {
+ if (info->info.agent_handle !=
+ (efi_handle_t)image_obj)
+ continue;
+ r = efi_close_protocol(
+ efiobj, &protocol->guid,
+ info->info.agent_handle,
+ info->info.controller_handle);
+ if (r != EFI_SUCCESS)
+ ret = r;
+ /*
+ * Closing protocols may results in further
+ * items being deleted. To play it safe loop
+ * over all elements again.
+ */
+ goto close_next;
+ }
+ }
+ }
+
+ efi_free_pages((uintptr_t)loaded_image_protocol->image_base,
+ efi_size_in_pages(loaded_image_protocol->image_size));
+ efi_delete_handle(&image_obj->header);
+
+ return ret;
}
/**
@@ -2701,7 +3143,87 @@ static efi_status_t EFIAPI efi_start_image(efi_handle_t image_handle,
*/
static efi_status_t EFIAPI efi_unload_image(efi_handle_t image_handle)
{
- return EFI_EXIT(EFI_UNSUPPORTED);
+ efi_status_t ret = EFI_SUCCESS;
+ struct efi_object *efiobj;
+ struct efi_loaded_image *loaded_image_protocol;
+
+ EFI_ENTRY("%p", image_handle);
+
+ efiobj = efi_search_obj(image_handle);
+ if (!efiobj) {
+ ret = EFI_INVALID_PARAMETER;
+ goto out;
+ }
+ /* Find the loaded image protocol */
+ ret = EFI_CALL(efi_open_protocol(image_handle, &efi_loaded_image_protocol_guid,
+ (void **)&loaded_image_protocol,
+ NULL, NULL,
+ EFI_OPEN_PROTOCOL_GET_PROTOCOL));
+ if (ret != EFI_SUCCESS) {
+ ret = EFI_INVALID_PARAMETER;
+ goto out;
+ }
+
+ if (IS_ENABLED(CONFIG_EFI_LOADER_DEBUG_SUPPORT))
+ efi_core_remove_debug_image_info_entry(image_handle);
+
+ switch (efiobj->type) {
+ case EFI_OBJECT_TYPE_STARTED_IMAGE:
+ /* Call the unload function */
+ if (!loaded_image_protocol->unload) {
+ ret = EFI_UNSUPPORTED;
+ goto out;
+ }
+ ret = EFI_CALL(loaded_image_protocol->unload(image_handle));
+ if (ret != EFI_SUCCESS)
+ goto out;
+ break;
+ case EFI_OBJECT_TYPE_LOADED_IMAGE:
+ break;
+ default:
+ ret = EFI_INVALID_PARAMETER;
+ goto out;
+ }
+ efi_delete_image((struct efi_loaded_image_obj *)efiobj,
+ loaded_image_protocol);
+out:
+ return EFI_EXIT(ret);
+}
+
+/**
+ * efi_update_exit_data() - fill exit data parameters of StartImage()
+ *
+ * @image_obj: image handle
+ * @exit_data_size: size of the exit data buffer
+ * @exit_data: buffer with data returned by UEFI payload
+ * Return: status code
+ */
+static efi_status_t efi_update_exit_data(struct efi_loaded_image_obj *image_obj,
+ efi_uintn_t exit_data_size,
+ u16 *exit_data)
+{
+ efi_status_t ret;
+
+ /*
+ * If exit_data is not provided to StartImage(), exit_data_size must be
+ * ignored.
+ */
+ if (!image_obj->exit_data)
+ return EFI_SUCCESS;
+ if (image_obj->exit_data_size)
+ *image_obj->exit_data_size = exit_data_size;
+ if (exit_data_size && exit_data) {
+ ret = efi_allocate_pool(EFI_BOOT_SERVICES_DATA,
+ exit_data_size,
+ (void **)image_obj->exit_data,
+ "exitdata");
+ if (ret != EFI_SUCCESS)
+ return ret;
+ memcpy(*image_obj->exit_data, exit_data, exit_data_size);
+ } else {
+ image_obj->exit_data = NULL;
+ }
+ return EFI_SUCCESS;
}
/**
@@ -2723,10 +3245,71 @@ static efi_status_t EFIAPI efi_exit(efi_handle_t image_handle,
efi_uintn_t exit_data_size,
u16 *exit_data)
{
+ /*
+ * TODO: We should call the unload procedure of the loaded
+ * image protocol.
+ */
+ efi_status_t ret;
+ struct efi_loaded_image *loaded_image_protocol;
+ struct efi_loaded_image_obj *image_obj =
+ (struct efi_loaded_image_obj *)image_handle;
+ struct jmp_buf_data *exit_jmp;
+
EFI_ENTRY("%p, %ld, %zu, %p", image_handle, exit_status,
exit_data_size, exit_data);
- return EFI_EXIT(EFI_UNSUPPORTED);
+ /* Check parameters */
+ ret = EFI_CALL(efi_open_protocol(image_handle, &efi_loaded_image_protocol_guid,
+ (void **)&loaded_image_protocol,
+ NULL, NULL,
+ EFI_OPEN_PROTOCOL_GET_PROTOCOL));
+ if (ret != EFI_SUCCESS) {
+ ret = EFI_INVALID_PARAMETER;
+ goto out;
+ }
+
+ /* Unloading of unstarted images */
+ switch (image_obj->header.type) {
+ case EFI_OBJECT_TYPE_STARTED_IMAGE:
+ break;
+ case EFI_OBJECT_TYPE_LOADED_IMAGE:
+ efi_delete_image(image_obj, loaded_image_protocol);
+ ret = EFI_SUCCESS;
+ goto out;
+ default:
+ /* Handle does not refer to loaded image */
+ ret = EFI_INVALID_PARAMETER;
+ goto out;
+ }
+ /* A started image can only be unloaded it is the last one started. */
+ if (image_handle != current_image) {
+ ret = EFI_INVALID_PARAMETER;
+ goto out;
+ }
+
+ /* Exit data is only foreseen in case of failure. */
+ if (exit_status != EFI_SUCCESS) {
+ ret = efi_update_exit_data(image_obj, exit_data_size,
+ exit_data);
+ /* Exiting has priority. Don't return error to caller. */
+ if (ret != EFI_SUCCESS)
+ EFI_PRINT("%s: out of memory\n", __func__);
+ }
+ /* efi_delete_image() frees image_obj. Copy before the call. */
+ exit_jmp = image_obj->exit_jmp;
+ *image_obj->exit_status = exit_status;
+ if (image_obj->image_type == IMAGE_SUBSYSTEM_EFI_APPLICATION ||
+ exit_status != EFI_SUCCESS)
+ efi_delete_image(image_obj, loaded_image_protocol);
+
+ /* Make sure entry/exit counts for EFI world cross-overs match */
+ EFI_EXIT(exit_status);
+
+ longjmp(exit_jmp, 1);
+
+ panic("EFI application exited");
+out:
+ return EFI_EXIT(ret);
}
/**
@@ -3170,7 +3753,7 @@ static struct efi_boot_services efi_boot_services = {
.locate_handle = efi_locate_handle_ext,
.locate_device_path = efi_locate_device_path,
.install_configuration_table = efi_install_configuration_table_ext,
- .load_image = (void *)efi_unimplemented,
+ .load_image = efiloader_load_image,
.start_image = efi_start_image,
.exit = efi_exit,
.unload_image = efi_unload_image,
diff --git a/include/efi/loader.h b/include/efi/loader.h
index 466541c1e2b5..74d177568353 100644
--- a/include/efi/loader.h
+++ b/include/efi/loader.h
@@ -29,6 +29,10 @@ efi_status_t efi_initialize_system_table(void);
/* Called by efi_set_watchdog_timer to reset the timer */
efi_status_t efi_set_watchdog(unsigned long timeout);
+efi_status_t __efi_start_image(efi_handle_t image_handle,
+ efi_uintn_t *exit_data_size,
+ u16 **exit_data, int flags);
+
/* Allocate boot service data pool memory */
void *efi_alloc(size_t len, const char *name);
/* Reallocate boot service data pool memory */
--
2.47.3
^ permalink raw reply [flat|nested] 55+ messages in thread* [PATCH v1 27/54] efi: loader: add EFI load option handling
2025-12-18 10:37 [PATCH v1 00/54] efi: implement EFI loader support in barebox Ahmad Fatoum
` (25 preceding siblings ...)
2025-12-18 10:37 ` [PATCH v1 26/54] efi: loader: boot: implement LoadImage BootService Ahmad Fatoum
@ 2025-12-18 10:37 ` Ahmad Fatoum
2025-12-18 10:37 ` [PATCH v1 28/54] efi: loader: protocol: add graphical output protocol support Ahmad Fatoum
` (26 subsequent siblings)
53 siblings, 0 replies; 55+ messages in thread
From: Ahmad Fatoum @ 2025-12-18 10:37 UTC (permalink / raw)
To: barebox; +Cc: Ahmad Fatoum
Import the load option utility functions from U-Boot, so barebox can
pass along the kernel command line.
Signed-off-by: Ahmad Fatoum <a.fatoum@pengutronix.de>
---
efi/loader/Makefile | 1 +
efi/loader/loadopts.c | 181 ++++++++++++++++++++++++++++++++++++
include/efi/loader/option.h | 36 +++++++
3 files changed, 218 insertions(+)
create mode 100644 efi/loader/loadopts.c
create mode 100644 include/efi/loader/option.h
diff --git a/efi/loader/Makefile b/efi/loader/Makefile
index bb6aa05c3748..44ba3a86e4d4 100644
--- a/efi/loader/Makefile
+++ b/efi/loader/Makefile
@@ -11,3 +11,4 @@ obj-y += runtime.o
obj-y += setup.o
obj-y += watchdog.o
obj-y += pe.o
+obj-y += loadopts.o
diff --git a/efi/loader/loadopts.c b/efi/loader/loadopts.c
new file mode 100644
index 000000000000..388b8bf699c4
--- /dev/null
+++ b/efi/loader/loadopts.c
@@ -0,0 +1,181 @@
+// SPDX-License-Identifier: GPL-2.0+
+// SPDX-Comment: Origin-URL: https://github.com/u-boot/u-boot/blob/c38cb227d39f8ce9983df8051f31dcc8466f311e/lib/efi_loader/efi_load_options.c
+/*
+ * EFI boot manager
+ *
+ * Copyright (c) 2018 AKASHI Takahiro, et.al.
+ */
+
+#define pr_fmt(fmt) "efi-loader: loadopts: " fmt
+
+#include <charset.h>
+#include <malloc.h>
+#include <efi/loader.h>
+#include <efi/loader/object.h>
+#include <efi/loader/option.h>
+#include <efi/error.h>
+#include <efi/guid.h>
+#include <efi/devicepath.h>
+#include <asm/unaligned.h>
+
+/**
+ * efi_set_load_options() - set the load options of a loaded image
+ *
+ * @handle: the image handle
+ * @load_options_size: size of load options
+ * @load_options: pointer to load options
+ * Return: status code
+ */
+efi_status_t efi_set_load_options(efi_handle_t handle,
+ efi_uintn_t load_options_size,
+ void *load_options)
+{
+ struct efi_loaded_image *loaded_image_info;
+ struct efi_handler *handler;
+ efi_status_t ret;
+
+ ret = efi_search_protocol(handle, &efi_loaded_image_protocol_guid, &handler);
+ if (ret != EFI_SUCCESS)
+ return EFI_INVALID_PARAMETER;
+
+ loaded_image_info = handler->protocol_interface;
+ loaded_image_info->load_options = load_options;
+ loaded_image_info->load_options_size = load_options_size;
+
+ return EFI_SUCCESS;
+}
+
+/**
+ * efi_dp_check_length() - check length of a device path
+ *
+ * @dp: pointer to device path
+ * @maxlen: maximum length of the device path
+ * Return:
+ * * length of the device path if it is less or equal @maxlen
+ * * -1 if the device path is longer then @maxlen
+ * * -1 if a device path node has a length of less than 4
+ * * -EINVAL if maxlen exceeds SSIZE_MAX
+ */
+static ssize_t efi_dp_check_length(const struct efi_device_path *dp,
+ const size_t maxlen)
+{
+ ssize_t ret = 0;
+ u16 len;
+
+ if (maxlen > SSIZE_MAX)
+ return -EINVAL;
+ for (;;) {
+ len = dp->length;
+ if (len < 4)
+ return -1;
+ ret += len;
+ if (ret > maxlen)
+ return -1;
+ if (dp->type == DEVICE_PATH_TYPE_END &&
+ dp->sub_type == DEVICE_PATH_SUB_TYPE_END)
+ return ret;
+ dp = (const struct efi_device_path *)((const u8 *)dp + len);
+ }
+}
+
+/**
+ * efi_deserialize_load_option() - parse serialized data
+ *
+ * Parse serialized data describing a load option and transform it to the
+ * efi_load_option structure.
+ *
+ * @lo: pointer to target
+ * @data: serialized data
+ * @size: size of the load option, on return size of the optional data
+ * Return: status code
+ */
+efi_status_t efi_deserialize_load_option(struct efi_load_option_unpacked *lo, u8 *data,
+ efi_uintn_t *size)
+{
+ efi_uintn_t len;
+
+ len = sizeof(u32);
+ if (*size < len + 2 * sizeof(u16))
+ return EFI_INVALID_PARAMETER;
+ lo->attributes = get_unaligned_le32(data);
+ data += len;
+ *size -= len;
+
+ len = sizeof(u16);
+ lo->file_path_length = get_unaligned_le16(data);
+ data += len;
+ *size -= len;
+
+ lo->label = (u16 *)data;
+ len = u16_strnlen(lo->label, *size / sizeof(u16) - 1);
+ if (lo->label[len])
+ return EFI_INVALID_PARAMETER;
+ len = (len + 1) * sizeof(u16);
+ if (*size < len)
+ return EFI_INVALID_PARAMETER;
+ data += len;
+ *size -= len;
+
+ len = lo->file_path_length;
+ if (*size < len)
+ return EFI_INVALID_PARAMETER;
+ lo->file_path = (struct efi_device_path *)data;
+ if (efi_dp_check_length(lo->file_path, len) < 0)
+ return EFI_INVALID_PARAMETER;
+ data += len;
+ *size -= len;
+
+ lo->optional_data = data;
+
+ return EFI_SUCCESS;
+}
+
+/**
+ * efi_serialize_load_option() - serialize load option
+ *
+ * Serialize efi_load_option structure into byte stream for BootXXXX.
+ *
+ * @data: buffer for serialized data
+ * @lo: load option
+ * Return: size of allocated buffer
+ */
+size_t efi_serialize_load_option(struct efi_load_option_unpacked *lo, u8 **data)
+{
+ size_t label_len;
+ size_t size;
+ u8 *p;
+
+ label_len = u16_strsize(lo->label);
+
+ /* total size */
+ size = sizeof(lo->attributes);
+ size += sizeof(lo->file_path_length);
+ size += label_len;
+ size += lo->file_path_length;
+ if (lo->optional_data)
+ size += (utf8_utf16_strlen((const char *)lo->optional_data)
+ + 1) * sizeof(u16);
+ p = malloc(size);
+ if (!p)
+ return 0;
+
+ /* copy data */
+ *data = p;
+ memcpy(p, &lo->attributes, sizeof(lo->attributes));
+ p += sizeof(lo->attributes);
+
+ memcpy(p, &lo->file_path_length, sizeof(lo->file_path_length));
+ p += sizeof(lo->file_path_length);
+
+ memcpy(p, lo->label, label_len);
+ p += label_len;
+
+ memcpy(p, lo->file_path, lo->file_path_length);
+ p += lo->file_path_length;
+
+ if (lo->optional_data) {
+ utf8_utf16_strcpy((u16 **)&p, (const char *)lo->optional_data);
+ p += sizeof(u16); /* size of trailing \0 */
+ }
+ return size;
+}
diff --git a/include/efi/loader/option.h b/include/efi/loader/option.h
new file mode 100644
index 000000000000..9efc62c20e0b
--- /dev/null
+++ b/include/efi/loader/option.h
@@ -0,0 +1,36 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+#ifndef __EFI_LOADER_OPTION_H__
+#define __EFI_LOADER_OPTION_H__
+
+#include <efi/types.h>
+
+/*
+ * See section 3.1.3 in the v2.7 UEFI spec for more details on
+ * the layout of EFI_LOAD_OPTION. In short it is:
+ *
+ * typedef struct _EFI_LOAD_OPTION {
+ * UINT32 Attributes;
+ * UINT16 FilePathListLength;
+ * // CHAR16 Description[]; <-- variable length, NULL terminated
+ * // EFI_DEVICE_PATH_PROTOCOL FilePathList[];
+ * <-- FilePathListLength bytes
+ * // UINT8 OptionalData[];
+ * } EFI_LOAD_OPTION;
+ */
+struct efi_load_option_unpacked {
+ u32 attributes;
+ u16 file_path_length;
+ u16 *label;
+ struct efi_device_path *file_path;
+ const u8 *optional_data;
+};
+
+efi_status_t efi_deserialize_load_option(struct efi_load_option_unpacked *lo,
+ u8 *data, efi_uintn_t *size);
+size_t efi_serialize_load_option(struct efi_load_option_unpacked *lo,
+ u8 **data);
+efi_status_t efi_set_load_options(efi_handle_t handle,
+ efi_uintn_t load_options_size,
+ void *load_options);
+
+#endif
--
2.47.3
^ permalink raw reply [flat|nested] 55+ messages in thread* [PATCH v1 28/54] efi: loader: protocol: add graphical output protocol support
2025-12-18 10:37 [PATCH v1 00/54] efi: implement EFI loader support in barebox Ahmad Fatoum
` (26 preceding siblings ...)
2025-12-18 10:37 ` [PATCH v1 27/54] efi: loader: add EFI load option handling Ahmad Fatoum
@ 2025-12-18 10:37 ` Ahmad Fatoum
2025-12-18 10:37 ` [PATCH v1 29/54] efi: loader: protocol: add console support Ahmad Fatoum
` (25 subsequent siblings)
53 siblings, 0 replies; 55+ messages in thread
From: Ahmad Fatoum @ 2025-12-18 10:37 UTC (permalink / raw)
To: barebox; +Cc: Ahmad Fatoum
Add support for the graphical output protocol, so EFI applications can
draw to a barebox-provided framebuffer.
Signed-off-by: Ahmad Fatoum <a.fatoum@pengutronix.de>
---
efi/loader/protocols/Makefile | 1 +
efi/loader/protocols/gop.c | 572 ++++++++++++++++++++++++++++++++++
include/efi/protocol/gop.h | 9 +
3 files changed, 582 insertions(+)
create mode 100644 efi/loader/protocols/gop.c
diff --git a/efi/loader/protocols/Makefile b/efi/loader/protocols/Makefile
index 91bea23677e9..03095352d225 100644
--- a/efi/loader/protocols/Makefile
+++ b/efi/loader/protocols/Makefile
@@ -2,3 +2,4 @@
obj-$(CONFIG_FS) += file.o
obj-$(CONFIG_DISK) += disk.o
+obj-$(CONFIG_VIDEO) += gop.o
diff --git a/efi/loader/protocols/gop.c b/efi/loader/protocols/gop.c
new file mode 100644
index 000000000000..2d87acc9bcc7
--- /dev/null
+++ b/efi/loader/protocols/gop.c
@@ -0,0 +1,572 @@
+// SPDX-License-Identifier: GPL-2.0+
+// SPDX-Comment: Origin-URL: https://github.com/u-boot/u-boot/blob/459dbcb24837bb57750e854ad3ec6e49e11171ec/lib/efi_loader/efi_gop.c
+/*
+ * EFI application disk support
+ *
+ * Copyright (c) 2016 Alexander Graf
+ */
+
+#define pr_fmt(fmt) "efi-loader: gop: " fmt
+
+#include <efi/guid.h>
+#include <efi/loader.h>
+#include <efi/error.h>
+#include <efi/loader/object.h>
+#include <efi/loader/trace.h>
+#include <efi/protocol/gop.h>
+#include <malloc.h>
+#include <fb.h>
+#include <gui/graphic_utils.h>
+
+/**
+ * struct efi_gop_obj - graphical output protocol object
+ *
+ * @header: EFI object header
+ * @ops: graphical output protocol interface
+ * @info: graphical output mode information
+ * @mode: graphical output mode
+ * @fbi: backing video device's framebuffer info
+ * @fb: frame buffer
+ */
+struct efi_gop_obj {
+ struct efi_object header;
+ struct efi_graphics_output_protocol ops;
+ struct efi_graphics_output_mode_info info;
+ struct efi_graphics_output_protocol_mode mode;
+ struct fb_info *fbi;
+ /* Fields we only have access to during init */
+ void *fb;
+};
+
+static efi_status_t EFIAPI gop_query_mode(struct efi_graphics_output_protocol *this,
+ u32 mode_number,
+ efi_uintn_t *size_of_info,
+ struct efi_graphics_output_mode_info **info)
+{
+ struct efi_gop_obj *gopobj;
+ efi_status_t ret = EFI_SUCCESS;
+
+ EFI_ENTRY("%p, %x, %p, %p", this, mode_number, size_of_info, info);
+
+ if (!this || !size_of_info || !info || mode_number) {
+ ret = EFI_INVALID_PARAMETER;
+ goto out;
+ }
+
+ gopobj = container_of(this, struct efi_gop_obj, ops);
+ ret = efi_allocate_pool(EFI_BOOT_SERVICES_DATA, sizeof(gopobj->info),
+ (void **)info, "gop");
+ if (ret != EFI_SUCCESS)
+ goto out;
+ *size_of_info = sizeof(gopobj->info);
+ memcpy(*info, &gopobj->info, sizeof(gopobj->info));
+
+out:
+ return EFI_EXIT(ret);
+}
+
+static __always_inline struct efi_pixel_bitmask efi_vid30_to_blt_col(u32 vid)
+{
+ struct efi_pixel_bitmask blt = {
+ .reserved_mask = 0,
+ };
+
+ blt.blue_mask = (vid & 0x3ff) >> 2;
+ vid >>= 10;
+ blt.green_mask = (vid & 0x3ff) >> 2;
+ vid >>= 10;
+ blt.red_mask = (vid & 0x3ff) >> 2;
+ return blt;
+}
+
+static __always_inline u32 efi_blt_col_to_vid30(struct efi_pixel_bitmask *blt)
+{
+ return (u32)(blt->red_mask << 2) << 20 |
+ (u32)(blt->green_mask << 2) << 10 |
+ (u32)(blt->blue_mask << 2);
+}
+
+static __always_inline struct efi_pixel_bitmask efi_vid16_to_blt_col(u16 vid)
+{
+ struct efi_pixel_bitmask blt = {
+ .reserved_mask = 0,
+ };
+
+ blt.blue_mask = (vid & 0x1f) << 3;
+ vid >>= 5;
+ blt.green_mask = (vid & 0x3f) << 2;
+ vid >>= 6;
+ blt.red_mask = (vid & 0x1f) << 3;
+ return blt;
+}
+
+static __always_inline u16 efi_blt_col_to_vid16(struct efi_pixel_bitmask *blt)
+{
+ return (u16)(blt->red_mask >> 3) << 11 |
+ (u16)(blt->green_mask >> 2) << 5 |
+ (u16)(blt->blue_mask >> 3);
+}
+
+static __always_inline efi_status_t gop_blt_int(struct efi_graphics_output_protocol *this,
+ struct efi_pixel_bitmask *bufferp,
+ u32 operation, efi_uintn_t sx,
+ efi_uintn_t sy, efi_uintn_t dx,
+ efi_uintn_t dy,
+ efi_uintn_t width,
+ efi_uintn_t height,
+ efi_uintn_t delta,
+ efi_uintn_t vid_bpp)
+{
+ struct efi_gop_obj *gopobj = container_of(this, struct efi_gop_obj, ops);
+ efi_uintn_t i, j, linelen, slineoff = 0, dlineoff, swidth, dwidth;
+ u32 *fb32 = gopobj->fb;
+ u16 *fb16 = gopobj->fb;
+ struct efi_pixel_bitmask *buffer = __builtin_assume_aligned(bufferp, 4);
+ bool blt_to_video = (operation != EFI_BLT_VIDEO_TO_BLT_BUFFER);
+
+ if (delta) {
+ /* Check for 4 byte alignment */
+ if (delta & 3)
+ return EFI_INVALID_PARAMETER;
+ linelen = delta >> 2;
+ } else {
+ linelen = width;
+ }
+
+ /* Check source rectangle */
+ switch (operation) {
+ case EFI_BLT_VIDEO_FILL:
+ break;
+ case EFI_BLT_BUFFER_TO_VIDEO:
+ if (sx + width > linelen)
+ return EFI_INVALID_PARAMETER;
+ break;
+ case EFI_BLT_VIDEO_TO_BLT_BUFFER:
+ case EFI_BLT_VIDEO_TO_VIDEO:
+ if (sx + width > gopobj->info.horizontal_resolution ||
+ sy + height > gopobj->info.vertical_resolution)
+ return EFI_INVALID_PARAMETER;
+ break;
+ default:
+ return EFI_INVALID_PARAMETER;
+ }
+
+ /* Check destination rectangle */
+ switch (operation) {
+ case EFI_BLT_VIDEO_FILL:
+ case EFI_BLT_BUFFER_TO_VIDEO:
+ case EFI_BLT_VIDEO_TO_VIDEO:
+ if (dx + width > gopobj->info.horizontal_resolution ||
+ dy + height > gopobj->info.vertical_resolution)
+ return EFI_INVALID_PARAMETER;
+ break;
+ case EFI_BLT_VIDEO_TO_BLT_BUFFER:
+ if (dx + width > linelen)
+ return EFI_INVALID_PARAMETER;
+ break;
+ }
+
+ /* Calculate line width */
+ switch (operation) {
+ case EFI_BLT_BUFFER_TO_VIDEO:
+ swidth = linelen;
+ break;
+ case EFI_BLT_VIDEO_TO_BLT_BUFFER:
+ case EFI_BLT_VIDEO_TO_VIDEO:
+ swidth = gopobj->info.horizontal_resolution;
+ if (!vid_bpp)
+ return EFI_UNSUPPORTED;
+ break;
+ case EFI_BLT_VIDEO_FILL:
+ swidth = 0;
+ break;
+ }
+
+ switch (operation) {
+ case EFI_BLT_BUFFER_TO_VIDEO:
+ case EFI_BLT_VIDEO_FILL:
+ case EFI_BLT_VIDEO_TO_VIDEO:
+ dwidth = gopobj->info.horizontal_resolution;
+ if (!vid_bpp)
+ return EFI_UNSUPPORTED;
+ break;
+ case EFI_BLT_VIDEO_TO_BLT_BUFFER:
+ dwidth = linelen;
+ break;
+ }
+
+ slineoff = swidth * sy;
+ dlineoff = dwidth * dy;
+ for (i = 0; i < height; i++) {
+ for (j = 0; j < width; j++) {
+ struct efi_pixel_bitmask pix;
+
+ /* Read source pixel */
+ switch (operation) {
+ case EFI_BLT_VIDEO_FILL:
+ pix = *buffer;
+ break;
+ case EFI_BLT_BUFFER_TO_VIDEO:
+ pix = buffer[slineoff + j + sx];
+ break;
+ case EFI_BLT_VIDEO_TO_BLT_BUFFER:
+ case EFI_BLT_VIDEO_TO_VIDEO:
+ if (vid_bpp == 32)
+ pix = *(struct efi_pixel_bitmask *)&fb32[
+ slineoff + j + sx];
+ else if (vid_bpp == 30)
+ pix = efi_vid30_to_blt_col(fb32[
+ slineoff + j + sx]);
+ else
+ pix = efi_vid16_to_blt_col(fb16[
+ slineoff + j + sx]);
+ break;
+ }
+
+ /* Write destination pixel */
+ switch (operation) {
+ case EFI_BLT_VIDEO_TO_BLT_BUFFER:
+ buffer[dlineoff + j + dx] = pix;
+ break;
+ case EFI_BLT_BUFFER_TO_VIDEO:
+ case EFI_BLT_VIDEO_FILL:
+ case EFI_BLT_VIDEO_TO_VIDEO:
+ if (vid_bpp == 32)
+ fb32[dlineoff + j + dx] = *(u32 *)&pix;
+ else if (vid_bpp == 30)
+ fb32[dlineoff + j + dx] =
+ efi_blt_col_to_vid30(&pix);
+ else
+ fb16[dlineoff + j + dx] =
+ efi_blt_col_to_vid16(&pix);
+ break;
+ }
+ }
+ slineoff += swidth;
+ dlineoff += dwidth;
+ }
+
+ if (blt_to_video) {
+ struct fb_rect rect = { dx, dy, width, height };
+ fb_damage(gopobj->fbi, &rect);
+ }
+
+ return EFI_SUCCESS;
+}
+
+static efi_uintn_t gop_get_bpp(struct efi_graphics_output_protocol *this)
+{
+ struct efi_gop_obj *gopobj = container_of(this, struct efi_gop_obj, ops);
+ efi_uintn_t vid_bpp = 0;
+
+ switch (gopobj->fbi->bits_per_pixel) {
+ case 32:
+ if (gopobj->info.pixel_format == EFI_GOT_BGRA8)
+ vid_bpp = 32;
+ else
+ vid_bpp = 30;
+ break;
+ case 16:
+ vid_bpp = 16;
+ break;
+ }
+
+ return vid_bpp;
+}
+
+/*
+ * GCC can't optimize our BLT function well, but we need to make sure that
+ * our 2-dimensional loop gets executed very quickly, otherwise the system
+ * will feel slow.
+ *
+ * By manually putting all obvious branch targets into functions which call
+ * our generic BLT function with constants, the compiler can successfully
+ * optimize for speed.
+ */
+static efi_status_t gop_blt_video_fill(struct efi_graphics_output_protocol *this,
+ struct efi_pixel_bitmask *buffer,
+ u32 foo, efi_uintn_t sx,
+ efi_uintn_t sy, efi_uintn_t dx,
+ efi_uintn_t dy, efi_uintn_t width,
+ efi_uintn_t height, efi_uintn_t delta,
+ efi_uintn_t vid_bpp)
+{
+ return gop_blt_int(this, buffer, EFI_BLT_VIDEO_FILL, sx, sy, dx,
+ dy, width, height, delta, vid_bpp);
+}
+
+static efi_status_t gop_blt_buf_to_vid16(struct efi_graphics_output_protocol *this,
+ struct efi_pixel_bitmask *buffer,
+ u32 foo, efi_uintn_t sx,
+ efi_uintn_t sy, efi_uintn_t dx,
+ efi_uintn_t dy, efi_uintn_t width,
+ efi_uintn_t height, efi_uintn_t delta)
+{
+ return gop_blt_int(this, buffer, EFI_BLT_BUFFER_TO_VIDEO, sx, sy, dx,
+ dy, width, height, delta, 16);
+}
+
+static efi_status_t gop_blt_buf_to_vid30(struct efi_graphics_output_protocol *this,
+ struct efi_pixel_bitmask *buffer,
+ u32 foo, efi_uintn_t sx,
+ efi_uintn_t sy, efi_uintn_t dx,
+ efi_uintn_t dy, efi_uintn_t width,
+ efi_uintn_t height, efi_uintn_t delta)
+{
+ return gop_blt_int(this, buffer, EFI_BLT_BUFFER_TO_VIDEO, sx, sy, dx,
+ dy, width, height, delta, 30);
+}
+
+static efi_status_t gop_blt_buf_to_vid32(struct efi_graphics_output_protocol *this,
+ struct efi_pixel_bitmask *buffer,
+ u32 foo, efi_uintn_t sx,
+ efi_uintn_t sy, efi_uintn_t dx,
+ efi_uintn_t dy, efi_uintn_t width,
+ efi_uintn_t height, efi_uintn_t delta)
+{
+ return gop_blt_int(this, buffer, EFI_BLT_BUFFER_TO_VIDEO, sx, sy, dx,
+ dy, width, height, delta, 32);
+}
+
+static efi_status_t gop_blt_vid_to_vid(struct efi_graphics_output_protocol *this,
+ struct efi_pixel_bitmask *buffer,
+ u32 foo, efi_uintn_t sx,
+ efi_uintn_t sy, efi_uintn_t dx,
+ efi_uintn_t dy, efi_uintn_t width,
+ efi_uintn_t height, efi_uintn_t delta,
+ efi_uintn_t vid_bpp)
+{
+ return gop_blt_int(this, buffer, EFI_BLT_VIDEO_TO_VIDEO, sx, sy, dx,
+ dy, width, height, delta, vid_bpp);
+}
+
+static efi_status_t gop_blt_vid_to_buf(struct efi_graphics_output_protocol *this,
+ struct efi_pixel_bitmask *buffer,
+ u32 foo, efi_uintn_t sx,
+ efi_uintn_t sy, efi_uintn_t dx,
+ efi_uintn_t dy, efi_uintn_t width,
+ efi_uintn_t height, efi_uintn_t delta,
+ efi_uintn_t vid_bpp)
+{
+ return gop_blt_int(this, buffer, EFI_BLT_VIDEO_TO_BLT_BUFFER, sx, sy,
+ dx, dy, width, height, delta, vid_bpp);
+}
+
+/**
+ * gop_set_mode() - set graphical output mode
+ *
+ * This function implements the SetMode() service.
+ *
+ * See the Unified Extensible Firmware Interface (UEFI) specification for
+ * details.
+ *
+ * @this: the graphical output protocol
+ * @mode_number: the mode to be set
+ * Return: status code
+ */
+static efi_status_t EFIAPI gop_set_mode(struct efi_graphics_output_protocol *this,
+ u32 mode_number)
+{
+ struct efi_gop_obj *gopobj;
+ struct efi_pixel_bitmask buffer = {0, 0, 0, 0};
+ efi_uintn_t vid_bpp;
+ efi_status_t ret = EFI_SUCCESS;
+
+ EFI_ENTRY("%p, %x", this, mode_number);
+
+ if (!this) {
+ ret = EFI_INVALID_PARAMETER;
+ goto out;
+ }
+ if (mode_number) {
+ ret = EFI_UNSUPPORTED;
+ goto out;
+ }
+ gopobj = container_of(this, struct efi_gop_obj, ops);
+ vid_bpp = gop_get_bpp(this);
+ ret = gop_blt_video_fill(this, &buffer, EFI_BLT_VIDEO_FILL, 0, 0, 0, 0,
+ gopobj->info.horizontal_resolution,
+ gopobj->info.vertical_resolution, 0,
+ vid_bpp);
+out:
+ return EFI_EXIT(ret);
+}
+
+/*
+ * Copy rectangle.
+ *
+ * This function implements the Blt service of the EFI_GRAPHICS_OUTPUT_PROTOCOL.
+ * See the Unified Extensible Firmware Interface (UEFI) specification for
+ * details.
+ *
+ * @this: EFI_GRAPHICS_OUTPUT_PROTOCOL
+ * @buffer: pixel buffer
+ * @sx: source x-coordinate
+ * @sy: source y-coordinate
+ * @dx: destination x-coordinate
+ * @dy: destination y-coordinate
+ * @width: width of rectangle
+ * @height: height of rectangle
+ * @delta: length in bytes of a line in the pixel buffer (optional)
+ * Return: status code
+ */
+static efi_status_t EFIAPI gop_blt(struct efi_graphics_output_protocol *this,
+ struct efi_pixel_bitmask *buffer,
+ u32 operation, efi_uintn_t sx,
+ efi_uintn_t sy, efi_uintn_t dx,
+ efi_uintn_t dy, efi_uintn_t width,
+ efi_uintn_t height, efi_uintn_t delta)
+{
+ efi_status_t ret = EFI_INVALID_PARAMETER;
+ struct efi_gop_obj *gopobj = container_of(this, struct efi_gop_obj, ops);
+ efi_uintn_t vid_bpp;
+
+ EFI_ENTRY("%p, %p, %u, %zu, %zu, %zu, %zu, %zu, %zu, %zu", this,
+ buffer, operation, sx, sy, dx, dy, width, height, delta);
+
+ vid_bpp = gop_get_bpp(this);
+
+ /* Allow for compiler optimization */
+ switch (operation) {
+ case EFI_BLT_VIDEO_FILL:
+ ret = gop_blt_video_fill(this, buffer, operation, sx, sy, dx,
+ dy, width, height, delta, vid_bpp);
+ break;
+ case EFI_BLT_BUFFER_TO_VIDEO:
+ /* This needs to be super-fast, so duplicate for 16/32bpp */
+ if (vid_bpp == 32)
+ ret = gop_blt_buf_to_vid32(this, buffer, operation, sx,
+ sy, dx, dy, width, height,
+ delta);
+ else if (vid_bpp == 30)
+ ret = gop_blt_buf_to_vid30(this, buffer, operation, sx,
+ sy, dx, dy, width, height,
+ delta);
+ else
+ ret = gop_blt_buf_to_vid16(this, buffer, operation, sx,
+ sy, dx, dy, width, height,
+ delta);
+ break;
+ case EFI_BLT_VIDEO_TO_VIDEO:
+ ret = gop_blt_vid_to_vid(this, buffer, operation, sx, sy, dx,
+ dy, width, height, delta, vid_bpp);
+ break;
+ case EFI_BLT_VIDEO_TO_BLT_BUFFER:
+ ret = gop_blt_vid_to_buf(this, buffer, operation, sx, sy, dx,
+ dy, width, height, delta, vid_bpp);
+ break;
+ default:
+ ret = EFI_INVALID_PARAMETER;
+ }
+
+ if (ret != EFI_SUCCESS)
+ return EFI_EXIT(ret);
+
+ fb_flush(gopobj->fbi);
+
+ return EFI_EXIT(EFI_SUCCESS);
+}
+
+static bool format_is_x2r10g10b10(struct fb_info *fbi)
+{
+ return fbi->red.length == 10 && fbi->green.length == 10 &&
+ fbi->blue.length == 10;
+}
+
+static char *fbdev = "/dev/fb0";
+
+/*
+ * Install graphical output protocol if possible.
+ */
+static efi_status_t efi_gop_register(void *data)
+{
+ struct efi_gop_obj *gopobj;
+ u32 col, row;
+ efi_status_t ret;
+ struct fb_info *fbi;
+ struct screen *sc;
+ struct efi_pixel_bitmask *pixel_information;
+
+ sc = fb_open(fbdev);
+ if (IS_ERR(sc)) {
+ pr_debug("fb_open: %pe\n", sc);
+ return EFI_UNSUPPORTED;
+ }
+
+ fbi = sc->info;
+ col = fbi->xres;
+ row = fbi->yres;
+
+ switch (fbi->bits_per_pixel) {
+ case 16:
+ case 32:
+ break;
+ default:
+ /* So far, we only work in 16 or 32 bit mode */
+ pr_err("Unsupported video mode\n");
+ return EFI_UNSUPPORTED;
+ }
+
+ gopobj = calloc(1, sizeof(*gopobj));
+ if (!gopobj) {
+ pr_err("Out of memory\n");
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ /* Hook up to the device list */
+ efi_add_handle(&gopobj->header);
+
+ /* Fill in object data */
+ ret = efi_add_protocol(&gopobj->header, &efi_gop_guid,
+ &gopobj->ops);
+ if (ret != EFI_SUCCESS) {
+ pr_err("Failure adding GOP protocol\n");
+ return ret;
+ }
+ gopobj->ops.query_mode = gop_query_mode;
+ gopobj->ops.set_mode = gop_set_mode;
+ gopobj->ops.blt = gop_blt;
+ gopobj->ops.mode = &gopobj->mode;
+
+ gopobj->mode.max_mode = 1;
+ gopobj->mode.info = &gopobj->info;
+ gopobj->mode.size_of_info = sizeof(gopobj->info);
+
+ gopobj->mode.frame_buffer_base = fbi->screen_base;
+ gopobj->mode.frame_buffer_size = fbi->line_length * fbi->yres;
+
+ gopobj->info.version = 0;
+ gopobj->info.horizontal_resolution = col;
+ gopobj->info.vertical_resolution = row;
+
+ pixel_information = &gopobj->info.pixel_information;
+
+ if (fbi->bits_per_pixel == 32) {
+ if (format_is_x2r10g10b10(fbi)) {
+ gopobj->info.pixel_format = EFI_GOT_BITMASK;
+ pixel_information->red_mask = 0x3ff00000;
+ pixel_information->green_mask = 0x000ffc00;
+ pixel_information->blue_mask = 0x000003ff;
+ pixel_information->reserved_mask = 0xc0000000;
+ } else {
+ gopobj->info.pixel_format = EFI_GOT_BGRA8;
+ }
+ } else {
+ gopobj->info.pixel_format = EFI_GOT_BITMASK;
+ pixel_information->red_mask = 0xf800;
+ pixel_information->green_mask = 0x07e0;
+ pixel_information->blue_mask = 0x001f;
+ }
+ gopobj->info.pixels_per_scan_line = col;
+ gopobj->fb = fb_get_screen_base(fbi);
+ gopobj->fbi = fbi;
+
+ return EFI_SUCCESS;
+}
+
+static int efi_gop_init(void)
+{
+ efi_register_deferred_init(efi_gop_register, NULL);
+ return 0;
+}
+device_initcall(efi_gop_init);
diff --git a/include/efi/protocol/gop.h b/include/efi/protocol/gop.h
index 04d139ffcb3f..12a5ea6d0fe0 100644
--- a/include/efi/protocol/gop.h
+++ b/include/efi/protocol/gop.h
@@ -10,6 +10,10 @@
#define PIXEL_BLT_ONLY 3
#define PIXEL_FORMAT_MAX 4
+#define EFI_GOT_RGBA8 0
+#define EFI_GOT_BGRA8 1
+#define EFI_GOT_BITMASK 2
+
struct efi_pixel_bitmask {
u32 red_mask;
u32 green_mask;
@@ -17,6 +21,11 @@ struct efi_pixel_bitmask {
u32 reserved_mask;
};
+#define EFI_BLT_VIDEO_FILL 0
+#define EFI_BLT_VIDEO_TO_BLT_BUFFER 1
+#define EFI_BLT_BUFFER_TO_VIDEO 2
+#define EFI_BLT_VIDEO_TO_VIDEO 3
+
struct efi_graphics_output_mode_info {
u32 version;
u32 horizontal_resolution;
--
2.47.3
^ permalink raw reply [flat|nested] 55+ messages in thread* [PATCH v1 29/54] efi: loader: protocol: add console support
2025-12-18 10:37 [PATCH v1 00/54] efi: implement EFI loader support in barebox Ahmad Fatoum
` (27 preceding siblings ...)
2025-12-18 10:37 ` [PATCH v1 28/54] efi: loader: protocol: add graphical output protocol support Ahmad Fatoum
@ 2025-12-18 10:37 ` Ahmad Fatoum
2025-12-18 10:37 ` [PATCH v1 30/54] efi: loader: protocol: add HII support Ahmad Fatoum
` (24 subsequent siblings)
53 siblings, 0 replies; 55+ messages in thread
From: Ahmad Fatoum @ 2025-12-18 10:37 UTC (permalink / raw)
To: barebox; +Cc: Ahmad Fatoum
barebox will need to provide EFI protocols for text input and output for
use by the EFI applications. Import the necessary support from U-Boot.
Signed-off-by: Ahmad Fatoum <a.fatoum@pengutronix.de>
---
efi/loader/boot.c | 10 +
efi/loader/protocols/Makefile | 1 +
efi/loader/protocols/console.c | 1327 ++++++++++++++++++++++++++++++++
include/efi/loader.h | 3 +
include/efi/protocol/text.h | 32 +-
5 files changed, 1367 insertions(+), 6 deletions(-)
create mode 100644 efi/loader/protocols/console.c
diff --git a/efi/loader/boot.c b/efi/loader/boot.c
index 2d208c4cafbd..29cab7acd1c0 100644
--- a/efi/loader/boot.c
+++ b/efi/loader/boot.c
@@ -3814,6 +3814,16 @@ efi_status_t efi_alloc_system_table(void)
*/
efi_status_t efi_initialize_system_table(void)
{
+ /*
+ * These entries will be set to NULL in ExitBootServices(). To avoid
+ * relocation in SetVirtualAddressMap(), set them dynamically.
+ */
+ systab.con_in_handle = efi_root;
+ systab.con_in = &efi_con_in;
+ systab.con_out_handle = efi_root;
+ systab.con_out = &efi_con_out;
+ systab.stderr_handle = efi_root;
+ systab.std_err = &efi_con_out;
systab.boottime = &efi_boot_services;
/*
diff --git a/efi/loader/protocols/Makefile b/efi/loader/protocols/Makefile
index 03095352d225..472dd54116c5 100644
--- a/efi/loader/protocols/Makefile
+++ b/efi/loader/protocols/Makefile
@@ -3,3 +3,4 @@
obj-$(CONFIG_FS) += file.o
obj-$(CONFIG_DISK) += disk.o
obj-$(CONFIG_VIDEO) += gop.o
+obj-$(CONFIG_CONSOLE_FULL) += console.o
diff --git a/efi/loader/protocols/console.c b/efi/loader/protocols/console.c
new file mode 100644
index 000000000000..d2c13196e4c2
--- /dev/null
+++ b/efi/loader/protocols/console.c
@@ -0,0 +1,1327 @@
+// SPDX-License-Identifier: GPL-2.0+
+// SPDX-Comment: Origin-URL: https://github.com/u-boot/u-boot/blob/419cc25aa15784510a276f78441efbaf470b8577/lib/efi_loader/efi_console.c
+/*
+ * EFI application console interface
+ *
+ * Copyright (c) 2016 Alexander Graf
+ */
+
+#define pr_fmt(fmt) "efi-loader: console: " fmt
+
+#include <malloc.h>
+#include <charset.h>
+#include <clock.h>
+#include <stdio.h>
+#include <efi/loader.h>
+#include <efi/devicepath.h>
+#include <efi/loader/object.h>
+#include <efi/loader/event.h>
+#include <efi/loader/devicepath.h>
+#include <efi/protocol/text.h>
+#include <efi/error.h>
+#include <efi/guid.h>
+
+#ifdef DEBUG
+#include <efi/loader/trace.h>
+#else
+#define EFI_ENTRY(...) (void)0
+#define EFI_EXIT(ret) ({ ret; })
+#define EFI_PRINT no_printf
+#endif
+
+#define EFI_COUT_MODE_2 2
+#define EFI_MAX_COUT_MODE 3
+
+struct cout_mode {
+ unsigned long columns;
+ unsigned long rows;
+ int present;
+};
+
+static struct efi_object uart_obj;
+static struct cout_mode efi_cout_modes[] = {
+ /* EFI Mode 0 is 80x25 and always present */
+ {
+ .columns = 80,
+ .rows = 25,
+ .present = 1,
+ },
+ /* EFI Mode 1 is always 80x50 */
+ {
+ .columns = 80,
+ .rows = 50,
+ .present = 0,
+ },
+ /* Value are unknown until we query the console */
+ {
+ .columns = 0,
+ .rows = 0,
+ .present = 0,
+ },
+};
+
+const efi_guid_t efi_text_input_protocol_guid =
+ EFI_SIMPLE_TEXT_IN_PROTOCOL_GUID;
+const efi_guid_t efi_text_output_protocol_guid =
+ EFI_SIMPLE_TEXT_OUT_PROTOCOL_GUID;
+
+#define cESC '\x1b'
+#define ESC "\x1b"
+
+/*
+ * efi_con_mode - mode information of the Simple Text Output Protocol
+ *
+ * Use safe settings before efi_setup_console_size() is called.
+ * By default enable only the 80x25 mode which must always exist.
+ */
+static struct simple_text_output_mode efi_con_mode = {
+ .max_mode = 1,
+ .mode = 0,
+ .attribute = 0,
+ .cursor_column = 0,
+ .cursor_row = 0,
+ .cursor_visible = 1,
+};
+
+/**
+ * term_get_char() - read a character from the console
+ *
+ * Wait for up to 100 ms to read a character from the console.
+ *
+ * @c: pointer to the buffer to receive the character
+ * Return: 0 on success, 1 otherwise
+ */
+static int term_get_char(s32 *c)
+{
+ u64 timeout;
+
+ /* Wait up to 100 ms for a character */
+ timeout = get_time_ns();
+
+ while (!tstc())
+ if (is_timeout(timeout, 100 * MSECOND))
+ return 1;
+
+ *c = getchar();
+ return 0;
+}
+
+/**
+ * term_read_reply() - receive and parse a reply from the terminal
+ *
+ * @n: array of return values
+ * @num: number of return values expected
+ * @end_char: character indicating end of terminal message
+ * Return: non-zero indicates error
+ */
+static int term_read_reply(int *n, int num, char end_char)
+{
+ s32 c;
+ int i = 0;
+
+ if (term_get_char(&c) || c != cESC)
+ return -1;
+
+ if (term_get_char(&c) || c != '[')
+ return -1;
+
+ n[0] = 0;
+ while (1) {
+ if (!term_get_char(&c)) {
+ if (c == ';') {
+ i++;
+ if (i >= num)
+ return -1;
+ n[i] = 0;
+ continue;
+ } else if (c == end_char) {
+ break;
+ } else if (c > '9' || c < '0') {
+ return -1;
+ }
+
+ /* Read one more decimal position */
+ n[i] *= 10;
+ n[i] += c - '0';
+ } else {
+ return -1;
+ }
+ }
+ if (i != num - 1)
+ return -1;
+
+ return 0;
+}
+
+/**
+ * efi_cout_output_string() - write Unicode string to console
+ *
+ * This function implements the OutputString service of the simple text output
+ * protocol. See the Unified Extensible Firmware Interface (UEFI) specification
+ * for details.
+ *
+ * @this: simple text output protocol
+ * @string: u16 string
+ * Return: status code
+ */
+static efi_status_t EFIAPI efi_cout_output_string(
+ struct efi_simple_text_output_protocol *this,
+ const wchar_t *string)
+{
+ struct simple_text_output_mode *con = &efi_con_mode;
+ struct cout_mode *mode = &efi_cout_modes[con->mode];
+ char *buf, *pos;
+ const u16 *p;
+ efi_status_t ret = EFI_SUCCESS;
+
+ EFI_ENTRY("%p, '%ls'", this, string);
+
+ if (!this || !string) {
+ ret = EFI_INVALID_PARAMETER;
+ goto out;
+ }
+
+ buf = malloc(utf16_utf8_strlen(string) + 1);
+ if (!buf) {
+ ret = EFI_OUT_OF_RESOURCES;
+ goto out;
+ }
+ pos = buf;
+ utf16_utf8_strcpy(&pos, string);
+ puts(buf);
+ free(buf);
+
+ /*
+ * Update the cursor position.
+ *
+ * The UEFI spec provides advance rules for U+0000, U+0008, U+000A,
+ * and U000D. All other control characters are ignored. Any non-control
+ * character increase the column by one.
+ */
+ for (p = string; *p; ++p) {
+ switch (*p) {
+ case '\b': /* U+0008, backspace */
+ if (con->cursor_column)
+ con->cursor_column--;
+ break;
+ case '\n': /* U+000A, newline */
+ con->cursor_column = 0;
+ con->cursor_row++;
+ break;
+ case '\r': /* U+000D, carriage-return */
+ con->cursor_column = 0;
+ break;
+ case 0xd800 ... 0xdbff:
+ /*
+ * Ignore high surrogates, we do not want to count a
+ * Unicode character twice.
+ */
+ break;
+ default:
+ /* Exclude control codes */
+ if (*p > 0x1f)
+ con->cursor_column++;
+ break;
+ }
+ if (con->cursor_column >= mode->columns) {
+ con->cursor_column = 0;
+ con->cursor_row++;
+ }
+ /*
+ * When we exceed the row count the terminal will scroll up one
+ * line. We have to adjust the cursor position.
+ */
+ if (con->cursor_row >= mode->rows && con->cursor_row)
+ con->cursor_row--;
+ }
+
+out:
+ return EFI_EXIT(ret);
+}
+
+/**
+ * efi_cout_test_string() - test writing Unicode string to console
+ *
+ * This function implements the TestString service of the simple text output
+ * protocol. See the Unified Extensible Firmware Interface (UEFI) specification
+ * for details.
+ *
+ * As in OutputString we simply convert UTF-16 to UTF-8 there are no unsupported
+ * code points and we can always return EFI_SUCCESS.
+ *
+ * @this: simple text output protocol
+ * @string: u16 string
+ * Return: status code
+ */
+static efi_status_t EFIAPI efi_cout_test_string(
+ struct efi_simple_text_output_protocol *this,
+ const u16 *string)
+{
+ EFI_ENTRY("%p, %p", this, string);
+ return EFI_EXIT(EFI_SUCCESS);
+}
+
+/**
+ * cout_mode_matches() - check if mode has given terminal size
+ *
+ * @mode: text mode
+ * @rows: number of rows
+ * @cols: number of columns
+ * Return: true if number of rows and columns matches the mode and
+ * the mode is present
+ */
+static bool cout_mode_matches(struct cout_mode *mode, int rows, int cols)
+{
+ if (!mode->present)
+ return false;
+
+ return (mode->rows == rows) && (mode->columns == cols);
+}
+
+/**
+ * query_console_serial() - query serial console size
+ *
+ * When using a serial console or the net console we can only devise the
+ * terminal size by querying the terminal using ECMA-48 control sequences.
+ *
+ * @rows: pointer to return number of rows
+ * @cols: pointer to return number of columns
+ * Returns: 0 on success
+ */
+static int query_console_serial(int *rows, int *cols)
+{
+ int ret = 0;
+ int n[2];
+
+ /* Empty input buffer */
+ while (tstc())
+ getchar();
+
+ /*
+ * Not all terminals understand CSI [18t for querying the console size.
+ * We should adhere to escape sequences documented in the console_codes
+ * man page and the ECMA-48 standard.
+ *
+ * So here we follow a different approach. We position the cursor to the
+ * bottom right and query its position. Before leaving the function we
+ * restore the original cursor position.
+ */
+ printf(ESC "7" /* Save cursor position */
+ ESC "[r" /* Set scrolling region to full window */
+ ESC "[999;999H" /* Move to bottom right corner */
+ ESC "[6n"); /* Query cursor position */
+
+ /* Read {rows,cols} */
+ if (term_read_reply(n, 2, 'R')) {
+ ret = 1;
+ goto out;
+ }
+
+ *cols = n[1];
+ *rows = n[0];
+out:
+ printf(ESC "8"); /* Restore cursor position */
+ return ret;
+}
+
+static void efi_setup_console_size(void)
+{
+ int rows = 25, cols = 80;
+ int ret = -ENODEV;
+
+ ret = query_console_serial(&rows, &cols);
+ if (ret)
+ return;
+
+ /* Test if we can have Mode 1 */
+ if (cols >= 80 && rows >= 50) {
+ efi_cout_modes[1].present = 1;
+ efi_con_mode.max_mode = 2;
+ }
+
+ /*
+ * Install our mode as mode 2 if it is different
+ * than mode 0 or 1 and set it as the currently selected mode
+ */
+ if (!cout_mode_matches(&efi_cout_modes[0], rows, cols) &&
+ !cout_mode_matches(&efi_cout_modes[1], rows, cols)) {
+ efi_cout_modes[EFI_COUT_MODE_2].columns = cols;
+ efi_cout_modes[EFI_COUT_MODE_2].rows = rows;
+ efi_cout_modes[EFI_COUT_MODE_2].present = 1;
+ efi_con_mode.max_mode = EFI_MAX_COUT_MODE;
+ efi_con_mode.mode = EFI_COUT_MODE_2;
+ }
+}
+
+/**
+ * efi_cout_query_mode() - get terminal size for a text mode
+ *
+ * This function implements the QueryMode service of the simple text output
+ * protocol. See the Unified Extensible Firmware Interface (UEFI) specification
+ * for details.
+ *
+ * @this: simple text output protocol
+ * @mode_number: mode number to retrieve information on
+ * @columns: number of columns
+ * @rows: number of rows
+ * Return: status code
+ */
+static efi_status_t EFIAPI efi_cout_query_mode(
+ struct efi_simple_text_output_protocol *this,
+ unsigned long mode_number, unsigned long *columns,
+ unsigned long *rows)
+{
+ EFI_ENTRY("%p, %ld, %p, %p", this, mode_number, columns, rows);
+
+ if (mode_number >= efi_con_mode.max_mode)
+ return EFI_EXIT(EFI_UNSUPPORTED);
+
+ if (efi_cout_modes[mode_number].present != 1)
+ return EFI_EXIT(EFI_UNSUPPORTED);
+
+ if (columns)
+ *columns = efi_cout_modes[mode_number].columns;
+ if (rows)
+ *rows = efi_cout_modes[mode_number].rows;
+
+ return EFI_EXIT(EFI_SUCCESS);
+}
+
+static const struct {
+ u8 fg;
+ u8 bg;
+} color[] = {
+ { 30, 40 }, /* 0: black */
+ { 34, 44 }, /* 1: blue */
+ { 32, 42 }, /* 2: green */
+ { 36, 46 }, /* 3: cyan */
+ { 31, 41 }, /* 4: red */
+ { 35, 45 }, /* 5: magenta */
+ { 33, 43 }, /* 6: brown, map to yellow as EDK2 does*/
+ { 37, 47 }, /* 7: light gray, map to white */
+};
+
+/**
+ * efi_cout_set_attribute() - set fore- and background color
+ *
+ * This function implements the SetAttribute service of the simple text output
+ * protocol. See the Unified Extensible Firmware Interface (UEFI) specification
+ * for details.
+ *
+ * @this: simple text output protocol
+ * @attribute: foreground color - bits 0-3, background color - bits 4-6
+ * Return: status code
+ */
+static efi_status_t EFIAPI efi_cout_set_attribute(
+ struct efi_simple_text_output_protocol *this,
+ unsigned long attribute)
+{
+ unsigned int bold = EFI_ATTR_BOLD(attribute);
+ unsigned int fg = EFI_ATTR_FG(attribute);
+ unsigned int bg = EFI_ATTR_BG(attribute);
+
+ EFI_ENTRY("%p, %lx", this, attribute);
+
+ efi_con_mode.attribute = attribute;
+ if (attribute)
+ printf(ESC"[%u;%u;%um", bold, color[fg].fg, color[bg].bg);
+ else
+ printf(ESC"[0;37;40m");
+
+ return EFI_EXIT(EFI_SUCCESS);
+}
+
+/**
+ * efi_clear_screen() - clear screen
+ */
+static void efi_clear_screen(void)
+{
+ /*
+ * The Linux console wants both a clear and a home command. The video
+ * uclass does not support <ESC>[H without coordinates, yet.
+ */
+ printf(ESC "[2J" ESC "[1;1H");
+ efi_con_mode.cursor_column = 0;
+ efi_con_mode.cursor_row = 0;
+}
+
+/**
+ * efi_cout_clear_screen() - clear screen
+ *
+ * This function implements the ClearScreen service of the simple text output
+ * protocol. See the Unified Extensible Firmware Interface (UEFI) specification
+ * for details.
+ *
+ * @this: pointer to the protocol instance
+ * Return: status code
+ */
+static efi_status_t EFIAPI efi_cout_clear_screen(
+ struct efi_simple_text_output_protocol *this)
+{
+ EFI_ENTRY("%p", this);
+
+ /* Set default colors if not done yet */
+ if (efi_con_mode.attribute == 0) {
+ efi_con_mode.attribute = 0x07;
+ printf(ESC "[0;37;40m");
+ }
+
+ efi_clear_screen();
+
+ return EFI_EXIT(EFI_SUCCESS);
+}
+
+/**
+ * efi_cout_clear_set_mode() - set text model
+ *
+ * This function implements the SetMode service of the simple text output
+ * protocol. See the Unified Extensible Firmware Interface (UEFI) specification
+ * for details.
+ *
+ * @this: pointer to the protocol instance
+ * @mode_number: number of the text mode to set
+ * Return: status code
+ */
+static efi_status_t EFIAPI efi_cout_set_mode(
+ struct efi_simple_text_output_protocol *this,
+ unsigned long mode_number)
+{
+ EFI_ENTRY("%p, %ld", this, mode_number);
+
+ if (mode_number >= efi_con_mode.max_mode)
+ return EFI_EXIT(EFI_UNSUPPORTED);
+
+ if (!efi_cout_modes[mode_number].present)
+ return EFI_EXIT(EFI_UNSUPPORTED);
+
+ efi_con_mode.mode = mode_number;
+ efi_clear_screen();
+
+ return EFI_EXIT(EFI_SUCCESS);
+}
+
+/**
+ * efi_cout_reset() - reset the terminal
+ *
+ * This function implements the Reset service of the simple text output
+ * protocol. See the Unified Extensible Firmware Interface (UEFI) specification
+ * for details.
+ *
+ * @this: pointer to the protocol instance
+ * @extended_verification: if set an extended verification may be executed
+ * Return: status code
+ */
+static efi_status_t EFIAPI efi_cout_reset(
+ struct efi_simple_text_output_protocol *this,
+ char extended_verification)
+{
+ EFI_ENTRY("%p, %d", this, extended_verification);
+
+ /* Set default colors */
+ efi_con_mode.attribute = 0x07;
+ printf(ESC "[0;37;40m");
+ /* Clear screen */
+ efi_clear_screen();
+
+ return EFI_EXIT(EFI_SUCCESS);
+}
+
+/**
+ * efi_cout_set_cursor_position() - reset the terminal
+ *
+ * This function implements the SetCursorPosition service of the simple text
+ * output protocol. See the Unified Extensible Firmware Interface (UEFI)
+ * specification for details.
+ *
+ * @this: pointer to the protocol instance
+ * @column: column to move to
+ * @row: row to move to
+ * Return: status code
+ */
+static efi_status_t EFIAPI efi_cout_set_cursor_position(
+ struct efi_simple_text_output_protocol *this,
+ unsigned long column, unsigned long row)
+{
+ efi_status_t ret = EFI_SUCCESS;
+ struct simple_text_output_mode *con = &efi_con_mode;
+ struct cout_mode *mode = &efi_cout_modes[con->mode];
+
+ EFI_ENTRY("%p, %ld, %ld", this, column, row);
+
+ /* Check parameters */
+ if (!this) {
+ ret = EFI_INVALID_PARAMETER;
+ goto out;
+ }
+ if (row >= mode->rows || column >= mode->columns) {
+ ret = EFI_UNSUPPORTED;
+ goto out;
+ }
+
+ /*
+ * Set cursor position by sending CSI H.
+ * EFI origin is [0, 0], terminal origin is [1, 1].
+ */
+ printf(ESC "[%d;%dH", (int)row + 1, (int)column + 1);
+ efi_con_mode.cursor_column = column;
+ efi_con_mode.cursor_row = row;
+out:
+ return EFI_EXIT(ret);
+}
+
+/**
+ * efi_cout_enable_cursor() - enable the cursor
+ *
+ * This function implements the EnableCursor service of the simple text output
+ * protocol. See the Unified Extensible Firmware Interface (UEFI) specification
+ * for details.
+ *
+ * @this: pointer to the protocol instance
+ * @enable: if true enable, if false disable the cursor
+ * Return: status code
+ */
+static efi_status_t EFIAPI efi_cout_enable_cursor(
+ struct efi_simple_text_output_protocol *this,
+ bool enable)
+{
+ EFI_ENTRY("%p, %d", this, enable);
+
+ printf(ESC"[?25%c", enable ? 'h' : 'l');
+ efi_con_mode.cursor_visible = !!enable;
+
+ return EFI_EXIT(EFI_SUCCESS);
+}
+
+struct efi_simple_text_output_protocol efi_con_out = {
+ .reset = efi_cout_reset,
+ .output_string = efi_cout_output_string,
+ .test_string = efi_cout_test_string,
+ .query_mode = efi_cout_query_mode,
+ .set_mode = efi_cout_set_mode,
+ .set_attribute = efi_cout_set_attribute,
+ .clear_screen = efi_cout_clear_screen,
+ .set_cursor_position = efi_cout_set_cursor_position,
+ .enable_cursor = efi_cout_enable_cursor,
+ .mode = (void*)&efi_con_mode,
+};
+
+/**
+ * struct efi_cin_notify_function - registered console input notify function
+ *
+ * @link: link to list
+ * @key: key to notify
+ * @function: function to call
+ */
+struct efi_cin_notify_function {
+ struct list_head link;
+ struct efi_key_data key;
+ efi_status_t (EFIAPI *function)
+ (struct efi_key_data *key_data);
+};
+
+static bool key_available;
+static struct efi_key_data next_key;
+static LIST_HEAD(cin_notify_functions);
+
+/**
+ * set_shift_mask() - set shift mask
+ *
+ * @mod: Xterm shift mask
+ * @key_state: receives the state of the shift, alt, control, and logo keys
+ */
+static void set_shift_mask(int mod, struct efi_key_state *key_state)
+{
+ key_state->shift_state = EFI_SHIFT_STATE_VALID;
+ if (mod) {
+ --mod;
+ if (mod & 1)
+ key_state->shift_state |= EFI_LEFT_SHIFT_PRESSED;
+ if (mod & 2)
+ key_state->shift_state |= EFI_LEFT_ALT_PRESSED;
+ if (mod & 4)
+ key_state->shift_state |= EFI_LEFT_CONTROL_PRESSED;
+ if (!mod || (mod & 8))
+ key_state->shift_state |= EFI_LEFT_LOGO_PRESSED;
+ }
+}
+
+/**
+ * analyze_modifiers() - analyze modifiers (shift, alt, ctrl) for function keys
+ *
+ * This gets called when we have already parsed CSI.
+ *
+ * @key_state: receives the state of the shift, alt, control, and logo keys
+ * Return: the unmodified code
+ */
+static int analyze_modifiers(struct efi_key_state *key_state)
+{
+ int c, mod = 0, ret = 0;
+
+ c = getchar();
+
+ if (c != ';') {
+ ret = c;
+ if (c == '~')
+ goto out;
+ c = getchar();
+ }
+ for (;;) {
+ switch (c) {
+ case '0'...'9':
+ mod *= 10;
+ mod += c - '0';
+ /* fall through */
+ case ';':
+ c = getchar();
+ break;
+ default:
+ goto out;
+ }
+ }
+out:
+ set_shift_mask(mod, key_state);
+ if (!ret)
+ ret = c;
+ return ret;
+}
+
+/**
+ * efi_cin_read_key() - read a key from the console input
+ *
+ * @key: - key received
+ * Return: - status code
+ */
+static efi_status_t efi_cin_read_key(struct efi_key_data *key)
+{
+ struct efi_input_key pressed_key = {
+ .scan_code = 0,
+ .unicode_char = 0,
+ };
+ s32 ch;
+
+ if (console_read_unicode(&ch))
+ return EFI_NOT_READY;
+
+ key->state.shift_state = EFI_SHIFT_STATE_INVALID;
+ key->state.toggle_state = EFI_TOGGLE_STATE_INVALID;
+
+ /* We do not support multi-word codes */
+ if (ch >= 0x10000)
+ ch = '?';
+
+ switch (ch) {
+ case 0x1b:
+ /*
+ * If a second key is received within 10 ms, assume that we are
+ * dealing with an escape sequence. Otherwise consider this the
+ * escape key being hit. 10 ms is long enough to work fine at
+ * 1200 baud and above.
+ */
+ udelay(10000);
+ if (!tstc()) {
+ pressed_key.scan_code = 23;
+ break;
+ }
+ /*
+ * Xterm Control Sequences
+ * https://www.xfree86.org/4.8.0/ctlseqs.html
+ */
+ ch = getchar();
+ switch (ch) {
+ case cESC: /* ESC */
+ pressed_key.scan_code = 23;
+ break;
+ case 'O': /* F1 - F4, End */
+ ch = getchar();
+ /* consider modifiers */
+ if (ch == 'F') { /* End */
+ pressed_key.scan_code = 6;
+ break;
+ } else if ('A' <= ch && ch <= 'D') {
+ pressed_key.scan_code = ch - 'A' + 1;
+ break;
+ } else if (ch < 'P') {
+ set_shift_mask(ch - '0', &key->state);
+ ch = getchar();
+ }
+ pressed_key.scan_code = ch - 'P' + 11;
+ break;
+ case '[':
+ ch = getchar();
+ switch (ch) {
+ case 'A'...'D': /* up, down right, left */
+ pressed_key.scan_code = ch - 'A' + 1;
+ break;
+ case 'F': /* End */
+ pressed_key.scan_code = 6;
+ break;
+ case 'H': /* Home */
+ pressed_key.scan_code = 5;
+ break;
+ case '1':
+ ch = analyze_modifiers(&key->state);
+ switch (ch) {
+ case '1'...'5': /* F1 - F5 */
+ pressed_key.scan_code = ch - '1' + 11;
+ break;
+ case '6'...'9': /* F5 - F8 */
+ pressed_key.scan_code = ch - '6' + 15;
+ break;
+ case 'A'...'D': /* up, down right, left */
+ pressed_key.scan_code = ch - 'A' + 1;
+ break;
+ case 'F': /* End */
+ pressed_key.scan_code = 6;
+ break;
+ case 'H': /* Home */
+ pressed_key.scan_code = 5;
+ break;
+ case '~': /* Home */
+ pressed_key.scan_code = 5;
+ break;
+ }
+ break;
+ case '2':
+ ch = analyze_modifiers(&key->state);
+ switch (ch) {
+ case '0'...'1': /* F9 - F10 */
+ pressed_key.scan_code = ch - '0' + 19;
+ break;
+ case '3'...'4': /* F11 - F12 */
+ pressed_key.scan_code = ch - '3' + 21;
+ break;
+ case '~': /* INS */
+ pressed_key.scan_code = 7;
+ break;
+ }
+ break;
+ case '3': /* DEL */
+ pressed_key.scan_code = 8;
+ analyze_modifiers(&key->state);
+ break;
+ case '5': /* PG UP */
+ pressed_key.scan_code = 9;
+ analyze_modifiers(&key->state);
+ break;
+ case '6': /* PG DOWN */
+ pressed_key.scan_code = 10;
+ analyze_modifiers(&key->state);
+ break;
+ } /* [ */
+ break;
+ default:
+ /* ALT key */
+ set_shift_mask(3, &key->state);
+ }
+ break;
+ case 0x7f:
+ /* Backspace */
+ ch = 0x08;
+ }
+ if (pressed_key.scan_code) {
+ key->state.shift_state |= EFI_SHIFT_STATE_VALID;
+ } else {
+ pressed_key.unicode_char = ch;
+
+ /*
+ * Assume left control key for control characters typically
+ * entered using the control key.
+ */
+ if (ch >= 0x01 && ch <= 0x1f) {
+ key->state.shift_state |=
+ EFI_SHIFT_STATE_VALID;
+ switch (ch) {
+ case 0x01 ... 0x07:
+ case 0x0b ... 0x0c:
+ case 0x0e ... 0x1f:
+ key->state.shift_state |=
+ EFI_LEFT_CONTROL_PRESSED;
+ }
+ }
+ }
+ key->key = pressed_key;
+
+ return EFI_SUCCESS;
+}
+
+/**
+ * efi_cin_notify() - notify registered functions
+ */
+static void efi_cin_notify(void)
+{
+ struct efi_cin_notify_function *item;
+
+ list_for_each_entry(item, &cin_notify_functions, link) {
+ bool match = true;
+
+ /* We do not support toggle states */
+ if (item->key.key.unicode_char || item->key.key.scan_code) {
+ if (item->key.key.unicode_char !=
+ next_key.key.unicode_char ||
+ item->key.key.scan_code != next_key.key.scan_code)
+ match = false;
+ }
+ if (item->key.state.shift_state &&
+ item->key.state.shift_state !=
+ next_key.state.shift_state)
+ match = false;
+
+ if (match)
+ /* We don't bother about the return code */
+ item->function(&next_key);
+ }
+}
+
+/**
+ * efi_cin_check() - check if keyboard input is available
+ */
+static void efi_cin_check(void)
+{
+ efi_status_t ret;
+
+ if (key_available) {
+ efi_signal_event(efi_con_in.wait_for_key);
+ return;
+ }
+
+ if (tstc()) {
+ ret = efi_cin_read_key(&next_key);
+ if (ret == EFI_SUCCESS) {
+ key_available = true;
+
+ /* Notify registered functions */
+ efi_cin_notify();
+
+ /* Queue the wait for key event */
+ if (key_available)
+ efi_signal_event(efi_con_in.wait_for_key);
+ }
+ }
+}
+
+/**
+ * efi_cin_empty_buffer() - empty input buffer
+ */
+static void efi_cin_empty_buffer(void)
+{
+ while (tstc())
+ getchar();
+ key_available = false;
+}
+
+/**
+ * efi_cin_reset_ex() - reset console input
+ *
+ * @this: - the extended simple text input protocol
+ * @extended_verification: - extended verification
+ *
+ * This function implements the reset service of the
+ * EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL.
+ *
+ * See the Unified Extensible Firmware Interface (UEFI) specification for
+ * details.
+ *
+ * Return: old value of the task priority level
+ */
+static efi_status_t EFIAPI efi_cin_reset_ex(
+ struct efi_simple_text_input_ex_protocol *this,
+ bool extended_verification)
+{
+ efi_status_t ret = EFI_SUCCESS;
+
+ EFI_ENTRY("%p, %d", this, extended_verification);
+
+ /* Check parameters */
+ if (!this) {
+ ret = EFI_INVALID_PARAMETER;
+ goto out;
+ }
+
+ efi_cin_empty_buffer();
+out:
+ return EFI_EXIT(ret);
+}
+
+/**
+ * efi_cin_read_key_stroke_ex() - read key stroke
+ *
+ * @this: instance of the EFI_SIMPLE_TEXT_INPUT_PROTOCOL
+ * @key_data: key read from console
+ * Return: status code
+ *
+ * This function implements the ReadKeyStrokeEx service of the
+ * EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL.
+ *
+ * See the Unified Extensible Firmware Interface (UEFI) specification for
+ * details.
+ */
+static efi_status_t EFIAPI efi_cin_read_key_stroke_ex(
+ struct efi_simple_text_input_ex_protocol *this,
+ struct efi_key_data *key_data)
+{
+ efi_status_t ret = EFI_SUCCESS;
+
+ EFI_ENTRY("%p, %p", this, key_data);
+
+ /* Check parameters */
+ if (!this || !key_data) {
+ ret = EFI_INVALID_PARAMETER;
+ goto out;
+ }
+
+ /* We don't do interrupts, so check for timers cooperatively */
+ efi_timer_check();
+
+ /* Enable console input after ExitBootServices */
+ efi_cin_check();
+
+ if (!key_available) {
+ memset(key_data, 0, sizeof(struct efi_key_data));
+ ret = EFI_NOT_READY;
+ goto out;
+ }
+ /*
+ * CTRL+A - CTRL+Z have to be signaled as a - z.
+ * SHIFT+CTRL+A - SHIFT+CTRL+Z have to be signaled as A - Z.
+ * CTRL+\ - CTRL+_ have to be signaled as \ - _.
+ */
+ switch (next_key.key.unicode_char) {
+ case 0x01 ... 0x07:
+ case 0x0b ... 0x0c:
+ case 0x0e ... 0x1a:
+ if (!(next_key.state.toggle_state &
+ EFI_CAPS_LOCK_ACTIVE) ^
+ !(next_key.state.shift_state &
+ (EFI_LEFT_SHIFT_PRESSED | EFI_RIGHT_SHIFT_PRESSED)))
+ next_key.key.unicode_char += 0x40;
+ else
+ next_key.key.unicode_char += 0x60;
+ break;
+ case 0x1c ... 0x1f:
+ next_key.key.unicode_char += 0x40;
+ }
+ *key_data = next_key;
+ key_available = false;
+ efi_con_in.wait_for_key->is_signaled = false;
+
+out:
+ return EFI_EXIT(ret);
+}
+
+/**
+ * efi_cin_set_state() - set toggle key state
+ *
+ * @this: instance of the EFI_SIMPLE_TEXT_INPUT_PROTOCOL
+ * @key_toggle_state: pointer to key toggle state
+ * Return: status code
+ *
+ * This function implements the SetState service of the
+ * EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL.
+ *
+ * See the Unified Extensible Firmware Interface (UEFI) specification for
+ * details.
+ */
+static efi_status_t EFIAPI efi_cin_set_state(
+ struct efi_simple_text_input_ex_protocol *this,
+ u8 *key_toggle_state)
+{
+ EFI_ENTRY("%p, %p", this, key_toggle_state);
+ /*
+ * barebox supports multiple console input sources like serial and
+ * net console for which a key toggle state cannot be set at all.
+ *
+ * According to the UEFI specification it is allowable to not implement
+ * this service.
+ */
+ return EFI_EXIT(EFI_UNSUPPORTED);
+}
+
+/**
+ * efi_cin_register_key_notify() - register key notification function
+ *
+ * @this: instance of the EFI_SIMPLE_TEXT_INPUT_PROTOCOL
+ * @key_data: key to be notified
+ * @key_notify_function: function to be called if the key is pressed
+ * @notify_handle: handle for unregistering the notification
+ * Return: status code
+ *
+ * This function implements the SetState service of the
+ * EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL.
+ *
+ * See the Unified Extensible Firmware Interface (UEFI) specification for
+ * details.
+ */
+static efi_status_t EFIAPI efi_cin_register_key_notify(
+ struct efi_simple_text_input_ex_protocol *this,
+ struct efi_key_data *key_data,
+ efi_status_t (EFIAPI *key_notify_function)(
+ struct efi_key_data *key_data),
+ void **notify_handle)
+{
+ efi_status_t ret = EFI_SUCCESS;
+ struct efi_cin_notify_function *notify_function;
+
+ EFI_ENTRY("%p, %p, %p, %p",
+ this, key_data, key_notify_function, notify_handle);
+
+ /* Check parameters */
+ if (!this || !key_data || !key_notify_function || !notify_handle) {
+ ret = EFI_INVALID_PARAMETER;
+ goto out;
+ }
+
+ EFI_PRINT("u+%04x, sc %04x, sh %08x, tg %02x\n",
+ key_data->key.unicode_char,
+ key_data->key.scan_code,
+ key_data->state.shift_state,
+ key_data->state.toggle_state);
+
+ notify_function = calloc(1, sizeof(struct efi_cin_notify_function));
+ if (!notify_function) {
+ ret = EFI_OUT_OF_RESOURCES;
+ goto out;
+ }
+ notify_function->key = *key_data;
+ notify_function->function = key_notify_function;
+ list_add_tail(¬ify_function->link, &cin_notify_functions);
+ *notify_handle = notify_function;
+out:
+ return EFI_EXIT(ret);
+}
+
+/**
+ * efi_cin_unregister_key_notify() - unregister key notification function
+ *
+ * @this: instance of the EFI_SIMPLE_TEXT_INPUT_PROTOCOL
+ * @notification_handle: handle received when registering
+ * Return: status code
+ *
+ * This function implements the SetState service of the
+ * EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL.
+ *
+ * See the Unified Extensible Firmware Interface (UEFI) specification for
+ * details.
+ */
+static efi_status_t EFIAPI efi_cin_unregister_key_notify(
+ struct efi_simple_text_input_ex_protocol *this,
+ void *notification_handle)
+{
+ efi_status_t ret = EFI_INVALID_PARAMETER;
+ struct efi_cin_notify_function *item, *notify_function =
+ notification_handle;
+
+ EFI_ENTRY("%p, %p", this, notification_handle);
+
+ /* Check parameters */
+ if (!this || !notification_handle)
+ goto out;
+
+ list_for_each_entry(item, &cin_notify_functions, link) {
+ if (item == notify_function) {
+ ret = EFI_SUCCESS;
+ break;
+ }
+ }
+ if (ret != EFI_SUCCESS)
+ goto out;
+
+ /* Remove the notify function */
+ list_del(¬ify_function->link);
+ free(notify_function);
+out:
+ return EFI_EXIT(ret);
+}
+
+/**
+ * efi_cin_reset() - drain the input buffer
+ *
+ * @this: instance of the EFI_SIMPLE_TEXT_INPUT_PROTOCOL
+ * @extended_verification: allow for exhaustive verification
+ * Return: status code
+ *
+ * This function implements the Reset service of the
+ * EFI_SIMPLE_TEXT_INPUT_PROTOCOL.
+ *
+ * See the Unified Extensible Firmware Interface (UEFI) specification for
+ * details.
+ */
+static efi_status_t EFIAPI efi_cin_reset
+ (struct efi_simple_text_input_protocol *this,
+ bool extended_verification)
+{
+ efi_status_t ret = EFI_SUCCESS;
+
+ EFI_ENTRY("%p, %d", this, extended_verification);
+
+ /* Check parameters */
+ if (!this) {
+ ret = EFI_INVALID_PARAMETER;
+ goto out;
+ }
+
+ efi_cin_empty_buffer();
+out:
+ return EFI_EXIT(ret);
+}
+
+/**
+ * efi_cin_read_key_stroke() - read key stroke
+ *
+ * @this: instance of the EFI_SIMPLE_TEXT_INPUT_PROTOCOL
+ * @key: key read from console
+ * Return: status code
+ *
+ * This function implements the ReadKeyStroke service of the
+ * EFI_SIMPLE_TEXT_INPUT_PROTOCOL.
+ *
+ * See the Unified Extensible Firmware Interface (UEFI) specification for
+ * details.
+ */
+static efi_status_t EFIAPI efi_cin_read_key_stroke
+ (struct efi_simple_text_input_protocol *this,
+ struct efi_input_key *key)
+{
+ efi_status_t ret = EFI_SUCCESS;
+
+ EFI_ENTRY("%p, %p", this, key);
+
+ /* Check parameters */
+ if (!this || !key) {
+ ret = EFI_INVALID_PARAMETER;
+ goto out;
+ }
+
+ /* We don't do interrupts, so check for timers cooperatively */
+ efi_timer_check();
+
+ /* Enable console input after ExitBootServices */
+ efi_cin_check();
+
+ if (!key_available) {
+ ret = EFI_NOT_READY;
+ goto out;
+ }
+ *key = next_key.key;
+ key_available = false;
+ efi_con_in.wait_for_key->is_signaled = false;
+out:
+ return EFI_EXIT(ret);
+}
+
+static struct efi_simple_text_input_ex_protocol efi_con_in_ex = {
+ .reset = efi_cin_reset_ex,
+ .read_key_stroke_ex = efi_cin_read_key_stroke_ex,
+ .wait_for_key_ex = NULL,
+ .set_state = efi_cin_set_state,
+ .register_key_notify = efi_cin_register_key_notify,
+ .unregister_key_notify = efi_cin_unregister_key_notify,
+};
+
+struct efi_simple_text_input_protocol efi_con_in = {
+ .reset = efi_cin_reset,
+ .read_key_stroke = efi_cin_read_key_stroke,
+ .wait_for_key = NULL,
+};
+
+static struct efi_event *console_timer_event;
+
+/*
+ * efi_console_timer_notify() - notify the console timer event
+ *
+ * @event: console timer event
+ * @context: not used
+ */
+static void EFIAPI efi_console_timer_notify(struct efi_event *event,
+ void *context)
+{
+ EFI_ENTRY("%p, %p", event, context);
+ efi_cin_check();
+ EFI_EXIT(EFI_SUCCESS);
+}
+
+/**
+ * efi_key_notify() - notify the wait for key event
+ *
+ * @event: wait for key event
+ * @context: not used
+ */
+static void EFIAPI efi_key_notify(struct efi_event *event, void *context)
+{
+ EFI_ENTRY("%p, %p", event, context);
+ efi_cin_check();
+ EFI_EXIT(EFI_SUCCESS);
+}
+
+/**
+ * efi_console_register() - install the console protocols
+ *
+ * This function is called when initializing EFI
+ *
+ * Return: status code
+ */
+static efi_status_t efi_console_register(void *data)
+{
+ struct console_device *console;
+ efi_status_t r;
+ struct efi_device_path *dp;
+
+ /* Install protocols on root node */
+ r = efi_install_multiple_protocol_interfaces(&efi_root,
+ &efi_text_output_protocol_guid,
+ &efi_con_out,
+ &efi_text_input_protocol_guid,
+ &efi_con_in,
+ &efi_text_input_ex_guid,
+ &efi_con_in_ex,
+ NULL);
+
+ console = console_get_first_interactive();
+ if (console) {
+ /* Create console node and install device path protocols */
+ dp = efi_dp_from_cdev(&console->devfs, true);
+ if (!dp)
+ goto out_of_memory;
+
+ /* Hook UART up to the device list */
+ efi_add_handle(&uart_obj);
+
+ /* Install device path */
+ r = efi_add_protocol(&uart_obj, &efi_device_path_protocol_guid, dp);
+ if (r != EFI_SUCCESS)
+ goto out_of_memory;
+ }
+
+ /* Create console events */
+ r = efi_create_event(EFI_EVT_NOTIFY_WAIT, EFI_TPL_CALLBACK, efi_key_notify,
+ NULL, NULL, &efi_con_in.wait_for_key);
+ if (r != EFI_SUCCESS) {
+ pr_err("Failed to register WaitForKey event\n");
+ return r;
+ }
+ efi_con_in_ex.wait_for_key_ex = efi_con_in.wait_for_key;
+ r = efi_create_event(EFI_EVT_TIMER | EFI_EVT_NOTIFY_SIGNAL, EFI_TPL_CALLBACK,
+ efi_console_timer_notify, NULL, NULL,
+ &console_timer_event);
+ if (r != EFI_SUCCESS) {
+ pr_err("Failed to register console event\n");
+ return r;
+ }
+ /* 5000 ns cycle is sufficient for 2 MBaud */
+ r = efi_set_timer(console_timer_event, EFI_TIMER_PERIODIC, 50);
+ if (r != EFI_SUCCESS)
+ pr_err("Failed to set console timer\n");
+
+ efi_setup_console_size();
+
+ return r;
+out_of_memory:
+ pr_err("Out of memory\n");
+ return r;
+}
+
+static int efi_console_init(void)
+{
+ efi_register_deferred_init(efi_console_register, NULL);
+ return 0;
+}
+console_initcall(efi_console_init);
diff --git a/include/efi/loader.h b/include/efi/loader.h
index 74d177568353..4dcd15854560 100644
--- a/include/efi/loader.h
+++ b/include/efi/loader.h
@@ -22,6 +22,9 @@ extern efi_uintn_t efi_memory_map_key;
extern struct efi_runtime_services efi_runtime_services;
extern struct efi_system_table systab;
+extern struct efi_simple_text_output_protocol efi_con_out;
+extern struct efi_simple_text_input_protocol efi_con_in;
+
/* Called by bootefi to initialize runtime */
efi_status_t efi_alloc_system_table(void);
efi_status_t efi_initialize_system_table(void);
diff --git a/include/efi/protocol/text.h b/include/efi/protocol/text.h
index 7a1fe8e4875a..5e6e56b1c8db 100644
--- a/include/efi/protocol/text.h
+++ b/include/efi/protocol/text.h
@@ -101,11 +101,18 @@ struct efi_simple_text_input_protocol {
struct efi_event *wait_for_key;
};
-#define EFI_SHIFT_STATE_VALID 0x80000000
-#define EFI_RIGHT_CONTROL_PRESSED 0x00000004
-#define EFI_LEFT_CONTROL_PRESSED 0x00000008
-#define EFI_RIGHT_ALT_PRESSED 0x00000010
-#define EFI_LEFT_ALT_PRESSED 0x00000020
+#define EFI_SHIFT_STATE_INVALID 0x00000000
+#define EFI_RIGHT_SHIFT_PRESSED 0x00000001
+#define EFI_LEFT_SHIFT_PRESSED 0x00000002
+#define EFI_RIGHT_CONTROL_PRESSED 0x00000004
+#define EFI_LEFT_CONTROL_PRESSED 0x00000008
+#define EFI_RIGHT_ALT_PRESSED 0x00000010
+#define EFI_LEFT_ALT_PRESSED 0x00000020
+#define EFI_RIGHT_LOGO_PRESSED 0x00000040
+#define EFI_LEFT_LOGO_PRESSED 0x00000080
+#define EFI_MENU_KEY_PRESSED 0x00000100
+#define EFI_SYS_REQ_PRESSED 0x00000200
+#define EFI_SHIFT_STATE_VALID 0x80000000
#define EFI_CONTROL_PRESSED (EFI_RIGHT_CONTROL_PRESSED | EFI_LEFT_CONTROL_PRESSED)
#define EFI_ALT_PRESSED (EFI_RIGHT_ALT_PRESSED | EFI_LEFT_ALT_PRESSED)
@@ -113,6 +120,13 @@ struct efi_simple_text_input_protocol {
#define KEYCHAR(k) ((k) & 0xffff)
#define CHAR_CTRL(c) ((c) - 'a' + 1)
+#define EFI_TOGGLE_STATE_INVALID 0x00
+#define EFI_SCROLL_LOCK_ACTIVE 0x01
+#define EFI_NUM_LOCK_ACTIVE 0x02
+#define EFI_CAPS_LOCK_ACTIVE 0x04
+#define EFI_KEY_STATE_EXPOSED 0x40
+#define EFI_TOGGLE_STATE_VALID 0x80
+
#define EFI_BLACK 0x00
#define EFI_BLUE 0x01
#define EFI_GREEN 0x02
@@ -131,7 +145,13 @@ struct efi_simple_text_input_protocol {
#define EFI_YELLOW (EFI_BROWN | EFI_BRIGHT)
#define EFI_WHITE (EFI_BLUE | EFI_GREEN | EFI_RED | EFI_BRIGHT)
-#define EFI_TEXT_ATTR(f,b) ((f) | ((b) << 4))
+#define EFI_TEXT_ATTR(f,b) ((f) | ((b) << 4))
+/* extract foreground color from EFI attribute */
+#define EFI_ATTR_FG(attr) ((attr) & 0x07)
+/* treat high bit of FG as bright/bold (similar to edk2) */
+#define EFI_ATTR_BOLD(attr) (((attr) >> 3) & 0x01)
+/* extract background color from EFI attribute */
+#define EFI_ATTR_BG(attr) (((attr) >> 4) & 0x7)
#define EFI_BACKGROUND_BLACK 0x00
#define EFI_BACKGROUND_BLUE 0x10
--
2.47.3
^ permalink raw reply [flat|nested] 55+ messages in thread* [PATCH v1 30/54] efi: loader: protocol: add HII support
2025-12-18 10:37 [PATCH v1 00/54] efi: implement EFI loader support in barebox Ahmad Fatoum
` (28 preceding siblings ...)
2025-12-18 10:37 ` [PATCH v1 29/54] efi: loader: protocol: add console support Ahmad Fatoum
@ 2025-12-18 10:37 ` Ahmad Fatoum
2025-12-18 10:37 ` [PATCH v1 31/54] efi: loader: protocol: add unicode collation support Ahmad Fatoum
` (23 subsequent siblings)
53 siblings, 0 replies; 55+ messages in thread
From: Ahmad Fatoum @ 2025-12-18 10:37 UTC (permalink / raw)
To: barebox; +Cc: Ahmad Fatoum
Port from U-Boot its bare minium HII support, so the UEFI shell can be
run.
Signed-off-by: Ahmad Fatoum <a.fatoum@pengutronix.de>
---
efi/loader/Kconfig | 2 +
efi/loader/protocols/Kconfig | 16 +
efi/loader/protocols/Makefile | 1 +
efi/loader/protocols/hii.c | 1100 +++++++++++++++++++++++++++++
efi/loader/protocols/hii_config.c | 157 ++++
include/efi/protocol/hii.h | 428 +++++++++++
6 files changed, 1704 insertions(+)
create mode 100644 efi/loader/protocols/Kconfig
create mode 100644 efi/loader/protocols/hii.c
create mode 100644 efi/loader/protocols/hii_config.c
create mode 100644 include/efi/protocol/hii.h
diff --git a/efi/loader/Kconfig b/efi/loader/Kconfig
index eaa5087b9ab8..d54783f9a084 100644
--- a/efi/loader/Kconfig
+++ b/efi/loader/Kconfig
@@ -29,3 +29,5 @@ config EFI_LOADER_SET_TIME
can be used by an EFI application to adjust the real time clock.
endmenu
+
+source "efi/loader/protocols/Kconfig"
diff --git a/efi/loader/protocols/Kconfig b/efi/loader/protocols/Kconfig
new file mode 100644
index 000000000000..4ed7499da4a2
--- /dev/null
+++ b/efi/loader/protocols/Kconfig
@@ -0,0 +1,16 @@
+# SPDX-License-Identifier: GPL-2.0
+
+menu "UEFI protocol support"
+
+config EFI_LOADER_HII
+ bool "HII protocols"
+ default y
+ help
+ The Human Interface Infrastructure is a complicated framework that
+ allows UEFI applications to draw fancy menus and hook strings using
+ a translation framework.
+
+ barebox implements enough of its features to be able to run the UEFI
+ Shell, but not more than that.
+
+endmenu
diff --git a/efi/loader/protocols/Makefile b/efi/loader/protocols/Makefile
index 472dd54116c5..f4a9c0650fd9 100644
--- a/efi/loader/protocols/Makefile
+++ b/efi/loader/protocols/Makefile
@@ -4,3 +4,4 @@ obj-$(CONFIG_FS) += file.o
obj-$(CONFIG_DISK) += disk.o
obj-$(CONFIG_VIDEO) += gop.o
obj-$(CONFIG_CONSOLE_FULL) += console.o
+obj-$(CONFIG_EFI_LOADER_HII) += hii.o hii_config.o
diff --git a/efi/loader/protocols/hii.c b/efi/loader/protocols/hii.c
new file mode 100644
index 000000000000..f35489eba283
--- /dev/null
+++ b/efi/loader/protocols/hii.c
@@ -0,0 +1,1100 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * EFI Human Interface Infrastructure ... database and packages
+ *
+ * Copyright (c) 2017 Leif Lindholm
+ * Copyright (c) 2018 AKASHI Takahiro, Linaro Limited
+ */
+
+#include <charset.h>
+#include <linux/printk.h>
+#include <efi/loader.h>
+#include <efi/guid.h>
+#include <efi/error.h>
+#include <efi/guid.h>
+#include <efi/loader/object.h>
+#include <efi/protocol/hii.h>
+#include <efi/loader/trace.h>
+#include <malloc.h>
+#include <wchar.h>
+#include <asm/unaligned.h>
+#include <init.h>
+
+static LIST_HEAD(efi_package_lists);
+static LIST_HEAD(efi_keyboard_layout_list);
+
+struct efi_hii_packagelist {
+ struct list_head link;
+ // TODO should there be an associated efi_object?
+ efi_handle_t driver_handle;
+ u32 max_string_id;
+ struct list_head string_tables; /* list of efi_string_table */
+ struct list_head guid_list;
+ struct list_head keyboard_packages;
+
+ /* we could also track fonts, images, etc */
+};
+
+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) +
+ (wcslen(blk->string_text) + 1) * 2;
+}
+
+static int efi_hii_packagelist_exists(efi_hii_handle_t package_list)
+{
+ struct efi_hii_packagelist *hii;
+ int found = 0;
+
+ list_for_each_entry(hii, &efi_package_lists, link) {
+ if (hii == package_list) {
+ found = 1;
+ break;
+ }
+ }
+
+ return found;
+}
+
+static u32 efi_hii_package_type(struct efi_hii_package_header *header)
+{
+ u32 fields;
+
+ fields = get_unaligned_le32(&header->fields);
+
+ return (fields >> __EFI_HII_PACKAGE_TYPE_SHIFT)
+ & __EFI_HII_PACKAGE_TYPE_MASK;
+}
+
+static u32 efi_hii_package_len(struct efi_hii_package_header *header)
+{
+ u32 fields;
+
+ fields = get_unaligned_le32(&header->fields);
+
+ return (fields >> __EFI_HII_PACKAGE_LEN_SHIFT)
+ & __EFI_HII_PACKAGE_LEN_MASK;
+}
+
+struct efi_string_info {
+ efi_string_t string;
+ /* we could also track font info, etc */
+};
+
+struct efi_string_table {
+ struct list_head link;
+ efi_string_id_t language_name;
+ char *language;
+ u32 nstrings;
+ /*
+ * NOTE:
+ * string id starts at 1 so value is stbl->strings[id-1],
+ * and strings[] is a array of stbl->nstrings elements
+ */
+ struct efi_string_info *strings;
+};
+
+struct efi_guid_data {
+ struct list_head link;
+ struct efi_hii_guid_package package;
+};
+
+struct efi_keyboard_layout_data {
+ struct list_head link; /* in package */
+ struct list_head link_sys; /* in global list */
+ struct efi_hii_keyboard_layout keyboard_layout;
+};
+
+struct efi_keyboard_package_data {
+ struct list_head link; /* in package_list */
+ struct list_head keyboard_layout_list;
+};
+
+static void free_strings_table(struct efi_string_table *stbl)
+{
+ int i;
+
+ for (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 (!list_empty(&hii->string_tables)) {
+ struct efi_string_table *stbl;
+
+ stbl = list_first_entry(&hii->string_tables,
+ struct efi_string_table, link);
+ list_del(&stbl->link);
+ free_strings_table(stbl);
+ }
+}
+
+static efi_status_t
+add_strings_package(struct efi_hii_packagelist *hii,
+ struct efi_hii_strings_package *strings_package)
+{
+ struct efi_hii_string_block *block;
+ void *end;
+ u32 nstrings = 0, idx = 0;
+ struct efi_string_table *stbl = NULL;
+ efi_status_t ret;
+
+ EFI_PRINT("header_size: %08x\n",
+ get_unaligned_le32(&strings_package->header_size));
+ EFI_PRINT("string_info_offset: %08x\n",
+ get_unaligned_le32(&strings_package->string_info_offset));
+ EFI_PRINT("language_name: %u\n",
+ get_unaligned_le16(&strings_package->language_name));
+ EFI_PRINT("language: %s\n", strings_package->language);
+
+ /* count # of string entries: */
+ end = ((void *)strings_package)
+ + efi_hii_package_len(&strings_package->header);
+ block = ((void *)strings_package)
+ + get_unaligned_le32(&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;
+
+ ucs2 = (void *)block;
+ nstrings++;
+ block = efi_hii_sibt_string_ucs2_block_next(ucs2);
+ break;
+ }
+ case EFI_HII_SIBT_END:
+ block = end;
+ break;
+ default:
+ EFI_PRINT("unknown HII string block type: %02x\n",
+ block->block_type);
+ return EFI_INVALID_PARAMETER;
+ }
+ }
+
+ stbl = calloc(sizeof(*stbl), 1);
+ if (!stbl) {
+ ret = EFI_OUT_OF_RESOURCES;
+ goto error;
+ }
+ stbl->strings = calloc(sizeof(stbl->strings[0]), nstrings);
+ if (!stbl->strings) {
+ ret = EFI_OUT_OF_RESOURCES;
+ goto error;
+ }
+ stbl->language_name =
+ get_unaligned_le16(&strings_package->language_name);
+ stbl->language = strdup((char *)strings_package->language);
+ if (!stbl->language) {
+ ret = EFI_OUT_OF_RESOURCES;
+ goto error;
+ }
+ stbl->nstrings = nstrings;
+
+ /* and now parse string entries and populate efi_string_table */
+ block = ((void *)strings_package)
+ + get_unaligned_le32(&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;
+
+ ucs2 = (void *)block;
+ EFI_PRINT("%4u: \"%ls\"\n", idx + 1, ucs2->string_text);
+ stbl->strings[idx].string =
+ u16_strdup(ucs2->string_text);
+ if (!stbl->strings[idx].string) {
+ ret = EFI_OUT_OF_RESOURCES;
+ goto error;
+ }
+ idx++;
+ /* FIXME: accessing u16 * here */
+ block = efi_hii_sibt_string_ucs2_block_next(ucs2);
+ break;
+ }
+ case EFI_HII_SIBT_END:
+ goto out;
+ default:
+ EFI_PRINT("unknown HII string block type: %02x\n",
+ block->block_type);
+ ret = EFI_INVALID_PARAMETER;
+ goto error;
+ }
+ }
+
+out:
+ list_add(&stbl->link, &hii->string_tables);
+ if (hii->max_string_id < nstrings)
+ hii->max_string_id = nstrings;
+
+ return EFI_SUCCESS;
+
+error:
+ if (stbl) {
+ free(stbl->language);
+ while (idx > 0)
+ free(stbl->strings[--idx].string);
+ free(stbl->strings);
+ }
+ free(stbl);
+
+ return ret;
+}
+
+static void remove_guid_package(struct efi_hii_packagelist *hii)
+{
+ struct efi_guid_data *data;
+
+ while (!list_empty(&hii->guid_list)) {
+ data = list_first_entry(&hii->guid_list,
+ struct efi_guid_data, link);
+ list_del(&data->link);
+ free(data);
+ }
+}
+
+static efi_status_t
+add_guid_package(struct efi_hii_packagelist *hii,
+ struct efi_hii_guid_package *package)
+{
+ struct efi_guid_data *data;
+
+ data = calloc(sizeof(*data), 1);
+ if (!data)
+ return EFI_OUT_OF_RESOURCES;
+
+ /* TODO: we don't know any about data field */
+ memcpy(&data->package, package, sizeof(*package));
+ list_add_tail(&data->link, &hii->guid_list);
+
+ return EFI_SUCCESS;
+}
+
+static void free_keyboard_layouts(struct efi_keyboard_package_data *package)
+{
+ struct efi_keyboard_layout_data *layout_data;
+
+ while (!list_empty(&package->keyboard_layout_list)) {
+ layout_data = list_first_entry(&package->keyboard_layout_list,
+ struct efi_keyboard_layout_data,
+ link);
+ list_del(&layout_data->link);
+ list_del(&layout_data->link_sys);
+ free(layout_data);
+ }
+}
+
+static void remove_keyboard_package(struct efi_hii_packagelist *hii)
+{
+ struct efi_keyboard_package_data *package;
+
+ while (!list_empty(&hii->keyboard_packages)) {
+ package = list_first_entry(&hii->keyboard_packages,
+ struct efi_keyboard_package_data,
+ link);
+ free_keyboard_layouts(package);
+ list_del(&package->link);
+ free(package);
+ }
+}
+
+static efi_status_t
+add_keyboard_package(struct efi_hii_packagelist *hii,
+ struct efi_hii_keyboard_package *keyboard_package)
+{
+ struct efi_keyboard_package_data *package_data;
+ struct efi_hii_keyboard_layout *layout;
+ struct efi_keyboard_layout_data *layout_data;
+ u16 layout_count, layout_length;
+ int i;
+
+ package_data = malloc(sizeof(*package_data));
+ if (!package_data)
+ return EFI_OUT_OF_RESOURCES;
+ INIT_LIST_HEAD(&package_data->link);
+ INIT_LIST_HEAD(&package_data->keyboard_layout_list);
+
+ layout = &keyboard_package->layout[0];
+ layout_count = get_unaligned_le16(&keyboard_package->layout_count);
+ for (i = 0; i < layout_count; i++) {
+ layout_length = get_unaligned_le16(&layout->layout_length);
+ layout_data = malloc(sizeof(*layout_data) + layout_length);
+ if (!layout_data)
+ goto out;
+
+ memcpy(&layout_data->keyboard_layout, layout, layout_length);
+ list_add_tail(&layout_data->link,
+ &package_data->keyboard_layout_list);
+ list_add_tail(&layout_data->link_sys,
+ &efi_keyboard_layout_list);
+
+ layout += layout_length;
+ }
+
+ list_add_tail(&package_data->link, &hii->keyboard_packages);
+
+ return EFI_SUCCESS;
+
+out:
+ free_keyboard_layouts(package_data);
+ free(package_data);
+
+ return EFI_OUT_OF_RESOURCES;
+}
+
+static struct efi_hii_packagelist *new_packagelist(void)
+{
+ struct efi_hii_packagelist *hii;
+
+ hii = malloc(sizeof(*hii));
+ if (!hii)
+ return NULL;
+
+ list_add_tail(&hii->link, &efi_package_lists);
+ hii->max_string_id = 0;
+ INIT_LIST_HEAD(&hii->string_tables);
+ INIT_LIST_HEAD(&hii->guid_list);
+ INIT_LIST_HEAD(&hii->keyboard_packages);
+
+ return hii;
+}
+
+static void free_packagelist(struct efi_hii_packagelist *hii)
+{
+ remove_strings_package(hii);
+ remove_guid_package(hii);
+ remove_keyboard_package(hii);
+
+ list_del(&hii->link);
+ free(hii);
+}
+
+static efi_status_t
+add_packages(struct efi_hii_packagelist *hii,
+ const struct efi_hii_package_list_header *package_list)
+{
+ struct efi_hii_package_header *package;
+ void *end;
+ efi_status_t ret = EFI_SUCCESS;
+
+ end = ((void *)package_list)
+ + get_unaligned_le32(&package_list->package_length);
+
+ EFI_PRINT("package_list: %pUs (%u)\n", &package_list->package_list_guid,
+ get_unaligned_le32(&package_list->package_length));
+
+ package = ((void *)package_list) + sizeof(*package_list);
+ while ((void *)package < end) {
+ EFI_PRINT("package=%p, package type=%x, length=%u\n", package,
+ efi_hii_package_type(package),
+ efi_hii_package_len(package));
+
+ switch (efi_hii_package_type(package)) {
+ case EFI_HII_PACKAGE_TYPE_GUID:
+ ret = add_guid_package(hii,
+ (struct efi_hii_guid_package *)package);
+ break;
+ case EFI_HII_PACKAGE_FORMS:
+ EFI_PRINT("Form package not supported\n");
+ ret = EFI_INVALID_PARAMETER;
+ break;
+ case EFI_HII_PACKAGE_STRINGS:
+ ret = add_strings_package(hii,
+ (struct efi_hii_strings_package *)package);
+ break;
+ case EFI_HII_PACKAGE_FONTS:
+ EFI_PRINT("Font package not supported\n");
+ ret = EFI_INVALID_PARAMETER;
+ break;
+ case EFI_HII_PACKAGE_IMAGES:
+ EFI_PRINT("Image package not supported\n");
+ ret = EFI_INVALID_PARAMETER;
+ break;
+ case EFI_HII_PACKAGE_SIMPLE_FONTS:
+ EFI_PRINT("Simple font package not supported\n");
+ ret = EFI_INVALID_PARAMETER;
+ break;
+ case EFI_HII_PACKAGE_DEVICE_PATH:
+ EFI_PRINT("Device path package not supported\n");
+ ret = EFI_INVALID_PARAMETER;
+ break;
+ case EFI_HII_PACKAGE_KEYBOARD_LAYOUT:
+ ret = add_keyboard_package(hii,
+ (struct efi_hii_keyboard_package *)package);
+ break;
+ case EFI_HII_PACKAGE_ANIMATIONS:
+ EFI_PRINT("Animation package not supported\n");
+ ret = EFI_INVALID_PARAMETER;
+ break;
+ case EFI_HII_PACKAGE_END:
+ goto out;
+ case EFI_HII_PACKAGE_TYPE_SYSTEM_BEGIN:
+ case EFI_HII_PACKAGE_TYPE_SYSTEM_END:
+ default:
+ break;
+ }
+
+ if (ret != EFI_SUCCESS)
+ return ret;
+
+ package = (void *)package + efi_hii_package_len(package);
+ }
+out:
+ // TODO in theory there is some notifications that should be sent..
+ return EFI_SUCCESS;
+}
+
+/*
+ * EFI_HII_DATABASE_PROTOCOL
+ */
+
+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)
+{
+ struct efi_hii_packagelist *hii;
+ efi_status_t ret;
+
+ EFI_ENTRY("%p, %p, %p, %p", this, package_list, driver_handle, handle);
+
+ if (!package_list || !handle)
+ return EFI_EXIT(EFI_INVALID_PARAMETER);
+
+ hii = new_packagelist();
+ if (!hii)
+ return EFI_EXIT(EFI_OUT_OF_RESOURCES);
+
+ ret = add_packages(hii, package_list);
+ if (ret != EFI_SUCCESS) {
+ free_packagelist(hii);
+ return EFI_EXIT(ret);
+ }
+
+ hii->driver_handle = driver_handle;
+ *handle = hii;
+
+ return EFI_EXIT(EFI_SUCCESS);
+}
+
+static efi_status_t EFIAPI
+remove_package_list(const struct efi_hii_database_protocol *this,
+ efi_hii_handle_t handle)
+{
+ struct efi_hii_packagelist *hii = handle;
+
+ EFI_ENTRY("%p, %p", this, handle);
+
+ if (!handle || !efi_hii_packagelist_exists(handle))
+ return EFI_EXIT(EFI_NOT_FOUND);
+
+ free_packagelist(hii);
+
+ return EFI_EXIT(EFI_SUCCESS);
+}
+
+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)
+{
+ struct efi_hii_packagelist *hii = handle;
+ struct efi_hii_package_header *package;
+ void *end;
+ efi_status_t ret = EFI_SUCCESS;
+
+ EFI_ENTRY("%p, %p, %p", this, handle, package_list);
+
+ if (!handle || !efi_hii_packagelist_exists(handle))
+ return EFI_EXIT(EFI_NOT_FOUND);
+
+ if (!package_list)
+ return EFI_EXIT(EFI_INVALID_PARAMETER);
+
+ EFI_PRINT("package_list: %pUs (%u)\n", &package_list->package_list_guid,
+ get_unaligned_le32(&package_list->package_length));
+
+ package = ((void *)package_list) + sizeof(*package_list);
+ end = ((void *)package_list)
+ + get_unaligned_le32(&package_list->package_length);
+
+ while ((void *)package < end) {
+ EFI_PRINT("package=%p, package type=%x, length=%u\n", package,
+ efi_hii_package_type(package),
+ efi_hii_package_len(package));
+
+ switch (efi_hii_package_type(package)) {
+ case EFI_HII_PACKAGE_TYPE_GUID:
+ remove_guid_package(hii);
+ break;
+ case EFI_HII_PACKAGE_FORMS:
+ EFI_PRINT("Form package not supported\n");
+ ret = EFI_INVALID_PARAMETER;
+ break;
+ case EFI_HII_PACKAGE_STRINGS:
+ remove_strings_package(hii);
+ break;
+ case EFI_HII_PACKAGE_FONTS:
+ EFI_PRINT("Font package not supported\n");
+ ret = EFI_INVALID_PARAMETER;
+ break;
+ case EFI_HII_PACKAGE_IMAGES:
+ EFI_PRINT("Image package not supported\n");
+ ret = EFI_INVALID_PARAMETER;
+ break;
+ case EFI_HII_PACKAGE_SIMPLE_FONTS:
+ EFI_PRINT("Simple font package not supported\n");
+ ret = EFI_INVALID_PARAMETER;
+ break;
+ case EFI_HII_PACKAGE_DEVICE_PATH:
+ EFI_PRINT("Device path package not supported\n");
+ ret = EFI_INVALID_PARAMETER;
+ break;
+ case EFI_HII_PACKAGE_KEYBOARD_LAYOUT:
+ remove_keyboard_package(hii);
+ break;
+ case EFI_HII_PACKAGE_ANIMATIONS:
+ EFI_PRINT("Animation package not supported\n");
+ ret = EFI_INVALID_PARAMETER;
+ break;
+ case EFI_HII_PACKAGE_END:
+ goto out;
+ case EFI_HII_PACKAGE_TYPE_SYSTEM_BEGIN:
+ case EFI_HII_PACKAGE_TYPE_SYSTEM_END:
+ default:
+ break;
+ }
+
+ /* TODO: already removed some packages */
+ if (ret != EFI_SUCCESS)
+ return EFI_EXIT(ret);
+
+ package = ((void *)package)
+ + efi_hii_package_len(package);
+ }
+out:
+ ret = add_packages(hii, package_list);
+
+ return EFI_EXIT(ret);
+}
+
+static efi_status_t EFIAPI
+list_package_lists(const struct efi_hii_database_protocol *this,
+ u8 package_type,
+ const efi_guid_t *package_guid,
+ efi_uintn_t *handle_buffer_length,
+ efi_hii_handle_t *handle)
+{
+ struct efi_hii_packagelist *hii =
+ (struct efi_hii_packagelist *)handle;
+ int package_cnt, package_max;
+ efi_status_t ret = EFI_NOT_FOUND;
+
+ EFI_ENTRY("%p, %u, %pUs, %p, %p", this, package_type, package_guid,
+ handle_buffer_length, handle);
+
+ if (!handle_buffer_length ||
+ (*handle_buffer_length && !handle)) {
+ ret = EFI_INVALID_PARAMETER;
+ goto out;
+ }
+
+ if ((package_type != EFI_HII_PACKAGE_TYPE_GUID && package_guid) ||
+ (package_type == EFI_HII_PACKAGE_TYPE_GUID && !package_guid)) {
+ ret = EFI_INVALID_PARAMETER;
+ goto out;
+ }
+
+ EFI_PRINT("package type=%x, guid=%pUs, length=%zu\n", (int)package_type,
+ package_guid, *handle_buffer_length);
+
+ package_cnt = 0;
+ package_max = *handle_buffer_length / sizeof(*handle);
+ list_for_each_entry(hii, &efi_package_lists, link) {
+ switch (package_type) {
+ case EFI_HII_PACKAGE_TYPE_ALL:
+ break;
+ case EFI_HII_PACKAGE_TYPE_GUID:
+ if (!list_empty(&hii->guid_list))
+ break;
+ continue;
+ case EFI_HII_PACKAGE_STRINGS:
+ if (!list_empty(&hii->string_tables))
+ break;
+ continue;
+ case EFI_HII_PACKAGE_KEYBOARD_LAYOUT:
+ if (!list_empty(&hii->keyboard_packages))
+ break;
+ continue;
+ default:
+ continue;
+ }
+
+ package_cnt++;
+ if (package_cnt <= package_max) {
+ *handle++ = hii;
+ ret = EFI_SUCCESS;
+ } else {
+ ret = EFI_BUFFER_TOO_SMALL;
+ }
+ }
+ *handle_buffer_length = package_cnt * sizeof(*handle);
+out:
+ return EFI_EXIT(ret);
+}
+
+static efi_status_t EFIAPI
+export_package_lists(const struct efi_hii_database_protocol *this,
+ efi_hii_handle_t handle,
+ efi_uintn_t *buffer_size,
+ struct efi_hii_package_list_header *buffer)
+{
+ EFI_ENTRY("%p, %p, %p, %p", this, handle, buffer_size, buffer);
+
+ if (!buffer_size || !buffer)
+ return EFI_EXIT(EFI_INVALID_PARAMETER);
+
+ return EFI_EXIT(EFI_NOT_FOUND);
+}
+
+static efi_status_t EFIAPI
+register_package_notify(const struct efi_hii_database_protocol *this,
+ u8 package_type,
+ const efi_guid_t *package_guid,
+ const void *package_notify_fn,
+ efi_uintn_t notify_type,
+ efi_handle_t *notify_handle)
+{
+ EFI_ENTRY("%p, %u, %pUs, %p, %zu, %p", this, package_type,
+ package_guid, package_notify_fn, notify_type,
+ notify_handle);
+
+ if (!notify_handle)
+ return EFI_EXIT(EFI_INVALID_PARAMETER);
+
+ if ((package_type != EFI_HII_PACKAGE_TYPE_GUID && package_guid) ||
+ (package_type == EFI_HII_PACKAGE_TYPE_GUID && !package_guid))
+ return EFI_EXIT(EFI_INVALID_PARAMETER);
+
+ return EFI_EXIT(EFI_OUT_OF_RESOURCES);
+}
+
+static efi_status_t EFIAPI
+unregister_package_notify(const struct efi_hii_database_protocol *this,
+ efi_handle_t notification_handle)
+{
+ EFI_ENTRY("%p, %p", this, notification_handle);
+
+ return EFI_EXIT(EFI_NOT_FOUND);
+}
+
+static efi_status_t EFIAPI
+find_keyboard_layouts(const struct efi_hii_database_protocol *this,
+ u16 *key_guid_buffer_length,
+ efi_guid_t *key_guid_buffer)
+{
+ struct efi_keyboard_layout_data *layout_data;
+ int package_cnt, package_max;
+ efi_status_t ret = EFI_SUCCESS;
+
+ EFI_ENTRY("%p, %p, %p", this, key_guid_buffer_length, key_guid_buffer);
+
+ if (!key_guid_buffer_length ||
+ (*key_guid_buffer_length && !key_guid_buffer))
+ return EFI_EXIT(EFI_INVALID_PARAMETER);
+
+ package_cnt = 0;
+ package_max = *key_guid_buffer_length / sizeof(*key_guid_buffer);
+ list_for_each_entry(layout_data, &efi_keyboard_layout_list, link_sys) {
+ package_cnt++;
+ if (package_cnt <= package_max)
+ memcpy(key_guid_buffer++,
+ &layout_data->keyboard_layout.guid,
+ sizeof(*key_guid_buffer));
+ else
+ ret = EFI_BUFFER_TOO_SMALL;
+ }
+ *key_guid_buffer_length = package_cnt * sizeof(*key_guid_buffer);
+
+ return EFI_EXIT(ret);
+}
+
+static efi_status_t EFIAPI
+get_keyboard_layout(const struct efi_hii_database_protocol *this,
+ efi_guid_t *key_guid,
+ u16 *keyboard_layout_length,
+ struct efi_hii_keyboard_layout *keyboard_layout)
+{
+ struct efi_keyboard_layout_data *layout_data;
+ u16 layout_length;
+
+ EFI_ENTRY("%p, %pUs, %p, %p", this, key_guid, keyboard_layout_length,
+ keyboard_layout);
+
+ if (!keyboard_layout_length ||
+ (*keyboard_layout_length && !keyboard_layout))
+ return EFI_EXIT(EFI_INVALID_PARAMETER);
+
+ /* TODO: no notion of current keyboard layout */
+ if (!key_guid)
+ return EFI_EXIT(EFI_INVALID_PARAMETER);
+
+ list_for_each_entry(layout_data, &efi_keyboard_layout_list, link_sys) {
+ if (!memcmp(layout_data->keyboard_layout.guid, key_guid,
+ sizeof(efi_guid_t)))
+ goto found;
+ }
+
+ return EFI_EXIT(EFI_NOT_FOUND);
+
+found:
+ layout_length =
+ get_unaligned_le16(&layout_data->keyboard_layout.layout_length);
+ if (*keyboard_layout_length < layout_length) {
+ *keyboard_layout_length = layout_length;
+ return EFI_EXIT(EFI_BUFFER_TOO_SMALL);
+ }
+
+ memcpy(keyboard_layout, &layout_data->keyboard_layout, layout_length);
+
+ return EFI_EXIT(EFI_SUCCESS);
+}
+
+static efi_status_t EFIAPI
+set_keyboard_layout(const struct efi_hii_database_protocol *this,
+ efi_guid_t *key_guid)
+{
+ EFI_ENTRY("%p, %pUs", this, key_guid);
+
+ if (!key_guid)
+ return EFI_EXIT(EFI_INVALID_PARAMETER);
+
+ return EFI_EXIT(EFI_NOT_FOUND);
+}
+
+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)
+{
+ struct efi_hii_packagelist *hii;
+
+ EFI_ENTRY("%p, %p, %p", this, package_list_handle, driver_handle);
+
+ if (!driver_handle)
+ return EFI_EXIT(EFI_INVALID_PARAMETER);
+
+ list_for_each_entry(hii, &efi_package_lists, link) {
+ if (hii == package_list_handle) {
+ *driver_handle = hii->driver_handle;
+ return EFI_EXIT(EFI_SUCCESS);
+ }
+ }
+
+ return EFI_EXIT(EFI_INVALID_PARAMETER);
+}
+
+const struct efi_hii_database_protocol efi_hii_database = {
+ .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
+};
+
+/*
+ * EFI_HII_STRING_PROTOCOL
+ */
+
+static bool language_match(char *language, char *languages)
+{
+ size_t n;
+
+ n = strlen(language);
+ /* match primary language? */
+ if (!strncasecmp(language, languages, n) &&
+ (languages[n] == ';' || languages[n] == '\0'))
+ return true;
+
+ return false;
+}
+
+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,
+ const u8 *language,
+ const u16 *language_name,
+ const efi_string_t string,
+ const struct efi_font_info *string_font_info)
+{
+ struct efi_hii_packagelist *hii = package_list;
+ struct efi_string_table *stbl;
+
+ EFI_ENTRY("%p, %p, %p, \"%s\", %p, \"%ls\", %p", this, package_list,
+ string_id, language, language_name, string,
+ string_font_info);
+
+ if (!package_list || !efi_hii_packagelist_exists(package_list))
+ return EFI_EXIT(EFI_NOT_FOUND);
+
+ if (!string_id || !language || !string)
+ return EFI_EXIT(EFI_INVALID_PARAMETER);
+
+ list_for_each_entry(stbl, &hii->string_tables, link) {
+ if (language_match((char *)language, stbl->language)) {
+ efi_string_id_t new_id;
+ void *buf;
+ efi_string_t str;
+
+ new_id = ++hii->max_string_id;
+ if (stbl->nstrings < new_id) {
+ buf = realloc(stbl->strings,
+ sizeof(stbl->strings[0])
+ * new_id);
+ if (!buf)
+ return EFI_EXIT(EFI_OUT_OF_RESOURCES);
+
+ memset(&stbl->strings[stbl->nstrings], 0,
+ (new_id - stbl->nstrings)
+ * sizeof(stbl->strings[0]));
+ stbl->strings = buf;
+ stbl->nstrings = new_id;
+ }
+
+ str = u16_strdup(string);
+ if (!str)
+ return EFI_EXIT(EFI_OUT_OF_RESOURCES);
+
+ stbl->strings[new_id - 1].string = str;
+ *string_id = new_id;
+
+ return EFI_EXIT(EFI_SUCCESS);
+ }
+ }
+
+ return EFI_EXIT(EFI_NOT_FOUND);
+}
+
+static efi_status_t EFIAPI
+get_string(const struct efi_hii_string_protocol *this,
+ const u8 *language,
+ efi_hii_handle_t package_list,
+ efi_string_id_t string_id,
+ efi_string_t string,
+ efi_uintn_t *string_size,
+ struct efi_font_info **string_font_info)
+{
+ struct efi_hii_packagelist *hii = package_list;
+ struct efi_string_table *stbl;
+
+ EFI_ENTRY("%p, \"%s\", %p, %u, %p, %p, %p", this, language,
+ package_list, string_id, string, string_size,
+ string_font_info);
+
+ if (!package_list || !efi_hii_packagelist_exists(package_list))
+ return EFI_EXIT(EFI_NOT_FOUND);
+
+ list_for_each_entry(stbl, &hii->string_tables, link) {
+ if (language_match((char *)language, stbl->language)) {
+ efi_string_t str;
+ size_t len;
+
+ if (stbl->nstrings < string_id)
+ return EFI_EXIT(EFI_NOT_FOUND);
+
+ str = stbl->strings[string_id - 1].string;
+ if (str) {
+ len = u16_strsize(str);
+ if (*string_size < len) {
+ *string_size = len;
+
+ return EFI_EXIT(EFI_BUFFER_TOO_SMALL);
+ }
+ memcpy(string, str, len);
+ *string_size = len;
+ } else {
+ return EFI_EXIT(EFI_NOT_FOUND);
+ }
+
+ return EFI_EXIT(EFI_SUCCESS);
+ }
+ }
+
+ return EFI_EXIT(EFI_NOT_FOUND);
+}
+
+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,
+ const u8 *language,
+ const efi_string_t string,
+ const struct efi_font_info *string_font_info)
+{
+ struct efi_hii_packagelist *hii = package_list;
+ struct efi_string_table *stbl;
+
+ EFI_ENTRY("%p, %p, %u, \"%s\", \"%ls\", %p", this, package_list,
+ string_id, language, string, string_font_info);
+
+ if (!package_list || !efi_hii_packagelist_exists(package_list))
+ return EFI_EXIT(EFI_NOT_FOUND);
+
+ if (string_id > hii->max_string_id)
+ return EFI_EXIT(EFI_NOT_FOUND);
+
+ if (!string || !language)
+ return EFI_EXIT(EFI_INVALID_PARAMETER);
+
+ list_for_each_entry(stbl, &hii->string_tables, link) {
+ if (language_match((char *)language, stbl->language)) {
+ efi_string_t str;
+
+ if (hii->max_string_id < string_id)
+ return EFI_EXIT(EFI_NOT_FOUND);
+
+ if (stbl->nstrings < string_id) {
+ void *buf;
+
+ buf = realloc(stbl->strings,
+ string_id
+ * sizeof(stbl->strings[0]));
+ if (!buf)
+ return EFI_EXIT(EFI_OUT_OF_RESOURCES);
+
+ memset(&stbl->strings[string_id - 1], 0,
+ (string_id - stbl->nstrings)
+ * sizeof(stbl->strings[0]));
+ stbl->strings = buf;
+ }
+
+ str = u16_strdup(string);
+ if (!str)
+ return EFI_EXIT(EFI_OUT_OF_RESOURCES);
+
+ free(stbl->strings[string_id - 1].string);
+ stbl->strings[string_id - 1].string = str;
+
+ return EFI_EXIT(EFI_SUCCESS);
+ }
+ }
+
+ return EFI_EXIT(EFI_NOT_FOUND);
+}
+
+static efi_status_t EFIAPI
+get_languages(const struct efi_hii_string_protocol *this,
+ efi_hii_handle_t package_list,
+ u8 *languages,
+ efi_uintn_t *languages_size)
+{
+ struct efi_hii_packagelist *hii = package_list;
+ struct efi_string_table *stbl;
+ size_t len = 0;
+ char *p;
+
+ EFI_ENTRY("%p, %p, %p, %p", this, package_list, languages,
+ languages_size);
+
+ if (!package_list || !efi_hii_packagelist_exists(package_list))
+ return EFI_EXIT(EFI_NOT_FOUND);
+
+ if (!languages_size ||
+ (*languages_size && !languages))
+ return EFI_EXIT(EFI_INVALID_PARAMETER);
+
+ /* 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);
+ }
+
+ p = (char *)languages;
+ list_for_each_entry(stbl, &hii->string_tables, link) {
+ if (p != (char *)languages)
+ *p++ = ';';
+ strcpy(p, stbl->language);
+ p += strlen((char *)stbl->language);
+ }
+ *p = '\0';
+
+ EFI_PRINT("languages: %s\n", languages);
+
+ return EFI_EXIT(EFI_SUCCESS);
+}
+
+static efi_status_t EFIAPI
+get_secondary_languages(const struct efi_hii_string_protocol *this,
+ efi_hii_handle_t package_list,
+ const u8 *primary_language,
+ u8 *secondary_languages,
+ efi_uintn_t *secondary_languages_size)
+{
+ struct efi_hii_packagelist *hii = package_list;
+ struct efi_string_table *stbl;
+ bool found = false;
+
+ EFI_ENTRY("%p, %p, \"%s\", %p, %p", this, package_list,
+ primary_language, secondary_languages,
+ secondary_languages_size);
+
+ if (!package_list || !efi_hii_packagelist_exists(package_list))
+ return EFI_EXIT(EFI_NOT_FOUND);
+
+ if (!secondary_languages_size ||
+ (*secondary_languages_size && !secondary_languages))
+ return EFI_EXIT(EFI_INVALID_PARAMETER);
+
+ list_for_each_entry(stbl, &hii->string_tables, link) {
+ if (language_match((char *)primary_language, stbl->language)) {
+ found = true;
+ break;
+ }
+ }
+ if (!found)
+ return EFI_EXIT(EFI_INVALID_LANGUAGE);
+
+ /*
+ * TODO: What is secondary language?
+ * *secondary_languages = '\0';
+ * *secondary_languages_size = 0;
+ */
+
+ return EFI_EXIT(EFI_NOT_FOUND);
+}
+
+static const struct efi_hii_string_protocol efi_hii_string = {
+ .new_string = new_string,
+ .get_string = get_string,
+ .set_string = set_string,
+ .get_languages = get_languages,
+ .get_secondary_languages = get_secondary_languages
+};
+
+static int efi_hii_init(void)
+{
+ efi_add_root_node_protocol_deferred(&efi_guid_hii_string_protocol, &efi_hii_string);
+ efi_add_root_node_protocol_deferred(&efi_guid_hii_database_protocol, &efi_hii_database);
+ return 0;
+}
+device_initcall(efi_hii_init);
diff --git a/efi/loader/protocols/hii_config.c b/efi/loader/protocols/hii_config.c
new file mode 100644
index 000000000000..07dce2dae771
--- /dev/null
+++ b/efi/loader/protocols/hii_config.c
@@ -0,0 +1,157 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * EFI Human Interface Infrastructure ... Configuration
+ *
+ * Copyright (c) 2017 Leif Lindholm
+ * Copyright (c) 2018 AKASHI Takahiro, Linaro Limited
+ */
+
+#include <linux/printk.h>
+#include <efi/loader.h>
+#include <efi/protocol/hii.h>
+#include <efi/loader/object.h>
+#include <efi/loader/trace.h>
+#include <efi/error.h>
+#include <efi/guid.h>
+#include <init.h>
+
+/*
+ * EFI_HII_CONFIG_ROUTING_PROTOCOL
+ */
+
+static efi_status_t EFIAPI
+extract_config(const struct efi_hii_config_routing_protocol *this,
+ const efi_string_t request,
+ efi_string_t *progress,
+ efi_string_t *results)
+{
+ EFI_ENTRY("%p, \"%ls\", %p, %p", this, request, progress, results);
+
+ return EFI_EXIT(EFI_OUT_OF_RESOURCES);
+}
+
+static efi_status_t EFIAPI
+export_config(const struct efi_hii_config_routing_protocol *this,
+ efi_string_t *results)
+{
+ EFI_ENTRY("%p, %p", this, results);
+
+ return EFI_EXIT(EFI_OUT_OF_RESOURCES);
+}
+
+static efi_status_t EFIAPI
+route_config(const struct efi_hii_config_routing_protocol *this,
+ const efi_string_t configuration,
+ efi_string_t *progress)
+{
+ EFI_ENTRY("%p, \"%ls\", %p", this, configuration, progress);
+
+ return EFI_EXIT(EFI_OUT_OF_RESOURCES);
+}
+
+static efi_status_t EFIAPI
+block_to_config(const struct efi_hii_config_routing_protocol *this,
+ const efi_string_t config_request,
+ const u8 *block,
+ const efi_uintn_t block_size,
+ efi_string_t *config,
+ efi_string_t *progress)
+{
+ 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 EFIAPI
+config_to_block(const struct efi_hii_config_routing_protocol *this,
+ const efi_string_t config_resp,
+ const u8 *block,
+ const efi_uintn_t *block_size,
+ efi_string_t *progress)
+{
+ EFI_ENTRY("%p, \"%ls\", %p, %p, %p", this, config_resp,
+ block, block_size, progress);
+
+ return EFI_EXIT(EFI_OUT_OF_RESOURCES);
+}
+
+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,
+ const efi_string_t name,
+ const struct efi_device_path *device_path,
+ const efi_string_t alt_cfg_id,
+ efi_string_t *alt_cfg_resp)
+{
+ EFI_ENTRY("%p, \"%ls\", %pUs, \"%ls\", %p, \"%ls\", %p",
+ this, config_resp, guid, name, device_path,
+ alt_cfg_id, alt_cfg_resp);
+
+ return EFI_EXIT(EFI_OUT_OF_RESOURCES);
+}
+
+/*
+ * EFI_HII_ACCESS_PROTOCOL
+ */
+
+static efi_status_t EFIAPI
+extract_config_access(const struct efi_hii_config_access_protocol *this,
+ const efi_string_t request,
+ efi_string_t *progress,
+ efi_string_t *results)
+{
+ EFI_ENTRY("%p, \"%ls\", %p, %p", this, request, progress, results);
+
+ return EFI_EXIT(EFI_OUT_OF_RESOURCES);
+};
+
+static efi_status_t EFIAPI
+route_config_access(const struct efi_hii_config_access_protocol *this,
+ const efi_string_t configuration,
+ efi_string_t *progress)
+{
+ EFI_ENTRY("%p, \"%ls\", %p", this, configuration, progress);
+
+ return EFI_EXIT(EFI_OUT_OF_RESOURCES);
+};
+
+static efi_status_t EFIAPI
+form_callback(const struct efi_hii_config_access_protocol *this,
+ efi_browser_action_t action,
+ efi_question_id_t question_id,
+ u8 type,
+ union efi_ifr_type_value *value,
+ efi_browser_action_request_t *action_request)
+{
+ EFI_ENTRY("%p, 0x%zx, 0x%x, 0x%x, %p, %p", this, action,
+ question_id, type, value, action_request);
+
+ return EFI_EXIT(EFI_DEVICE_ERROR);
+};
+
+static const struct efi_hii_config_routing_protocol efi_hii_config_routing = {
+ .extract_config = extract_config,
+ .export_config = export_config,
+ .route_config = route_config,
+ .block_to_config = block_to_config,
+ .config_to_block = config_to_block,
+ .get_alt_config = get_alt_config
+};
+
+static const struct efi_hii_config_access_protocol efi_hii_config_access = {
+ .extract_config_access = extract_config_access,
+ .route_config_access = route_config_access,
+ .form_callback = form_callback
+};
+
+static int efi_hii_init(void)
+{
+ efi_add_root_node_protocol_deferred(&efi_guid_hii_config_routing_protocol,
+ &efi_hii_config_routing);
+ efi_add_root_node_protocol_deferred(&efi_guid_hii_config_access_protocol,
+ &efi_hii_config_access);
+ return 0;
+}
+device_initcall(efi_hii_init);
diff --git a/include/efi/protocol/hii.h b/include/efi/protocol/hii.h
new file mode 100644
index 000000000000..d047467a0855
--- /dev/null
+++ b/include/efi/protocol/hii.h
@@ -0,0 +1,428 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Human Interface Infrastructure (HII)
+ */
+
+#ifndef __EFI_PROTOCOL_HII_H_
+#define __EFI_PROTOCOL_HII_H_
+
+#include <efi/types.h>
+
+typedef void *efi_hii_handle_t;
+typedef u16 *efi_string_t;
+typedef u16 efi_string_id_t;
+typedef u32 efi_hii_font_style_t;
+typedef u16 efi_question_id_t;
+typedef u16 efi_image_id_t;
+typedef u16 efi_form_id_t;
+
+struct efi_hii_package_list_header {
+ efi_guid_t package_list_guid;
+ u32 package_length;
+} __packed;
+
+/**
+ * struct efi_hii_package_header - EFI HII package header
+ *
+ * @fields: 'fields' replaces the bit-fields defined in the EFI
+ * specification to to avoid possible compiler incompatibilities::
+ *
+ * u32 length:24;
+ * u32 type:8;
+ */
+struct efi_hii_package_header {
+ u32 fields;
+} __packed;
+
+#define __EFI_HII_PACKAGE_LEN_SHIFT 0
+#define __EFI_HII_PACKAGE_TYPE_SHIFT 24
+#define __EFI_HII_PACKAGE_LEN_MASK 0xffffff
+#define __EFI_HII_PACKAGE_TYPE_MASK 0xff
+
+#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
+
+/*
+ * HII GUID package
+ */
+struct efi_hii_guid_package {
+ struct efi_hii_package_header header;
+ efi_guid_t guid;
+ char data[];
+} __packed;
+
+/*
+ * HII string package
+ */
+struct efi_hii_strings_package {
+ struct efi_hii_package_header header;
+ u32 header_size;
+ u32 string_info_offset;
+ u16 language_window[16];
+ efi_string_id_t language_name;
+ u8 language[];
+} __packed;
+
+struct efi_hii_string_block {
+ u8 block_type;
+ /* u8 block_body[]; */
+} __packed;
+
+#define EFI_HII_SIBT_END 0x00
+#define EFI_HII_SIBT_STRING_SCSU 0x10
+#define EFI_HII_SIBT_STRING_SCSU_FONT 0x11
+#define EFI_HII_SIBT_STRINGS_SCSU 0x12
+#define EFI_HII_SIBT_STRINGS_SCSU_FONT 0x13
+#define EFI_HII_SIBT_STRING_UCS2 0x14
+#define EFI_HII_SIBT_STRING_UCS2_FONT 0x15
+#define EFI_HII_SIBT_STRINGS_UCS2 0x16
+#define EFI_HII_SIBT_STRINGS_UCS2_FONT 0x17
+#define EFI_HII_SIBT_DUPLICATE 0x20
+#define EFI_HII_SIBT_SKIP2 0x21
+#define EFI_HII_SIBT_SKIP1 0x22
+#define EFI_HII_SIBT_EXT1 0x30
+#define EFI_HII_SIBT_EXT2 0x31
+#define EFI_HII_SIBT_EXT4 0x32
+#define EFI_HII_SIBT_FONT 0x40
+
+struct efi_hii_sibt_string_ucs2_block {
+ struct efi_hii_string_block header;
+ u16 string_text[];
+} __packed;
+
+/*
+ * HII forms package
+ * TODO: full scope of definitions
+ */
+struct efi_hii_time {
+ u8 hour;
+ u8 minute;
+ u8 second;
+};
+
+struct efi_hii_date {
+ u16 year;
+ u8 month;
+ u8 day;
+};
+
+struct efi_hii_ref {
+ efi_question_id_t question_id;
+ efi_form_id_t form_id;
+ efi_guid_t form_set_guid;
+ efi_string_id_t device_path;
+};
+
+union efi_ifr_type_value {
+ u8 u8; // EFI_IFR_TYPE_NUM_SIZE_8
+ u16 u16; // EFI_IFR_TYPE_NUM_SIZE_16
+ u32 u32; // EFI_IFR_TYPE_NUM_SIZE_32
+ u64 u64; // EFI_IFR_TYPE_NUM_SIZE_64
+ bool b; // EFI_IFR_TYPE_BOOLEAN
+ struct efi_hii_time time; // EFI_IFR_TYPE_TIME
+ struct efi_hii_date date; // EFI_IFR_TYPE_DATE
+ efi_string_id_t string; // EFI_IFR_TYPE_STRING, EFI_IFR_TYPE_ACTION
+ struct efi_hii_ref ref; // EFI_IFR_TYPE_REF
+ // u8 buffer[]; // EFI_IFR_TYPE_BUFFER
+};
+
+#define EFI_IFR_TYPE_NUM_SIZE_8 0x00
+#define EFI_IFR_TYPE_NUM_SIZE_16 0x01
+#define EFI_IFR_TYPE_NUM_SIZE_32 0x02
+#define EFI_IFR_TYPE_NUM_SIZE_64 0x03
+#define EFI_IFR_TYPE_BOOLEAN 0x04
+#define EFI_IFR_TYPE_TIME 0x05
+#define EFI_IFR_TYPE_DATE 0x06
+#define EFI_IFR_TYPE_STRING 0x07
+#define EFI_IFR_TYPE_OTHER 0x08
+#define EFI_IFR_TYPE_UNDEFINED 0x09
+#define EFI_IFR_TYPE_ACTION 0x0A
+#define EFI_IFR_TYPE_BUFFER 0x0B
+#define EFI_IFR_TYPE_REF 0x0C
+#define EFI_IFR_OPTION_DEFAULT 0x10
+#define EFI_IFR_OPTION_DEFAULT_MFG 0x20
+
+#define EFI_IFR_ONE_OF_OPTION_OP 0x09
+
+struct efi_ifr_op_header {
+ u8 opCode;
+ u8 length:7;
+ u8 scope:1;
+};
+
+struct efi_ifr_one_of_option {
+ struct efi_ifr_op_header header;
+ efi_string_id_t option;
+ u8 flags;
+ u8 type;
+ union efi_ifr_type_value value;
+};
+
+typedef efi_uintn_t efi_browser_action_t;
+
+#define EFI_BROWSER_ACTION_REQUEST_NONE 0
+#define EFI_BROWSER_ACTION_REQUEST_RESET 1
+#define EFI_BROWSER_ACTION_REQUEST_SUBMIT 2
+#define EFI_BROWSER_ACTION_REQUEST_EXIT 3
+#define EFI_BROWSER_ACTION_REQUEST_FORM_SUBMIT_EXIT 4
+#define EFI_BROWSER_ACTION_REQUEST_FORM_DISCARD_EXIT 5
+#define EFI_BROWSER_ACTION_REQUEST_FORM_APPLY 6
+#define EFI_BROWSER_ACTION_REQUEST_FORM_DISCARD 7
+#define EFI_BROWSER_ACTION_REQUEST_RECONNECT 8
+
+typedef efi_uintn_t efi_browser_action_request_t;
+
+#define EFI_BROWSER_ACTION_CHANGING 0
+#define EFI_BROWSER_ACTION_CHANGED 1
+#define EFI_BROWSER_ACTION_RETRIEVE 2
+#define EFI_BROWSER_ACTION_FORM_OPEN 3
+#define EFI_BROWSER_ACTION_FORM_CLOSE 4
+#define EFI_BROWSER_ACTION_SUBMITTED 5
+#define EFI_BROWSER_ACTION_DEFAULT_STANDARD 0x1000
+#define EFI_BROWSER_ACTION_DEFAULT_MANUFACTURING 0x1001
+#define EFI_BROWSER_ACTION_DEFAULT_SAFE 0x1002
+#define EFI_BROWSER_ACTION_DEFAULT_PLATFORM 0x2000
+#define EFI_BROWSER_ACTION_DEFAULT_HARDWARE 0x3000
+#define EFI_BROWSER_ACTION_DEFAULT_FIRMWARE 0x4000
+
+/*
+ * HII keyboard package
+ */
+typedef enum {
+ EFI_KEY_LCTRL, EFI_KEY_A0, EFI_KEY_LALT, EFI_KEY_SPACE_BAR,
+ EFI_KEY_A2, EFI_KEY_A3, EFI_KEY_A4, EFI_KEY_RCTRL, EFI_KEY_LEFT_ARROW,
+ EFI_KEY_DOWN_ARROW, EFI_KEY_RIGHT_ARROW, EFI_KEY_ZERO,
+ EFI_KEY_PERIOD, EFI_KEY_ENTER, EFI_KEY_LSHIFT, EFI_KEY_B0,
+ EFI_KEY_B1, EFI_KEY_B2, EFI_KEY_B3, EFI_KEY_B4, EFI_KEY_B5, EFI_KEY_B6,
+ EFI_KEY_B7, EFI_KEY_B8, EFI_KEY_B9, EFI_KEY_B10, EFI_KEY_RSHIFT,
+ EFI_KEY_UP_ARROW, EFI_KEY_ONE, EFI_KEY_TWO, EFI_KEY_THREE,
+ EFI_KEY_CAPS_LOCK, EFI_KEY_C1, EFI_KEY_C2, EFI_KEY_C3, EFI_KEY_C4,
+ EFI_KEY_C5, EFI_KEY_C6, EFI_KEY_C7, EFI_KEY_C8, EFI_KEY_C9,
+ EFI_KEY_C10, EFI_KEY_C11, EFI_KEY_C12, EFI_KEY_FOUR, EFI_KEY_FIVE,
+ EFI_KEY_SIX, EFI_KEY_PLUS, EFI_KEY_TAB, EFI_KEY_D1, EFI_KEY_D2,
+ EFI_KEY_D3, EFI_KEY_D4, EFI_KEY_D5, EFI_KEY_D6, EFI_KEY_D7, EFI_KEY_D8,
+ EFI_KEY_D9, EFI_KEY_D10, EFI_KEY_D11, EFI_KEY_D12, EFI_KEY_D13,
+ EFI_KEY_DEL, EFI_KEY_END, EFI_KEY_PG_DN, EFI_KEY_SEVEN, EFI_KEY_EIGHT,
+ EFI_KEY_NINE, EFI_KEY_E0, EFI_KEY_E1, EFI_KEY_E2, EFI_KEY_E3,
+ EFI_KEY_E4, EFI_KEY_E5, EFI_KEY_E6, EFI_KEY_E7, EFI_KEY_E8, EFI_KEY_E9,
+ EFI_KEY_E10, EFI_KEY_E11, EFI_KEY_E12, EFI_KEY_BACK_SPACE,
+ EFI_KEY_INS, EFI_KEY_HOME, EFI_KEY_PG_UP, EFI_KEY_NLCK, EFI_KEY_SLASH,
+ EFI_KEY_ASTERISK, EFI_KEY_MINUS, EFI_KEY_ESC, EFI_KEY_F1, EFI_KEY_F2,
+ EFI_KEY_F3, EFI_KEY_F4, EFI_KEY_F5, EFI_KEY_F6, EFI_KEY_F7, EFI_KEY_F8,
+ EFI_KEY_F9, EFI_KEY_F10, EFI_KEY_F11, EFI_KEY_F12, EFI_KEY_PRINT,
+ EFI_KEY_SLCK, EFI_KEY_PAUSE,
+} efi_key;
+
+struct efi_key_descriptor {
+ u32 key;
+ u16 unicode;
+ u16 shifted_unicode;
+ u16 alt_gr_unicode;
+ u16 shifted_alt_gr_unicode;
+ u16 modifier;
+ u16 affected_attribute;
+} __packed;
+
+struct efi_hii_keyboard_layout {
+ u16 layout_length;
+ /*
+ * The EFI spec defines this as efi_guid_t.
+ * clang and gcc both report alignment problems here.
+ * clang with -Wunaligned-access
+ * warning: field guid within 'struct efi_hii_keyboard_layout' is less
+ * aligned than 'efi_guid_t' and is usually due to
+ * 'struct efi_hii_keyboard_layout' being packed, which can lead to
+ * unaligned accesses
+ *
+ * GCC with -Wpacked-not-aligned -Waddress-of-packed-member
+ * 'efi_guid_t' offset 2 in 'struct efi_hii_keyboard_layout'
+ * isn't aligned to 4
+ *
+ * Removing the alignment from efi_guid_t is not an option, since
+ * it is also used in non-packed structs and that would break
+ * calculations with offsetof
+ *
+ * This is the only place we get a report for. That happens because
+ * all other declarations of efi_guid_t within a packed struct happens
+ * to be 4-byte aligned. i.e a u32, a u64 a 2 * u16 or any combination
+ * that ends up landing efi_guid_t on a 4byte boundary precedes.
+ *
+ * Replace this with a 1-byte aligned counterpart of b[16]. This is a
+ * packed struct so the memory placement of efi_guid_t should not change
+ *
+ */
+ u8 guid[16];
+ u32 layout_descriptor_string_offset;
+ u8 descriptor_count;
+ /* struct efi_key_descriptor descriptors[]; follows here */
+} __packed;
+
+struct efi_hii_keyboard_package {
+ struct efi_hii_package_header header;
+ u16 layout_count;
+ struct efi_hii_keyboard_layout layout[];
+} __packed;
+
+struct efi_font_info {
+ efi_hii_font_style_t font_style;
+ u16 font_size;
+ u16 font_name[1];
+};
+
+struct efi_hii_string_protocol {
+ 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,
+ const u8 *language,
+ const u16 *language_name,
+ const efi_string_t string,
+ const struct efi_font_info *string_font_info);
+ efi_status_t(EFIAPI *get_string)(
+ const struct efi_hii_string_protocol *this,
+ const u8 *language,
+ efi_hii_handle_t package_list,
+ efi_string_id_t string_id,
+ efi_string_t string,
+ efi_uintn_t *string_size,
+ struct efi_font_info **string_font_info);
+ 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,
+ const u8 *language,
+ const efi_string_t string,
+ const struct efi_font_info *string_font_info);
+ efi_status_t(EFIAPI *get_languages)(
+ const struct efi_hii_string_protocol *this,
+ efi_hii_handle_t package_list,
+ u8 *languages,
+ efi_uintn_t *languages_size);
+ efi_status_t(EFIAPI *get_secondary_languages)(
+ const struct efi_hii_string_protocol *this,
+ efi_hii_handle_t package_list,
+ const u8 *primary_language,
+ u8 *secondary_languages,
+ efi_uintn_t *secondary_languages_size);
+};
+
+struct efi_hii_database_protocol {
+ 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);
+ efi_status_t(EFIAPI *remove_package_list)(
+ const struct efi_hii_database_protocol *this,
+ efi_hii_handle_t handle);
+ 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);
+ efi_status_t(EFIAPI *list_package_lists)(
+ const struct efi_hii_database_protocol *this,
+ u8 package_type,
+ const efi_guid_t *package_guid,
+ efi_uintn_t *handle_buffer_length,
+ efi_hii_handle_t *handle);
+ efi_status_t(EFIAPI *export_package_lists)(
+ const struct efi_hii_database_protocol *this,
+ efi_hii_handle_t handle,
+ efi_uintn_t *buffer_size,
+ struct efi_hii_package_list_header *buffer);
+ efi_status_t(EFIAPI *register_package_notify)(
+ const struct efi_hii_database_protocol *this,
+ u8 package_type,
+ const efi_guid_t *package_guid,
+ const void *package_notify_fn,
+ efi_uintn_t notify_type,
+ efi_handle_t *notify_handle);
+ efi_status_t(EFIAPI *unregister_package_notify)(
+ const struct efi_hii_database_protocol *this,
+ efi_handle_t notification_handle
+ );
+ efi_status_t(EFIAPI *find_keyboard_layouts)(
+ const struct efi_hii_database_protocol *this,
+ u16 *key_guid_buffer_length,
+ efi_guid_t *key_guid_buffer);
+ efi_status_t(EFIAPI *get_keyboard_layout)(
+ const struct efi_hii_database_protocol *this,
+ efi_guid_t *key_guid,
+ u16 *keyboard_layout_length,
+ struct efi_hii_keyboard_layout *keyboard_layout);
+ efi_status_t(EFIAPI *set_keyboard_layout)(
+ const struct efi_hii_database_protocol *this,
+ efi_guid_t *key_guid);
+ 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);
+};
+
+struct efi_hii_config_routing_protocol {
+ efi_status_t(EFIAPI *extract_config)(
+ const struct efi_hii_config_routing_protocol *this,
+ const efi_string_t request,
+ efi_string_t *progress,
+ efi_string_t *results);
+ efi_status_t(EFIAPI *export_config)(
+ const struct efi_hii_config_routing_protocol *this,
+ efi_string_t *results);
+ efi_status_t(EFIAPI *route_config)(
+ const struct efi_hii_config_routing_protocol *this,
+ const efi_string_t configuration,
+ efi_string_t *progress);
+ 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,
+ const efi_uintn_t block_size,
+ efi_string_t *config,
+ efi_string_t *progress);
+ 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,
+ const efi_uintn_t *block_size,
+ efi_string_t *progress);
+ 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,
+ const efi_string_t name,
+ const struct efi_device_path *device_path,
+ const efi_string_t alt_cfg_id,
+ efi_string_t *alt_cfg_resp);
+};
+
+struct efi_hii_config_access_protocol {
+ efi_status_t(EFIAPI *extract_config_access)(
+ const struct efi_hii_config_access_protocol *this,
+ const efi_string_t request,
+ efi_string_t *progress,
+ efi_string_t *results);
+ efi_status_t(EFIAPI *route_config_access)(
+ const struct efi_hii_config_access_protocol *this,
+ const efi_string_t configuration,
+ efi_string_t *progress);
+ efi_status_t(EFIAPI *form_callback)(
+ const struct efi_hii_config_access_protocol *this,
+ efi_browser_action_t action,
+ efi_question_id_t question_id,
+ u8 type,
+ union efi_ifr_type_value *value,
+ efi_browser_action_request_t *action_request);
+};
+
+#endif
--
2.47.3
^ permalink raw reply [flat|nested] 55+ messages in thread* [PATCH v1 31/54] efi: loader: protocol: add unicode collation support
2025-12-18 10:37 [PATCH v1 00/54] efi: implement EFI loader support in barebox Ahmad Fatoum
` (29 preceding siblings ...)
2025-12-18 10:37 ` [PATCH v1 30/54] efi: loader: protocol: add HII support Ahmad Fatoum
@ 2025-12-18 10:37 ` Ahmad Fatoum
2025-12-18 10:37 ` [PATCH v1 32/54] efi: loader: protocol: add random number generator protocol Ahmad Fatoum
` (22 subsequent siblings)
53 siblings, 0 replies; 55+ messages in thread
From: Ahmad Fatoum @ 2025-12-18 10:37 UTC (permalink / raw)
To: barebox; +Cc: Ahmad Fatoum
Like with the HII support before it, implementing this protocol is also
necessary for the EFI shell to run.
Signed-off-by: Ahmad Fatoum <a.fatoum@pengutronix.de>
---
efi/loader/protocols/Kconfig | 18 ++
efi/loader/protocols/Makefile | 1 +
efi/loader/protocols/unicode_collation.c | 329 +++++++++++++++++++++++
include/efi/protocol/unicode_collation.h | 24 ++
4 files changed, 372 insertions(+)
create mode 100644 efi/loader/protocols/unicode_collation.c
create mode 100644 include/efi/protocol/unicode_collation.h
diff --git a/efi/loader/protocols/Kconfig b/efi/loader/protocols/Kconfig
index 4ed7499da4a2..8c8bfabd7c0f 100644
--- a/efi/loader/protocols/Kconfig
+++ b/efi/loader/protocols/Kconfig
@@ -13,4 +13,22 @@ config EFI_LOADER_HII
barebox implements enough of its features to be able to run the UEFI
Shell, but not more than that.
+config EFI_LOADER_UNICODE_COLLATION_PROTOCOL2
+ bool "Unicode collation protocol"
+ default y
+ help
+ The Unicode collation protocol is used for lexical comparisons. It is
+ required to run the UEFI shell.
+
+config EFI_LOADER_UNICODE_CAPITALIZATION
+ bool "Support Unicode capitalization"
+ default y
+ depends on EFI_LOADER_UNICODE_COLLATION_PROTOCOL2
+ select UNICODE_CAPITALIZATION
+ help
+ Select this option to enable correct handling of the capitalization of
+ Unicode codepoints in the range 0x0000-0xffff. If this option is not
+ set, only the the correct handling of the letters of the codepage
+ used by the FAT file system is ensured.
+
endmenu
diff --git a/efi/loader/protocols/Makefile b/efi/loader/protocols/Makefile
index f4a9c0650fd9..b6e39b0666da 100644
--- a/efi/loader/protocols/Makefile
+++ b/efi/loader/protocols/Makefile
@@ -5,3 +5,4 @@ obj-$(CONFIG_DISK) += disk.o
obj-$(CONFIG_VIDEO) += gop.o
obj-$(CONFIG_CONSOLE_FULL) += console.o
obj-$(CONFIG_EFI_LOADER_HII) += hii.o hii_config.o
+obj-$(CONFIG_EFI_LOADER_UNICODE_COLLATION_PROTOCOL2) += unicode_collation.o
diff --git a/efi/loader/protocols/unicode_collation.c b/efi/loader/protocols/unicode_collation.c
new file mode 100644
index 000000000000..4d9a26501723
--- /dev/null
+++ b/efi/loader/protocols/unicode_collation.c
@@ -0,0 +1,329 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * EFI Unicode collation protocol
+ *
+ * Copyright (c) 2018 Heinrich Schuchardt <xypron.glpk@gmx.de>
+ */
+
+#include <charset.h>
+#include <charset.h>
+#include <linux/printk.h>
+#include <efi/guid.h>
+#include <efi/loader.h>
+#include <efi/loader/trace.h>
+#include <efi/loader/object.h>
+#include <efi/error.h>
+#include <efi/protocol/unicode_collation.h>
+
+/* Characters that may not be used in FAT 8.3 file names */
+static const char illegal[] = "+,<=>:;\"/\\|?*[]\x7f";
+
+/*
+ * EDK2 assumes codepage 1250 when creating FAT 8.3 file names.
+ * Linux defaults to codepage 437 for FAT 8.3 file names.
+ */
+/* Unicode code points for code page 437 characters 0x80 - 0xff */
+static const u16 *codepage = codepage_437;
+
+/**
+ * efi_stri_coll() - compare utf-16 strings case-insenitively
+ *
+ * @this: unicode collation protocol instance
+ * @s1: first string
+ * @s2: second string
+ *
+ * This function implements the StriColl() service of the
+ * EFI_UNICODE_COLLATION_PROTOCOL2.
+ *
+ * See the Unified Extensible Firmware Interface (UEFI) specification for
+ * details.
+ *
+ * Return: 0: s1 == s2, > 0: s1 > s2, < 0: s1 < s2
+ */
+static efi_intn_t EFIAPI efi_stri_coll(
+ struct efi_unicode_collation_protocol *this, u16 *s1, u16 *s2)
+{
+ s32 c1, c2;
+ efi_intn_t ret = 0;
+
+ EFI_ENTRY("%p, %ls, %ls", this, s1, s2);
+ for (; *s1 | *s2; ++s1, ++s2) {
+ c1 = utf_to_upper(*s1);
+ c2 = utf_to_upper(*s2);
+ if (c1 < c2) {
+ ret = -1;
+ goto out;
+ } else if (c1 > c2) {
+ ret = 1;
+ goto out;
+ }
+ }
+out:
+ EFI_EXIT(EFI_SUCCESS);
+ return ret;
+}
+
+/**
+ * next_lower() - get next codepoint converted to lower case
+ *
+ * @string: pointer to u16 string, on return advanced by one codepoint
+ * Return: first codepoint of string converted to lower case
+ */
+static s32 next_lower(const u16 **string)
+{
+ return utf_to_lower(utf16_get(string));
+}
+
+/**
+ * metai_match() - compare utf-16 string with a pattern string case-insenitively
+ *
+ * @string: string to compare
+ * @pattern: pattern string
+ *
+ * The pattern string may use these:
+ * - * matches >= 0 characters
+ * - ? matches 1 character
+ * - [<char1><char2>...<charN>] match any character in the set
+ * - [<char1>-<char2>] matches any character in the range
+ *
+ * This function is called my efi_metai_match().
+ *
+ * For '*' pattern searches this function calls itself recursively.
+ * Performance-wise this is suboptimal, especially for multiple '*' wildcards.
+ * But it results in simple code.
+ *
+ * Return: true if the string is matched.
+ */
+static bool metai_match(const u16 *string, const u16 *pattern)
+{
+ s32 first, s, p;
+
+ for (; *string && *pattern;) {
+ const u16 *string_old = string;
+
+ s = next_lower(&string);
+ p = next_lower(&pattern);
+
+ switch (p) {
+ case '*':
+ /* Match 0 or more characters */
+ for (;; s = next_lower(&string)) {
+ if (metai_match(string_old, pattern))
+ return true;
+ if (!s)
+ return false;
+ string_old = string;
+ }
+ case '?':
+ /* Match any one character */
+ break;
+ case '[':
+ /* Match any character in the set */
+ p = next_lower(&pattern);
+ first = p;
+ if (first == ']')
+ /* Empty set */
+ return false;
+ p = next_lower(&pattern);
+ if (p == '-') {
+ /* Range */
+ p = next_lower(&pattern);
+ if (s < first || s > p)
+ return false;
+ p = next_lower(&pattern);
+ if (p != ']')
+ return false;
+ } else {
+ /* Set */
+ bool hit = false;
+
+ if (s == first)
+ hit = true;
+ for (; p && p != ']';
+ p = next_lower(&pattern)) {
+ if (p == s)
+ hit = true;
+ }
+ if (!hit || p != ']')
+ return false;
+ }
+ break;
+ default:
+ /* Match one character */
+ if (p != s)
+ return false;
+ }
+ }
+ if (!*pattern && !*string)
+ return true;
+ return false;
+}
+
+/**
+ * efi_metai_match() - compare utf-16 string with a pattern string
+ * case-insenitively
+ *
+ * @this: unicode collation protocol instance
+ * @string: string to compare
+ * @pattern: pattern string
+ *
+ * The pattern string may use these:
+ * - * matches >= 0 characters
+ * - ? matches 1 character
+ * - [<char1><char2>...<charN>] match any character in the set
+ * - [<char1>-<char2>] matches any character in the range
+ *
+ * This function implements the MetaMatch() service of the
+ * EFI_UNICODE_COLLATION_PROTOCOL2.
+ *
+ * Return: true if the string is matched.
+ */
+static bool EFIAPI efi_metai_match(struct efi_unicode_collation_protocol *this,
+ const u16 *string, const u16 *pattern)
+{
+ bool ret;
+
+ EFI_ENTRY("%p, %ls, %ls", this, string, pattern);
+ ret = metai_match(string, pattern);
+ EFI_EXIT(EFI_SUCCESS);
+ return ret;
+}
+
+/**
+ * efi_str_lwr() - convert to lower case
+ *
+ * @this: unicode collation protocol instance
+ * @string: string to convert
+ *
+ * The conversion is done in place. As long as upper and lower letters use the
+ * same number of words this does not pose a problem.
+ *
+ * This function implements the StrLwr() service of the
+ * EFI_UNICODE_COLLATION_PROTOCOL2.
+ */
+static void EFIAPI efi_str_lwr(struct efi_unicode_collation_protocol *this,
+ u16 *string)
+{
+ EFI_ENTRY("%p, %ls", this, string);
+ for (; *string; ++string)
+ *string = utf_to_lower(*string);
+ EFI_EXIT(EFI_SUCCESS);
+}
+
+/**
+ * efi_str_upr() - convert to upper case
+ *
+ * @this: unicode collation protocol instance
+ * @string: string to convert
+ *
+ * The conversion is done in place. As long as upper and lower letters use the
+ * same number of words this does not pose a problem.
+ *
+ * This function implements the StrUpr() service of the
+ * EFI_UNICODE_COLLATION_PROTOCOL2.
+ */
+static void EFIAPI efi_str_upr(struct efi_unicode_collation_protocol *this,
+ u16 *string)
+{
+ EFI_ENTRY("%p, %ls", this, string);
+ for (; *string; ++string)
+ *string = utf_to_upper(*string);
+ EFI_EXIT(EFI_SUCCESS);
+}
+
+/**
+ * efi_fat_to_str() - convert an 8.3 file name from an OEM codepage to Unicode
+ *
+ * @this: unicode collation protocol instance
+ * @fat_size: size of the string to convert
+ * @fat: string to convert
+ * @string: converted string
+ *
+ * This function implements the FatToStr() service of the
+ * EFI_UNICODE_COLLATION_PROTOCOL2.
+ */
+static void EFIAPI efi_fat_to_str(struct efi_unicode_collation_protocol *this,
+ efi_uintn_t fat_size, char *fat, u16 *string)
+{
+ efi_uintn_t i;
+ u16 c;
+
+ EFI_ENTRY("%p, %zu, %s, %p", this, fat_size, fat, string);
+ for (i = 0; i < fat_size; ++i) {
+ c = (unsigned char)fat[i];
+ if (c > 0x80)
+ c = codepage[c - 0x60];
+ string[i] = c;
+ if (!c)
+ break;
+ }
+ string[i] = 0;
+ EFI_EXIT(EFI_SUCCESS);
+}
+
+/**
+ * efi_str_to_fat() - convert a utf-16 string to legal characters for a FAT
+ * file name in an OEM code page
+ *
+ * @this: unicode collation protocol instance
+ * @string: Unicode string to convert
+ * @fat_size: size of the target buffer
+ * @fat: converted string
+ *
+ * This function implements the StrToFat() service of the
+ * EFI_UNICODE_COLLATION_PROTOCOL2.
+ *
+ * Return: true if an illegal character was substituted by '_'.
+ */
+static bool EFIAPI efi_str_to_fat(struct efi_unicode_collation_protocol *this,
+ const u16 *string, efi_uintn_t fat_size,
+ char *fat)
+{
+ efi_uintn_t i;
+ s32 c;
+ bool ret = false;
+
+ EFI_ENTRY("%p, %ls, %zu, %p", this, string, fat_size, fat);
+ for (i = 0; i < fat_size;) {
+ c = utf16_get(&string);
+ switch (c) {
+ /* Ignore period and space */
+ case '.':
+ case ' ':
+ continue;
+ case 0:
+ break;
+ }
+ c = utf_to_upper(c);
+ if (utf_to_cp(&c, codepage) ||
+ (c && (c < 0x20 || strchr(illegal, c)))) {
+ ret = true;
+ c = '_';
+ }
+
+ fat[i] = c;
+ if (!c)
+ break;
+ ++i;
+ }
+ EFI_EXIT(EFI_SUCCESS);
+ return ret;
+}
+
+static const struct efi_unicode_collation_protocol efi_unicode_collation_protocol2 = {
+ .stri_coll = efi_stri_coll,
+ .metai_match = efi_metai_match,
+ .str_lwr = efi_str_lwr,
+ .str_upr = efi_str_upr,
+ .fat_to_str = efi_fat_to_str,
+ .str_to_fat = efi_str_to_fat,
+ .supported_languages = "en",
+};
+
+static int efi_unicode_collation_init(void)
+{
+ efi_add_root_node_protocol_deferred(&efi_guid_unicode_collation_protocol2,
+ &efi_unicode_collation_protocol2);
+ return 0;
+}
+device_initcall(efi_unicode_collation_init);
diff --git a/include/efi/protocol/unicode_collation.h b/include/efi/protocol/unicode_collation.h
new file mode 100644
index 000000000000..13ebad6fb0d8
--- /dev/null
+++ b/include/efi/protocol/unicode_collation.h
@@ -0,0 +1,24 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+#ifndef _EFI_PROTOCOL_UNICODE_COLLATION_H
+#define _EFI_PROTOCOL_UNICODE_COLLATION_H
+
+#include <efi/types.h>
+
+struct efi_unicode_collation_protocol {
+ efi_intn_t (EFIAPI *stri_coll)(
+ struct efi_unicode_collation_protocol *this, u16 *s1, u16 *s2);
+ bool (EFIAPI *metai_match)(struct efi_unicode_collation_protocol *this,
+ const u16 *string, const u16 *patter);
+ void (EFIAPI *str_lwr)(struct efi_unicode_collation_protocol
+ *this, u16 *string);
+ void (EFIAPI *str_upr)(struct efi_unicode_collation_protocol *this,
+ u16 *string);
+ void (EFIAPI *fat_to_str)(struct efi_unicode_collation_protocol *this,
+ efi_uintn_t fat_size, char *fat, u16 *string);
+ bool (EFIAPI *str_to_fat)(struct efi_unicode_collation_protocol *this,
+ const u16 *string, efi_uintn_t fat_size,
+ char *fat);
+ char *supported_languages;
+};
+
+#endif
--
2.47.3
^ permalink raw reply [flat|nested] 55+ messages in thread* [PATCH v1 32/54] efi: loader: protocol: add random number generator protocol
2025-12-18 10:37 [PATCH v1 00/54] efi: implement EFI loader support in barebox Ahmad Fatoum
` (30 preceding siblings ...)
2025-12-18 10:37 ` [PATCH v1 31/54] efi: loader: protocol: add unicode collation support Ahmad Fatoum
@ 2025-12-18 10:37 ` Ahmad Fatoum
2025-12-18 10:37 ` [PATCH v1 33/54] efi: loader: protocol: add device_path_utilities Ahmad Fatoum
` (21 subsequent siblings)
53 siblings, 0 replies; 55+ messages in thread
From: Ahmad Fatoum @ 2025-12-18 10:37 UTC (permalink / raw)
To: barebox; +Cc: Ahmad Fatoum
Make available the default random number generator in barebox to EFI
applications during boot time.
Signed-off-by: Ahmad Fatoum <a.fatoum@pengutronix.de>
---
efi/loader/protocols/Kconfig | 8 ++
efi/loader/protocols/Makefile | 1 +
efi/loader/protocols/rng.c | 165 ++++++++++++++++++++++++++++++++++
3 files changed, 174 insertions(+)
create mode 100644 efi/loader/protocols/rng.c
diff --git a/efi/loader/protocols/Kconfig b/efi/loader/protocols/Kconfig
index 8c8bfabd7c0f..7d5e203d121a 100644
--- a/efi/loader/protocols/Kconfig
+++ b/efi/loader/protocols/Kconfig
@@ -31,4 +31,12 @@ config EFI_LOADER_UNICODE_CAPITALIZATION
set, only the the correct handling of the letters of the codepage
used by the FAT file system is ensured.
+config EFI_LOADER_RNG
+ bool "EFI_RNG_PROTOCOL support"
+ depends on HWRNG
+ default y
+ help
+ Provide a EFI_RNG_PROTOCOL implementation using the hardware random
+ number generator of the platform.
+
endmenu
diff --git a/efi/loader/protocols/Makefile b/efi/loader/protocols/Makefile
index b6e39b0666da..ecc0fc392856 100644
--- a/efi/loader/protocols/Makefile
+++ b/efi/loader/protocols/Makefile
@@ -6,3 +6,4 @@ obj-$(CONFIG_VIDEO) += gop.o
obj-$(CONFIG_CONSOLE_FULL) += console.o
obj-$(CONFIG_EFI_LOADER_HII) += hii.o hii_config.o
obj-$(CONFIG_EFI_LOADER_UNICODE_COLLATION_PROTOCOL2) += unicode_collation.o
+obj-$(CONFIG_EFI_LOADER_RNG) += rng.o
diff --git a/efi/loader/protocols/rng.c b/efi/loader/protocols/rng.c
new file mode 100644
index 000000000000..9c3caa82f884
--- /dev/null
+++ b/efi/loader/protocols/rng.c
@@ -0,0 +1,165 @@
+// SPDX-License-Identifier: GPL-2.0+
+// SPDX-Comment: Origin-URL: https://github.com/u-boot/u-boot/blob/58da850c87240c397242ebd47341c69aa6278119/lib/efi_loader/efi_rng.c
+/*
+ * Copyright (c) 2019, Linaro Limited
+ */
+
+#include <linux/hw_random.h>
+#include <stdlib.h>
+#include <efi/loader.h>
+#include <efi/protocol/rng.h>
+#include <efi/devicepath.h>
+#include <efi/loader/devicepath.h>
+#include <efi/loader/object.h>
+#include <efi/loader/trace.h>
+#include <efi/guid.h>
+#include <efi/error.h>
+#include <xfuncs.h>
+
+struct efi_rng_obj {
+ struct efi_object header;
+ struct hwrng *hwrng;
+ struct efi_rng_protocol ops;
+};
+
+#define to_efi_rng_obj(this) container_of(this, struct efi_rng_obj, ops)
+
+/**
+ * rng_getinfo() - get information about random number generation
+ *
+ * This function implement the GetInfo() service of the EFI random number
+ * generator protocol. See the UEFI spec for details.
+ *
+ * @this: random number generator protocol instance
+ * @rng_algorithm_list_size: number of random number generation algorithms
+ * @rng_algorithm_list: descriptions of random number generation
+ * algorithms
+ * Return: status code
+ */
+static efi_status_t EFIAPI rng_getinfo(struct efi_rng_protocol *this,
+ efi_uintn_t *rng_algorithm_list_size,
+ efi_guid_t *rng_algorithm_list)
+{
+ efi_status_t ret = EFI_SUCCESS;
+ efi_guid_t rng_algo_guid = EFI_RNG_ALGORITHM_RAW;
+
+ EFI_ENTRY("%p, %p, %p", this, rng_algorithm_list_size,
+ rng_algorithm_list);
+
+ if (!this || !rng_algorithm_list_size) {
+ ret = EFI_INVALID_PARAMETER;
+ goto back;
+ }
+
+ if (!rng_algorithm_list ||
+ *rng_algorithm_list_size < sizeof(*rng_algorithm_list)) {
+ *rng_algorithm_list_size = sizeof(*rng_algorithm_list);
+ ret = EFI_BUFFER_TOO_SMALL;
+ goto back;
+ }
+
+ /*
+ * For now, use EFI_RNG_ALGORITHM_RAW as the default
+ * algorithm. If a new algorithm gets added in the
+ * future through a Kconfig, rng_algo_guid will be set
+ * based on that Kconfig option
+ */
+ *rng_algorithm_list_size = sizeof(*rng_algorithm_list);
+ *rng_algorithm_list = rng_algo_guid;
+
+back:
+ return EFI_EXIT(ret);
+}
+
+/**
+ * rng_getval() - get random value
+ *
+ * This function implement the GetRng() service of the EFI random number
+ * generator protocol. See the UEFI spec for details.
+ *
+ * @this: random number generator protocol instance
+ * @rng_algorithm: random number generation algorithm
+ * @rng_value_length: number of random bytes to generate, buffer length
+ * @rng_value: buffer to receive random bytes
+ * Return: status code
+ */
+static efi_status_t EFIAPI rng_getval(struct efi_rng_protocol *this,
+ efi_guid_t *rng_algorithm,
+ efi_uintn_t rng_value_length,
+ uint8_t *rng_value)
+{
+ int ret;
+ efi_status_t status = EFI_SUCCESS;
+ const efi_guid_t rng_raw_guid = EFI_RNG_ALGORITHM_RAW;
+ struct efi_rng_obj *rngobj = to_efi_rng_obj(this);
+
+ EFI_ENTRY("%p, %p, %zu, %p", this, rng_algorithm, rng_value_length,
+ rng_value);
+
+ if (!this || !rng_value || !rng_value_length) {
+ status = EFI_INVALID_PARAMETER;
+ goto back;
+ }
+
+ if (rng_algorithm) {
+ EFI_PRINT("RNG algorithm %pUl\n", rng_algorithm);
+ if (efi_guidcmp(*rng_algorithm, rng_raw_guid)) {
+ status = EFI_UNSUPPORTED;
+ goto back;
+ }
+ }
+
+ ret = hwrng_get_crypto_bytes(rngobj->hwrng, rng_value, rng_value_length);
+ if (ret < 0) {
+ EFI_PRINT("Rng device read failed\n");
+ status = EFI_DEVICE_ERROR;
+ goto back;
+ }
+
+back:
+ return EFI_EXIT(status);
+}
+
+/**
+ * efi_rng_register() - register EFI_RNG_PROTOCOL
+ *
+ * If a HWRNG device is available, the Random Number Generator Protocol is
+ * registered.
+ *
+ * Return: An EFI error status
+ */
+static efi_status_t efi_rng_register(void *data)
+{
+ struct efi_rng_obj *rngobj;
+ struct hwrng *rng;
+ struct efi_device_path *dp;
+ efi_handle_t handle;
+
+ rng = hwrng_get_first();
+ if (IS_ERR(rng))
+ return EFI_UNSUPPORTED;
+
+ rngobj = xzalloc(sizeof(*rngobj));
+
+ rngobj->hwrng = rng;
+ rngobj->ops.get_info = rng_getinfo;
+ rngobj->ops.get_rng = rng_getval;
+
+ dp = efi_dp_from_cdev(&rng->cdev, true);
+
+ efi_add_handle(&rngobj->header);
+
+ handle = &rngobj->header;
+
+ return efi_install_multiple_protocol_interfaces(&handle,
+ &efi_device_path_protocol_guid, dp,
+ &efi_rng_protocol_guid, &rngobj->ops,
+ NULL);
+}
+
+static int efi_rng_init(void)
+{
+ efi_register_deferred_init(efi_rng_register, NULL);
+ return 0;
+}
+device_initcall(efi_rng_init);
--
2.47.3
^ permalink raw reply [flat|nested] 55+ messages in thread* [PATCH v1 33/54] efi: loader: protocol: add device_path_utilities
2025-12-18 10:37 [PATCH v1 00/54] efi: implement EFI loader support in barebox Ahmad Fatoum
` (31 preceding siblings ...)
2025-12-18 10:37 ` [PATCH v1 32/54] efi: loader: protocol: add random number generator protocol Ahmad Fatoum
@ 2025-12-18 10:37 ` Ahmad Fatoum
2025-12-18 10:37 ` [PATCH v1 34/54] efi: loader: support formatting only first device path node to text Ahmad Fatoum
` (20 subsequent siblings)
53 siblings, 0 replies; 55+ messages in thread
From: Ahmad Fatoum @ 2025-12-18 10:37 UTC (permalink / raw)
To: barebox; +Cc: Ahmad Fatoum
This protocol doesn't do much in barebox, but enough to be able to
execute the UEFI shell.
Signed-off-by: Ahmad Fatoum <a.fatoum@pengutronix.de>
---
efi/loader/protocols/Kconfig | 7 +
efi/loader/protocols/Makefile | 1 +
efi/loader/protocols/device_path_utilities.c | 210 +++++++++++++++++++
include/efi/protocol/devicepath.h | 31 +++
4 files changed, 249 insertions(+)
create mode 100644 efi/loader/protocols/device_path_utilities.c
create mode 100644 include/efi/protocol/devicepath.h
diff --git a/efi/loader/protocols/Kconfig b/efi/loader/protocols/Kconfig
index 7d5e203d121a..3405de1acdfb 100644
--- a/efi/loader/protocols/Kconfig
+++ b/efi/loader/protocols/Kconfig
@@ -39,4 +39,11 @@ config EFI_LOADER_RNG
Provide a EFI_RNG_PROTOCOL implementation using the hardware random
number generator of the platform.
+config EFI_LOADER_DEVICE_PATH_UTIL
+ bool "Device path utilities protocol"
+ default y
+ help
+ The device path utilities protocol creates and manipulates device
+ paths and device nodes. It is required to run the EFI Shell.
+
endmenu
diff --git a/efi/loader/protocols/Makefile b/efi/loader/protocols/Makefile
index ecc0fc392856..b5ec5cf5dd46 100644
--- a/efi/loader/protocols/Makefile
+++ b/efi/loader/protocols/Makefile
@@ -7,3 +7,4 @@ obj-$(CONFIG_CONSOLE_FULL) += console.o
obj-$(CONFIG_EFI_LOADER_HII) += hii.o hii_config.o
obj-$(CONFIG_EFI_LOADER_UNICODE_COLLATION_PROTOCOL2) += unicode_collation.o
obj-$(CONFIG_EFI_LOADER_RNG) += rng.o
+obj-$(CONFIG_EFI_LOADER_DEVICE_PATH_UTIL) += device_path_utilities.o
diff --git a/efi/loader/protocols/device_path_utilities.c b/efi/loader/protocols/device_path_utilities.c
new file mode 100644
index 000000000000..ef88e2d647fb
--- /dev/null
+++ b/efi/loader/protocols/device_path_utilities.c
@@ -0,0 +1,210 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * EFI device path interface
+ *
+ * Copyright (c) 2017 Leif Lindholm
+ */
+
+#include <efi/loader.h>
+#include <efi/devicepath.h>
+#include <efi/loader/devicepath.h>
+#include <efi/protocol/devicepath.h>
+#include <efi/loader/trace.h>
+#include <efi/loader/object.h>
+#include <efi/guid.h>
+#include <init.h>
+
+/*
+ * Get size of a device path.
+ *
+ * This function implements the GetDevicePathSize service of the device path
+ * utilities protocol. The device path length includes the end of path tag
+ * which may be an instance end.
+ *
+ * See the Unified Extensible Firmware Interface (UEFI) specification
+ * for details.
+ *
+ * @device_path device path
+ * Return: size in bytes
+ */
+static efi_uintn_t EFIAPI get_device_path_size(
+ const struct efi_device_path *device_path)
+{
+ efi_uintn_t sz = 0;
+
+ EFI_ENTRY("%pD", device_path);
+ /* size includes the END node: */
+ if (device_path)
+ sz = efi_dp_size(device_path) + sizeof(struct efi_device_path);
+ return EFI_EXIT(sz);
+}
+
+/*
+ * Duplicate a device path.
+ *
+ * This function implements the DuplicateDevicePath service of the device path
+ * utilities protocol.
+ *
+ * The UEFI spec does not indicate what happens to the end tag. We follow the
+ * EDK2 logic: In case the device path ends with an end of instance tag, the
+ * copy will also end with an end of instance tag.
+ *
+ * See the Unified Extensible Firmware Interface (UEFI) specification
+ * for details.
+ *
+ * @device_path device path
+ * Return: copy of the device path
+ */
+static struct efi_device_path * EFIAPI duplicate_device_path(
+ const struct efi_device_path *device_path)
+{
+ EFI_ENTRY("%pD", device_path);
+ return EFI_EXIT(efi_dp_dup(device_path));
+}
+
+/*
+ * Append device path.
+ *
+ * This function implements the AppendDevicePath service of the device path
+ * utilities protocol.
+ *
+ * See the Unified Extensible Firmware Interface (UEFI) specification
+ * for details.
+ *
+ * @src1 1st device path
+ * @src2 2nd device path
+ * Return: concatenated device path
+ */
+static struct efi_device_path * EFIAPI append_device_path(
+ const struct efi_device_path *src1,
+ const struct efi_device_path *src2)
+{
+ EFI_ENTRY("%pD, %pD", src1, src2);
+ return EFI_EXIT(efi_dp_concat(src1, src2, 0));
+}
+
+/*
+ * Append device path node.
+ *
+ * This function implements the AppendDeviceNode service of the device path
+ * utilities protocol.
+ *
+ * See the Unified Extensible Firmware Interface (UEFI) specification
+ * for details.
+ *
+ * @device_path device path
+ * @device_node device node
+ * Return: concatenated device path
+ */
+static struct efi_device_path * EFIAPI append_device_node(
+ const struct efi_device_path *device_path,
+ const struct efi_device_path *device_node)
+{
+ EFI_ENTRY("%pD, %p", device_path, device_node);
+ return EFI_EXIT(efi_dp_append_node(device_path, device_node));
+}
+
+/*
+ * Append device path instance.
+ *
+ * This function implements the AppendDevicePathInstance service of the device
+ * path utilities protocol.
+ *
+ * See the Unified Extensible Firmware Interface (UEFI) specification
+ * for details.
+ *
+ * @device_path 1st device path
+ * @device_path_instance 2nd device path
+ * Return: concatenated device path
+ */
+static struct efi_device_path * EFIAPI append_device_path_instance(
+ const struct efi_device_path *device_path,
+ const struct efi_device_path *device_path_instance)
+{
+ EFI_ENTRY("%pD, %pD", device_path, device_path_instance);
+ return EFI_EXIT(efi_dp_append_instance(device_path,
+ device_path_instance));
+}
+
+/*
+ * Get next device path instance.
+ *
+ * This function implements the GetNextDevicePathInstance service of the device
+ * path utilities protocol.
+ *
+ * See the Unified Extensible Firmware Interface (UEFI) specification
+ * for details.
+ *
+ * @device_path_instance next device path instance
+ * @device_path_instance_size size of the device path instance
+ * Return: concatenated device path
+ */
+static struct efi_device_path * EFIAPI get_next_device_path_instance(
+ struct efi_device_path **device_path_instance,
+ efi_uintn_t *device_path_instance_size)
+{
+ EFI_ENTRY("%pD, %p", device_path_instance, device_path_instance_size);
+ return EFI_EXIT(efi_dp_get_next_instance(device_path_instance,
+ device_path_instance_size));
+}
+
+/*
+ * Check if a device path contains more than one instance.
+ *
+ * This function implements the AppendDeviceNode service of the device path
+ * utilities protocol.
+ *
+ * See the Unified Extensible Firmware Interface (UEFI) specification
+ * for details.
+ *
+ * @device_path device path
+ * @device_node device node
+ * Return: concatenated device path
+ */
+static bool EFIAPI is_device_path_multi_instance(
+ const struct efi_device_path *device_path)
+{
+ EFI_ENTRY("%pD", device_path);
+ return EFI_EXIT(efi_dp_is_multi_instance(device_path));
+}
+
+/*
+ * Create device node.
+ *
+ * This function implements the CreateDeviceNode service of the device path
+ * utilities protocol.
+ *
+ * See the Unified Extensible Firmware Interface (UEFI) specification
+ * for details.
+ *
+ * @node_type node type
+ * @node_sub_type node sub type
+ * @node_length node length
+ * Return: device path node
+ */
+static struct efi_device_path * EFIAPI create_device_node(
+ uint8_t node_type, uint8_t node_sub_type, uint16_t node_length)
+{
+ EFI_ENTRY("%u, %u, %u", node_type, node_sub_type, node_length);
+ return EFI_EXIT(efi_dp_create_device_node(node_type, node_sub_type,
+ node_length));
+}
+
+static const struct efi_device_path_utilities_protocol efi_device_path_utilities = {
+ .get_device_path_size = get_device_path_size,
+ .duplicate_device_path = duplicate_device_path,
+ .append_device_path = append_device_path,
+ .append_device_node = append_device_node,
+ .append_device_path_instance = append_device_path_instance,
+ .get_next_device_path_instance = get_next_device_path_instance,
+ .is_device_path_multi_instance = is_device_path_multi_instance,
+ .create_device_node = create_device_node,
+};
+
+static int efi_device_path_utilities_init(void)
+{
+ efi_add_root_node_protocol_deferred(&efi_device_path_utilities_protocol_guid,
+ &efi_device_path_utilities);
+ return 0;
+}
+postcore_initcall(efi_device_path_utilities_init);
diff --git a/include/efi/protocol/devicepath.h b/include/efi/protocol/devicepath.h
new file mode 100644
index 000000000000..19a35888f9cc
--- /dev/null
+++ b/include/efi/protocol/devicepath.h
@@ -0,0 +1,31 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+#ifndef __EFI_PROTOCOL_DEVICE_PATH_H
+#define __EFI_PROTOCOL_DEVICE_PATH_H
+
+#include <efi/types.h>
+
+struct efi_device_path_utilities_protocol {
+ size_t (EFIAPI *get_device_path_size)(
+ const struct efi_device_path *device_path);
+ struct efi_device_path *(EFIAPI *duplicate_device_path)(
+ const struct efi_device_path *device_path);
+ struct efi_device_path *(EFIAPI *append_device_path)(
+ const struct efi_device_path *src1,
+ const struct efi_device_path *src2);
+ struct efi_device_path *(EFIAPI *append_device_node)(
+ const struct efi_device_path *device_path,
+ const struct efi_device_path *device_node);
+ struct efi_device_path *(EFIAPI *append_device_path_instance)(
+ const struct efi_device_path *device_path,
+ const struct efi_device_path *device_path_instance);
+ struct efi_device_path *(EFIAPI *get_next_device_path_instance)(
+ struct efi_device_path **device_path_instance,
+ size_t *device_path_instance_size);
+ bool (EFIAPI *is_device_path_multi_instance)(
+ const struct efi_device_path *device_path);
+ struct efi_device_path *(EFIAPI *create_device_node)(
+ uint8_t node_type,
+ uint8_t node_sub_type,
+ uint16_t node_length);
+};
+#endif
--
2.47.3
^ permalink raw reply [flat|nested] 55+ messages in thread* [PATCH v1 34/54] efi: loader: support formatting only first device path node to text
2025-12-18 10:37 [PATCH v1 00/54] efi: implement EFI loader support in barebox Ahmad Fatoum
` (32 preceding siblings ...)
2025-12-18 10:37 ` [PATCH v1 33/54] efi: loader: protocol: add device_path_utilities Ahmad Fatoum
@ 2025-12-18 10:37 ` Ahmad Fatoum
2025-12-18 10:37 ` [PATCH v1 35/54] efi: loader: protocol: add efi_device_path_to_text support Ahmad Fatoum
` (19 subsequent siblings)
53 siblings, 0 replies; 55+ messages in thread
From: Ahmad Fatoum @ 2025-12-18 10:37 UTC (permalink / raw)
To: barebox; +Cc: Ahmad Fatoum
For use in the follow-up efi_device_path_to_text, teach
device_path_to_str_buf() to support formatting the first node only.
Signed-off-by: Ahmad Fatoum <a.fatoum@pengutronix.de>
---
commands/efi_handle_dump.c | 2 +-
drivers/efi/efi-device.c | 2 +-
efi/devicepath.c | 17 +++++++++++------
efi/loader/devicepath.c | 4 ++--
efi/payload/init.c | 2 +-
include/efi/devicepath.h | 5 +++--
lib/vsprintf.c | 2 +-
7 files changed, 20 insertions(+), 14 deletions(-)
diff --git a/commands/efi_handle_dump.c b/commands/efi_handle_dump.c
index c5c6b6bbc3bc..183a7d4bc3a6 100644
--- a/commands/efi_handle_dump.c
+++ b/commands/efi_handle_dump.c
@@ -28,7 +28,7 @@ static void efi_devpath(struct efi_boot_services *bs,
if (EFI_ERROR(efiret))
return;
- dev_path_str = device_path_to_str(devpath);
+ dev_path_str = device_path_to_str(devpath, true);
if (dev_path_str) {
printf(" %s: \n %s\n", desc, dev_path_str);
free(dev_path_str);
diff --git a/drivers/efi/efi-device.c b/drivers/efi/efi-device.c
index 6bf43cebea8d..490faff6f53d 100644
--- a/drivers/efi/efi-device.c
+++ b/drivers/efi/efi-device.c
@@ -196,7 +196,7 @@ static int efi_register_device(struct efi_device *efidev)
if (ret)
return ret;
- dev_path_str = device_path_to_str(efidev->devpath);
+ dev_path_str = device_path_to_str(efidev->devpath, true);
if (dev_path_str) {
dev_add_param_fixed(&efidev->dev, "devpath", "%s", dev_path_str);
free(dev_path_str);
diff --git a/efi/devicepath.c b/efi/devicepath.c
index e4170d701e47..8298073572ae 100644
--- a/efi/devicepath.c
+++ b/efi/devicepath.c
@@ -674,7 +674,7 @@ struct {
};
static void __device_path_to_str(struct string *str,
- const struct efi_device_path *dev_path)
+ const struct efi_device_path *dev_path, bool all_nodes)
{
const struct efi_device_path *dev_path_node;
void (*dump_node) (struct string *, const void *);
@@ -702,6 +702,9 @@ static void __device_path_to_str(struct string *str,
dump_node(str, dev_path_node);
+ if (!all_nodes)
+ return;
+
dev_path_node = next_device_path_node(dev_path_node);
}
}
@@ -712,19 +715,20 @@ static void __device_path_to_str(struct string *str,
* @dev_path: The EFI device path to format
* @buf: The buffer to format into or optionally NULL if @len is zero
* @len: The number of bytes that may be written into @buf
+ * @all_nodes: Whether to format the whole path or only the first node
* Return: total number of bytes that are required to store the formatted
* result, excluding the terminating NUL byte, which is always
* written.
*/
size_t device_path_to_str_buf(const struct efi_device_path *dev_path,
- char *buf, size_t len)
+ char *buf, size_t len, bool all_nodes)
{
struct string str = {
.str = buf,
.allocated = len,
};
- __device_path_to_str(&str, dev_path);
+ __device_path_to_str(&str, dev_path, all_nodes);
return str.used;
}
@@ -733,20 +737,21 @@ size_t device_path_to_str_buf(const struct efi_device_path *dev_path,
* device_path_to_str() - formats a device path into a newly allocated buffer
*
* @dev_path: The EFI device path to format
+ * @all_nodes: Whether to format the whole path or only the first node
* Return: A pointer to the nul-terminated formatted device path.
*/
-char *device_path_to_str(const struct efi_device_path *dev_path)
+char *device_path_to_str(const struct efi_device_path *dev_path, bool all_nodes)
{
void *buf;
size_t size;
- size = device_path_to_str_buf(dev_path, NULL, 0);
+ size = device_path_to_str_buf(dev_path, NULL, 0, all_nodes);
buf = malloc(size + 1);
if (!buf)
return NULL;
- device_path_to_str_buf(dev_path, buf, size + 1);
+ device_path_to_str_buf(dev_path, buf, size + 1, all_nodes);
return buf;
}
diff --git a/efi/loader/devicepath.c b/efi/loader/devicepath.c
index cd783477ef3e..17c659e4c5d8 100644
--- a/efi/loader/devicepath.c
+++ b/efi/loader/devicepath.c
@@ -1050,7 +1050,7 @@ struct efi_device_path *efi_dp_from_file(int dirfd, const char *path)
/**
* efi_dp_from_file_tostr() - format barebox VFS path as EFI device path string
*
- * Convert any barebox virtual file system path to an UEFI device path
+ * Convert any barebox virtual file system path to a full UEFI device path
* and then return its full string representation.
*
* @dirfd: directory fd
@@ -1065,7 +1065,7 @@ char *efi_dp_from_file_tostr(int dirfd, const char *path)
dp = efi_dp_from_file(dirfd, path);
if (dp)
- dpstr = device_path_to_str(dp);
+ dpstr = device_path_to_str(dp, true);
efi_free_pool(dp);
diff --git a/efi/payload/init.c b/efi/payload/init.c
index 7e361bab8ab3..d8a4410f22e6 100644
--- a/efi/payload/init.c
+++ b/efi/payload/init.c
@@ -94,7 +94,7 @@ static struct efi_boot *efi_get_boot(int num)
boot->path = memdup(ptr, boot->file_path_len);
- printf("path: %s\n", device_path_to_str(boot->path));
+ printf("path: %s\n", device_path_to_str(boot->path, true));
return boot;
}
diff --git a/include/efi/devicepath.h b/include/efi/devicepath.h
index 405f4446310e..125f114bd846 100644
--- a/include/efi/devicepath.h
+++ b/include/efi/devicepath.h
@@ -397,8 +397,9 @@ struct efi_device_path_bbs_bbs {
#define DEVICE_PATH_END_LENGTH (sizeof(struct efi_device_path))
const struct efi_device_path *device_path_from_handle(efi_handle_t handle);
-char *device_path_to_str(const struct efi_device_path *dev_path);
-size_t device_path_to_str_buf(const struct efi_device_path *dev_path, char buf[], size_t size);
+char *device_path_to_str(const struct efi_device_path *dev_path, bool all_nodes);
+size_t device_path_to_str_buf(const struct efi_device_path *dev_path,
+ char buf[], size_t size, bool all_nodes);
u8 device_path_to_type(const struct efi_device_path *dev_path);
u8 device_path_to_subtype(const struct efi_device_path *dev_path);
char *device_path_to_partuuid(const struct efi_device_path *dev_path);
diff --git a/lib/vsprintf.c b/lib/vsprintf.c
index 5ef4a075d0f6..6c9dae467496 100644
--- a/lib/vsprintf.c
+++ b/lib/vsprintf.c
@@ -395,7 +395,7 @@ static char *device_path_string(char *buf, const char *end, const struct efi_dev
if (!dp)
return string(buf, end, NULL, field_width, precision, flags);
- return buf + device_path_to_str_buf(dp, buf, end - buf);
+ return buf + device_path_to_str_buf(dp, buf, end - buf, true);
}
static noinline_for_stack
--
2.47.3
^ permalink raw reply [flat|nested] 55+ messages in thread* [PATCH v1 35/54] efi: loader: protocol: add efi_device_path_to_text support
2025-12-18 10:37 [PATCH v1 00/54] efi: implement EFI loader support in barebox Ahmad Fatoum
` (33 preceding siblings ...)
2025-12-18 10:37 ` [PATCH v1 34/54] efi: loader: support formatting only first device path node to text Ahmad Fatoum
@ 2025-12-18 10:37 ` Ahmad Fatoum
2025-12-18 10:37 ` [PATCH v1 36/54] restart: allow drivers to register runtime restart handler Ahmad Fatoum
` (18 subsequent siblings)
53 siblings, 0 replies; 55+ messages in thread
From: Ahmad Fatoum @ 2025-12-18 10:37 UTC (permalink / raw)
To: barebox; +Cc: Ahmad Fatoum
For use by EFI payloads that don't want to convert device paths into
strings themselves, the loader can provide a protocol with access to its
own routines. device_path_to_str(), which we already in the EFI payload
can fill that role, so add a protocol that calls into it.
Signed-off-by: Ahmad Fatoum <a.fatoum@pengutronix.de>
---
efi/loader/protocols/Kconfig | 7 ++
efi/loader/protocols/Makefile | 1 +
efi/loader/protocols/device_path_to_text.c | 130 +++++++++++++++++++++
include/efi/protocol/devicepath.h | 11 ++
4 files changed, 149 insertions(+)
create mode 100644 efi/loader/protocols/device_path_to_text.c
diff --git a/efi/loader/protocols/Kconfig b/efi/loader/protocols/Kconfig
index 3405de1acdfb..ba6f562b7585 100644
--- a/efi/loader/protocols/Kconfig
+++ b/efi/loader/protocols/Kconfig
@@ -39,6 +39,13 @@ config EFI_LOADER_RNG
Provide a EFI_RNG_PROTOCOL implementation using the hardware random
number generator of the platform.
+config EFI_LOADER_DEVICE_PATH_TO_TEXT
+ bool "Device path to text protocol"
+ default y
+ help
+ The device path to text protocol converts device nodes and paths to
+ human readable strings.
+
config EFI_LOADER_DEVICE_PATH_UTIL
bool "Device path utilities protocol"
default y
diff --git a/efi/loader/protocols/Makefile b/efi/loader/protocols/Makefile
index b5ec5cf5dd46..a323927b89e3 100644
--- a/efi/loader/protocols/Makefile
+++ b/efi/loader/protocols/Makefile
@@ -8,3 +8,4 @@ obj-$(CONFIG_EFI_LOADER_HII) += hii.o hii_config.o
obj-$(CONFIG_EFI_LOADER_UNICODE_COLLATION_PROTOCOL2) += unicode_collation.o
obj-$(CONFIG_EFI_LOADER_RNG) += rng.o
obj-$(CONFIG_EFI_LOADER_DEVICE_PATH_UTIL) += device_path_utilities.o
+obj-$(CONFIG_EFI_LOADER_DEVICE_PATH_TO_TEXT) += device_path_to_text.o
diff --git a/efi/loader/protocols/device_path_to_text.c b/efi/loader/protocols/device_path_to_text.c
new file mode 100644
index 000000000000..d2907915469c
--- /dev/null
+++ b/efi/loader/protocols/device_path_to_text.c
@@ -0,0 +1,130 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * EFI device path interface
+ *
+ * Copyright (c) 2017 Heinrich Schuchardt
+ */
+
+#include <charset.h>
+#include <efi/devicepath.h>
+#include <efi/loader/devicepath.h>
+#include <efi/protocol/devicepath.h>
+#include <efi/loader/object.h>
+#include <efi/loader/trace.h>
+#include <efi/loader.h>
+#include <efi/guid.h>
+#include <efi/error.h>
+#include <init.h>
+
+/**
+ * efi_str_to_u16() - convert ASCII string to UTF-16
+ *
+ * A u16 buffer is allocated from pool. The ASCII string is copied to the u16
+ * buffer.
+ *
+ * @str: ASCII string
+ * Return: UTF-16 string. NULL if out of memory.
+ */
+static u16 *efi_str_to_u16(const char *str)
+{
+ size_t len;
+ u16 *out, *dst;
+ efi_status_t ret;
+
+ if (!str)
+ return NULL;
+
+ len = sizeof(u16) * (utf8_utf16_strlen(str) + 1);
+ ret = efi_allocate_pool(EFI_BOOT_SERVICES_DATA, len, (void **)&out, "DP");
+ if (ret != EFI_SUCCESS)
+ return NULL;
+ dst = out;
+ utf8_utf16_strcpy(&dst, str);
+ return out;
+}
+
+static uint16_t EFIAPI *__efi_convert_device_path_to_text(
+ const struct efi_device_path *dp,
+ bool all_nodes)
+{
+ char *str;
+ uint16_t *text;
+
+ if (!dp)
+ return NULL;
+
+ str = device_path_to_str(dp, all_nodes);
+ text = efi_str_to_u16(str);
+ free(str);
+
+ return text;
+}
+
+/*
+ * This function implements the ConvertDeviceNodeToText service of the
+ * EFI_DEVICE_PATH_TO_TEXT_PROTOCOL.
+ * See the Unified Extensible Firmware Interface (UEFI) specification
+ * for details.
+ *
+ * device_node device node to be converted
+ * display_only true if the shorter text representation shall be used
+ * allow_shortcuts true if shortcut forms may be used
+ * Return: text representation of the device path
+ * NULL if out of memory of device_path is NULL
+ */
+static uint16_t EFIAPI *efi_convert_device_node_to_text(
+ const struct efi_device_path *device_node,
+ bool display_only,
+ bool allow_shortcuts)
+{
+ uint16_t *text;
+
+ EFI_ENTRY("%p, %d, %d", device_node, display_only, allow_shortcuts);
+
+ text = __efi_convert_device_path_to_text(device_node, false);
+
+ EFI_EXIT(EFI_SUCCESS);
+
+ return text;
+}
+
+/*
+ * This function implements the ConvertDevicePathToText service of the
+ * EFI_DEVICE_PATH_TO_TEXT_PROTOCOL.
+ * See the Unified Extensible Firmware Interface (UEFI) specification
+ * for details.
+ *
+ * device_path device path to be converted
+ * display_only true if the shorter text representation shall be used
+ * allow_shortcuts true if shortcut forms may be used
+ * Return: text representation of the device path
+ * NULL if out of memory of device_path is NULL
+ */
+static uint16_t EFIAPI *efi_convert_device_path_to_text(
+ const struct efi_device_path *device_path,
+ bool display_only,
+ bool allow_shortcuts)
+{
+ uint16_t *text;
+
+ EFI_ENTRY("%p, %d, %d", device_path, display_only, allow_shortcuts);
+
+ text = __efi_convert_device_path_to_text(device_path, true);
+
+ EFI_EXIT(EFI_SUCCESS);
+
+ return text;
+}
+
+static const struct efi_device_path_to_text_protocol efi_device_path_to_text = {
+ .convert_device_node_to_text = efi_convert_device_node_to_text,
+ .convert_device_path_to_text = efi_convert_device_path_to_text,
+};
+
+static int efi_device_path_to_text_init(void)
+{
+ efi_add_root_node_protocol_deferred(&efi_device_path_to_text_protocol_guid,
+ &efi_device_path_to_text);
+ return 0;
+}
+postcore_initcall(efi_device_path_to_text_init);
diff --git a/include/efi/protocol/devicepath.h b/include/efi/protocol/devicepath.h
index 19a35888f9cc..ca0cbd94d5ce 100644
--- a/include/efi/protocol/devicepath.h
+++ b/include/efi/protocol/devicepath.h
@@ -4,6 +4,17 @@
#include <efi/types.h>
+struct efi_device_path_to_text_protocol {
+ uint16_t *(EFIAPI *convert_device_node_to_text)(
+ const struct efi_device_path *device_node,
+ bool display_only,
+ bool allow_shortcuts);
+ uint16_t *(EFIAPI *convert_device_path_to_text)(
+ const struct efi_device_path *device_path,
+ bool display_only,
+ bool allow_shortcuts);
+};
+
struct efi_device_path_utilities_protocol {
size_t (EFIAPI *get_device_path_size)(
const struct efi_device_path *device_path);
--
2.47.3
^ permalink raw reply [flat|nested] 55+ messages in thread* [PATCH v1 36/54] restart: allow drivers to register runtime restart handler
2025-12-18 10:37 [PATCH v1 00/54] efi: implement EFI loader support in barebox Ahmad Fatoum
` (34 preceding siblings ...)
2025-12-18 10:37 ` [PATCH v1 35/54] efi: loader: protocol: add efi_device_path_to_text support Ahmad Fatoum
@ 2025-12-18 10:37 ` Ahmad Fatoum
2025-12-18 10:37 ` [PATCH v1 37/54] poweroff: allow drivers to register runtime poweroff handler Ahmad Fatoum
` (17 subsequent siblings)
53 siblings, 0 replies; 55+ messages in thread
From: Ahmad Fatoum @ 2025-12-18 10:37 UTC (permalink / raw)
To: barebox; +Cc: Ahmad Fatoum, Ahmad Fatoum
From: Ahmad Fatoum <a.fatoum@barebox.org>
A runtime restart handler needs to be usable from within the barebox EFI
runtime services, even when everything else in barebox has been
discarded and barebox no longer has exclusive access to peripherals.
This driver allows existing drivers for boot time barebox to probe as
normal and optionally register a runtime restart handler, which is
suitable for being called from within EFI runtime code.
Signed-off-by: Ahmad Fatoum <a.fatoum@pengutronix.de>
---
common/restart.c | 46 ++++++++++++++++++++++++++++++++++++++++++++++
include/restart.h | 3 +++
2 files changed, 49 insertions(+)
diff --git a/common/restart.c b/common/restart.c
index d0ceeab7acd0..391d7bd3773c 100644
--- a/common/restart.c
+++ b/common/restart.c
@@ -7,11 +7,42 @@
#include <common.h>
#include <restart.h>
#include <malloc.h>
+#include <asm/sections.h>
+#include <efi/types.h>
#include <of.h>
static LIST_HEAD(restart_handler_list);
static unsigned resetidx;
+static __efi_runtime_data void (*rt_restart)(unsigned long flags);
+static int rt_restart_prio = INT_MIN;
+static __efi_runtime_data void (*rt_restart_warm)(unsigned long flags);
+static int rt_restart_warm_prio = INT_MIN;
+
+static void rt_restart_handler_register(struct restart_handler *handler)
+{
+ if (!IS_ENABLED(CONFIG_EFI_RUNTIME))
+ return;
+ if (!handler->rt_restart)
+ return;
+ if (!in_barebox_efi_runtime((ulong)handler->rt_restart)) {
+ /* Check if __efi_runtime attribute is missing */
+ pr_warn("handler outside EFI runtime section\n");
+ return;
+ }
+
+ if (handler->priority > rt_restart_prio) {
+ rt_restart = handler->rt_restart;
+ rt_restart_prio = handler->priority;
+ }
+
+ if (handler->priority > rt_restart_warm_prio &&
+ handler->flags & RESTART_WARM) {
+ rt_restart_warm = handler->rt_restart;
+ rt_restart_warm_prio = handler->priority;
+ }
+}
+
/**
* restart_handler_register() - register a handler for restarting the system
* @rst: The handler struct
@@ -35,6 +66,8 @@ int restart_handler_register(struct restart_handler *rst)
rst->flags |= RESTART_WARM;
}
+ rt_restart_handler_register(rst);
+
list_add_tail(&rst->list, &restart_handler_list);
pr_debug("registering restart handler \"%s\" with priority %d\n",
@@ -100,6 +133,19 @@ struct restart_handler *restart_handler_get_by_name(const char *name, int flags)
return rst;
}
+/**
+ * rt_restart_machine() - reset the machine from a runtime service
+ */
+void __noreturn __efi_runtime rt_restart_machine(unsigned long flags)
+{
+ if ((flags & RESTART_WARM) && rt_restart_warm)
+ rt_restart_warm(flags);
+ if (rt_restart)
+ rt_restart(flags);
+
+ __hang();
+}
+
/**
* restart_machine() - reset the whole system
*/
diff --git a/include/restart.h b/include/restart.h
index 5414fc1fcb69..cafddc14c656 100644
--- a/include/restart.h
+++ b/include/restart.h
@@ -5,11 +5,13 @@
#include <linux/compiler.h>
#include <linux/types.h>
#include <linux/bitops.h>
+#include <efi/types.h>
struct device_node;
void restart_handlers_print(void);
#define RESTART_WARM BIT(0)
+void __noreturn __efi_runtime rt_restart_machine(unsigned long flags);
void __noreturn restart_machine(unsigned long restart_flags);
struct restart_handler *restart_handler_get_by_name(const char *name, int flags);
@@ -17,6 +19,7 @@ struct device_node;
struct restart_handler {
void (*restart)(struct restart_handler *, unsigned long);
+ void (*rt_restart)(unsigned long);
int priority;
int flags;
struct device_node *of_node;
--
2.47.3
^ permalink raw reply [flat|nested] 55+ messages in thread* [PATCH v1 37/54] poweroff: allow drivers to register runtime poweroff handler
2025-12-18 10:37 [PATCH v1 00/54] efi: implement EFI loader support in barebox Ahmad Fatoum
` (35 preceding siblings ...)
2025-12-18 10:37 ` [PATCH v1 36/54] restart: allow drivers to register runtime restart handler Ahmad Fatoum
@ 2025-12-18 10:37 ` Ahmad Fatoum
2025-12-18 10:37 ` [PATCH v1 38/54] ARM: psci: client: register runtime service " Ahmad Fatoum
` (16 subsequent siblings)
53 siblings, 0 replies; 55+ messages in thread
From: Ahmad Fatoum @ 2025-12-18 10:37 UTC (permalink / raw)
To: barebox; +Cc: Ahmad Fatoum
A runtime poweroff handler needs to be usable from within the barebox EFI
runtime services, even when everything else in barebox has been
discarded and barebox no longer has exclusive access to peripherals.
This driver allows existing drivers for boot time barebox to probe as
normal and optionally register a runtime poweroff handler, which is
suitable for being called from within EFI runtime code.
Signed-off-by: Ahmad Fatoum <a.fatoum@pengutronix.de>
---
common/poweroff.c | 36 ++++++++++++++++++++++++++++++++++++
include/poweroff.h | 2 ++
2 files changed, 38 insertions(+)
diff --git a/common/poweroff.c b/common/poweroff.c
index 896716bb258c..dfbd727b969c 100644
--- a/common/poweroff.c
+++ b/common/poweroff.c
@@ -7,10 +7,33 @@
#include <common.h>
#include <poweroff.h>
#include <malloc.h>
+#include <efi/types.h>
+#include <asm/sections.h>
#include <of.h>
static LIST_HEAD(poweroff_handler_list);
+static __efi_runtime_data void (*rt_poweroff)(unsigned long flags);
+static int rt_poweroff_prio = INT_MIN;
+
+static void rt_poweroff_handler_register(struct poweroff_handler *handler)
+{
+ if (!IS_ENABLED(CONFIG_EFI_RUNTIME))
+ return;
+ if (!handler->rt_poweroff)
+ return;
+ if (!in_barebox_efi_runtime((ulong)handler->rt_poweroff)) {
+ /* Check if __efi_runtime attribute is missing */
+ pr_warn("handler outside EFI runtime section\n");
+ return;
+ }
+ if (handler->priority <= rt_poweroff_prio)
+ return;
+
+ rt_poweroff = handler->rt_poweroff;
+ rt_poweroff_prio = handler->priority;
+}
+
/**
* poweroff_handler_register() - register a handler for poweroffing the system
* @rst: The handler struct
@@ -26,6 +49,8 @@ int poweroff_handler_register(struct poweroff_handler *handler)
if (!handler->priority)
handler->priority = POWEROFF_DEFAULT_PRIORITY;
+ rt_poweroff_handler_register(handler);
+
list_add_tail(&handler->list, &poweroff_handler_list);
pr_debug("registering poweroff handler \"%s\" with priority %d\n",
@@ -61,6 +86,17 @@ int poweroff_handler_register_fn(void (*poweroff_fn)(struct poweroff_handler *,
return ret;
}
+/**
+ * rt_poweroff_machine() - power off the machine from a runtime service
+ */
+void __noreturn __efi_runtime rt_poweroff_machine(unsigned long flags)
+{
+ if (rt_poweroff)
+ rt_poweroff(flags);
+
+ __hang();
+}
+
/**
* poweroff_machine() - power off the machine
*/
diff --git a/include/poweroff.h b/include/poweroff.h
index 3e86003563a9..d8b2856ced9f 100644
--- a/include/poweroff.h
+++ b/include/poweroff.h
@@ -6,10 +6,12 @@
#include <linux/types.h>
void __noreturn poweroff_machine(unsigned long poweroff_flags);
+void __noreturn rt_poweroff_machine(unsigned long poweroff_flags);
struct poweroff_handler {
void (*poweroff)(struct poweroff_handler *,
unsigned long flags);
+ void (*rt_poweroff)(unsigned long flags);
int priority;
const char *name;
struct list_head list;
--
2.47.3
^ permalink raw reply [flat|nested] 55+ messages in thread* [PATCH v1 38/54] ARM: psci: client: register runtime service poweroff handler
2025-12-18 10:37 [PATCH v1 00/54] efi: implement EFI loader support in barebox Ahmad Fatoum
` (36 preceding siblings ...)
2025-12-18 10:37 ` [PATCH v1 37/54] poweroff: allow drivers to register runtime poweroff handler Ahmad Fatoum
@ 2025-12-18 10:37 ` Ahmad Fatoum
2025-12-18 10:37 ` [PATCH v1 39/54] ARM: psci: client: register runtime service restart handler Ahmad Fatoum
` (15 subsequent siblings)
53 siblings, 0 replies; 55+ messages in thread
From: Ahmad Fatoum @ 2025-12-18 10:37 UTC (permalink / raw)
To: barebox; +Cc: Ahmad Fatoum
With the infrastructure added to register runtime callbacks, let's add
PSCI poweroff on both ARM32 and ARM64 as the first to be provided by
barebox.
Given that the runtime poweroff callback is registered by the driver, we
can add new poweroff handlers in future without one conflicting with
another, maintaining our ability to target multiple SoCs and boards with
the same build or even the same image.
Signed-off-by: Ahmad Fatoum <a.fatoum@pengutronix.de>
---
arch/arm/cpu/psci-client.c | 20 ++++++++++++++++----
1 file changed, 16 insertions(+), 4 deletions(-)
diff --git a/arch/arm/cpu/psci-client.c b/arch/arm/cpu/psci-client.c
index 75cc3f59e0c1..17b021898884 100644
--- a/arch/arm/cpu/psci-client.c
+++ b/arch/arm/cpu/psci-client.c
@@ -12,8 +12,12 @@
#include <poweroff.h>
#include <restart.h>
#include <linux/arm-smccc.h>
+#include <efi/types.h>
static struct restart_handler restart;
+static struct poweroff_handler poweroff;
+
+static __efi_runtime_data u32 (*psci_invoke_fn)(ulong, ulong, ulong, ulong);
static void __noreturn psci_invoke_noreturn(ulong function)
{
@@ -37,6 +41,12 @@ static void __noreturn psci_restart(struct restart_handler *rst,
psci_invoke_noreturn(ARM_PSCI_0_2_FN_SYSTEM_RESET);
}
+static void __noreturn __efi_runtime rt_psci_poweroff(unsigned long flags)
+{
+ psci_invoke_fn(ARM_PSCI_0_2_FN_SYSTEM_OFF, 0, 0, 0);
+ __hang();
+}
+
static u32 version;
int psci_get_version(void)
{
@@ -46,7 +56,6 @@ int psci_get_version(void)
return version;
}
-static u32 (*psci_invoke_fn)(ulong, ulong, ulong, ulong);
static int psci_xlate_error(s32 errnum)
{
@@ -96,14 +105,14 @@ int psci_invoke(ulong function, ulong arg0, ulong arg1, ulong arg2,
return psci_xlate_error(ret);
}
-static u32 invoke_psci_fn_hvc(ulong function, ulong arg0, ulong arg1, ulong arg2)
+static u32 __efi_runtime invoke_psci_fn_hvc(ulong function, ulong arg0, ulong arg1, ulong arg2)
{
struct arm_smccc_res res;
arm_smccc_hvc(function, arg0, arg1, arg2, 0, 0, 0, 0, &res);
return res.a0;
}
-static u32 invoke_psci_fn_smc(ulong function, ulong arg0, ulong arg1, ulong arg2)
+static u32 __efi_runtime invoke_psci_fn_smc(ulong function, ulong arg0, ulong arg1, ulong arg2)
{
struct arm_smccc_res res;
arm_smccc_smc(function, arg0, arg1, arg2, 0, 0, 0, 0, &res);
@@ -165,7 +174,10 @@ static int __init psci_probe(struct device *dev)
if (actual_version != of_version)
of_register_fixup(of_psci_do_fixup, (void *)method);
- ret = poweroff_handler_register_fn(psci_poweroff);
+ poweroff.name = "psci";
+ poweroff.poweroff = psci_poweroff;
+ poweroff.rt_poweroff = rt_psci_poweroff;
+ ret = poweroff_handler_register(&poweroff);
if (ret)
dev_warn(dev, "error registering poweroff handler: %pe\n",
ERR_PTR(ret));
--
2.47.3
^ permalink raw reply [flat|nested] 55+ messages in thread* [PATCH v1 39/54] ARM: psci: client: register runtime service restart handler
2025-12-18 10:37 [PATCH v1 00/54] efi: implement EFI loader support in barebox Ahmad Fatoum
` (37 preceding siblings ...)
2025-12-18 10:37 ` [PATCH v1 38/54] ARM: psci: client: register runtime service " Ahmad Fatoum
@ 2025-12-18 10:37 ` Ahmad Fatoum
2025-12-18 10:38 ` [PATCH v1 40/54] hardening: disable some features when EFI runtime support is enabled Ahmad Fatoum
` (14 subsequent siblings)
53 siblings, 0 replies; 55+ messages in thread
From: Ahmad Fatoum @ 2025-12-18 10:37 UTC (permalink / raw)
To: barebox; +Cc: Ahmad Fatoum
Like done previously for poweroff, also register a runtime handler for
restarting the board.
Signed-off-by: Ahmad Fatoum <a.fatoum@pengutronix.de>
---
arch/arm/cpu/psci-client.c | 7 +++++++
1 file changed, 7 insertions(+)
diff --git a/arch/arm/cpu/psci-client.c b/arch/arm/cpu/psci-client.c
index 17b021898884..f53181d18473 100644
--- a/arch/arm/cpu/psci-client.c
+++ b/arch/arm/cpu/psci-client.c
@@ -47,6 +47,12 @@ static void __noreturn __efi_runtime rt_psci_poweroff(unsigned long flags)
__hang();
}
+static void __noreturn __efi_runtime rt_psci_restart(unsigned long flags)
+{
+ psci_invoke_fn(ARM_PSCI_0_2_FN_SYSTEM_RESET, 0, 0, 0);
+ __hang();
+}
+
static u32 version;
int psci_get_version(void)
{
@@ -184,6 +190,7 @@ static int __init psci_probe(struct device *dev)
restart.name = "psci";
restart.restart = psci_restart;
+ restart.rt_restart = rt_psci_restart;
restart.priority = 400;
ret = restart_handler_register(&restart);
--
2.47.3
^ permalink raw reply [flat|nested] 55+ messages in thread* [PATCH v1 40/54] hardening: disable some features when EFI runtime support is enabled
2025-12-18 10:37 [PATCH v1 00/54] efi: implement EFI loader support in barebox Ahmad Fatoum
` (38 preceding siblings ...)
2025-12-18 10:37 ` [PATCH v1 39/54] ARM: psci: client: register runtime service restart handler Ahmad Fatoum
@ 2025-12-18 10:38 ` Ahmad Fatoum
2025-12-18 10:38 ` [PATCH v1 41/54] filetype: add new filetype for efi-stubbed ARM zImages Ahmad Fatoum
` (13 subsequent siblings)
53 siblings, 0 replies; 55+ messages in thread
From: Ahmad Fatoum @ 2025-12-18 10:38 UTC (permalink / raw)
To: barebox; +Cc: Ahmad Fatoum
The way we compile the barebox EFI runtime makes it clash with some
of our hardening measures:
- ARM_MMU_PERMISSIONS: currently handles only a single text/rodata
section, but with .efi_runtime we have twice of each, but the code
can't yet handle applying permissions. But even with that fixed, eFI
payloads expet to be mapped RWX apparently? Needs some more thought
put into it.
- Stack protector: Stack protector sits outside of EFI runtime section
for now.
Signed-off-by: Ahmad Fatoum <a.fatoum@pengutronix.de>
---
arch/arm/Kconfig | 1 +
lib/Kconfig.hardening | 2 ++
2 files changed, 3 insertions(+)
diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig
index 4b7f5b83c67e..ae3de9504a9b 100644
--- a/arch/arm/Kconfig
+++ b/arch/arm/Kconfig
@@ -414,6 +414,7 @@ config ARM_UNWIND
config ARM_MMU_PERMISSIONS
bool "Map with extended RO/X permissions"
+ depends on !EFI_RUNTIME
default y
help
Enable this option to map readonly sections as readonly, executable
diff --git a/lib/Kconfig.hardening b/lib/Kconfig.hardening
index 3b3ba6267aec..59dd02c9cfae 100644
--- a/lib/Kconfig.hardening
+++ b/lib/Kconfig.hardening
@@ -169,6 +169,7 @@ config STACKPROTECTOR_NONE
config STACKPROTECTOR_STRONG
bool "Strong"
depends on $(cc-option,-fstack-protector-strong)
+ depends on !EFI_RUNTIME
select STACKPROTECTOR
help
This option turns on the "stack-protector" GCC feature. This
@@ -196,6 +197,7 @@ config STACKPROTECTOR_ALL
bool "All"
depends on $(cc-option,-fstack-protector-all)
depends on COMPILE_TEST
+ depends on !EFI_RUNTIME
select STACKPROTECTOR
help
This pushes and verifies stack protector canaries on all functions,
--
2.47.3
^ permalink raw reply [flat|nested] 55+ messages in thread* [PATCH v1 41/54] filetype: add new filetype for efi-stubbed ARM zImages
2025-12-18 10:37 [PATCH v1 00/54] efi: implement EFI loader support in barebox Ahmad Fatoum
` (39 preceding siblings ...)
2025-12-18 10:38 ` [PATCH v1 40/54] hardening: disable some features when EFI runtime support is enabled Ahmad Fatoum
@ 2025-12-18 10:38 ` Ahmad Fatoum
2025-12-18 10:38 ` [PATCH v1 42/54] bootm: add global.bootm.efi toggle Ahmad Fatoum
` (12 subsequent siblings)
53 siblings, 0 replies; 55+ messages in thread
From: Ahmad Fatoum @ 2025-12-18 10:38 UTC (permalink / raw)
To: barebox; +Cc: Ahmad Fatoum
To allow booting EFI payloads on ARM and to control whether EFI stub or
normal DT boot should be attempted, add a filetype for EFI-stubbed
zImages.
Signed-off-by: Ahmad Fatoum <a.fatoum@pengutronix.de>
---
common/filetype.c | 4 +++-
include/filetype.h | 1 +
2 files changed, 4 insertions(+), 1 deletion(-)
diff --git a/common/filetype.c b/common/filetype.c
index 196591698768..b82667c30a4b 100644
--- a/common/filetype.c
+++ b/common/filetype.c
@@ -31,6 +31,7 @@ static const struct filetype_str filetype_str[] = {
[filetype_unknown] = { "unknown", "unknown" },
[filetype_empty] = { "empty", "empty" },
[filetype_arm_zimage] = { "ARM Linux zImage", "arm-zimage" },
+ [filetype_arm_efi_zimage] = { "ARM/EFI Linux zImage", "arm-efi-zimage" },
[filetype_lzo_compressed] = { "LZO compressed", "lzo" },
[filetype_lz4_compressed] = { "LZ4 compressed", "lz4" },
[filetype_arm_barebox] = { "ARM barebox image", "arm-barebox" },
@@ -441,7 +442,7 @@ enum filetype file_detect_type(const void *_buf, size_t bufsize)
if (is_barebox_arm_head(_buf))
return filetype_arm_barebox;
if (buf[9] == 0x016f2818 || buf[9] == 0x18286f01)
- return filetype_arm_zimage;
+ return is_dos_exe(buf8) ? filetype_arm_efi_zimage : filetype_arm_zimage;
if (is_dos_exe(buf8))
return filetype_exe;
@@ -585,6 +586,7 @@ bool filetype_is_barebox_image(enum filetype ft)
bool filetype_is_linux_efi_image(enum filetype ft)
{
switch (ft) {
+ case filetype_arm_efi_zimage:
case filetype_arm64_efi_linux_image:
case filetype_riscv_efi_linux_image:
case filetype_x86_efi_linux_image:
diff --git a/include/filetype.h b/include/filetype.h
index 283b8fee4c70..aed02fcc2566 100644
--- a/include/filetype.h
+++ b/include/filetype.h
@@ -13,6 +13,7 @@ enum filetype {
filetype_unknown,
filetype_empty,
filetype_arm_zimage,
+ filetype_arm_efi_zimage,
filetype_lzo_compressed,
filetype_lz4_compressed,
filetype_arm_barebox,
--
2.47.3
^ permalink raw reply [flat|nested] 55+ messages in thread* [PATCH v1 42/54] bootm: add global.bootm.efi toggle
2025-12-18 10:37 [PATCH v1 00/54] efi: implement EFI loader support in barebox Ahmad Fatoum
` (40 preceding siblings ...)
2025-12-18 10:38 ` [PATCH v1 41/54] filetype: add new filetype for efi-stubbed ARM zImages Ahmad Fatoum
@ 2025-12-18 10:38 ` Ahmad Fatoum
2025-12-18 10:38 ` [PATCH v1 43/54] efi: loader: add ESP boot entry provider Ahmad Fatoum
` (11 subsequent siblings)
53 siblings, 0 replies; 55+ messages in thread
From: Ahmad Fatoum @ 2025-12-18 10:38 UTC (permalink / raw)
To: barebox; +Cc: Ahmad Fatoum
EFI-stubbed kernel images can be booted via EFI or completely via DT.
To allow barebox to support both in the same build, introduce a
global.bootm.efi variable that controls this when loader support is
enabled. The default will be to use EFI if support in both barebox and
kernel is available, but there is an option to disable use of EFI and to
enforce it.
Signed-off-by: Ahmad Fatoum <a.fatoum@pengutronix.de>
---
arch/arm/lib64/armlinux.c | 14 ++++---------
arch/riscv/lib/bootm.c | 14 ++++---------
common/bootm.c | 43 +++++++++++++++++++++++++++++++++++++++
common/filetype.c | 16 +++++++++++++++
efi/payload/bootm.c | 1 +
include/bootm.h | 12 +++++++++++
include/filetype.h | 1 +
7 files changed, 81 insertions(+), 20 deletions(-)
diff --git a/arch/arm/lib64/armlinux.c b/arch/arm/lib64/armlinux.c
index 7803d5671719..e40b61a26b07 100644
--- a/arch/arm/lib64/armlinux.c
+++ b/arch/arm/lib64/armlinux.c
@@ -33,15 +33,10 @@ static int do_bootm_linux(struct image_data *data)
}
static struct image_handler aarch64_linux_handler = {
- .name = "ARM aarch64 Linux image",
- .bootm = do_bootm_linux,
- .filetype = filetype_arm64_linux_image,
-};
-
-static struct image_handler aarch64_linux_efi_handler = {
- .name = "ARM aarch64 Linux/EFI image",
- .bootm = do_bootm_linux,
- .filetype = filetype_arm64_efi_linux_image,
+ .name = "ARM aarch64 Linux image",
+ .bootm = do_bootm_linux,
+ .check_image = bootm_efi_check_image,
+ .filetype = filetype_arm64_linux_image,
};
static int do_bootm_barebox(struct image_data *data)
@@ -87,7 +82,6 @@ static int aarch64_register_image_handler(void)
if (efi_is_payload())
return 0;
- register_image_handler(&aarch64_linux_efi_handler);
register_image_handler(&aarch64_linux_handler);
register_image_handler(&aarch64_barebox_handler);
diff --git a/arch/riscv/lib/bootm.c b/arch/riscv/lib/bootm.c
index 08791458f824..2a47579b41ef 100644
--- a/arch/riscv/lib/bootm.c
+++ b/arch/riscv/lib/bootm.c
@@ -31,15 +31,10 @@ static int do_bootm_linux(struct image_data *data)
}
static struct image_handler riscv_linux_handler = {
- .name = "RISC-V Linux image",
- .bootm = do_bootm_linux,
- .filetype = filetype_riscv_linux_image,
-};
-
-static struct image_handler riscv_linux_efi_handler = {
- .name = "RISC-V Linux/EFI image",
- .bootm = do_bootm_linux,
- .filetype = filetype_riscv_efi_linux_image,
+ .name = "RISC-V Linux image",
+ .bootm = do_bootm_linux,
+ .check_image = bootm_efi_check_image,
+ .filetype = filetype_riscv_linux_image,
};
static struct image_handler riscv_barebox_handler = {
@@ -51,7 +46,6 @@ static struct image_handler riscv_barebox_handler = {
static int riscv_register_image_handler(void)
{
register_image_handler(&riscv_linux_handler);
- register_image_handler(&riscv_linux_efi_handler);
register_image_handler(&riscv_barebox_handler);
return 0;
diff --git a/common/bootm.c b/common/bootm.c
index 620eb19ff1d3..4e4d940d9198 100644
--- a/common/bootm.c
+++ b/common/bootm.c
@@ -6,6 +6,7 @@
#include <bootm-overrides.h>
#include <fs.h>
#include <fcntl.h>
+#include <efi/mode.h>
#include <malloc.h>
#include <memory.h>
#include <block.h>
@@ -73,6 +74,7 @@ static int bootm_earlycon;
static int bootm_provide_machine_id;
static int bootm_provide_hostname;
static int bootm_verbosity;
+static int bootm_efi_mode = BOOTM_EFI_AVAILABLE;
void bootm_data_init_defaults(struct bootm_data *data)
{
@@ -95,6 +97,7 @@ void bootm_data_init_defaults(struct bootm_data *data)
data->provide_hostname = bootm_provide_hostname;
data->verbose = bootm_verbosity;
data->dryrun = bootm_dryrun;
+ data->efi_boot = bootm_efi_mode;
}
void bootm_data_restore_defaults(const struct bootm_data *data)
@@ -115,6 +118,7 @@ void bootm_data_restore_defaults(const struct bootm_data *data)
bootm_provide_hostname = data->provide_hostname;
bootm_verbosity = data->verbose;
bootm_dryrun = data->dryrun;
+ bootm_efi_mode = data->efi_boot;
}
static enum bootm_verify bootm_verify_mode = BOOTM_VERIFY_AVAILABLE;
@@ -807,6 +811,7 @@ int bootm_boot(struct bootm_data *bootm_data)
data->initrd_address = bootm_data->initrd_address;
data->os_address = bootm_data->os_address;
data->os_entry = bootm_data->os_entry;
+ data->efi_boot = bootm_data->efi_boot;
ret = read_file_2(data->os_file, &size, &data->os_header, PAGE_SIZE);
if (ret < 0 && ret != -EFBIG) {
@@ -1052,6 +1057,30 @@ struct bootm_overrides bootm_set_overrides(const struct bootm_overrides override
}
#endif
+bool bootm_efi_check_image(struct image_handler *handler,
+ struct image_data *data,
+ enum filetype detected_filetype)
+{
+ /* This is our default: Use EFI when support is compiled in,
+ * and fallback to normal boot otherwise.
+ */
+ if (data->efi_boot == BOOTM_EFI_AVAILABLE) {
+ if (IS_ENABLED(CONFIG_EFI_LOADER))
+ data->efi_boot = BOOTM_EFI_REQUIRED;
+ else
+ data->efi_boot = BOOTM_EFI_DISABLED;
+ }
+
+ /* If EFI is disabled, we assume EFI-stubbed images are
+ * just normal non-EFI stubbed ones
+ */
+ if (data->efi_boot == BOOTM_EFI_DISABLED)
+ return handler->filetype == filetype_no_efistub(detected_filetype);
+
+ /* If EFI is required, we enforce handlers to match exactly */
+ return detected_filetype == handler->filetype;
+}
+
static int do_bootm_compressed(struct image_data *img_data)
{
struct bootm_data bootm_data = {
@@ -1140,6 +1169,12 @@ static struct image_handler zstd_bootm_handler = {
int linux_rootwait_secs = 10;
+static const char * const bootm_efi_loader_mode_names[] = {
+ [BOOTM_EFI_DISABLED] = "disabled",
+ [BOOTM_EFI_AVAILABLE] = "available",
+ [BOOTM_EFI_REQUIRED] = "required",
+};
+
static int bootm_init(void)
{
globalvar_add_simple("bootm.image", NULL);
@@ -1174,6 +1209,11 @@ static int bootm_init(void)
globalvar_add_simple_int("linux.rootwait",
&linux_rootwait_secs, "%d");
+ if (IS_ENABLED(CONFIG_EFI_LOADER) && !efi_is_payload())
+ globalvar_add_simple_enum("bootm.efi", &bootm_efi_mode,
+ bootm_efi_loader_mode_names,
+ ARRAY_SIZE(bootm_efi_loader_mode_names));
+
if (IS_ENABLED(CONFIG_BZLIB))
register_image_handler(&bzip2_bootm_handler);
if (IS_ENABLED(CONFIG_ZLIB))
@@ -1200,6 +1240,9 @@ BAREBOX_MAGICVAR(global.bootm.oftree, "bootm default oftree");
BAREBOX_MAGICVAR(global.bootm.tee, "bootm default tee image");
BAREBOX_MAGICVAR(global.bootm.dryrun, "bootm default dryrun level");
BAREBOX_MAGICVAR(global.bootm.verify, "bootm default verify level");
+#ifdef CONFIG_EFI_LOADER
+BAREBOX_MAGICVAR(global.bootm.efi, "efiloader: Behavior for EFI-stubbable boot images: EFI \"disabled\", EFI if \"available\" (default), EFI is \"required\"");
+#endif
BAREBOX_MAGICVAR(global.bootm.verbose, "bootm default verbosity level (0=quiet)");
BAREBOX_MAGICVAR(global.bootm.earlycon, "Add earlycon option to Kernel for early log output");
BAREBOX_MAGICVAR(global.bootm.appendroot, "Add root= option to Kernel to mount rootfs from the device the Kernel comes from (default, device can be overridden via global.bootm.root_dev)");
diff --git a/common/filetype.c b/common/filetype.c
index b82667c30a4b..9b348377f222 100644
--- a/common/filetype.c
+++ b/common/filetype.c
@@ -595,3 +595,19 @@ bool filetype_is_linux_efi_image(enum filetype ft)
return false;
}
}
+
+enum filetype filetype_no_efistub(enum filetype ft)
+{
+ switch (ft) {
+ case filetype_arm_efi_zimage:
+ return filetype_arm_zimage;
+ case filetype_arm64_efi_linux_image:
+ return filetype_arm64_linux_image;
+ case filetype_riscv_efi_linux_image:
+ return filetype_riscv_linux_image;
+ case filetype_x86_efi_linux_image:
+ return filetype_x86_linux_image;
+ default:
+ return ft;
+ }
+}
diff --git a/efi/payload/bootm.c b/efi/payload/bootm.c
index 87bf1de709b4..ea5b1265aeaa 100644
--- a/efi/payload/bootm.c
+++ b/efi/payload/bootm.c
@@ -316,6 +316,7 @@ struct image_handler efi_x86_linux_handle_tr = {
static struct image_handler efi_arm64_handle_tr = {
.name = "EFI ARM64 Linux kernel",
.bootm = do_bootm_efi_stub,
+ .check_image = bootm_efi_check_image,
.filetype = filetype_arm64_efi_linux_image,
};
diff --git a/include/bootm.h b/include/bootm.h
index b08e20c50da7..e56a999f0b11 100644
--- a/include/bootm.h
+++ b/include/bootm.h
@@ -13,6 +13,12 @@ enum bootm_verify {
BOOTM_VERIFY_AVAILABLE,
};
+enum bootm_efi_mode {
+ BOOTM_EFI_DISABLED,
+ BOOTM_EFI_AVAILABLE,
+ BOOTM_EFI_REQUIRED,
+};
+
struct bootm_data {
const char *os_file;
const char *initrd_file;
@@ -40,6 +46,7 @@ struct bootm_data {
* of global.hostname to Kernel.
*/
bool provide_hostname;
+ enum bootm_efi_mode efi_boot;
unsigned long initrd_address;
unsigned long os_address;
unsigned long os_entry;
@@ -109,6 +116,7 @@ struct image_data {
int verbose;
int force;
int dryrun;
+ enum bootm_efi_mode efi_boot;
};
struct image_handler {
@@ -165,4 +173,8 @@ void bootm_force_signed_images(void);
void *booti_load_image(struct image_data *data, phys_addr_t *oftree);
+bool bootm_efi_check_image(struct image_handler *handler,
+ struct image_data *data,
+ enum filetype detected_filetype);
+
#endif /* __BOOTM_H */
diff --git a/include/filetype.h b/include/filetype.h
index aed02fcc2566..0e19e46c513d 100644
--- a/include/filetype.h
+++ b/include/filetype.h
@@ -91,6 +91,7 @@ enum filetype is_fat_or_mbr(const unsigned char *sector, unsigned long *bootsec)
int is_fat_boot_sector(const void *_buf);
bool filetype_is_barebox_image(enum filetype ft);
bool filetype_is_linux_efi_image(enum filetype ft);
+enum filetype filetype_no_efistub(enum filetype);
static inline bool file_is_compressed_file(enum filetype ft)
{
--
2.47.3
^ permalink raw reply [flat|nested] 55+ messages in thread* [PATCH v1 43/54] efi: loader: add ESP boot entry provider
2025-12-18 10:37 [PATCH v1 00/54] efi: implement EFI loader support in barebox Ahmad Fatoum
` (41 preceding siblings ...)
2025-12-18 10:38 ` [PATCH v1 42/54] bootm: add global.bootm.efi toggle Ahmad Fatoum
@ 2025-12-18 10:38 ` Ahmad Fatoum
2025-12-18 10:38 ` [PATCH v1 44/54] efi: loader: add rudimentary EFI boot manager Ahmad Fatoum
` (10 subsequent siblings)
53 siblings, 0 replies; 55+ messages in thread
From: Ahmad Fatoum @ 2025-12-18 10:38 UTC (permalink / raw)
To: barebox; +Cc: Ahmad Fatoum
When no Boot variables have been configured, the default per spec
is to look through known media for an EFI payload at the removable
media path (CONFIG_EFI_PAYLOAD_DEFAULT_PATH).
Implement a boot entry provider that does just that.
This is going to be called by an incoming efibootmgr target, so bump the
priority below the existing ones, so it's not taken as the default.
Signed-off-by: Ahmad Fatoum <a.fatoum@pengutronix.de>
---
efi/loader/Makefile | 1 +
efi/loader/bootesp.c | 260 +++++++++++++++++++++++++++++++++++++++++++
include/libfile.h | 2 +
lib/libfile.c | 56 ++++++++++
4 files changed, 319 insertions(+)
create mode 100644 efi/loader/bootesp.c
diff --git a/efi/loader/Makefile b/efi/loader/Makefile
index 44ba3a86e4d4..84a8bf1ca229 100644
--- a/efi/loader/Makefile
+++ b/efi/loader/Makefile
@@ -12,3 +12,4 @@ obj-y += setup.o
obj-y += watchdog.o
obj-y += pe.o
obj-y += loadopts.o
+obj-$(CONFIG_BOOT) += bootesp.o
diff --git a/efi/loader/bootesp.c b/efi/loader/bootesp.c
new file mode 100644
index 000000000000..afce5118aa68
--- /dev/null
+++ b/efi/loader/bootesp.c
@@ -0,0 +1,260 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#define pr_fmt(fmt) "efi-loader: bootesp: " fmt
+
+#include <malloc.h>
+#include <fcntl.h>
+#include <libfile.h>
+#include <libbb.h>
+#include <init.h>
+#include <bootm.h>
+#include <driver.h>
+#include <fs.h>
+#include <globalvar.h>
+#include <linux/stat.h>
+#include <linux/list.h>
+#include <linux/err.h>
+#include <spec/dps.h>
+#include <boot.h>
+#include <memory.h>
+#include <firmware.h>
+#include <command.h>
+
+#include <bootscan.h>
+
+struct esp_entry {
+ struct bootentry entry;
+
+ struct cdev *cdev;
+ const char *exepath;
+ const char *rootpath;
+};
+
+/*
+ * esp_boot - boot an entry
+ *
+ * This boots an entry. On success this function does not return.
+ * In case of an error the error code is returned. This function may
+ * return 0 in case of a successful dry run.
+ */
+static int esp_boot(struct bootentry *be, int verbose, int dryrun)
+{
+ struct esp_entry *entry = container_of(be, struct esp_entry, entry);
+ struct bootm_data data = {};
+ int ret;
+
+ bootm_data_init_defaults(&data);
+ data.verbose = max(verbose, data.verbose);
+ data.dryrun = dryrun;
+ data.efi_boot = BOOTM_EFI_REQUIRED;
+
+ data.os_file = strdup_const(entry->exepath);
+
+ /* TODO:
+ * 1) move firmware/overlay handling into common/bootm.c and then
+ * 2) implement device tree overlay patching protocol using it?
+ */
+
+ ret = bootm_boot(&data);
+ if (ret)
+ pr_err("Booting failed\n");
+
+ free_const(data.os_file);
+
+ return ret;
+}
+
+static void esp_entry_free(struct bootentry *be)
+{
+ struct esp_entry *entry = container_of(be, struct esp_entry, entry);
+
+ free_const(entry->exepath);
+ free_const(entry->rootpath);
+ free(entry);
+}
+
+static struct esp_entry *esp_entry_alloc(struct bootentries *bootentries)
+{
+ struct esp_entry *entry;
+
+ entry = xzalloc(sizeof(*entry));
+
+ entry->entry.release = esp_entry_free;
+ entry->entry.boot = esp_boot;
+
+ return entry;
+}
+
+static int __esp_scan_file(struct bootentries *bootentries, const char *root,
+ const char *exename)
+{
+ char *devname = NULL, *hwdevname = NULL;
+ struct esp_entry *entry;
+
+ pr_debug("%s: %s\n", __func__, root);
+
+ entry = esp_entry_alloc(bootentries);
+ if (IS_ERR(entry))
+ return PTR_ERR(entry);
+
+ root = root ?: get_mounted_path(exename);
+ entry->rootpath = xstrdup_const(root);
+ entry->exepath = xstrdup_const(exename);
+ entry->cdev = get_cdev_by_mountpath(root);
+
+ if (entry->cdev && entry->cdev->dev) {
+ devname = xstrdup(dev_name(entry->cdev->dev));
+ if (entry->cdev->dev->parent)
+ hwdevname = xstrdup(dev_name(entry->cdev->dev->parent));
+ }
+
+ entry->entry.title = xasprintf("EFI payload (%s)", exename);
+ entry->entry.description = basprintf("ESP entry, device: %s hwdevice: %s",
+ devname ? devname : "none",
+ hwdevname ? hwdevname : "none");
+ free(devname);
+ free(hwdevname);
+
+ entry->entry.me.type = MENU_ENTRY_NORMAL;
+ entry->entry.release = esp_entry_free;
+
+ bootentries_add_entry(bootentries, &entry->entry);
+ return 1;
+}
+
+static int esp_scan_file(struct bootscanner *scanner,
+ struct bootentries *bootentries,
+ const char *exename)
+{
+ u8 magic[2];
+
+ if (read_file_into_buf(exename, &magic, 2) != 2 ||!is_dos_exe(magic))
+ return 0;
+
+ return __esp_scan_file(bootentries, NULL, exename);
+}
+
+/*
+ * esp_scan_directory - scan over a directory
+ *
+ * Given a root path collects all bootentries.
+ *
+ * returns the number of entries found or a negative error value otherwise.
+ */
+static int esp_scan_directory(struct bootscanner *bootscanner,
+ struct bootentries *bootentries,
+ const char *root)
+{
+ const char *path = CONFIG_EFI_PAYLOAD_DEFAULT_PATH;
+ char *abspath;
+ int fd, ret, found = 0;
+ struct stat s;
+
+ pr_debug("%s: %s\n", __func__, root);
+
+ fd = open(root, O_DIRECTORY);
+ if (fd < 0)
+ return fd;
+
+ ret = fixup_path_case(fd, &path);
+ if (ret < 0)
+ goto out;
+ fd = ret;
+
+ ret = fstat(fd, &s);
+ if (ret || !S_ISREG(s.st_mode))
+ goto out;
+
+ abspath = xasprintf("%s/%s", root, path);
+
+ ret = __esp_scan_file(bootentries, root, abspath);
+ if (ret > 0)
+ found += ret;
+
+ free(abspath);
+
+ ret = found;
+out:
+ close(fd);
+ return ret;
+}
+
+static int esp_scan_with_flag(struct bootscanner *scanner,
+ struct bootentries *bootentries, struct cdev *cdev,
+ unsigned flags)
+{
+ struct cdev *partcdev;
+ int found = 0, err = 0;
+ int ret;
+
+ for_each_cdev_partition(partcdev, cdev) {
+ if ((partcdev->flags & flags) != flags)
+ continue;
+
+ err = -ENOENT;
+
+ if (partcdev->typeflags & DPS_TYPE_FLAG_NO_AUTO) {
+ pr_debug("%s: partition skipped from autodiscovery\n",
+ partcdev->name);
+ continue;
+ }
+
+ ret = boot_scan_cdev(scanner, bootentries, partcdev, true);
+ if (ret > 0)
+ found += ret;
+ }
+
+ return found ?: err;
+}
+
+/*
+ * esp_scan_disk - scan a disk cdev for ESP
+ *
+ * Given a cdev this functions scans over all child partitions looking
+ * for the ESP.
+ * Returns the number of entries found or a negative error code if some unexpected
+ * error occurred.
+ */
+static int esp_scan_disk(struct bootscanner *scanner,
+ struct bootentries *bootentries, struct cdev *cdev)
+{
+ int nesp = 0, nlegacy = 0;
+
+ pr_debug("%s: %s\n", __func__, cdev->name);
+
+ nesp = esp_scan_with_flag(scanner, bootentries, cdev,
+ DEVFS_PARTITION_BOOTABLE_ESP);
+ if (nesp < 0)
+ return nesp;
+
+ nlegacy = esp_scan_with_flag(scanner, bootentries, cdev,
+ DEVFS_PARTITION_BOOTABLE_LEGACY);
+ if (nlegacy < 0)
+ return nlegacy;
+
+ return nesp + nlegacy;
+}
+
+static struct bootscanner esp_scanner = {
+ .name = "esp",
+ .scan_file = esp_scan_file,
+ .scan_directory = esp_scan_directory,
+ .scan_disk = esp_scan_disk,
+};
+
+static int esp_bootentry_generate(struct bootentries *bootentries, const char *name)
+{
+ return bootentry_scan_generate(&esp_scanner, bootentries, name);
+}
+
+static struct bootentry_provider esp_bootentry_provider = {
+ .name = "esp",
+ .generate = esp_bootentry_generate,
+ .priority = -50,
+};
+
+static int esp_init(void)
+{
+ return bootentry_register_provider(&esp_bootentry_provider);
+}
+device_initcall(esp_init);
diff --git a/include/libfile.h b/include/libfile.h
index cdcad4b173d0..dc292e422e0b 100644
--- a/include/libfile.h
+++ b/include/libfile.h
@@ -57,4 +57,6 @@ int cache_file(const char *path, char **newpath);
struct resource *file_to_sdram(const char *filename, unsigned long adr,
enum resource_memtype memtype);
+int fixup_path_case(int dirfd, const char **path);
+
#endif /* __LIBFILE_H */
diff --git a/lib/libfile.c b/lib/libfile.c
index a6690b0b904a..01189773b7e3 100644
--- a/lib/libfile.c
+++ b/lib/libfile.c
@@ -20,6 +20,7 @@
#include <libfile.h>
#include <progress.h>
#include <stdlib.h>
+#include <string.h>
#include <linux/stat.h>
/*
@@ -831,3 +832,58 @@ struct resource *file_to_sdram(const char *filename, unsigned long adr,
return res;
}
+
+int fixup_path_case(int fd, const char **path)
+{
+ DIR *dir;
+ struct dirent *entry;
+ char *resolved_path, *curr, *next;
+
+ next = resolved_path = xstrdup(*path);
+
+ while ((curr = strsep(&next, "/"))) {
+ size_t nextlen = strlen(curr);
+ char *imatch = NULL;
+
+ dir = fdopendir(fd);
+ if (!dir)
+ goto err;
+
+ while ((entry = readdir(dir)) != NULL) {
+ size_t d_namelen = strlen(entry->d_name);
+
+ if (nextlen != d_namelen)
+ continue;
+
+ if (!strcmp(entry->d_name, curr))
+ goto next_component;
+
+ if (!imatch && !strcasecmp(entry->d_name, curr))
+ imatch = xstrdup(entry->d_name);
+ }
+
+ if (!imatch) {
+ errno = ENOENT;
+ goto err;
+ }
+
+ strncpy(curr, imatch, nextlen);
+ free(imatch);
+
+next_component:
+ fd = openat(fd, curr, next ? O_DIRECTORY : 0);
+ closedir(dir);
+
+ if (fd < 0)
+ goto err;
+
+ if (next)
+ curr[nextlen] = '/';
+ }
+
+ *path = resolved_path;
+ return fd;
+err:
+ free(resolved_path);
+ return -errno;
+}
--
2.47.3
^ permalink raw reply [flat|nested] 55+ messages in thread* [PATCH v1 44/54] efi: loader: add rudimentary EFI boot manager
2025-12-18 10:37 [PATCH v1 00/54] efi: implement EFI loader support in barebox Ahmad Fatoum
` (42 preceding siblings ...)
2025-12-18 10:38 ` [PATCH v1 43/54] efi: loader: add ESP boot entry provider Ahmad Fatoum
@ 2025-12-18 10:38 ` Ahmad Fatoum
2025-12-18 10:38 ` [PATCH v1 45/54] efi: loader: implement bootm handler Ahmad Fatoum
` (9 subsequent siblings)
53 siblings, 0 replies; 55+ messages in thread
From: Ahmad Fatoum @ 2025-12-18 10:38 UTC (permalink / raw)
To: barebox; +Cc: Ahmad Fatoum
Actual efibootmgr is a bit more involved and would require evaluating
the Boot#### variables. This is planned, but for now let's only
implement the fallback when no variables have been found.
Signed-off-by: Ahmad Fatoum <a.fatoum@pengutronix.de>
---
common/boot.c | 7 ++--
efi/loader/Kconfig | 11 ++++++
efi/loader/Makefile | 1 +
efi/loader/efibootmgr.c | 79 +++++++++++++++++++++++++++++++++++++++++
4 files changed, 95 insertions(+), 3 deletions(-)
create mode 100644 efi/loader/efibootmgr.c
diff --git a/common/boot.c b/common/boot.c
index a8a92057844b..7b2ce4740759 100644
--- a/common/boot.c
+++ b/common/boot.c
@@ -136,9 +136,10 @@ static int init_boot(void)
{
if (!global_boot_default)
global_boot_default = xstrdup(
- IF_ENABLED(CONFIG_BOOT_DEFAULTS, "bootsource ")
- IF_ENABLED(CONFIG_BOOT_DEFAULTS, "storage.builtin ")
- IF_ENABLED(CONFIG_BOOT_DEFAULTS, "storage.removable ")
+ IF_ENABLED(CONFIG_EFI_LOADER_BOOTMGR, "efibootmgr ")
+ IF_ENABLED(CONFIG_BOOT_DEFAULTS, "bootsource ")
+ IF_ENABLED(CONFIG_BOOT_DEFAULTS, "storage.builtin ")
+ IF_ENABLED(CONFIG_BOOT_DEFAULTS, "storage.removable ")
"net"
);
diff --git a/efi/loader/Kconfig b/efi/loader/Kconfig
index d54783f9a084..ca0ec6b5010a 100644
--- a/efi/loader/Kconfig
+++ b/efi/loader/Kconfig
@@ -1,6 +1,17 @@
# SPDX-License-Identifier: GPL-2.0-only
# SPDX-Comment: Origin-URL: https://github.com/u-boot/u-boot/blob/a0fe8cedcbe8c76403a77e57eac228b8f778a3ae/lib/efi_loader/Kconfig
+config EFI_LOADER_BOOTMGR
+ bool "Rudimentary UEFI Boot Manager support"
+ select BOOT
+ select BOOT_DEFAULTS
+ default y
+ help
+ This boot manager doesn't yet make use of Boot# variables, but instead
+ hardcodes order to lookup removable and then builtin storage devices
+ for EFI payloads located at the removable media path indicated by
+ CONFIG_EFI_PAYLOAD_DEFAULT_PATH within their respective devices.
+
config EFI_LOADER_DEBUG_SUPPORT
bool "EFI Debug Support"
default y
diff --git a/efi/loader/Makefile b/efi/loader/Makefile
index 84a8bf1ca229..62483057b426 100644
--- a/efi/loader/Makefile
+++ b/efi/loader/Makefile
@@ -13,3 +13,4 @@ obj-y += watchdog.o
obj-y += pe.o
obj-y += loadopts.o
obj-$(CONFIG_BOOT) += bootesp.o
+obj-$(CONFIG_EFI_LOADER_BOOTMGR) += efibootmgr.o
diff --git a/efi/loader/efibootmgr.c b/efi/loader/efibootmgr.c
new file mode 100644
index 000000000000..4f8b04d8298c
--- /dev/null
+++ b/efi/loader/efibootmgr.c
@@ -0,0 +1,79 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#define pr_fmt(fmt) "efi-loader: efibootmgr: " fmt
+
+#include <boot.h>
+#include <driver.h>
+
+struct resolve_ctx {
+ struct bootentry_provider *provider;
+ struct bootentries *entries;
+};
+
+static int populate_esp_bootentries(struct cdev *cdev, void *_ctx)
+{
+ struct resolve_ctx *ctx = _ctx;
+
+ pr_debug("processing %s\n", cdev->name);
+
+ return ctx->provider->generate(ctx->entries, cdev->name);
+}
+
+static int efibootmgr_add_entry_from_bootvars(struct bootentries *entries,
+ const char *name)
+{
+ /* TODO: actually make use of the Boot# variables
+ * instead of only hardcoding order
+ */
+ return 0;
+}
+
+static int efibootmgr_add_entry_from_fallback(struct bootentries *entries,
+ const char *name)
+{
+ struct resolve_ctx ctx;
+ int nremovable, nbuiltin;
+
+ ctx.provider = get_bootentry_provider("esp");
+ if (!ctx.provider)
+ return -ENOENT;
+
+ ctx.entries = entries;
+
+ nremovable = cdev_alias_resolve_for_each("storage.removable",
+ populate_esp_bootentries, &ctx);
+ if (nremovable < 0)
+ return nremovable;
+
+ nbuiltin = cdev_alias_resolve_for_each("storage.builtin",
+ populate_esp_bootentries, &ctx);
+ if (nbuiltin < 0)
+ return nbuiltin;
+
+ return nbuiltin + nremovable;
+}
+
+static int efibootmgr_add_entry(struct bootentries *entries, const char *name)
+{
+ int nentries;
+
+ if (strcmp(name, "efibootmgr"))
+ return 0;
+
+ nentries = efibootmgr_add_entry_from_bootvars(entries, name);
+ if (nentries)
+ return nentries;
+
+ return efibootmgr_add_entry_from_fallback(entries, name);
+}
+
+static struct bootentry_provider efibootmgr_entry_provider = {
+ .name = "efibootmgr",
+ .generate = efibootmgr_add_entry,
+};
+
+static int efibootmgr_init(void)
+{
+ return bootentry_register_provider(&efibootmgr_entry_provider);
+}
+device_initcall(efibootmgr_init);
--
2.47.3
^ permalink raw reply [flat|nested] 55+ messages in thread* [PATCH v1 45/54] efi: loader: implement bootm handler
2025-12-18 10:37 [PATCH v1 00/54] efi: implement EFI loader support in barebox Ahmad Fatoum
` (43 preceding siblings ...)
2025-12-18 10:38 ` [PATCH v1 44/54] efi: loader: add rudimentary EFI boot manager Ahmad Fatoum
@ 2025-12-18 10:38 ` Ahmad Fatoum
2025-12-18 10:38 ` [PATCH v1 46/54] efi: runtime: add EFI variable support Ahmad Fatoum
` (8 subsequent siblings)
53 siblings, 0 replies; 55+ messages in thread
From: Ahmad Fatoum @ 2025-12-18 10:38 UTC (permalink / raw)
To: barebox; +Cc: Ahmad Fatoum
As counterpart to the EFI-less bootm handlers for the different
architectures, define EFI-enabled variants that will initialize the EFI
loader support and load the PE by EFI means.
Signed-off-by: Ahmad Fatoum <a.fatoum@pengutronix.de>
---
efi/loader/Makefile | 1 +
efi/loader/bootm.c | 358 ++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 359 insertions(+)
create mode 100644 efi/loader/bootm.c
diff --git a/efi/loader/Makefile b/efi/loader/Makefile
index 62483057b426..2a78361e58d5 100644
--- a/efi/loader/Makefile
+++ b/efi/loader/Makefile
@@ -14,3 +14,4 @@ obj-y += pe.o
obj-y += loadopts.o
obj-$(CONFIG_BOOT) += bootesp.o
obj-$(CONFIG_EFI_LOADER_BOOTMGR) += efibootmgr.o
+obj-$(CONFIG_BOOTM) += bootm.o
diff --git a/efi/loader/bootm.c b/efi/loader/bootm.c
new file mode 100644
index 000000000000..dffe53bcf9c8
--- /dev/null
+++ b/efi/loader/bootm.c
@@ -0,0 +1,358 @@
+// SPDX-License-Identifier: GPL-2.0
+// SPDX-Comment: Origin-URL: https://github.com/u-boot/u-boot/blob/162a6b3df349295bf766c1d128d71b4547e8d56e/lib/efi_loader/efi_helper.c
+/*
+ * Copyright (c) 2016 Alexander Graf
+ */
+#define pr_fmt(fmt) "efi-loader: bootm: " fmt
+
+#include <clock.h>
+#include <linux/sizes.h>
+#include <memory.h>
+#include <command.h>
+#include <magicvar.h>
+#include <init.h>
+#include <driver.h>
+#include <io.h>
+#include <bootargs.h>
+#include <malloc.h>
+#include <string.h>
+#include <linux/err.h>
+#include <fs.h>
+#include <filetype.h>
+#include <libfile.h>
+#include <bootm.h>
+#include <wchar.h>
+#include <efi/mode.h>
+#include <efi/loader.h>
+#include <efi/loader/object.h>
+#include <efi/loader/option.h>
+#include <efi/loader/table.h>
+#include <efi/loader/devicepath.h>
+#include <efi/loader/image.h>
+#include <efi/loader/event.h>
+#include <efi/guid.h>
+#include <efi/services.h>
+#include <efi/error.h>
+#include <efi/initrd.h>
+#include <efi/devicepath.h>
+
+/**
+ * copy_fdt() - Copy the device tree to a new location available to EFI
+ *
+ * The FDT is copied to a suitable location within the EFI memory map.
+ * Additional 12 KiB are added to the space in case the device tree needs to be
+ * expanded later with fdt_open_into().
+ *
+ * @fdtp: On entry a pointer to the flattened device tree.
+ * On exit a pointer to the copy of the flattened device tree.
+ * FDT start
+ * Return: status code
+ */
+static efi_status_t copy_fdt(void **fdtp)
+{
+ efi_status_t efiret;
+ void *fdt, *new_fdt;
+ static u64 new_fdt_addr;
+ static efi_uintn_t fdt_pages;
+ size_t fdt_size;
+
+ /*
+ * Remove the configuration table that might already be
+ * installed, ignoring EFI_NOT_FOUND if no device-tree
+ * is installed
+ */
+ efi_install_configuration_table(&efi_fdt_guid, NULL);
+
+ if (new_fdt_addr) {
+ pr_debug("%s: Found allocated memory at %#llx, with %#zx pages\n",
+ __func__, new_fdt_addr, fdt_pages);
+
+ efiret = efi_free_pages(new_fdt_addr, fdt_pages);
+ if (efiret != EFI_SUCCESS)
+ pr_err("Unable to free up existing FDT memory region\n");
+
+ new_fdt_addr = 0;
+ fdt_pages = 0;
+ }
+
+ /*
+ * Give us at least 12 KiB of breathing room in case the device tree
+ * needs to be expanded later.
+ */
+ fdt = *fdtp;
+ fdt_pages = efi_size_in_pages(fdt_totalsize(fdt) + 0x3000);
+ fdt_size = fdt_pages << EFI_PAGE_SHIFT;
+
+ /*
+ * Safe fdt location is at 127 MiB.
+ * On the sandbox convert from the sandbox address space.
+ */
+ efiret = efi_allocate_pages(EFI_ALLOCATE_ANY_PAGES,
+ EFI_ACPI_RECLAIM_MEMORY, fdt_pages,
+ &new_fdt_addr, "FDT");
+ if (efiret != EFI_SUCCESS) {
+ pr_err("Failed to reserve space for FDT\n");
+ goto done;
+ }
+
+ new_fdt = (void *)(uintptr_t)new_fdt_addr;
+ memcpy(new_fdt, fdt, fdt_totalsize(fdt));
+ fdt_set_totalsize(new_fdt, fdt_size);
+
+ *fdtp = new_fdt;
+
+done:
+ return efiret;
+}
+
+/**
+ * efi_install_fdt() - install device tree
+ *
+ * If fdt is not NULL, the device tree located at that memory
+ * address will will be installed as configuration table, otherwise the device
+ * tree located at the address indicated by environment variable fdt_addr or as
+ * fallback fdtcontroladdr will be used.
+ *
+ * On architectures using ACPI tables device trees shall not be installed as
+ * configuration table.
+ *
+ * @fdt: address of device tree
+ * the hardware device tree as indicated by environment variable
+ * fdt_addr or as fallback the internal device tree as indicated by
+ * the environment variable fdtcontroladdr
+ * Return: status code
+ */
+static efi_status_t efi_install_fdt(void *fdt)
+{
+ /*
+ * The EBBR spec requires that we have either an FDT or an ACPI table
+ * but not both.
+ */
+ efi_status_t ret;
+
+ /* Install device tree */
+ if (fdt_check_header(fdt)) {
+ pr_err("invalid device tree\n");
+ return EFI_LOAD_ERROR;
+ }
+
+ /* Prepare device tree for payload */
+ ret = copy_fdt(&fdt);
+ if (ret) {
+ pr_err("out of memory\n");
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ /* Install device tree as UEFI table */
+ ret = efi_install_configuration_table(&efi_fdt_guid, fdt);
+ if (ret != EFI_SUCCESS) {
+ pr_err("failed to install device tree\n");
+ return ret;
+ }
+
+ return EFI_SUCCESS;
+}
+
+/**
+ * efi_install_initrd() - install initrd
+ *
+ * Install the initrd located at @initrd using the EFI_LOAD_FILE2
+ * protocol.
+ *
+ * @initrd: address of initrd or NULL if none is provided
+ * @initrd_sz: size of initrd
+ * Return: status code
+ */
+static efi_status_t efi_install_initrd(struct image_data *data,
+ struct resource *source)
+{
+ const struct resource *initrd_res;
+ unsigned long initrd_start;
+
+ if (!IS_ENABLED(CONFIG_BOOTM_INITRD))
+ return EFI_SUCCESS;
+
+ if (UIMAGE_IS_ADDRESS_VALID(data->initrd_address))
+ initrd_start = data->initrd_address;
+ else
+ initrd_start = EFI_PAGE_ALIGN(source->end + 1);
+
+ initrd_res = bootm_load_initrd(data, initrd_start);
+ if (IS_ERR(initrd_res))
+ return PTR_ERR(initrd_res);
+ if (initrd_res)
+ efi_initrd_register((void *)initrd_res->start,
+ resource_size(initrd_res));
+
+ return EFI_SUCCESS;
+}
+
+static int efi_loader_bootm(struct image_data *data)
+{
+ resource_size_t start, end;
+ void *load_option = NULL;
+ u32 load_option_size = 0;
+ efi_handle_t handle;
+ struct efi_device_path *file_path = NULL;
+ struct resource *source;
+ struct efi_event *evt;
+ size_t exit_data_size = 0;
+ u16 *exit_data = NULL;
+ efi_status_t efiret;
+ int ret = 0;
+ void *fdt;
+ int flags = 0;
+
+ memory_bank_first_find_space(&start, &end);
+ data->os_address = start;
+
+ source = file_to_sdram(data->os_file, data->os_address, MEMTYPE_LOADER_CODE);
+ if (!source)
+ return -EINVAL;
+
+ if (filetype_is_linux_efi_image(data->os_type)) {
+ const char *options;
+
+ options = linux_bootargs_get();
+ if (options) {
+ load_option = xstrdup_char_to_wchar(options);
+ load_option_size = (strlen(options) + 1) * sizeof(wchar_t);
+ }
+ }
+
+ file_path = efi_dp_from_file(AT_FDCWD, data->os_file);
+
+ pr_info("Loading %pD\n", file_path);
+
+ /* Initialize EFI drivers */
+ efiret = efi_init_obj_list();
+ if (efiret) {
+ pr_err("Cannot initialize UEFI sub-system: %pe\n",
+ ERR_PTR(-efi_errno(ret)));
+ goto out;
+ }
+
+ ret = -EINVAL;
+
+ fdt = bootm_get_devicetree(data);
+ if (IS_ERR(fdt))
+ return PTR_ERR(fdt);
+ if (fdt) {
+ ret = efi_install_fdt(fdt);
+ if (ret)
+ return ret;
+ }
+
+ efiret = efi_install_initrd(data, source);
+ if(efiret != EFI_SUCCESS)
+ goto out;
+
+ if (data->verbose >= 1)
+ flags |= EFI_VERBOSE_RUN;
+ if (data->dryrun)
+ flags |= EFI_DRYRUN;
+
+ efiret = efiloader_load_image(false, efi_root, file_path,
+ (void *)source->start,
+ resource_size(source), &handle);
+ if (efiret != EFI_SUCCESS) {
+ pr_err("Loading image failed\n");
+ goto out;
+ }
+
+ efiret = efi_set_load_options(handle, load_option_size, load_option);
+ if (efiret)
+ goto out;
+
+ /* On ARM, PBL should have already moved us into EL2 here, so
+ * no need to switch modes
+ */
+
+ /*
+ * The UEFI standard requires that the watchdog timer is set to five
+ * minutes when invoking an EFI boot option.
+ *
+ * Unified Extensible Firmware Interface (UEFI), version 2.7 Errata A
+ * 7.5. Miscellaneous Boot Services - EFI_BOOT_SERVICES.SetWatchdogTimer
+ */
+ ret = efi_set_watchdog(300);
+ if (ret != EFI_SUCCESS) {
+ pr_err("failed to set watchdog timer\n");
+ goto out;
+ }
+
+ /* Call our payload! */
+ ret = __efi_start_image(handle, &exit_data_size, &exit_data, flags);
+ if (ret != EFI_SUCCESS) {
+ pr_err("## Application failed, r = %lu\n",
+ ret & ~EFI_ERROR_MASK);
+ if (exit_data) {
+ pr_err("## %ls\n", exit_data);
+ efi_free_pool(exit_data);
+ }
+ }
+
+ /* Notify EFI_EVENT_GROUP_RETURN_TO_EFIBOOTMGR event group. */
+ list_for_each_entry(evt, &efi_events, link) {
+ if (evt->group &&
+ !efi_guidcmp(*evt->group,
+ efi_guid_event_group_return_to_efibootmgr)) {
+ efi_signal_event(evt);
+ systab.boottime->close_event(evt);
+ break;
+ }
+ }
+
+ /* Control is returned to us, disable EFI watchdog */
+ efi_set_watchdog(0);
+
+ return ret;
+
+out:
+ efi_initrd_unregister();
+ efi_install_configuration_table(&efi_fdt_guid, NULL);
+ efi_free_pool(file_path);
+ free(load_option);
+ release_sdram_region(source);
+
+ return ret ?: -efi_errno(efiret);
+}
+
+static struct image_handler riscv_linux_efi_handler = {
+ .name = "RISC-V Linux/EFI image",
+ .bootm = efi_loader_bootm,
+ .check_image = bootm_efi_check_image,
+ .filetype = filetype_riscv_efi_linux_image,
+};
+
+static struct image_handler aarch64_linux_efi_handler = {
+ .name = "ARM aarch64 Linux/EFI image",
+ .bootm = efi_loader_bootm,
+ .check_image = bootm_efi_check_image,
+ .filetype = filetype_arm64_efi_linux_image,
+};
+
+static struct image_handler arm32_linux_efi_handler = {
+ .name = "ARM arm32 Linux/EFI image",
+ .bootm = efi_loader_bootm,
+ .check_image = bootm_efi_check_image,
+ .filetype = filetype_arm_efi_zimage,
+};
+
+static struct image_handler efi_image_loader = {
+ .name = "EFI Application",
+ .bootm = efi_loader_bootm,
+ .filetype = filetype_exe,
+};
+
+static int efiloader_bootm_register(void)
+{
+ if (IS_ENABLED(CONFIG_ARM32))
+ register_image_handler(&arm32_linux_efi_handler);
+ if (IS_ENABLED(CONFIG_ARM64))
+ register_image_handler(&aarch64_linux_efi_handler);
+ if (IS_ENABLED(CONFIG_RISCV))
+ register_image_handler(&riscv_linux_efi_handler);
+ return register_image_handler(&efi_image_loader);
+}
+late_initcall(efiloader_bootm_register);
--
2.47.3
^ permalink raw reply [flat|nested] 55+ messages in thread* [PATCH v1 46/54] efi: runtime: add EFI variable support
2025-12-18 10:37 [PATCH v1 00/54] efi: implement EFI loader support in barebox Ahmad Fatoum
` (44 preceding siblings ...)
2025-12-18 10:38 ` [PATCH v1 45/54] efi: loader: implement bootm handler Ahmad Fatoum
@ 2025-12-18 10:38 ` Ahmad Fatoum
2025-12-18 10:38 ` [PATCH v1 47/54] efi: loader: populate OsIndicationsSupported/PlatformLang variables Ahmad Fatoum
` (7 subsequent siblings)
53 siblings, 0 replies; 55+ messages in thread
From: Ahmad Fatoum @ 2025-12-18 10:38 UTC (permalink / raw)
To: barebox; +Cc: Ahmad Fatoum
The in-memory EFI variable support in U-Boot is pretty self-contained,
so import it as-is to barebox. This is not power-fail safe and we'll
probably want to add StandAloneMM support for reliable use later.
Signed-off-by: Ahmad Fatoum <a.fatoum@pengutronix.de>
---
common/Kconfig.debug | 16 ++
efi/Makefile | 1 +
efi/loader/Kconfig | 29 +++
efi/loader/Makefile | 4 +
efi/loader/efi_var_common.c | 475 ++++++++++++++++++++++++++++++++++
efi/loader/efi_var_file.c | 203 +++++++++++++++
efi/loader/efi_var_mem.c | 80 ++++++
efi/loader/efi_variable.c | 214 +++++++++++++++
efi/loader/protocols/disk.c | 11 +
efi/loader/runtime.c | 10 +-
efi/loader/setup.c | 1 +
efi/loader/variable.h | 33 +++
efi/runtime/Makefile | 43 +++
efi/runtime/common.h | 35 +++
efi/runtime/efi_var_mem.c | 380 +++++++++++++++++++++++++++
efi/runtime/efi_variable.c | 211 +++++++++++++++
include/efi/loader/variable.h | 153 +++++++++++
include/efi/runtime.h | 164 ++++++++++++
include/efi/variable.h | 7 +-
19 files changed, 2065 insertions(+), 5 deletions(-)
create mode 100644 efi/loader/efi_var_common.c
create mode 100644 efi/loader/efi_var_file.c
create mode 100644 efi/loader/efi_var_mem.c
create mode 100644 efi/loader/efi_variable.c
create mode 100644 efi/loader/variable.h
create mode 100644 efi/runtime/Makefile
create mode 100644 efi/runtime/common.h
create mode 100644 efi/runtime/efi_var_mem.c
create mode 100644 efi/runtime/efi_variable.c
create mode 100644 include/efi/loader/variable.h
create mode 100644 include/efi/runtime.h
diff --git a/common/Kconfig.debug b/common/Kconfig.debug
index 64127143a5d6..b307b84202a3 100644
--- a/common/Kconfig.debug
+++ b/common/Kconfig.debug
@@ -84,6 +84,22 @@ config DEBUG_EFI_LOADER_ENTRY
If enabled, this will print whenever an EFI payload/app calls
into barebox or is returned to before ExitBootServices.
+config DEBUG_EFI_RUNTIME_ENTRY
+ bool "Debug EFI runtime entry/exit"
+ depends on EFI_RUNTIME
+ depends on DEBUG_LL
+ help
+ If enabled this will print whenever the operating system calls
+ into barebox or is returned to after ExitBootServices.
+
+ This requires DEBUG_LL support and board-specific setup, so
+ any UART MMIO regions needed for the barebox runtime services
+ are marked as such in the UEFI memory map.
+
+ This option is most convenient to use with a second UART that
+ the OS doesn't use, e.g. CONFIG_DEBUG_SEMIHOSTING when running
+ under QEMU.
+
config DMA_API_DEBUG
bool "Enable debugging of DMA-API usage"
depends on HAS_DMA
diff --git a/efi/Makefile b/efi/Makefile
index e0f6ac549009..7032e1fe1ded 100644
--- a/efi/Makefile
+++ b/efi/Makefile
@@ -2,6 +2,7 @@
obj-$(CONFIG_EFI_PAYLOAD) += payload/
obj-$(CONFIG_EFI_LOADER) += loader/
+obj-$(CONFIG_EFI_RUNTIME) += runtime/
obj-$(CONFIG_EFI_GUID) += guid.o
obj-$(CONFIG_EFI_DEVICEPATH) += devicepath.o
obj-y += errno.o handle.o efivar.o efivar-filename.o
diff --git a/efi/loader/Kconfig b/efi/loader/Kconfig
index ca0ec6b5010a..4a5e4c375fd4 100644
--- a/efi/loader/Kconfig
+++ b/efi/loader/Kconfig
@@ -21,6 +21,9 @@ config EFI_LOADER_DEBUG_SUPPORT
agent or an external debugger to determine loaded image information
in a quiescent manner.
+config EFI_LOADER_SECURE_BOOT
+ bool
+
menu "UEFI services"
config EFI_LOADER_GET_TIME
@@ -39,6 +42,32 @@ config EFI_LOADER_SET_TIME
Provide the SetTime() runtime service at boottime. This service
can be used by an EFI application to adjust the real time clock.
+choice
+ prompt "Store for non-volatile UEFI variables"
+ default EFI_VARIABLE_FILE_STORE
+ help
+ Select where non-volatile UEFI variables shall be stored.
+
+config EFI_VARIABLE_FILE_STORE
+ bool "Store non-volatile UEFI variables as file"
+ depends on FS_FAT_WRITE
+ select EFI_RUNTIME_GET_VARIABLE
+ select EFI_RUNTIME_GET_NEXT_VARIABLE_NAME
+ select EFI_RUNTIME_QUERY_VARIABLE_INFO
+ help
+ Select this option if you want non-volatile UEFI variables to be
+ stored under the file name indicated by ${efi.vars.filestore}
+ on the EFI system partition.
+
+config EFI_VARIABLE_NO_STORE
+ bool "Don't persist non-volatile UEFI variables"
+ help
+ If you choose this option, non-volatile variables cannot be persisted.
+ You could still provide non-volatile variables via the barebox
+ environment.
+
+endchoice
+
endmenu
source "efi/loader/protocols/Kconfig"
diff --git a/efi/loader/Makefile b/efi/loader/Makefile
index 2a78361e58d5..5fd74a2e0c42 100644
--- a/efi/loader/Makefile
+++ b/efi/loader/Makefile
@@ -15,3 +15,7 @@ obj-y += loadopts.o
obj-$(CONFIG_BOOT) += bootesp.o
obj-$(CONFIG_EFI_LOADER_BOOTMGR) += efibootmgr.o
obj-$(CONFIG_BOOTM) += bootm.o
+obj-y += efi_var_common.o
+obj-y += efi_variable.o
+obj-y += efi_var_mem.o
+obj-y += efi_var_file.o
diff --git a/efi/loader/efi_var_common.c b/efi/loader/efi_var_common.c
new file mode 100644
index 000000000000..bd5af007ed9c
--- /dev/null
+++ b/efi/loader/efi_var_common.c
@@ -0,0 +1,475 @@
+// SPDX-License-Identifier: GPL-2.0-only
+// SPDX-Comment: Origin-URL: https://github.com/u-boot/u-boot/blob/e9c34fab18a9a0022b36729afd8e262e062764e2/lib/efi_loader/efi_var_common.c
+/*
+ * UEFI runtime variable services
+ *
+ * Copyright (c) 2020, Heinrich Schuchardt <xypron.glpk@gmx.de>
+ * Copyright (c) 2020 Linaro Limited, Author: AKASHI Takahiro
+ */
+
+#define pr_fmt(fmt) "efi-loader: efibootmgr: " fmt
+
+#include <linux/kernel.h>
+#include <efi/loader/variable.h>
+#include <efi/loader/trace.h>
+#include <efi/variable.h>
+#include <efi/runtime.h>
+#include <efi/error.h>
+#include <efi/guid.h>
+#include <efi/variable.h>
+#include <stdlib.h>
+#include <crc.h>
+
+#include "variable.h"
+
+enum efi_secure_mode {
+ EFI_MODE_SETUP,
+ EFI_MODE_USER,
+ EFI_MODE_AUDIT,
+ EFI_MODE_DEPLOYED,
+};
+
+struct efi_auth_var_name_type {
+ const u16 *name;
+ const efi_guid_t *guid;
+ const enum efi_auth_var_type type;
+};
+
+static const struct efi_auth_var_name_type name_type[] = {
+ {u"PK", &efi_global_variable_guid, EFI_AUTH_VAR_PK},
+ {u"KEK", &efi_global_variable_guid, EFI_AUTH_VAR_KEK},
+ {u"db", &efi_guid_image_security_database, EFI_AUTH_VAR_DB},
+ {u"dbx", &efi_guid_image_security_database, EFI_AUTH_VAR_DBX},
+ {u"dbt", &efi_guid_image_security_database, EFI_AUTH_VAR_DBT},
+ {u"dbr", &efi_guid_image_security_database, EFI_AUTH_VAR_DBR},
+ {u"AuditMode", &efi_global_variable_guid, EFI_AUTH_MODE},
+ {u"DeployedMode", &efi_global_variable_guid, EFI_AUTH_MODE},
+};
+
+static bool efi_secure_boot;
+static enum efi_secure_mode efi_secure_mode;
+
+/**
+ * efi_get_variable_boot() - retrieve value of a UEFI variable
+ *
+ * This function implements the GetVariable runtime service.
+ *
+ * See the Unified Extensible Firmware Interface (UEFI) specification for
+ * details.
+ *
+ * @variable_name: name of the variable
+ * @vendor: vendor GUID
+ * @attributes: attributes of the variable
+ * @data_size: size of the buffer to which the variable value is copied
+ * @data: buffer to which the variable value is copied
+ * Return: status code
+ */
+efi_status_t EFIAPI efi_get_variable_boot(u16 *variable_name,
+ const efi_guid_t *vendor, u32 *attributes,
+ efi_uintn_t *data_size, void *data)
+{
+ efi_status_t ret;
+
+ EFI_ENTRY("\"%ls\" %pUs %p %p %p", variable_name, vendor, attributes,
+ data_size, data);
+
+ ret = efi_get_variable_int(variable_name, vendor, attributes,
+ data_size, data, NULL);
+
+ /* Remove EFI_VARIABLE_READ_ONLY flag */
+ if (attributes)
+ *attributes &= EFI_VARIABLE_MASK;
+
+ return EFI_EXIT(ret);
+}
+
+/**
+ * efi_set_variable_boot() - set value of a UEFI variable
+ *
+ * This function implements the SetVariable runtime service.
+ *
+ * See the Unified Extensible Firmware Interface (UEFI) specification for
+ * details.
+ *
+ * @variable_name: name of the variable
+ * @vendor: vendor GUID
+ * @attributes: attributes of the variable
+ * @data_size: size of the buffer with the variable value
+ * @data: buffer with the variable value
+ * Return: status code
+ */
+efi_status_t EFIAPI efi_set_variable_boot(u16 *variable_name,
+ const efi_guid_t *vendor, u32 attributes,
+ efi_uintn_t data_size, const void *data)
+{
+ efi_status_t ret;
+
+ EFI_ENTRY("\"%ls\" %pUs %x %zu %p", variable_name, vendor, attributes,
+ data_size, data);
+
+ /* Make sure that the EFI_VARIABLE_READ_ONLY flag is not set */
+ if (attributes & ~EFI_VARIABLE_MASK)
+ ret = EFI_INVALID_PARAMETER;
+ else
+ ret = efi_set_variable_int(variable_name, vendor, attributes,
+ data_size, data, true);
+
+ return EFI_EXIT(ret);
+}
+
+/**
+ * efi_get_next_variable_name_boot() - enumerate the current variable names
+ *
+ * @variable_name_size: size of variable_name buffer in byte
+ * @variable_name: name of uefi variable's name in u16
+ * @vendor: vendor's guid
+ *
+ * See the Unified Extensible Firmware Interface (UEFI) specification for
+ * details.
+ *
+ * Return: status code
+ */
+efi_status_t EFIAPI efi_get_next_variable_name_boot(efi_uintn_t *variable_name_size,
+ u16 *variable_name,
+ efi_guid_t *vendor)
+{
+ efi_status_t ret;
+
+ EFI_ENTRY("%p \"%ls\" %pUs", variable_name_size, variable_name, vendor);
+
+ ret = efi_get_next_variable_name_int(variable_name_size, variable_name,
+ vendor);
+
+ return EFI_EXIT(ret);
+}
+
+/**
+ * efi_query_variable_info_boot() - get information about EFI variables
+ *
+ * This function implements the QueryVariableInfo() runtime service.
+ *
+ * See the Unified Extensible Firmware Interface (UEFI) specification for
+ * details.
+ *
+ * @attributes: bitmask to select variables to be
+ * queried
+ * @maximum_variable_storage_size: maximum size of storage area for the
+ * selected variable types
+ * @remaining_variable_storage_size: remaining size of storage are for the
+ * selected variable types
+ * @maximum_variable_size: maximum size of a variable of the
+ * selected type
+ * Returns: status code
+ */
+efi_status_t EFIAPI efi_query_variable_info_boot(
+ u32 attributes, u64 *maximum_variable_storage_size,
+ u64 *remaining_variable_storage_size,
+ u64 *maximum_variable_size)
+{
+ efi_status_t ret;
+
+ EFI_ENTRY("%x %p %p %p", attributes, maximum_variable_storage_size,
+ remaining_variable_storage_size, maximum_variable_size);
+
+ ret = efi_query_variable_info_int(attributes,
+ maximum_variable_storage_size,
+ remaining_variable_storage_size,
+ maximum_variable_size);
+
+ return EFI_EXIT(ret);
+}
+
+/**
+ * efi_set_secure_state - modify secure boot state variables
+ * @secure_boot: value of SecureBoot
+ * @setup_mode: value of SetupMode
+ * @audit_mode: value of AuditMode
+ * @deployed_mode: value of DeployedMode
+ *
+ * Modify secure boot status related variables as indicated.
+ *
+ * Return: status code
+ */
+static efi_status_t efi_set_secure_state(u8 secure_boot, u8 setup_mode,
+ u8 audit_mode, u8 deployed_mode)
+{
+ efi_status_t ret;
+ const u32 attributes_ro = EFI_VARIABLE_BOOTSERVICE_ACCESS |
+ EFI_VARIABLE_RUNTIME_ACCESS |
+ EFI_VARIABLE_READ_ONLY;
+ const u32 attributes_rw = EFI_VARIABLE_BOOTSERVICE_ACCESS |
+ EFI_VARIABLE_RUNTIME_ACCESS;
+
+ efi_secure_boot = secure_boot;
+
+ ret = efi_set_variable_int(u"SecureBoot", &efi_global_variable_guid,
+ attributes_ro, sizeof(secure_boot),
+ &secure_boot, false);
+ if (ret != EFI_SUCCESS)
+ goto err;
+
+ ret = efi_set_variable_int(u"SetupMode", &efi_global_variable_guid,
+ attributes_ro, sizeof(setup_mode),
+ &setup_mode, false);
+ if (ret != EFI_SUCCESS)
+ goto err;
+
+ ret = efi_set_variable_int(u"AuditMode", &efi_global_variable_guid,
+ audit_mode || setup_mode ?
+ attributes_ro : attributes_rw,
+ sizeof(audit_mode), &audit_mode, false);
+ if (ret != EFI_SUCCESS)
+ goto err;
+
+ ret = efi_set_variable_int(u"DeployedMode",
+ &efi_global_variable_guid,
+ audit_mode || deployed_mode || setup_mode ?
+ attributes_ro : attributes_rw,
+ sizeof(deployed_mode), &deployed_mode,
+ false);
+err:
+ return ret;
+}
+
+/**
+ * efi_transfer_secure_state - handle a secure boot state transition
+ * @mode: new state
+ *
+ * Depending on @mode, secure boot related variables are updated.
+ * Those variables are *read-only* for users, efi_set_variable_int()
+ * is called here.
+ *
+ * Return: status code
+ */
+static efi_status_t efi_transfer_secure_state(enum efi_secure_mode mode)
+{
+ efi_status_t ret;
+
+ EFI_PRINT("Switching secure state from %d to %d\n", efi_secure_mode,
+ mode);
+
+ if (mode == EFI_MODE_DEPLOYED) {
+ ret = efi_set_secure_state(1, 0, 0, 1);
+ if (ret != EFI_SUCCESS)
+ goto err;
+ } else if (mode == EFI_MODE_AUDIT) {
+ ret = efi_set_variable_int(u"PK", &efi_global_variable_guid,
+ EFI_VARIABLE_BOOTSERVICE_ACCESS |
+ EFI_VARIABLE_RUNTIME_ACCESS,
+ 0, NULL, false);
+ if (ret != EFI_SUCCESS)
+ goto err;
+
+ ret = efi_set_secure_state(0, 1, 1, 0);
+ if (ret != EFI_SUCCESS)
+ goto err;
+ } else if (mode == EFI_MODE_USER) {
+ ret = efi_set_secure_state(1, 0, 0, 0);
+ if (ret != EFI_SUCCESS)
+ goto err;
+ } else if (mode == EFI_MODE_SETUP) {
+ ret = efi_set_secure_state(0, 1, 0, 0);
+ if (ret != EFI_SUCCESS)
+ goto err;
+ } else {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ efi_secure_mode = mode;
+
+ return EFI_SUCCESS;
+
+err:
+ /* TODO: What action should be taken here? */
+ pr_err("Secure state transition failed\n");
+ return ret;
+}
+
+efi_status_t efi_init_secure_state(void)
+{
+ enum efi_secure_mode mode;
+ u8 efi_vendor_keys = 0;
+ efi_uintn_t size;
+ efi_status_t ret;
+ u8 deployed_mode = 0;
+ u8 audit_mode = 0;
+ u8 setup_mode = 1;
+
+ if (IS_ENABLED(CONFIG_EFI_LOADER_SECURE_BOOT)) {
+ size = sizeof(deployed_mode);
+ ret = efi_get_variable_int(u"DeployedMode", &efi_global_variable_guid,
+ NULL, &size, &deployed_mode, NULL);
+ size = sizeof(audit_mode);
+ ret = efi_get_variable_int(u"AuditMode", &efi_global_variable_guid,
+ NULL, &size, &audit_mode, NULL);
+ size = 0;
+ ret = efi_get_variable_int(u"PK", &efi_global_variable_guid,
+ NULL, &size, NULL, NULL);
+ if (ret == EFI_BUFFER_TOO_SMALL) {
+ setup_mode = 0;
+ audit_mode = 0;
+ } else {
+ setup_mode = 1;
+ deployed_mode = 0;
+ }
+ }
+ if (deployed_mode)
+ mode = EFI_MODE_DEPLOYED;
+ else if (audit_mode)
+ mode = EFI_MODE_AUDIT;
+ else if (setup_mode)
+ mode = EFI_MODE_SETUP;
+ else
+ mode = EFI_MODE_USER;
+
+ ret = efi_transfer_secure_state(mode);
+ if (ret != EFI_SUCCESS)
+ return ret;
+
+ /* As we do not provide vendor keys this variable is always 0. */
+ ret = efi_set_variable_int(u"VendorKeys",
+ &efi_global_variable_guid,
+ EFI_VARIABLE_BOOTSERVICE_ACCESS |
+ EFI_VARIABLE_RUNTIME_ACCESS |
+ EFI_VARIABLE_READ_ONLY,
+ sizeof(efi_vendor_keys),
+ &efi_vendor_keys, false);
+ return ret;
+}
+
+/**
+ * efi_secure_boot_enabled - return if secure boot is enabled or not
+ *
+ * Return: true if enabled, false if disabled
+ */
+bool efi_secure_boot_enabled(void)
+{
+ return efi_secure_boot;
+}
+
+enum efi_auth_var_type efi_auth_var_get_type(const u16 *name,
+ const efi_guid_t *guid)
+{
+ for (size_t i = 0; i < ARRAY_SIZE(name_type); ++i) {
+ if (!wcscmp(name, name_type[i].name) &&
+ !efi_guidcmp(*guid, *name_type[i].guid))
+ return name_type[i].type;
+ }
+ return EFI_AUTH_VAR_NONE;
+}
+
+const efi_guid_t *efi_auth_var_get_guid(const u16 *name)
+{
+ for (size_t i = 0; i < ARRAY_SIZE(name_type); ++i) {
+ if (!wcscmp(name, name_type[i].name))
+ return name_type[i].guid;
+ }
+ return &efi_global_variable_guid;
+}
+
+/**
+ * efi_get_var() - read value of an EFI variable
+ *
+ * @name: variable name
+ * @start: vendor GUID
+ * @size: size of allocated buffer
+ *
+ * Return: buffer with variable data or NULL
+ */
+void *efi_get_var(const u16 *name, const efi_guid_t *vendor, efi_uintn_t *size)
+{
+ efi_status_t ret;
+ void *buf = NULL;
+
+ *size = 0;
+ ret = efi_get_variable_int(name, vendor, NULL, size, buf, NULL);
+ if (ret == EFI_BUFFER_TOO_SMALL) {
+ buf = malloc(*size);
+ if (!buf)
+ return NULL;
+ ret = efi_get_variable_int(name, vendor, NULL, size, buf, NULL);
+ }
+
+ if (ret != EFI_SUCCESS) {
+ free(buf);
+ *size = 0;
+ return NULL;
+ }
+
+ return buf;
+}
+
+/**
+ * efi_var_collect() - Copy EFI variables matching attributes mask
+ *
+ * @bufp: buffer containing variable collection
+ * @lenp: buffer length
+ * @attr_mask: mask of matched attributes
+ *
+ * Return: Status code
+ */
+efi_status_t __maybe_unused efi_var_collect(struct efi_var_file **bufp, loff_t *lenp,
+ u32 check_attr_mask)
+{
+ unsigned len;
+ struct efi_var_file *buf;
+ struct efi_var_entry *var, *old_var;
+ size_t old_var_name_length = 2;
+
+ efi_var_buf_get(&len);
+
+ *bufp = NULL; /* Avoid double free() */
+ buf = calloc(1, len);
+ if (!buf)
+ return EFI_OUT_OF_RESOURCES;
+ var = buf->var;
+ old_var = var;
+ for (;;) {
+ efi_uintn_t data_length, var_name_length;
+ u8 *data;
+ efi_status_t ret;
+
+ if ((uintptr_t)buf + len <=
+ (uintptr_t)var->name + old_var_name_length)
+ return EFI_BUFFER_TOO_SMALL;
+
+ var_name_length = (uintptr_t)buf + len - (uintptr_t)var->name;
+ memcpy(var->name, old_var->name, old_var_name_length);
+ var->guid = old_var->guid;
+ ret = efi_get_next_variable_name_int(
+ &var_name_length, var->name, &var->guid);
+ if (ret == EFI_NOT_FOUND)
+ break;
+ if (ret != EFI_SUCCESS) {
+ free(buf);
+ return ret;
+ }
+ old_var_name_length = var_name_length;
+ old_var = var;
+
+ data = (u8 *)var->name + old_var_name_length;
+ data_length = (uintptr_t)buf + len - (uintptr_t)data;
+ ret = efi_get_variable_int(var->name, &var->guid,
+ &var->attr, &data_length, data,
+ &var->time);
+ if (ret != EFI_SUCCESS) {
+ free(buf);
+ return ret;
+ }
+ if ((var->attr & check_attr_mask) == check_attr_mask) {
+ var->length = data_length;
+ var = (struct efi_var_entry *)ALIGN((uintptr_t)data + data_length, 8);
+ }
+ }
+
+ buf->reserved = 0;
+ buf->magic = EFI_VAR_FILE_MAGIC;
+ len = (uintptr_t)var - (uintptr_t)buf;
+ buf->crc32 = crc32(0, (u8 *)buf->var,
+ len - sizeof(struct efi_var_file));
+ buf->length = len;
+ *bufp = buf;
+ *lenp = len;
+
+ return EFI_SUCCESS;
+}
diff --git a/efi/loader/efi_var_file.c b/efi/loader/efi_var_file.c
new file mode 100644
index 000000000000..08a5c172cced
--- /dev/null
+++ b/efi/loader/efi_var_file.c
@@ -0,0 +1,203 @@
+// SPDX-License-Identifier: GPL-2.0+
+// SPDX-Comment: Origin-URL: https://github.com/u-boot/u-boot/blob/bc4fe5666dae6ab01a433970c3f5e6eb4833ebe7/lib/efi_loader/efi_var_file.c
+/*
+ * File interface for UEFI variables
+ *
+ * Copyright (c) 2020, Heinrich Schuchardt
+ */
+
+#define pr_fmt(fmt) "efi-loader: var-file: " fmt
+
+#include <linux/kernel.h>
+#include <efi/loader.h>
+#include <efi/mode.h>
+#include <efi/loader/variable.h>
+#include <efi/variable.h>
+#include <efi/runtime.h>
+#include <efi/error.h>
+#include <efi/guid.h>
+#include <stdlib.h>
+#include <crc.h>
+#include <fs.h>
+#include <libfile.h>
+#include <string.h>
+#include <globalvar.h>
+#include <magicvar.h>
+
+#include "variable.h"
+
+/* Filename within ESP to save/restore EFI variables from
+ * when CONFIG_EFI_VARIABLE_FILE_STORE=y
+ */
+char *efi_var_file_name;
+
+/**
+ * efi_var_to_file() - save non-volatile variables as file
+ *
+ * File indiciated by efi_var_file_name with EFI variables is
+ * created on the EFI system partion.
+ *
+ * Return: status code
+ */
+efi_status_t efi_var_to_file(void)
+{
+ efi_status_t efiret;
+ struct efi_var_file *buf;
+ loff_t len;
+ int err, fd, dirfd;
+ static bool once;
+
+ if (!IS_ENABLED(CONFIG_EFI_VARIABLE_FILE_STORE))
+ return EFI_SUCCESS;
+
+ efiret = efi_var_collect(&buf, &len, EFI_VARIABLE_NON_VOLATILE);
+ if (efiret != EFI_SUCCESS) {
+ err = ENOMEM;
+ goto error;
+ }
+
+ dirfd = efiloader_esp_mount_dir();
+ if (dirfd < 0) {
+ if (!once) {
+ pr_warn("Cannot persist EFI variables without system partition\n");
+ once = true;
+ }
+ efiret = EFI_NO_MEDIA;
+ err = dirfd;
+ goto out;
+ }
+
+ // TODO: replace with rename
+ fd = openat(dirfd, efi_var_file_name, O_WRONLY | O_TRUNC | O_CREAT);
+ if (fd < 0) {
+ efiret = EFI_DEVICE_ERROR;
+ err = fd;
+ goto error;
+ }
+
+ err = write_full(fd, buf, len);
+
+ close(fd);
+
+ if (err < 0) {
+ efiret = EFI_DEVICE_ERROR;
+ goto error;
+ }
+
+ return 0;
+
+error:
+ if (efiret != EFI_SUCCESS)
+ pr_err("Failed to persist EFI variables %pe\n", ERR_PTR(err));
+
+out:
+ free(buf);
+ return efiret;
+}
+
+static efi_status_t efi_var_restore(struct efi_var_file *buf, bool safe)
+{
+ struct efi_var_entry *var, *last_var;
+ u16 *data;
+ efi_status_t ret;
+
+ if (buf->reserved || buf->magic != EFI_VAR_FILE_MAGIC ||
+ buf->crc32 != crc32(0, (u8 *)buf->var,
+ buf->length - sizeof(struct efi_var_file))) {
+ pr_err("Invalid EFI variables file\n");
+ return EFI_INVALID_PARAMETER;
+ }
+
+ last_var = (struct efi_var_entry *)((u8 *)buf + buf->length);
+ for (var = buf->var; var < last_var;
+ var = (struct efi_var_entry *)
+ ALIGN((uintptr_t)data + var->length, 8)) {
+
+ data = var->name + wcslen(var->name) + 1;
+
+ /*
+ * Secure boot related and volatile variables shall only be
+ * restored from the preseed, but we didn't implement this yet.
+ */
+ if (!safe &&
+ (efi_auth_var_get_type(var->name, &var->guid) !=
+ EFI_AUTH_VAR_NONE ||
+ !efi_guidcmp(var->guid, shim_lock_guid) ||
+ !(var->attr & EFI_VARIABLE_NON_VOLATILE)))
+ continue;
+ if (!var->length)
+ continue;
+ if (efi_var_mem_find(&var->guid, var->name, NULL))
+ continue;
+ ret = efi_var_mem_ins(var->name, &var->guid, var->attr,
+ var->length, data, 0, NULL,
+ var->time);
+ if (ret != EFI_SUCCESS)
+ pr_err("Failed to set EFI variable %ls\n", var->name);
+ }
+ return EFI_SUCCESS;
+}
+
+/**
+ * efi_var_from_file() - read variables from file
+ *
+ * Specified file is read from the EFI system partitions and the variables
+ * stored in the file are created.
+ *
+ * On first boot the file may not exist yet. This is why we must
+ * return EFI_SUCCESS in this case.
+ *
+ * If the variable file is corrupted, e.g. incorrect CRC32, we do not want to
+ * stop the boot process. We deliberately return EFI_SUCCESS in this case, too.
+ *
+ * @dirfd directory fd
+ * @filename name of the file to load variables from
+ * Return: status code
+ */
+efi_status_t efi_var_from_file(int dirfd, const char *filename)
+{
+ struct efi_var_file *buf;
+ efi_status_t ret;
+ size_t len;
+ int fd;
+
+ fd = openat(dirfd, filename, O_RDONLY);
+ if (fd < 0)
+ return EFI_NOT_FOUND;
+
+ buf = read_fd(fd, &len);
+
+ close(fd);
+
+ if (!buf || len < sizeof(struct efi_var_file)) {
+ ret = EFI_NOT_FOUND;
+ goto error;
+ }
+
+ if (buf->length != len || efi_var_restore(buf, false) != EFI_SUCCESS) {
+ ret = EFI_CRC_ERROR;
+ goto error;
+ }
+
+ return EFI_SUCCESS;
+error:
+ free(buf);
+ return ret;
+}
+static int efi_init_var_params(void)
+{
+ if (efi_is_payload())
+ return 0;
+
+ /* Not 8.3, but brboxefi.var looks just too ugly */
+ efi_var_file_name = xstrdup("barebox.efivars");
+ dev_add_param_string(&efidev, "vars.filestore", NULL, NULL,
+ &efi_var_file_name, NULL);
+
+
+ return 0;
+}
+late_initcall(efi_init_var_params);
+
+BAREBOX_MAGICVAR(efi.vars.filestore,
+ "efiloader: Name of file within ESP to store variables to (WARNING: Not power-fail safe!)");
diff --git a/efi/loader/efi_var_mem.c b/efi/loader/efi_var_mem.c
new file mode 100644
index 000000000000..d2c89d4b0851
--- /dev/null
+++ b/efi/loader/efi_var_mem.c
@@ -0,0 +1,80 @@
+// SPDX-License-Identifier: GPL-2.0+
+// SPDX-Comment: Origin-URL: https://github.com/u-boot/u-boot/blob/530e869ff89d9575103637af201fe97864d4f577/lib/efi_loader/efi_var_mem.c
+/*
+ * File interface for UEFI variables
+ *
+ * Copyright (c) 2020, Heinrich Schuchardt
+ */
+
+#define pr_fmt(fmt) "efi-loader: variable-mem: " fmt
+
+#include <linux/kernel.h>
+#include <linux/sizes.h>
+#include <efi/loader/variable.h>
+#include <efi/runtime.h>
+#include <efi/mode.h>
+#include <efi/services.h>
+#include <efi/loader.h>
+#include <efi/variable.h>
+#include <efi/error.h>
+#include <param.h>
+#include <magicvar.h>
+
+static unsigned efi_var_buf_size = SZ_128K;
+
+efi_status_t efi_var_mem_init(void)
+{
+ struct efi_var_file *efi_var_buf;
+ u64 memory;
+ efi_status_t ret;
+
+ ret = efi_allocate_pages(EFI_ALLOCATE_ANY_PAGES,
+ EFI_RUNTIME_SERVICES_DATA,
+ efi_size_in_pages(efi_var_buf_size),
+ &memory, "vars");
+ if (ret != EFI_SUCCESS)
+ return ret;
+ efi_var_buf = (struct efi_var_file *)(uintptr_t)memory;
+ memset(efi_var_buf, 0, efi_var_buf_size);
+ efi_var_buf->magic = EFI_VAR_FILE_MAGIC;
+ efi_var_buf->length = (uintptr_t)efi_var_buf->var -
+ (uintptr_t)efi_var_buf;
+
+ /* we would have to register EFI_EVT_SIGNAL_VIRTUAL_ADDRESS_CHANGE otherwise */
+ static_assert(!IS_ENABLED(EFI_RUNTIME_SET_VIRTUAL_ADDRESS_MAP));
+
+ efi_var_buf_set(efi_var_buf, efi_var_buf_size);
+
+ return EFI_SUCCESS;
+}
+
+void efi_var_buf_update(struct efi_var_file *var_buf)
+{
+ unsigned len;
+ struct efi_var_file *dst = efi_var_buf_get(&len);
+
+ memcpy(dst, var_buf, len);
+}
+
+static int efi_var_buf_size_set(struct param_d *param, void *priv)
+{
+ if (!efi_var_buf_size || efi_var_buf_size % EFI_PAGE_SIZE)
+ return -EINVAL;
+
+ return 0;
+}
+
+static int efi_init_var_params(void)
+{
+ if (efi_is_payload())
+ return 0;
+
+ dev_add_param_uint32(&efidev, "vars.bufsize", efi_var_buf_size_set, NULL,
+ &efi_var_buf_size, "%u", NULL);
+
+ return 0;
+}
+late_initcall(efi_init_var_params);
+
+BAREBOX_MAGICVAR(efi.vars.bufsize,
+ "efiloader: Size in bytes of the memory area reserved for keeping UEFI variables");
diff --git a/efi/loader/efi_variable.c b/efi/loader/efi_variable.c
new file mode 100644
index 000000000000..e5b03c26edd0
--- /dev/null
+++ b/efi/loader/efi_variable.c
@@ -0,0 +1,214 @@
+// SPDX-License-Identifier: GPL-2.0+
+// SPDX-Comment: Origin-URL: https://github.com/u-boot/u-boot/blob/58d825fb18053ec7e432de7bbb70a452b9228076/lib/efi_loader/efi_variable.c
+/*
+ * UEFI runtime variable services
+ *
+ * Copyright (c) 2017 Rob Clark
+ */
+
+#define pr_fmt(fmt) "efi-loader: variable: " fmt
+
+#include <linux/kernel.h>
+#include <efi/loader.h>
+#include <efi/error.h>
+#include <efi/variable.h>
+#include <efi/runtime.h>
+#include <efi/loader/trace.h>
+#include <efi/loader/variable.h>
+#include <stdlib.h>
+#include <crc.h>
+#include <fcntl.h>
+
+#include "variable.h"
+
+/* Initial set of efi variables. This is not written back and
+ * usually from barebox' built-in env
+ */
+#define BAREBOX_ENV_INIT_EFIVARS "/env/data/init.efivars"
+
+/**
+ * efi_variable_authenticate - authenticate a variable
+ * @variable: Variable name in u16
+ * @vendor: Guid of variable
+ * @data_size: Size of @data
+ * @data: Pointer to variable's value
+ * @given_attr: Attributes to be given at SetVariable()
+ * @env_attr: Attributes that an existing variable holds
+ * @time: signed time that an existing variable holds
+ *
+ *
+ * Return: success as variable auth is not yet supported
+ */
+static efi_status_t efi_variable_authenticate(const u16 *variable,
+ const efi_guid_t *vendor,
+ efi_uintn_t *data_size,
+ const void **data, u32 given_attr,
+ u32 *env_attr, u64 *time)
+{
+ return EFI_SUCCESS;
+}
+
+efi_status_t efi_set_variable_int(const u16 *variable_name,
+ const efi_guid_t *vendor,
+ u32 attributes, efi_uintn_t data_size,
+ const void *data, bool ro_check)
+{
+ struct efi_var_entry *var;
+ efi_uintn_t ret;
+ bool append, delete;
+ u64 time = 0;
+ enum efi_auth_var_type var_type;
+
+ ret = efi_setvariable_allowed(variable_name, vendor, attributes, data_size,
+ data);
+ if (ret != EFI_SUCCESS)
+ return ret;
+
+ /* check if a variable exists */
+ var = efi_var_mem_find(vendor, variable_name, NULL);
+ append = !!(attributes & EFI_VARIABLE_APPEND_WRITE);
+ delete = !append && (!data_size || !attributes);
+
+ /* check attributes */
+ var_type = efi_auth_var_get_type(variable_name, vendor);
+ if (var) {
+ if (ro_check && (var->attr & EFI_VARIABLE_READ_ONLY))
+ return EFI_WRITE_PROTECTED;
+
+ if (var_type >= EFI_AUTH_VAR_PK)
+ return EFI_WRITE_PROTECTED;
+
+ /* attributes won't be changed */
+ if (!delete &&
+ ((ro_check && var->attr != (attributes & ~EFI_VARIABLE_APPEND_WRITE)) ||
+ (!ro_check && ((var->attr & ~EFI_VARIABLE_READ_ONLY)
+ != (attributes & ~EFI_VARIABLE_READ_ONLY))))) {
+ return EFI_INVALID_PARAMETER;
+ }
+ time = var->time;
+ } else {
+ /*
+ * UEFI specification does not clearly describe the expected
+ * behavior of append write with data size 0, we follow
+ * the EDK II reference implementation.
+ */
+ if (append && !data_size)
+ return EFI_SUCCESS;
+
+ /*
+ * EFI_VARIABLE_APPEND_WRITE to non-existent variable is accepted
+ * and new variable is created in EDK II reference implementation.
+ * We follow it and only check the deletion here.
+ */
+ if (delete)
+ /* Trying to delete a non-existent variable. */
+ return EFI_NOT_FOUND;
+ }
+
+ if (var_type >= EFI_AUTH_VAR_PK) {
+ /* authentication is mandatory */
+ if (!(attributes &
+ EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS)) {
+ EFI_PRINT("%ls: TIME_BASED_AUTHENTICATED_WRITE_ACCESS required\n",
+ variable_name);
+ return EFI_INVALID_PARAMETER;
+ }
+ }
+
+ /* authenticate a variable */
+ if (IS_ENABLED(CONFIG_EFI_LOADER_SECURE_BOOT)) {
+ if (attributes &
+ EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS) {
+ u32 env_attr;
+
+ ret = efi_variable_authenticate(variable_name, vendor,
+ &data_size, &data,
+ attributes, &env_attr,
+ &time);
+ if (ret != EFI_SUCCESS)
+ return ret;
+
+ /* last chance to check for delete */
+ if (!data_size)
+ delete = true;
+ }
+ } else {
+ if (attributes &
+ EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS) {
+ EFI_PRINT("Secure boot is not configured\n");
+ return EFI_INVALID_PARAMETER;
+ }
+ }
+
+ if (delete) {
+ /* EFI_NOT_FOUND has been handled before */
+ attributes = var->attr;
+ ret = EFI_SUCCESS;
+ } else if (append && var) {
+ /*
+ * data is appended if EFI_VARIABLE_APPEND_WRITE is set and
+ * variable exists.
+ */
+ u16 *old_data = var->name;
+
+ for (; *old_data; ++old_data)
+ ;
+ ++old_data;
+ ret = efi_var_mem_ins(variable_name, vendor,
+ attributes & ~EFI_VARIABLE_APPEND_WRITE,
+ var->length, old_data, data_size, data,
+ time);
+ } else {
+ ret = efi_var_mem_ins(variable_name, vendor, attributes,
+ data_size, data, 0, NULL, time);
+ }
+
+ if (ret != EFI_SUCCESS)
+ return ret;
+
+ efi_var_mem_del(var);
+
+ if (var_type == EFI_AUTH_VAR_PK)
+ ret = efi_init_secure_state();
+ else
+ ret = EFI_SUCCESS;
+
+ /*
+ * Write non-volatile EFI variables to file
+ * TODO: check if a value change has occured to avoid superfluous writes
+ */
+ if (attributes & EFI_VARIABLE_NON_VOLATILE)
+ efi_var_to_file();
+
+ return EFI_SUCCESS;
+}
+
+/**
+ * efi_init_variables() - initialize variable services
+ *
+ * Return: status code
+ */
+static efi_status_t efi_init_variables(void *data)
+{
+ efi_status_t ret;
+
+ ret = efi_var_mem_init();
+ if (ret != EFI_SUCCESS)
+ return ret;
+
+ ret = efi_var_from_file(AT_FDCWD, BAREBOX_ENV_INIT_EFIVARS);
+ if (ret != EFI_SUCCESS && ret != EFI_NOT_FOUND) {
+ pr_err(BAREBOX_ENV_INIT_EFIVARS
+ " : Invalid EFI variables file (%lx)\n", ret);
+ return ret;
+ }
+
+ return efi_init_secure_state();
+}
+
+static int efi_variables_init(void)
+{
+ efi_register_deferred_init(efi_init_variables, NULL);
+ return 0;
+}
+postcore_initcall(efi_variables_init);
diff --git a/efi/loader/protocols/disk.c b/efi/loader/protocols/disk.c
index cb1771e0cf99..835dee4e2e26 100644
--- a/efi/loader/protocols/disk.c
+++ b/efi/loader/protocols/disk.c
@@ -16,6 +16,7 @@
#include <efi/loader/object.h>
#include <efi/loader/devicepath.h>
#include <efi/loader/file.h>
+#include <efi/loader/variable.h>
#include <efi/loader/trace.h>
#include <efi/partition.h>
#include <malloc.h>
@@ -390,6 +391,16 @@ static efi_status_t efi_disk_register(void *data)
symlink_esp();
+ if (IS_ENABLED(CONFIG_EFI_VARIABLE_FILE_STORE) && efi_var_file_name) {
+ int dirfd = efiloader_esp_mount_dir();
+ if (dirfd >= 0) {
+ ret = efi_var_from_file(dirfd, efi_var_file_name);
+ if (ret != EFI_SUCCESS && ret != EFI_NOT_FOUND)
+ pr_warn("Failed to load /esp/%s: %lx\n",
+ efi_var_file_name, ret);
+ }
+ }
+
return EFI_SUCCESS;
}
diff --git a/efi/loader/runtime.c b/efi/loader/runtime.c
index 76b9bc101a06..b46c85eeaa7b 100644
--- a/efi/loader/runtime.c
+++ b/efi/loader/runtime.c
@@ -22,6 +22,8 @@
#include <restart.h>
#include <linux/rtc.h>
+#include "variable.h"
+
/*
* EFI runtime code lives in two stages. In the first stage, EFI loader and an
@@ -297,12 +299,12 @@ struct efi_runtime_services efi_runtime_services = {
.set_wakeup_time = (void *)efi_unimplemented,
.set_virtual_address_map = (void *)efi_unimplemented,
.convert_pointer = (void *)efi_unimplemented,
- .get_variable = (void *)efi_unimplemented,
- .get_next_variable = (void *)efi_unimplemented,
- .set_variable = (void *)efi_unimplemented,
+ .get_variable = efi_get_variable_boot,
+ .get_next_variable = efi_get_next_variable_name_boot,
+ .set_variable = efi_set_variable_boot,
.get_next_high_mono_count = (void *)efi_unimplemented,
.reset_system = &efi_reset_system_boottime,
.update_capsule = (void *)efi_unimplemented,
.query_capsule_caps = (void *)efi_unimplemented,
- .query_variable_info = (void *)efi_unimplemented,
+ .query_variable_info = efi_query_variable_info_boot,
};
diff --git a/efi/loader/setup.c b/efi/loader/setup.c
index 87b7d3a68dc8..a24d3044afdc 100644
--- a/efi/loader/setup.c
+++ b/efi/loader/setup.c
@@ -11,6 +11,7 @@
#include <linux/printk.h>
#include <linux/array_size.h>
+#include <linux/sizes.h>
#include <driver.h>
#include <string.h>
#include <magicvar.h>
diff --git a/efi/loader/variable.h b/efi/loader/variable.h
new file mode 100644
index 000000000000..3710be84a2d1
--- /dev/null
+++ b/efi/loader/variable.h
@@ -0,0 +1,33 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#include <efi/types.h>
+#include <efi/error.h>
+
+efi_status_t EFIAPI efi_get_variable_boot(u16 *variable_name,
+ const efi_guid_t *vendor, u32 *attributes,
+ efi_uintn_t *data_size, void *data);
+
+efi_status_t EFIAPI efi_set_variable_boot(u16 *variable_name,
+ const efi_guid_t *vendor, u32 attributes,
+ efi_uintn_t data_size, const void *data);
+
+efi_status_t EFIAPI efi_get_next_variable_name_boot(efi_uintn_t *variable_name_size,
+ u16 *variable_name,
+ efi_guid_t *vendor);
+
+efi_status_t EFIAPI efi_query_variable_info_boot(
+ u32 attributes, u64 *maximum_variable_storage_size,
+ u64 *remaining_variable_storage_size,
+ u64 *maximum_variable_size);
+
+efi_status_t efi_init_secure_state(void);
+
+bool efi_secure_boot_enabled(void);
+
+void *efi_get_var(const u16 *name, const efi_guid_t *vendor, efi_uintn_t *size);
+
+struct efi_var_file;
+
+efi_status_t efi_var_collect(struct efi_var_file **bufp, loff_t *lenp,
+ u32 check_attr_mask);
+
+efi_status_t efi_var_to_file(void);
diff --git a/efi/runtime/Makefile b/efi/runtime/Makefile
new file mode 100644
index 000000000000..90ed3ef454be
--- /dev/null
+++ b/efi/runtime/Makefile
@@ -0,0 +1,43 @@
+# SPDX-License-Identifier: GPL-2.0
+
+lib-y += efi_var_mem.o efi_variable.o
+
+KASAN_SANITIZE := n
+UBSAN_SANITIZE := n
+
+KBUILD_CFLAGS += $(picflags-y) -include $(srctree)/efi/runtime/common.h
+
+# Even when -mbranch-protection=none is set, Clang will generate a
+# .note.gnu.property for code-less object files (like lib/ctype.c),
+# so work around this by explicitly removing the unwanted section.
+# https://bugs.llvm.org/show_bug.cgi?id=46480
+EFIRTCP_FLAGS-y += --remove-section=.note.gnu.property
+
+# Cover compiler-generaed symbols like __func__ as well
+EFIRTCP_FLAGS-y += --prefix-alloc-sections .efi_runtime
+
+EFIRTCP_FLAGS-y += --redefine-sym memset=__pi_memset \
+ --redefine-sym memcpy=__pi_memcpy \
+ --redefine-sym memmove=__pi_memmove \
+ --redefine-sym crc32=__pi_crc32
+
+#
+# Strip debug sections and some other sections that may legally contain
+# absolute relocations, so that we can inspect the remaining sections for
+# such relocations. If none are found, regenerate the output object, but
+# this time, use objcopy and leave all sections in place.
+#
+quiet_cmd_efirtcopy = EFIRTCP $@
+ cmd_efirtcopy = \
+ $(STRIP) --strip-debug -o $@ $<; \
+ if [ -n "$(EFIRTCP_RELOC-y)" ] && \
+ $(OBJDUMP) -r $@ | grep $(EFIRTCP_RELOC-y); then \
+ echo "$@: absolute symbol references not allowed in the EFI runtime" >&2; \
+ /bin/false; \
+ fi; \
+ $(OBJCOPY) $(EFIRTCP_FLAGS-y) $< $@
+
+$(obj)/%.efirt.o: $(obj)/%.o FORCE
+ $(call if_changed,efirtcopy)
+
+obj-y := $(patsubst %.o,%.efirt.o,$(lib-y))
diff --git a/efi/runtime/common.h b/efi/runtime/common.h
new file mode 100644
index 000000000000..c3d147ebe0a7
--- /dev/null
+++ b/efi/runtime/common.h
@@ -0,0 +1,35 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+
+#ifndef __EFI_RUNTME_H_
+#define __EFI_RUNTME_H_
+
+/* For potential use in headers */
+#define __EFI_RUNTIME__
+
+/* We use objcopy(1) to prefix, so avoid double prefixing here */
+#define __efi_runtime
+#define __efi_runtime_data
+#define __efi_runtime_rodata
+#define EFI_RUNTIME_SECTION(sect) sect
+
+#ifdef CONFIG_DEBUG_EFI_RUNTIME_ENTRY
+#include <debug_ll.h>
+
+#define EFI_ENTRY(wstr, ...) do { \
+ puts_ll(__func__); \
+ puts_ll("("); \
+ putws_ll(wstr ?: L"<NULL>"); \
+ puts_ll(")"); \
+} while (0)
+#define EFI_EXIT(ret) ({ \
+ puts_ll("\t= "); \
+ puthex_ll((ulong)ret); \
+ puts_ll("\n"); \
+ (ret); \
+})
+#else
+#define EFI_ENTRY(...) (void)0
+#define EFI_EXIT(ret) ({ ret; })
+#endif /* CONFIG_DEBUG_EFI_RUNTIME_ENTRY */
+
+#endif
diff --git a/efi/runtime/efi_var_mem.c b/efi/runtime/efi_var_mem.c
new file mode 100644
index 000000000000..83b5c53b063b
--- /dev/null
+++ b/efi/runtime/efi_var_mem.c
@@ -0,0 +1,380 @@
+// SPDX-License-Identifier: GPL-2.0+
+// SPDX-Comment: Origin-URL: https://github.com/u-boot/u-boot/blob/530e869ff89d9575103637af201fe97864d4f577/lib/efi_loader/efi_var_mem.c
+/*
+ * File interface for UEFI variables
+ *
+ * Copyright (c) 2020, Heinrich Schuchardt
+ */
+
+#include <linux/kernel.h>
+#include <efi/loader.h>
+#include <efi/loader/variable.h>
+#include <efi/variable.h>
+#include <efi/runtime.h>
+#include <efi/error.h>
+#include <charset.h>
+#include <crc.h>
+
+#define efi_memcpy_runtime memcpy
+
+/*
+ * Below variables must be static to avoid referencing them via the global
+ * offset table (section .got). The GOT is neither mapped as
+ * EfiRuntimeServicesData nor do we support its relocation during
+ * SetVirtualAddressMap().
+ *
+ * TODO: We probably want some vDSO-like mechanism.
+ */
+static struct efi_var_file __efi_runtime_data *efi_var_buf;
+static unsigned __efi_runtime_data efi_var_buf_rtsize;
+static struct efi_var_entry __efi_runtime_data *efi_current_var;
+static const u16 __efi_runtime_rodata vtf[] = u"VarToFile";
+
+__efi_runtime void efi_var_buf_set(struct efi_var_file *buf, unsigned size)
+{
+ efi_var_buf = buf;
+ efi_var_buf_rtsize = size;
+}
+
+__efi_runtime struct efi_var_file *efi_var_buf_get(unsigned *size)
+{
+ if (size)
+ *size = efi_var_buf_rtsize;
+ return efi_var_buf;
+}
+
+/**
+ * efi_var_mem_compare() - compare GUID and name with a variable
+ *
+ * @var: variable to compare
+ * @guid: GUID to compare
+ * @name: variable name to compare
+ * @next: pointer to next variable
+ * Return: true if match
+ */
+static bool __efi_runtime
+efi_var_mem_compare(struct efi_var_entry *var, const efi_guid_t *guid,
+ const u16 *name, struct efi_var_entry **next)
+{
+ int i;
+ u8 *guid1, *guid2;
+ const u16 *data, *var_name;
+ bool match = true;
+
+ for (guid1 = (u8 *)&var->guid, guid2 = (u8 *)guid, i = 0;
+ i < sizeof(efi_guid_t) && match; ++i)
+ match = (guid1[i] == guid2[i]);
+
+ for (data = var->name, var_name = name;; ++data) {
+ if (match)
+ match = (*data == *var_name);
+ if (!*data)
+ break;
+ if (*var_name)
+ ++var_name;
+ }
+
+ ++data;
+
+ if (next)
+ *next = (struct efi_var_entry *)
+ ALIGN((uintptr_t)data + var->length, 8);
+
+ if (match)
+ efi_current_var = var;
+
+ return match;
+}
+
+/**
+ * efi_var_entry_len() - Get the entry len including headers & name
+ *
+ * @var: pointer to variable start
+ *
+ * Return: 8-byte aligned variable entry length
+ */
+
+static u32 __efi_runtime efi_var_entry_len(struct efi_var_entry *var)
+{
+ if (!var)
+ return 0;
+
+ return ALIGN((sizeof(u16) * (u16_strlen(var->name) + 1)) +
+ var->length + sizeof(*var), 8);
+}
+
+struct efi_var_entry __efi_runtime
+*efi_var_mem_find(const efi_guid_t *guid, const u16 *name,
+ struct efi_var_entry **next)
+{
+ struct efi_var_entry *var, *last;
+
+ last = (struct efi_var_entry *)
+ ((uintptr_t)efi_var_buf + efi_var_buf->length);
+
+ if (!*name) {
+ if (next) {
+ *next = efi_var_buf->var;
+ if (*next >= last)
+ *next = NULL;
+ }
+ return NULL;
+ }
+ if (efi_current_var &&
+ efi_var_mem_compare(efi_current_var, guid, name, next)) {
+ if (next && *next >= last)
+ *next = NULL;
+ return efi_current_var;
+ }
+
+ var = efi_var_buf->var;
+ if (var < last) {
+ for (; var;) {
+ struct efi_var_entry *pos;
+ bool match;
+
+ match = efi_var_mem_compare(var, guid, name, &pos);
+ if (pos >= last)
+ pos = NULL;
+ if (match) {
+ if (next)
+ *next = pos;
+ return var;
+ }
+ var = pos;
+ }
+ }
+ if (next)
+ *next = NULL;
+ return NULL;
+}
+
+void __efi_runtime efi_var_mem_del(struct efi_var_entry *var)
+{
+ u16 *data;
+ struct efi_var_entry *next, *last;
+
+ if (!var)
+ return;
+
+ last = (struct efi_var_entry *)
+ ((uintptr_t)efi_var_buf + efi_var_buf->length);
+ if (var <= efi_current_var)
+ efi_current_var = NULL;
+
+ for (data = var->name; *data; ++data)
+ ;
+ ++data;
+ next = (struct efi_var_entry *)
+ ALIGN((uintptr_t)data + var->length, 8);
+ efi_var_buf->length -= (uintptr_t)next - (uintptr_t)var;
+
+ /* efi_memcpy_runtime() can be used because next >= var. */
+ efi_memcpy_runtime(var, next, (uintptr_t)last - (uintptr_t)next);
+ efi_var_buf->crc32 = crc32(0, (u8 *)efi_var_buf->var,
+ efi_var_buf->length -
+ sizeof(struct efi_var_file));
+}
+
+efi_status_t __efi_runtime efi_var_mem_ins(
+ const u16 *variable_name,
+ const efi_guid_t *vendor, u32 attributes,
+ const efi_uintn_t size1, const void *data1,
+ const efi_uintn_t size2, const void *data2,
+ const u64 time)
+{
+ u16 *data;
+ struct efi_var_entry *var;
+ u32 var_name_len;
+
+ var = (struct efi_var_entry *)
+ ((uintptr_t)efi_var_buf + efi_var_buf->length);
+ var_name_len = u16_strlen(variable_name) + 1;
+ data = var->name + var_name_len;
+
+ if ((uintptr_t)data - (uintptr_t)efi_var_buf + size1 + size2 >
+ efi_var_buf_rtsize)
+ return EFI_OUT_OF_RESOURCES;
+
+ var->attr = attributes;
+ var->length = size1 + size2;
+ var->time = time;
+
+ efi_memcpy_runtime(&var->guid, vendor, sizeof(efi_guid_t));
+ efi_memcpy_runtime(var->name, variable_name,
+ sizeof(u16) * var_name_len);
+ efi_memcpy_runtime(data, data1, size1);
+ efi_memcpy_runtime((u8 *)data + size1, data2, size2);
+
+ var = (struct efi_var_entry *)
+ ALIGN((uintptr_t)data + var->length, 8);
+ efi_var_buf->length = (uintptr_t)var - (uintptr_t)efi_var_buf;
+ efi_var_buf->crc32 = crc32(0, (u8 *)efi_var_buf->var,
+ efi_var_buf->length -
+ sizeof(struct efi_var_file));
+
+ return EFI_SUCCESS;
+}
+
+u64 __efi_runtime efi_var_mem_free(void)
+{
+ if (efi_var_buf->length + sizeof(struct efi_var_entry) >=
+ efi_var_buf_rtsize)
+ return 0;
+
+ return efi_var_buf_rtsize - efi_var_buf->length -
+ sizeof(struct efi_var_entry);
+}
+
+/**
+ * efi_var_collect_mem() - Copy EFI variables matching attributes mask from
+ * efi_var_buf
+ *
+ * @buf: buffer containing variable collection
+ * @lenp: buffer length
+ * @mask: mask of matched attributes
+ *
+ * Return: Status code
+ */
+static efi_status_t __efi_runtime
+efi_var_collect_mem(struct efi_var_file *buf, efi_uintn_t *lenp, u32 mask)
+{
+ static struct efi_var_file __efi_runtime_data hdr = {
+ .magic = EFI_VAR_FILE_MAGIC,
+ };
+ struct efi_var_entry *last, *var, *var_to;
+
+ hdr.length = sizeof(struct efi_var_file);
+
+ var = efi_var_buf->var;
+ last = (struct efi_var_entry *)
+ ((uintptr_t)efi_var_buf + efi_var_buf->length);
+ if (buf)
+ var_to = buf->var;
+
+ while (var < last) {
+ u32 len = efi_var_entry_len(var);
+
+ if ((var->attr & mask) != mask) {
+ var = (void *)((uintptr_t)var + len);
+ continue;
+ }
+
+ hdr.length += len;
+
+ if (buf && hdr.length <= *lenp) {
+ efi_memcpy_runtime(var_to, var, len);
+ var_to = (void *)var_to + len;
+ }
+ var = (void *)var + len;
+ }
+
+ if (!buf && hdr.length <= *lenp) {
+ *lenp = hdr.length;
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if (!buf || hdr.length > *lenp) {
+ *lenp = hdr.length;
+ return EFI_BUFFER_TOO_SMALL;
+ }
+ hdr.crc32 = crc32(0, (u8 *)buf->var,
+ hdr.length - sizeof(struct efi_var_file));
+
+ efi_memcpy_runtime(buf, &hdr, sizeof(hdr));
+ *lenp = hdr.length;
+
+ return EFI_SUCCESS;
+}
+
+efi_status_t __efi_runtime
+efi_get_variable_mem(const u16 *variable_name, const efi_guid_t *vendor,
+ u32 *attributes, efi_uintn_t *data_size, void *data,
+ u64 *timep, u32 mask)
+{
+ efi_uintn_t old_size;
+ struct efi_var_entry *var;
+ u16 *pdata;
+
+ if (!variable_name || !vendor || !data_size)
+ return EFI_INVALID_PARAMETER;
+ var = efi_var_mem_find(vendor, variable_name, NULL);
+ if (!var)
+ return EFI_NOT_FOUND;
+
+ /*
+ * This function is used at runtime to dump EFI variables.
+ * The memory backend we keep around has BS-only variables as
+ * well. At runtime we filter them here
+ */
+ if (mask && !((var->attr & mask) == mask))
+ return EFI_NOT_FOUND;
+
+ if (attributes)
+ *attributes = var->attr;
+ if (timep)
+ *timep = var->time;
+
+ if (!u16_strcmp(variable_name, vtf))
+ return efi_var_collect_mem(data, data_size, EFI_VARIABLE_NON_VOLATILE);
+
+ old_size = *data_size;
+ *data_size = var->length;
+ if (old_size < var->length)
+ return EFI_BUFFER_TOO_SMALL;
+
+ if (!data)
+ return EFI_INVALID_PARAMETER;
+
+ for (pdata = var->name; *pdata; ++pdata)
+ ;
+ ++pdata;
+
+ efi_memcpy_runtime(data, pdata, var->length);
+
+ return EFI_SUCCESS;
+}
+
+efi_status_t __efi_runtime
+efi_get_next_variable_name_mem(efi_uintn_t *variable_name_size,
+ u16 *variable_name, efi_guid_t *vendor,
+ u32 mask)
+{
+ struct efi_var_entry *var;
+ efi_uintn_t len, old_size;
+ u16 *pdata;
+
+ if (!variable_name_size || !variable_name || !vendor)
+ return EFI_INVALID_PARAMETER;
+
+skip:
+ len = *variable_name_size >> 1;
+ if (u16_strnlen(variable_name, len) == len)
+ return EFI_INVALID_PARAMETER;
+
+ if (!efi_var_mem_find(vendor, variable_name, &var) && *variable_name)
+ return EFI_INVALID_PARAMETER;
+
+ if (!var)
+ return EFI_NOT_FOUND;
+
+ for (pdata = var->name; *pdata; ++pdata)
+ ;
+ ++pdata;
+
+ old_size = *variable_name_size;
+ *variable_name_size = (uintptr_t)pdata - (uintptr_t)var->name;
+
+ if (old_size < *variable_name_size)
+ return EFI_BUFFER_TOO_SMALL;
+
+ efi_memcpy_runtime(variable_name, var->name, *variable_name_size);
+ efi_memcpy_runtime(vendor, &var->guid, sizeof(efi_guid_t));
+
+ if (mask && !((var->attr & mask) == mask)) {
+ *variable_name_size = old_size;
+ goto skip;
+ }
+
+ return EFI_SUCCESS;
+}
diff --git a/efi/runtime/efi_variable.c b/efi/runtime/efi_variable.c
new file mode 100644
index 000000000000..5dc10846f5d3
--- /dev/null
+++ b/efi/runtime/efi_variable.c
@@ -0,0 +1,211 @@
+// SPDX-License-Identifier: GPL-2.0+
+// SPDX-Comment: Origin-URL: https://github.com/u-boot/u-boot/blob/58d825fb18053ec7e432de7bbb70a452b9228076/lib/efi_loader/efi_variable.c
+/*
+ * UEFI runtime variable services
+ *
+ * Copyright (c) 2017 Rob Clark
+ */
+
+#include <linux/kernel.h>
+#include <efi/loader/variable.h>
+#include <efi/services.h>
+#include <efi/variable.h>
+#include <efi/loader.h>
+#include <efi/error.h>
+#include <efi/runtime.h>
+#include <charset.h>
+#include <stdlib.h>
+
+efi_status_t __efi_runtime
+efi_get_variable_int(const u16 *variable_name, const efi_guid_t *vendor,
+ u32 *attributes, efi_uintn_t *data_size, void *data,
+ u64 *timep)
+{
+ return efi_get_variable_mem(variable_name, vendor, attributes, data_size,
+ data, timep, 0);
+}
+
+efi_status_t __efi_runtime
+efi_get_next_variable_name_int(efi_uintn_t *variable_name_size,
+ u16 *variable_name, efi_guid_t *vendor)
+{
+ return efi_get_next_variable_name_mem(variable_name_size, variable_name,
+ vendor, 0);
+}
+
+/**
+ * efi_setvariable_allowed() - checks defined by the UEFI spec for setvariable
+ *
+ * @variable_name: name of the variable
+ * @vendor: vendor GUID
+ * @attributes: attributes of the variable
+ * @data_size: size of the buffer with the variable value
+ * @data: buffer with the variable value
+ * Return: status code
+ */
+efi_status_t __efi_runtime
+efi_setvariable_allowed(const u16 *variable_name, const efi_guid_t *vendor,
+ u32 attributes, efi_uintn_t data_size, const void *data)
+{
+ if (!variable_name || !*variable_name || !vendor)
+ return EFI_INVALID_PARAMETER;
+
+ if (data_size && !data)
+ return EFI_INVALID_PARAMETER;
+
+ /*
+ * EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS is deprecated.
+ * We don't support EFI_VARIABLE_ENHANCED_AUTHENTICATED_ACCESS.
+ */
+ if (attributes & (EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS | \
+ EFI_VARIABLE_ENHANCED_AUTHENTICATED_ACCESS))
+ return EFI_UNSUPPORTED;
+
+ /* Make sure if runtime bit is set, boot service bit is set also */
+ if ((attributes &
+ (EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_BOOTSERVICE_ACCESS)) ==
+ EFI_VARIABLE_RUNTIME_ACCESS)
+ return EFI_INVALID_PARAMETER;
+
+ /* only EFI_VARIABLE_NON_VOLATILE attribute is invalid */
+ if ((attributes & EFI_VARIABLE_MASK) == EFI_VARIABLE_NON_VOLATILE)
+ return EFI_INVALID_PARAMETER;
+
+ /* Make sure HR is set with NV, BS and RT */
+ if (attributes & EFI_VARIABLE_HARDWARE_ERROR_RECORD &&
+ (!(attributes & EFI_VARIABLE_NON_VOLATILE) ||
+ !(attributes & EFI_VARIABLE_RUNTIME_ACCESS) ||
+ !(attributes & EFI_VARIABLE_BOOTSERVICE_ACCESS)))
+ return EFI_INVALID_PARAMETER;
+
+ return EFI_SUCCESS;
+}
+
+efi_status_t __efi_runtime
+efi_query_variable_info_int(u32 attributes,
+ u64 *maximum_variable_storage_size,
+ u64 *remaining_variable_storage_size,
+ u64 *maximum_variable_size)
+{
+ unsigned size;
+
+ if (!maximum_variable_storage_size ||
+ !remaining_variable_storage_size ||
+ !maximum_variable_size || !attributes)
+ return EFI_INVALID_PARAMETER;
+
+ /* EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS is deprecated */
+ if ((attributes & EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS) ||
+ ((attributes & EFI_VARIABLE_MASK) == 0))
+ return EFI_UNSUPPORTED;
+
+ if ((attributes & EFI_VARIABLE_MASK) == EFI_VARIABLE_NON_VOLATILE)
+ return EFI_INVALID_PARAMETER;
+
+ /* Make sure if runtime bit is set, boot service bit is set also. */
+ if ((attributes &
+ (EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_BOOTSERVICE_ACCESS)) ==
+ EFI_VARIABLE_RUNTIME_ACCESS)
+ return EFI_INVALID_PARAMETER;
+
+ if (attributes & ~EFI_VARIABLE_MASK)
+ return EFI_INVALID_PARAMETER;
+
+ efi_var_buf_get(&size);
+
+ *maximum_variable_storage_size = size - sizeof(struct efi_var_file);
+ *remaining_variable_storage_size = efi_var_mem_free();
+ *maximum_variable_size = size - sizeof(struct efi_var_file) -
+ sizeof(struct efi_var_entry);
+ return EFI_SUCCESS;
+}
+
+/**
+ * efirt_set_variable() - runtime implementation of SetVariable()
+ *
+ * @variable_name: name of the variable
+ * @vendor: vendor GUID
+ * @attributes: attributes of the variable
+ * @data_size: size of the buffer with the variable value
+ * @data: buffer with the variable value
+ * Return: status code
+ */
+efi_status_t __efi_runtime EFIAPI
+efirt_set_variable(u16 *variable_name, const efi_guid_t *vendor,
+ u32 attributes, efi_uintn_t data_size,
+ const void *data)
+{
+ struct efi_var_entry *var;
+ efi_uintn_t ret;
+ bool append, delete;
+ u64 time = 0;
+
+ if (!IS_ENABLED(CONFIG_EFI_RT_VOLATILE_STORE))
+ return EFI_UNSUPPORTED;
+
+ /*
+ * Authenticated variables are not supported. The EFI spec
+ * in §32.3.6 requires keys to be stored in non-volatile storage which
+ * is tamper and delete resistant.
+ * The rest of the checks are in efi_setvariable_allowed()
+ */
+ if (attributes & EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS)
+ return EFI_INVALID_PARAMETER;
+
+ ret = efi_setvariable_allowed(variable_name, vendor, attributes, data_size,
+ data);
+ if (ret != EFI_SUCCESS)
+ return ret;
+
+ /* check if a variable exists */
+ var = efi_var_mem_find(vendor, variable_name, NULL);
+ append = !!(attributes & EFI_VARIABLE_APPEND_WRITE);
+ attributes &= ~EFI_VARIABLE_APPEND_WRITE;
+ delete = !append && (!data_size || !attributes);
+
+ /* BS only variables are hidden deny writing them */
+ if (!delete && !(attributes & EFI_VARIABLE_RUNTIME_ACCESS))
+ return EFI_INVALID_PARAMETER;
+
+ if (var) {
+ if (var->attr & EFI_VARIABLE_READ_ONLY ||
+ !(var->attr & EFI_VARIABLE_NON_VOLATILE))
+ return EFI_WRITE_PROTECTED;
+
+ /* attributes won't be changed */
+ if (!delete && (((var->attr & ~EFI_VARIABLE_READ_ONLY) !=
+ (attributes & ~EFI_VARIABLE_READ_ONLY))))
+ return EFI_INVALID_PARAMETER;
+ time = var->time;
+ } else {
+ if (!(attributes & EFI_VARIABLE_NON_VOLATILE))
+ return EFI_INVALID_PARAMETER;
+ if (append && !data_size)
+ return EFI_SUCCESS;
+ if (delete)
+ return EFI_NOT_FOUND;
+ }
+
+ if (delete) {
+ /* EFI_NOT_FOUND has been handled before */
+ attributes = var->attr;
+ ret = EFI_SUCCESS;
+ } else if (append && var) {
+ u16 *old_data = (void *)((uintptr_t)var->name +
+ sizeof(u16) * (u16_strlen(var->name) + 1));
+
+ ret = efi_var_mem_ins(variable_name, vendor, attributes,
+ var->length, old_data, data_size, data,
+ time);
+ } else {
+ ret = efi_var_mem_ins(variable_name, vendor, attributes,
+ data_size, data, 0, NULL, time);
+ }
+
+ if (ret != EFI_SUCCESS)
+ return ret;
+ /* We are always inserting new variables, get rid of the old copy */
+ efi_var_mem_del(var);
+
+ return EFI_SUCCESS;
+}
diff --git a/include/efi/loader/variable.h b/include/efi/loader/variable.h
new file mode 100644
index 000000000000..2f0038d8350c
--- /dev/null
+++ b/include/efi/loader/variable.h
@@ -0,0 +1,153 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/* SPDX-Comment: Origin-URL: https://github.com/u-boot/u-boot/blob/89d911e386cc439c68ab961e76937c377e7d7127/include/efi_variable.h */
+
+#ifndef _EFI_LOADER_VARIABLE_H
+#define _EFI_LOADER_VARIABLE_H
+
+#include <efi/types.h>
+#include <efi/error.h>
+
+#define EFI_VARIABLE_READ_ONLY 0x80000000
+
+enum efi_auth_var_type {
+ EFI_AUTH_VAR_NONE = 0,
+ EFI_AUTH_MODE,
+ EFI_AUTH_VAR_PK,
+ EFI_AUTH_VAR_KEK,
+ EFI_AUTH_VAR_DB,
+ EFI_AUTH_VAR_DBX,
+ EFI_AUTH_VAR_DBT,
+ EFI_AUTH_VAR_DBR,
+};
+
+/**
+ * efi_auth_var_get_type() - convert variable name and guid to enum
+ *
+ * @name: name of UEFI variable
+ * @guid: guid of UEFI variable
+ * Return: identifier for authentication related variables
+ */
+enum efi_auth_var_type efi_auth_var_get_type(const u16 *name,
+ const efi_guid_t *guid);
+
+/**
+ * efi_auth_var_get_guid() - get the predefined GUID for a variable name
+ *
+ * @name: name of UEFI variable
+ * Return: guid of UEFI variable
+ */
+const efi_guid_t *efi_auth_var_get_guid(const u16 *name);
+
+/**
+ * efi_var_mem_init() - set-up variable list
+ *
+ * Return: status code
+ */
+efi_status_t efi_var_mem_init(void);
+
+/**
+ * efi_get_next_variable_name_mem() - Runtime common code across efi variable
+ * implementations for GetNextVariable()
+ * from the cached memory copy
+ *
+ * @variable_name_size: size of variable_name buffer in bytes
+ * @variable_name: name of uefi variable's name in u16
+ * @mask: bitmask with required attributes of variables to be collected.
+ * variables are only collected if all of the required
+ * attributes match. Use 0 to skip matching
+ * @vendor: vendor's guid
+ *
+ * Return: status code
+ */
+efi_status_t __efi_runtime
+efi_get_next_variable_name_mem(efi_uintn_t *variable_name_size, u16 *variable_name,
+ efi_guid_t *vendor, u32 mask);
+/**
+ * efi_get_variable_mem() - Runtime common code across efi variable
+ * implementations for GetVariable() from
+ * the cached memory copy
+ *
+ * @variable_name: name of the variable
+ * @vendor: vendor GUID
+ * @attributes: attributes of the variable
+ * @data_size: size of the buffer to which the variable value is copied
+ * @data: buffer to which the variable value is copied
+ * @timep: authentication time (seconds since start of epoch)
+ * @mask: bitmask with required attributes of variables to be collected.
+ * variables are only collected if all of the required
+ * attributes match. Use 0 to skip matching
+ * Return: status code
+ */
+efi_status_t __efi_runtime
+efi_get_variable_mem(const u16 *variable_name, const efi_guid_t *vendor,
+ u32 *attributes, efi_uintn_t *data_size, void *data,
+ u64 *timep, u32 mask);
+
+/*
+ * This constant identifies the file format for storing UEFI variables in
+ * struct efi_var_file.
+ */
+#define EFI_VAR_FILE_MAGIC 0x0161566966456255 /* UbEfiVa, version 1 */
+
+/**
+ * struct efi_var_entry - UEFI variable file entry
+ *
+ * @length: length of enty, multiple of 8
+ * @attr: variable attributes
+ * @time: authentication time (seconds since start of epoch)
+ * @guid: vendor GUID
+ * @name: UTF16 variable name
+ */
+struct efi_var_entry {
+ u32 length;
+ u32 attr;
+ u64 time;
+ efi_guid_t guid;
+ u16 name[];
+};
+
+/**
+ * struct efi_var_file - file for storing UEFI variables
+ *
+ * @reserved: unused, may be overwritten by memory probing
+ * @magic: identifies file format, takes value %EFI_VAR_FILE_MAGIC
+ * @length: length including header
+ * @crc32: CRC32 without header
+ * @var: variables
+ */
+struct efi_var_file {
+ u64 reserved;
+ u64 magic;
+ u32 length;
+ u32 crc32;
+ struct efi_var_entry var[];
+};
+
+/**
+ * efi_var_buf_update() - udpate memory buffer for variables
+ *
+ * @var_buf: source buffer
+ *
+ * This function copies to the memory buffer for UEFI variables. Call this
+ * function in ExitBootServices() if memory backed variables are only used
+ * at runtime to fill the buffer.
+ */
+void efi_var_buf_update(struct efi_var_file *var_buf);
+
+/* The name within the ESP of the file used to store barebox' EFI variables */
+extern char *efi_var_file_name;
+
+/**
+ * efi_var_from_file() - read variables from file
+ *
+ * File ubootefi.var is read from the EFI system partitions and the variables
+ * stored in the file are created.
+ *
+ * In case the file does not exist yet or a variable cannot be set EFI_SUCCESS
+ * is returned.
+ *
+ * Return: status code
+ */
+efi_status_t efi_var_from_file(int dirfd, const char *filename);
+
+#endif
diff --git a/include/efi/runtime.h b/include/efi/runtime.h
new file mode 100644
index 000000000000..d7f4f4264106
--- /dev/null
+++ b/include/efi/runtime.h
@@ -0,0 +1,164 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/* SPDX-Comment: Origin-URL: https://github.com/u-boot/u-boot/blob/89d911e386cc439c68ab961e76937c377e7d7127/include/efi_variable.h */
+/*
+ * Copyright (c) 2020, Heinrich Schuchardt <xypron.glpk@gmx.de>
+ */
+#ifndef _EFI_RUNTIME_H
+#define _EFI_RUNTIME_H
+
+#include <efi/types.h>
+
+#define EFI_VARIABLE_READ_ONLY 0x80000000
+
+/**
+ * efi_get_variable_int() - retrieve value of a UEFI variable
+ *
+ * @variable_name: name of the variable
+ * @vendor: vendor GUID
+ * @attributes: attributes of the variable
+ * @data_size: size of the buffer to which the variable value is copied
+ * @data: buffer to which the variable value is copied
+ * @timep: authentication time (seconds since start of epoch)
+ * Return: status code
+ */
+efi_status_t __efi_runtime
+efi_get_variable_int(const u16 *variable_name,
+ const efi_guid_t *vendor,
+ u32 *attributes, efi_uintn_t *data_size,
+ void *data, u64 *timep);
+
+/**
+ * efi_set_variable_int() - set value of a UEFI variable
+ *
+ * @variable_name: name of the variable
+ * @vendor: vendor GUID
+ * @attributes: attributes of the variable
+ * @data_size: size of the buffer with the variable value
+ * @data: buffer with the variable value
+ * @ro_check: check the read only read only bit in attributes
+ * Return: status code
+ */
+efi_status_t efi_set_variable_int(const u16 *variable_name,
+ const efi_guid_t *vendor,
+ u32 attributes, efi_uintn_t data_size,
+ const void *data, bool ro_check);
+
+/**
+ * efi_query_variable_info_int() - get information about EFI variables
+ *
+ * This function implements the QueryVariableInfo() runtime service.
+ *
+ * See the Unified Extensible Firmware Interface (UEFI) specification for
+ * details.
+ *
+ * @attributes: bitmask to select variables to be
+ * queried
+ * @maximum_variable_storage_size: maximum size of storage area for the
+ * selected variable types
+ * @remaining_variable_storage_size: remaining size of storage are for the
+ * selected variable types
+ * @maximum_variable_size: maximum size of a variable of the
+ * selected type
+ * Returns: status code
+ */
+efi_status_t __efi_runtime
+efi_query_variable_info_int(u32 attributes,
+ u64 *maximum_variable_storage_size,
+ u64 *remaining_variable_storage_size,
+ u64 *maximum_variable_size);
+
+/**
+ * efi_setvariable_allowed() - checks defined by the UEFI spec for setvariable
+ *
+ * @variable_name: name of the variable
+ * @vendor: vendor GUID
+ * @attributes: attributes of the variable
+ * @data_size: size of the buffer with the variable value
+ * @data: buffer with the variable value
+ * Return: status code
+ */
+efi_status_t __efi_runtime
+efi_setvariable_allowed(const u16 *variable_name, const efi_guid_t *vendor,
+ u32 attributes, efi_uintn_t data_size, const void *data);
+
+
+efi_status_t __efi_runtime EFIAPI
+efirt_set_variable(u16 *variable_name, const efi_guid_t *vendor,
+ u32 attributes, efi_uintn_t data_size,
+ const void *data);
+
+/**
+ * efi_var_mem_find() - find a variable in the list
+ *
+ * @guid: GUID of the variable
+ * @name: name of the variable
+ * @next: on exit pointer to the next variable after the found one
+ * Return: found variable
+ */
+struct efi_var_entry *efi_var_mem_find(const efi_guid_t *guid, const u16 *name,
+ struct efi_var_entry **next);
+
+/**
+ * efi_var_mem_ins() - append a variable to the list of variables
+ *
+ * The variable is appended without checking if a variable of the same name
+ * already exists. The two data buffers are concatenated.
+ *
+ * @variable_name: variable name
+ * @vendor: GUID
+ * @attributes: variable attributes
+ * @size1: size of the first data buffer
+ * @data1: first data buffer
+ * @size2: size of the second data field
+ * @data2: second data buffer
+ * @time: time of authentication (as seconds since start of epoch)
+ * Result: status code
+ */
+efi_status_t __efi_runtime
+efi_var_mem_ins(const u16 *variable_name,
+ const efi_guid_t *vendor, u32 attributes,
+ const efi_uintn_t size1, const void *data1,
+ const efi_uintn_t size2, const void *data2,
+ const u64 time);
+
+/**
+ * efi_get_next_variable_name_int() - enumerate the current variable names
+ *
+ * @variable_name_size: size of variable_name buffer in byte
+ * @variable_name: name of uefi variable's name in u16
+ * @vendor: vendor's guid
+ *
+ * See the Unified Extensible Firmware Interface (UEFI) specification for
+ * details.
+ *
+ * Return: status code
+ */
+efi_status_t __efi_runtime
+efi_get_next_variable_name_int(efi_uintn_t *variable_name_size,
+ u16 *variable_name,
+ efi_guid_t *vendor);
+
+/**
+ * efi_var_mem_del() - delete a variable from the list of variables
+ *
+ * @var: variable to delete
+ */
+void __efi_runtime efi_var_mem_del(struct efi_var_entry *var);
+
+/**
+ * efi_var_mem_free() - determine free memory for variables
+ *
+ * Return: maximum data size plus variable name size
+ */
+u64 __efi_runtime efi_var_mem_free(void);
+
+struct efi_var_file;
+extern __efi_runtime void efi_var_buf_set(struct efi_var_file *buf, unsigned size);
+extern __efi_runtime struct efi_var_file *efi_var_buf_get(unsigned *size);
+
+struct efi_system_table;
+void __efi_runtime efirt_trace_init(void *_ring);
+void efi_runtime_prep(void);
+void __efi_runtime efi_runtime_detach(struct efi_system_table *systab);
+
+#endif
diff --git a/include/efi/variable.h b/include/efi/variable.h
index 53f465117cd5..12c39ef592a8 100644
--- a/include/efi/variable.h
+++ b/include/efi/variable.h
@@ -15,6 +15,10 @@
#define EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS 0x0000000000000010
#define EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS 0x0000000000000020
#define EFI_VARIABLE_APPEND_WRITE 0x0000000000000040
+#define EFI_VARIABLE_ENHANCED_AUTHENTICATED_ACCESS 0x00000080
+
+#define EFI_VARIABLE_ACCESSIBLE(attrs) \
+ ((attrs) & (EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS))
#define EFI_VARIABLE_MASK (EFI_VARIABLE_NON_VOLATILE | \
EFI_VARIABLE_BOOTSERVICE_ACCESS | \
@@ -22,7 +26,8 @@
EFI_VARIABLE_HARDWARE_ERROR_RECORD | \
EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS | \
EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS | \
- EFI_VARIABLE_APPEND_WRITE)
+ EFI_VARIABLE_APPEND_WRITE | \
+ EFI_VARIABLE_ENHANCED_AUTHENTICATED_ACCESS)
/*
* Length of a GUID string (strlen("aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee"))
* not including trailing NUL
--
2.47.3
^ permalink raw reply [flat|nested] 55+ messages in thread* [PATCH v1 47/54] efi: loader: populate OsIndicationsSupported/PlatformLang variables
2025-12-18 10:37 [PATCH v1 00/54] efi: implement EFI loader support in barebox Ahmad Fatoum
` (45 preceding siblings ...)
2025-12-18 10:38 ` [PATCH v1 46/54] efi: runtime: add EFI variable support Ahmad Fatoum
@ 2025-12-18 10:38 ` Ahmad Fatoum
2025-12-18 10:38 ` [PATCH v1 48/54] ARM: don't disable MMU when EFI booting Ahmad Fatoum
` (6 subsequent siblings)
53 siblings, 0 replies; 55+ messages in thread
From: Ahmad Fatoum @ 2025-12-18 10:38 UTC (permalink / raw)
To: barebox; +Cc: Ahmad Fatoum
For consultation by the OS, define some default variables.
We don't do anyting fancy with them yet, like signal capsule support or
allow reboot to barebox shell, so we only supply default values.
Signed-off-by: Ahmad Fatoum <a.fatoum@pengutronix.de>
---
efi/loader/Makefile | 1 +
efi/loader/defaultvars.c | 122 +++++++++++++++++++++++++++++++++++++++
2 files changed, 123 insertions(+)
create mode 100644 efi/loader/defaultvars.c
diff --git a/efi/loader/Makefile b/efi/loader/Makefile
index 5fd74a2e0c42..c06d1f7c8c35 100644
--- a/efi/loader/Makefile
+++ b/efi/loader/Makefile
@@ -19,3 +19,4 @@ obj-y += efi_var_common.o
obj-y += efi_variable.o
obj-y += efi_var_mem.o
obj-y += efi_var_file.o
+obj-y += defaultvars.o
diff --git a/efi/loader/defaultvars.c b/efi/loader/defaultvars.c
new file mode 100644
index 000000000000..4d8ec72d9166
--- /dev/null
+++ b/efi/loader/defaultvars.c
@@ -0,0 +1,122 @@
+// SPDX-License-Identifier: GPL-2.0+
+// SPDX-Comment: Origin-URL: https://github.com/u-boot/u-boot/blob/e7a85ec651ed5794eb9a837e1073f6b3146af501/lib/efi_loader/efi_setup.c
+
+#define pr_fmt(fmt) "efi-loader: defaultvars: " fmt
+
+#include <efi/types.h>
+#include <efi/runtime.h>
+#include <efi/error.h>
+#include <efi/guid.h>
+#include <efi/loader.h>
+#include <efi/variable.h>
+#include <linux/printk.h>
+#include <linux/string.h>
+#include <init.h>
+#include <param.h>
+#include <magicvar.h>
+#include <xfuncs.h>
+
+
+/**
+ * efi_init_platform_lang() - define supported languages
+ *
+ * Set the PlatformLangCodes and PlatformLang variables.
+ *
+ * Return: status code
+ */
+static efi_status_t efi_init_platform_lang(void *_efi_lang_codes)
+{
+ efi_status_t ret;
+ efi_uintn_t data_size = 0;
+ char *efi_lang_codes = _efi_lang_codes;
+ char *lang = efi_lang_codes;
+ char *pos;
+
+ /*
+ * Variable PlatformLangCodes defines the language codes that the
+ * machine can support.
+ */
+ ret = efi_set_variable_int(u"PlatformLangCodes",
+ &efi_global_variable_guid,
+ EFI_VARIABLE_BOOTSERVICE_ACCESS |
+ EFI_VARIABLE_RUNTIME_ACCESS |
+ EFI_VARIABLE_READ_ONLY,
+ strlen(efi_lang_codes) + 1,
+ efi_lang_codes, false);
+ if (ret != EFI_SUCCESS)
+ goto out;
+
+ /*
+ * Variable PlatformLang defines the language that the machine has been
+ * configured for.
+ */
+ ret = efi_get_variable_int(u"PlatformLang",
+ &efi_global_variable_guid,
+ NULL, &data_size, &pos, NULL);
+ if (ret == EFI_BUFFER_TOO_SMALL) {
+ /* The variable is already set. Do not change it. */
+ ret = EFI_SUCCESS;
+ goto out;
+ }
+
+ /*
+ * The list of supported languages is semicolon separated. Use the first
+ * language to initialize PlatformLang.
+ */
+ pos = strchr(lang, ';');
+ if (pos)
+ *pos = 0;
+
+ ret = efi_set_variable_int(u"PlatformLang",
+ &efi_global_variable_guid,
+ EFI_VARIABLE_NON_VOLATILE |
+ EFI_VARIABLE_BOOTSERVICE_ACCESS |
+ EFI_VARIABLE_RUNTIME_ACCESS,
+ 1 + strlen(lang), lang, false);
+out:
+ if (ret != EFI_SUCCESS)
+ pr_warn("EFI: cannot initialize platform language settings\n");
+ return ret;
+}
+
+/**
+ * efi_init_os_indications() - indicate supported features for OS requests
+ *
+ * Set the OsIndicationsSupported variable.
+ *
+ * Return: status code
+ */
+static efi_status_t efi_init_os_indications(void *data)
+{
+ u64 os_indications_supported = 0;
+
+ return efi_set_variable_int(u"OsIndicationsSupported",
+ &efi_global_variable_guid,
+ EFI_VARIABLE_BOOTSERVICE_ACCESS |
+ EFI_VARIABLE_RUNTIME_ACCESS |
+ EFI_VARIABLE_READ_ONLY,
+ sizeof(os_indications_supported),
+ &os_indications_supported, false);
+}
+
+/*
+ * This value is used to initialize the PlatformLangCodes variable. Its
+ * value is a semicolon (;) separated list of language codes in native
+ * RFC 4646 format, e.g. "en-US;de-DE". The first language code is used
+ * to initialize the PlatformLang variable.
+ */
+static char *efi_lang_codes;
+
+static int efi_defaultvars_init(void)
+{
+ efi_lang_codes = xstrdup("en-US");
+ dev_add_param_string(&efidev, "lang.codes", NULL, NULL,
+ &efi_lang_codes, NULL);
+
+ efi_register_deferred_init(efi_init_platform_lang, efi_lang_codes);
+ efi_register_deferred_init(efi_init_os_indications, NULL);
+ return 0;
+}
+late_initcall(efi_defaultvars_init);
+
+BAREBOX_MAGICVAR(efi.lang.codes, "semicolon-separated list of RFC 4646 formatted languages for PlatformLangCodes");
--
2.47.3
^ permalink raw reply [flat|nested] 55+ messages in thread* [PATCH v1 48/54] ARM: don't disable MMU when EFI booting
2025-12-18 10:37 [PATCH v1 00/54] efi: implement EFI loader support in barebox Ahmad Fatoum
` (46 preceding siblings ...)
2025-12-18 10:38 ` [PATCH v1 47/54] efi: loader: populate OsIndicationsSupported/PlatformLang variables Ahmad Fatoum
@ 2025-12-18 10:38 ` Ahmad Fatoum
2025-12-18 10:38 ` [PATCH v1 49/54] efi: runtime: add runtime service support after ExitBootServices Ahmad Fatoum
` (5 subsequent siblings)
53 siblings, 0 replies; 55+ messages in thread
From: Ahmad Fatoum @ 2025-12-18 10:38 UTC (permalink / raw)
To: barebox; +Cc: Ahmad Fatoum
EFI boot does not disable MMU or caches and Linux will log errors if we
disable them. Therefore selectively, skip MMU disablement.
This may give us some performance boost when we slim down the support
for direct EFI-stub boot.
Signed-off-by: Ahmad Fatoum <a.fatoum@pengutronix.de>
---
arch/arm/cpu/cpu.c | 3 +++
1 file changed, 3 insertions(+)
diff --git a/arch/arm/cpu/cpu.c b/arch/arm/cpu/cpu.c
index 0c678f54c023..eb9a2310534f 100644
--- a/arch/arm/cpu/cpu.c
+++ b/arch/arm/cpu/cpu.c
@@ -90,6 +90,9 @@ static void arch_shutdown(void)
disable_interrupts();
+ if (efi_is_loader() == EFI_LOADER_RUNTIME)
+ return;
+
mmu_disable();
icache_invalidate();
--
2.47.3
^ permalink raw reply [flat|nested] 55+ messages in thread* [PATCH v1 49/54] efi: runtime: add runtime service support after ExitBootServices
2025-12-18 10:37 [PATCH v1 00/54] efi: implement EFI loader support in barebox Ahmad Fatoum
` (47 preceding siblings ...)
2025-12-18 10:38 ` [PATCH v1 48/54] ARM: don't disable MMU when EFI booting Ahmad Fatoum
@ 2025-12-18 10:38 ` Ahmad Fatoum
2025-12-18 10:38 ` [PATCH v1 50/54] efi: runtime: add relocation check Ahmad Fatoum
` (4 subsequent siblings)
53 siblings, 0 replies; 55+ messages in thread
From: Ahmad Fatoum @ 2025-12-18 10:38 UTC (permalink / raw)
To: barebox; +Cc: Ahmad Fatoum
Before supporting ExitBootServices, we need the runtime service table to
be located completely in the .efi_runtime section and not reference
anything outside.
This is easiest by just maintaining a second runtime-only EFI runtime
service table, so do just that.
Signed-off-by: Ahmad Fatoum <a.fatoum@pengutronix.de>
---
arch/arm/Kconfig | 1 +
efi/runtime/Makefile | 1 +
efi/runtime/services.c | 303 +++++++++++++++++++++++++++++++++++++++++
3 files changed, 305 insertions(+)
create mode 100644 efi/runtime/services.c
diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig
index ae3de9504a9b..5123e9b1402c 100644
--- a/arch/arm/Kconfig
+++ b/arch/arm/Kconfig
@@ -317,6 +317,7 @@ config AEABI
config AAPCS64
bool "Observe ARM64 Procedure Call Standard" if COMPILE_TEST
depends on CPU_V8
+ default EFI_RUNTIME
help
This option instructs barebox to observe the AAPCS64 for
interoperability with other firmware.
diff --git a/efi/runtime/Makefile b/efi/runtime/Makefile
index 90ed3ef454be..a9af06de60ab 100644
--- a/efi/runtime/Makefile
+++ b/efi/runtime/Makefile
@@ -1,5 +1,6 @@
# SPDX-License-Identifier: GPL-2.0
+lib-y += services.o
lib-y += efi_var_mem.o efi_variable.o
KASAN_SANITIZE := n
diff --git a/efi/runtime/services.c b/efi/runtime/services.c
new file mode 100644
index 000000000000..11a1c5cb6cb6
--- /dev/null
+++ b/efi/runtime/services.c
@@ -0,0 +1,303 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+// SPDX-FileCopyrightText: 2022 Ahmad Fatoum <a.fatoum@pengutronix.de>
+/*
+ * EFI application runtime services - detached callbacks after
+ * boot services have been exited.
+ */
+
+/* LSP-Workaround as our compile_commands.json target doesn't handle lib-y yet */
+#include "common.h"
+
+#include <efi/loader.h>
+#include <efi/runtime.h>
+#include <efi/loader/variable.h>
+#include <efi/variable.h>
+#include <efi/error.h>
+#include <generated/version.h>
+#include <poweroff.h>
+#include <restart.h>
+#include <barebox.h>
+#include <debug_ll.h>
+
+/**
+ * efirt_unimplemented() - replacement macro, returns EFI_UNSUPPORTED
+ *
+ * This macro is used after SetVirtualAddressMap() to be called as replacement
+ * for services that are not available anymore due to constraints of the barebox
+ * implementation.
+ *
+ * Return: EFI_UNSUPPORTED
+ */
+#define efirt_unimplemented() ({ \
+ EFI_ENTRY(L""); \
+ EFI_EXIT(EFI_UNSUPPORTED); \
+})
+
+/**
+ * efirt_reset_system() - reset system at runtime
+ *
+ * This function implements the ResetSystem() runtime service after
+ * SetVirtualAddressMap() is called. As this placeholder cannot reset the
+ * system it simply return to the caller.
+ *
+ * Boards may override the helpers below to implement reset functionality.
+ *
+ * See the Unified Extensible Firmware Interface (UEFI) specification for
+ * details.
+ *
+ * @reset_type: type of reset to perform
+ * @reset_status: status code for the reset
+ * @data_size: size of reset_data
+ * @reset_data: information about the reset
+ */
+static void EFIAPI efirt_reset_system(enum efi_reset_type reset_type,
+ efi_status_t reset_status,
+ size_t data_size, void *reset_data)
+{
+ switch (reset_type) {
+ case EFI_RESET_WARM:
+ EFI_ENTRY(L"warm");
+ rt_restart_machine(RESTART_WARM);
+ break;
+ case EFI_RESET_COLD:
+ EFI_ENTRY(L"cold");
+ rt_restart_machine(0);
+ break;
+ case EFI_RESET_PLATFORM_SPECIFIC:
+ EFI_ENTRY(L"platform");
+ rt_restart_machine(0);
+ break;
+ case EFI_RESET_SHUTDOWN:
+ EFI_ENTRY(L"shutdown");
+ rt_poweroff_machine(0);
+ break;
+ }
+
+ __hang();
+}
+
+/**
+ * efirt_get_time() - get current time
+ *
+ * This function implements the GetTime runtime service after
+ * SetVirtualAddressMap() is called. As the barebox driver are not available
+ * anymore only an error code is returned.
+ *
+ * See the Unified Extensible Firmware Interface (UEFI) specification
+ * for details.
+ *
+ * @time: pointer to structure to receive current time
+ * @capabilities: pointer to structure to receive RTC properties
+ * Returns: status code
+ */
+static efi_status_t EFIAPI efirt_get_time(
+ struct efi_time *time,
+ struct efi_time_cap *capabilities)
+{
+ return efirt_unimplemented();
+}
+
+/**
+ * efirt_set_time() - set current time
+ *
+ * This function implements the SetTime runtime service after
+ * SetVirtualAddressMap() is called. As the barebox driver are not available
+ * anymore only an error code is returned.
+ *
+ * See the Unified Extensible Firmware Interface (UEFI) specification
+ * for details.
+ *
+ * @time: pointer to structure to with current time
+ * Returns: status code
+ */
+static efi_status_t EFIAPI efirt_set_time(struct efi_time *time)
+{
+ return efirt_unimplemented();
+}
+
+/**
+ * efirt_update_capsule_unsupported() - process information from operating system
+ *
+ * This function implements the UpdateCapsule() runtime service.
+ *
+ * See the Unified Extensible Firmware Interface (UEFI) specification for
+ * details.
+ *
+ * @capsule_header_array: pointer to array of virtual pointers
+ * @capsule_count: number of pointers in capsule_header_array
+ * @scatter_gather_list: pointer to array of physical pointers
+ * Returns: status code
+ */
+static efi_status_t EFIAPI efirt_update_capsule_unsupported(
+ struct efi_capsule_header **capsule_header_array,
+ size_t capsule_count,
+ u64 scatter_gather_list)
+{
+ return efirt_unimplemented();
+}
+
+/**
+ * efirt_query_capsule_caps_unsupported() - check if capsule is supported
+ *
+ * This function implements the QueryCapsuleCapabilities() runtime service.
+ *
+ * See the Unified Extensible Firmware Interface (UEFI) specification for
+ * details.
+ *
+ * @capsule_header_array: pointer to array of virtual pointers
+ * @capsule_count: number of pointers in capsule_header_array
+ * @maximum_capsule_size: maximum capsule size
+ * @reset_type: type of reset needed for capsule update
+ * Returns: status code
+ */
+static efi_status_t EFIAPI efirt_query_capsule_caps_unsupported(
+ struct efi_capsule_header **capsule_header_array,
+ size_t capsule_count,
+ u64 *maximum_capsule_size,
+ u32 *reset_type)
+{
+ return efirt_unimplemented();
+}
+
+static efi_status_t EFIAPI efirt_get_wakeup_time(char *enabled, char *pending,
+ struct efi_time *time)
+{
+ return efirt_unimplemented();
+}
+
+static efi_status_t EFIAPI efirt_set_wakeup_time(char enabled,
+ struct efi_time *time)
+{
+ return efirt_unimplemented();
+}
+
+static efi_status_t EFIAPI efirt_set_virtual_address_map(size_t memory_map_size,
+ size_t descriptor_size,
+ uint32_t descriptor_version,
+ struct efi_memory_desc *virtmap)
+{
+ return efirt_unimplemented();
+}
+
+static efi_status_t EFIAPI efirt_convert_pointer(unsigned long dbg, void **address)
+{
+ return efirt_unimplemented();
+}
+
+static efi_status_t EFIAPI
+efirt_get_variable(u16 *variable_name, const efi_guid_t *guid,
+ u32 *attributes, efi_uintn_t *data_size, void *data)
+{
+ efi_status_t ret;
+
+ EFI_ENTRY(variable_name);
+
+ ret = efi_get_variable_mem(variable_name, guid, attributes, data_size,
+ data, NULL, EFI_VARIABLE_RUNTIME_ACCESS);
+
+ /* Remove EFI_VARIABLE_READ_ONLY flag */
+ if (attributes)
+ *attributes &= EFI_VARIABLE_MASK;
+
+ return EFI_EXIT(ret);
+}
+
+static efi_status_t EFIAPI
+efirt_get_next_variable_name(efi_uintn_t *variable_name_size,
+ u16 *variable_name, efi_guid_t *guid)
+{
+ efi_status_t ret;
+
+ EFI_ENTRY(variable_name);
+
+ ret = efi_get_next_variable_name_mem(variable_name_size, variable_name,
+ guid, EFI_VARIABLE_RUNTIME_ACCESS);
+
+ return EFI_EXIT(ret);
+}
+
+/**
+ * efirt_query_variable_info_runtime() - runtime implementation of
+ * QueryVariableInfo()
+ *
+ * @attributes: bitmask to select variables to be
+ * queried
+ * @maximum_variable_storage_size: maximum size of storage area for the
+ * selected variable types
+ * @remaining_variable_storage_size: remaining size of storage are for the
+ * selected variable types
+ * @maximum_variable_size: maximum size of a variable of the
+ * selected type
+ * Returns: status code
+ */
+static efi_status_t EFIAPI
+efirt_query_variable_info(u32 attributes,
+ u64 *maximum_variable_storage_size,
+ u64 *remaining_variable_storage_size,
+ u64 *maximum_variable_size)
+{
+ efi_status_t ret;
+
+ EFI_ENTRY(L"");
+
+ if (!(attributes & EFI_VARIABLE_RUNTIME_ACCESS))
+ return EFI_INVALID_PARAMETER;
+ if ((attributes & (EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS |
+ EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS |
+ EFI_VARIABLE_ENHANCED_AUTHENTICATED_ACCESS)))
+ return EFI_UNSUPPORTED;
+
+ ret = efi_query_variable_info_int(attributes,
+ maximum_variable_storage_size,
+ remaining_variable_storage_size,
+ maximum_variable_size);
+
+ return EFI_EXIT(ret);
+}
+
+static efi_status_t EFIAPI efirt_get_next_high_mono_count(uint32_t *high_count)
+{
+ return efirt_unimplemented();
+}
+
+static struct efi_runtime_services efi_runtime_services_detached = {
+ .hdr = {
+ .signature = EFI_RUNTIME_SERVICES_SIGNATURE,
+ .revision = EFI_SPECIFICATION_VERSION,
+ .headersize = sizeof(struct efi_runtime_services),
+ },
+ .get_time = efirt_get_time,
+ .set_time = efirt_set_time,
+ .get_wakeup_time = efirt_get_wakeup_time,
+ .set_wakeup_time = efirt_set_wakeup_time,
+ .set_virtual_address_map = efirt_set_virtual_address_map,
+ .convert_pointer = efirt_convert_pointer,
+ .get_variable = efirt_get_variable,
+ .get_next_variable = efirt_get_next_variable_name,
+ .set_variable = efirt_set_variable,
+ .get_next_high_mono_count = efirt_get_next_high_mono_count,
+ .reset_system = efirt_reset_system,
+ .update_capsule = efirt_update_capsule_unsupported,
+ .query_capsule_caps = efirt_query_capsule_caps_unsupported,
+ .query_variable_info = efirt_query_variable_info,
+};
+
+void efi_runtime_detach(struct efi_system_table *systab)
+{
+ /* Disable boot time services */
+ systab->con_in_handle = NULL;
+ systab->con_in = NULL;
+ systab->con_out_handle = NULL;
+ systab->con_out = NULL;
+ systab->stderr_handle = NULL;
+ systab->std_err = NULL;
+ systab->boottime = NULL;
+
+ systab->runtime = &efi_runtime_services_detached;
+
+ /* Recalculate CRC32 */
+ efi_update_table_header_crc32(&systab->hdr);
+ efi_update_table_header_crc32(&systab->runtime->hdr);
+
+ puts_ll("detached\n");
+}
--
2.47.3
^ permalink raw reply [flat|nested] 55+ messages in thread* [PATCH v1 50/54] efi: runtime: add relocation check
2025-12-18 10:37 [PATCH v1 00/54] efi: implement EFI loader support in barebox Ahmad Fatoum
` (48 preceding siblings ...)
2025-12-18 10:38 ` [PATCH v1 49/54] efi: runtime: add runtime service support after ExitBootServices Ahmad Fatoum
@ 2025-12-18 10:38 ` Ahmad Fatoum
2025-12-18 10:38 ` [PATCH v1 51/54] efi: loader: CONFIG_EFI_RT_VOLATILE_STORE Ahmad Fatoum
` (3 subsequent siblings)
53 siblings, 0 replies; 55+ messages in thread
From: Ahmad Fatoum @ 2025-12-18 10:38 UTC (permalink / raw)
To: barebox; +Cc: Ahmad Fatoum
I am not happy with the way we embed the runtime services. While we make
use of NOCROSSREFS_TO() to catch stray references, we need to list all
sections that may not be referenced and things can slip through.
Also, Clang does not support the directive. I anticipate this be solved
properly when barebox proper is packaged as proper ELF: Then PBL would
be responsible for applying relocations, setting up MMU protections and
can at the same time relocate a distinct EFI runtime services DSO.
Until then, let's a hacky check to verify that we don't access
efi_runtime_data global variables through the GOT as we only have a
single GOT and it's not marked as runtime data.
Signed-off-by: Ahmad Fatoum <a.fatoum@pengutronix.de>
---
efi/runtime/Makefile | 9 +++++++++
scripts/check-efi-runtime.sh | 13 +++++++++++++
2 files changed, 22 insertions(+)
create mode 100755 scripts/check-efi-runtime.sh
diff --git a/efi/runtime/Makefile b/efi/runtime/Makefile
index a9af06de60ab..0aca6aeedc38 100644
--- a/efi/runtime/Makefile
+++ b/efi/runtime/Makefile
@@ -42,3 +42,12 @@ $(obj)/%.efirt.o: $(obj)/%.o FORCE
$(call if_changed,efirtcopy)
obj-y := $(patsubst %.o,%.efirt.o,$(lib-y))
+BAREBOX_EFIRT_OBJS := $(patsubst %.o,$(obj)/%.o,$(obj-y))
+
+quiet_cmd_chkrel__ ?= CHKREL $@
+ cmd_chkrel__ ?= $(srctree)/scripts/check-efi-runtime.sh $@ $(2)
+
+$(obj)/efirt.so: $(BAREBOX_EFIRT_OBJS) FORCE
+ $(call if_changed,chkrel__,$(BAREBOX_EFIRT_OBJS))
+
+always-y += efirt.so
diff --git a/scripts/check-efi-runtime.sh b/scripts/check-efi-runtime.sh
new file mode 100755
index 000000000000..46688b724d8e
--- /dev/null
+++ b/scripts/check-efi-runtime.sh
@@ -0,0 +1,13 @@
+#!/bin/sh
+# SPDX-License-Identifier: GPL-2.0
+
+if [ "$#" -lt 2 ]; then
+ 2>&1 echo "USAGE: $0 OUTPUT INPUT..."
+ exit 2
+fi
+
+${LD} -shared --gc-sections --whole-archive -o "$@" || {
+ 2>&1 echo
+ 2>&1 echo "Link check failed. Relocations to outside EFI runtime code?"
+ exit 1;
+}
--
2.47.3
^ permalink raw reply [flat|nested] 55+ messages in thread* [PATCH v1 51/54] efi: loader: CONFIG_EFI_RT_VOLATILE_STORE
2025-12-18 10:37 [PATCH v1 00/54] efi: implement EFI loader support in barebox Ahmad Fatoum
` (49 preceding siblings ...)
2025-12-18 10:38 ` [PATCH v1 50/54] efi: runtime: add relocation check Ahmad Fatoum
@ 2025-12-18 10:38 ` Ahmad Fatoum
2025-12-18 10:38 ` [PATCH v1 52/54] efi: loader: support ExitBootServices Ahmad Fatoum
` (2 subsequent siblings)
53 siblings, 0 replies; 55+ messages in thread
From: Ahmad Fatoum @ 2025-12-18 10:38 UTC (permalink / raw)
To: barebox; +Cc: Ahmad Fatoum
If we store EFI variables on ESP during boottime, we need cooperation
from the OS to write that file to implement SetVariable.
Populate the necessary options to allow efivar(1) to read a dump
of the variables after writing them and then persist them to ESP.
Note that this violates the EFI spec and it's not power-fail safe, but
it's an ok fallback for now.
Signed-off-by: Ahmad Fatoum <a.fatoum@pengutronix.de>
---
efi/loader/Kconfig | 18 ++++++++++++++
efi/loader/efi_var_file.c | 51 +++++++++++++++++++++++++++++++++++++++
efi/loader/runtime.c | 4 +++
efi/loader/variable.h | 2 ++
4 files changed, 75 insertions(+)
diff --git a/efi/loader/Kconfig b/efi/loader/Kconfig
index 4a5e4c375fd4..5692e54ebe01 100644
--- a/efi/loader/Kconfig
+++ b/efi/loader/Kconfig
@@ -68,6 +68,24 @@ config EFI_VARIABLE_NO_STORE
endchoice
+config EFI_RT_VOLATILE_STORE
+ bool "Allow variable runtime services in volatile storage (e.g RAM)"
+ depends on EFI_VARIABLE_FILE_STORE
+ select EFI_RUNTIME_SET_VARIABLE
+ default y
+ help
+ When EFI variables are stored on file we don't allow SetVariableRT,
+ since the OS doesn't know how to write that file. At the same time
+ we copy runtime variables in DRAM and support GetVariableRT
+
+ Enable this option to allow SetVariableRT on the RAM backend of
+ the EFI variable storage. The OS will be responsible for syncing
+ the RAM contents to the file, otherwise any changes made during
+ runtime won't persist reboots.
+ Authenticated variables are not supported. Note that this will
+ violate the EFI spec since writing auth variables will return
+ EFI_INVALID_PARAMETER
+
endmenu
source "efi/loader/protocols/Kconfig"
diff --git a/efi/loader/efi_var_file.c b/efi/loader/efi_var_file.c
index 08a5c172cced..652354693ae7 100644
--- a/efi/loader/efi_var_file.c
+++ b/efi/loader/efi_var_file.c
@@ -184,6 +184,57 @@ efi_status_t efi_var_from_file(int dirfd, const char *filename)
free(buf);
return ret;
}
+
+// SPDX-SnippetBegin
+// SPDX-Snippet-Comment: Origin-URL: https://github.com/u-boot/u-boot/blob/e9c34fab18a9a0022b36729afd8e262e062764e2/lib/efi_loader/efi_runtime.c
+
+efi_status_t efi_init_runtime_variable_supported(void)
+{
+ u8 s = 0;
+ int ret;
+
+ if (!IS_ENABLED(CONFIG_EFI_RT_VOLATILE_STORE))
+ return EFI_SUCCESS;
+
+ ret = efi_set_variable_int(u"RTStorageVolatile",
+ &efi_file_store_vars_guid,
+ EFI_VARIABLE_BOOTSERVICE_ACCESS |
+ EFI_VARIABLE_RUNTIME_ACCESS |
+ EFI_VARIABLE_READ_ONLY,
+ strlen(efi_var_file_name) + 1,
+ efi_var_file_name, false);
+ if (ret != EFI_SUCCESS) {
+ pr_err("Failed to set RTStorageVolatile\n");
+ return ret;
+ }
+ /*
+ * This variable needs to be visible so users can read it,
+ * but the real contents are going to be filled during
+ * GetVariable
+ */
+ ret = efi_set_variable_int(u"VarToFile",
+ &efi_file_store_vars_guid,
+ EFI_VARIABLE_BOOTSERVICE_ACCESS |
+ EFI_VARIABLE_RUNTIME_ACCESS |
+ EFI_VARIABLE_READ_ONLY,
+ sizeof(s),
+ &s, false);
+ if (ret != EFI_SUCCESS) {
+ pr_err("Failed to set VarToFile\n");
+ efi_set_variable_int(u"RTStorageVolatile",
+ &efi_file_store_vars_guid,
+ EFI_VARIABLE_BOOTSERVICE_ACCESS |
+ EFI_VARIABLE_RUNTIME_ACCESS |
+ EFI_VARIABLE_READ_ONLY,
+ 0, NULL, false);
+ return ret;
+ }
+
+ return EFI_SUCCESS;
+}
+
+// SPDX-SnippetEnd
+
static int efi_init_var_params(void)
{
if (efi_is_payload())
diff --git a/efi/loader/runtime.c b/efi/loader/runtime.c
index b46c85eeaa7b..4b3cb6df1350 100644
--- a/efi/loader/runtime.c
+++ b/efi/loader/runtime.c
@@ -71,6 +71,10 @@ efi_status_t efi_init_runtime_supported(void)
CHECK_RT_FLAG(QUERY_CAPSULE_CAPABILITIES) |
CHECK_RT_FLAG(QUERY_VARIABLE_INFO);
+ ret = efi_init_runtime_variable_supported();
+ if (ret != EFI_SUCCESS)
+ return ret;
+
return efi_install_configuration_table(&efi_rt_properties_table_guid, rt_table);
}
diff --git a/efi/loader/variable.h b/efi/loader/variable.h
index 3710be84a2d1..775bd11dc450 100644
--- a/efi/loader/variable.h
+++ b/efi/loader/variable.h
@@ -2,6 +2,8 @@
#include <efi/types.h>
#include <efi/error.h>
+efi_status_t efi_init_runtime_variable_supported(void);
+
efi_status_t EFIAPI efi_get_variable_boot(u16 *variable_name,
const efi_guid_t *vendor, u32 *attributes,
efi_uintn_t *data_size, void *data);
--
2.47.3
^ permalink raw reply [flat|nested] 55+ messages in thread* [PATCH v1 52/54] efi: loader: support ExitBootServices
2025-12-18 10:37 [PATCH v1 00/54] efi: implement EFI loader support in barebox Ahmad Fatoum
` (50 preceding siblings ...)
2025-12-18 10:38 ` [PATCH v1 51/54] efi: loader: CONFIG_EFI_RT_VOLATILE_STORE Ahmad Fatoum
@ 2025-12-18 10:38 ` Ahmad Fatoum
2025-12-18 10:38 ` [PATCH v1 53/54] efi: loader: pass along SMBIOS table Ahmad Fatoum
2025-12-18 10:38 ` [PATCH v1 54/54] ARM: configs: add multi_v7/8_efiloader_defconfig Ahmad Fatoum
53 siblings, 0 replies; 55+ messages in thread
From: Ahmad Fatoum @ 2025-12-18 10:38 UTC (permalink / raw)
To: barebox; +Cc: Ahmad Fatoum
With everything now in place, let's support ExitBootServices, so a
kernel can startup and communicate to barebox acting as EFI runtime.
Signed-off-by: Ahmad Fatoum <a.fatoum@pengutronix.de>
---
efi/loader/boot.c | 67 ++++++++++++++++++++++++++++++++++++++++++++++-
1 file changed, 66 insertions(+), 1 deletion(-)
diff --git a/efi/loader/boot.c b/efi/loader/boot.c
index 29cab7acd1c0..93a3412f5794 100644
--- a/efi/loader/boot.c
+++ b/efi/loader/boot.c
@@ -27,7 +27,9 @@
#include <efi/error.h>
#include <efi/variable.h>
#include <efi/devicepath.h>
+#include <efi/mode.h>
#include <efi/loader/trace.h>
+#include <efi/runtime.h>
#include <malloc.h>
#include <pe.h>
#include <asm/cache.h>
@@ -2049,9 +2051,72 @@ efi_status_t EFIAPI efiloader_load_image(bool boot_policy,
static efi_status_t EFIAPI efi_exit_boot_services(efi_handle_t image_handle,
efi_uintn_t map_key)
{
+ struct efi_event *evt, *next_event;
+ efi_status_t ret = EFI_SUCCESS;
+
EFI_ENTRY("%p, %zx", image_handle, map_key);
- return EFI_EXIT(EFI_UNSUPPORTED);
+ /* Check that the caller has read the current memory map */
+ if (map_key != efi_memory_map_key) {
+ ret = EFI_INVALID_PARAMETER;
+ goto out;
+ }
+
+ /* Check if ExitBootServices has already been called */
+ if (!systab.boottime)
+ goto out;
+
+ /* Notify EFI_EVENT_GROUP_BEFORE_EXIT_BOOT_SERVICES event group. */
+ list_for_each_entry(evt, &efi_events, link) {
+ if (evt->group &&
+ !efi_guidcmp(*evt->group,
+ efi_guid_event_group_before_exit_boot_services)) {
+ efi_signal_event(evt);
+ break;
+ }
+ }
+
+ /* Stop all timer related activities */
+ timers_enabled = false;
+
+ /* Add related events to the event group */
+ list_for_each_entry(evt, &efi_events, link) {
+ if (evt->type == EFI_EVT_SIGNAL_EXIT_BOOT_SERVICES)
+ evt->group = &efi_guid_event_group_exit_boot_services;
+ }
+ /* Notify that ExitBootServices is invoked. */
+ list_for_each_entry(evt, &efi_events, link) {
+ if (evt->group &&
+ !efi_guidcmp(*evt->group,
+ efi_guid_event_group_exit_boot_services)) {
+ efi_signal_event(evt);
+ break;
+ }
+ }
+
+ /* Make sure that notification functions are not called anymore */
+ efi_tpl = EFI_TPL_HIGH_LEVEL;
+
+ /* Remove all events except EFI_EVT_SIGNAL_VIRTUAL_ADDRESS_CHANGE */
+ list_for_each_entry_safe(evt, next_event, &efi_events, link) {
+ if (evt->type != EFI_EVT_SIGNAL_VIRTUAL_ADDRESS_CHANGE)
+ list_del(&evt->link);
+ }
+
+ /* Patch out unsupported runtime function */
+ efi_runtime_detach(&systab);
+
+ efi_loader_set_state(EFI_LOADER_RUNTIME);
+
+ resched();
+
+ shutdown_barebox();
+
+ /* Give the payload some time to boot */
+ efi_set_watchdog(0);
+out:
+
+ return EFI_EXIT(ret);
}
/**
--
2.47.3
^ permalink raw reply [flat|nested] 55+ messages in thread* [PATCH v1 53/54] efi: loader: pass along SMBIOS table
2025-12-18 10:37 [PATCH v1 00/54] efi: implement EFI loader support in barebox Ahmad Fatoum
` (51 preceding siblings ...)
2025-12-18 10:38 ` [PATCH v1 52/54] efi: loader: support ExitBootServices Ahmad Fatoum
@ 2025-12-18 10:38 ` Ahmad Fatoum
2025-12-18 10:38 ` [PATCH v1 54/54] ARM: configs: add multi_v7/8_efiloader_defconfig Ahmad Fatoum
53 siblings, 0 replies; 55+ messages in thread
From: Ahmad Fatoum @ 2025-12-18 10:38 UTC (permalink / raw)
To: barebox; +Cc: Ahmad Fatoum
This allows e.g. dmidecode in Linux to query the information that barebox
has collected.
Signed-off-by: Ahmad Fatoum <a.fatoum@pengutronix.de>
---
common/Kconfig | 13 +++++++++++
efi/loader/Makefile | 1 +
efi/loader/smbios.c | 55 +++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 69 insertions(+)
create mode 100644 efi/loader/smbios.c
diff --git a/common/Kconfig b/common/Kconfig
index b61a5bc40a1f..d4d9d930a996 100644
--- a/common/Kconfig
+++ b/common/Kconfig
@@ -755,6 +755,19 @@ config MMCBLKDEV_ROOTARG
[1] fa2d0aa96941 ("mmc: core: Allow setting slot index via device tree
alias")
+config GENERATE_SMBIOS_TABLE
+ bool "Generate an SMBIOS (System Management BIOS) table"
+ depends on EFI_LOADER && OF
+ select SMBIOS
+ default y
+ help
+ The System Management BIOS (SMBIOS) specification addresses how
+ motherboard and system vendors present management information about
+ their products in a standard format by extending the BIOS interface
+ on Intel architecture systems.
+
+ Check http://www.dmtf.org/standards/smbios for details.
+
config BAREBOX_UPDATE
bool "In-system barebox update infrastructure"
diff --git a/efi/loader/Makefile b/efi/loader/Makefile
index c06d1f7c8c35..7992a4f5caa0 100644
--- a/efi/loader/Makefile
+++ b/efi/loader/Makefile
@@ -15,6 +15,7 @@ obj-y += loadopts.o
obj-$(CONFIG_BOOT) += bootesp.o
obj-$(CONFIG_EFI_LOADER_BOOTMGR) += efibootmgr.o
obj-$(CONFIG_BOOTM) += bootm.o
+obj-$(CONFIG_GENERATE_SMBIOS_TABLE) += smbios.o
obj-y += efi_var_common.o
obj-y += efi_variable.o
obj-y += efi_var_mem.o
diff --git a/efi/loader/smbios.c b/efi/loader/smbios.c
new file mode 100644
index 000000000000..81ba77c24017
--- /dev/null
+++ b/efi/loader/smbios.c
@@ -0,0 +1,55 @@
+// SPDX-License-Identifier: GPL-2.0+
+// SPDX-FileCopyrightText: 2016 Alexander Graf
+// SPDX-Comment: Origin-URL: https://github.com/u-boot/u-boot/blob/1c5aab803c0b0f07be1d3b0029f4031463497acf/lib/efi_loader/efi_smbios.c
+
+#define pr_fmt(fmt) "efi-loader: smbios: " fmt
+
+#include <efi/loader.h>
+#include <efi/guid.h>
+#include <efi/error.h>
+#include <efi/memory.h>
+#include <efi/loader/table.h>
+#include <linux/printk.h>
+#include <malloc.h>
+#include <smbios.h>
+#include <init.h>
+#include <linux/sizes.h>
+
+#define TABLE_SIZE SZ_4K
+
+/*
+ * Install the SMBIOS table as a configuration table.
+ *
+ * Return: status code
+ */
+static efi_status_t efi_smbios_register(void *data)
+{
+ efi_status_t ret;
+ u64 memory;
+ void *buf;
+
+ /* Align the table to a 4KB boundary to keep EFI happy */
+ ret = efi_allocate_pages(EFI_ALLOCATE_ANY_PAGES,
+ EFI_RUNTIME_SERVICES_DATA,
+ efi_size_in_pages(TABLE_SIZE),
+ &memory, "smbios");
+ if (ret != EFI_SUCCESS)
+ return ret;
+
+ buf = efi_phys_to_virt(memory);
+
+ if (!write_smbios_table(buf)) {
+ pr_err("Failed to write SMBIOS table\n");
+ return -EINVAL;
+ }
+
+ /* Install SMBIOS information as configuration table */
+ return efi_install_configuration_table(&efi_smbios3_guid, buf);
+}
+
+static int efi_smbios_init(void)
+{
+ efi_register_deferred_init(efi_smbios_register, NULL);
+ return 0;
+}
+postenvironment_initcall(efi_smbios_init);
--
2.47.3
^ permalink raw reply [flat|nested] 55+ messages in thread* [PATCH v1 54/54] ARM: configs: add multi_v7/8_efiloader_defconfig
2025-12-18 10:37 [PATCH v1 00/54] efi: implement EFI loader support in barebox Ahmad Fatoum
` (52 preceding siblings ...)
2025-12-18 10:38 ` [PATCH v1 53/54] efi: loader: pass along SMBIOS table Ahmad Fatoum
@ 2025-12-18 10:38 ` Ahmad Fatoum
53 siblings, 0 replies; 55+ messages in thread
From: Ahmad Fatoum @ 2025-12-18 10:38 UTC (permalink / raw)
To: barebox; +Cc: Ahmad Fatoum
We won't enable this in the defconfigs yet, until this gets wider
testing, but let's provide a fragment and a pattern rule to generate
efiloader enabled defconfigs.
Signed-off-by: Ahmad Fatoum <a.fatoum@pengutronix.de>
---
Makefile | 3 +++
arch/arm/Makefile | 4 ++++
common/boards/configs/efi-loader.config | 2 ++
3 files changed, 9 insertions(+)
create mode 100644 common/boards/configs/efi-loader.config
diff --git a/Makefile b/Makefile
index d74c57da2a99..c4a758493abc 100644
--- a/Makefile
+++ b/Makefile
@@ -618,6 +618,9 @@ include $(srctree)/arch/$(SRCARCH)/Makefile
export KBUILD_DEFCONFIG CC_VERSION_TEXT
endif
+%_efiloader_defconfig: FORCE
+ $(call merge_into_defconfig,$*_defconfig,efi-loader)
+
config: outputmakefile scripts_basic FORCE
$(Q)$(MAKE) $(build)=scripts/kconfig KCONFIG_DEFCONFIG_LIST= $@
diff --git a/arch/arm/Makefile b/arch/arm/Makefile
index 4d84f3bf9c72..5efc4c3457db 100644
--- a/arch/arm/Makefile
+++ b/arch/arm/Makefile
@@ -12,6 +12,10 @@ multi_v8_efi_defconfig:
$(call merge_into_defconfig,multi_v8_defconfig,efi-payload)
generated_configs += multi_v8_efi_defconfig
+# Generic rule applying the fragment is in top-level Makefile
+generated_configs += $(patsubst %_defconfig,%_efiloader_defconfig, \
+ multi_v7_defconfig multi_v8_defconfig)
+
KBUILD_CPPFLAGS += -D__ARM__ -fno-strict-aliasing
# Explicitly specifiy 32-bit ARM ISA since toolchain default can be -mthumb:
ifeq ($(CONFIG_CPU_64),y)
diff --git a/common/boards/configs/efi-loader.config b/common/boards/configs/efi-loader.config
new file mode 100644
index 000000000000..7fadc60e41a8
--- /dev/null
+++ b/common/boards/configs/efi-loader.config
@@ -0,0 +1,2 @@
+CONFIG_EFI_LOADER=y
+CONFIG_FS_EFIVARFS=y
--
2.47.3
^ permalink raw reply [flat|nested] 55+ messages in thread