All of lore.kernel.org
 help / color / mirror / Atom feed
* [U-Boot] [RFC 0/6] efi_loader: support runtime variable access via cache
@ 2019-06-05  4:21 AKASHI Takahiro
  2019-06-05  4:21 ` [U-Boot] [RFC 1/6] efi_loader: runtime: make SetVirtualAddressMap configurable AKASHI Takahiro
                   ` (6 more replies)
  0 siblings, 7 replies; 32+ messages in thread
From: AKASHI Takahiro @ 2019-06-05  4:21 UTC (permalink / raw)
  To: u-boot

EBBR v1.0 section 2.5.3 says
  Even when SetVariable() is not supported during runtime services,
  firmware should cache variable names and values in EfiRuntimeServicesData
  memory so that GetVariable() and GetNextVeriableName() can behave as
  specified.

This is an experimental patch set and the aim is to enable this feature.

Cache buffer is in the same format as U-Boot environment hash table,
but we cannot use functions in hashtable.c partly because most of U-Boot
code are not available at UEFI runtime and partly because entries in
a table are allocated by malloc(), which are again not available
at UEFI runtime. It is quite painful to modify exiting U-Boot code
so as to make it executable at UEFI runtime.

So I implemented a limited version of hsearch_r(). This may be
a discussion. Given that there are not so many UEFI variables,
we may want to use a simpler table format for caching.

Known issues:
* Currently I test this feature with a test which is temporarily embedded
  in ExitBootServices.
* After SetVirtualAddressMap, it won't work (TODO).
  ConvertPointer was implemented here just for this future work.
* So how can we test the feature?
  We can't use "printenv" command as its relocation won't take place.
* I see some Travis CI errors.
  This is probably due to no storage configured for UEFI variables.

Patch#1 to #4 are preparatory patches.
Patch#5 is mainly for testing.
Patch#6 is core part of this patch set.

AKASHI Takahiro (5):
  efi_loader: runtime: make SetVirtualAddressMap configurable
  efi: add RuntimeServicesSupported variable
  efi_loader: support convert_pointer at runtime
  cmd: efidebug: add "boot exit" sub-command
  efi_loader: variable: support runtime variable access via cache

Alexander Graf (1):
  efi_loader: Patch non-runtime code out at ExitBootServices already

 cmd/efidebug.c                     |  63 ++++
 include/efi_api.h                  |  15 +
 include/efi_loader.h               |  22 ++
 lib/efi_loader/Kconfig             |  24 ++
 lib/efi_loader/efi_boottime.c      |  11 +-
 lib/efi_loader/efi_runtime.c       | 155 ++++++++--
 lib/efi_loader/efi_setup.c         |   5 +
 lib/efi_loader/efi_variable.c      | 467 +++++++++++++++++++++++++++++
 test/py/tests/test_efi_selftest.py |   4 +-
 9 files changed, 735 insertions(+), 31 deletions(-)

-- 
2.21.0

^ permalink raw reply	[flat|nested] 32+ messages in thread

* [U-Boot] [RFC 1/6] efi_loader: runtime: make SetVirtualAddressMap configurable
  2019-06-05  4:21 [U-Boot] [RFC 0/6] efi_loader: support runtime variable access via cache AKASHI Takahiro
@ 2019-06-05  4:21 ` AKASHI Takahiro
  2019-06-15 19:46   ` Heinrich Schuchardt
  2019-06-05  4:21 ` [U-Boot] [RFC 2/6] efi: add RuntimeServicesSupported variable AKASHI Takahiro
                   ` (5 subsequent siblings)
  6 siblings, 1 reply; 32+ messages in thread
From: AKASHI Takahiro @ 2019-06-05  4:21 UTC (permalink / raw)
  To: u-boot

OS does not always need to call SetVirtualAddressMap.
(Ard confirmed this on arm64 linux.)
So let this API configurable. If disabled, it will return EFI_UNSUPPORTED
as UEFI specification requires.

Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org>
---
 lib/efi_loader/Kconfig       | 7 +++++++
 lib/efi_loader/efi_runtime.c | 8 ++++++++
 2 files changed, 15 insertions(+)

diff --git a/lib/efi_loader/Kconfig b/lib/efi_loader/Kconfig
index 8bf4b1754d06..bb9c7582b14d 100644
--- a/lib/efi_loader/Kconfig
+++ b/lib/efi_loader/Kconfig
@@ -44,6 +44,13 @@ config EFI_SET_TIME
 	  Provide the SetTime() runtime service at boottime. This service
 	  can be used by an EFI application to adjust the real time clock.
 
+config EFI_RUNTIME_SET_VIRTUAL_ADDRESS_MAP
+	bool "runtime service: SetVirtualAddressMap"
+	default n
+	help
+	  Enable SetVirtualAddressMap runtime service. This API will be
+	  called by OS just before it enters into virtual address mode.
+
 config EFI_DEVICE_PATH_TO_TEXT
 	bool "Device path to text protocol"
 	default y
diff --git a/lib/efi_loader/efi_runtime.c b/lib/efi_loader/efi_runtime.c
index 9c50955c9bd0..60442cb21d37 100644
--- a/lib/efi_loader/efi_runtime.c
+++ b/lib/efi_loader/efi_runtime.c
@@ -374,10 +374,12 @@ static const struct efi_runtime_detach_list_struct efi_runtime_detach_list[] = {
 		/* do_reset is gone */
 		.ptr = &efi_runtime_services.reset_system,
 		.patchto = efi_reset_system,
+#ifdef CONFIG_RUNTIME_SET_VIRTUAL_ADDRESS_MAP
 	}, {
 		/* invalidate_*cache_all are gone */
 		.ptr = &efi_runtime_services.set_virtual_address_map,
 		.patchto = &efi_unimplemented,
+#endif
 	}, {
 		/* RTC accessors are gone */
 		.ptr = &efi_runtime_services.get_time,
@@ -512,6 +514,7 @@ void efi_runtime_relocate(ulong offset, struct efi_mem_desc *map)
         invalidate_icache_all();
 }
 
+#ifdef CONFIG_RUNTIME_SET_VIRTUAL_ADDRESS_MAP
 /**
  * efi_set_virtual_address_map() - change from physical to virtual mapping
  *
@@ -619,6 +622,7 @@ static efi_status_t EFIAPI efi_set_virtual_address_map(
 
 	return EFI_EXIT(EFI_INVALID_PARAMETER);
 }
+#endif /* CONFIG_RUNTIME_SET_VIRTUAL_ADDRESS_MAP */
 
 /**
  * efi_add_runtime_mmio() - add memory-mapped IO region
@@ -796,7 +800,11 @@ struct efi_runtime_services __efi_runtime_data efi_runtime_services = {
 	.set_time = &efi_set_time_boottime,
 	.get_wakeup_time = (void *)&efi_unimplemented,
 	.set_wakeup_time = (void *)&efi_unimplemented,
+#ifdef CONFIG_RUNTIME_SET_VIRTUAL_ADDRESS_MAP
 	.set_virtual_address_map = &efi_set_virtual_address_map,
+#else
+	.set_virtual_address_map = (void *)&efi_unimplemented,
+#endif
 	.convert_pointer = (void *)&efi_invalid_parameter,
 	.get_variable = efi_get_variable,
 	.get_next_variable_name = efi_get_next_variable_name,
-- 
2.21.0

^ permalink raw reply related	[flat|nested] 32+ messages in thread

* [U-Boot] [RFC 2/6] efi: add RuntimeServicesSupported variable
  2019-06-05  4:21 [U-Boot] [RFC 0/6] efi_loader: support runtime variable access via cache AKASHI Takahiro
  2019-06-05  4:21 ` [U-Boot] [RFC 1/6] efi_loader: runtime: make SetVirtualAddressMap configurable AKASHI Takahiro
@ 2019-06-05  4:21 ` AKASHI Takahiro
  2019-06-13  5:56   ` Heinrich Schuchardt
  2019-06-05  4:21 ` [U-Boot] [RFC 3/6] efi_loader: support convert_pointer at runtime AKASHI Takahiro
                   ` (4 subsequent siblings)
  6 siblings, 1 reply; 32+ messages in thread
From: AKASHI Takahiro @ 2019-06-05  4:21 UTC (permalink / raw)
  To: u-boot

This variable is defined in UEFI specification 2.8, section 8.1.
Its value should be updated whenever we add any usable runtime services
function.

Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org>
---
 include/efi_api.h            | 15 +++++++++++++++
 include/efi_loader.h         |  3 +++
 lib/efi_loader/efi_runtime.c | 28 ++++++++++++++++++++++++++++
 lib/efi_loader/efi_setup.c   |  5 +++++
 4 files changed, 51 insertions(+)

diff --git a/include/efi_api.h b/include/efi_api.h
index 65584dd2d82a..d7d95edd4dfc 100644
--- a/include/efi_api.h
+++ b/include/efi_api.h
@@ -213,6 +213,21 @@ struct efi_capsule_header {
 	u32 capsule_image_size;
 };
 
+#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
+
 struct efi_runtime_services {
 	struct efi_table_hdr hdr;
 	efi_status_t (EFIAPI *get_time)(struct efi_time *time,
diff --git a/include/efi_loader.h b/include/efi_loader.h
index 23ce73226762..7bd8002e303e 100644
--- a/include/efi_loader.h
+++ b/include/efi_loader.h
@@ -573,6 +573,9 @@ static inline int guidcmp(const efi_guid_t *g1, const efi_guid_t *g2)
 #define __efi_runtime_data __attribute__ ((section (".data.efi_runtime")))
 #define __efi_runtime __attribute__ ((section (".text.efi_runtime")))
 
+/* Indicate supported runtime services */
+efi_status_t efi_init_runtime_supported(void);
+
 /* Update CRC32 in table header */
 void __efi_runtime efi_update_table_header_crc32(struct efi_table_hdr *table);
 
diff --git a/lib/efi_loader/efi_runtime.c b/lib/efi_loader/efi_runtime.c
index 60442cb21d37..cf202bb9ec07 100644
--- a/lib/efi_loader/efi_runtime.c
+++ b/lib/efi_loader/efi_runtime.c
@@ -89,6 +89,34 @@ struct elf_rela {
  * handle a good number of runtime callbacks
  */
 
+efi_status_t efi_init_runtime_supported(void)
+{
+	u16 efi_runtime_services_supported;
+
+	/*
+	 * This value must be synced with efi_runtime_detach_list
+	 * as well as efi_runtime_services.
+	 */
+	efi_runtime_services_supported = EFI_RT_SUPPORTED_RESET_SYSTEM;
+#ifdef CONFIG_EFI_GET_TIME
+	efi_runtime_services_supported |= EFI_RT_SUPPORTED_GET_TIME;
+#endif
+#ifdef CONFIG_EFI_SET_TIME
+	efi_runtime_services_supported |= EFI_RT_SUPPORTED_SET_TIME;
+#endif
+#ifdef CONFIG_EFI_RUNTIME_SET_VIRTUAL_ADDRESS_MAP
+	efi_runtime_services_supported |=
+				EFI_RT_SUPPORTED_SET_VIRTUAL_ADDRESS_MAP;
+#endif
+
+	return EFI_CALL(efi_set_variable(L"RuntimeServicesSupported",
+					 &efi_global_variable_guid,
+					 EFI_VARIABLE_BOOTSERVICE_ACCESS |
+					 EFI_VARIABLE_RUNTIME_ACCESS,
+					 sizeof(efi_runtime_services_supported),
+					 &efi_runtime_services_supported));
+}
+
 /**
  * efi_update_table_header_crc32() - Update crc32 in table header
  *
diff --git a/lib/efi_loader/efi_setup.c b/lib/efi_loader/efi_setup.c
index 45d6aca051f3..75fa344060d5 100644
--- a/lib/efi_loader/efi_setup.c
+++ b/lib/efi_loader/efi_setup.c
@@ -123,6 +123,11 @@ efi_status_t efi_init_obj_list(void)
 	if (ret != EFI_SUCCESS)
 		goto out;
 
+	/* Indicate supported runtime services */
+	ret = efi_init_runtime_supported();
+	if (ret != EFI_SUCCESS)
+		goto out;
+
 	/* Initialize system table */
 	ret = efi_initialize_system_table();
 	if (ret != EFI_SUCCESS)
-- 
2.21.0

^ permalink raw reply related	[flat|nested] 32+ messages in thread

* [U-Boot] [RFC 3/6] efi_loader: support convert_pointer at runtime
  2019-06-05  4:21 [U-Boot] [RFC 0/6] efi_loader: support runtime variable access via cache AKASHI Takahiro
  2019-06-05  4:21 ` [U-Boot] [RFC 1/6] efi_loader: runtime: make SetVirtualAddressMap configurable AKASHI Takahiro
  2019-06-05  4:21 ` [U-Boot] [RFC 2/6] efi: add RuntimeServicesSupported variable AKASHI Takahiro
@ 2019-06-05  4:21 ` AKASHI Takahiro
  2019-06-15 19:41   ` Heinrich Schuchardt
  2019-06-05  4:21 ` [U-Boot] [RFC 4/6] efi_loader: Patch non-runtime code out at ExitBootServices already AKASHI Takahiro
                   ` (3 subsequent siblings)
  6 siblings, 1 reply; 32+ messages in thread
From: AKASHI Takahiro @ 2019-06-05  4:21 UTC (permalink / raw)
  To: u-boot

With this patch, ConvertPointer runtime service is enabled.
This function will be useful only after SetVirtualAddressMap is called
and before it exits according to UEFI specification.

Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org>
---
 lib/efi_loader/Kconfig       |  8 ++++
 lib/efi_loader/efi_runtime.c | 81 ++++++++++++++++++++++++++----------
 2 files changed, 66 insertions(+), 23 deletions(-)

diff --git a/lib/efi_loader/Kconfig b/lib/efi_loader/Kconfig
index bb9c7582b14d..e2ef43157568 100644
--- a/lib/efi_loader/Kconfig
+++ b/lib/efi_loader/Kconfig
@@ -51,6 +51,14 @@ config EFI_RUNTIME_SET_VIRTUAL_ADDRESS_MAP
 	  Enable SetVirtualAddressMap runtime service. This API will be
 	  called by OS just before it enters into virtual address mode.
 
+config EFI_RUNTIME_CONVERT_POINTER
+	bool "runtime service: ConvertPointer"
+	default n
+	help
+	  Enable ConvertPointer runtime service. This API will be expected
+	  to be called by UEFI drivers in relocating themselves to virtual
+	  address space.
+
 config EFI_DEVICE_PATH_TO_TEXT
 	bool "Device path to text protocol"
 	default y
diff --git a/lib/efi_loader/efi_runtime.c b/lib/efi_loader/efi_runtime.c
index cf202bb9ec07..ff3684a4b692 100644
--- a/lib/efi_loader/efi_runtime.c
+++ b/lib/efi_loader/efi_runtime.c
@@ -27,7 +27,6 @@ LIST_HEAD(efi_runtime_mmio);
 
 static efi_status_t __efi_runtime EFIAPI efi_unimplemented(void);
 static efi_status_t __efi_runtime EFIAPI efi_device_error(void);
-static efi_status_t __efi_runtime EFIAPI efi_invalid_parameter(void);
 
 /*
  * TODO(sjg at chromium.org): These defines and structures should come from the ELF
@@ -108,6 +107,10 @@ efi_status_t efi_init_runtime_supported(void)
 	efi_runtime_services_supported |=
 				EFI_RT_SUPPORTED_SET_VIRTUAL_ADDRESS_MAP;
 #endif
+#ifdef CONFIG_EFI_RUNTIME_CONVERT_POINTER
+	efi_runtime_services_supported |=
+				EFI_RT_SUPPORTED_CONVERT_POINTER;
+#endif
 
 	return EFI_CALL(efi_set_variable(L"RuntimeServicesSupported",
 					 &efi_global_variable_guid,
@@ -392,6 +395,39 @@ efi_status_t __weak __efi_runtime EFIAPI efi_set_time(struct efi_time *time)
 	return EFI_UNSUPPORTED;
 }
 
+#ifdef CONFIG_EFI_RUNTIME_CONVERT_POINTER
+static struct efi_mem_desc *efi_virtmap __efi_runtime_data;
+static int efi_virtmap_num __efi_runtime_data;
+
+static efi_status_t __efi_runtime EFIAPI efi_convert_pointer(unsigned long dbg,
+							     void **address)
+{
+	struct efi_mem_desc *map;
+	efi_physical_addr_t addr;
+	int i;
+
+	if (!efi_virtmap)
+		return EFI_UNSUPPORTED;
+
+	if (!address)
+		return EFI_INVALID_PARAMETER;
+
+	for (i = 0, map = efi_virtmap; i < efi_virtmap_num; i++, map++) {
+		addr = (efi_physical_addr_t)*address;
+		if (addr >= map->physical_start &&
+		    (addr < (map->physical_start
+			     + (map->num_pages << EFI_PAGE_SHIFT)))) {
+			*address = (void *)map->virtual_start;
+			*address += addr - map->physical_start;
+
+			return EFI_SUCCESS;
+		}
+	}
+
+	return EFI_NOT_FOUND;
+}
+#endif
+
 struct efi_runtime_detach_list_struct {
 	void *ptr;
 	void *patchto;
@@ -599,6 +635,10 @@ static efi_status_t EFIAPI efi_set_virtual_address_map(
 		return EFI_EXIT(EFI_INVALID_PARAMETER);
 	}
 
+	efi_virtmap = virtmap;
+	efi_virtmap_num = n;
+
+#if 0 /* FIXME: This code is fragile as calloc is used in add_runtime_mmio */
 	/* Rebind mmio pointers */
 	for (i = 0; i < n; i++) {
 		struct efi_mem_desc *map = (void*)virtmap +
@@ -622,14 +662,14 @@ static efi_status_t EFIAPI efi_set_virtual_address_map(
 				*lmmio->ptr = (void *)new_addr;
 			}
 		}
-		if ((map_start <= (uintptr_t)systab.tables) &&
-		    (map_end >= (uintptr_t)systab.tables)) {
-			char *ptr = (char *)systab.tables;
-
-			ptr += off;
-			systab.tables = (struct efi_configuration_table *)ptr;
-		}
 	}
+#endif
+
+	/* FIXME */
+	efi_convert_pointer(0, (void **)&systab.tables);
+
+	/* All fixes must be done before this line */
+	efi_virtmap = NULL;
 
 	/* Move the actual runtime code over */
 	for (i = 0; i < n; i++) {
@@ -644,6 +684,11 @@ static efi_status_t EFIAPI efi_set_virtual_address_map(
 			/* Once we're virtual, we can no longer handle
 			   complex callbacks */
 			efi_runtime_detach(new_offset);
+
+			/*
+			 * FIXME:
+			 * We can no longer update RuntimeServicesSupported.
+			 */
 			return EFI_EXIT(EFI_SUCCESS);
 		}
 	}
@@ -733,20 +778,6 @@ static efi_status_t __efi_runtime EFIAPI efi_device_error(void)
 	return EFI_DEVICE_ERROR;
 }
 
-/**
- * efi_invalid_parameter() - replacement function, returns EFI_INVALID_PARAMETER
- *
- * This function is used after SetVirtualAddressMap() is called as replacement
- * for services that are not available anymore due to constraints of the U-Boot
- * implementation.
- *
- * Return:	EFI_INVALID_PARAMETER
- */
-static efi_status_t __efi_runtime EFIAPI efi_invalid_parameter(void)
-{
-	return EFI_INVALID_PARAMETER;
-}
-
 /**
  * efi_update_capsule() - process information from operating system
  *
@@ -833,7 +864,11 @@ struct efi_runtime_services __efi_runtime_data efi_runtime_services = {
 #else
 	.set_virtual_address_map = (void *)&efi_unimplemented,
 #endif
-	.convert_pointer = (void *)&efi_invalid_parameter,
+#ifdef CONFIG_EFI_RUNTIME_CONVERT_POINTER
+	.convert_pointer = &efi_convert_pointer,
+#else
+	.convert_pointer = (void *)&efi_unimplemented,
+#endif
 	.get_variable = efi_get_variable,
 	.get_next_variable_name = efi_get_next_variable_name,
 	.set_variable = efi_set_variable,
-- 
2.21.0

^ permalink raw reply related	[flat|nested] 32+ messages in thread

* [U-Boot] [RFC 4/6] efi_loader: Patch non-runtime code out at ExitBootServices already
  2019-06-05  4:21 [U-Boot] [RFC 0/6] efi_loader: support runtime variable access via cache AKASHI Takahiro
                   ` (2 preceding siblings ...)
  2019-06-05  4:21 ` [U-Boot] [RFC 3/6] efi_loader: support convert_pointer at runtime AKASHI Takahiro
@ 2019-06-05  4:21 ` AKASHI Takahiro
  2019-06-05  4:21 ` [U-Boot] [RFC 5/6] cmd: efidebug: add "boot exit" sub-command AKASHI Takahiro
                   ` (2 subsequent siblings)
  6 siblings, 0 replies; 32+ messages in thread
From: AKASHI Takahiro @ 2019-06-05  4:21 UTC (permalink / raw)
  To: u-boot

From: Alexander Graf <agraf@suse.de>

While discussing something completely different, Ard pointed out
that it might be legal to omit calling SetVirtualAddressMap altogether.

There is even a patch on the Linux Kernel Mailing List that implements
such behavior by now:

  https://patchwork.kernel.org/patch/10782393/

While that sounds great, we currently rely on the SetVirtualAddressMap
call to remove all references to code that would not work outside of
boot services.

So let's patch out those bits already on the call to ExitBootServices,
so that we can successfully run even when an OS chooses to omit
any call to SetVirtualAddressMap.

Reported-by: Ard Biesheuvel <ard.biesheuvel@linaro.org>
Signed-off-by: Alexander Graf <agraf@suse.de>

OpenBSD is not calling SetVirtualAddressMap on ARM 32 bit.

Adjust selftest: expect 'U-Boot' instead of 'resetting'.

Reviewed-by: Heinrich Schuchardt <xypron.glpk@gmx.de>
---
 include/efi_loader.h               |  2 ++
 lib/efi_loader/efi_boottime.c      |  1 +
 lib/efi_loader/efi_runtime.c       | 25 ++++++++++++++++++++-----
 test/py/tests/test_efi_selftest.py |  4 ++--
 4 files changed, 25 insertions(+), 7 deletions(-)

diff --git a/include/efi_loader.h b/include/efi_loader.h
index 7bd8002e303e..93f7ece814a0 100644
--- a/include/efi_loader.h
+++ b/include/efi_loader.h
@@ -381,6 +381,8 @@ void efi_save_gd(void);
 void efi_restore_gd(void);
 /* Call this to relocate the runtime section to an address space */
 void efi_runtime_relocate(ulong offset, struct efi_mem_desc *map);
+/* Call this when we start to live in a runtime only world */
+void efi_runtime_detach(ulong offset);
 /* Call this to set the current device name */
 void efi_set_bootdev(const char *dev, const char *devnr, const char *path);
 /* Add a new object to the object list. */
diff --git a/lib/efi_loader/efi_boottime.c b/lib/efi_loader/efi_boottime.c
index 7d1d6e92138e..e4abaf3601d9 100644
--- a/lib/efi_loader/efi_boottime.c
+++ b/lib/efi_loader/efi_boottime.c
@@ -1932,6 +1932,7 @@ static efi_status_t EFIAPI efi_exit_boot_services(efi_handle_t image_handle,
 	bootm_disable_interrupts();
 
 	/* Disable boot time services */
+	efi_runtime_detach((ulong)gd->relocaddr);
 	systab.con_in_handle = NULL;
 	systab.con_in = NULL;
 	systab.con_out_handle = NULL;
diff --git a/lib/efi_loader/efi_runtime.c b/lib/efi_loader/efi_runtime.c
index ff3684a4b692..fc5bdee80e00 100644
--- a/lib/efi_loader/efi_runtime.c
+++ b/lib/efi_loader/efi_runtime.c
@@ -433,7 +433,7 @@ struct efi_runtime_detach_list_struct {
 	void *patchto;
 };
 
-static const struct efi_runtime_detach_list_struct efi_runtime_detach_list[] = {
+static struct efi_runtime_detach_list_struct efi_runtime_detach_list[] = {
 	{
 		/* do_reset is gone */
 		.ptr = &efi_runtime_services.reset_system,
@@ -490,7 +490,15 @@ static bool efi_runtime_tobedetached(void *p)
 	return false;
 }
 
-static void efi_runtime_detach(ulong offset)
+/**
+ * efi_runtime_detach() - Remove any dependency on non-runtime sections
+ *
+ * This function patches all remaining code to be self-sufficient inside
+ * runtime sections. Any calls to non-runtime will be removed after this.
+ *
+ * @offset:		relocaddr for pre-set_v_a_space, offset to VA after
+ */
+__efi_runtime void efi_runtime_detach(ulong offset)
 {
 	int i;
 	ulong patchoff = offset - (ulong)gd->relocaddr;
@@ -506,6 +514,8 @@ static void efi_runtime_detach(ulong offset)
 
 	/* Update CRC32 */
 	efi_update_table_header_crc32(&efi_runtime_services.hdr);
+
+	invalidate_icache_all();
 }
 
 /* Relocate EFI runtime to uboot_reloc_base = offset */
@@ -593,19 +603,25 @@ void efi_runtime_relocate(ulong offset, struct efi_mem_desc *map)
  * @virtmap:		virtual address mapping information
  * Return:		status code
  */
-static efi_status_t EFIAPI efi_set_virtual_address_map(
+static __efi_runtime efi_status_t EFIAPI efi_set_virtual_address_map(
 			unsigned long memory_map_size,
 			unsigned long descriptor_size,
 			uint32_t descriptor_version,
 			struct efi_mem_desc *virtmap)
 {
+	static __efi_runtime_data bool is_patched;
 	int n = memory_map_size / descriptor_size;
 	int i;
 	int rt_code_sections = 0;
 
+	if (is_patched)
+		return EFI_INVALID_PARAMETER;
+
 	EFI_ENTRY("%lx %lx %x %p", memory_map_size, descriptor_size,
 		  descriptor_version, virtmap);
 
+	is_patched = true;
+
 	/*
 	 * TODO:
 	 * Further down we are cheating. While really we should implement
@@ -681,8 +697,7 @@ static efi_status_t EFIAPI efi_set_virtual_address_map(
 					   map->physical_start + gd->relocaddr;
 
 			efi_runtime_relocate(new_offset, map);
-			/* Once we're virtual, we can no longer handle
-			   complex callbacks */
+			/* We need to repatch callbacks for their new VA */
 			efi_runtime_detach(new_offset);
 
 			/*
diff --git a/test/py/tests/test_efi_selftest.py b/test/py/tests/test_efi_selftest.py
index 07e4db045284..f950911655a3 100644
--- a/test/py/tests/test_efi_selftest.py
+++ b/test/py/tests/test_efi_selftest.py
@@ -20,7 +20,7 @@ def test_efi_selftest(u_boot_console):
 	if m != 0:
 		raise Exception('Failures occurred during the EFI selftest')
 	u_boot_console.run_command(cmd='', wait_for_echo=False, wait_for_prompt=False);
-	m = u_boot_console.p.expect(['resetting', 'U-Boot'])
+	m = u_boot_console.p.expect(['U-Boot'])
 	if m != 0:
 		raise Exception('Reset failed during the EFI selftest')
 	u_boot_console.restart_uboot();
@@ -47,7 +47,7 @@ def test_efi_selftest_watchdog_reboot(u_boot_console):
 	assert '\'watchdog reboot\'' in output
 	u_boot_console.run_command(cmd='setenv efi_selftest watchdog reboot')
 	u_boot_console.run_command(cmd='bootefi selftest', wait_for_prompt=False)
-	m = u_boot_console.p.expect(['resetting', 'U-Boot'])
+	m = u_boot_console.p.expect(['U-Boot'])
 	if m != 0:
 		raise Exception('Reset failed in \'watchdog reboot\' test')
 	u_boot_console.restart_uboot();
-- 
2.21.0

^ permalink raw reply related	[flat|nested] 32+ messages in thread

* [U-Boot] [RFC 5/6] cmd: efidebug: add "boot exit" sub-command
  2019-06-05  4:21 [U-Boot] [RFC 0/6] efi_loader: support runtime variable access via cache AKASHI Takahiro
                   ` (3 preceding siblings ...)
  2019-06-05  4:21 ` [U-Boot] [RFC 4/6] efi_loader: Patch non-runtime code out at ExitBootServices already AKASHI Takahiro
@ 2019-06-05  4:21 ` AKASHI Takahiro
  2019-06-05  4:21 ` [U-Boot] [RFC 6/6] efi_loader: variable: support runtime variable access via cache AKASHI Takahiro
  2019-06-05 10:34 ` [U-Boot] [RFC 0/6] efi_loader: " Heinrich Schuchardt
  6 siblings, 0 replies; 32+ messages in thread
From: AKASHI Takahiro @ 2019-06-05  4:21 UTC (permalink / raw)
  To: u-boot

With "boot exit" sub-command, we will let the system to exit
UEFI Boottime Services. This is mainly for testing.

Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org>
---
 cmd/efidebug.c | 63 ++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 63 insertions(+)

diff --git a/cmd/efidebug.c b/cmd/efidebug.c
index e65722625455..d6abf08c93cc 100644
--- a/cmd/efidebug.c
+++ b/cmd/efidebug.c
@@ -977,6 +977,66 @@ out:
 	return ret;
 }
 
+/**
+ * do_efi_boot_exit() - exit boottime services
+ *
+ * @cmdtp:	Command table
+ * @flag:	Command flag
+ * @argc:	Number of arguments
+ * @argv:	Argument array
+ * Return:	CMD_RET_SUCCESS on success,
+ *		CMD_RET_USAGE or CMD_RET_RET_FAILURE on failure
+ *
+ * Implement efidebug "boot exit" sub-command.
+ *   - boot exit
+ */
+static int do_efi_boot_exit(cmd_tbl_t *cmdtp, int flag,
+			    int argc, char * const argv[])
+{
+	struct efi_mem_desc *memmap;
+	efi_uintn_t map_size, map_key, desc_size;
+	u32 desc_version;
+	efi_status_t ret;
+
+	if (argc != 1)
+		return CMD_RET_USAGE;
+
+	memmap = NULL;
+	map_size = 0;
+	ret = EFI_CALL(BS->get_memory_map(&map_size, memmap, &map_key,
+					  &desc_size, &desc_version));
+	if (ret == EFI_BUFFER_TOO_SMALL) {
+		map_size += sizeof(struct efi_mem_desc); /* for my own */
+		ret = EFI_CALL(BS->allocate_pool(EFI_LOADER_DATA,
+						 map_size, (void *)&memmap));
+		if (ret != EFI_SUCCESS) {
+			printf("Out of memory\n");
+			return CMD_RET_FAILURE;
+		}
+		ret = EFI_CALL(BS->get_memory_map(&map_size, memmap,
+						  &map_key,
+						  &desc_size, &desc_version));
+	}
+	if (ret != EFI_SUCCESS) {
+		printf("Getting UEFI memory map failed (%zu)\n",
+		       ret & ~EFI_ERROR_MASK);
+		goto err;
+	}
+
+	ret = EFI_CALL(BS->exit_boot_services(efi_root, map_key));
+	if (ret == EFI_SUCCESS)
+		return CMD_RET_SUCCESS;
+
+	printf("exiting Boottime Services failed (%zu)\n",
+	       ret & ~EFI_ERROR_MASK);
+
+err:
+	if (memmap)
+		EFI_CALL(BS->free_pool(memmap));
+
+	return CMD_RET_FAILURE;
+}
+
 static cmd_tbl_t cmd_efidebug_boot_sub[] = {
 	U_BOOT_CMD_MKENT(add, CONFIG_SYS_MAXARGS, 1, do_efi_boot_add, "", ""),
 	U_BOOT_CMD_MKENT(rm, CONFIG_SYS_MAXARGS, 1, do_efi_boot_rm, "", ""),
@@ -984,6 +1044,7 @@ static cmd_tbl_t cmd_efidebug_boot_sub[] = {
 	U_BOOT_CMD_MKENT(next, CONFIG_SYS_MAXARGS, 1, do_efi_boot_next, "", ""),
 	U_BOOT_CMD_MKENT(order, CONFIG_SYS_MAXARGS, 1, do_efi_boot_order,
 			 "", ""),
+	U_BOOT_CMD_MKENT(exit, CONFIG_SYS_MAXARGS, 1, do_efi_boot_exit, "", ""),
 };
 
 /**
@@ -1087,6 +1148,8 @@ static char efidebug_help_text[] =
 	"  - set UEFI BootNext variable\n"
 	"efidebug boot order [<bootid#1> [<bootid#2> [<bootid#3> [...]]]]\n"
 	"  - set/show UEFI boot order\n"
+	"efidebug boot exit\n"
+	"  - exit UEFI Boottime Services\n"
 	"\n"
 	"efidebug devices\n"
 	"  - show uefi devices\n"
-- 
2.21.0

^ permalink raw reply related	[flat|nested] 32+ messages in thread

* [U-Boot] [RFC 6/6] efi_loader: variable: support runtime variable access via cache
  2019-06-05  4:21 [U-Boot] [RFC 0/6] efi_loader: support runtime variable access via cache AKASHI Takahiro
                   ` (4 preceding siblings ...)
  2019-06-05  4:21 ` [U-Boot] [RFC 5/6] cmd: efidebug: add "boot exit" sub-command AKASHI Takahiro
@ 2019-06-05  4:21 ` AKASHI Takahiro
  2019-06-15 19:01   ` Heinrich Schuchardt
  2019-06-05 10:34 ` [U-Boot] [RFC 0/6] efi_loader: " Heinrich Schuchardt
  6 siblings, 1 reply; 32+ messages in thread
From: AKASHI Takahiro @ 2019-06-05  4:21 UTC (permalink / raw)
  To: u-boot

With this patch, cache buffer for UEFI variables will be created
so that we will still be able to access, at least retrieve,
UEFI variables when we exit from boottime services,

This feature is a "should" behavior described in EBBR v1.0
section 2.5.3.

Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org>
---
 include/efi_loader.h          |  17 ++
 lib/efi_loader/Kconfig        |   9 +
 lib/efi_loader/efi_boottime.c |  10 +-
 lib/efi_loader/efi_runtime.c  |  13 +
 lib/efi_loader/efi_variable.c | 467 ++++++++++++++++++++++++++++++++++
 5 files changed, 515 insertions(+), 1 deletion(-)

diff --git a/include/efi_loader.h b/include/efi_loader.h
index 93f7ece814a0..acab657b9d70 100644
--- a/include/efi_loader.h
+++ b/include/efi_loader.h
@@ -620,6 +620,23 @@ efi_status_t EFIAPI efi_set_variable(u16 *variable_name,
 				     const efi_guid_t *vendor, u32 attributes,
 				     efi_uintn_t data_size, const void *data);
 
+#ifdef CONFIG_EFI_RUNTIME_GET_VARIABLE_CACHING
+efi_status_t efi_freeze_variable_table(void);
+
+/* runtime version of APIs */
+efi_status_t
+__efi_runtime EFIAPI efi_get_variable_runtime(u16 *variable_name,
+					      const efi_guid_t *vendor,
+					      u32 *attributes,
+					      efi_uintn_t *data_size,
+					      void *data);
+efi_status_t
+__efi_runtime EFIAPI efi_get_next_variable_name_runtime(
+						efi_uintn_t *variable_name_size,
+						u16 *variable_name,
+						const efi_guid_t *vendor);
+#endif /* CONFIG_EFI_RUNTIME_GET_VARIABLE_CACHING */
+
 /*
  * 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:
diff --git a/lib/efi_loader/Kconfig b/lib/efi_loader/Kconfig
index e2ef43157568..3f284795648f 100644
--- a/lib/efi_loader/Kconfig
+++ b/lib/efi_loader/Kconfig
@@ -59,6 +59,15 @@ config EFI_RUNTIME_CONVERT_POINTER
 	  to be called by UEFI drivers in relocating themselves to virtual
 	  address space.
 
+config EFI_RUNTIME_GET_VARIABLE_CACHING
+	bool "runtime_service: GetVariable: Enable runtime access via cache (read-only)"
+	default y
+	help
+	  Select this option if you want to access UEFI variables at
+	  runtime even though you cannot update values on the fly.
+	  With or without this option, you can access UEFI variables
+	  at boottime.
+
 config EFI_DEVICE_PATH_TO_TEXT
 	bool "Device path to text protocol"
 	default y
diff --git a/lib/efi_loader/efi_boottime.c b/lib/efi_loader/efi_boottime.c
index e4abaf3601d9..14e343abbd43 100644
--- a/lib/efi_loader/efi_boottime.c
+++ b/lib/efi_loader/efi_boottime.c
@@ -1892,6 +1892,9 @@ static efi_status_t EFIAPI efi_exit_boot_services(efi_handle_t image_handle,
 						  efi_uintn_t map_key)
 {
 	struct efi_event *evt;
+#ifdef CONFIG_EFI_RUNTIME_GET_VARIABLE_CACHING
+	efi_status_t ret;
+#endif
 
 	EFI_ENTRY("%p, %zx", image_handle, map_key);
 
@@ -1921,7 +1924,12 @@ static efi_status_t EFIAPI efi_exit_boot_services(efi_handle_t image_handle,
 		}
 	}
 
-	/* TODO: Should persist EFI variables here */
+#ifdef CONFIG_EFI_RUNTIME_GET_VARIABLE_CACHING
+	/* No more variable update */
+	ret = efi_freeze_variable_table();
+	if (ret != EFI_SUCCESS)
+		return EFI_EXIT(ret);
+#endif
 
 	board_quiesce_devices();
 
diff --git a/lib/efi_loader/efi_runtime.c b/lib/efi_loader/efi_runtime.c
index fc5bdee80e00..b60f70f04613 100644
--- a/lib/efi_loader/efi_runtime.c
+++ b/lib/efi_loader/efi_runtime.c
@@ -111,6 +111,11 @@ efi_status_t efi_init_runtime_supported(void)
 	efi_runtime_services_supported |=
 				EFI_RT_SUPPORTED_CONVERT_POINTER;
 #endif
+#ifdef CONFIG_EFI_RUNTIME_GET_VARIABLE_CACHING
+	efi_runtime_services_supported |=
+				(EFI_RT_SUPPORTED_GET_VARIABLE |
+				 EFI_RT_SUPPORTED_GET_NEXT_VARIABLE_NAME);
+#endif
 
 	return EFI_CALL(efi_set_variable(L"RuntimeServicesSupported",
 					 &efi_global_variable_guid,
@@ -469,10 +474,18 @@ static struct efi_runtime_detach_list_struct efi_runtime_detach_list[] = {
 		.patchto = NULL,
 	}, {
 		.ptr = &efi_runtime_services.get_variable,
+#ifdef CONFIG_EFI_RUNTIME_GET_VARIABLE_CACHING
+		.patchto = &efi_get_variable_runtime,
+#else
 		.patchto = &efi_device_error,
+#endif
 	}, {
 		.ptr = &efi_runtime_services.get_next_variable_name,
+#ifdef CONFIG_EFI_RUNTIME_GET_VARIABLE_CACHING
+		.patchto = &efi_get_next_variable_name,
+#else
 		.patchto = &efi_device_error,
+#endif
 	}, {
 		.ptr = &efi_runtime_services.set_variable,
 		.patchto = &efi_device_error,
diff --git a/lib/efi_loader/efi_variable.c b/lib/efi_loader/efi_variable.c
index d9887be938c2..ee21892dd291 100644
--- a/lib/efi_loader/efi_variable.c
+++ b/lib/efi_loader/efi_variable.c
@@ -706,3 +706,470 @@ efi_status_t EFIAPI efi_set_variable(u16 *variable_name,
 
 	return EFI_EXIT(ret);
 }
+
+#ifdef CONFIG_EFI_RUNTIME_GET_VARIABLE_CACHING
+/*
+ * runtime version of APIs
+ * We only support read-only variable access.
+ * The table is in U-Boot's hash table format, but has its own
+ * _ENTRY structure for specific use.
+ *
+ * Except for efi_freeze_variable_table(), which is to be called in
+ * exit_boot_services(), all the functions and data below must be
+ * placed in either RUNTIME_SERVICES_CODE or RUNTIME_SERVICES_DATA.
+ */
+typedef struct _ENTRY {
+	unsigned int used;	/* hash value; 0 for not used */
+	size_t name;		/* name offset from itself */
+	efi_guid_t vendor;
+	u32 attributes;
+	size_t data;		/* data offset from itself */
+	size_t data_size;
+} _ENTRY;
+
+static inline u16 *entry_name(_ENTRY *e) { return (void *)e + e->name; }
+static inline u16 *entry_data(_ENTRY *e) { return (void *)e + e->data; }
+
+static struct hsearch_data *efi_variable_table __efi_runtime_data;
+
+static size_t __efi_runtime u16_strlen_runtime(const u16 *s1)
+{
+	size_t n = 0;
+
+	while (*s1) {
+		n++;
+		s1++;
+	}
+
+	return n;
+}
+
+static int __efi_runtime memcmp_runtime(const void *m1, const void *m2,
+					size_t n)
+{
+	while (n && *(u8 *)m1 == *(u8 *)m2) {
+		n--;
+		m1++;
+		m2++;
+	}
+
+	if (n)
+		return *(u8 *)m1 - *(u8 *)m2;
+
+	return 0;
+}
+
+static void __efi_runtime memcpy_runtime(void *m1, const void *m2, size_t n)
+{
+	for (; n; n--, m1++, m2++)
+		*(u8 *)m1 = *(u8 *)m2;
+}
+
+static int __efi_runtime efi_cmpkey(_ENTRY *e, const u16 *name,
+				    const efi_guid_t *vendor)
+{
+	size_t name_len;
+
+	name_len = u16_strlen_runtime(entry_name(e));
+
+	/* return zero if matched */
+	return name_len != u16_strlen_runtime(name) ||
+	       memcmp_runtime(entry_name(e), name, name_len * 2) ||
+	       memcmp_runtime(e->vendor.b, vendor->b, sizeof(vendor));
+}
+
+/* simplified and slightly different version of hsearch_r() */
+static int __efi_runtime hsearch_runtime(const u16 *name,
+					 const efi_guid_t *vendor,
+					 ACTION action,
+					 _ENTRY **retval,
+					 struct hsearch_data *htab)
+{
+	unsigned int hval;
+	unsigned int count;
+	unsigned int len;
+	unsigned int idx, new;
+
+	/* Compute an value for the given string. */
+	len = u16_strlen_runtime(name);
+	hval = len;
+	count = len;
+	while (count-- > 0) {
+		hval <<= 4;
+		hval += name[count];
+	}
+
+	/*
+	 * First hash function:
+	 * simply take the modulo but prevent zero.
+	 */
+	hval %= htab->size;
+	if (hval == 0)
+		++hval;
+
+	/* The first index tried. */
+	new = -1; /* not found */
+	idx = hval;
+
+	if (htab->table[idx].used) {
+		/*
+		 * Further action might be required according to the
+		 * action value.
+		 */
+		unsigned int hval2;
+
+		if (htab->table[idx].used == hval &&
+		    !efi_cmpkey(&htab->table[idx], name, vendor)) {
+			if (action == FIND) {
+				*retval = &htab->table[idx];
+				return idx;
+			}
+			/* we don't need to support overwrite */
+			return -1;
+		}
+
+		/*
+		 * Second hash function:
+		 * as suggested in [Knuth]
+		 */
+		hval2 = 1 + hval % (htab->size - 2);
+
+		do {
+			/*
+			 * Because SIZE is prime this guarantees to
+			 * step through all available indices.
+			 */
+			if (idx <= hval2)
+				idx = htab->size + idx - hval2;
+			else
+				idx -= hval2;
+
+			/*
+			 * If we visited all entries leave the loop
+			 * unsuccessfully.
+			 */
+			if (idx == hval)
+				break;
+
+			/* If entry is found use it. */
+			if (htab->table[idx].used == hval &&
+			    !efi_cmpkey(&htab->table[idx], name, vendor)) {
+				if (action == FIND) {
+					*retval = &htab->table[idx];
+					return idx;
+				}
+				/* we don't need to support overwrite */
+				return -1;
+			}
+		} while (htab->table[idx].used);
+
+		if (!htab->table[idx].used)
+			new = idx;
+	} else {
+		new = idx;
+	}
+
+	/*
+	 * An empty bucket has been found.
+	 * The following code should never be executed after
+	 * exit_boot_services()
+	 */
+	if (action == ENTER) {
+		/*
+		 * If table is full and another entry should be
+		 * entered return with error.
+		 */
+		if (htab->filled == htab->size) {
+			*retval = NULL;
+			return 0;
+		}
+
+		/* Create new entry */
+		htab->table[new].used = hval;
+		++htab->filled;
+
+		/* return new entry */
+		*retval = &htab->table[new];
+		return 1;
+	}
+
+	*retval = NULL;
+	return 0;
+}
+
+/* from lib/hashtable.c */
+static inline int isprime(unsigned int number)
+{
+	/* no even number will be passed */
+	unsigned int div = 3;
+
+	while (div * div < number && number % div != 0)
+		div += 2;
+
+	return number % div != 0;
+}
+
+efi_status_t efi_freeze_variable_table(void)
+{
+	int var_num = 0;
+	size_t var_data_size = 0;
+	u16 *name;
+	efi_uintn_t name_buf_len, name_len;
+	efi_guid_t vendor;
+	u32 attributes;
+	u8 *mem_pool, *var_buf = NULL;
+	size_t table_size, var_size, var_buf_size;
+	_ENTRY *new = NULL;
+	efi_status_t ret;
+
+	/* phase-1 loop */
+	name_buf_len = 128;
+	name = malloc(name_buf_len);
+	if (!name)
+		return EFI_OUT_OF_RESOURCES;
+	name[0] = 0;
+	for (;;) {
+		name_len = name_buf_len;
+		ret = EFI_CALL(efi_get_next_variable_name(&name_len, name,
+							  &vendor));
+		if (ret == EFI_NOT_FOUND) {
+			break;
+		} else if (ret == EFI_BUFFER_TOO_SMALL) {
+			u16 *buf;
+
+			name_buf_len = name_len;
+			buf = realloc(name, name_buf_len);
+			if (!buf) {
+				free(name);
+				return EFI_OUT_OF_RESOURCES;
+			}
+			name = buf;
+			name_len = name_buf_len;
+			ret = EFI_CALL(efi_get_next_variable_name(&name_len,
+								  name,
+								  &vendor));
+		}
+
+		if (ret != EFI_SUCCESS)
+			return ret;
+
+		var_size = 0;
+		ret = EFI_CALL(efi_get_variable(name, &vendor, &attributes,
+						&var_size, NULL));
+		if (ret != EFI_BUFFER_TOO_SMALL)
+			return ret;
+
+		if (!(attributes & EFI_VARIABLE_RUNTIME_ACCESS))
+			continue;
+
+		var_num++;
+		var_data_size += (u16_strlen_runtime(name) + 1) * sizeof(u16);
+		var_data_size += var_size;
+		/* mem_pool must 2-byte aligned for u16 variable name */
+		if (var_data_size & 0x1)
+			var_data_size++;
+	}
+
+	/*
+	 * total of entries in hash table must be a prime number.
+	 * The logic below comes from lib/hashtable.c
+	 */
+	var_num |= 1;               /* make odd */
+	while (!isprime(var_num))
+		var_num += 2;
+
+	/* We need table[var_num] for hsearch_runtime algo */
+	table_size = sizeof(*efi_variable_table)
+			+ sizeof(_ENTRY) * (var_num + 1) + var_data_size;
+	ret = efi_allocate_pool(EFI_RUNTIME_SERVICES_DATA,
+				table_size, (void **)&efi_variable_table);
+	if (ret != EFI_SUCCESS)
+		return ret;
+
+	efi_variable_table->size = var_num;
+	efi_variable_table->table = (void *)efi_variable_table
+					+ sizeof(*efi_variable_table);
+	mem_pool = (u8 *)efi_variable_table->table
+			+ sizeof(_ENTRY) * (var_num + 1);
+
+	var_buf_size = 128;
+	var_buf = malloc(var_buf_size);
+	if (!var_buf) {
+		ret = EFI_OUT_OF_RESOURCES;
+		goto err;
+	}
+
+	/* phase-2 loop */
+	name[0] = 0;
+	name_len = name_buf_len;
+	for (;;) {
+		name_len = name_buf_len;
+		ret = EFI_CALL(efi_get_next_variable_name(&name_len, name,
+							  &vendor));
+		if (ret == EFI_NOT_FOUND)
+			break;
+		else if (ret != EFI_SUCCESS)
+			goto err;
+
+		var_size = var_buf_size;
+		ret = EFI_CALL(efi_get_variable(name, &vendor, &attributes,
+						&var_size, var_buf));
+		if (ret == EFI_BUFFER_TOO_SMALL) {
+			free(var_buf);
+			var_buf_size = var_size;
+			var_buf = malloc(var_buf_size);
+			if (!var_buf) {
+				ret = EFI_OUT_OF_RESOURCES;
+				goto err;
+			}
+			ret = EFI_CALL(efi_get_variable(name, &vendor,
+							&attributes,
+							&var_size, var_buf));
+		}
+		if (ret != EFI_SUCCESS)
+			goto err;
+
+		if (!(attributes & EFI_VARIABLE_RUNTIME_ACCESS))
+			continue;
+
+		if (hsearch_runtime(name, &vendor, ENTER, &new,
+				    efi_variable_table) <= 0) {
+			/* This should not happen */
+			ret = EFI_INVALID_PARAMETER;
+			goto err;
+		}
+
+		/* allocate space from RUNTIME DATA */
+		name_len = (u16_strlen_runtime(name) + 1) * sizeof(u16);
+		memcpy_runtime(mem_pool, name, name_len);
+		new->name = mem_pool - (u8 *)new; /* offset */
+		mem_pool += name_len;
+
+		memcpy_runtime(&new->vendor.b, &vendor.b, sizeof(vendor));
+
+		new->attributes = attributes;
+
+		memcpy_runtime(mem_pool, var_buf, var_size);
+		new->data = mem_pool - (u8 *)new; /* offset */
+		new->data_size = var_size;
+		mem_pool += var_size;
+
+		/* mem_pool must 2-byte aligned for u16 variable name */
+		if ((uintptr_t)mem_pool & 0x1)
+			mem_pool++;
+	}
+#ifdef DEBUG
+	name[0] = 0;
+	name_len = name_buf_len;
+	for (;;) {
+		name_len = name_buf_len;
+		ret = efi_get_next_variable_name_runtime(&name_len, name,
+							 &vendor);
+		if (ret == EFI_NOT_FOUND)
+			break;
+		else if (ret != EFI_SUCCESS)
+			goto err;
+
+		var_size = var_buf_size;
+		ret = efi_get_variable_runtime(name, &vendor, &attributes,
+					       &var_size, var_buf);
+		if (ret != EFI_SUCCESS)
+			goto err;
+
+		printf("%ls_%pUl:\n", name, &vendor);
+		printf("    attributes: 0x%x\n", attributes);
+		printf("    value (size: 0x%lx)\n", var_size);
+	}
+#endif
+	ret = EFI_SUCCESS;
+
+err:
+	free(name);
+	free(var_buf);
+	if (ret != EFI_SUCCESS && efi_variable_table) {
+		efi_free_pool(efi_variable_table);
+		efi_variable_table = NULL;
+	}
+
+	return ret;
+}
+
+efi_status_t
+__efi_runtime EFIAPI efi_get_variable_runtime(u16 *variable_name,
+					      const efi_guid_t *vendor,
+					      u32 *attributes,
+					      efi_uintn_t *data_size,
+					      void *data)
+{
+	_ENTRY *new;
+
+	if (!variable_name || !vendor || !data_size)
+		return EFI_EXIT(EFI_INVALID_PARAMETER);
+
+	if (hsearch_runtime(variable_name, vendor, FIND, &new,
+			    efi_variable_table) <= 0)
+		return EFI_NOT_FOUND;
+
+	if (attributes)
+		*attributes = new->attributes;
+	if (*data_size < new->data_size) {
+		*data_size = new->data_size;
+		return EFI_BUFFER_TOO_SMALL;
+	}
+
+	*data_size = new->data_size;
+	memcpy_runtime(data, entry_data(new), new->data_size);
+
+	return EFI_SUCCESS;
+}
+
+static int prev_idx __efi_runtime_data;
+
+efi_status_t
+__efi_runtime EFIAPI efi_get_next_variable_name_runtime(
+						efi_uintn_t *variable_name_size,
+						u16 *variable_name,
+						const efi_guid_t *vendor)
+{
+	_ENTRY *e;
+	u16 *name;
+	efi_uintn_t name_size;
+
+	if (!variable_name_size || !variable_name || !vendor)
+		return EFI_INVALID_PARAMETER;
+
+	if (variable_name[0]) {
+		/* sanity check for previous variable */
+		if (prev_idx < 0)
+			return EFI_INVALID_PARAMETER;
+
+		e = &efi_variable_table->table[prev_idx];
+		if (!e->used || efi_cmpkey(e, variable_name, vendor))
+			return EFI_INVALID_PARAMETER;
+	} else {
+		prev_idx = -1;
+	}
+
+	/* next variable */
+	while (++prev_idx <= efi_variable_table->size) {
+		e = &efi_variable_table->table[prev_idx];
+		if (e->used)
+			break;
+	}
+	if (prev_idx > efi_variable_table->size)
+		return EFI_NOT_FOUND;
+
+	name = entry_name(e);
+	name_size = (u16_strlen_runtime(name) + 1)
+			* sizeof(u16);
+	if (*variable_name_size < name_size) {
+		*variable_name_size = name_size;
+		return EFI_BUFFER_TOO_SMALL;
+	}
+
+	memcpy_runtime(variable_name, name, name_size);
+	memcpy_runtime((void *)&vendor->b, &e->vendor.b, sizeof(vendor));
+
+	return EFI_SUCCESS;
+}
+#endif /* CONFIG_EFI_RUNTIME_GET_VARIABLE_CACHING */
-- 
2.21.0

^ permalink raw reply related	[flat|nested] 32+ messages in thread

* [U-Boot] [RFC 0/6] efi_loader: support runtime variable access via cache
  2019-06-05  4:21 [U-Boot] [RFC 0/6] efi_loader: support runtime variable access via cache AKASHI Takahiro
                   ` (5 preceding siblings ...)
  2019-06-05  4:21 ` [U-Boot] [RFC 6/6] efi_loader: variable: support runtime variable access via cache AKASHI Takahiro
@ 2019-06-05 10:34 ` Heinrich Schuchardt
  6 siblings, 0 replies; 32+ messages in thread
From: Heinrich Schuchardt @ 2019-06-05 10:34 UTC (permalink / raw)
  To: u-boot

On 6/5/19 6:21 AM, AKASHI Takahiro wrote:
> EBBR v1.0 section 2.5.3 says
>   Even when SetVariable() is not supported during runtime services,
>   firmware should cache variable names and values in EfiRuntimeServicesData
>   memory so that GetVariable() and GetNextVeriableName() can behave as
>   specified.
>
> This is an experimental patch set and the aim is to enable this feature.


Hello Takahiro,

thanks a lot for all the effort you put in this.

Reusing parts of env is a valid possibility but I am not sure if this is
the right approach for something that has to work at runtime.

I will need some time to layout my ideas of the design. Unfortunately I
am on a sea kayaking trip this weekend. So I may need until June 16th
for a thorough suggestion.

I have already picked the trivial patches from this patch series and
will put them into the next pull request.

http://git.denx.de/?p=u-boot-efi.git;a=shortlog;h=refs/tags/efi-2019-07-rc4-2

Best regards

Heinich

>
> Cache buffer is in the same format as U-Boot environment hash table,
> but we cannot use functions in hashtable.c partly because most of U-Boot
> code are not available at UEFI runtime and partly because entries in
> a table are allocated by malloc(), which are again not available
> at UEFI runtime. It is quite painful to modify exiting U-Boot code
> so as to make it executable at UEFI runtime.
>
> So I implemented a limited version of hsearch_r(). This may be
> a discussion. Given that there are not so many UEFI variables,
> we may want to use a simpler table format for caching.
>
> Known issues:
> * Currently I test this feature with a test which is temporarily embedded
>   in ExitBootServices.
> * After SetVirtualAddressMap, it won't work (TODO).
>   ConvertPointer was implemented here just for this future work.
> * So how can we test the feature?
>   We can't use "printenv" command as its relocation won't take place.
> * I see some Travis CI errors.
>   This is probably due to no storage configured for UEFI variables.
>
> Patch#1 to #4 are preparatory patches.
> Patch#5 is mainly for testing.
> Patch#6 is core part of this patch set.
>
> AKASHI Takahiro (5):
>   efi_loader: runtime: make SetVirtualAddressMap configurable
>   efi: add RuntimeServicesSupported variable
>   efi_loader: support convert_pointer at runtime
>   cmd: efidebug: add "boot exit" sub-command
>   efi_loader: variable: support runtime variable access via cache
>
> Alexander Graf (1):
>   efi_loader: Patch non-runtime code out at ExitBootServices already
>
>  cmd/efidebug.c                     |  63 ++++
>  include/efi_api.h                  |  15 +
>  include/efi_loader.h               |  22 ++
>  lib/efi_loader/Kconfig             |  24 ++
>  lib/efi_loader/efi_boottime.c      |  11 +-
>  lib/efi_loader/efi_runtime.c       | 155 ++++++++--
>  lib/efi_loader/efi_setup.c         |   5 +
>  lib/efi_loader/efi_variable.c      | 467 +++++++++++++++++++++++++++++
>  test/py/tests/test_efi_selftest.py |   4 +-
>  9 files changed, 735 insertions(+), 31 deletions(-)
>

^ permalink raw reply	[flat|nested] 32+ messages in thread

* [U-Boot] [RFC 2/6] efi: add RuntimeServicesSupported variable
  2019-06-05  4:21 ` [U-Boot] [RFC 2/6] efi: add RuntimeServicesSupported variable AKASHI Takahiro
@ 2019-06-13  5:56   ` Heinrich Schuchardt
  2019-06-13  7:06     ` AKASHI Takahiro
  0 siblings, 1 reply; 32+ messages in thread
From: Heinrich Schuchardt @ 2019-06-13  5:56 UTC (permalink / raw)
  To: u-boot



On 6/5/19 6:21 AM, AKASHI Takahiro wrote:
> This variable is defined in UEFI specification 2.8, section 8.1.
> Its value should be updated whenever we add any usable runtime services
> function.

It is also required by the EBBR specification.

>
> Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org>
> ---
>  include/efi_api.h            | 15 +++++++++++++++
>  include/efi_loader.h         |  3 +++
>  lib/efi_loader/efi_runtime.c | 28 ++++++++++++++++++++++++++++
>  lib/efi_loader/efi_setup.c   |  5 +++++
>  4 files changed, 51 insertions(+)
>
> diff --git a/include/efi_api.h b/include/efi_api.h
> index 65584dd2d82a..d7d95edd4dfc 100644
> --- a/include/efi_api.h
> +++ b/include/efi_api.h
> @@ -213,6 +213,21 @@ struct efi_capsule_header {
>  	u32 capsule_image_size;
>  };
>
> +#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
> +
>  struct efi_runtime_services {
>  	struct efi_table_hdr hdr;
>  	efi_status_t (EFIAPI *get_time)(struct efi_time *time,
> diff --git a/include/efi_loader.h b/include/efi_loader.h
> index 23ce73226762..7bd8002e303e 100644
> --- a/include/efi_loader.h
> +++ b/include/efi_loader.h
> @@ -573,6 +573,9 @@ static inline int guidcmp(const efi_guid_t *g1, const efi_guid_t *g2)
>  #define __efi_runtime_data __attribute__ ((section (".data.efi_runtime")))
>  #define __efi_runtime __attribute__ ((section (".text.efi_runtime")))
>
> +/* Indicate supported runtime services */
> +efi_status_t efi_init_runtime_supported(void);
> +
>  /* Update CRC32 in table header */
>  void __efi_runtime efi_update_table_header_crc32(struct efi_table_hdr *table);
>
> diff --git a/lib/efi_loader/efi_runtime.c b/lib/efi_loader/efi_runtime.c
> index 60442cb21d37..cf202bb9ec07 100644
> --- a/lib/efi_loader/efi_runtime.c
> +++ b/lib/efi_loader/efi_runtime.c
> @@ -89,6 +89,34 @@ struct elf_rela {
>   * handle a good number of runtime callbacks
>   */
>
> +efi_status_t efi_init_runtime_supported(void)
> +{
> +	u16 efi_runtime_services_supported;
> +
> +	/*
> +	 * This value must be synced with efi_runtime_detach_list
> +	 * as well as efi_runtime_services.
> +	 */
> +	efi_runtime_services_supported = EFI_RT_SUPPORTED_RESET_SYSTEM;

This support is system dependent, e.g. on RK3288 systems it does not exist.

> +#ifdef CONFIG_EFI_GET_TIME
> +	efi_runtime_services_supported |= EFI_RT_SUPPORTED_GET_TIME;

We do not support this at runtime.

> +#endif
> +#ifdef CONFIG_EFI_SET_TIME
> +	efi_runtime_services_supported |= EFI_RT_SUPPORTED_SET_TIME;

We do not support this at runtime.

> +#endif
> +#ifdef CONFIG_EFI_RUNTIME_SET_VIRTUAL_ADDRESS_MAP
> +	efi_runtime_services_supported |=
> +				EFI_RT_SUPPORTED_SET_VIRTUAL_ADDRESS_MAP;

Our support is incomplete as we have not implemented ConvertPointer().

For unsupported services we will have to change the return value to
EFI_UNSUPPORTED. But that will be a separate patch.

Best regards

Heinrich

> +#endif
> +
> +	return EFI_CALL(efi_set_variable(L"RuntimeServicesSupported",
> +					 &efi_global_variable_guid,
> +					 EFI_VARIABLE_BOOTSERVICE_ACCESS |
> +					 EFI_VARIABLE_RUNTIME_ACCESS,
> +					 sizeof(efi_runtime_services_supported),
> +					 &efi_runtime_services_supported));
> +}
> +
>  /**
>   * efi_update_table_header_crc32() - Update crc32 in table header
>   *
> diff --git a/lib/efi_loader/efi_setup.c b/lib/efi_loader/efi_setup.c
> index 45d6aca051f3..75fa344060d5 100644
> --- a/lib/efi_loader/efi_setup.c
> +++ b/lib/efi_loader/efi_setup.c
> @@ -123,6 +123,11 @@ efi_status_t efi_init_obj_list(void)
>  	if (ret != EFI_SUCCESS)
>  		goto out;
>
> +	/* Indicate supported runtime services */
> +	ret = efi_init_runtime_supported();
> +	if (ret != EFI_SUCCESS)
> +		goto out;
> +
>  	/* Initialize system table */
>  	ret = efi_initialize_system_table();
>  	if (ret != EFI_SUCCESS)
>

^ permalink raw reply	[flat|nested] 32+ messages in thread

* [U-Boot] [RFC 2/6] efi: add RuntimeServicesSupported variable
  2019-06-13  5:56   ` Heinrich Schuchardt
@ 2019-06-13  7:06     ` AKASHI Takahiro
  2019-06-13  9:17       ` Heinrich Schuchardt
  0 siblings, 1 reply; 32+ messages in thread
From: AKASHI Takahiro @ 2019-06-13  7:06 UTC (permalink / raw)
  To: u-boot

On Thu, Jun 13, 2019 at 07:56:19AM +0200, Heinrich Schuchardt wrote:
> 
> 
> On 6/5/19 6:21 AM, AKASHI Takahiro wrote:
> > This variable is defined in UEFI specification 2.8, section 8.1.
> > Its value should be updated whenever we add any usable runtime services
> > function.
> 
> It is also required by the EBBR specification.
> 
> >
> > Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org>
> > ---
> >  include/efi_api.h            | 15 +++++++++++++++
> >  include/efi_loader.h         |  3 +++
> >  lib/efi_loader/efi_runtime.c | 28 ++++++++++++++++++++++++++++
> >  lib/efi_loader/efi_setup.c   |  5 +++++
> >  4 files changed, 51 insertions(+)
> >
> > diff --git a/include/efi_api.h b/include/efi_api.h
> > index 65584dd2d82a..d7d95edd4dfc 100644
> > --- a/include/efi_api.h
> > +++ b/include/efi_api.h
> > @@ -213,6 +213,21 @@ struct efi_capsule_header {
> >  	u32 capsule_image_size;
> >  };
> >
> > +#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
> > +
> >  struct efi_runtime_services {
> >  	struct efi_table_hdr hdr;
> >  	efi_status_t (EFIAPI *get_time)(struct efi_time *time,
> > diff --git a/include/efi_loader.h b/include/efi_loader.h
> > index 23ce73226762..7bd8002e303e 100644
> > --- a/include/efi_loader.h
> > +++ b/include/efi_loader.h
> > @@ -573,6 +573,9 @@ static inline int guidcmp(const efi_guid_t *g1, const efi_guid_t *g2)
> >  #define __efi_runtime_data __attribute__ ((section (".data.efi_runtime")))
> >  #define __efi_runtime __attribute__ ((section (".text.efi_runtime")))
> >
> > +/* Indicate supported runtime services */
> > +efi_status_t efi_init_runtime_supported(void);
> > +
> >  /* Update CRC32 in table header */
> >  void __efi_runtime efi_update_table_header_crc32(struct efi_table_hdr *table);
> >
> > diff --git a/lib/efi_loader/efi_runtime.c b/lib/efi_loader/efi_runtime.c
> > index 60442cb21d37..cf202bb9ec07 100644
> > --- a/lib/efi_loader/efi_runtime.c
> > +++ b/lib/efi_loader/efi_runtime.c
> > @@ -89,6 +89,34 @@ struct elf_rela {
> >   * handle a good number of runtime callbacks
> >   */
> >
> > +efi_status_t efi_init_runtime_supported(void)
> > +{
> > +	u16 efi_runtime_services_supported;
> > +
> > +	/*
> > +	 * This value must be synced with efi_runtime_detach_list
> > +	 * as well as efi_runtime_services.
> > +	 */
> > +	efi_runtime_services_supported = EFI_RT_SUPPORTED_RESET_SYSTEM;
> 
> This support is system dependent, e.g. on RK3288 systems it does not exist.

efi_reset_system() is defined as a weak function and so
there is no easy way to determine whether this API is supported or not.

> > +#ifdef CONFIG_EFI_GET_TIME
> > +	efi_runtime_services_supported |= EFI_RT_SUPPORTED_GET_TIME;
> 
> We do not support this at runtime.

Okay, I will drop it.

> > +#endif
> > +#ifdef CONFIG_EFI_SET_TIME
> > +	efi_runtime_services_supported |= EFI_RT_SUPPORTED_SET_TIME;
> 
> We do not support this at runtime.

ditto

> > +#endif
> > +#ifdef CONFIG_EFI_RUNTIME_SET_VIRTUAL_ADDRESS_MAP
> > +	efi_runtime_services_supported |=
> > +				EFI_RT_SUPPORTED_SET_VIRTUAL_ADDRESS_MAP;
> 
> Our support is incomplete as we have not implemented ConvertPointer().

Incomplete?
My patch#3 adds ConvertPointer().

-Takahiro Akashi

> For unsupported services we will have to change the return value to
> EFI_UNSUPPORTED. But that will be a separate patch.
> 
> Best regards
> 
> Heinrich
> 
> > +#endif
> > +
> > +	return EFI_CALL(efi_set_variable(L"RuntimeServicesSupported",
> > +					 &efi_global_variable_guid,
> > +					 EFI_VARIABLE_BOOTSERVICE_ACCESS |
> > +					 EFI_VARIABLE_RUNTIME_ACCESS,
> > +					 sizeof(efi_runtime_services_supported),
> > +					 &efi_runtime_services_supported));
> > +}
> > +
> >  /**
> >   * efi_update_table_header_crc32() - Update crc32 in table header
> >   *
> > diff --git a/lib/efi_loader/efi_setup.c b/lib/efi_loader/efi_setup.c
> > index 45d6aca051f3..75fa344060d5 100644
> > --- a/lib/efi_loader/efi_setup.c
> > +++ b/lib/efi_loader/efi_setup.c
> > @@ -123,6 +123,11 @@ efi_status_t efi_init_obj_list(void)
> >  	if (ret != EFI_SUCCESS)
> >  		goto out;
> >
> > +	/* Indicate supported runtime services */
> > +	ret = efi_init_runtime_supported();
> > +	if (ret != EFI_SUCCESS)
> > +		goto out;
> > +
> >  	/* Initialize system table */
> >  	ret = efi_initialize_system_table();
> >  	if (ret != EFI_SUCCESS)
> >

^ permalink raw reply	[flat|nested] 32+ messages in thread

* [U-Boot] [RFC 2/6] efi: add RuntimeServicesSupported variable
  2019-06-13  7:06     ` AKASHI Takahiro
@ 2019-06-13  9:17       ` Heinrich Schuchardt
  2019-06-13  9:21         ` Heinrich Schuchardt
  0 siblings, 1 reply; 32+ messages in thread
From: Heinrich Schuchardt @ 2019-06-13  9:17 UTC (permalink / raw)
  To: u-boot



On 6/13/19 9:06 AM, AKASHI Takahiro wrote:
> On Thu, Jun 13, 2019 at 07:56:19AM +0200, Heinrich Schuchardt wrote:
>>
>>
>> On 6/5/19 6:21 AM, AKASHI Takahiro wrote:
>>> This variable is defined in UEFI specification 2.8, section 8.1.
>>> Its value should be updated whenever we add any usable runtime services
>>> function.
>>
>> It is also required by the EBBR specification.
>>
>>>
>>> Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org>
>>> ---
>>>  include/efi_api.h            | 15 +++++++++++++++
>>>  include/efi_loader.h         |  3 +++
>>>  lib/efi_loader/efi_runtime.c | 28 ++++++++++++++++++++++++++++
>>>  lib/efi_loader/efi_setup.c   |  5 +++++
>>>  4 files changed, 51 insertions(+)
>>>
>>> diff --git a/include/efi_api.h b/include/efi_api.h
>>> index 65584dd2d82a..d7d95edd4dfc 100644
>>> --- a/include/efi_api.h
>>> +++ b/include/efi_api.h
>>> @@ -213,6 +213,21 @@ struct efi_capsule_header {
>>>  	u32 capsule_image_size;
>>>  };
>>>
>>> +#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
>>> +
>>>  struct efi_runtime_services {
>>>  	struct efi_table_hdr hdr;
>>>  	efi_status_t (EFIAPI *get_time)(struct efi_time *time,
>>> diff --git a/include/efi_loader.h b/include/efi_loader.h
>>> index 23ce73226762..7bd8002e303e 100644
>>> --- a/include/efi_loader.h
>>> +++ b/include/efi_loader.h
>>> @@ -573,6 +573,9 @@ static inline int guidcmp(const efi_guid_t *g1, const efi_guid_t *g2)
>>>  #define __efi_runtime_data __attribute__ ((section (".data.efi_runtime")))
>>>  #define __efi_runtime __attribute__ ((section (".text.efi_runtime")))
>>>
>>> +/* Indicate supported runtime services */
>>> +efi_status_t efi_init_runtime_supported(void);
>>> +
>>>  /* Update CRC32 in table header */
>>>  void __efi_runtime efi_update_table_header_crc32(struct efi_table_hdr *table);
>>>
>>> diff --git a/lib/efi_loader/efi_runtime.c b/lib/efi_loader/efi_runtime.c
>>> index 60442cb21d37..cf202bb9ec07 100644
>>> --- a/lib/efi_loader/efi_runtime.c
>>> +++ b/lib/efi_loader/efi_runtime.c
>>> @@ -89,6 +89,34 @@ struct elf_rela {
>>>   * handle a good number of runtime callbacks
>>>   */
>>>
>>> +efi_status_t efi_init_runtime_supported(void)
>>> +{
>>> +	u16 efi_runtime_services_supported;
>>> +
>>> +	/*
>>> +	 * This value must be synced with efi_runtime_detach_list
>>> +	 * as well as efi_runtime_services.
>>> +	 */
>>> +	efi_runtime_services_supported = EFI_RT_SUPPORTED_RESET_SYSTEM;
>>
>> This support is system dependent, e.g. on RK3288 systems it does not exist.
>
> efi_reset_system() is defined as a weak function and so
> there is no easy way to determine whether this API is supported or not.
>
>>> +#ifdef CONFIG_EFI_GET_TIME
>>> +	efi_runtime_services_supported |= EFI_RT_SUPPORTED_GET_TIME;
>>
>> We do not support this at runtime.
>
> Okay, I will drop it.
>
>>> +#endif
>>> +#ifdef CONFIG_EFI_SET_TIME
>>> +	efi_runtime_services_supported |= EFI_RT_SUPPORTED_SET_TIME;
>>
>> We do not support this at runtime.
>
> ditto
>
>>> +#endif
>>> +#ifdef CONFIG_EFI_RUNTIME_SET_VIRTUAL_ADDRESS_MAP
>>> +	efi_runtime_services_supported |=
>>> +				EFI_RT_SUPPORTED_SET_VIRTUAL_ADDRESS_MAP;
>>
>> Our support is incomplete as we have not implemented ConvertPointer().
>
> Incomplete?
> My patch#3 adds ConvertPointer().

That is a later patch. So I did not consider it for this patch.
Furthermore the implementation of ConvertPointer depends on
CONFIG_EFI_RUNTIME_CONVERT_POINTER.

Best regards

Heinrich

>
> -Takahiro Akashi
>
>> For unsupported services we will have to change the return value to
>> EFI_UNSUPPORTED. But that will be a separate patch.
>>
>> Best regards
>>
>> Heinrich
>>
>>> +#endif
>>> +
>>> +	return EFI_CALL(efi_set_variable(L"RuntimeServicesSupported",
>>> +					 &efi_global_variable_guid,
>>> +					 EFI_VARIABLE_BOOTSERVICE_ACCESS |
>>> +					 EFI_VARIABLE_RUNTIME_ACCESS,
>>> +					 sizeof(efi_runtime_services_supported),
>>> +					 &efi_runtime_services_supported));
>>> +}
>>> +
>>>  /**
>>>   * efi_update_table_header_crc32() - Update crc32 in table header
>>>   *
>>> diff --git a/lib/efi_loader/efi_setup.c b/lib/efi_loader/efi_setup.c
>>> index 45d6aca051f3..75fa344060d5 100644
>>> --- a/lib/efi_loader/efi_setup.c
>>> +++ b/lib/efi_loader/efi_setup.c
>>> @@ -123,6 +123,11 @@ efi_status_t efi_init_obj_list(void)
>>>  	if (ret != EFI_SUCCESS)
>>>  		goto out;
>>>
>>> +	/* Indicate supported runtime services */
>>> +	ret = efi_init_runtime_supported();
>>> +	if (ret != EFI_SUCCESS)
>>> +		goto out;
>>> +
>>>  	/* Initialize system table */
>>>  	ret = efi_initialize_system_table();
>>>  	if (ret != EFI_SUCCESS)
>>>
>

^ permalink raw reply	[flat|nested] 32+ messages in thread

* [U-Boot] [RFC 2/6] efi: add RuntimeServicesSupported variable
  2019-06-13  9:17       ` Heinrich Schuchardt
@ 2019-06-13  9:21         ` Heinrich Schuchardt
  0 siblings, 0 replies; 32+ messages in thread
From: Heinrich Schuchardt @ 2019-06-13  9:21 UTC (permalink / raw)
  To: u-boot



On 6/13/19 11:17 AM, Heinrich Schuchardt wrote:
>
>
> On 6/13/19 9:06 AM, AKASHI Takahiro wrote:
>> On Thu, Jun 13, 2019 at 07:56:19AM +0200, Heinrich Schuchardt wrote:
>>>
>>>
>>> On 6/5/19 6:21 AM, AKASHI Takahiro wrote:
>>>> This variable is defined in UEFI specification 2.8, section 8.1.
>>>> Its value should be updated whenever we add any usable runtime services
>>>> function.
>>>
>>> It is also required by the EBBR specification.
>>>
>>>>
>>>> Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org>
>>>> ---
>>>>  include/efi_api.h            | 15 +++++++++++++++
>>>>  include/efi_loader.h         |  3 +++
>>>>  lib/efi_loader/efi_runtime.c | 28 ++++++++++++++++++++++++++++
>>>>  lib/efi_loader/efi_setup.c   |  5 +++++
>>>>  4 files changed, 51 insertions(+)
>>>>
>>>> diff --git a/include/efi_api.h b/include/efi_api.h
>>>> index 65584dd2d82a..d7d95edd4dfc 100644
>>>> --- a/include/efi_api.h
>>>> +++ b/include/efi_api.h
>>>> @@ -213,6 +213,21 @@ struct efi_capsule_header {
>>>>  	u32 capsule_image_size;
>>>>  };
>>>>
>>>> +#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
>>>> +
>>>>  struct efi_runtime_services {
>>>>  	struct efi_table_hdr hdr;
>>>>  	efi_status_t (EFIAPI *get_time)(struct efi_time *time,
>>>> diff --git a/include/efi_loader.h b/include/efi_loader.h
>>>> index 23ce73226762..7bd8002e303e 100644
>>>> --- a/include/efi_loader.h
>>>> +++ b/include/efi_loader.h
>>>> @@ -573,6 +573,9 @@ static inline int guidcmp(const efi_guid_t *g1, const efi_guid_t *g2)
>>>>  #define __efi_runtime_data __attribute__ ((section (".data.efi_runtime")))
>>>>  #define __efi_runtime __attribute__ ((section (".text.efi_runtime")))
>>>>
>>>> +/* Indicate supported runtime services */
>>>> +efi_status_t efi_init_runtime_supported(void);
>>>> +
>>>>  /* Update CRC32 in table header */
>>>>  void __efi_runtime efi_update_table_header_crc32(struct efi_table_hdr *table);
>>>>
>>>> diff --git a/lib/efi_loader/efi_runtime.c b/lib/efi_loader/efi_runtime.c
>>>> index 60442cb21d37..cf202bb9ec07 100644
>>>> --- a/lib/efi_loader/efi_runtime.c
>>>> +++ b/lib/efi_loader/efi_runtime.c
>>>> @@ -89,6 +89,34 @@ struct elf_rela {
>>>>   * handle a good number of runtime callbacks
>>>>   */
>>>>
>>>> +efi_status_t efi_init_runtime_supported(void)
>>>> +{
>>>> +	u16 efi_runtime_services_supported;
>>>> +
>>>> +	/*
>>>> +	 * This value must be synced with efi_runtime_detach_list
>>>> +	 * as well as efi_runtime_services.
>>>> +	 */
>>>> +	efi_runtime_services_supported = EFI_RT_SUPPORTED_RESET_SYSTEM;
>>>
>>> This support is system dependent, e.g. on RK3288 systems it does not exist.
>>
>> efi_reset_system() is defined as a weak function and so
>> there is no easy way to determine whether this API is supported or not.
>>
>>>> +#ifdef CONFIG_EFI_GET_TIME
>>>> +	efi_runtime_services_supported |= EFI_RT_SUPPORTED_GET_TIME;
>>>
>>> We do not support this at runtime.
>>
>> Okay, I will drop it.
>>
>>>> +#endif
>>>> +#ifdef CONFIG_EFI_SET_TIME
>>>> +	efi_runtime_services_supported |= EFI_RT_SUPPORTED_SET_TIME;
>>>
>>> We do not support this at runtime.
>>
>> ditto
>>
>>>> +#endif
>>>> +#ifdef CONFIG_EFI_RUNTIME_SET_VIRTUAL_ADDRESS_MAP
>>>> +	efi_runtime_services_supported |=
>>>> +				EFI_RT_SUPPORTED_SET_VIRTUAL_ADDRESS_MAP;
>>>
>>> Our support is incomplete as we have not implemented ConvertPointer().
>>
>> Incomplete?
>> My patch#3 adds ConvertPointer().
>
> That is a later patch. So I did not consider it for this patch.
> Furthermore the implementation of ConvertPointer depends on
> CONFIG_EFI_RUNTIME_CONVERT_POINTER.

EFI_RT_SUPPORTED_SET_VIRTUAL_ADDRESS_MAP and
EFI_RT_SUPPORTED_CONVERT_POINTER are separate values.

You are right in switching EFI_RT_SUPPORTED_SET_VIRTUAL_ADDRESS_MAP on here.

Best regards

Heinrich

>>
>> -Takahiro Akashi
>>
>>> For unsupported services we will have to change the return value to
>>> EFI_UNSUPPORTED. But that will be a separate patch.
>>>
>>> Best regards
>>>
>>> Heinrich
>>>
>>>> +#endif
>>>> +
>>>> +	return EFI_CALL(efi_set_variable(L"RuntimeServicesSupported",
>>>> +					 &efi_global_variable_guid,
>>>> +					 EFI_VARIABLE_BOOTSERVICE_ACCESS |
>>>> +					 EFI_VARIABLE_RUNTIME_ACCESS,
>>>> +					 sizeof(efi_runtime_services_supported),
>>>> +					 &efi_runtime_services_supported));
>>>> +}
>>>> +
>>>>  /**
>>>>   * efi_update_table_header_crc32() - Update crc32 in table header
>>>>   *
>>>> diff --git a/lib/efi_loader/efi_setup.c b/lib/efi_loader/efi_setup.c
>>>> index 45d6aca051f3..75fa344060d5 100644
>>>> --- a/lib/efi_loader/efi_setup.c
>>>> +++ b/lib/efi_loader/efi_setup.c
>>>> @@ -123,6 +123,11 @@ efi_status_t efi_init_obj_list(void)
>>>>  	if (ret != EFI_SUCCESS)
>>>>  		goto out;
>>>>
>>>> +	/* Indicate supported runtime services */
>>>> +	ret = efi_init_runtime_supported();
>>>> +	if (ret != EFI_SUCCESS)
>>>> +		goto out;
>>>> +
>>>>  	/* Initialize system table */
>>>>  	ret = efi_initialize_system_table();
>>>>  	if (ret != EFI_SUCCESS)
>>>>
>>
>

^ permalink raw reply	[flat|nested] 32+ messages in thread

* [U-Boot] [RFC 6/6] efi_loader: variable: support runtime variable access via cache
  2019-06-05  4:21 ` [U-Boot] [RFC 6/6] efi_loader: variable: support runtime variable access via cache AKASHI Takahiro
@ 2019-06-15 19:01   ` Heinrich Schuchardt
  2019-06-17  1:51     ` AKASHI Takahiro
  0 siblings, 1 reply; 32+ messages in thread
From: Heinrich Schuchardt @ 2019-06-15 19:01 UTC (permalink / raw)
  To: u-boot

On 6/5/19 6:21 AM, AKASHI Takahiro wrote:
> With this patch, cache buffer for UEFI variables will be created
> so that we will still be able to access, at least retrieve,
> UEFI variables when we exit from boottime services,
>
> This feature is a "should" behavior described in EBBR v1.0
> section 2.5.3.
>
> Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org>
> ---
>   include/efi_loader.h          |  17 ++
>   lib/efi_loader/Kconfig        |   9 +
>   lib/efi_loader/efi_boottime.c |  10 +-
>   lib/efi_loader/efi_runtime.c  |  13 +
>   lib/efi_loader/efi_variable.c | 467 ++++++++++++++++++++++++++++++++++
>   5 files changed, 515 insertions(+), 1 deletion(-)

Please, put the cache into a separate file.

>
> diff --git a/include/efi_loader.h b/include/efi_loader.h
> index 93f7ece814a0..acab657b9d70 100644
> --- a/include/efi_loader.h
> +++ b/include/efi_loader.h
> @@ -620,6 +620,23 @@ efi_status_t EFIAPI efi_set_variable(u16 *variable_name,
>   				     const efi_guid_t *vendor, u32 attributes,
>   				     efi_uintn_t data_size, const void *data);
>
> +#ifdef CONFIG_EFI_RUNTIME_GET_VARIABLE_CACHING
> +efi_status_t efi_freeze_variable_table(void);
> +
> +/* runtime version of APIs */
> +efi_status_t
> +__efi_runtime EFIAPI efi_get_variable_runtime(u16 *variable_name,

I think one version of the functions serving at runtime and boottime is
enough.

The cache should be used both at runtime and at boottime. Essentially I
expect three modules working together:

UEFI API implementation <-> Cache <-> Persistence driver

I would suggest to put each of these into a separate file.

Both the API implementation and the Cache have to be available at
Boottime and at Runtime. A first version of the persistence driver may
only be working at boottime.

The NV-cache content should be written to non-volatile memory on Reset()
and on ExitBootServices() and if possible when updating variables at
runtime.

> +					      const efi_guid_t *vendor,
> +					      u32 *attributes,
> +					      efi_uintn_t *data_size,
> +					      void *data);
> +efi_status_t
> +__efi_runtime EFIAPI efi_get_next_variable_name_runtime(
> +						efi_uintn_t *variable_name_size,
> +						u16 *variable_name,
> +						const efi_guid_t *vendor);
> +#endif /* CONFIG_EFI_RUNTIME_GET_VARIABLE_CACHING */
> +
>   /*
>    * 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:
> diff --git a/lib/efi_loader/Kconfig b/lib/efi_loader/Kconfig
> index e2ef43157568..3f284795648f 100644
> --- a/lib/efi_loader/Kconfig
> +++ b/lib/efi_loader/Kconfig
> @@ -59,6 +59,15 @@ config EFI_RUNTIME_CONVERT_POINTER
>   	  to be called by UEFI drivers in relocating themselves to virtual
>   	  address space.
>
> +config EFI_RUNTIME_GET_VARIABLE_CACHING
> +	bool "runtime_service: GetVariable: Enable runtime access via cache (read-only)"
> +	default y
> +	help
> +	  Select this option if you want to access UEFI variables at
> +	  runtime even though you cannot update values on the fly.
> +	  With or without this option, you can access UEFI variables
> +	  at boottime.

Updates of volatile variables should always be possible.

> +
>   config EFI_DEVICE_PATH_TO_TEXT
>   	bool "Device path to text protocol"
>   	default y
> diff --git a/lib/efi_loader/efi_boottime.c b/lib/efi_loader/efi_boottime.c
> index e4abaf3601d9..14e343abbd43 100644
> --- a/lib/efi_loader/efi_boottime.c
> +++ b/lib/efi_loader/efi_boottime.c
> @@ -1892,6 +1892,9 @@ static efi_status_t EFIAPI efi_exit_boot_services(efi_handle_t image_handle,
>   						  efi_uintn_t map_key)
>   {
>   	struct efi_event *evt;
> +#ifdef CONFIG_EFI_RUNTIME_GET_VARIABLE_CACHING
> +	efi_status_t ret;
> +#endif
>
>   	EFI_ENTRY("%p, %zx", image_handle, map_key);
>
> @@ -1921,7 +1924,12 @@ static efi_status_t EFIAPI efi_exit_boot_services(efi_handle_t image_handle,
>   		}
>   	}
>
> -	/* TODO: Should persist EFI variables here */
> +#ifdef CONFIG_EFI_RUNTIME_GET_VARIABLE_CACHING

Can we have weak functions for initializing and persisting the cache, e.g.

efi_status_t __weak
efi_load_variable_cache(cache_entry *cache, size_t *size)
{
         cache->len = 0;
         return EFI_SUCCESS;
}

efi_status_t __runtime __weak
efi_write_variable_cache(cache_entry *cache, size_t size)
{
         return EFI_UNSUPPORTED;
}

Then we can override these in whatever driver we implement.


> +	/* No more variable update */
> +	ret = efi_freeze_variable_table();
> +	if (ret != EFI_SUCCESS)
> +		return EFI_EXIT(ret);
> +#endif
>
>   	board_quiesce_devices();
>
> diff --git a/lib/efi_loader/efi_runtime.c b/lib/efi_loader/efi_runtime.c
> index fc5bdee80e00..b60f70f04613 100644
> --- a/lib/efi_loader/efi_runtime.c
> +++ b/lib/efi_loader/efi_runtime.c
> @@ -111,6 +111,11 @@ efi_status_t efi_init_runtime_supported(void)
>   	efi_runtime_services_supported |=
>   				EFI_RT_SUPPORTED_CONVERT_POINTER;
>   #endif
> +#ifdef CONFIG_EFI_RUNTIME_GET_VARIABLE_CACHING
> +	efi_runtime_services_supported |=
> +				(EFI_RT_SUPPORTED_GET_VARIABLE |
> +				 EFI_RT_SUPPORTED_GET_NEXT_VARIABLE_NAME);
> +#endif
>
>   	return EFI_CALL(efi_set_variable(L"RuntimeServicesSupported",
>   					 &efi_global_variable_guid,
> @@ -469,10 +474,18 @@ static struct efi_runtime_detach_list_struct efi_runtime_detach_list[] = {
>   		.patchto = NULL,
>   	}, {
>   		.ptr = &efi_runtime_services.get_variable,
> +#ifdef CONFIG_EFI_RUNTIME_GET_VARIABLE_CACHING
> +		.patchto = &efi_get_variable_runtime,
> +#else
>   		.patchto = &efi_device_error,
> +#endif
>   	}, {
>   		.ptr = &efi_runtime_services.get_next_variable_name,
> +#ifdef CONFIG_EFI_RUNTIME_GET_VARIABLE_CACHING
> +		.patchto = &efi_get_next_variable_name,
> +#else
>   		.patchto = &efi_device_error,
> +#endif
>   	}, {
>   		.ptr = &efi_runtime_services.set_variable,
>   		.patchto = &efi_device_error,
> diff --git a/lib/efi_loader/efi_variable.c b/lib/efi_loader/efi_variable.c
> index d9887be938c2..ee21892dd291 100644
> --- a/lib/efi_loader/efi_variable.c
> +++ b/lib/efi_loader/efi_variable.c
> @@ -706,3 +706,470 @@ efi_status_t EFIAPI efi_set_variable(u16 *variable_name,
>
>   	return EFI_EXIT(ret);
>   }
> +
> +#ifdef CONFIG_EFI_RUNTIME_GET_VARIABLE_CACHING
> +/*
> + * runtime version of APIs
> + * We only support read-only variable access.
> + * The table is in U-Boot's hash table format, but has its own
> + * _ENTRY structure for specific use.
> + *
> + * Except for efi_freeze_variable_table(), which is to be called in
> + * exit_boot_services(), all the functions and data below must be
> + * placed in either RUNTIME_SERVICES_CODE or RUNTIME_SERVICES_DATA.
> + */
> +typedef struct _ENTRY {
> +	unsigned int used;	/* hash value; 0 for not used */
> +	size_t name;		/* name offset from itself */
> +	efi_guid_t vendor;
> +	u32 attributes;
> +	size_t data;		/* data offset from itself */
> +	size_t data_size;
> +} _ENTRY;
> +
> +static inline u16 *entry_name(_ENTRY *e) { return (void *)e + e->name; }
> +static inline u16 *entry_data(_ENTRY *e) { return (void *)e + e->data; }
> +
> +static struct hsearch_data *efi_variable_table __efi_runtime_data;
> +
> +static size_t __efi_runtime u16_strlen_runtime(const u16 *s1)


Please, do not duplicate existing functions. If they have to be runtime
simply change the existing function to __runtime.

> +{
> +	size_t n = 0;
> +
> +	while (*s1) {
> +		n++;
> +		s1++;
> +	}
> +
> +	return n;
> +}
> +
> +static int __efi_runtime memcmp_runtime(const void *m1, const void *m2,
> +					size_t n)

I dislike duplicate code. Can't we simply define the existing memcmp
function as __runtime?
> +{
> +	while (n && *(u8 *)m1 == *(u8 *)m2) {
> +		n--;
> +		m1++;
> +		m2++;
> +	}
> +
> +	if (n)
> +		return *(u8 *)m1 - *(u8 *)m2;
> +
> +	return 0;
> +}
> +
> +static void __efi_runtime memcpy_runtime(void *m1, const void *m2, size_t n)
> +{

Can't we simply define the existing memcpy function as __runtime?

> +	for (; n; n--, m1++, m2++)
> +		*(u8 *)m1 = *(u8 *)m2;
> +}
> +
> +static int __efi_runtime efi_cmpkey(_ENTRY *e, const u16 *name,
> +				    const efi_guid_t *vendor)
> +{
> +	size_t name_len;
> +
> +	name_len = u16_strlen_runtime(entry_name(e));
> +
> +	/* return zero if matched */
> +	return name_len != u16_strlen_runtime(name) ||
> +	       memcmp_runtime(entry_name(e), name, name_len * 2) ||
> +	       memcmp_runtime(e->vendor.b, vendor->b, sizeof(vendor));
> +}
> +
> +/* simplified and slightly different version of hsearch_r() */

These hash functions are so complicated that they really need a unit
test testing them rigorously.

> +static int __efi_runtime hsearch_runtime(const u16 *name,
> +					 const efi_guid_t *vendor,
> +					 ACTION action,
> +					 _ENTRY **retval,
> +					 struct hsearch_data *htab)
> +{
> +	unsigned int hval;
> +	unsigned int count;
> +	unsigned int len;
> +	unsigned int idx, new;
> +
> +	/* Compute an value for the given string. */
> +	len = u16_strlen_runtime(name);

Can't the same variable name exist for different GUIDs? Why is the GUID
not considered in the hash?

> +	hval = len;
> +	count = len;
> +	while (count-- > 0) {
> +		hval <<= 4;
> +		hval += name[count];
> +	}
> +
> +	/*
> +	 * First hash function:
> +	 * simply take the modulo but prevent zero.
> +	 */
> +	hval %= htab->size;
> +	if (hval == 0)
> +		++hval;
> +
> +	/* The first index tried. */
> +	new = -1; /* not found */
> +	idx = hval;
> +
> +	if (htab->table[idx].used) {
> +		/*
> +		 * Further action might be required according to the
> +		 * action value.
> +		 */
> +		unsigned int hval2;
> +
> +		if (htab->table[idx].used == hval &&
> +		    !efi_cmpkey(&htab->table[idx], name, vendor)) {
> +			if (action == FIND) {
> +				*retval = &htab->table[idx];
> +				return idx;
> +			}
> +			/* we don't need to support overwrite */
> +			return -1;
> +		}
> +
> +		/*
> +		 * Second hash function:
> +		 * as suggested in [Knuth]
> +		 */
> +		hval2 = 1 + hval % (htab->size - 2);
> +
> +		do {
> +			/*
> +			 * Because SIZE is prime this guarantees to
> +			 * step through all available indices.
> +			 */
> +			if (idx <= hval2)
> +				idx = htab->size + idx - hval2;
> +			else
> +				idx -= hval2;
> +
> +			/*
> +			 * If we visited all entries leave the loop
> +			 * unsuccessfully.
> +			 */
> +			if (idx == hval)
> +				break;
> +
> +			/* If entry is found use it. */
> +			if (htab->table[idx].used == hval &&
> +			    !efi_cmpkey(&htab->table[idx], name, vendor)) {
> +				if (action == FIND) {
> +					*retval = &htab->table[idx];
> +					return idx;
> +				}
> +				/* we don't need to support overwrite */
> +				return -1;
> +			}
> +		} while (htab->table[idx].used);
> +
> +		if (!htab->table[idx].used)
> +			new = idx;
> +	} else {
> +		new = idx;
> +	}
> +
> +	/*
> +	 * An empty bucket has been found.
> +	 * The following code should never be executed after
> +	 * exit_boot_services()
> +	 */
> +	if (action == ENTER) {
> +		/*
> +		 * If table is full and another entry should be
> +		 * entered return with error.
> +		 */
> +		if (htab->filled == htab->size) {
> +			*retval = NULL;
> +			return 0;
> +		}
> +
> +		/* Create new entry */
> +		htab->table[new].used = hval;
> +		++htab->filled;
> +
> +		/* return new entry */
> +		*retval = &htab->table[new];
> +		return 1;
> +	}
> +
> +	*retval = NULL;
> +	return 0;
> +}
> +
> +/* from lib/hashtable.c */
> +static inline int isprime(unsigned int number)
> +{
> +	/* no even number will be passed */
> +	unsigned int div = 3;
> +
> +	while (div * div < number && number % div != 0)
> +		div += 2;
> +
> +	return number % div != 0;
> +}
> +
> +efi_status_t efi_freeze_variable_table(void)

Please, add comments to your functions. It is not self-evident what this
function is meant to do.

I cannot imagine why a variable cache should be frozen. It is a living
data structure until the system is switched off.

For a variable cache I expect that you allocate memory before handling
the first variable and never again. At runtime you will not have chance
to allocate memory anyway.

This function is way too long. Pleae, break it down.

Best regards

Heinrich

> +{
> +	int var_num = 0;
> +	size_t var_data_size = 0;
> +	u16 *name;
> +	efi_uintn_t name_buf_len, name_len;
> +	efi_guid_t vendor;
> +	u32 attributes;
> +	u8 *mem_pool, *var_buf = NULL;
> +	size_t table_size, var_size, var_buf_size;
> +	_ENTRY *new = NULL;
> +	efi_status_t ret;
> +
> +	/* phase-1 loop */
> +	name_buf_len = 128;
> +	name = malloc(name_buf_len);
> +	if (!name)
> +		return EFI_OUT_OF_RESOURCES;
> +	name[0] = 0;
> +	for (;;) {
> +		name_len = name_buf_len;
> +		ret = EFI_CALL(efi_get_next_variable_name(&name_len, name,
> +							  &vendor));
> +		if (ret == EFI_NOT_FOUND) {
> +			break;
> +		} else if (ret == EFI_BUFFER_TOO_SMALL) {
> +			u16 *buf;
> +
> +			name_buf_len = name_len;
> +			buf = realloc(name, name_buf_len);
> +			if (!buf) {
> +				free(name);
> +				return EFI_OUT_OF_RESOURCES;
> +			}
> +			name = buf;
> +			name_len = name_buf_len;
> +			ret = EFI_CALL(efi_get_next_variable_name(&name_len,
> +								  name,
> +								  &vendor));
> +		}
> +
> +		if (ret != EFI_SUCCESS)
> +			return ret;
> +
> +		var_size = 0;
> +		ret = EFI_CALL(efi_get_variable(name, &vendor, &attributes,
> +						&var_size, NULL));
> +		if (ret != EFI_BUFFER_TOO_SMALL)
> +			return ret;
> +
> +		if (!(attributes & EFI_VARIABLE_RUNTIME_ACCESS))
> +			continue;
> +
> +		var_num++;
> +		var_data_size += (u16_strlen_runtime(name) + 1) * sizeof(u16);
> +		var_data_size += var_size;
> +		/* mem_pool must 2-byte aligned for u16 variable name */
> +		if (var_data_size & 0x1)
> +			var_data_size++;
> +	}
> +
> +	/*
> +	 * total of entries in hash table must be a prime number.
> +	 * The logic below comes from lib/hashtable.c
> +	 */
> +	var_num |= 1;               /* make odd */
> +	while (!isprime(var_num))
> +		var_num += 2;
> +
> +	/* We need table[var_num] for hsearch_runtime algo */
> +	table_size = sizeof(*efi_variable_table)
> +			+ sizeof(_ENTRY) * (var_num + 1) + var_data_size;
> +	ret = efi_allocate_pool(EFI_RUNTIME_SERVICES_DATA,
> +				table_size, (void **)&efi_variable_table);
> +	if (ret != EFI_SUCCESS)
> +		return ret;
> +
> +	efi_variable_table->size = var_num;
> +	efi_variable_table->table = (void *)efi_variable_table
> +					+ sizeof(*efi_variable_table);
> +	mem_pool = (u8 *)efi_variable_table->table
> +			+ sizeof(_ENTRY) * (var_num + 1);
> +
> +	var_buf_size = 128;
> +	var_buf = malloc(var_buf_size);
> +	if (!var_buf) {
> +		ret = EFI_OUT_OF_RESOURCES;
> +		goto err;
> +	}
> +
> +	/* phase-2 loop */
> +	name[0] = 0;
> +	name_len = name_buf_len;
> +	for (;;) {
> +		name_len = name_buf_len;
> +		ret = EFI_CALL(efi_get_next_variable_name(&name_len, name,
> +							  &vendor));
> +		if (ret == EFI_NOT_FOUND)
> +			break;
> +		else if (ret != EFI_SUCCESS)
> +			goto err;
> +
> +		var_size = var_buf_size;
> +		ret = EFI_CALL(efi_get_variable(name, &vendor, &attributes,
> +						&var_size, var_buf));
> +		if (ret == EFI_BUFFER_TOO_SMALL) {
> +			free(var_buf);
> +			var_buf_size = var_size;
> +			var_buf = malloc(var_buf_size);
> +			if (!var_buf) {
> +				ret = EFI_OUT_OF_RESOURCES;
> +				goto err;
> +			}
> +			ret = EFI_CALL(efi_get_variable(name, &vendor,
> +							&attributes,
> +							&var_size, var_buf));
> +		}
> +		if (ret != EFI_SUCCESS)
> +			goto err;
> +
> +		if (!(attributes & EFI_VARIABLE_RUNTIME_ACCESS))
> +			continue;
> +
> +		if (hsearch_runtime(name, &vendor, ENTER, &new,
> +				    efi_variable_table) <= 0) {
> +			/* This should not happen */
> +			ret = EFI_INVALID_PARAMETER;
> +			goto err;
> +		}
> +
> +		/* allocate space from RUNTIME DATA */
> +		name_len = (u16_strlen_runtime(name) + 1) * sizeof(u16);
> +		memcpy_runtime(mem_pool, name, name_len);
> +		new->name = mem_pool - (u8 *)new; /* offset */
> +		mem_pool += name_len;
> +
> +		memcpy_runtime(&new->vendor.b, &vendor.b, sizeof(vendor));
> +
> +		new->attributes = attributes;
> +
> +		memcpy_runtime(mem_pool, var_buf, var_size);
> +		new->data = mem_pool - (u8 *)new; /* offset */
> +		new->data_size = var_size;
> +		mem_pool += var_size;
> +
> +		/* mem_pool must 2-byte aligned for u16 variable name */
> +		if ((uintptr_t)mem_pool & 0x1)
> +			mem_pool++;
> +	}
> +#ifdef DEBUG
> +	name[0] = 0;
> +	name_len = name_buf_len;
> +	for (;;) {
> +		name_len = name_buf_len;
> +		ret = efi_get_next_variable_name_runtime(&name_len, name,
> +							 &vendor);
> +		if (ret == EFI_NOT_FOUND)
> +			break;
> +		else if (ret != EFI_SUCCESS)
> +			goto err;
> +
> +		var_size = var_buf_size;
> +		ret = efi_get_variable_runtime(name, &vendor, &attributes,
> +					       &var_size, var_buf);
> +		if (ret != EFI_SUCCESS)
> +			goto err;
> +
> +		printf("%ls_%pUl:\n", name, &vendor);
> +		printf("    attributes: 0x%x\n", attributes);
> +		printf("    value (size: 0x%lx)\n", var_size);
> +	}
> +#endif
> +	ret = EFI_SUCCESS;
> +
> +err:
> +	free(name);
> +	free(var_buf);
> +	if (ret != EFI_SUCCESS && efi_variable_table) {
> +		efi_free_pool(efi_variable_table);
> +		efi_variable_table = NULL;
> +	}
> +
> +	return ret;
> +}
> +
> +efi_status_t
> +__efi_runtime EFIAPI efi_get_variable_runtime(u16 *variable_name,
> +					      const efi_guid_t *vendor,
> +					      u32 *attributes,
> +					      efi_uintn_t *data_size,
> +					      void *data)
> +{
> +	_ENTRY *new;
> +
> +	if (!variable_name || !vendor || !data_size)
> +		return EFI_EXIT(EFI_INVALID_PARAMETER);
> +
> +	if (hsearch_runtime(variable_name, vendor, FIND, &new,
> +			    efi_variable_table) <= 0)
> +		return EFI_NOT_FOUND;
> +
> +	if (attributes)
> +		*attributes = new->attributes;
> +	if (*data_size < new->data_size) {
> +		*data_size = new->data_size;
> +		return EFI_BUFFER_TOO_SMALL;
> +	}
> +
> +	*data_size = new->data_size;
> +	memcpy_runtime(data, entry_data(new), new->data_size);
> +
> +	return EFI_SUCCESS;
> +}
> +
> +static int prev_idx __efi_runtime_data;
> +
> +efi_status_t
> +__efi_runtime EFIAPI efi_get_next_variable_name_runtime(
> +						efi_uintn_t *variable_name_size,
> +						u16 *variable_name,
> +						const efi_guid_t *vendor)
> +{
> +	_ENTRY *e;
> +	u16 *name;
> +	efi_uintn_t name_size;
> +
> +	if (!variable_name_size || !variable_name || !vendor)
> +		return EFI_INVALID_PARAMETER;
> +
> +	if (variable_name[0]) {
> +		/* sanity check for previous variable */
> +		if (prev_idx < 0)
> +			return EFI_INVALID_PARAMETER;
> +
> +		e = &efi_variable_table->table[prev_idx];
> +		if (!e->used || efi_cmpkey(e, variable_name, vendor))
> +			return EFI_INVALID_PARAMETER;
> +	} else {
> +		prev_idx = -1;
> +	}
> +
> +	/* next variable */
> +	while (++prev_idx <= efi_variable_table->size) {
> +		e = &efi_variable_table->table[prev_idx];
> +		if (e->used)
> +			break;
> +	}
> +	if (prev_idx > efi_variable_table->size)
> +		return EFI_NOT_FOUND;
> +
> +	name = entry_name(e);
> +	name_size = (u16_strlen_runtime(name) + 1)
> +			* sizeof(u16);
> +	if (*variable_name_size < name_size) {
> +		*variable_name_size = name_size;
> +		return EFI_BUFFER_TOO_SMALL;
> +	}
> +
> +	memcpy_runtime(variable_name, name, name_size);
> +	memcpy_runtime((void *)&vendor->b, &e->vendor.b, sizeof(vendor));
> +
> +	return EFI_SUCCESS;
> +}
> +#endif /* CONFIG_EFI_RUNTIME_GET_VARIABLE_CACHING */
>

^ permalink raw reply	[flat|nested] 32+ messages in thread

* [U-Boot] [RFC 3/6] efi_loader: support convert_pointer at runtime
  2019-06-05  4:21 ` [U-Boot] [RFC 3/6] efi_loader: support convert_pointer at runtime AKASHI Takahiro
@ 2019-06-15 19:41   ` Heinrich Schuchardt
  2019-06-17  1:15     ` AKASHI Takahiro
  0 siblings, 1 reply; 32+ messages in thread
From: Heinrich Schuchardt @ 2019-06-15 19:41 UTC (permalink / raw)
  To: u-boot

On 6/5/19 6:21 AM, AKASHI Takahiro wrote:
> With this patch, ConvertPointer runtime service is enabled.
> This function will be useful only after SetVirtualAddressMap is called
> and before it exits according to UEFI specification.

ConvertPointer() is called by drivers upon calling the notification
functions of events registered as EVT_SIGNAL_VIRTUAL_ADDRESS_CHANGE.

We still lack support for these events.

>
> Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org>
> ---
>   lib/efi_loader/Kconfig       |  8 ++++
>   lib/efi_loader/efi_runtime.c | 81 ++++++++++++++++++++++++++----------
>   2 files changed, 66 insertions(+), 23 deletions(-)
>
> diff --git a/lib/efi_loader/Kconfig b/lib/efi_loader/Kconfig
> index bb9c7582b14d..e2ef43157568 100644
> --- a/lib/efi_loader/Kconfig
> +++ b/lib/efi_loader/Kconfig
> @@ -51,6 +51,14 @@ config EFI_RUNTIME_SET_VIRTUAL_ADDRESS_MAP
>   	  Enable SetVirtualAddressMap runtime service. This API will be
>   	  called by OS just before it enters into virtual address mode.
>
> +config EFI_RUNTIME_CONVERT_POINTER
> +	bool "runtime service: ConvertPointer"
> +	default n
> +	help
> +	  Enable ConvertPointer runtime service. This API will be expected
> +	  to be called by UEFI drivers in relocating themselves to virtual
> +	  address space.
> +
>   config EFI_DEVICE_PATH_TO_TEXT
>   	bool "Device path to text protocol"
>   	default y
> diff --git a/lib/efi_loader/efi_runtime.c b/lib/efi_loader/efi_runtime.c
> index cf202bb9ec07..ff3684a4b692 100644
> --- a/lib/efi_loader/efi_runtime.c
> +++ b/lib/efi_loader/efi_runtime.c
> @@ -27,7 +27,6 @@ LIST_HEAD(efi_runtime_mmio);
>
>   static efi_status_t __efi_runtime EFIAPI efi_unimplemented(void);
>   static efi_status_t __efi_runtime EFIAPI efi_device_error(void);
> -static efi_status_t __efi_runtime EFIAPI efi_invalid_parameter(void);
>
>   /*
>    * TODO(sjg at chromium.org): These defines and structures should come from the ELF
> @@ -108,6 +107,10 @@ efi_status_t efi_init_runtime_supported(void)
>   	efi_runtime_services_supported |=
>   				EFI_RT_SUPPORTED_SET_VIRTUAL_ADDRESS_MAP;
>   #endif
> +#ifdef CONFIG_EFI_RUNTIME_CONVERT_POINTER
> +	efi_runtime_services_supported |=
> +				EFI_RT_SUPPORTED_CONVERT_POINTER;
> +#endif
>
>   	return EFI_CALL(efi_set_variable(L"RuntimeServicesSupported",
>   					 &efi_global_variable_guid,
> @@ -392,6 +395,39 @@ efi_status_t __weak __efi_runtime EFIAPI efi_set_time(struct efi_time *time)
>   	return EFI_UNSUPPORTED;
>   }
>
> +#ifdef CONFIG_EFI_RUNTIME_CONVERT_POINTER
> +static struct efi_mem_desc *efi_virtmap __efi_runtime_data;
> +static int efi_virtmap_num __efi_runtime_data;
> +

Please, put a description of the function and its parameters here.
> +static efi_status_t __efi_runtime EFIAPI efi_convert_pointer(unsigned long dbg,
> +							     void **address)
> +{
> +	struct efi_mem_desc *map;
> +	efi_physical_addr_t addr;
> +	int i;
> +
> +	if (!efi_virtmap)
> +		return EFI_UNSUPPORTED;
> +
> +	if (!address)
> +		return EFI_INVALID_PARAMETER;
> +
> +	for (i = 0, map = efi_virtmap; i < efi_virtmap_num; i++, map++) {
> +		addr = (efi_physical_addr_t)*address;
This line should be before the loop.

> +		if (addr >= map->physical_start &&
> +		    (addr < (map->physical_start

%s/(addr/addr/  No need for that extra parenthesis.

> +			     + (map->num_pages << EFI_PAGE_SHIFT)))) {
> +			*address = (void *)map->virtual_start;

I guess on 32bit this will end in a warning? Wouldn't you need to
convert to uintptr_t first?

> +			*address += addr - map->physical_start;

I think a single assignment is enough. Here you are updating a 32bit
pointer with the difference of two u64. I would expect a warning.

*address = (void *)(uintptr_t)
(addr - map->physical_start + map->virtual_start);

Or do all calculation with addr before copying to *address.

> +
> +			return EFI_SUCCESS;
> +		}
> +	}
> +
> +	return EFI_NOT_FOUND;
> +}
> +#endif
> +
>   struct efi_runtime_detach_list_struct {
>   	void *ptr;
>   	void *patchto;
> @@ -599,6 +635,10 @@ static efi_status_t EFIAPI efi_set_virtual_address_map(
>   		return EFI_EXIT(EFI_INVALID_PARAMETER);
>   	}
>
> +	efi_virtmap = virtmap;
> +	efi_virtmap_num = n;
> +
> +#if 0 /* FIXME: This code is fragile as calloc is used in add_runtime_mmio */
>   	/* Rebind mmio pointers */
>   	for (i = 0; i < n; i++) {
>   		struct efi_mem_desc *map = (void*)virtmap +
> @@ -622,14 +662,14 @@ static efi_status_t EFIAPI efi_set_virtual_address_map(
>   				*lmmio->ptr = (void *)new_addr;
>   			}
>   		}
> -		if ((map_start <= (uintptr_t)systab.tables) &&
> -		    (map_end >= (uintptr_t)systab.tables)) {
> -			char *ptr = (char *)systab.tables;
> -
> -			ptr += off;
> -			systab.tables = (struct efi_configuration_table *)ptr;
> -		}

This looks like an unrelated change.

Put it into a separate patch, please.

Best regards

Heinrich

>   	}
> +#endif
> +
> +	/* FIXME */
> +	efi_convert_pointer(0, (void **)&systab.tables);
> +
> +	/* All fixes must be done before this line */
> +	efi_virtmap = NULL;
>
>   	/* Move the actual runtime code over */
>   	for (i = 0; i < n; i++) {
> @@ -644,6 +684,11 @@ static efi_status_t EFIAPI efi_set_virtual_address_map(
>   			/* Once we're virtual, we can no longer handle
>   			   complex callbacks */
>   			efi_runtime_detach(new_offset);
> +
> +			/*
> +			 * FIXME:
> +			 * We can no longer update RuntimeServicesSupported.
> +			 */
>   			return EFI_EXIT(EFI_SUCCESS);
>   		}
>   	}
> @@ -733,20 +778,6 @@ static efi_status_t __efi_runtime EFIAPI efi_device_error(void)
>   	return EFI_DEVICE_ERROR;
>   }
>
> -/**
> - * efi_invalid_parameter() - replacement function, returns EFI_INVALID_PARAMETER
> - *
> - * This function is used after SetVirtualAddressMap() is called as replacement
> - * for services that are not available anymore due to constraints of the U-Boot
> - * implementation.
> - *
> - * Return:	EFI_INVALID_PARAMETER
> - */
> -static efi_status_t __efi_runtime EFIAPI efi_invalid_parameter(void)
> -{
> -	return EFI_INVALID_PARAMETER;
> -}
> -
>   /**
>    * efi_update_capsule() - process information from operating system
>    *
> @@ -833,7 +864,11 @@ struct efi_runtime_services __efi_runtime_data efi_runtime_services = {
>   #else
>   	.set_virtual_address_map = (void *)&efi_unimplemented,
>   #endif
> -	.convert_pointer = (void *)&efi_invalid_parameter,
> +#ifdef CONFIG_EFI_RUNTIME_CONVERT_POINTER
> +	.convert_pointer = &efi_convert_pointer,
> +#else
> +	.convert_pointer = (void *)&efi_unimplemented,

I feel uneasy using a function efi_unimplemented() with a different
number of parameters here. Depending on the ABI this may lead to a crash.

Best regards

Heinrich

> +#endif
>   	.get_variable = efi_get_variable,
>   	.get_next_variable_name = efi_get_next_variable_name,
>   	.set_variable = efi_set_variable,
>

^ permalink raw reply	[flat|nested] 32+ messages in thread

* [U-Boot] [RFC 1/6] efi_loader: runtime: make SetVirtualAddressMap configurable
  2019-06-05  4:21 ` [U-Boot] [RFC 1/6] efi_loader: runtime: make SetVirtualAddressMap configurable AKASHI Takahiro
@ 2019-06-15 19:46   ` Heinrich Schuchardt
  2019-06-15 21:14     ` Mark Kettenis
  2019-06-17  1:05     ` AKASHI Takahiro
  0 siblings, 2 replies; 32+ messages in thread
From: Heinrich Schuchardt @ 2019-06-15 19:46 UTC (permalink / raw)
  To: u-boot

On 6/5/19 6:21 AM, AKASHI Takahiro wrote:
> OS does not always need to call SetVirtualAddressMap.
> (Ard confirmed this on arm64 linux.)
> So let this API configurable. If disabled, it will return EFI_UNSUPPORTED
> as UEFI specification requires.

Currently we do not support this scenario. Alex's patch should go in first.

>
> Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org>
> ---
>   lib/efi_loader/Kconfig       | 7 +++++++
>   lib/efi_loader/efi_runtime.c | 8 ++++++++
>   2 files changed, 15 insertions(+)
>
> diff --git a/lib/efi_loader/Kconfig b/lib/efi_loader/Kconfig
> index 8bf4b1754d06..bb9c7582b14d 100644
> --- a/lib/efi_loader/Kconfig
> +++ b/lib/efi_loader/Kconfig
> @@ -44,6 +44,13 @@ config EFI_SET_TIME
>   	  Provide the SetTime() runtime service at boottime. This service
>   	  can be used by an EFI application to adjust the real time clock.
>
> +config EFI_RUNTIME_SET_VIRTUAL_ADDRESS_MAP
> +	bool "runtime service: SetVirtualAddressMap"
> +	default n
> +	help
> +	  Enable SetVirtualAddressMap runtime service. This API will be
> +	  called by OS just before it enters into virtual address mode.
> +
>   config EFI_DEVICE_PATH_TO_TEXT
>   	bool "Device path to text protocol"
>   	default y
> diff --git a/lib/efi_loader/efi_runtime.c b/lib/efi_loader/efi_runtime.c
> index 9c50955c9bd0..60442cb21d37 100644
> --- a/lib/efi_loader/efi_runtime.c
> +++ b/lib/efi_loader/efi_runtime.c
> @@ -374,10 +374,12 @@ static const struct efi_runtime_detach_list_struct efi_runtime_detach_list[] = {
>   		/* do_reset is gone */
>   		.ptr = &efi_runtime_services.reset_system,
>   		.patchto = efi_reset_system,
> +#ifdef CONFIG_RUNTIME_SET_VIRTUAL_ADDRESS_MAP
>   	}, {
>   		/* invalidate_*cache_all are gone */
>   		.ptr = &efi_runtime_services.set_virtual_address_map,
>   		.patchto = &efi_unimplemented,
> +#endif
>   	}, {
>   		/* RTC accessors are gone */
>   		.ptr = &efi_runtime_services.get_time,
> @@ -512,6 +514,7 @@ void efi_runtime_relocate(ulong offset, struct efi_mem_desc *map)
>           invalidate_icache_all();
>   }
>
> +#ifdef CONFIG_RUNTIME_SET_VIRTUAL_ADDRESS_MAP
>   /**
>    * efi_set_virtual_address_map() - change from physical to virtual mapping
>    *
> @@ -619,6 +622,7 @@ static efi_status_t EFIAPI efi_set_virtual_address_map(
>
>   	return EFI_EXIT(EFI_INVALID_PARAMETER);
>   }
> +#endif /* CONFIG_RUNTIME_SET_VIRTUAL_ADDRESS_MAP */
>
>   /**
>    * efi_add_runtime_mmio() - add memory-mapped IO region
> @@ -796,7 +800,11 @@ struct efi_runtime_services __efi_runtime_data efi_runtime_services = {
>   	.set_time = &efi_set_time_boottime,
>   	.get_wakeup_time = (void *)&efi_unimplemented,
>   	.set_wakeup_time = (void *)&efi_unimplemented,
> +#ifdef CONFIG_RUNTIME_SET_VIRTUAL_ADDRESS_MAP
>   	.set_virtual_address_map = &efi_set_virtual_address_map,
> +#else
> +	.set_virtual_address_map = (void *)&efi_unimplemented,

Depending on the ABI it is not save to use a function with another set
of parameters.

Best regards

Heinrich

> +#endif
>   	.convert_pointer = (void *)&efi_invalid_parameter,
>   	.get_variable = efi_get_variable,
>   	.get_next_variable_name = efi_get_next_variable_name,
>

^ permalink raw reply	[flat|nested] 32+ messages in thread

* [U-Boot] [RFC 1/6] efi_loader: runtime: make SetVirtualAddressMap configurable
  2019-06-15 19:46   ` Heinrich Schuchardt
@ 2019-06-15 21:14     ` Mark Kettenis
  2019-06-16 21:52       ` Heinrich Schuchardt
  2019-06-17  1:05     ` AKASHI Takahiro
  1 sibling, 1 reply; 32+ messages in thread
From: Mark Kettenis @ 2019-06-15 21:14 UTC (permalink / raw)
  To: u-boot

> From: Heinrich Schuchardt <xypron.glpk@gmx.de>
> Date: Sat, 15 Jun 2019 21:46:02 +0200
> 
> On 6/5/19 6:21 AM, AKASHI Takahiro wrote:
> > OS does not always need to call SetVirtualAddressMap.
> > (Ard confirmed this on arm64 linux.)
> > So let this API configurable. If disabled, it will return EFI_UNSUPPORTED
> > as UEFI specification requires.
> 
> Currently we do not support this scenario. Alex's patch should go in first.

OpenBSD/arm64 will always call this function.  It does this in order
to randomize the virtual addresses used by runtime services to make it
harder for an attacker to call into UEFI runtime services.  Note that
the UEFI 2.7 standard provides no indication that this interface might
be optional, so I don't think OpenBSD is doing anything wrong here.

I think it is unwise to make this API configurable.  But disabling it
by default like this diff does would be a seriously bad idea.  It
means U-Boot would no longer be backwards compatible with UEFI 2.7.


> > Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org>
> > ---
> >   lib/efi_loader/Kconfig       | 7 +++++++
> >   lib/efi_loader/efi_runtime.c | 8 ++++++++
> >   2 files changed, 15 insertions(+)
> >
> > diff --git a/lib/efi_loader/Kconfig b/lib/efi_loader/Kconfig
> > index 8bf4b1754d06..bb9c7582b14d 100644
> > --- a/lib/efi_loader/Kconfig
> > +++ b/lib/efi_loader/Kconfig
> > @@ -44,6 +44,13 @@ config EFI_SET_TIME
> >   	  Provide the SetTime() runtime service at boottime. This service
> >   	  can be used by an EFI application to adjust the real time clock.
> >
> > +config EFI_RUNTIME_SET_VIRTUAL_ADDRESS_MAP
> > +	bool "runtime service: SetVirtualAddressMap"
> > +	default n
> > +	help
> > +	  Enable SetVirtualAddressMap runtime service. This API will be
> > +	  called by OS just before it enters into virtual address mode.
> > +
> >   config EFI_DEVICE_PATH_TO_TEXT
> >   	bool "Device path to text protocol"
> >   	default y
> > diff --git a/lib/efi_loader/efi_runtime.c b/lib/efi_loader/efi_runtime.c
> > index 9c50955c9bd0..60442cb21d37 100644
> > --- a/lib/efi_loader/efi_runtime.c
> > +++ b/lib/efi_loader/efi_runtime.c
> > @@ -374,10 +374,12 @@ static const struct efi_runtime_detach_list_struct efi_runtime_detach_list[] = {
> >   		/* do_reset is gone */
> >   		.ptr = &efi_runtime_services.reset_system,
> >   		.patchto = efi_reset_system,
> > +#ifdef CONFIG_RUNTIME_SET_VIRTUAL_ADDRESS_MAP
> >   	}, {
> >   		/* invalidate_*cache_all are gone */
> >   		.ptr = &efi_runtime_services.set_virtual_address_map,
> >   		.patchto = &efi_unimplemented,
> > +#endif
> >   	}, {
> >   		/* RTC accessors are gone */
> >   		.ptr = &efi_runtime_services.get_time,
> > @@ -512,6 +514,7 @@ void efi_runtime_relocate(ulong offset, struct efi_mem_desc *map)
> >           invalidate_icache_all();
> >   }
> >
> > +#ifdef CONFIG_RUNTIME_SET_VIRTUAL_ADDRESS_MAP
> >   /**
> >    * efi_set_virtual_address_map() - change from physical to virtual mapping
> >    *
> > @@ -619,6 +622,7 @@ static efi_status_t EFIAPI efi_set_virtual_address_map(
> >
> >   	return EFI_EXIT(EFI_INVALID_PARAMETER);
> >   }
> > +#endif /* CONFIG_RUNTIME_SET_VIRTUAL_ADDRESS_MAP */
> >
> >   /**
> >    * efi_add_runtime_mmio() - add memory-mapped IO region
> > @@ -796,7 +800,11 @@ struct efi_runtime_services __efi_runtime_data efi_runtime_services = {
> >   	.set_time = &efi_set_time_boottime,
> >   	.get_wakeup_time = (void *)&efi_unimplemented,
> >   	.set_wakeup_time = (void *)&efi_unimplemented,
> > +#ifdef CONFIG_RUNTIME_SET_VIRTUAL_ADDRESS_MAP
> >   	.set_virtual_address_map = &efi_set_virtual_address_map,
> > +#else
> > +	.set_virtual_address_map = (void *)&efi_unimplemented,
> 
> Depending on the ABI it is not save to use a function with another set
> of parameters.
> 
> Best regards
> 
> Heinrich
> 
> > +#endif
> >   	.convert_pointer = (void *)&efi_invalid_parameter,
> >   	.get_variable = efi_get_variable,
> >   	.get_next_variable_name = efi_get_next_variable_name,
> >
> 
> _______________________________________________
> U-Boot mailing list
> U-Boot at lists.denx.de
> https://lists.denx.de/listinfo/u-boot

^ permalink raw reply	[flat|nested] 32+ messages in thread

* [U-Boot] [RFC 1/6] efi_loader: runtime: make SetVirtualAddressMap configurable
  2019-06-15 21:14     ` Mark Kettenis
@ 2019-06-16 21:52       ` Heinrich Schuchardt
  2019-06-18 18:11         ` Mark Kettenis
  0 siblings, 1 reply; 32+ messages in thread
From: Heinrich Schuchardt @ 2019-06-16 21:52 UTC (permalink / raw)
  To: u-boot

On 6/15/19 11:14 PM, Mark Kettenis wrote:
>> From: Heinrich Schuchardt <xypron.glpk@gmx.de>
>> Date: Sat, 15 Jun 2019 21:46:02 +0200
>>
>> On 6/5/19 6:21 AM, AKASHI Takahiro wrote:
>>> OS does not always need to call SetVirtualAddressMap.
>>> (Ard confirmed this on arm64 linux.)
>>> So let this API configurable. If disabled, it will return EFI_UNSUPPORTED
>>> as UEFI specification requires.
>>
>> Currently we do not support this scenario. Alex's patch should go in first.
>
> OpenBSD/arm64 will always call this function.  It does this in order

OpenBSD/arm32 does not call it. I have no clue why the 32bit and 64bit
code have diverged.

> to randomize the virtual addresses used by runtime services to make it
> harder for an attacker to call into UEFI runtime services.  Note that
> the UEFI 2.7 standard provides no indication that this interface might
> be optional, so I don't think OpenBSD is doing anything wrong here.
>
> I think it is unwise to make this API configurable.  But disabling it
> by default like this diff does would be a seriously bad idea.  It
> means U-Boot would no longer be backwards compatible with UEFI 2.7.

The current UEFI specification is 2.8. It foresees that
SetVirtualAddressMap() can be marked as unsupported but that does not
imply that we have to make it customizable. And disabling it by default
does not make much sense while most operating systems use it.

Best regards

Heinrich

>
>
>>> Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org>
>>> ---
>>>    lib/efi_loader/Kconfig       | 7 +++++++
>>>    lib/efi_loader/efi_runtime.c | 8 ++++++++
>>>    2 files changed, 15 insertions(+)
>>>
>>> diff --git a/lib/efi_loader/Kconfig b/lib/efi_loader/Kconfig
>>> index 8bf4b1754d06..bb9c7582b14d 100644
>>> --- a/lib/efi_loader/Kconfig
>>> +++ b/lib/efi_loader/Kconfig
>>> @@ -44,6 +44,13 @@ config EFI_SET_TIME
>>>    	  Provide the SetTime() runtime service at boottime. This service
>>>    	  can be used by an EFI application to adjust the real time clock.
>>>
>>> +config EFI_RUNTIME_SET_VIRTUAL_ADDRESS_MAP
>>> +	bool "runtime service: SetVirtualAddressMap"
>>> +	default n
>>> +	help
>>> +	  Enable SetVirtualAddressMap runtime service. This API will be
>>> +	  called by OS just before it enters into virtual address mode.
>>> +
>>>    config EFI_DEVICE_PATH_TO_TEXT
>>>    	bool "Device path to text protocol"
>>>    	default y
>>> diff --git a/lib/efi_loader/efi_runtime.c b/lib/efi_loader/efi_runtime.c
>>> index 9c50955c9bd0..60442cb21d37 100644
>>> --- a/lib/efi_loader/efi_runtime.c
>>> +++ b/lib/efi_loader/efi_runtime.c
>>> @@ -374,10 +374,12 @@ static const struct efi_runtime_detach_list_struct efi_runtime_detach_list[] = {
>>>    		/* do_reset is gone */
>>>    		.ptr = &efi_runtime_services.reset_system,
>>>    		.patchto = efi_reset_system,
>>> +#ifdef CONFIG_RUNTIME_SET_VIRTUAL_ADDRESS_MAP
>>>    	}, {
>>>    		/* invalidate_*cache_all are gone */
>>>    		.ptr = &efi_runtime_services.set_virtual_address_map,
>>>    		.patchto = &efi_unimplemented,
>>> +#endif
>>>    	}, {
>>>    		/* RTC accessors are gone */
>>>    		.ptr = &efi_runtime_services.get_time,
>>> @@ -512,6 +514,7 @@ void efi_runtime_relocate(ulong offset, struct efi_mem_desc *map)
>>>            invalidate_icache_all();
>>>    }
>>>
>>> +#ifdef CONFIG_RUNTIME_SET_VIRTUAL_ADDRESS_MAP
>>>    /**
>>>     * efi_set_virtual_address_map() - change from physical to virtual mapping
>>>     *
>>> @@ -619,6 +622,7 @@ static efi_status_t EFIAPI efi_set_virtual_address_map(
>>>
>>>    	return EFI_EXIT(EFI_INVALID_PARAMETER);
>>>    }
>>> +#endif /* CONFIG_RUNTIME_SET_VIRTUAL_ADDRESS_MAP */
>>>
>>>    /**
>>>     * efi_add_runtime_mmio() - add memory-mapped IO region
>>> @@ -796,7 +800,11 @@ struct efi_runtime_services __efi_runtime_data efi_runtime_services = {
>>>    	.set_time = &efi_set_time_boottime,
>>>    	.get_wakeup_time = (void *)&efi_unimplemented,
>>>    	.set_wakeup_time = (void *)&efi_unimplemented,
>>> +#ifdef CONFIG_RUNTIME_SET_VIRTUAL_ADDRESS_MAP
>>>    	.set_virtual_address_map = &efi_set_virtual_address_map,
>>> +#else
>>> +	.set_virtual_address_map = (void *)&efi_unimplemented,
>>
>> Depending on the ABI it is not save to use a function with another set
>> of parameters.
>>
>> Best regards
>>
>> Heinrich
>>
>>> +#endif
>>>    	.convert_pointer = (void *)&efi_invalid_parameter,
>>>    	.get_variable = efi_get_variable,
>>>    	.get_next_variable_name = efi_get_next_variable_name,
>>>
>>
>> _______________________________________________
>> U-Boot mailing list
>> U-Boot at lists.denx.de
>> https://lists.denx.de/listinfo/u-boot
>

^ permalink raw reply	[flat|nested] 32+ messages in thread

* [U-Boot] [RFC 1/6] efi_loader: runtime: make SetVirtualAddressMap configurable
  2019-06-15 19:46   ` Heinrich Schuchardt
  2019-06-15 21:14     ` Mark Kettenis
@ 2019-06-17  1:05     ` AKASHI Takahiro
  1 sibling, 0 replies; 32+ messages in thread
From: AKASHI Takahiro @ 2019-06-17  1:05 UTC (permalink / raw)
  To: u-boot

On Sat, Jun 15, 2019 at 09:46:02PM +0200, Heinrich Schuchardt wrote:
> On 6/5/19 6:21 AM, AKASHI Takahiro wrote:
> >OS does not always need to call SetVirtualAddressMap.
> >(Ard confirmed this on arm64 linux.)
> >So let this API configurable. If disabled, it will return EFI_UNSUPPORTED
> >as UEFI specification requires.
> 
> Currently we do not support this scenario. Alex's patch should go in first.
> 
> >
> >Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org>
> >---
> >  lib/efi_loader/Kconfig       | 7 +++++++
> >  lib/efi_loader/efi_runtime.c | 8 ++++++++
> >  2 files changed, 15 insertions(+)
> >
> >diff --git a/lib/efi_loader/Kconfig b/lib/efi_loader/Kconfig
> >index 8bf4b1754d06..bb9c7582b14d 100644
> >--- a/lib/efi_loader/Kconfig
> >+++ b/lib/efi_loader/Kconfig
> >@@ -44,6 +44,13 @@ config EFI_SET_TIME
> >  	  Provide the SetTime() runtime service at boottime. This service
> >  	  can be used by an EFI application to adjust the real time clock.
> >
> >+config EFI_RUNTIME_SET_VIRTUAL_ADDRESS_MAP
> >+	bool "runtime service: SetVirtualAddressMap"
> >+	default n
> >+	help
> >+	  Enable SetVirtualAddressMap runtime service. This API will be
> >+	  called by OS just before it enters into virtual address mode.
> >+
> >  config EFI_DEVICE_PATH_TO_TEXT
> >  	bool "Device path to text protocol"
> >  	default y
> >diff --git a/lib/efi_loader/efi_runtime.c b/lib/efi_loader/efi_runtime.c
> >index 9c50955c9bd0..60442cb21d37 100644
> >--- a/lib/efi_loader/efi_runtime.c
> >+++ b/lib/efi_loader/efi_runtime.c
> >@@ -374,10 +374,12 @@ static const struct efi_runtime_detach_list_struct efi_runtime_detach_list[] = {
> >  		/* do_reset is gone */
> >  		.ptr = &efi_runtime_services.reset_system,
> >  		.patchto = efi_reset_system,
> >+#ifdef CONFIG_RUNTIME_SET_VIRTUAL_ADDRESS_MAP
> >  	}, {
> >  		/* invalidate_*cache_all are gone */
> >  		.ptr = &efi_runtime_services.set_virtual_address_map,
> >  		.patchto = &efi_unimplemented,
> >+#endif
> >  	}, {
> >  		/* RTC accessors are gone */
> >  		.ptr = &efi_runtime_services.get_time,
> >@@ -512,6 +514,7 @@ void efi_runtime_relocate(ulong offset, struct efi_mem_desc *map)
> >          invalidate_icache_all();
> >  }
> >
> >+#ifdef CONFIG_RUNTIME_SET_VIRTUAL_ADDRESS_MAP
> >  /**
> >   * efi_set_virtual_address_map() - change from physical to virtual mapping
> >   *
> >@@ -619,6 +622,7 @@ static efi_status_t EFIAPI efi_set_virtual_address_map(
> >
> >  	return EFI_EXIT(EFI_INVALID_PARAMETER);
> >  }
> >+#endif /* CONFIG_RUNTIME_SET_VIRTUAL_ADDRESS_MAP */
> >
> >  /**
> >   * efi_add_runtime_mmio() - add memory-mapped IO region
> >@@ -796,7 +800,11 @@ struct efi_runtime_services __efi_runtime_data efi_runtime_services = {
> >  	.set_time = &efi_set_time_boottime,
> >  	.get_wakeup_time = (void *)&efi_unimplemented,
> >  	.set_wakeup_time = (void *)&efi_unimplemented,
> >+#ifdef CONFIG_RUNTIME_SET_VIRTUAL_ADDRESS_MAP
> >  	.set_virtual_address_map = &efi_set_virtual_address_map,
> >+#else
> >+	.set_virtual_address_map = (void *)&efi_unimplemented,
> 
> Depending on the ABI it is not save to use a function with another set
> of parameters.

I don't think it's a good practice, but there already are
a couple of precedents:
        get_wakeup_time
        set_wakeup_time
        get_next_high_mono_count, and
        convert_pointer

-Takahiro Akashi

> Best regards
> 
> Heinrich
> 
> >+#endif
> >  	.convert_pointer = (void *)&efi_invalid_parameter,
> >  	.get_variable = efi_get_variable,
> >  	.get_next_variable_name = efi_get_next_variable_name,
> >
> 

^ permalink raw reply	[flat|nested] 32+ messages in thread

* [U-Boot] [RFC 3/6] efi_loader: support convert_pointer at runtime
  2019-06-15 19:41   ` Heinrich Schuchardt
@ 2019-06-17  1:15     ` AKASHI Takahiro
  2019-06-17  5:41       ` Heinrich Schuchardt
  0 siblings, 1 reply; 32+ messages in thread
From: AKASHI Takahiro @ 2019-06-17  1:15 UTC (permalink / raw)
  To: u-boot

On Sat, Jun 15, 2019 at 09:41:31PM +0200, Heinrich Schuchardt wrote:
> On 6/5/19 6:21 AM, AKASHI Takahiro wrote:
> >With this patch, ConvertPointer runtime service is enabled.
> >This function will be useful only after SetVirtualAddressMap is called
> >and before it exits according to UEFI specification.
> 
> ConvertPointer() is called by drivers upon calling the notification
> functions of events registered as EVT_SIGNAL_VIRTUAL_ADDRESS_CHANGE.
> 
> We still lack support for these events.

So? What do you want me to do here?

> >
> >Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org>
> >---
> >  lib/efi_loader/Kconfig       |  8 ++++
> >  lib/efi_loader/efi_runtime.c | 81 ++++++++++++++++++++++++++----------
> >  2 files changed, 66 insertions(+), 23 deletions(-)
> >
> >diff --git a/lib/efi_loader/Kconfig b/lib/efi_loader/Kconfig
> >index bb9c7582b14d..e2ef43157568 100644
> >--- a/lib/efi_loader/Kconfig
> >+++ b/lib/efi_loader/Kconfig
> >@@ -51,6 +51,14 @@ config EFI_RUNTIME_SET_VIRTUAL_ADDRESS_MAP
> >  	  Enable SetVirtualAddressMap runtime service. This API will be
> >  	  called by OS just before it enters into virtual address mode.
> >
> >+config EFI_RUNTIME_CONVERT_POINTER
> >+	bool "runtime service: ConvertPointer"
> >+	default n
> >+	help
> >+	  Enable ConvertPointer runtime service. This API will be expected
> >+	  to be called by UEFI drivers in relocating themselves to virtual
> >+	  address space.
> >+
> >  config EFI_DEVICE_PATH_TO_TEXT
> >  	bool "Device path to text protocol"
> >  	default y
> >diff --git a/lib/efi_loader/efi_runtime.c b/lib/efi_loader/efi_runtime.c
> >index cf202bb9ec07..ff3684a4b692 100644
> >--- a/lib/efi_loader/efi_runtime.c
> >+++ b/lib/efi_loader/efi_runtime.c
> >@@ -27,7 +27,6 @@ LIST_HEAD(efi_runtime_mmio);
> >
> >  static efi_status_t __efi_runtime EFIAPI efi_unimplemented(void);
> >  static efi_status_t __efi_runtime EFIAPI efi_device_error(void);
> >-static efi_status_t __efi_runtime EFIAPI efi_invalid_parameter(void);
> >
> >  /*
> >   * TODO(sjg at chromium.org): These defines and structures should come from the ELF
> >@@ -108,6 +107,10 @@ efi_status_t efi_init_runtime_supported(void)
> >  	efi_runtime_services_supported |=
> >  				EFI_RT_SUPPORTED_SET_VIRTUAL_ADDRESS_MAP;
> >  #endif
> >+#ifdef CONFIG_EFI_RUNTIME_CONVERT_POINTER
> >+	efi_runtime_services_supported |=
> >+				EFI_RT_SUPPORTED_CONVERT_POINTER;
> >+#endif
> >
> >  	return EFI_CALL(efi_set_variable(L"RuntimeServicesSupported",
> >  					 &efi_global_variable_guid,
> >@@ -392,6 +395,39 @@ efi_status_t __weak __efi_runtime EFIAPI efi_set_time(struct efi_time *time)
> >  	return EFI_UNSUPPORTED;
> >  }
> >
> >+#ifdef CONFIG_EFI_RUNTIME_CONVERT_POINTER
> >+static struct efi_mem_desc *efi_virtmap __efi_runtime_data;
> >+static int efi_virtmap_num __efi_runtime_data;
> >+
> 
> Please, put a description of the function and its parameters here.

Okay.

> >+static efi_status_t __efi_runtime EFIAPI efi_convert_pointer(unsigned long dbg,
> >+							     void **address)
> >+{
> >+	struct efi_mem_desc *map;
> >+	efi_physical_addr_t addr;
> >+	int i;
> >+
> >+	if (!efi_virtmap)
> >+		return EFI_UNSUPPORTED;
> >+
> >+	if (!address)
> >+		return EFI_INVALID_PARAMETER;
> >+
> >+	for (i = 0, map = efi_virtmap; i < efi_virtmap_num; i++, map++) {
> >+		addr = (efi_physical_addr_t)*address;
> This line should be before the loop.
> 
> >+		if (addr >= map->physical_start &&
> >+		    (addr < (map->physical_start
> 
> %s/(addr/addr/  No need for that extra parenthesis.
> 
> >+			     + (map->num_pages << EFI_PAGE_SHIFT)))) {
> >+			*address = (void *)map->virtual_start;
> 
> I guess on 32bit this will end in a warning? Wouldn't you need to
> convert to uintptr_t first?
> 
> >+			*address += addr - map->physical_start;
> 
> I think a single assignment is enough. Here you are updating a 32bit
> pointer with the difference of two u64. I would expect a warning.

I will check.

> *address = (void *)(uintptr_t)
> (addr - map->physical_start + map->virtual_start);
> 
> Or do all calculation with addr before copying to *address.
> 
> >+
> >+			return EFI_SUCCESS;
> >+		}
> >+	}
> >+
> >+	return EFI_NOT_FOUND;
> >+}
> >+#endif
> >+
> >  struct efi_runtime_detach_list_struct {
> >  	void *ptr;
> >  	void *patchto;
> >@@ -599,6 +635,10 @@ static efi_status_t EFIAPI efi_set_virtual_address_map(
> >  		return EFI_EXIT(EFI_INVALID_PARAMETER);
> >  	}
> >
> >+	efi_virtmap = virtmap;
> >+	efi_virtmap_num = n;
> >+
> >+#if 0 /* FIXME: This code is fragile as calloc is used in add_runtime_mmio */
> >  	/* Rebind mmio pointers */
> >  	for (i = 0; i < n; i++) {
> >  		struct efi_mem_desc *map = (void*)virtmap +
> >@@ -622,14 +662,14 @@ static efi_status_t EFIAPI efi_set_virtual_address_map(
> >  				*lmmio->ptr = (void *)new_addr;
> >  			}
> >  		}
> >-		if ((map_start <= (uintptr_t)systab.tables) &&
> >-		    (map_end >= (uintptr_t)systab.tables)) {
> >-			char *ptr = (char *)systab.tables;
> >-
> >-			ptr += off;
> >-			systab.tables = (struct efi_configuration_table *)ptr;
> >-		}
> 
> This looks like an unrelated change.

It does.
This code should be replaced by:
> >+	/* FIXME */
> >+	efi_convert_pointer(0, (void **)&systab.tables);

-Takahiro Akashi

> Put it into a separate patch, please.
> 
> Best regards
> 
> Heinrich
> 
> >  	}
> >+#endif
> >+
> >+	/* FIXME */
> >+	efi_convert_pointer(0, (void **)&systab.tables);
> >+
> >+	/* All fixes must be done before this line */
> >+	efi_virtmap = NULL;
> >
> >  	/* Move the actual runtime code over */
> >  	for (i = 0; i < n; i++) {
> >@@ -644,6 +684,11 @@ static efi_status_t EFIAPI efi_set_virtual_address_map(
> >  			/* Once we're virtual, we can no longer handle
> >  			   complex callbacks */
> >  			efi_runtime_detach(new_offset);
> >+
> >+			/*
> >+			 * FIXME:
> >+			 * We can no longer update RuntimeServicesSupported.
> >+			 */
> >  			return EFI_EXIT(EFI_SUCCESS);
> >  		}
> >  	}
> >@@ -733,20 +778,6 @@ static efi_status_t __efi_runtime EFIAPI efi_device_error(void)
> >  	return EFI_DEVICE_ERROR;
> >  }
> >
> >-/**
> >- * efi_invalid_parameter() - replacement function, returns EFI_INVALID_PARAMETER
> >- *
> >- * This function is used after SetVirtualAddressMap() is called as replacement
> >- * for services that are not available anymore due to constraints of the U-Boot
> >- * implementation.
> >- *
> >- * Return:	EFI_INVALID_PARAMETER
> >- */
> >-static efi_status_t __efi_runtime EFIAPI efi_invalid_parameter(void)
> >-{
> >-	return EFI_INVALID_PARAMETER;
> >-}
> >-
> >  /**
> >   * efi_update_capsule() - process information from operating system
> >   *
> >@@ -833,7 +864,11 @@ struct efi_runtime_services __efi_runtime_data efi_runtime_services = {
> >  #else
> >  	.set_virtual_address_map = (void *)&efi_unimplemented,
> >  #endif
> >-	.convert_pointer = (void *)&efi_invalid_parameter,
> >+#ifdef CONFIG_EFI_RUNTIME_CONVERT_POINTER
> >+	.convert_pointer = &efi_convert_pointer,
> >+#else
> >+	.convert_pointer = (void *)&efi_unimplemented,
> 
> I feel uneasy using a function efi_unimplemented() with a different
> number of parameters here. Depending on the ABI this may lead to a crash.
> 
> Best regards
> 
> Heinrich
> 
> >+#endif
> >  	.get_variable = efi_get_variable,
> >  	.get_next_variable_name = efi_get_next_variable_name,
> >  	.set_variable = efi_set_variable,
> >
> 

^ permalink raw reply	[flat|nested] 32+ messages in thread

* [U-Boot] [RFC 6/6] efi_loader: variable: support runtime variable access via cache
  2019-06-15 19:01   ` Heinrich Schuchardt
@ 2019-06-17  1:51     ` AKASHI Takahiro
  2019-06-17 19:52       ` Heinrich Schuchardt
  0 siblings, 1 reply; 32+ messages in thread
From: AKASHI Takahiro @ 2019-06-17  1:51 UTC (permalink / raw)
  To: u-boot

On Sat, Jun 15, 2019 at 09:01:56PM +0200, Heinrich Schuchardt wrote:
> On 6/5/19 6:21 AM, AKASHI Takahiro wrote:
> >With this patch, cache buffer for UEFI variables will be created
> >so that we will still be able to access, at least retrieve,
> >UEFI variables when we exit from boottime services,
> >
> >This feature is a "should" behavior described in EBBR v1.0
> >section 2.5.3.
> >
> >Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org>
> >---
> >  include/efi_loader.h          |  17 ++
> >  lib/efi_loader/Kconfig        |   9 +
> >  lib/efi_loader/efi_boottime.c |  10 +-
> >  lib/efi_loader/efi_runtime.c  |  13 +
> >  lib/efi_loader/efi_variable.c | 467 ++++++++++++++++++++++++++++++++++
> >  5 files changed, 515 insertions(+), 1 deletion(-)
> 
> Please, put the cache into a separate file.

Why?

> >
> >diff --git a/include/efi_loader.h b/include/efi_loader.h
> >index 93f7ece814a0..acab657b9d70 100644
> >--- a/include/efi_loader.h
> >+++ b/include/efi_loader.h
> >@@ -620,6 +620,23 @@ efi_status_t EFIAPI efi_set_variable(u16 *variable_name,
> >  				     const efi_guid_t *vendor, u32 attributes,
> >  				     efi_uintn_t data_size, const void *data);
> >
> >+#ifdef CONFIG_EFI_RUNTIME_GET_VARIABLE_CACHING
> >+efi_status_t efi_freeze_variable_table(void);
> >+
> >+/* runtime version of APIs */
> >+efi_status_t
> >+__efi_runtime EFIAPI efi_get_variable_runtime(u16 *variable_name,
> 
> I think one version of the functions serving at runtime and boottime is
> enough.
> 
> The cache should be used both at runtime and at boottime.

So do you mean that we should replace the existing "boottime" version
of get/set_variable with my code (algorithm)?

This is a bit complicated work because we should be able to *udpate*
UEFI variables at boottime, but my version of hsearch_runtime() is
a stripped (and modified) version and doesn't support it.

Making the existing hsearch_r() executable at UEFI runtime is,
as I said before, quite painful.

> Essentially I
> expect three modules working together:
> 
> UEFI API implementation <-> Cache <-> Persistence driver
> 
> I would suggest to put each of these into a separate file.
> 
> Both the API implementation and the Cache have to be available at
> Boottime and at Runtime. A first version of the persistence driver may
> only be working at boottime.

Unfortunately, this is not practical right now because there is
already some sort of assumption (and consensus) that we would re-use
"Standalone MM services", which is already there in EDK2, as
secure storage for UEFI variables.
In the case, all the cache would be bypassed.
In my old prototype, I utilized the cache but dropped that feature
for several reasons.

> The NV-cache content should be written to non-volatile memory on Reset()
> and on ExitBootServices() and if possible when updating variables at
> runtime.

I'm not sure your intent here, but are you going to write back
the cache only once?
It won't work as every change of UEFI variable must be flushed
to persistent storage instantly.

> >+					      const efi_guid_t *vendor,
> >+					      u32 *attributes,
> >+					      efi_uintn_t *data_size,
> >+					      void *data);
> >+efi_status_t
> >+__efi_runtime EFIAPI efi_get_next_variable_name_runtime(
> >+						efi_uintn_t *variable_name_size,
> >+						u16 *variable_name,
> >+						const efi_guid_t *vendor);
> >+#endif /* CONFIG_EFI_RUNTIME_GET_VARIABLE_CACHING */
> >+
> >  /*
> >   * 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:
> >diff --git a/lib/efi_loader/Kconfig b/lib/efi_loader/Kconfig
> >index e2ef43157568..3f284795648f 100644
> >--- a/lib/efi_loader/Kconfig
> >+++ b/lib/efi_loader/Kconfig
> >@@ -59,6 +59,15 @@ config EFI_RUNTIME_CONVERT_POINTER
> >  	  to be called by UEFI drivers in relocating themselves to virtual
> >  	  address space.
> >
> >+config EFI_RUNTIME_GET_VARIABLE_CACHING
> >+	bool "runtime_service: GetVariable: Enable runtime access via cache (read-only)"
> >+	default y
> >+	help
> >+	  Select this option if you want to access UEFI variables at
> >+	  runtime even though you cannot update values on the fly.
> >+	  With or without this option, you can access UEFI variables
> >+	  at boottime.
> 
> Updates of volatile variables should always be possible.

Why "should"?
Give me any use case.
UEFI spec does not describe such a variant implementation at all.

> >+
> >  config EFI_DEVICE_PATH_TO_TEXT
> >  	bool "Device path to text protocol"
> >  	default y
> >diff --git a/lib/efi_loader/efi_boottime.c b/lib/efi_loader/efi_boottime.c
> >index e4abaf3601d9..14e343abbd43 100644
> >--- a/lib/efi_loader/efi_boottime.c
> >+++ b/lib/efi_loader/efi_boottime.c
> >@@ -1892,6 +1892,9 @@ static efi_status_t EFIAPI efi_exit_boot_services(efi_handle_t image_handle,
> >  						  efi_uintn_t map_key)
> >  {
> >  	struct efi_event *evt;
> >+#ifdef CONFIG_EFI_RUNTIME_GET_VARIABLE_CACHING
> >+	efi_status_t ret;
> >+#endif
> >
> >  	EFI_ENTRY("%p, %zx", image_handle, map_key);
> >
> >@@ -1921,7 +1924,12 @@ static efi_status_t EFIAPI efi_exit_boot_services(efi_handle_t image_handle,
> >  		}
> >  	}
> >
> >-	/* TODO: Should persist EFI variables here */
> >+#ifdef CONFIG_EFI_RUNTIME_GET_VARIABLE_CACHING
> 
> Can we have weak functions for initializing and persisting the cache, e.g.
> 
> efi_status_t __weak
> efi_load_variable_cache(cache_entry *cache, size_t *size)
> {
>         cache->len = 0;
>         return EFI_SUCCESS;
> }
> 
> efi_status_t __runtime __weak
> efi_write_variable_cache(cache_entry *cache, size_t size)
> {
>         return EFI_UNSUPPORTED;
> }
> 
> Then we can override these in whatever driver we implement.

What is the difference between yours and my env_efi_load/save()
with backing-storage driver?

> 
> >+	/* No more variable update */
> >+	ret = efi_freeze_variable_table();
> >+	if (ret != EFI_SUCCESS)
> >+		return EFI_EXIT(ret);
> >+#endif
> >
> >  	board_quiesce_devices();
> >
> >diff --git a/lib/efi_loader/efi_runtime.c b/lib/efi_loader/efi_runtime.c
> >index fc5bdee80e00..b60f70f04613 100644
> >--- a/lib/efi_loader/efi_runtime.c
> >+++ b/lib/efi_loader/efi_runtime.c
> >@@ -111,6 +111,11 @@ efi_status_t efi_init_runtime_supported(void)
> >  	efi_runtime_services_supported |=
> >  				EFI_RT_SUPPORTED_CONVERT_POINTER;
> >  #endif
> >+#ifdef CONFIG_EFI_RUNTIME_GET_VARIABLE_CACHING
> >+	efi_runtime_services_supported |=
> >+				(EFI_RT_SUPPORTED_GET_VARIABLE |
> >+				 EFI_RT_SUPPORTED_GET_NEXT_VARIABLE_NAME);
> >+#endif
> >
> >  	return EFI_CALL(efi_set_variable(L"RuntimeServicesSupported",
> >  					 &efi_global_variable_guid,
> >@@ -469,10 +474,18 @@ static struct efi_runtime_detach_list_struct efi_runtime_detach_list[] = {
> >  		.patchto = NULL,
> >  	}, {
> >  		.ptr = &efi_runtime_services.get_variable,
> >+#ifdef CONFIG_EFI_RUNTIME_GET_VARIABLE_CACHING
> >+		.patchto = &efi_get_variable_runtime,
> >+#else
> >  		.patchto = &efi_device_error,
> >+#endif
> >  	}, {
> >  		.ptr = &efi_runtime_services.get_next_variable_name,
> >+#ifdef CONFIG_EFI_RUNTIME_GET_VARIABLE_CACHING
> >+		.patchto = &efi_get_next_variable_name,
> >+#else
> >  		.patchto = &efi_device_error,
> >+#endif
> >  	}, {
> >  		.ptr = &efi_runtime_services.set_variable,
> >  		.patchto = &efi_device_error,
> >diff --git a/lib/efi_loader/efi_variable.c b/lib/efi_loader/efi_variable.c
> >index d9887be938c2..ee21892dd291 100644
> >--- a/lib/efi_loader/efi_variable.c
> >+++ b/lib/efi_loader/efi_variable.c
> >@@ -706,3 +706,470 @@ efi_status_t EFIAPI efi_set_variable(u16 *variable_name,
> >
> >  	return EFI_EXIT(ret);
> >  }
> >+
> >+#ifdef CONFIG_EFI_RUNTIME_GET_VARIABLE_CACHING
> >+/*
> >+ * runtime version of APIs
> >+ * We only support read-only variable access.
> >+ * The table is in U-Boot's hash table format, but has its own
> >+ * _ENTRY structure for specific use.
> >+ *
> >+ * Except for efi_freeze_variable_table(), which is to be called in
> >+ * exit_boot_services(), all the functions and data below must be
> >+ * placed in either RUNTIME_SERVICES_CODE or RUNTIME_SERVICES_DATA.
> >+ */
> >+typedef struct _ENTRY {
> >+	unsigned int used;	/* hash value; 0 for not used */
> >+	size_t name;		/* name offset from itself */
> >+	efi_guid_t vendor;
> >+	u32 attributes;
> >+	size_t data;		/* data offset from itself */
> >+	size_t data_size;
> >+} _ENTRY;
> >+
> >+static inline u16 *entry_name(_ENTRY *e) { return (void *)e + e->name; }
> >+static inline u16 *entry_data(_ENTRY *e) { return (void *)e + e->data; }
> >+
> >+static struct hsearch_data *efi_variable_table __efi_runtime_data;
> >+
> >+static size_t __efi_runtime u16_strlen_runtime(const u16 *s1)
> 
> 
> Please, do not duplicate existing functions. If they have to be runtime
> simply change the existing function to __runtime.

We should do that if possible, but please note that [str|mem]xxx() functions
are *architecture* dependent.
Do you want to mark all the functions across all the architectures?

> >+{
> >+	size_t n = 0;
> >+
> >+	while (*s1) {
> >+		n++;
> >+		s1++;
> >+	}
> >+
> >+	return n;
> >+}
> >+
> >+static int __efi_runtime memcmp_runtime(const void *m1, const void *m2,
> >+					size_t n)
> 
> I dislike duplicate code. Can't we simply define the existing memcmp
> function as __runtime?

ditto

> >+{
> >+	while (n && *(u8 *)m1 == *(u8 *)m2) {
> >+		n--;
> >+		m1++;
> >+		m2++;
> >+	}
> >+
> >+	if (n)
> >+		return *(u8 *)m1 - *(u8 *)m2;
> >+
> >+	return 0;
> >+}
> >+
> >+static void __efi_runtime memcpy_runtime(void *m1, const void *m2, size_t n)
> >+{
> 
> Can't we simply define the existing memcpy function as __runtime?
> 
> >+	for (; n; n--, m1++, m2++)
> >+		*(u8 *)m1 = *(u8 *)m2;
> >+}
> >+
> >+static int __efi_runtime efi_cmpkey(_ENTRY *e, const u16 *name,
> >+				    const efi_guid_t *vendor)
> >+{
> >+	size_t name_len;
> >+
> >+	name_len = u16_strlen_runtime(entry_name(e));
> >+
> >+	/* return zero if matched */
> >+	return name_len != u16_strlen_runtime(name) ||
> >+	       memcmp_runtime(entry_name(e), name, name_len * 2) ||
> >+	       memcmp_runtime(e->vendor.b, vendor->b, sizeof(vendor));
> >+}
> >+
> >+/* simplified and slightly different version of hsearch_r() */
> 
> These hash functions are so complicated that they really need a unit
> test testing them rigorously.

Do you know that there are no any tests for hsearch_r()?
Moreover, this function would better be exercised well
if we could add *runtime* tests.

> 
> >+static int __efi_runtime hsearch_runtime(const u16 *name,
> >+					 const efi_guid_t *vendor,
> >+					 ACTION action,
> >+					 _ENTRY **retval,
> >+					 struct hsearch_data *htab)
> >+{
> >+	unsigned int hval;
> >+	unsigned int count;
> >+	unsigned int len;
> >+	unsigned int idx, new;
> >+
> >+	/* Compute an value for the given string. */
> >+	len = u16_strlen_runtime(name);
> 
> Can't the same variable name exist for different GUIDs? Why is the GUID
> not considered in the hash?

efi_cmpkey() does take into consideration GUID as well as the name.

> >+	hval = len;
> >+	count = len;
> >+	while (count-- > 0) {
> >+		hval <<= 4;
> >+		hval += name[count];
> >+	}
> >+
> >+	/*
> >+	 * First hash function:
> >+	 * simply take the modulo but prevent zero.
> >+	 */
> >+	hval %= htab->size;
> >+	if (hval == 0)
> >+		++hval;
> >+
> >+	/* The first index tried. */
> >+	new = -1; /* not found */
> >+	idx = hval;
> >+
> >+	if (htab->table[idx].used) {
> >+		/*
> >+		 * Further action might be required according to the
> >+		 * action value.
> >+		 */
> >+		unsigned int hval2;
> >+
> >+		if (htab->table[idx].used == hval &&
> >+		    !efi_cmpkey(&htab->table[idx], name, vendor)) {
> >+			if (action == FIND) {
> >+				*retval = &htab->table[idx];
> >+				return idx;
> >+			}
> >+			/* we don't need to support overwrite */
> >+			return -1;
> >+		}
> >+
> >+		/*
> >+		 * Second hash function:
> >+		 * as suggested in [Knuth]
> >+		 */
> >+		hval2 = 1 + hval % (htab->size - 2);
> >+
> >+		do {
> >+			/*
> >+			 * Because SIZE is prime this guarantees to
> >+			 * step through all available indices.
> >+			 */
> >+			if (idx <= hval2)
> >+				idx = htab->size + idx - hval2;
> >+			else
> >+				idx -= hval2;
> >+
> >+			/*
> >+			 * If we visited all entries leave the loop
> >+			 * unsuccessfully.
> >+			 */
> >+			if (idx == hval)
> >+				break;
> >+
> >+			/* If entry is found use it. */
> >+			if (htab->table[idx].used == hval &&
> >+			    !efi_cmpkey(&htab->table[idx], name, vendor)) {
> >+				if (action == FIND) {
> >+					*retval = &htab->table[idx];
> >+					return idx;
> >+				}
> >+				/* we don't need to support overwrite */
> >+				return -1;
> >+			}
> >+		} while (htab->table[idx].used);
> >+
> >+		if (!htab->table[idx].used)
> >+			new = idx;
> >+	} else {
> >+		new = idx;
> >+	}
> >+
> >+	/*
> >+	 * An empty bucket has been found.
> >+	 * The following code should never be executed after
> >+	 * exit_boot_services()
> >+	 */
> >+	if (action == ENTER) {
> >+		/*
> >+		 * If table is full and another entry should be
> >+		 * entered return with error.
> >+		 */
> >+		if (htab->filled == htab->size) {
> >+			*retval = NULL;
> >+			return 0;
> >+		}
> >+
> >+		/* Create new entry */
> >+		htab->table[new].used = hval;
> >+		++htab->filled;
> >+
> >+		/* return new entry */
> >+		*retval = &htab->table[new];
> >+		return 1;
> >+	}
> >+
> >+	*retval = NULL;
> >+	return 0;
> >+}
> >+
> >+/* from lib/hashtable.c */
> >+static inline int isprime(unsigned int number)
> >+{
> >+	/* no even number will be passed */
> >+	unsigned int div = 3;
> >+
> >+	while (div * div < number && number % div != 0)
> >+		div += 2;
> >+
> >+	return number % div != 0;
> >+}
> >+
> >+efi_status_t efi_freeze_variable_table(void)
> 
> Please, add comments to your functions. It is not self-evident what this
> function is meant to do.
> 
> I cannot imagine why a variable cache should be frozen. It is a living
> data structure until the system is switched off.

I don't get your point.

> For a variable cache I expect that you allocate memory before handling
> the first variable and never again. At runtime you will not have chance
> to allocate memory anyway.

I don't get your point.

> This function is way too long. Pleae, break it down.

Freezing is implemented in 2-phase steps, and some complexity
is inevitable. I suppose adding some comments would be enough.

-Takahiro Akashi

> Best regards
> 
> Heinrich
> 
> >+{
> >+	int var_num = 0;
> >+	size_t var_data_size = 0;
> >+	u16 *name;
> >+	efi_uintn_t name_buf_len, name_len;
> >+	efi_guid_t vendor;
> >+	u32 attributes;
> >+	u8 *mem_pool, *var_buf = NULL;
> >+	size_t table_size, var_size, var_buf_size;
> >+	_ENTRY *new = NULL;
> >+	efi_status_t ret;
> >+
> >+	/* phase-1 loop */
> >+	name_buf_len = 128;
> >+	name = malloc(name_buf_len);
> >+	if (!name)
> >+		return EFI_OUT_OF_RESOURCES;
> >+	name[0] = 0;
> >+	for (;;) {
> >+		name_len = name_buf_len;
> >+		ret = EFI_CALL(efi_get_next_variable_name(&name_len, name,
> >+							  &vendor));
> >+		if (ret == EFI_NOT_FOUND) {
> >+			break;
> >+		} else if (ret == EFI_BUFFER_TOO_SMALL) {
> >+			u16 *buf;
> >+
> >+			name_buf_len = name_len;
> >+			buf = realloc(name, name_buf_len);
> >+			if (!buf) {
> >+				free(name);
> >+				return EFI_OUT_OF_RESOURCES;
> >+			}
> >+			name = buf;
> >+			name_len = name_buf_len;
> >+			ret = EFI_CALL(efi_get_next_variable_name(&name_len,
> >+								  name,
> >+								  &vendor));
> >+		}
> >+
> >+		if (ret != EFI_SUCCESS)
> >+			return ret;
> >+
> >+		var_size = 0;
> >+		ret = EFI_CALL(efi_get_variable(name, &vendor, &attributes,
> >+						&var_size, NULL));
> >+		if (ret != EFI_BUFFER_TOO_SMALL)
> >+			return ret;
> >+
> >+		if (!(attributes & EFI_VARIABLE_RUNTIME_ACCESS))
> >+			continue;
> >+
> >+		var_num++;
> >+		var_data_size += (u16_strlen_runtime(name) + 1) * sizeof(u16);
> >+		var_data_size += var_size;
> >+		/* mem_pool must 2-byte aligned for u16 variable name */
> >+		if (var_data_size & 0x1)
> >+			var_data_size++;
> >+	}
> >+
> >+	/*
> >+	 * total of entries in hash table must be a prime number.
> >+	 * The logic below comes from lib/hashtable.c
> >+	 */
> >+	var_num |= 1;               /* make odd */
> >+	while (!isprime(var_num))
> >+		var_num += 2;
> >+
> >+	/* We need table[var_num] for hsearch_runtime algo */
> >+	table_size = sizeof(*efi_variable_table)
> >+			+ sizeof(_ENTRY) * (var_num + 1) + var_data_size;
> >+	ret = efi_allocate_pool(EFI_RUNTIME_SERVICES_DATA,
> >+				table_size, (void **)&efi_variable_table);
> >+	if (ret != EFI_SUCCESS)
> >+		return ret;
> >+
> >+	efi_variable_table->size = var_num;
> >+	efi_variable_table->table = (void *)efi_variable_table
> >+					+ sizeof(*efi_variable_table);
> >+	mem_pool = (u8 *)efi_variable_table->table
> >+			+ sizeof(_ENTRY) * (var_num + 1);
> >+
> >+	var_buf_size = 128;
> >+	var_buf = malloc(var_buf_size);
> >+	if (!var_buf) {
> >+		ret = EFI_OUT_OF_RESOURCES;
> >+		goto err;
> >+	}
> >+
> >+	/* phase-2 loop */
> >+	name[0] = 0;
> >+	name_len = name_buf_len;
> >+	for (;;) {
> >+		name_len = name_buf_len;
> >+		ret = EFI_CALL(efi_get_next_variable_name(&name_len, name,
> >+							  &vendor));
> >+		if (ret == EFI_NOT_FOUND)
> >+			break;
> >+		else if (ret != EFI_SUCCESS)
> >+			goto err;
> >+
> >+		var_size = var_buf_size;
> >+		ret = EFI_CALL(efi_get_variable(name, &vendor, &attributes,
> >+						&var_size, var_buf));
> >+		if (ret == EFI_BUFFER_TOO_SMALL) {
> >+			free(var_buf);
> >+			var_buf_size = var_size;
> >+			var_buf = malloc(var_buf_size);
> >+			if (!var_buf) {
> >+				ret = EFI_OUT_OF_RESOURCES;
> >+				goto err;
> >+			}
> >+			ret = EFI_CALL(efi_get_variable(name, &vendor,
> >+							&attributes,
> >+							&var_size, var_buf));
> >+		}
> >+		if (ret != EFI_SUCCESS)
> >+			goto err;
> >+
> >+		if (!(attributes & EFI_VARIABLE_RUNTIME_ACCESS))
> >+			continue;
> >+
> >+		if (hsearch_runtime(name, &vendor, ENTER, &new,
> >+				    efi_variable_table) <= 0) {
> >+			/* This should not happen */
> >+			ret = EFI_INVALID_PARAMETER;
> >+			goto err;
> >+		}
> >+
> >+		/* allocate space from RUNTIME DATA */
> >+		name_len = (u16_strlen_runtime(name) + 1) * sizeof(u16);
> >+		memcpy_runtime(mem_pool, name, name_len);
> >+		new->name = mem_pool - (u8 *)new; /* offset */
> >+		mem_pool += name_len;
> >+
> >+		memcpy_runtime(&new->vendor.b, &vendor.b, sizeof(vendor));
> >+
> >+		new->attributes = attributes;
> >+
> >+		memcpy_runtime(mem_pool, var_buf, var_size);
> >+		new->data = mem_pool - (u8 *)new; /* offset */
> >+		new->data_size = var_size;
> >+		mem_pool += var_size;
> >+
> >+		/* mem_pool must 2-byte aligned for u16 variable name */
> >+		if ((uintptr_t)mem_pool & 0x1)
> >+			mem_pool++;
> >+	}
> >+#ifdef DEBUG
> >+	name[0] = 0;
> >+	name_len = name_buf_len;
> >+	for (;;) {
> >+		name_len = name_buf_len;
> >+		ret = efi_get_next_variable_name_runtime(&name_len, name,
> >+							 &vendor);
> >+		if (ret == EFI_NOT_FOUND)
> >+			break;
> >+		else if (ret != EFI_SUCCESS)
> >+			goto err;
> >+
> >+		var_size = var_buf_size;
> >+		ret = efi_get_variable_runtime(name, &vendor, &attributes,
> >+					       &var_size, var_buf);
> >+		if (ret != EFI_SUCCESS)
> >+			goto err;
> >+
> >+		printf("%ls_%pUl:\n", name, &vendor);
> >+		printf("    attributes: 0x%x\n", attributes);
> >+		printf("    value (size: 0x%lx)\n", var_size);
> >+	}
> >+#endif
> >+	ret = EFI_SUCCESS;
> >+
> >+err:
> >+	free(name);
> >+	free(var_buf);
> >+	if (ret != EFI_SUCCESS && efi_variable_table) {
> >+		efi_free_pool(efi_variable_table);
> >+		efi_variable_table = NULL;
> >+	}
> >+
> >+	return ret;
> >+}
> >+
> >+efi_status_t
> >+__efi_runtime EFIAPI efi_get_variable_runtime(u16 *variable_name,
> >+					      const efi_guid_t *vendor,
> >+					      u32 *attributes,
> >+					      efi_uintn_t *data_size,
> >+					      void *data)
> >+{
> >+	_ENTRY *new;
> >+
> >+	if (!variable_name || !vendor || !data_size)
> >+		return EFI_EXIT(EFI_INVALID_PARAMETER);
> >+
> >+	if (hsearch_runtime(variable_name, vendor, FIND, &new,
> >+			    efi_variable_table) <= 0)
> >+		return EFI_NOT_FOUND;
> >+
> >+	if (attributes)
> >+		*attributes = new->attributes;
> >+	if (*data_size < new->data_size) {
> >+		*data_size = new->data_size;
> >+		return EFI_BUFFER_TOO_SMALL;
> >+	}
> >+
> >+	*data_size = new->data_size;
> >+	memcpy_runtime(data, entry_data(new), new->data_size);
> >+
> >+	return EFI_SUCCESS;
> >+}
> >+
> >+static int prev_idx __efi_runtime_data;
> >+
> >+efi_status_t
> >+__efi_runtime EFIAPI efi_get_next_variable_name_runtime(
> >+						efi_uintn_t *variable_name_size,
> >+						u16 *variable_name,
> >+						const efi_guid_t *vendor)
> >+{
> >+	_ENTRY *e;
> >+	u16 *name;
> >+	efi_uintn_t name_size;
> >+
> >+	if (!variable_name_size || !variable_name || !vendor)
> >+		return EFI_INVALID_PARAMETER;
> >+
> >+	if (variable_name[0]) {
> >+		/* sanity check for previous variable */
> >+		if (prev_idx < 0)
> >+			return EFI_INVALID_PARAMETER;
> >+
> >+		e = &efi_variable_table->table[prev_idx];
> >+		if (!e->used || efi_cmpkey(e, variable_name, vendor))
> >+			return EFI_INVALID_PARAMETER;
> >+	} else {
> >+		prev_idx = -1;
> >+	}
> >+
> >+	/* next variable */
> >+	while (++prev_idx <= efi_variable_table->size) {
> >+		e = &efi_variable_table->table[prev_idx];
> >+		if (e->used)
> >+			break;
> >+	}
> >+	if (prev_idx > efi_variable_table->size)
> >+		return EFI_NOT_FOUND;
> >+
> >+	name = entry_name(e);
> >+	name_size = (u16_strlen_runtime(name) + 1)
> >+			* sizeof(u16);
> >+	if (*variable_name_size < name_size) {
> >+		*variable_name_size = name_size;
> >+		return EFI_BUFFER_TOO_SMALL;
> >+	}
> >+
> >+	memcpy_runtime(variable_name, name, name_size);
> >+	memcpy_runtime((void *)&vendor->b, &e->vendor.b, sizeof(vendor));
> >+
> >+	return EFI_SUCCESS;
> >+}
> >+#endif /* CONFIG_EFI_RUNTIME_GET_VARIABLE_CACHING */
> >
> 

^ permalink raw reply	[flat|nested] 32+ messages in thread

* [U-Boot] [RFC 3/6] efi_loader: support convert_pointer at runtime
  2019-06-17  1:15     ` AKASHI Takahiro
@ 2019-06-17  5:41       ` Heinrich Schuchardt
  2019-07-13  6:16         ` Heinrich Schuchardt
  0 siblings, 1 reply; 32+ messages in thread
From: Heinrich Schuchardt @ 2019-06-17  5:41 UTC (permalink / raw)
  To: u-boot

On 6/17/19 3:15 AM, AKASHI Takahiro wrote:
> On Sat, Jun 15, 2019 at 09:41:31PM +0200, Heinrich Schuchardt wrote:
>> On 6/5/19 6:21 AM, AKASHI Takahiro wrote:
>>> With this patch, ConvertPointer runtime service is enabled.
>>> This function will be useful only after SetVirtualAddressMap is called
>>> and before it exits according to UEFI specification.
>>
>> ConvertPointer() is called by drivers upon calling the notification
>> functions of events registered as EVT_SIGNAL_VIRTUAL_ADDRESS_CHANGE.
>>
>> We still lack support for these events.
>
> So? What do you want me to do here?

We will have to address this in a future patch.

Regards Heinrich

>
>>>
>>> Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org>
>>> ---
>>>   lib/efi_loader/Kconfig       |  8 ++++
>>>   lib/efi_loader/efi_runtime.c | 81 ++++++++++++++++++++++++++----------
>>>   2 files changed, 66 insertions(+), 23 deletions(-)
>>>
>>> diff --git a/lib/efi_loader/Kconfig b/lib/efi_loader/Kconfig
>>> index bb9c7582b14d..e2ef43157568 100644
>>> --- a/lib/efi_loader/Kconfig
>>> +++ b/lib/efi_loader/Kconfig
>>> @@ -51,6 +51,14 @@ config EFI_RUNTIME_SET_VIRTUAL_ADDRESS_MAP
>>>   	  Enable SetVirtualAddressMap runtime service. This API will be
>>>   	  called by OS just before it enters into virtual address mode.
>>>
>>> +config EFI_RUNTIME_CONVERT_POINTER
>>> +	bool "runtime service: ConvertPointer"
>>> +	default n
>>> +	help
>>> +	  Enable ConvertPointer runtime service. This API will be expected
>>> +	  to be called by UEFI drivers in relocating themselves to virtual
>>> +	  address space.
>>> +
>>>   config EFI_DEVICE_PATH_TO_TEXT
>>>   	bool "Device path to text protocol"
>>>   	default y
>>> diff --git a/lib/efi_loader/efi_runtime.c b/lib/efi_loader/efi_runtime.c
>>> index cf202bb9ec07..ff3684a4b692 100644
>>> --- a/lib/efi_loader/efi_runtime.c
>>> +++ b/lib/efi_loader/efi_runtime.c
>>> @@ -27,7 +27,6 @@ LIST_HEAD(efi_runtime_mmio);
>>>
>>>   static efi_status_t __efi_runtime EFIAPI efi_unimplemented(void);
>>>   static efi_status_t __efi_runtime EFIAPI efi_device_error(void);
>>> -static efi_status_t __efi_runtime EFIAPI efi_invalid_parameter(void);
>>>
>>>   /*
>>>    * TODO(sjg at chromium.org): These defines and structures should come from the ELF
>>> @@ -108,6 +107,10 @@ efi_status_t efi_init_runtime_supported(void)
>>>   	efi_runtime_services_supported |=
>>>   				EFI_RT_SUPPORTED_SET_VIRTUAL_ADDRESS_MAP;
>>>   #endif
>>> +#ifdef CONFIG_EFI_RUNTIME_CONVERT_POINTER
>>> +	efi_runtime_services_supported |=
>>> +				EFI_RT_SUPPORTED_CONVERT_POINTER;
>>> +#endif
>>>
>>>   	return EFI_CALL(efi_set_variable(L"RuntimeServicesSupported",
>>>   					 &efi_global_variable_guid,
>>> @@ -392,6 +395,39 @@ efi_status_t __weak __efi_runtime EFIAPI efi_set_time(struct efi_time *time)
>>>   	return EFI_UNSUPPORTED;
>>>   }
>>>
>>> +#ifdef CONFIG_EFI_RUNTIME_CONVERT_POINTER
>>> +static struct efi_mem_desc *efi_virtmap __efi_runtime_data;
>>> +static int efi_virtmap_num __efi_runtime_data;
>>> +
>>
>> Please, put a description of the function and its parameters here.
>
> Okay.
>
>>> +static efi_status_t __efi_runtime EFIAPI efi_convert_pointer(unsigned long dbg,
>>> +							     void **address)
>>> +{
>>> +	struct efi_mem_desc *map;
>>> +	efi_physical_addr_t addr;
>>> +	int i;
>>> +
>>> +	if (!efi_virtmap)
>>> +		return EFI_UNSUPPORTED;
>>> +
>>> +	if (!address)
>>> +		return EFI_INVALID_PARAMETER;
>>> +
>>> +	for (i = 0, map = efi_virtmap; i < efi_virtmap_num; i++, map++) {
>>> +		addr = (efi_physical_addr_t)*address;
>> This line should be before the loop.
>>
>>> +		if (addr >= map->physical_start &&
>>> +		    (addr < (map->physical_start
>>
>> %s/(addr/addr/  No need for that extra parenthesis.
>>
>>> +			     + (map->num_pages << EFI_PAGE_SHIFT)))) {
>>> +			*address = (void *)map->virtual_start;
>>
>> I guess on 32bit this will end in a warning? Wouldn't you need to
>> convert to uintptr_t first?
>>
>>> +			*address += addr - map->physical_start;
>>
>> I think a single assignment is enough. Here you are updating a 32bit
>> pointer with the difference of two u64. I would expect a warning.
>
> I will check.
>
>> *address = (void *)(uintptr_t)
>> (addr - map->physical_start + map->virtual_start);
>>
>> Or do all calculation with addr before copying to *address.
>>
>>> +
>>> +			return EFI_SUCCESS;
>>> +		}
>>> +	}
>>> +
>>> +	return EFI_NOT_FOUND;
>>> +}
>>> +#endif
>>> +
>>>   struct efi_runtime_detach_list_struct {
>>>   	void *ptr;
>>>   	void *patchto;
>>> @@ -599,6 +635,10 @@ static efi_status_t EFIAPI efi_set_virtual_address_map(
>>>   		return EFI_EXIT(EFI_INVALID_PARAMETER);
>>>   	}
>>>
>>> +	efi_virtmap = virtmap;
>>> +	efi_virtmap_num = n;
>>> +
>>> +#if 0 /* FIXME: This code is fragile as calloc is used in add_runtime_mmio */
>>>   	/* Rebind mmio pointers */
>>>   	for (i = 0; i < n; i++) {
>>>   		struct efi_mem_desc *map = (void*)virtmap +
>>> @@ -622,14 +662,14 @@ static efi_status_t EFIAPI efi_set_virtual_address_map(
>>>   				*lmmio->ptr = (void *)new_addr;
>>>   			}
>>>   		}
>>> -		if ((map_start <= (uintptr_t)systab.tables) &&
>>> -		    (map_end >= (uintptr_t)systab.tables)) {
>>> -			char *ptr = (char *)systab.tables;
>>> -
>>> -			ptr += off;
>>> -			systab.tables = (struct efi_configuration_table *)ptr;
>>> -		}
>>
>> This looks like an unrelated change.
>
> It does.
> This code should be replaced by:
>>> +	/* FIXME */
>>> +	efi_convert_pointer(0, (void **)&systab.tables);
>
> -Takahiro Akashi
>
>> Put it into a separate patch, please.
>>
>> Best regards
>>
>> Heinrich
>>
>>>   	}
>>> +#endif
>>> +
>>> +	/* FIXME */
>>> +	efi_convert_pointer(0, (void **)&systab.tables);
>>> +
>>> +	/* All fixes must be done before this line */
>>> +	efi_virtmap = NULL;
>>>
>>>   	/* Move the actual runtime code over */
>>>   	for (i = 0; i < n; i++) {
>>> @@ -644,6 +684,11 @@ static efi_status_t EFIAPI efi_set_virtual_address_map(
>>>   			/* Once we're virtual, we can no longer handle
>>>   			   complex callbacks */
>>>   			efi_runtime_detach(new_offset);
>>> +
>>> +			/*
>>> +			 * FIXME:
>>> +			 * We can no longer update RuntimeServicesSupported.
>>> +			 */
>>>   			return EFI_EXIT(EFI_SUCCESS);
>>>   		}
>>>   	}
>>> @@ -733,20 +778,6 @@ static efi_status_t __efi_runtime EFIAPI efi_device_error(void)
>>>   	return EFI_DEVICE_ERROR;
>>>   }
>>>
>>> -/**
>>> - * efi_invalid_parameter() - replacement function, returns EFI_INVALID_PARAMETER
>>> - *
>>> - * This function is used after SetVirtualAddressMap() is called as replacement
>>> - * for services that are not available anymore due to constraints of the U-Boot
>>> - * implementation.
>>> - *
>>> - * Return:	EFI_INVALID_PARAMETER
>>> - */
>>> -static efi_status_t __efi_runtime EFIAPI efi_invalid_parameter(void)
>>> -{
>>> -	return EFI_INVALID_PARAMETER;
>>> -}
>>> -
>>>   /**
>>>    * efi_update_capsule() - process information from operating system
>>>    *
>>> @@ -833,7 +864,11 @@ struct efi_runtime_services __efi_runtime_data efi_runtime_services = {
>>>   #else
>>>   	.set_virtual_address_map = (void *)&efi_unimplemented,
>>>   #endif
>>> -	.convert_pointer = (void *)&efi_invalid_parameter,
>>> +#ifdef CONFIG_EFI_RUNTIME_CONVERT_POINTER
>>> +	.convert_pointer = &efi_convert_pointer,
>>> +#else
>>> +	.convert_pointer = (void *)&efi_unimplemented,
>>
>> I feel uneasy using a function efi_unimplemented() with a different
>> number of parameters here. Depending on the ABI this may lead to a crash.
>>
>> Best regards
>>
>> Heinrich
>>
>>> +#endif
>>>   	.get_variable = efi_get_variable,
>>>   	.get_next_variable_name = efi_get_next_variable_name,
>>>   	.set_variable = efi_set_variable,
>>>
>>
>

^ permalink raw reply	[flat|nested] 32+ messages in thread

* [U-Boot] [RFC 6/6] efi_loader: variable: support runtime variable access via cache
  2019-06-17  1:51     ` AKASHI Takahiro
@ 2019-06-17 19:52       ` Heinrich Schuchardt
  2019-06-18  1:19         ` AKASHI Takahiro
  0 siblings, 1 reply; 32+ messages in thread
From: Heinrich Schuchardt @ 2019-06-17 19:52 UTC (permalink / raw)
  To: u-boot

On 6/17/19 3:51 AM, AKASHI Takahiro wrote:
> On Sat, Jun 15, 2019 at 09:01:56PM +0200, Heinrich Schuchardt wrote:
>> On 6/5/19 6:21 AM, AKASHI Takahiro wrote:
>>> With this patch, cache buffer for UEFI variables will be created
>>> so that we will still be able to access, at least retrieve,
>>> UEFI variables when we exit from boottime services,
>>>
>>> This feature is a "should" behavior described in EBBR v1.0
>>> section 2.5.3.
>>>
>>> Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org>
>>> ---
>>>   include/efi_loader.h          |  17 ++
>>>   lib/efi_loader/Kconfig        |   9 +
>>>   lib/efi_loader/efi_boottime.c |  10 +-
>>>   lib/efi_loader/efi_runtime.c  |  13 +
>>>   lib/efi_loader/efi_variable.c | 467 ++++++++++++++++++++++++++++++++++
>>>   5 files changed, 515 insertions(+), 1 deletion(-)
>>
>> Please, put the cache into a separate file.
>
> Why?

It is a separate set of functions. In C++ programming you wouldn't put
two classes into the same file.

>
>>>
>>> diff --git a/include/efi_loader.h b/include/efi_loader.h
>>> index 93f7ece814a0..acab657b9d70 100644
>>> --- a/include/efi_loader.h
>>> +++ b/include/efi_loader.h
>>> @@ -620,6 +620,23 @@ efi_status_t EFIAPI efi_set_variable(u16 *variable_name,
>>>   				     const efi_guid_t *vendor, u32 attributes,
>>>   				     efi_uintn_t data_size, const void *data);
>>>
>>> +#ifdef CONFIG_EFI_RUNTIME_GET_VARIABLE_CACHING
>>> +efi_status_t efi_freeze_variable_table(void);
>>> +
>>> +/* runtime version of APIs */
>>> +efi_status_t
>>> +__efi_runtime EFIAPI efi_get_variable_runtime(u16 *variable_name,
>>
>> I think one version of the functions serving at runtime and boottime is
>> enough.
>>
>> The cache should be used both at runtime and at boottime.
>
> So do you mean that we should replace the existing "boottime" version
> of get/set_variable with my code (algorithm)?
>
> This is a bit complicated work because we should be able to *udpate*
> UEFI variables at boottime, but my version of hsearch_runtime() is
> a stripped (and modified) version and doesn't support it.

Do we really need a multilevel hash table? I would not expect hundreds
of variables.

>
> Making the existing hsearch_r() executable at UEFI runtime is,
> as I said before, quite painful.

You could start the cache implementation with a less complicated data
structure like a linked list.

>
>> Essentially I
>> expect three modules working together:
>>
>> UEFI API implementation <-> Cache <-> Persistence driver
>>
>> I would suggest to put each of these into a separate file.
>>
>> Both the API implementation and the Cache have to be available at
>> Boottime and at Runtime. A first version of the persistence driver may
>> only be working at boottime.
>
> Unfortunately, this is not practical right now because there is
> already some sort of assumption (and consensus) that we would re-use
> "Standalone MM services", which is already there in EDK2, as
> secure storage for UEFI variables.
> In the case, all the cache would be bypassed.
> In my old prototype, I utilized the cache but dropped that feature
> for several reasons.

What has EDK2 code to do with it?

In case of write you could do a write-through in your cache if needed.

>
>> The NV-cache content should be written to non-volatile memory on Reset()
>> and on ExitBootServices() and if possible when updating variables at
>> runtime.
>
> I'm not sure your intent here, but are you going to write back
> the cache only once?
> It won't work as every change of UEFI variable must be flushed
> to persistent storage instantly.

The cache should support write and read. Only NV variables have to be
written to a medium. If you do not support this currently just return
some error code vor NV variables. But you could accept still accept
changes to non-NV variables. This way we can test the code at runtime
even before implementing runtime persistence.

>
>>> +					      const efi_guid_t *vendor,
>>> +					      u32 *attributes,
>>> +					      efi_uintn_t *data_size,
>>> +					      void *data);
>>> +efi_status_t
>>> +__efi_runtime EFIAPI efi_get_next_variable_name_runtime(
>>> +						efi_uintn_t *variable_name_size,
>>> +						u16 *variable_name,
>>> +						const efi_guid_t *vendor);
>>> +#endif /* CONFIG_EFI_RUNTIME_GET_VARIABLE_CACHING */
>>> +
>>>   /*
>>>    * 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:
>>> diff --git a/lib/efi_loader/Kconfig b/lib/efi_loader/Kconfig
>>> index e2ef43157568..3f284795648f 100644
>>> --- a/lib/efi_loader/Kconfig
>>> +++ b/lib/efi_loader/Kconfig
>>> @@ -59,6 +59,15 @@ config EFI_RUNTIME_CONVERT_POINTER
>>>   	  to be called by UEFI drivers in relocating themselves to virtual
>>>   	  address space.
>>>
>>> +config EFI_RUNTIME_GET_VARIABLE_CACHING
>>> +	bool "runtime_service: GetVariable: Enable runtime access via cache (read-only)"
>>> +	default y
>>> +	help
>>> +	  Select this option if you want to access UEFI variables at
>>> +	  runtime even though you cannot update values on the fly.
>>> +	  With or without this option, you can access UEFI variables
>>> +	  at boottime.
>>
>> Updates of volatile variables should always be possible.
>
> Why "should"?
> Give me any use case.
> UEFI spec does not describe such a variant implementation at all.

See above. I would like to be able to test setting variables at runtime
even if persisting NV variables is not yet implemented.

>
>>> +
>>>   config EFI_DEVICE_PATH_TO_TEXT
>>>   	bool "Device path to text protocol"
>>>   	default y
>>> diff --git a/lib/efi_loader/efi_boottime.c b/lib/efi_loader/efi_boottime.c
>>> index e4abaf3601d9..14e343abbd43 100644
>>> --- a/lib/efi_loader/efi_boottime.c
>>> +++ b/lib/efi_loader/efi_boottime.c
>>> @@ -1892,6 +1892,9 @@ static efi_status_t EFIAPI efi_exit_boot_services(efi_handle_t image_handle,
>>>   						  efi_uintn_t map_key)
>>>   {
>>>   	struct efi_event *evt;
>>> +#ifdef CONFIG_EFI_RUNTIME_GET_VARIABLE_CACHING
>>> +	efi_status_t ret;
>>> +#endif
>>>
>>>   	EFI_ENTRY("%p, %zx", image_handle, map_key);
>>>
>>> @@ -1921,7 +1924,12 @@ static efi_status_t EFIAPI efi_exit_boot_services(efi_handle_t image_handle,
>>>   		}
>>>   	}
>>>
>>> -	/* TODO: Should persist EFI variables here */
>>> +#ifdef CONFIG_EFI_RUNTIME_GET_VARIABLE_CACHING
>>
>> Can we have weak functions for initializing and persisting the cache, e.g.
>>
>> efi_status_t __weak
>> efi_load_variable_cache(cache_entry *cache, size_t *size)
>> {
>>          cache->len = 0;
>>          return EFI_SUCCESS;
>> }
>>
>> efi_status_t __runtime __weak
>> efi_write_variable_cache(cache_entry *cache, size_t size)
>> {
>>          return EFI_UNSUPPORTED;
>> }
>>
>> Then we can override these in whatever driver we implement.
>
> What is the difference between yours and my env_efi_load/save()
> with backing-storage driver?

I cannot see that you clearly separate the functions API, cache,
persistence.

I would like to see clearly define interfaces into which we can plug
different persistence implementations.

>
>>
>>> +	/* No more variable update */
>>> +	ret = efi_freeze_variable_table();
>>> +	if (ret != EFI_SUCCESS)
>>> +		return EFI_EXIT(ret);
>>> +#endif
>>>
>>>   	board_quiesce_devices();
>>>
>>> diff --git a/lib/efi_loader/efi_runtime.c b/lib/efi_loader/efi_runtime.c
>>> index fc5bdee80e00..b60f70f04613 100644
>>> --- a/lib/efi_loader/efi_runtime.c
>>> +++ b/lib/efi_loader/efi_runtime.c
>>> @@ -111,6 +111,11 @@ efi_status_t efi_init_runtime_supported(void)
>>>   	efi_runtime_services_supported |=
>>>   				EFI_RT_SUPPORTED_CONVERT_POINTER;
>>>   #endif
>>> +#ifdef CONFIG_EFI_RUNTIME_GET_VARIABLE_CACHING
>>> +	efi_runtime_services_supported |=
>>> +				(EFI_RT_SUPPORTED_GET_VARIABLE |
>>> +				 EFI_RT_SUPPORTED_GET_NEXT_VARIABLE_NAME);
>>> +#endif
>>>
>>>   	return EFI_CALL(efi_set_variable(L"RuntimeServicesSupported",
>>>   					 &efi_global_variable_guid,
>>> @@ -469,10 +474,18 @@ static struct efi_runtime_detach_list_struct efi_runtime_detach_list[] = {
>>>   		.patchto = NULL,
>>>   	}, {
>>>   		.ptr = &efi_runtime_services.get_variable,
>>> +#ifdef CONFIG_EFI_RUNTIME_GET_VARIABLE_CACHING
>>> +		.patchto = &efi_get_variable_runtime,
>>> +#else
>>>   		.patchto = &efi_device_error,
>>> +#endif
>>>   	}, {
>>>   		.ptr = &efi_runtime_services.get_next_variable_name,
>>> +#ifdef CONFIG_EFI_RUNTIME_GET_VARIABLE_CACHING
>>> +		.patchto = &efi_get_next_variable_name,
>>> +#else
>>>   		.patchto = &efi_device_error,
>>> +#endif
>>>   	}, {
>>>   		.ptr = &efi_runtime_services.set_variable,
>>>   		.patchto = &efi_device_error,
>>> diff --git a/lib/efi_loader/efi_variable.c b/lib/efi_loader/efi_variable.c
>>> index d9887be938c2..ee21892dd291 100644
>>> --- a/lib/efi_loader/efi_variable.c
>>> +++ b/lib/efi_loader/efi_variable.c
>>> @@ -706,3 +706,470 @@ efi_status_t EFIAPI efi_set_variable(u16 *variable_name,
>>>
>>>   	return EFI_EXIT(ret);
>>>   }
>>> +
>>> +#ifdef CONFIG_EFI_RUNTIME_GET_VARIABLE_CACHING
>>> +/*
>>> + * runtime version of APIs
>>> + * We only support read-only variable access.
>>> + * The table is in U-Boot's hash table format, but has its own
>>> + * _ENTRY structure for specific use.
>>> + *
>>> + * Except for efi_freeze_variable_table(), which is to be called in
>>> + * exit_boot_services(), all the functions and data below must be
>>> + * placed in either RUNTIME_SERVICES_CODE or RUNTIME_SERVICES_DATA.
>>> + */
>>> +typedef struct _ENTRY {
>>> +	unsigned int used;	/* hash value; 0 for not used */
>>> +	size_t name;		/* name offset from itself */
>>> +	efi_guid_t vendor;
>>> +	u32 attributes;
>>> +	size_t data;		/* data offset from itself */
>>> +	size_t data_size;
>>> +} _ENTRY;
>>> +
>>> +static inline u16 *entry_name(_ENTRY *e) { return (void *)e + e->name; }
>>> +static inline u16 *entry_data(_ENTRY *e) { return (void *)e + e->data; }
>>> +
>>> +static struct hsearch_data *efi_variable_table __efi_runtime_data;
>>> +
>>> +static size_t __efi_runtime u16_strlen_runtime(const u16 *s1)
>>
>>
>> Please, do not duplicate existing functions. If they have to be runtime
>> simply change the existing function to __runtime.
>
> We should do that if possible, but please note that [str|mem]xxx() functions
> are *architecture* dependent.
> Do you want to mark all the functions across all the architectures?
>
>>> +{
>>> +	size_t n = 0;
>>> +
>>> +	while (*s1) {
>>> +		n++;
>>> +		s1++;
>>> +	}
>>> +
>>> +	return n;
>>> +}
>>> +
>>> +static int __efi_runtime memcmp_runtime(const void *m1, const void *m2,
>>> +					size_t n)
>>
>> I dislike duplicate code. Can't we simply define the existing memcmp
>> function as __runtime?
>
> ditto
>
>>> +{
>>> +	while (n && *(u8 *)m1 == *(u8 *)m2) {
>>> +		n--;
>>> +		m1++;
>>> +		m2++;
>>> +	}
>>> +
>>> +	if (n)
>>> +		return *(u8 *)m1 - *(u8 *)m2;
>>> +
>>> +	return 0;
>>> +}
>>> +
>>> +static void __efi_runtime memcpy_runtime(void *m1, const void *m2, size_t n)
>>> +{
>>
>> Can't we simply define the existing memcpy function as __runtime?
>>
>>> +	for (; n; n--, m1++, m2++)
>>> +		*(u8 *)m1 = *(u8 *)m2;
>>> +}
>>> +
>>> +static int __efi_runtime efi_cmpkey(_ENTRY *e, const u16 *name,
>>> +				    const efi_guid_t *vendor)
>>> +{
>>> +	size_t name_len;
>>> +
>>> +	name_len = u16_strlen_runtime(entry_name(e));
>>> +
>>> +	/* return zero if matched */
>>> +	return name_len != u16_strlen_runtime(name) ||
>>> +	       memcmp_runtime(entry_name(e), name, name_len * 2) ||
>>> +	       memcmp_runtime(e->vendor.b, vendor->b, sizeof(vendor));
>>> +}
>>> +
>>> +/* simplified and slightly different version of hsearch_r() */
>>
>> These hash functions are so complicated that they really need a unit
>> test testing them rigorously.
>
> Do you know that there are no any tests for hsearch_r()?
> Moreover, this function would better be exercised well
> if we could add *runtime* tests.

Have a look at test/env/hashtable.c

I think efi_selftest could work after SetVirtualMemoryMap if we do not
change the map for memory actually used by U-Boot.

Or we use a minimum Linux kernel and put our tests into an init binary.

>
>>
>>> +static int __efi_runtime hsearch_runtime(const u16 *name,
>>> +					 const efi_guid_t *vendor,
>>> +					 ACTION action,
>>> +					 _ENTRY **retval,
>>> +					 struct hsearch_data *htab)
>>> +{
>>> +	unsigned int hval;
>>> +	unsigned int count;
>>> +	unsigned int len;
>>> +	unsigned int idx, new;
>>> +
>>> +	/* Compute an value for the given string. */
>>> +	len = u16_strlen_runtime(name);
  >>
>> Can't the same variable name exist for different GUIDs? Why is the GUID
>> not considered in the hash?
>
> efi_cmpkey() does take into consideration GUID as well as the name.

My question concerned the hash.

>
>>> +	hval = len;
>>> +	count = len;
>>> +	while (count-- > 0) {
>>> +		hval <<= 4;
>>> +		hval += name[count];
>>> +	}
>>> +
>>> +	/*
>>> +	 * First hash function:
>>> +	 * simply take the modulo but prevent zero.
>>> +	 */
>>> +	hval %= htab->size;
>>> +	if (hval == 0)
>>> +		++hval;
>>> +
>>> +	/* The first index tried. */
>>> +	new = -1; /* not found */
>>> +	idx = hval;
>>> +
>>> +	if (htab->table[idx].used) {
>>> +		/*
>>> +		 * Further action might be required according to the
>>> +		 * action value.
>>> +		 */
>>> +		unsigned int hval2;
>>> +
>>> +		if (htab->table[idx].used == hval &&
>>> +		    !efi_cmpkey(&htab->table[idx], name, vendor)) {
>>> +			if (action == FIND) {
>>> +				*retval = &htab->table[idx];
>>> +				return idx;
>>> +			}
>>> +			/* we don't need to support overwrite */
>>> +			return -1;
>>> +		}
>>> +
>>> +		/*
>>> +		 * Second hash function:
>>> +		 * as suggested in [Knuth]
>>> +		 */
>>> +		hval2 = 1 + hval % (htab->size - 2);
>>> +
>>> +		do {
>>> +			/*
>>> +			 * Because SIZE is prime this guarantees to
>>> +			 * step through all available indices.
>>> +			 */
>>> +			if (idx <= hval2)
>>> +				idx = htab->size + idx - hval2;
>>> +			else
>>> +				idx -= hval2;
>>> +
>>> +			/*
>>> +			 * If we visited all entries leave the loop
>>> +			 * unsuccessfully.
>>> +			 */
>>> +			if (idx == hval)
>>> +				break;
>>> +
>>> +			/* If entry is found use it. */
>>> +			if (htab->table[idx].used == hval &&
>>> +			    !efi_cmpkey(&htab->table[idx], name, vendor)) {
>>> +				if (action == FIND) {
>>> +					*retval = &htab->table[idx];
>>> +					return idx;
>>> +				}
>>> +				/* we don't need to support overwrite */
>>> +				return -1;
>>> +			}
>>> +		} while (htab->table[idx].used);
>>> +
>>> +		if (!htab->table[idx].used)
>>> +			new = idx;
>>> +	} else {
>>> +		new = idx;
>>> +	}
>>> +
>>> +	/*
>>> +	 * An empty bucket has been found.
>>> +	 * The following code should never be executed after
>>> +	 * exit_boot_services()
>>> +	 */
>>> +	if (action == ENTER) {
>>> +		/*
>>> +		 * If table is full and another entry should be
>>> +		 * entered return with error.
>>> +		 */
>>> +		if (htab->filled == htab->size) {
>>> +			*retval = NULL;
>>> +			return 0;
>>> +		}
>>> +
>>> +		/* Create new entry */
>>> +		htab->table[new].used = hval;
>>> +		++htab->filled;
>>> +
>>> +		/* return new entry */
>>> +		*retval = &htab->table[new];
>>> +		return 1;
>>> +	}
>>> +
>>> +	*retval = NULL;
>>> +	return 0;
>>> +}
>>> +
>>> +/* from lib/hashtable.c */
>>> +static inline int isprime(unsigned int number)
>>> +{
>>> +	/* no even number will be passed */
>>> +	unsigned int div = 3;
>>> +
>>> +	while (div * div < number && number % div != 0)
>>> +		div += 2;
>>> +
>>> +	return number % div != 0;
>>> +}
>>> +
>>> +efi_status_t efi_freeze_variable_table(void)
>>
>> Please, add comments to your functions. It is not self-evident what this
>> function is meant to do.
>>
>> I cannot imagine why a variable cache should be frozen. It is a living
>> data structure until the system is switched off.
>
> I don't get your point.

Please, explain what you mean by freeze.

I suggest that the cache is read/write at all times.

Best regards

Heinrich

>
>> For a variable cache I expect that you allocate memory before handling
>> the first variable and never again. At runtime you will not have chance
>> to allocate memory anyway.
>
> I don't get your point.
>
>> This function is way too long. Pleae, break it down.
>
> Freezing is implemented in 2-phase steps, and some complexity
> is inevitable. I suppose adding some comments would be enough.
>
> -Takahiro Akashi
>
>> Best regards
>>
>> Heinrich
>>
>>> +{
>>> +	int var_num = 0;
>>> +	size_t var_data_size = 0;
>>> +	u16 *name;
>>> +	efi_uintn_t name_buf_len, name_len;
>>> +	efi_guid_t vendor;
>>> +	u32 attributes;
>>> +	u8 *mem_pool, *var_buf = NULL;
>>> +	size_t table_size, var_size, var_buf_size;
>>> +	_ENTRY *new = NULL;
>>> +	efi_status_t ret;
>>> +
>>> +	/* phase-1 loop */
>>> +	name_buf_len = 128;
>>> +	name = malloc(name_buf_len);
>>> +	if (!name)
>>> +		return EFI_OUT_OF_RESOURCES;
>>> +	name[0] = 0;
>>> +	for (;;) {
>>> +		name_len = name_buf_len;
>>> +		ret = EFI_CALL(efi_get_next_variable_name(&name_len, name,
>>> +							  &vendor));
>>> +		if (ret == EFI_NOT_FOUND) {
>>> +			break;
>>> +		} else if (ret == EFI_BUFFER_TOO_SMALL) {
>>> +			u16 *buf;
>>> +
>>> +			name_buf_len = name_len;
>>> +			buf = realloc(name, name_buf_len);
>>> +			if (!buf) {
>>> +				free(name);
>>> +				return EFI_OUT_OF_RESOURCES;
>>> +			}
>>> +			name = buf;
>>> +			name_len = name_buf_len;
>>> +			ret = EFI_CALL(efi_get_next_variable_name(&name_len,
>>> +								  name,
>>> +								  &vendor));
>>> +		}
>>> +
>>> +		if (ret != EFI_SUCCESS)
>>> +			return ret;
>>> +
>>> +		var_size = 0;
>>> +		ret = EFI_CALL(efi_get_variable(name, &vendor, &attributes,
>>> +						&var_size, NULL));
>>> +		if (ret != EFI_BUFFER_TOO_SMALL)
>>> +			return ret;
>>> +
>>> +		if (!(attributes & EFI_VARIABLE_RUNTIME_ACCESS))
>>> +			continue;
>>> +
>>> +		var_num++;
>>> +		var_data_size += (u16_strlen_runtime(name) + 1) * sizeof(u16);
>>> +		var_data_size += var_size;
>>> +		/* mem_pool must 2-byte aligned for u16 variable name */
>>> +		if (var_data_size & 0x1)
>>> +			var_data_size++;
>>> +	}
>>> +
>>> +	/*
>>> +	 * total of entries in hash table must be a prime number.
>>> +	 * The logic below comes from lib/hashtable.c
>>> +	 */
>>> +	var_num |= 1;               /* make odd */
>>> +	while (!isprime(var_num))
>>> +		var_num += 2;
>>> +
>>> +	/* We need table[var_num] for hsearch_runtime algo */
>>> +	table_size = sizeof(*efi_variable_table)
>>> +			+ sizeof(_ENTRY) * (var_num + 1) + var_data_size;
>>> +	ret = efi_allocate_pool(EFI_RUNTIME_SERVICES_DATA,
>>> +				table_size, (void **)&efi_variable_table);
>>> +	if (ret != EFI_SUCCESS)
>>> +		return ret;
>>> +
>>> +	efi_variable_table->size = var_num;
>>> +	efi_variable_table->table = (void *)efi_variable_table
>>> +					+ sizeof(*efi_variable_table);
>>> +	mem_pool = (u8 *)efi_variable_table->table
>>> +			+ sizeof(_ENTRY) * (var_num + 1);
>>> +
>>> +	var_buf_size = 128;
>>> +	var_buf = malloc(var_buf_size);
>>> +	if (!var_buf) {
>>> +		ret = EFI_OUT_OF_RESOURCES;
>>> +		goto err;
>>> +	}
>>> +
>>> +	/* phase-2 loop */
>>> +	name[0] = 0;
>>> +	name_len = name_buf_len;
>>> +	for (;;) {
>>> +		name_len = name_buf_len;
>>> +		ret = EFI_CALL(efi_get_next_variable_name(&name_len, name,
>>> +							  &vendor));
>>> +		if (ret == EFI_NOT_FOUND)
>>> +			break;
>>> +		else if (ret != EFI_SUCCESS)
>>> +			goto err;
>>> +
>>> +		var_size = var_buf_size;
>>> +		ret = EFI_CALL(efi_get_variable(name, &vendor, &attributes,
>>> +						&var_size, var_buf));
>>> +		if (ret == EFI_BUFFER_TOO_SMALL) {
>>> +			free(var_buf);
>>> +			var_buf_size = var_size;
>>> +			var_buf = malloc(var_buf_size);
>>> +			if (!var_buf) {
>>> +				ret = EFI_OUT_OF_RESOURCES;
>>> +				goto err;
>>> +			}
>>> +			ret = EFI_CALL(efi_get_variable(name, &vendor,
>>> +							&attributes,
>>> +							&var_size, var_buf));
>>> +		}
>>> +		if (ret != EFI_SUCCESS)
>>> +			goto err;
>>> +
>>> +		if (!(attributes & EFI_VARIABLE_RUNTIME_ACCESS))
>>> +			continue;
>>> +
>>> +		if (hsearch_runtime(name, &vendor, ENTER, &new,
>>> +				    efi_variable_table) <= 0) {
>>> +			/* This should not happen */
>>> +			ret = EFI_INVALID_PARAMETER;
>>> +			goto err;
>>> +		}
>>> +
>>> +		/* allocate space from RUNTIME DATA */
>>> +		name_len = (u16_strlen_runtime(name) + 1) * sizeof(u16);
>>> +		memcpy_runtime(mem_pool, name, name_len);
>>> +		new->name = mem_pool - (u8 *)new; /* offset */
>>> +		mem_pool += name_len;
>>> +
>>> +		memcpy_runtime(&new->vendor.b, &vendor.b, sizeof(vendor));
>>> +
>>> +		new->attributes = attributes;
>>> +
>>> +		memcpy_runtime(mem_pool, var_buf, var_size);
>>> +		new->data = mem_pool - (u8 *)new; /* offset */
>>> +		new->data_size = var_size;
>>> +		mem_pool += var_size;
>>> +
>>> +		/* mem_pool must 2-byte aligned for u16 variable name */
>>> +		if ((uintptr_t)mem_pool & 0x1)
>>> +			mem_pool++;
>>> +	}
>>> +#ifdef DEBUG
>>> +	name[0] = 0;
>>> +	name_len = name_buf_len;
>>> +	for (;;) {
>>> +		name_len = name_buf_len;
>>> +		ret = efi_get_next_variable_name_runtime(&name_len, name,
>>> +							 &vendor);
>>> +		if (ret == EFI_NOT_FOUND)
>>> +			break;
>>> +		else if (ret != EFI_SUCCESS)
>>> +			goto err;
>>> +
>>> +		var_size = var_buf_size;
>>> +		ret = efi_get_variable_runtime(name, &vendor, &attributes,
>>> +					       &var_size, var_buf);
>>> +		if (ret != EFI_SUCCESS)
>>> +			goto err;
>>> +
>>> +		printf("%ls_%pUl:\n", name, &vendor);
>>> +		printf("    attributes: 0x%x\n", attributes);
>>> +		printf("    value (size: 0x%lx)\n", var_size);
>>> +	}
>>> +#endif
>>> +	ret = EFI_SUCCESS;
>>> +
>>> +err:
>>> +	free(name);
>>> +	free(var_buf);
>>> +	if (ret != EFI_SUCCESS && efi_variable_table) {
>>> +		efi_free_pool(efi_variable_table);
>>> +		efi_variable_table = NULL;
>>> +	}
>>> +
>>> +	return ret;
>>> +}
>>> +
>>> +efi_status_t
>>> +__efi_runtime EFIAPI efi_get_variable_runtime(u16 *variable_name,
>>> +					      const efi_guid_t *vendor,
>>> +					      u32 *attributes,
>>> +					      efi_uintn_t *data_size,
>>> +					      void *data)
>>> +{
>>> +	_ENTRY *new;
>>> +
>>> +	if (!variable_name || !vendor || !data_size)
>>> +		return EFI_EXIT(EFI_INVALID_PARAMETER);
>>> +
>>> +	if (hsearch_runtime(variable_name, vendor, FIND, &new,
>>> +			    efi_variable_table) <= 0)
>>> +		return EFI_NOT_FOUND;
>>> +
>>> +	if (attributes)
>>> +		*attributes = new->attributes;
>>> +	if (*data_size < new->data_size) {
>>> +		*data_size = new->data_size;
>>> +		return EFI_BUFFER_TOO_SMALL;
>>> +	}
>>> +
>>> +	*data_size = new->data_size;
>>> +	memcpy_runtime(data, entry_data(new), new->data_size);
>>> +
>>> +	return EFI_SUCCESS;
>>> +}
>>> +
>>> +static int prev_idx __efi_runtime_data;
>>> +
>>> +efi_status_t
>>> +__efi_runtime EFIAPI efi_get_next_variable_name_runtime(
>>> +						efi_uintn_t *variable_name_size,
>>> +						u16 *variable_name,
>>> +						const efi_guid_t *vendor)
>>> +{
>>> +	_ENTRY *e;
>>> +	u16 *name;
>>> +	efi_uintn_t name_size;
>>> +
>>> +	if (!variable_name_size || !variable_name || !vendor)
>>> +		return EFI_INVALID_PARAMETER;
>>> +
>>> +	if (variable_name[0]) {
>>> +		/* sanity check for previous variable */
>>> +		if (prev_idx < 0)
>>> +			return EFI_INVALID_PARAMETER;
>>> +
>>> +		e = &efi_variable_table->table[prev_idx];
>>> +		if (!e->used || efi_cmpkey(e, variable_name, vendor))
>>> +			return EFI_INVALID_PARAMETER;
>>> +	} else {
>>> +		prev_idx = -1;
>>> +	}
>>> +
>>> +	/* next variable */
>>> +	while (++prev_idx <= efi_variable_table->size) {
>>> +		e = &efi_variable_table->table[prev_idx];
>>> +		if (e->used)
>>> +			break;
>>> +	}
>>> +	if (prev_idx > efi_variable_table->size)
>>> +		return EFI_NOT_FOUND;
>>> +
>>> +	name = entry_name(e);
>>> +	name_size = (u16_strlen_runtime(name) + 1)
>>> +			* sizeof(u16);
>>> +	if (*variable_name_size < name_size) {
>>> +		*variable_name_size = name_size;
>>> +		return EFI_BUFFER_TOO_SMALL;
>>> +	}
>>> +
>>> +	memcpy_runtime(variable_name, name, name_size);
>>> +	memcpy_runtime((void *)&vendor->b, &e->vendor.b, sizeof(vendor));
>>> +
>>> +	return EFI_SUCCESS;
>>> +}
>>> +#endif /* CONFIG_EFI_RUNTIME_GET_VARIABLE_CACHING */
>>>
>>
>

^ permalink raw reply	[flat|nested] 32+ messages in thread

* [U-Boot] [RFC 6/6] efi_loader: variable: support runtime variable access via cache
  2019-06-17 19:52       ` Heinrich Schuchardt
@ 2019-06-18  1:19         ` AKASHI Takahiro
  2019-06-18  2:25           ` AKASHI Takahiro
  2019-06-18 10:34           ` Ilias Apalodimas
  0 siblings, 2 replies; 32+ messages in thread
From: AKASHI Takahiro @ 2019-06-18  1:19 UTC (permalink / raw)
  To: u-boot

On Mon, Jun 17, 2019 at 09:52:34PM +0200, Heinrich Schuchardt wrote:
> On 6/17/19 3:51 AM, AKASHI Takahiro wrote:
> >On Sat, Jun 15, 2019 at 09:01:56PM +0200, Heinrich Schuchardt wrote:
> >>On 6/5/19 6:21 AM, AKASHI Takahiro wrote:
> >>>With this patch, cache buffer for UEFI variables will be created
> >>>so that we will still be able to access, at least retrieve,
> >>>UEFI variables when we exit from boottime services,
> >>>
> >>>This feature is a "should" behavior described in EBBR v1.0
> >>>section 2.5.3.
> >>>
> >>>Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org>
> >>>---
> >>>  include/efi_loader.h          |  17 ++
> >>>  lib/efi_loader/Kconfig        |   9 +
> >>>  lib/efi_loader/efi_boottime.c |  10 +-
> >>>  lib/efi_loader/efi_runtime.c  |  13 +
> >>>  lib/efi_loader/efi_variable.c | 467 ++++++++++++++++++++++++++++++++++
> >>>  5 files changed, 515 insertions(+), 1 deletion(-)
> >>
> >>Please, put the cache into a separate file.
> >
> >Why?
> 
> It is a separate set of functions. In C++ programming you wouldn't put
> two classes into the same file.

? I don't get your point.
This is not C++, but C.

> >
> >>>
> >>>diff --git a/include/efi_loader.h b/include/efi_loader.h
> >>>index 93f7ece814a0..acab657b9d70 100644
> >>>--- a/include/efi_loader.h
> >>>+++ b/include/efi_loader.h
> >>>@@ -620,6 +620,23 @@ efi_status_t EFIAPI efi_set_variable(u16 *variable_name,
> >>>  				     const efi_guid_t *vendor, u32 attributes,
> >>>  				     efi_uintn_t data_size, const void *data);
> >>>
> >>>+#ifdef CONFIG_EFI_RUNTIME_GET_VARIABLE_CACHING
> >>>+efi_status_t efi_freeze_variable_table(void);
> >>>+
> >>>+/* runtime version of APIs */
> >>>+efi_status_t
> >>>+__efi_runtime EFIAPI efi_get_variable_runtime(u16 *variable_name,
> >>
> >>I think one version of the functions serving at runtime and boottime is
> >>enough.
> >>
> >>The cache should be used both at runtime and at boottime.
> >
> >So do you mean that we should replace the existing "boottime" version
> >of get/set_variable with my code (algorithm)?
> >
> >This is a bit complicated work because we should be able to *udpate*
> >UEFI variables at boottime, but my version of hsearch_runtime() is
> >a stripped (and modified) version and doesn't support it.
> 
> Do we really need a multilevel hash table? I would not expect hundreds
> of variables.

Please don't change your point suddenly.
Here we are discussing whether "The cache should be used both at runtime
and at boottime" or not.

> >
> >Making the existing hsearch_r() executable at UEFI runtime is,
> >as I said before, quite painful.
> 
> You could start the cache implementation with a less complicated data
> structure like a linked list.

This is totally a different issue. I listed this issue
in my cover letter.

> >
> >>Essentially I
> >>expect three modules working together:
> >>
> >>UEFI API implementation <-> Cache <-> Persistence driver
> >>
> >>I would suggest to put each of these into a separate file.
> >>
> >>Both the API implementation and the Cache have to be available at
> >>Boottime and at Runtime. A first version of the persistence driver may
> >>only be working at boottime.
> >
> >Unfortunately, this is not practical right now because there is
> >already some sort of assumption (and consensus) that we would re-use
> >"Standalone MM services", which is already there in EDK2, as
> >secure storage for UEFI variables.
> >In the case, all the cache would be bypassed.
> >In my old prototype, I utilized the cache but dropped that feature
> >for several reasons.
> 
> What has EDK2 code to do with it?

Did you follow my comment below?
> >Unfortunately, this is not practical right now because there is
> >already some sort of assumption (and consensus) that we would re-use
> >"Standalone MM services", which is already there in EDK2, as
> >secure storage for UEFI variables.

> In case of write you could do a write-through in your cache if needed.
> 
> >
> >>The NV-cache content should be written to non-volatile memory on Reset()
> >>and on ExitBootServices() and if possible when updating variables at
> >>runtime.
> >
> >I'm not sure your intent here, but are you going to write back
> >the cache only once?
> >It won't work as every change of UEFI variable must be flushed
> >to persistent storage instantly.
> 
> The cache should support write and read. Only NV variables have to be

Why?
I don't think it make sense that we support volatile variable, but not
non-volatile variable at runtime.
Even UEFI specification doesn't describe such an irregular behavior.
See EFI_RT_SUPPORTED_XXX definitions.
Do you have a meaningful use case that you want to support?

> written to a medium. If you do not support this currently just return
> some error code vor NV variables. But you could accept still accept
> changes to non-NV variables. This way we can test the code at runtime
> even before implementing runtime persistence.
> 
> >
> >>>+					      const efi_guid_t *vendor,
> >>>+					      u32 *attributes,
> >>>+					      efi_uintn_t *data_size,
> >>>+					      void *data);
> >>>+efi_status_t
> >>>+__efi_runtime EFIAPI efi_get_next_variable_name_runtime(
> >>>+						efi_uintn_t *variable_name_size,
> >>>+						u16 *variable_name,
> >>>+						const efi_guid_t *vendor);
> >>>+#endif /* CONFIG_EFI_RUNTIME_GET_VARIABLE_CACHING */
> >>>+
> >>>  /*
> >>>   * 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:
> >>>diff --git a/lib/efi_loader/Kconfig b/lib/efi_loader/Kconfig
> >>>index e2ef43157568..3f284795648f 100644
> >>>--- a/lib/efi_loader/Kconfig
> >>>+++ b/lib/efi_loader/Kconfig
> >>>@@ -59,6 +59,15 @@ config EFI_RUNTIME_CONVERT_POINTER
> >>>  	  to be called by UEFI drivers in relocating themselves to virtual
> >>>  	  address space.
> >>>
> >>>+config EFI_RUNTIME_GET_VARIABLE_CACHING
> >>>+	bool "runtime_service: GetVariable: Enable runtime access via cache (read-only)"
> >>>+	default y
> >>>+	help
> >>>+	  Select this option if you want to access UEFI variables at
> >>>+	  runtime even though you cannot update values on the fly.
> >>>+	  With or without this option, you can access UEFI variables
> >>>+	  at boottime.
> >>
> >>Updates of volatile variables should always be possible.
> >
> >Why "should"?
> >Give me any use case.
> >UEFI spec does not describe such a variant implementation at all.
> 
> See above. I would like to be able to test setting variables at runtime
> even if persisting NV variables is not yet implemented.

Just for testing? No real use case?
Please note that if we *have* persistent storage at runtime,
we don't need cache because we can access that storage.

> >
> >>>+
> >>>  config EFI_DEVICE_PATH_TO_TEXT
> >>>  	bool "Device path to text protocol"
> >>>  	default y
> >>>diff --git a/lib/efi_loader/efi_boottime.c b/lib/efi_loader/efi_boottime.c
> >>>index e4abaf3601d9..14e343abbd43 100644
> >>>--- a/lib/efi_loader/efi_boottime.c
> >>>+++ b/lib/efi_loader/efi_boottime.c
> >>>@@ -1892,6 +1892,9 @@ static efi_status_t EFIAPI efi_exit_boot_services(efi_handle_t image_handle,
> >>>  						  efi_uintn_t map_key)
> >>>  {
> >>>  	struct efi_event *evt;
> >>>+#ifdef CONFIG_EFI_RUNTIME_GET_VARIABLE_CACHING
> >>>+	efi_status_t ret;
> >>>+#endif
> >>>
> >>>  	EFI_ENTRY("%p, %zx", image_handle, map_key);
> >>>
> >>>@@ -1921,7 +1924,12 @@ static efi_status_t EFIAPI efi_exit_boot_services(efi_handle_t image_handle,
> >>>  		}
> >>>  	}
> >>>
> >>>-	/* TODO: Should persist EFI variables here */
> >>>+#ifdef CONFIG_EFI_RUNTIME_GET_VARIABLE_CACHING
> >>
> >>Can we have weak functions for initializing and persisting the cache, e.g.
> >>
> >>efi_status_t __weak
> >>efi_load_variable_cache(cache_entry *cache, size_t *size)
> >>{
> >>         cache->len = 0;
> >>         return EFI_SUCCESS;
> >>}
> >>
> >>efi_status_t __runtime __weak
> >>efi_write_variable_cache(cache_entry *cache, size_t size)
> >>{
> >>         return EFI_UNSUPPORTED;
> >>}
> >>
> >>Then we can override these in whatever driver we implement.
> >
> >What is the difference between yours and my env_efi_load/save()
> >with backing-storage driver?
> 
> I cannot see that you clearly separate the functions API, cache,
> persistence.

I don't get your point.
For instance, efi_boottime.c has a lot of different type of functions.
Do you want to separate them into different files?

> I would like to see clearly define interfaces into which we can plug
> different persistence implementations.

Your assertion doesn't clarify why you want to separate API and cache.

Regarding persistent storage, as I said, efi_[get|set]_variable()
will be completely replaced with the corresponding functions
(at least, in case of Standalone MM services).


> >
> >>
> >>>+	/* No more variable update */
> >>>+	ret = efi_freeze_variable_table();
> >>>+	if (ret != EFI_SUCCESS)
> >>>+		return EFI_EXIT(ret);
> >>>+#endif
> >>>
> >>>  	board_quiesce_devices();
> >>>
> >>>diff --git a/lib/efi_loader/efi_runtime.c b/lib/efi_loader/efi_runtime.c
> >>>index fc5bdee80e00..b60f70f04613 100644
> >>>--- a/lib/efi_loader/efi_runtime.c
> >>>+++ b/lib/efi_loader/efi_runtime.c
> >>>@@ -111,6 +111,11 @@ efi_status_t efi_init_runtime_supported(void)
> >>>  	efi_runtime_services_supported |=
> >>>  				EFI_RT_SUPPORTED_CONVERT_POINTER;
> >>>  #endif
> >>>+#ifdef CONFIG_EFI_RUNTIME_GET_VARIABLE_CACHING
> >>>+	efi_runtime_services_supported |=
> >>>+				(EFI_RT_SUPPORTED_GET_VARIABLE |
> >>>+				 EFI_RT_SUPPORTED_GET_NEXT_VARIABLE_NAME);
> >>>+#endif
> >>>
> >>>  	return EFI_CALL(efi_set_variable(L"RuntimeServicesSupported",
> >>>  					 &efi_global_variable_guid,
> >>>@@ -469,10 +474,18 @@ static struct efi_runtime_detach_list_struct efi_runtime_detach_list[] = {
> >>>  		.patchto = NULL,
> >>>  	}, {
> >>>  		.ptr = &efi_runtime_services.get_variable,
> >>>+#ifdef CONFIG_EFI_RUNTIME_GET_VARIABLE_CACHING
> >>>+		.patchto = &efi_get_variable_runtime,
> >>>+#else
> >>>  		.patchto = &efi_device_error,
> >>>+#endif
> >>>  	}, {
> >>>  		.ptr = &efi_runtime_services.get_next_variable_name,
> >>>+#ifdef CONFIG_EFI_RUNTIME_GET_VARIABLE_CACHING
> >>>+		.patchto = &efi_get_next_variable_name,
> >>>+#else
> >>>  		.patchto = &efi_device_error,
> >>>+#endif
> >>>  	}, {
> >>>  		.ptr = &efi_runtime_services.set_variable,
> >>>  		.patchto = &efi_device_error,
> >>>diff --git a/lib/efi_loader/efi_variable.c b/lib/efi_loader/efi_variable.c
> >>>index d9887be938c2..ee21892dd291 100644
> >>>--- a/lib/efi_loader/efi_variable.c
> >>>+++ b/lib/efi_loader/efi_variable.c
> >>>@@ -706,3 +706,470 @@ efi_status_t EFIAPI efi_set_variable(u16 *variable_name,
> >>>
> >>>  	return EFI_EXIT(ret);
> >>>  }
> >>>+
> >>>+#ifdef CONFIG_EFI_RUNTIME_GET_VARIABLE_CACHING
> >>>+/*
> >>>+ * runtime version of APIs
> >>>+ * We only support read-only variable access.
> >>>+ * The table is in U-Boot's hash table format, but has its own
> >>>+ * _ENTRY structure for specific use.
> >>>+ *
> >>>+ * Except for efi_freeze_variable_table(), which is to be called in
> >>>+ * exit_boot_services(), all the functions and data below must be
> >>>+ * placed in either RUNTIME_SERVICES_CODE or RUNTIME_SERVICES_DATA.
> >>>+ */
> >>>+typedef struct _ENTRY {
> >>>+	unsigned int used;	/* hash value; 0 for not used */
> >>>+	size_t name;		/* name offset from itself */
> >>>+	efi_guid_t vendor;
> >>>+	u32 attributes;
> >>>+	size_t data;		/* data offset from itself */
> >>>+	size_t data_size;
> >>>+} _ENTRY;
> >>>+
> >>>+static inline u16 *entry_name(_ENTRY *e) { return (void *)e + e->name; }
> >>>+static inline u16 *entry_data(_ENTRY *e) { return (void *)e + e->data; }
> >>>+
> >>>+static struct hsearch_data *efi_variable_table __efi_runtime_data;
> >>>+
> >>>+static size_t __efi_runtime u16_strlen_runtime(const u16 *s1)
> >>
> >>
> >>Please, do not duplicate existing functions. If they have to be runtime
> >>simply change the existing function to __runtime.
> >
> >We should do that if possible, but please note that [str|mem]xxx() functions
> >are *architecture* dependent.
> >Do you want to mark all the functions across all the architectures?

Agree or not agree?


> >>>+{
> >>>+	size_t n = 0;
> >>>+
> >>>+	while (*s1) {
> >>>+		n++;
> >>>+		s1++;
> >>>+	}
> >>>+
> >>>+	return n;
> >>>+}
> >>>+
> >>>+static int __efi_runtime memcmp_runtime(const void *m1, const void *m2,
> >>>+					size_t n)
> >>
> >>I dislike duplicate code. Can't we simply define the existing memcmp
> >>function as __runtime?
> >
> >ditto
> >
> >>>+{
> >>>+	while (n && *(u8 *)m1 == *(u8 *)m2) {
> >>>+		n--;
> >>>+		m1++;
> >>>+		m2++;
> >>>+	}
> >>>+
> >>>+	if (n)
> >>>+		return *(u8 *)m1 - *(u8 *)m2;
> >>>+
> >>>+	return 0;
> >>>+}
> >>>+
> >>>+static void __efi_runtime memcpy_runtime(void *m1, const void *m2, size_t n)
> >>>+{
> >>
> >>Can't we simply define the existing memcpy function as __runtime?
> >>
> >>>+	for (; n; n--, m1++, m2++)
> >>>+		*(u8 *)m1 = *(u8 *)m2;
> >>>+}
> >>>+
> >>>+static int __efi_runtime efi_cmpkey(_ENTRY *e, const u16 *name,
> >>>+				    const efi_guid_t *vendor)
> >>>+{
> >>>+	size_t name_len;
> >>>+
> >>>+	name_len = u16_strlen_runtime(entry_name(e));
> >>>+
> >>>+	/* return zero if matched */
> >>>+	return name_len != u16_strlen_runtime(name) ||
> >>>+	       memcmp_runtime(entry_name(e), name, name_len * 2) ||
> >>>+	       memcmp_runtime(e->vendor.b, vendor->b, sizeof(vendor));
> >>>+}
> >>>+
> >>>+/* simplified and slightly different version of hsearch_r() */
> >>
> >>These hash functions are so complicated that they really need a unit
> >>test testing them rigorously.
> >
> >Do you know that there are no any tests for hsearch_r()?
> >Moreover, this function would better be exercised well
> >if we could add *runtime* tests.
> 
> Have a look at test/env/hashtable.c

Okay, but why not re-use this test for my stripped version?

> I think efi_selftest could work after SetVirtualMemoryMap if we do not
> change the map for memory actually used by U-Boot.

Maybe.

-Takahiro Akashi

> Or we use a minimum Linux kernel and put our tests into an init binary.
> 
> >
> >>
> >>>+static int __efi_runtime hsearch_runtime(const u16 *name,
> >>>+					 const efi_guid_t *vendor,
> >>>+					 ACTION action,
> >>>+					 _ENTRY **retval,
> >>>+					 struct hsearch_data *htab)
> >>>+{
> >>>+	unsigned int hval;
> >>>+	unsigned int count;
> >>>+	unsigned int len;
> >>>+	unsigned int idx, new;
> >>>+
> >>>+	/* Compute an value for the given string. */
> >>>+	len = u16_strlen_runtime(name);
>  >>
> >>Can't the same variable name exist for different GUIDs? Why is the GUID
> >>not considered in the hash?
> >
> >efi_cmpkey() does take into consideration GUID as well as the name.
> 
> My question concerned the hash.
> 
> >
> >>>+	hval = len;
> >>>+	count = len;
> >>>+	while (count-- > 0) {
> >>>+		hval <<= 4;
> >>>+		hval += name[count];
> >>>+	}
> >>>+
> >>>+	/*
> >>>+	 * First hash function:
> >>>+	 * simply take the modulo but prevent zero.
> >>>+	 */
> >>>+	hval %= htab->size;
> >>>+	if (hval == 0)
> >>>+		++hval;
> >>>+
> >>>+	/* The first index tried. */
> >>>+	new = -1; /* not found */
> >>>+	idx = hval;
> >>>+
> >>>+	if (htab->table[idx].used) {
> >>>+		/*
> >>>+		 * Further action might be required according to the
> >>>+		 * action value.
> >>>+		 */
> >>>+		unsigned int hval2;
> >>>+
> >>>+		if (htab->table[idx].used == hval &&
> >>>+		    !efi_cmpkey(&htab->table[idx], name, vendor)) {
> >>>+			if (action == FIND) {
> >>>+				*retval = &htab->table[idx];
> >>>+				return idx;
> >>>+			}
> >>>+			/* we don't need to support overwrite */
> >>>+			return -1;
> >>>+		}
> >>>+
> >>>+		/*
> >>>+		 * Second hash function:
> >>>+		 * as suggested in [Knuth]
> >>>+		 */
> >>>+		hval2 = 1 + hval % (htab->size - 2);
> >>>+
> >>>+		do {
> >>>+			/*
> >>>+			 * Because SIZE is prime this guarantees to
> >>>+			 * step through all available indices.
> >>>+			 */
> >>>+			if (idx <= hval2)
> >>>+				idx = htab->size + idx - hval2;
> >>>+			else
> >>>+				idx -= hval2;
> >>>+
> >>>+			/*
> >>>+			 * If we visited all entries leave the loop
> >>>+			 * unsuccessfully.
> >>>+			 */
> >>>+			if (idx == hval)
> >>>+				break;
> >>>+
> >>>+			/* If entry is found use it. */
> >>>+			if (htab->table[idx].used == hval &&
> >>>+			    !efi_cmpkey(&htab->table[idx], name, vendor)) {
> >>>+				if (action == FIND) {
> >>>+					*retval = &htab->table[idx];
> >>>+					return idx;
> >>>+				}
> >>>+				/* we don't need to support overwrite */
> >>>+				return -1;
> >>>+			}
> >>>+		} while (htab->table[idx].used);
> >>>+
> >>>+		if (!htab->table[idx].used)
> >>>+			new = idx;
> >>>+	} else {
> >>>+		new = idx;
> >>>+	}
> >>>+
> >>>+	/*
> >>>+	 * An empty bucket has been found.
> >>>+	 * The following code should never be executed after
> >>>+	 * exit_boot_services()
> >>>+	 */
> >>>+	if (action == ENTER) {
> >>>+		/*
> >>>+		 * If table is full and another entry should be
> >>>+		 * entered return with error.
> >>>+		 */
> >>>+		if (htab->filled == htab->size) {
> >>>+			*retval = NULL;
> >>>+			return 0;
> >>>+		}
> >>>+
> >>>+		/* Create new entry */
> >>>+		htab->table[new].used = hval;
> >>>+		++htab->filled;
> >>>+
> >>>+		/* return new entry */
> >>>+		*retval = &htab->table[new];
> >>>+		return 1;
> >>>+	}
> >>>+
> >>>+	*retval = NULL;
> >>>+	return 0;
> >>>+}
> >>>+
> >>>+/* from lib/hashtable.c */
> >>>+static inline int isprime(unsigned int number)
> >>>+{
> >>>+	/* no even number will be passed */
> >>>+	unsigned int div = 3;
> >>>+
> >>>+	while (div * div < number && number % div != 0)
> >>>+		div += 2;
> >>>+
> >>>+	return number % div != 0;
> >>>+}
> >>>+
> >>>+efi_status_t efi_freeze_variable_table(void)
> >>
> >>Please, add comments to your functions. It is not self-evident what this
> >>function is meant to do.
> >>
> >>I cannot imagine why a variable cache should be frozen. It is a living
> >>data structure until the system is switched off.
> >
> >I don't get your point.
> 
> Please, explain what you mean by freeze.
> 
> I suggest that the cache is read/write at all times.
> 
> Best regards
> 
> Heinrich
> 
> >
> >>For a variable cache I expect that you allocate memory before handling
> >>the first variable and never again. At runtime you will not have chance
> >>to allocate memory anyway.
> >
> >I don't get your point.
> >
> >>This function is way too long. Pleae, break it down.
> >
> >Freezing is implemented in 2-phase steps, and some complexity
> >is inevitable. I suppose adding some comments would be enough.
> >
> >-Takahiro Akashi
> >
> >>Best regards
> >>
> >>Heinrich
> >>
> >>>+{
> >>>+	int var_num = 0;
> >>>+	size_t var_data_size = 0;
> >>>+	u16 *name;
> >>>+	efi_uintn_t name_buf_len, name_len;
> >>>+	efi_guid_t vendor;
> >>>+	u32 attributes;
> >>>+	u8 *mem_pool, *var_buf = NULL;
> >>>+	size_t table_size, var_size, var_buf_size;
> >>>+	_ENTRY *new = NULL;
> >>>+	efi_status_t ret;
> >>>+
> >>>+	/* phase-1 loop */
> >>>+	name_buf_len = 128;
> >>>+	name = malloc(name_buf_len);
> >>>+	if (!name)
> >>>+		return EFI_OUT_OF_RESOURCES;
> >>>+	name[0] = 0;
> >>>+	for (;;) {
> >>>+		name_len = name_buf_len;
> >>>+		ret = EFI_CALL(efi_get_next_variable_name(&name_len, name,
> >>>+							  &vendor));
> >>>+		if (ret == EFI_NOT_FOUND) {
> >>>+			break;
> >>>+		} else if (ret == EFI_BUFFER_TOO_SMALL) {
> >>>+			u16 *buf;
> >>>+
> >>>+			name_buf_len = name_len;
> >>>+			buf = realloc(name, name_buf_len);
> >>>+			if (!buf) {
> >>>+				free(name);
> >>>+				return EFI_OUT_OF_RESOURCES;
> >>>+			}
> >>>+			name = buf;
> >>>+			name_len = name_buf_len;
> >>>+			ret = EFI_CALL(efi_get_next_variable_name(&name_len,
> >>>+								  name,
> >>>+								  &vendor));
> >>>+		}
> >>>+
> >>>+		if (ret != EFI_SUCCESS)
> >>>+			return ret;
> >>>+
> >>>+		var_size = 0;
> >>>+		ret = EFI_CALL(efi_get_variable(name, &vendor, &attributes,
> >>>+						&var_size, NULL));
> >>>+		if (ret != EFI_BUFFER_TOO_SMALL)
> >>>+			return ret;
> >>>+
> >>>+		if (!(attributes & EFI_VARIABLE_RUNTIME_ACCESS))
> >>>+			continue;
> >>>+
> >>>+		var_num++;
> >>>+		var_data_size += (u16_strlen_runtime(name) + 1) * sizeof(u16);
> >>>+		var_data_size += var_size;
> >>>+		/* mem_pool must 2-byte aligned for u16 variable name */
> >>>+		if (var_data_size & 0x1)
> >>>+			var_data_size++;
> >>>+	}
> >>>+
> >>>+	/*
> >>>+	 * total of entries in hash table must be a prime number.
> >>>+	 * The logic below comes from lib/hashtable.c
> >>>+	 */
> >>>+	var_num |= 1;               /* make odd */
> >>>+	while (!isprime(var_num))
> >>>+		var_num += 2;
> >>>+
> >>>+	/* We need table[var_num] for hsearch_runtime algo */
> >>>+	table_size = sizeof(*efi_variable_table)
> >>>+			+ sizeof(_ENTRY) * (var_num + 1) + var_data_size;
> >>>+	ret = efi_allocate_pool(EFI_RUNTIME_SERVICES_DATA,
> >>>+				table_size, (void **)&efi_variable_table);
> >>>+	if (ret != EFI_SUCCESS)
> >>>+		return ret;
> >>>+
> >>>+	efi_variable_table->size = var_num;
> >>>+	efi_variable_table->table = (void *)efi_variable_table
> >>>+					+ sizeof(*efi_variable_table);
> >>>+	mem_pool = (u8 *)efi_variable_table->table
> >>>+			+ sizeof(_ENTRY) * (var_num + 1);
> >>>+
> >>>+	var_buf_size = 128;
> >>>+	var_buf = malloc(var_buf_size);
> >>>+	if (!var_buf) {
> >>>+		ret = EFI_OUT_OF_RESOURCES;
> >>>+		goto err;
> >>>+	}
> >>>+
> >>>+	/* phase-2 loop */
> >>>+	name[0] = 0;
> >>>+	name_len = name_buf_len;
> >>>+	for (;;) {
> >>>+		name_len = name_buf_len;
> >>>+		ret = EFI_CALL(efi_get_next_variable_name(&name_len, name,
> >>>+							  &vendor));
> >>>+		if (ret == EFI_NOT_FOUND)
> >>>+			break;
> >>>+		else if (ret != EFI_SUCCESS)
> >>>+			goto err;
> >>>+
> >>>+		var_size = var_buf_size;
> >>>+		ret = EFI_CALL(efi_get_variable(name, &vendor, &attributes,
> >>>+						&var_size, var_buf));
> >>>+		if (ret == EFI_BUFFER_TOO_SMALL) {
> >>>+			free(var_buf);
> >>>+			var_buf_size = var_size;
> >>>+			var_buf = malloc(var_buf_size);
> >>>+			if (!var_buf) {
> >>>+				ret = EFI_OUT_OF_RESOURCES;
> >>>+				goto err;
> >>>+			}
> >>>+			ret = EFI_CALL(efi_get_variable(name, &vendor,
> >>>+							&attributes,
> >>>+							&var_size, var_buf));
> >>>+		}
> >>>+		if (ret != EFI_SUCCESS)
> >>>+			goto err;
> >>>+
> >>>+		if (!(attributes & EFI_VARIABLE_RUNTIME_ACCESS))
> >>>+			continue;
> >>>+
> >>>+		if (hsearch_runtime(name, &vendor, ENTER, &new,
> >>>+				    efi_variable_table) <= 0) {
> >>>+			/* This should not happen */
> >>>+			ret = EFI_INVALID_PARAMETER;
> >>>+			goto err;
> >>>+		}
> >>>+
> >>>+		/* allocate space from RUNTIME DATA */
> >>>+		name_len = (u16_strlen_runtime(name) + 1) * sizeof(u16);
> >>>+		memcpy_runtime(mem_pool, name, name_len);
> >>>+		new->name = mem_pool - (u8 *)new; /* offset */
> >>>+		mem_pool += name_len;
> >>>+
> >>>+		memcpy_runtime(&new->vendor.b, &vendor.b, sizeof(vendor));
> >>>+
> >>>+		new->attributes = attributes;
> >>>+
> >>>+		memcpy_runtime(mem_pool, var_buf, var_size);
> >>>+		new->data = mem_pool - (u8 *)new; /* offset */
> >>>+		new->data_size = var_size;
> >>>+		mem_pool += var_size;
> >>>+
> >>>+		/* mem_pool must 2-byte aligned for u16 variable name */
> >>>+		if ((uintptr_t)mem_pool & 0x1)
> >>>+			mem_pool++;
> >>>+	}
> >>>+#ifdef DEBUG
> >>>+	name[0] = 0;
> >>>+	name_len = name_buf_len;
> >>>+	for (;;) {
> >>>+		name_len = name_buf_len;
> >>>+		ret = efi_get_next_variable_name_runtime(&name_len, name,
> >>>+							 &vendor);
> >>>+		if (ret == EFI_NOT_FOUND)
> >>>+			break;
> >>>+		else if (ret != EFI_SUCCESS)
> >>>+			goto err;
> >>>+
> >>>+		var_size = var_buf_size;
> >>>+		ret = efi_get_variable_runtime(name, &vendor, &attributes,
> >>>+					       &var_size, var_buf);
> >>>+		if (ret != EFI_SUCCESS)
> >>>+			goto err;
> >>>+
> >>>+		printf("%ls_%pUl:\n", name, &vendor);
> >>>+		printf("    attributes: 0x%x\n", attributes);
> >>>+		printf("    value (size: 0x%lx)\n", var_size);
> >>>+	}
> >>>+#endif
> >>>+	ret = EFI_SUCCESS;
> >>>+
> >>>+err:
> >>>+	free(name);
> >>>+	free(var_buf);
> >>>+	if (ret != EFI_SUCCESS && efi_variable_table) {
> >>>+		efi_free_pool(efi_variable_table);
> >>>+		efi_variable_table = NULL;
> >>>+	}
> >>>+
> >>>+	return ret;
> >>>+}
> >>>+
> >>>+efi_status_t
> >>>+__efi_runtime EFIAPI efi_get_variable_runtime(u16 *variable_name,
> >>>+					      const efi_guid_t *vendor,
> >>>+					      u32 *attributes,
> >>>+					      efi_uintn_t *data_size,
> >>>+					      void *data)
> >>>+{
> >>>+	_ENTRY *new;
> >>>+
> >>>+	if (!variable_name || !vendor || !data_size)
> >>>+		return EFI_EXIT(EFI_INVALID_PARAMETER);
> >>>+
> >>>+	if (hsearch_runtime(variable_name, vendor, FIND, &new,
> >>>+			    efi_variable_table) <= 0)
> >>>+		return EFI_NOT_FOUND;
> >>>+
> >>>+	if (attributes)
> >>>+		*attributes = new->attributes;
> >>>+	if (*data_size < new->data_size) {
> >>>+		*data_size = new->data_size;
> >>>+		return EFI_BUFFER_TOO_SMALL;
> >>>+	}
> >>>+
> >>>+	*data_size = new->data_size;
> >>>+	memcpy_runtime(data, entry_data(new), new->data_size);
> >>>+
> >>>+	return EFI_SUCCESS;
> >>>+}
> >>>+
> >>>+static int prev_idx __efi_runtime_data;
> >>>+
> >>>+efi_status_t
> >>>+__efi_runtime EFIAPI efi_get_next_variable_name_runtime(
> >>>+						efi_uintn_t *variable_name_size,
> >>>+						u16 *variable_name,
> >>>+						const efi_guid_t *vendor)
> >>>+{
> >>>+	_ENTRY *e;
> >>>+	u16 *name;
> >>>+	efi_uintn_t name_size;
> >>>+
> >>>+	if (!variable_name_size || !variable_name || !vendor)
> >>>+		return EFI_INVALID_PARAMETER;
> >>>+
> >>>+	if (variable_name[0]) {
> >>>+		/* sanity check for previous variable */
> >>>+		if (prev_idx < 0)
> >>>+			return EFI_INVALID_PARAMETER;
> >>>+
> >>>+		e = &efi_variable_table->table[prev_idx];
> >>>+		if (!e->used || efi_cmpkey(e, variable_name, vendor))
> >>>+			return EFI_INVALID_PARAMETER;
> >>>+	} else {
> >>>+		prev_idx = -1;
> >>>+	}
> >>>+
> >>>+	/* next variable */
> >>>+	while (++prev_idx <= efi_variable_table->size) {
> >>>+		e = &efi_variable_table->table[prev_idx];
> >>>+		if (e->used)
> >>>+			break;
> >>>+	}
> >>>+	if (prev_idx > efi_variable_table->size)
> >>>+		return EFI_NOT_FOUND;
> >>>+
> >>>+	name = entry_name(e);
> >>>+	name_size = (u16_strlen_runtime(name) + 1)
> >>>+			* sizeof(u16);
> >>>+	if (*variable_name_size < name_size) {
> >>>+		*variable_name_size = name_size;
> >>>+		return EFI_BUFFER_TOO_SMALL;
> >>>+	}
> >>>+
> >>>+	memcpy_runtime(variable_name, name, name_size);
> >>>+	memcpy_runtime((void *)&vendor->b, &e->vendor.b, sizeof(vendor));
> >>>+
> >>>+	return EFI_SUCCESS;
> >>>+}
> >>>+#endif /* CONFIG_EFI_RUNTIME_GET_VARIABLE_CACHING */
> >>>
> >>
> >
> 

^ permalink raw reply	[flat|nested] 32+ messages in thread

* [U-Boot] [RFC 6/6] efi_loader: variable: support runtime variable access via cache
  2019-06-18  1:19         ` AKASHI Takahiro
@ 2019-06-18  2:25           ` AKASHI Takahiro
  2019-06-18 10:34           ` Ilias Apalodimas
  1 sibling, 0 replies; 32+ messages in thread
From: AKASHI Takahiro @ 2019-06-18  2:25 UTC (permalink / raw)
  To: u-boot

Heinrich,

On Tue, Jun 18, 2019 at 10:19:06AM +0900, AKASHI Takahiro wrote:
> On Mon, Jun 17, 2019 at 09:52:34PM +0200, Heinrich Schuchardt wrote:
> > On 6/17/19 3:51 AM, AKASHI Takahiro wrote:
> > >On Sat, Jun 15, 2019 at 09:01:56PM +0200, Heinrich Schuchardt wrote:
> > >>On 6/5/19 6:21 AM, AKASHI Takahiro wrote:
> > >>>With this patch, cache buffer for UEFI variables will be created
> > >>>so that we will still be able to access, at least retrieve,
> > >>>UEFI variables when we exit from boottime services,
> > >>>
> > >>>This feature is a "should" behavior described in EBBR v1.0
> > >>>section 2.5.3.
> > >>>
> > >>>Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org>
> > >>>---
> > >>>  include/efi_loader.h          |  17 ++
> > >>>  lib/efi_loader/Kconfig        |   9 +
> > >>>  lib/efi_loader/efi_boottime.c |  10 +-
> > >>>  lib/efi_loader/efi_runtime.c  |  13 +
> > >>>  lib/efi_loader/efi_variable.c | 467 ++++++++++++++++++++++++++++++++++
> > >>>  5 files changed, 515 insertions(+), 1 deletion(-)
> > >>
> > >>Please, put the cache into a separate file.
> > >
> > >Why?
> > 
> > It is a separate set of functions. In C++ programming you wouldn't put
> > two classes into the same file.
> 
> ? I don't get your point.
> This is not C++, but C.
> 
> > >
> > >>>
> > >>>diff --git a/include/efi_loader.h b/include/efi_loader.h
> > >>>index 93f7ece814a0..acab657b9d70 100644
> > >>>--- a/include/efi_loader.h
> > >>>+++ b/include/efi_loader.h
> > >>>@@ -620,6 +620,23 @@ efi_status_t EFIAPI efi_set_variable(u16 *variable_name,
> > >>>  				     const efi_guid_t *vendor, u32 attributes,
> > >>>  				     efi_uintn_t data_size, const void *data);
> > >>>
> > >>>+#ifdef CONFIG_EFI_RUNTIME_GET_VARIABLE_CACHING
> > >>>+efi_status_t efi_freeze_variable_table(void);
> > >>>+
> > >>>+/* runtime version of APIs */
> > >>>+efi_status_t
> > >>>+__efi_runtime EFIAPI efi_get_variable_runtime(u16 *variable_name,
> > >>
> > >>I think one version of the functions serving at runtime and boottime is
> > >>enough.
> > >>
> > >>The cache should be used both at runtime and at boottime.
> > >
> > >So do you mean that we should replace the existing "boottime" version
> > >of get/set_variable with my code (algorithm)?
> > >
> > >This is a bit complicated work because we should be able to *udpate*
> > >UEFI variables at boottime, but my version of hsearch_runtime() is
> > >a stripped (and modified) version and doesn't support it.
> > 
> > Do we really need a multilevel hash table? I would not expect hundreds
> > of variables.
> 
> Please don't change your point suddenly.
> Here we are discussing whether "The cache should be used both at runtime
> and at boottime" or not.
> 
> > >
> > >Making the existing hsearch_r() executable at UEFI runtime is,
> > >as I said before, quite painful.
> > 
> > You could start the cache implementation with a less complicated data
> > structure like a linked list.
> 
> This is totally a different issue. I listed this issue
> in my cover letter.
> 
> > >
> > >>Essentially I
> > >>expect three modules working together:
> > >>
> > >>UEFI API implementation <-> Cache <-> Persistence driver
> > >>
> > >>I would suggest to put each of these into a separate file.
> > >>
> > >>Both the API implementation and the Cache have to be available at
> > >>Boottime and at Runtime. A first version of the persistence driver may
> > >>only be working at boottime.
> > >
> > >Unfortunately, this is not practical right now because there is
> > >already some sort of assumption (and consensus) that we would re-use
> > >"Standalone MM services", which is already there in EDK2, as
> > >secure storage for UEFI variables.
> > >In the case, all the cache would be bypassed.
> > >In my old prototype, I utilized the cache but dropped that feature
> > >for several reasons.
> > 
> > What has EDK2 code to do with it?
> 
> Did you follow my comment below?
> > >Unfortunately, this is not practical right now because there is
> > >already some sort of assumption (and consensus) that we would re-use
> > >"Standalone MM services", which is already there in EDK2, as
> > >secure storage for UEFI variables.

Let me elaborate more:
My tentative "secure boot" patch is self-contained on non-secure side
and provides the functionality that UEFI specification describes, but
there is one missing and essential aspect: Tamper-resistant.
Standalone MM services is expected to provides this semantics with
the same interfaces as [Get|Set]Variable for both volatile and
non-volatile variables alike.
There can be another implementation (with yet another interfaces to
secure side), but it is currently the only implementation available
as far as I know.

-Takahiro Akashi


> > In case of write you could do a write-through in your cache if needed.
> > 
> > >
> > >>The NV-cache content should be written to non-volatile memory on Reset()
> > >>and on ExitBootServices() and if possible when updating variables at
> > >>runtime.
> > >
> > >I'm not sure your intent here, but are you going to write back
> > >the cache only once?
> > >It won't work as every change of UEFI variable must be flushed
> > >to persistent storage instantly.
> > 
> > The cache should support write and read. Only NV variables have to be
> 
> Why?
> I don't think it make sense that we support volatile variable, but not
> non-volatile variable at runtime.
> Even UEFI specification doesn't describe such an irregular behavior.
> See EFI_RT_SUPPORTED_XXX definitions.
> Do you have a meaningful use case that you want to support?
> 
> > written to a medium. If you do not support this currently just return
> > some error code vor NV variables. But you could accept still accept
> > changes to non-NV variables. This way we can test the code at runtime
> > even before implementing runtime persistence.
> > 
> > >
> > >>>+					      const efi_guid_t *vendor,
> > >>>+					      u32 *attributes,
> > >>>+					      efi_uintn_t *data_size,
> > >>>+					      void *data);
> > >>>+efi_status_t
> > >>>+__efi_runtime EFIAPI efi_get_next_variable_name_runtime(
> > >>>+						efi_uintn_t *variable_name_size,
> > >>>+						u16 *variable_name,
> > >>>+						const efi_guid_t *vendor);
> > >>>+#endif /* CONFIG_EFI_RUNTIME_GET_VARIABLE_CACHING */
> > >>>+
> > >>>  /*
> > >>>   * 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:
> > >>>diff --git a/lib/efi_loader/Kconfig b/lib/efi_loader/Kconfig
> > >>>index e2ef43157568..3f284795648f 100644
> > >>>--- a/lib/efi_loader/Kconfig
> > >>>+++ b/lib/efi_loader/Kconfig
> > >>>@@ -59,6 +59,15 @@ config EFI_RUNTIME_CONVERT_POINTER
> > >>>  	  to be called by UEFI drivers in relocating themselves to virtual
> > >>>  	  address space.
> > >>>
> > >>>+config EFI_RUNTIME_GET_VARIABLE_CACHING
> > >>>+	bool "runtime_service: GetVariable: Enable runtime access via cache (read-only)"
> > >>>+	default y
> > >>>+	help
> > >>>+	  Select this option if you want to access UEFI variables at
> > >>>+	  runtime even though you cannot update values on the fly.
> > >>>+	  With or without this option, you can access UEFI variables
> > >>>+	  at boottime.
> > >>
> > >>Updates of volatile variables should always be possible.
> > >
> > >Why "should"?
> > >Give me any use case.
> > >UEFI spec does not describe such a variant implementation at all.
> > 
> > See above. I would like to be able to test setting variables at runtime
> > even if persisting NV variables is not yet implemented.
> 
> Just for testing? No real use case?
> Please note that if we *have* persistent storage at runtime,
> we don't need cache because we can access that storage.
> 
> > >
> > >>>+
> > >>>  config EFI_DEVICE_PATH_TO_TEXT
> > >>>  	bool "Device path to text protocol"
> > >>>  	default y
> > >>>diff --git a/lib/efi_loader/efi_boottime.c b/lib/efi_loader/efi_boottime.c
> > >>>index e4abaf3601d9..14e343abbd43 100644
> > >>>--- a/lib/efi_loader/efi_boottime.c
> > >>>+++ b/lib/efi_loader/efi_boottime.c
> > >>>@@ -1892,6 +1892,9 @@ static efi_status_t EFIAPI efi_exit_boot_services(efi_handle_t image_handle,
> > >>>  						  efi_uintn_t map_key)
> > >>>  {
> > >>>  	struct efi_event *evt;
> > >>>+#ifdef CONFIG_EFI_RUNTIME_GET_VARIABLE_CACHING
> > >>>+	efi_status_t ret;
> > >>>+#endif
> > >>>
> > >>>  	EFI_ENTRY("%p, %zx", image_handle, map_key);
> > >>>
> > >>>@@ -1921,7 +1924,12 @@ static efi_status_t EFIAPI efi_exit_boot_services(efi_handle_t image_handle,
> > >>>  		}
> > >>>  	}
> > >>>
> > >>>-	/* TODO: Should persist EFI variables here */
> > >>>+#ifdef CONFIG_EFI_RUNTIME_GET_VARIABLE_CACHING
> > >>
> > >>Can we have weak functions for initializing and persisting the cache, e.g.
> > >>
> > >>efi_status_t __weak
> > >>efi_load_variable_cache(cache_entry *cache, size_t *size)
> > >>{
> > >>         cache->len = 0;
> > >>         return EFI_SUCCESS;
> > >>}
> > >>
> > >>efi_status_t __runtime __weak
> > >>efi_write_variable_cache(cache_entry *cache, size_t size)
> > >>{
> > >>         return EFI_UNSUPPORTED;
> > >>}
> > >>
> > >>Then we can override these in whatever driver we implement.
> > >
> > >What is the difference between yours and my env_efi_load/save()
> > >with backing-storage driver?
> > 
> > I cannot see that you clearly separate the functions API, cache,
> > persistence.
> 
> I don't get your point.
> For instance, efi_boottime.c has a lot of different type of functions.
> Do you want to separate them into different files?
> 
> > I would like to see clearly define interfaces into which we can plug
> > different persistence implementations.
> 
> Your assertion doesn't clarify why you want to separate API and cache.
> 
> Regarding persistent storage, as I said, efi_[get|set]_variable()
> will be completely replaced with the corresponding functions
> (at least, in case of Standalone MM services).
> 
> 
> > >
> > >>
> > >>>+	/* No more variable update */
> > >>>+	ret = efi_freeze_variable_table();
> > >>>+	if (ret != EFI_SUCCESS)
> > >>>+		return EFI_EXIT(ret);
> > >>>+#endif
> > >>>
> > >>>  	board_quiesce_devices();
> > >>>
> > >>>diff --git a/lib/efi_loader/efi_runtime.c b/lib/efi_loader/efi_runtime.c
> > >>>index fc5bdee80e00..b60f70f04613 100644
> > >>>--- a/lib/efi_loader/efi_runtime.c
> > >>>+++ b/lib/efi_loader/efi_runtime.c
> > >>>@@ -111,6 +111,11 @@ efi_status_t efi_init_runtime_supported(void)
> > >>>  	efi_runtime_services_supported |=
> > >>>  				EFI_RT_SUPPORTED_CONVERT_POINTER;
> > >>>  #endif
> > >>>+#ifdef CONFIG_EFI_RUNTIME_GET_VARIABLE_CACHING
> > >>>+	efi_runtime_services_supported |=
> > >>>+				(EFI_RT_SUPPORTED_GET_VARIABLE |
> > >>>+				 EFI_RT_SUPPORTED_GET_NEXT_VARIABLE_NAME);
> > >>>+#endif
> > >>>
> > >>>  	return EFI_CALL(efi_set_variable(L"RuntimeServicesSupported",
> > >>>  					 &efi_global_variable_guid,
> > >>>@@ -469,10 +474,18 @@ static struct efi_runtime_detach_list_struct efi_runtime_detach_list[] = {
> > >>>  		.patchto = NULL,
> > >>>  	}, {
> > >>>  		.ptr = &efi_runtime_services.get_variable,
> > >>>+#ifdef CONFIG_EFI_RUNTIME_GET_VARIABLE_CACHING
> > >>>+		.patchto = &efi_get_variable_runtime,
> > >>>+#else
> > >>>  		.patchto = &efi_device_error,
> > >>>+#endif
> > >>>  	}, {
> > >>>  		.ptr = &efi_runtime_services.get_next_variable_name,
> > >>>+#ifdef CONFIG_EFI_RUNTIME_GET_VARIABLE_CACHING
> > >>>+		.patchto = &efi_get_next_variable_name,
> > >>>+#else
> > >>>  		.patchto = &efi_device_error,
> > >>>+#endif
> > >>>  	}, {
> > >>>  		.ptr = &efi_runtime_services.set_variable,
> > >>>  		.patchto = &efi_device_error,
> > >>>diff --git a/lib/efi_loader/efi_variable.c b/lib/efi_loader/efi_variable.c
> > >>>index d9887be938c2..ee21892dd291 100644
> > >>>--- a/lib/efi_loader/efi_variable.c
> > >>>+++ b/lib/efi_loader/efi_variable.c
> > >>>@@ -706,3 +706,470 @@ efi_status_t EFIAPI efi_set_variable(u16 *variable_name,
> > >>>
> > >>>  	return EFI_EXIT(ret);
> > >>>  }
> > >>>+
> > >>>+#ifdef CONFIG_EFI_RUNTIME_GET_VARIABLE_CACHING
> > >>>+/*
> > >>>+ * runtime version of APIs
> > >>>+ * We only support read-only variable access.
> > >>>+ * The table is in U-Boot's hash table format, but has its own
> > >>>+ * _ENTRY structure for specific use.
> > >>>+ *
> > >>>+ * Except for efi_freeze_variable_table(), which is to be called in
> > >>>+ * exit_boot_services(), all the functions and data below must be
> > >>>+ * placed in either RUNTIME_SERVICES_CODE or RUNTIME_SERVICES_DATA.
> > >>>+ */
> > >>>+typedef struct _ENTRY {
> > >>>+	unsigned int used;	/* hash value; 0 for not used */
> > >>>+	size_t name;		/* name offset from itself */
> > >>>+	efi_guid_t vendor;
> > >>>+	u32 attributes;
> > >>>+	size_t data;		/* data offset from itself */
> > >>>+	size_t data_size;
> > >>>+} _ENTRY;
> > >>>+
> > >>>+static inline u16 *entry_name(_ENTRY *e) { return (void *)e + e->name; }
> > >>>+static inline u16 *entry_data(_ENTRY *e) { return (void *)e + e->data; }
> > >>>+
> > >>>+static struct hsearch_data *efi_variable_table __efi_runtime_data;
> > >>>+
> > >>>+static size_t __efi_runtime u16_strlen_runtime(const u16 *s1)
> > >>
> > >>
> > >>Please, do not duplicate existing functions. If they have to be runtime
> > >>simply change the existing function to __runtime.
> > >
> > >We should do that if possible, but please note that [str|mem]xxx() functions
> > >are *architecture* dependent.
> > >Do you want to mark all the functions across all the architectures?
> 
> Agree or not agree?
> 
> 
> > >>>+{
> > >>>+	size_t n = 0;
> > >>>+
> > >>>+	while (*s1) {
> > >>>+		n++;
> > >>>+		s1++;
> > >>>+	}
> > >>>+
> > >>>+	return n;
> > >>>+}
> > >>>+
> > >>>+static int __efi_runtime memcmp_runtime(const void *m1, const void *m2,
> > >>>+					size_t n)
> > >>
> > >>I dislike duplicate code. Can't we simply define the existing memcmp
> > >>function as __runtime?
> > >
> > >ditto
> > >
> > >>>+{
> > >>>+	while (n && *(u8 *)m1 == *(u8 *)m2) {
> > >>>+		n--;
> > >>>+		m1++;
> > >>>+		m2++;
> > >>>+	}
> > >>>+
> > >>>+	if (n)
> > >>>+		return *(u8 *)m1 - *(u8 *)m2;
> > >>>+
> > >>>+	return 0;
> > >>>+}
> > >>>+
> > >>>+static void __efi_runtime memcpy_runtime(void *m1, const void *m2, size_t n)
> > >>>+{
> > >>
> > >>Can't we simply define the existing memcpy function as __runtime?
> > >>
> > >>>+	for (; n; n--, m1++, m2++)
> > >>>+		*(u8 *)m1 = *(u8 *)m2;
> > >>>+}
> > >>>+
> > >>>+static int __efi_runtime efi_cmpkey(_ENTRY *e, const u16 *name,
> > >>>+				    const efi_guid_t *vendor)
> > >>>+{
> > >>>+	size_t name_len;
> > >>>+
> > >>>+	name_len = u16_strlen_runtime(entry_name(e));
> > >>>+
> > >>>+	/* return zero if matched */
> > >>>+	return name_len != u16_strlen_runtime(name) ||
> > >>>+	       memcmp_runtime(entry_name(e), name, name_len * 2) ||
> > >>>+	       memcmp_runtime(e->vendor.b, vendor->b, sizeof(vendor));
> > >>>+}
> > >>>+
> > >>>+/* simplified and slightly different version of hsearch_r() */
> > >>
> > >>These hash functions are so complicated that they really need a unit
> > >>test testing them rigorously.
> > >
> > >Do you know that there are no any tests for hsearch_r()?
> > >Moreover, this function would better be exercised well
> > >if we could add *runtime* tests.
> > 
> > Have a look at test/env/hashtable.c
> 
> Okay, but why not re-use this test for my stripped version?
> 
> > I think efi_selftest could work after SetVirtualMemoryMap if we do not
> > change the map for memory actually used by U-Boot.
> 
> Maybe.
> 
> -Takahiro Akashi
> 
> > Or we use a minimum Linux kernel and put our tests into an init binary.
> > 
> > >
> > >>
> > >>>+static int __efi_runtime hsearch_runtime(const u16 *name,
> > >>>+					 const efi_guid_t *vendor,
> > >>>+					 ACTION action,
> > >>>+					 _ENTRY **retval,
> > >>>+					 struct hsearch_data *htab)
> > >>>+{
> > >>>+	unsigned int hval;
> > >>>+	unsigned int count;
> > >>>+	unsigned int len;
> > >>>+	unsigned int idx, new;
> > >>>+
> > >>>+	/* Compute an value for the given string. */
> > >>>+	len = u16_strlen_runtime(name);
> >  >>
> > >>Can't the same variable name exist for different GUIDs? Why is the GUID
> > >>not considered in the hash?
> > >
> > >efi_cmpkey() does take into consideration GUID as well as the name.
> > 
> > My question concerned the hash.
> > 
> > >
> > >>>+	hval = len;
> > >>>+	count = len;
> > >>>+	while (count-- > 0) {
> > >>>+		hval <<= 4;
> > >>>+		hval += name[count];
> > >>>+	}
> > >>>+
> > >>>+	/*
> > >>>+	 * First hash function:
> > >>>+	 * simply take the modulo but prevent zero.
> > >>>+	 */
> > >>>+	hval %= htab->size;
> > >>>+	if (hval == 0)
> > >>>+		++hval;
> > >>>+
> > >>>+	/* The first index tried. */
> > >>>+	new = -1; /* not found */
> > >>>+	idx = hval;
> > >>>+
> > >>>+	if (htab->table[idx].used) {
> > >>>+		/*
> > >>>+		 * Further action might be required according to the
> > >>>+		 * action value.
> > >>>+		 */
> > >>>+		unsigned int hval2;
> > >>>+
> > >>>+		if (htab->table[idx].used == hval &&
> > >>>+		    !efi_cmpkey(&htab->table[idx], name, vendor)) {
> > >>>+			if (action == FIND) {
> > >>>+				*retval = &htab->table[idx];
> > >>>+				return idx;
> > >>>+			}
> > >>>+			/* we don't need to support overwrite */
> > >>>+			return -1;
> > >>>+		}
> > >>>+
> > >>>+		/*
> > >>>+		 * Second hash function:
> > >>>+		 * as suggested in [Knuth]
> > >>>+		 */
> > >>>+		hval2 = 1 + hval % (htab->size - 2);
> > >>>+
> > >>>+		do {
> > >>>+			/*
> > >>>+			 * Because SIZE is prime this guarantees to
> > >>>+			 * step through all available indices.
> > >>>+			 */
> > >>>+			if (idx <= hval2)
> > >>>+				idx = htab->size + idx - hval2;
> > >>>+			else
> > >>>+				idx -= hval2;
> > >>>+
> > >>>+			/*
> > >>>+			 * If we visited all entries leave the loop
> > >>>+			 * unsuccessfully.
> > >>>+			 */
> > >>>+			if (idx == hval)
> > >>>+				break;
> > >>>+
> > >>>+			/* If entry is found use it. */
> > >>>+			if (htab->table[idx].used == hval &&
> > >>>+			    !efi_cmpkey(&htab->table[idx], name, vendor)) {
> > >>>+				if (action == FIND) {
> > >>>+					*retval = &htab->table[idx];
> > >>>+					return idx;
> > >>>+				}
> > >>>+				/* we don't need to support overwrite */
> > >>>+				return -1;
> > >>>+			}
> > >>>+		} while (htab->table[idx].used);
> > >>>+
> > >>>+		if (!htab->table[idx].used)
> > >>>+			new = idx;
> > >>>+	} else {
> > >>>+		new = idx;
> > >>>+	}
> > >>>+
> > >>>+	/*
> > >>>+	 * An empty bucket has been found.
> > >>>+	 * The following code should never be executed after
> > >>>+	 * exit_boot_services()
> > >>>+	 */
> > >>>+	if (action == ENTER) {
> > >>>+		/*
> > >>>+		 * If table is full and another entry should be
> > >>>+		 * entered return with error.
> > >>>+		 */
> > >>>+		if (htab->filled == htab->size) {
> > >>>+			*retval = NULL;
> > >>>+			return 0;
> > >>>+		}
> > >>>+
> > >>>+		/* Create new entry */
> > >>>+		htab->table[new].used = hval;
> > >>>+		++htab->filled;
> > >>>+
> > >>>+		/* return new entry */
> > >>>+		*retval = &htab->table[new];
> > >>>+		return 1;
> > >>>+	}
> > >>>+
> > >>>+	*retval = NULL;
> > >>>+	return 0;
> > >>>+}
> > >>>+
> > >>>+/* from lib/hashtable.c */
> > >>>+static inline int isprime(unsigned int number)
> > >>>+{
> > >>>+	/* no even number will be passed */
> > >>>+	unsigned int div = 3;
> > >>>+
> > >>>+	while (div * div < number && number % div != 0)
> > >>>+		div += 2;
> > >>>+
> > >>>+	return number % div != 0;
> > >>>+}
> > >>>+
> > >>>+efi_status_t efi_freeze_variable_table(void)
> > >>
> > >>Please, add comments to your functions. It is not self-evident what this
> > >>function is meant to do.
> > >>
> > >>I cannot imagine why a variable cache should be frozen. It is a living
> > >>data structure until the system is switched off.
> > >
> > >I don't get your point.
> > 
> > Please, explain what you mean by freeze.
> > 
> > I suggest that the cache is read/write at all times.
> > 
> > Best regards
> > 
> > Heinrich
> > 
> > >
> > >>For a variable cache I expect that you allocate memory before handling
> > >>the first variable and never again. At runtime you will not have chance
> > >>to allocate memory anyway.
> > >
> > >I don't get your point.
> > >
> > >>This function is way too long. Pleae, break it down.
> > >
> > >Freezing is implemented in 2-phase steps, and some complexity
> > >is inevitable. I suppose adding some comments would be enough.
> > >
> > >-Takahiro Akashi
> > >
> > >>Best regards
> > >>
> > >>Heinrich
> > >>
> > >>>+{
> > >>>+	int var_num = 0;
> > >>>+	size_t var_data_size = 0;
> > >>>+	u16 *name;
> > >>>+	efi_uintn_t name_buf_len, name_len;
> > >>>+	efi_guid_t vendor;
> > >>>+	u32 attributes;
> > >>>+	u8 *mem_pool, *var_buf = NULL;
> > >>>+	size_t table_size, var_size, var_buf_size;
> > >>>+	_ENTRY *new = NULL;
> > >>>+	efi_status_t ret;
> > >>>+
> > >>>+	/* phase-1 loop */
> > >>>+	name_buf_len = 128;
> > >>>+	name = malloc(name_buf_len);
> > >>>+	if (!name)
> > >>>+		return EFI_OUT_OF_RESOURCES;
> > >>>+	name[0] = 0;
> > >>>+	for (;;) {
> > >>>+		name_len = name_buf_len;
> > >>>+		ret = EFI_CALL(efi_get_next_variable_name(&name_len, name,
> > >>>+							  &vendor));
> > >>>+		if (ret == EFI_NOT_FOUND) {
> > >>>+			break;
> > >>>+		} else if (ret == EFI_BUFFER_TOO_SMALL) {
> > >>>+			u16 *buf;
> > >>>+
> > >>>+			name_buf_len = name_len;
> > >>>+			buf = realloc(name, name_buf_len);
> > >>>+			if (!buf) {
> > >>>+				free(name);
> > >>>+				return EFI_OUT_OF_RESOURCES;
> > >>>+			}
> > >>>+			name = buf;
> > >>>+			name_len = name_buf_len;
> > >>>+			ret = EFI_CALL(efi_get_next_variable_name(&name_len,
> > >>>+								  name,
> > >>>+								  &vendor));
> > >>>+		}
> > >>>+
> > >>>+		if (ret != EFI_SUCCESS)
> > >>>+			return ret;
> > >>>+
> > >>>+		var_size = 0;
> > >>>+		ret = EFI_CALL(efi_get_variable(name, &vendor, &attributes,
> > >>>+						&var_size, NULL));
> > >>>+		if (ret != EFI_BUFFER_TOO_SMALL)
> > >>>+			return ret;
> > >>>+
> > >>>+		if (!(attributes & EFI_VARIABLE_RUNTIME_ACCESS))
> > >>>+			continue;
> > >>>+
> > >>>+		var_num++;
> > >>>+		var_data_size += (u16_strlen_runtime(name) + 1) * sizeof(u16);
> > >>>+		var_data_size += var_size;
> > >>>+		/* mem_pool must 2-byte aligned for u16 variable name */
> > >>>+		if (var_data_size & 0x1)
> > >>>+			var_data_size++;
> > >>>+	}
> > >>>+
> > >>>+	/*
> > >>>+	 * total of entries in hash table must be a prime number.
> > >>>+	 * The logic below comes from lib/hashtable.c
> > >>>+	 */
> > >>>+	var_num |= 1;               /* make odd */
> > >>>+	while (!isprime(var_num))
> > >>>+		var_num += 2;
> > >>>+
> > >>>+	/* We need table[var_num] for hsearch_runtime algo */
> > >>>+	table_size = sizeof(*efi_variable_table)
> > >>>+			+ sizeof(_ENTRY) * (var_num + 1) + var_data_size;
> > >>>+	ret = efi_allocate_pool(EFI_RUNTIME_SERVICES_DATA,
> > >>>+				table_size, (void **)&efi_variable_table);
> > >>>+	if (ret != EFI_SUCCESS)
> > >>>+		return ret;
> > >>>+
> > >>>+	efi_variable_table->size = var_num;
> > >>>+	efi_variable_table->table = (void *)efi_variable_table
> > >>>+					+ sizeof(*efi_variable_table);
> > >>>+	mem_pool = (u8 *)efi_variable_table->table
> > >>>+			+ sizeof(_ENTRY) * (var_num + 1);
> > >>>+
> > >>>+	var_buf_size = 128;
> > >>>+	var_buf = malloc(var_buf_size);
> > >>>+	if (!var_buf) {
> > >>>+		ret = EFI_OUT_OF_RESOURCES;
> > >>>+		goto err;
> > >>>+	}
> > >>>+
> > >>>+	/* phase-2 loop */
> > >>>+	name[0] = 0;
> > >>>+	name_len = name_buf_len;
> > >>>+	for (;;) {
> > >>>+		name_len = name_buf_len;
> > >>>+		ret = EFI_CALL(efi_get_next_variable_name(&name_len, name,
> > >>>+							  &vendor));
> > >>>+		if (ret == EFI_NOT_FOUND)
> > >>>+			break;
> > >>>+		else if (ret != EFI_SUCCESS)
> > >>>+			goto err;
> > >>>+
> > >>>+		var_size = var_buf_size;
> > >>>+		ret = EFI_CALL(efi_get_variable(name, &vendor, &attributes,
> > >>>+						&var_size, var_buf));
> > >>>+		if (ret == EFI_BUFFER_TOO_SMALL) {
> > >>>+			free(var_buf);
> > >>>+			var_buf_size = var_size;
> > >>>+			var_buf = malloc(var_buf_size);
> > >>>+			if (!var_buf) {
> > >>>+				ret = EFI_OUT_OF_RESOURCES;
> > >>>+				goto err;
> > >>>+			}
> > >>>+			ret = EFI_CALL(efi_get_variable(name, &vendor,
> > >>>+							&attributes,
> > >>>+							&var_size, var_buf));
> > >>>+		}
> > >>>+		if (ret != EFI_SUCCESS)
> > >>>+			goto err;
> > >>>+
> > >>>+		if (!(attributes & EFI_VARIABLE_RUNTIME_ACCESS))
> > >>>+			continue;
> > >>>+
> > >>>+		if (hsearch_runtime(name, &vendor, ENTER, &new,
> > >>>+				    efi_variable_table) <= 0) {
> > >>>+			/* This should not happen */
> > >>>+			ret = EFI_INVALID_PARAMETER;
> > >>>+			goto err;
> > >>>+		}
> > >>>+
> > >>>+		/* allocate space from RUNTIME DATA */
> > >>>+		name_len = (u16_strlen_runtime(name) + 1) * sizeof(u16);
> > >>>+		memcpy_runtime(mem_pool, name, name_len);
> > >>>+		new->name = mem_pool - (u8 *)new; /* offset */
> > >>>+		mem_pool += name_len;
> > >>>+
> > >>>+		memcpy_runtime(&new->vendor.b, &vendor.b, sizeof(vendor));
> > >>>+
> > >>>+		new->attributes = attributes;
> > >>>+
> > >>>+		memcpy_runtime(mem_pool, var_buf, var_size);
> > >>>+		new->data = mem_pool - (u8 *)new; /* offset */
> > >>>+		new->data_size = var_size;
> > >>>+		mem_pool += var_size;
> > >>>+
> > >>>+		/* mem_pool must 2-byte aligned for u16 variable name */
> > >>>+		if ((uintptr_t)mem_pool & 0x1)
> > >>>+			mem_pool++;
> > >>>+	}
> > >>>+#ifdef DEBUG
> > >>>+	name[0] = 0;
> > >>>+	name_len = name_buf_len;
> > >>>+	for (;;) {
> > >>>+		name_len = name_buf_len;
> > >>>+		ret = efi_get_next_variable_name_runtime(&name_len, name,
> > >>>+							 &vendor);
> > >>>+		if (ret == EFI_NOT_FOUND)
> > >>>+			break;
> > >>>+		else if (ret != EFI_SUCCESS)
> > >>>+			goto err;
> > >>>+
> > >>>+		var_size = var_buf_size;
> > >>>+		ret = efi_get_variable_runtime(name, &vendor, &attributes,
> > >>>+					       &var_size, var_buf);
> > >>>+		if (ret != EFI_SUCCESS)
> > >>>+			goto err;
> > >>>+
> > >>>+		printf("%ls_%pUl:\n", name, &vendor);
> > >>>+		printf("    attributes: 0x%x\n", attributes);
> > >>>+		printf("    value (size: 0x%lx)\n", var_size);
> > >>>+	}
> > >>>+#endif
> > >>>+	ret = EFI_SUCCESS;
> > >>>+
> > >>>+err:
> > >>>+	free(name);
> > >>>+	free(var_buf);
> > >>>+	if (ret != EFI_SUCCESS && efi_variable_table) {
> > >>>+		efi_free_pool(efi_variable_table);
> > >>>+		efi_variable_table = NULL;
> > >>>+	}
> > >>>+
> > >>>+	return ret;
> > >>>+}
> > >>>+
> > >>>+efi_status_t
> > >>>+__efi_runtime EFIAPI efi_get_variable_runtime(u16 *variable_name,
> > >>>+					      const efi_guid_t *vendor,
> > >>>+					      u32 *attributes,
> > >>>+					      efi_uintn_t *data_size,
> > >>>+					      void *data)
> > >>>+{
> > >>>+	_ENTRY *new;
> > >>>+
> > >>>+	if (!variable_name || !vendor || !data_size)
> > >>>+		return EFI_EXIT(EFI_INVALID_PARAMETER);
> > >>>+
> > >>>+	if (hsearch_runtime(variable_name, vendor, FIND, &new,
> > >>>+			    efi_variable_table) <= 0)
> > >>>+		return EFI_NOT_FOUND;
> > >>>+
> > >>>+	if (attributes)
> > >>>+		*attributes = new->attributes;
> > >>>+	if (*data_size < new->data_size) {
> > >>>+		*data_size = new->data_size;
> > >>>+		return EFI_BUFFER_TOO_SMALL;
> > >>>+	}
> > >>>+
> > >>>+	*data_size = new->data_size;
> > >>>+	memcpy_runtime(data, entry_data(new), new->data_size);
> > >>>+
> > >>>+	return EFI_SUCCESS;
> > >>>+}
> > >>>+
> > >>>+static int prev_idx __efi_runtime_data;
> > >>>+
> > >>>+efi_status_t
> > >>>+__efi_runtime EFIAPI efi_get_next_variable_name_runtime(
> > >>>+						efi_uintn_t *variable_name_size,
> > >>>+						u16 *variable_name,
> > >>>+						const efi_guid_t *vendor)
> > >>>+{
> > >>>+	_ENTRY *e;
> > >>>+	u16 *name;
> > >>>+	efi_uintn_t name_size;
> > >>>+
> > >>>+	if (!variable_name_size || !variable_name || !vendor)
> > >>>+		return EFI_INVALID_PARAMETER;
> > >>>+
> > >>>+	if (variable_name[0]) {
> > >>>+		/* sanity check for previous variable */
> > >>>+		if (prev_idx < 0)
> > >>>+			return EFI_INVALID_PARAMETER;
> > >>>+
> > >>>+		e = &efi_variable_table->table[prev_idx];
> > >>>+		if (!e->used || efi_cmpkey(e, variable_name, vendor))
> > >>>+			return EFI_INVALID_PARAMETER;
> > >>>+	} else {
> > >>>+		prev_idx = -1;
> > >>>+	}
> > >>>+
> > >>>+	/* next variable */
> > >>>+	while (++prev_idx <= efi_variable_table->size) {
> > >>>+		e = &efi_variable_table->table[prev_idx];
> > >>>+		if (e->used)
> > >>>+			break;
> > >>>+	}
> > >>>+	if (prev_idx > efi_variable_table->size)
> > >>>+		return EFI_NOT_FOUND;
> > >>>+
> > >>>+	name = entry_name(e);
> > >>>+	name_size = (u16_strlen_runtime(name) + 1)
> > >>>+			* sizeof(u16);
> > >>>+	if (*variable_name_size < name_size) {
> > >>>+		*variable_name_size = name_size;
> > >>>+		return EFI_BUFFER_TOO_SMALL;
> > >>>+	}
> > >>>+
> > >>>+	memcpy_runtime(variable_name, name, name_size);
> > >>>+	memcpy_runtime((void *)&vendor->b, &e->vendor.b, sizeof(vendor));
> > >>>+
> > >>>+	return EFI_SUCCESS;
> > >>>+}
> > >>>+#endif /* CONFIG_EFI_RUNTIME_GET_VARIABLE_CACHING */
> > >>>
> > >>
> > >
> > 

^ permalink raw reply	[flat|nested] 32+ messages in thread

* [U-Boot] [RFC 6/6] efi_loader: variable: support runtime variable access via cache
  2019-06-18  1:19         ` AKASHI Takahiro
  2019-06-18  2:25           ` AKASHI Takahiro
@ 2019-06-18 10:34           ` Ilias Apalodimas
  2019-06-18 20:45             ` Heinrich Schuchardt
  1 sibling, 1 reply; 32+ messages in thread
From: Ilias Apalodimas @ 2019-06-18 10:34 UTC (permalink / raw)
  To: u-boot

Hi all,

[...]
> > >>I think one version of the functions serving at runtime and boottime is
> > >>enough.
> > >>
> > >>The cache should be used both at runtime and at boottime.
> > >
> > >So do you mean that we should replace the existing "boottime" version
> > >of get/set_variable with my code (algorithm)?
> > >
> > >This is a bit complicated work because we should be able to *udpate*
> > >UEFI variables at boottime, but my version of hsearch_runtime() is
> > >a stripped (and modified) version and doesn't support it.
> > 
> > Do we really need a multilevel hash table? I would not expect hundreds
> > of variables.
> 
> Please don't change your point suddenly.
> Here we are discussing whether "The cache should be used both at runtime
> and at boottime" or not.
> 
Heinrich, the idea here is to present a copy of the variables on the OS
and totally disable RT variable updating from the OS.
If someone wants to update UEFI variables, we can pack them to a Capsule
(using FIT image format) and apply them on next reboot.
Given the fact that UEFI variables are not updated that often, isn't this a
viable option? If it is, then we need to keep the access separated
(as Akashi-san suggests) allowing bootime to change the variables.

> > >
> > >Making the existing hsearch_r() executable at UEFI runtime is,
> > >as I said before, quite painful.
> > 
> > You could start the cache implementation with a less complicated data
> > structure like a linked list.
> 
> This is totally a different issue. I listed this issue
> in my cover letter.
> 
> > >
> > >>Essentially I
> > >>expect three modules working together:
> > >>
> > >>UEFI API implementation <-> Cache <-> Persistence driver
> > >>
> > >>I would suggest to put each of these into a separate file.
> > >>
> > >>Both the API implementation and the Cache have to be available at
> > >>Boottime and at Runtime. A first version of the persistence driver may
> > >>only be working at boottime.
> > >
> > >Unfortunately, this is not practical right now because there is
> > >already some sort of assumption (and consensus) that we would re-use
> > >"Standalone MM services", which is already there in EDK2, as
> > >secure storage for UEFI variables.
> > >In the case, all the cache would be bypassed.
> > >In my old prototype, I utilized the cache but dropped that feature
> > >for several reasons.
> > 
> > What has EDK2 code to do with it?
> 
> Did you follow my comment below?
> > >Unfortunately, this is not practical right now because there is
> > >already some sort of assumption (and consensus) that we would re-use
> > >"Standalone MM services", which is already there in EDK2, as
> > >secure storage for UEFI variables.
We are already working towards having StandAloneMM as an early OP-TEE TA.
This will provide us with a secure variable storage for armv7/v8.

> 
> > In case of write you could do a write-through in your cache if needed.
> > 
> > >
> > >>The NV-cache content should be written to non-volatile memory on Reset()
> > >>and on ExitBootServices() and if possible when updating variables at
> > >>runtime.
> > >
> > >I'm not sure your intent here, but are you going to write back
> > >the cache only once?
> > >It won't work as every change of UEFI variable must be flushed
> > >to persistent storage instantly.
> > 
> > >


Thanks
/Ilias

^ permalink raw reply	[flat|nested] 32+ messages in thread

* [U-Boot] [RFC 1/6] efi_loader: runtime: make SetVirtualAddressMap configurable
  2019-06-16 21:52       ` Heinrich Schuchardt
@ 2019-06-18 18:11         ` Mark Kettenis
  0 siblings, 0 replies; 32+ messages in thread
From: Mark Kettenis @ 2019-06-18 18:11 UTC (permalink / raw)
  To: u-boot

> From: Heinrich Schuchardt <xypron.glpk@gmx.de>
> Date: Sun, 16 Jun 2019 23:52:49 +0200
> 
> On 6/15/19 11:14 PM, Mark Kettenis wrote:
> >> From: Heinrich Schuchardt <xypron.glpk@gmx.de>
> >> Date: Sat, 15 Jun 2019 21:46:02 +0200
> >>
> >> On 6/5/19 6:21 AM, AKASHI Takahiro wrote:
> >>> OS does not always need to call SetVirtualAddressMap.
> >>> (Ard confirmed this on arm64 linux.)
> >>> So let this API configurable. If disabled, it will return EFI_UNSUPPORTED
> >>> as UEFI specification requires.
> >>
> >> Currently we do not support this scenario. Alex's patch should go in first.
> >
> > OpenBSD/arm64 will always call this function.  It does this in order
> 
> OpenBSD/arm32 does not call it. I have no clue why the 32bit and 64bit
> code have diverged.

Beyond the device drivers, there isn't much shared code between
OpenBSD/armv7 and OpenBSD/arm64.  And the code that handles EFI
runtime services is tied quite closely to the memory management code
which is quite different between AArch32 and AArch64.  The primary
reason for adding support for EFI runtime services to OpenBSD/amd64
was to support getting and setting the time on machines with
"fullblown" UEFI support.  Machines that have armv7 CPUs *and*
fullblown UEFI support are pretty much non-existent so I didn't
implement support for armv7 yet.

> > to randomize the virtual addresses used by runtime services to make it
> > harder for an attacker to call into UEFI runtime services.  Note that
> > the UEFI 2.7 standard provides no indication that this interface might
> > be optional, so I don't think OpenBSD is doing anything wrong here.
> >
> > I think it is unwise to make this API configurable.  But disabling it
> > by default like this diff does would be a seriously bad idea.  It
> > means U-Boot would no longer be backwards compatible with UEFI 2.7.
> 
> The current UEFI specification is 2.8. It foresees that
> SetVirtualAddressMap() can be marked as unsupported but that does not
> imply that we have to make it customizable. And disabling it by default
> does not make much sense while most operating systems use it.

Right!  Thanks,

Mark

> >>> Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org>
> >>> ---
> >>>    lib/efi_loader/Kconfig       | 7 +++++++
> >>>    lib/efi_loader/efi_runtime.c | 8 ++++++++
> >>>    2 files changed, 15 insertions(+)
> >>>
> >>> diff --git a/lib/efi_loader/Kconfig b/lib/efi_loader/Kconfig
> >>> index 8bf4b1754d06..bb9c7582b14d 100644
> >>> --- a/lib/efi_loader/Kconfig
> >>> +++ b/lib/efi_loader/Kconfig
> >>> @@ -44,6 +44,13 @@ config EFI_SET_TIME
> >>>    	  Provide the SetTime() runtime service at boottime. This service
> >>>    	  can be used by an EFI application to adjust the real time clock.
> >>>
> >>> +config EFI_RUNTIME_SET_VIRTUAL_ADDRESS_MAP
> >>> +	bool "runtime service: SetVirtualAddressMap"
> >>> +	default n
> >>> +	help
> >>> +	  Enable SetVirtualAddressMap runtime service. This API will be
> >>> +	  called by OS just before it enters into virtual address mode.
> >>> +
> >>>    config EFI_DEVICE_PATH_TO_TEXT
> >>>    	bool "Device path to text protocol"
> >>>    	default y
> >>> diff --git a/lib/efi_loader/efi_runtime.c b/lib/efi_loader/efi_runtime.c
> >>> index 9c50955c9bd0..60442cb21d37 100644
> >>> --- a/lib/efi_loader/efi_runtime.c
> >>> +++ b/lib/efi_loader/efi_runtime.c
> >>> @@ -374,10 +374,12 @@ static const struct efi_runtime_detach_list_struct efi_runtime_detach_list[] = {
> >>>    		/* do_reset is gone */
> >>>    		.ptr = &efi_runtime_services.reset_system,
> >>>    		.patchto = efi_reset_system,
> >>> +#ifdef CONFIG_RUNTIME_SET_VIRTUAL_ADDRESS_MAP
> >>>    	}, {
> >>>    		/* invalidate_*cache_all are gone */
> >>>    		.ptr = &efi_runtime_services.set_virtual_address_map,
> >>>    		.patchto = &efi_unimplemented,
> >>> +#endif
> >>>    	}, {
> >>>    		/* RTC accessors are gone */
> >>>    		.ptr = &efi_runtime_services.get_time,
> >>> @@ -512,6 +514,7 @@ void efi_runtime_relocate(ulong offset, struct efi_mem_desc *map)
> >>>            invalidate_icache_all();
> >>>    }
> >>>
> >>> +#ifdef CONFIG_RUNTIME_SET_VIRTUAL_ADDRESS_MAP
> >>>    /**
> >>>     * efi_set_virtual_address_map() - change from physical to virtual mapping
> >>>     *
> >>> @@ -619,6 +622,7 @@ static efi_status_t EFIAPI efi_set_virtual_address_map(
> >>>
> >>>    	return EFI_EXIT(EFI_INVALID_PARAMETER);
> >>>    }
> >>> +#endif /* CONFIG_RUNTIME_SET_VIRTUAL_ADDRESS_MAP */
> >>>
> >>>    /**
> >>>     * efi_add_runtime_mmio() - add memory-mapped IO region
> >>> @@ -796,7 +800,11 @@ struct efi_runtime_services __efi_runtime_data efi_runtime_services = {
> >>>    	.set_time = &efi_set_time_boottime,
> >>>    	.get_wakeup_time = (void *)&efi_unimplemented,
> >>>    	.set_wakeup_time = (void *)&efi_unimplemented,
> >>> +#ifdef CONFIG_RUNTIME_SET_VIRTUAL_ADDRESS_MAP
> >>>    	.set_virtual_address_map = &efi_set_virtual_address_map,
> >>> +#else
> >>> +	.set_virtual_address_map = (void *)&efi_unimplemented,
> >>
> >> Depending on the ABI it is not save to use a function with another set
> >> of parameters.
> >>
> >> Best regards
> >>
> >> Heinrich
> >>
> >>> +#endif
> >>>    	.convert_pointer = (void *)&efi_invalid_parameter,
> >>>    	.get_variable = efi_get_variable,
> >>>    	.get_next_variable_name = efi_get_next_variable_name,
> >>>
> >>
> >> _______________________________________________
> >> U-Boot mailing list
> >> U-Boot at lists.denx.de
> >> https://lists.denx.de/listinfo/u-boot
> >
> 
> 

^ permalink raw reply	[flat|nested] 32+ messages in thread

* [U-Boot] [RFC 6/6] efi_loader: variable: support runtime variable access via cache
  2019-06-18 10:34           ` Ilias Apalodimas
@ 2019-06-18 20:45             ` Heinrich Schuchardt
  2019-06-19  1:25               ` AKASHI Takahiro
  0 siblings, 1 reply; 32+ messages in thread
From: Heinrich Schuchardt @ 2019-06-18 20:45 UTC (permalink / raw)
  To: u-boot

On 6/18/19 12:34 PM, Ilias Apalodimas wrote:
> Hi all,
>
> [...]
>>>>> I think one version of the functions serving at runtime and boottime is
>>>>> enough.
>>>>>
>>>>> The cache should be used both at runtime and at boottime.
>>>>
>>>> So do you mean that we should replace the existing "boottime" version
>>>> of get/set_variable with my code (algorithm)?
>>>>
>>>> This is a bit complicated work because we should be able to *udpate*
>>>> UEFI variables at boottime, but my version of hsearch_runtime() is
>>>> a stripped (and modified) version and doesn't support it.
>>>
>>> Do we really need a multilevel hash table? I would not expect hundreds
>>> of variables.
>>
>> Please don't change your point suddenly.
>> Here we are discussing whether "The cache should be used both at runtime
>> and at boottime" or not.
>>
> Heinrich, the idea here is to present a copy of the variables on the OS
> and totally disable RT variable updating from the OS.
> If someone wants to update UEFI variables, we can pack them to a Capsule
> (using FIT image format) and apply them on next reboot.
> Given the fact that UEFI variables are not updated that often, isn't this a
> viable option? If it is, then we need to keep the access separated
> (as Akashi-san suggests) allowing bootime to change the variables.
>
>>>>
>>>> Making the existing hsearch_r() executable at UEFI runtime is,
>>>> as I said before, quite painful.
>>>
>>> You could start the cache implementation with a less complicated data
>>> structure like a linked list.
>>
>> This is totally a different issue. I listed this issue
>> in my cover letter.
>>
>>>>
>>>>> Essentially I
>>>>> expect three modules working together:
>>>>>
>>>>> UEFI API implementation <-> Cache <-> Persistence driver
>>>>>
>>>>> I would suggest to put each of these into a separate file.
>>>>>
>>>>> Both the API implementation and the Cache have to be available at
>>>>> Boottime and at Runtime. A first version of the persistence driver may
>>>>> only be working at boottime.
>>>>
>>>> Unfortunately, this is not practical right now because there is
>>>> already some sort of assumption (and consensus) that we would re-use
>>>> "Standalone MM services", which is already there in EDK2, as
>>>> secure storage for UEFI variables.
>>>> In the case, all the cache would be bypassed.
>>>> In my old prototype, I utilized the cache but dropped that feature
>>>> for several reasons.
>>>
>>> What has EDK2 code to do with it?
>>
>> Did you follow my comment below?
>>>> Unfortunately, this is not practical right now because there is
>>>> already some sort of assumption (and consensus) that we would re-use
>>>> "Standalone MM services", which is already there in EDK2, as
>>>> secure storage for UEFI variables.
> We are already working towards having StandAloneMM as an early OP-TEE TA.
> This will provide us with a secure variable storage for armv7/v8.

What would this OP-TEE binary do? - This seems to be a source of
misunderstanding when reviewing this patch.

My guess is that OP-TEE is used to read non-volatile variables only once
when starting U-Boot and to write non-volatile variables whenever they
are changed.

All further reading of non-volatile variables and all access to volatile
variables will be handled by the U-Boot internal variable cache.

For volatile variables I would assume OP-TEE to never see them. This
requires that the U-Boot variable cachek supports reading from and
writing to the cache at runtime.

StandaloneMmPkg seems to be the hardware independent part of the
solution. Where will the hardware driver reside in your OP-TEE solution?

Is the EDK2 hardware store for variables of the MACCHIATObin here:
edk2-platforms/Silicon/Marvell/Drivers/Spi/MvFvbDxe/MvFvbDxe.c?

Which hardware platform will you use for testing the U-Boot development
of you OP-TEE driver?

Best regards

Heinrich

>
>>
>>> In case of write you could do a write-through in your cache if needed.
>>>
>>>>
>>>>> The NV-cache content should be written to non-volatile memory on Reset()
>>>>> and on ExitBootServices() and if possible when updating variables at
>>>>> runtime.
>>>>
>>>> I'm not sure your intent here, but are you going to write back
>>>> the cache only once?
>>>> It won't work as every change of UEFI variable must be flushed
>>>> to persistent storage instantly.
>>>
>>>>
>
>
> Thanks
> /Ilias
>

^ permalink raw reply	[flat|nested] 32+ messages in thread

* [U-Boot] [RFC 6/6] efi_loader: variable: support runtime variable access via cache
  2019-06-18 20:45             ` Heinrich Schuchardt
@ 2019-06-19  1:25               ` AKASHI Takahiro
  2019-06-19  5:13                 ` Ilias Apalodimas
  0 siblings, 1 reply; 32+ messages in thread
From: AKASHI Takahiro @ 2019-06-19  1:25 UTC (permalink / raw)
  To: u-boot

Heinrich,

On Tue, Jun 18, 2019 at 10:45:26PM +0200, Heinrich Schuchardt wrote:
> On 6/18/19 12:34 PM, Ilias Apalodimas wrote:
> >Hi all,
> >
> >[...]
> >>>>>I think one version of the functions serving at runtime and boottime is
> >>>>>enough.
> >>>>>
> >>>>>The cache should be used both at runtime and at boottime.
> >>>>
> >>>>So do you mean that we should replace the existing "boottime" version
> >>>>of get/set_variable with my code (algorithm)?
> >>>>
> >>>>This is a bit complicated work because we should be able to *udpate*
> >>>>UEFI variables at boottime, but my version of hsearch_runtime() is
> >>>>a stripped (and modified) version and doesn't support it.
> >>>
> >>>Do we really need a multilevel hash table? I would not expect hundreds
> >>>of variables.
> >>
> >>Please don't change your point suddenly.
> >>Here we are discussing whether "The cache should be used both at runtime
> >>and at boottime" or not.
> >>
> >Heinrich, the idea here is to present a copy of the variables on the OS
> >and totally disable RT variable updating from the OS.
> >If someone wants to update UEFI variables, we can pack them to a Capsule
> >(using FIT image format) and apply them on next reboot.
> >Given the fact that UEFI variables are not updated that often, isn't this a
> >viable option? If it is, then we need to keep the access separated
> >(as Akashi-san suggests) allowing bootime to change the variables.
> >
> >>>>
> >>>>Making the existing hsearch_r() executable at UEFI runtime is,
> >>>>as I said before, quite painful.
> >>>
> >>>You could start the cache implementation with a less complicated data
> >>>structure like a linked list.
> >>
> >>This is totally a different issue. I listed this issue
> >>in my cover letter.
> >>
> >>>>
> >>>>>Essentially I
> >>>>>expect three modules working together:
> >>>>>
> >>>>>UEFI API implementation <-> Cache <-> Persistence driver
> >>>>>
> >>>>>I would suggest to put each of these into a separate file.
> >>>>>
> >>>>>Both the API implementation and the Cache have to be available at
> >>>>>Boottime and at Runtime. A first version of the persistence driver may
> >>>>>only be working at boottime.
> >>>>
> >>>>Unfortunately, this is not practical right now because there is
> >>>>already some sort of assumption (and consensus) that we would re-use
> >>>>"Standalone MM services", which is already there in EDK2, as
> >>>>secure storage for UEFI variables.
> >>>>In the case, all the cache would be bypassed.
> >>>>In my old prototype, I utilized the cache but dropped that feature
> >>>>for several reasons.
> >>>
> >>>What has EDK2 code to do with it?
> >>
> >>Did you follow my comment below?
> >>>>Unfortunately, this is not practical right now because there is
> >>>>already some sort of assumption (and consensus) that we would re-use
> >>>>"Standalone MM services", which is already there in EDK2, as
> >>>>secure storage for UEFI variables.
> >We are already working towards having StandAloneMM as an early OP-TEE TA.
> >This will provide us with a secure variable storage for armv7/v8.
> 
> What would this OP-TEE binary do? - This seems to be a source of
> misunderstanding when reviewing this patch.

I and Ilias will give you more details offline, here's a short(?)
answer:

Standalone MM services here means a SPD entity which provides
[Get|Set]Variable APIs to non-secure side firmware, that is
currently EDK2. So the source code of Standalone MM services
is included in EDK2 repository as a matter of fact.

Here is one drawback: It won't allow for other entities running
concurrently on secure side. One example of useful secure feature
is (software-based) TPM. So Linaro is working on modifying/transforming 
Standalone MM to one OP-TEE application, which Ilias mentioned above.

> My guess is that OP-TEE is used to read non-volatile variables only once
> when starting U-Boot and to write non-volatile variables whenever they
> are changed.

So OP-TEE version of StMM is still on-going project and I assume
that this OP-TEE app will support the same set of functionality/APIs
as StMM does.

> All further reading of non-volatile variables and all access to volatile
> variables will be handled by the U-Boot internal variable cache.
> 
> For volatile variables I would assume OP-TEE to never see them. This
> requires that the U-Boot variable cachek supports reading from and
> writing to the cache at runtime.

No. As far as I correctly understand, StMM handles volatile
variables as well as non-volatile variables.
EDK2 on non-secure side will redirect user's request directly
to secure side even without *caching* variable's values.

> StandaloneMmPkg seems to be the hardware independent part of the
> solution. Where will the hardware driver reside in your OP-TEE solution?
> 
> Is the EDK2 hardware store for variables of the MACCHIATObin here:
> edk2-platforms/Silicon/Marvell/Drivers/Spi/MvFvbDxe/MvFvbDxe.c?
> 
> Which hardware platform will you use for testing the U-Boot development
> of you OP-TEE driver?

Ilias will be able to answer those questions.

Thanks,
-Takahiro Akashi

> Best regards
> 
> Heinrich
> 
> >
> >>
> >>>In case of write you could do a write-through in your cache if needed.
> >>>
> >>>>
> >>>>>The NV-cache content should be written to non-volatile memory on Reset()
> >>>>>and on ExitBootServices() and if possible when updating variables at
> >>>>>runtime.
> >>>>
> >>>>I'm not sure your intent here, but are you going to write back
> >>>>the cache only once?
> >>>>It won't work as every change of UEFI variable must be flushed
> >>>>to persistent storage instantly.
> >>>
> >>>>
> >
> >
> >Thanks
> >/Ilias
> >
> 

^ permalink raw reply	[flat|nested] 32+ messages in thread

* [U-Boot] [RFC 6/6] efi_loader: variable: support runtime variable access via cache
  2019-06-19  1:25               ` AKASHI Takahiro
@ 2019-06-19  5:13                 ` Ilias Apalodimas
  2019-06-19  6:24                   ` Heinrich Schuchardt
  0 siblings, 1 reply; 32+ messages in thread
From: Ilias Apalodimas @ 2019-06-19  5:13 UTC (permalink / raw)
  To: u-boot

Heinrich,

[...]
> > >>>>Unfortunately, this is not practical right now because there is
> > >>>>already some sort of assumption (and consensus) that we would re-use
> > >>>>"Standalone MM services", which is already there in EDK2, as
> > >>>>secure storage for UEFI variables.
> > >>>>In the case, all the cache would be bypassed.
> > >>>>In my old prototype, I utilized the cache but dropped that feature
> > >>>>for several reasons.
> > >>>
> > >>>What has EDK2 code to do with it?
> > >>
> > >>Did you follow my comment below?
> > >>>>Unfortunately, this is not practical right now because there is
> > >>>>already some sort of assumption (and consensus) that we would re-use
> > >>>>"Standalone MM services", which is already there in EDK2, as
> > >>>>secure storage for UEFI variables.
> > >We are already working towards having StandAloneMM as an early OP-TEE TA.
> > >This will provide us with a secure variable storage for armv7/v8.
> > 
> > What would this OP-TEE binary do? - This seems to be a source of
> > misunderstanding when reviewing this patch.
> 
> I and Ilias will give you more details offline, here's a short(?)
> answer:
> 
> Standalone MM services here means a SPD entity which provides
> [Get|Set]Variable APIs to non-secure side firmware, that is
> currently EDK2. So the source code of Standalone MM services
> is included in EDK2 repository as a matter of fact.
> 
> Here is one drawback: It won't allow for other entities running
> concurrently on secure side. One example of useful secure feature
> is (software-based) TPM. So Linaro is working on modifying/transforming 
> Standalone MM to one OP-TEE application, which Ilias mentioned above.
> 
Exactly. The current StMM implementation exists for Armv8 *only* in SPM (Secure
Partition Manager). The idea is to make it an OP-TEE application, so we can run
it on on Armv7s as well. As Akashi-san mentions SPD (Secure Payload Dispatcher)
and SPM are mutually exclusive so having everything as OP-TEE trusted
applications gives us a number of advantages at the moment.

> > My guess is that OP-TEE is used to read non-volatile variables only once
> > when starting U-Boot and to write non-volatile variables whenever they
> > are changed.
> 
> So OP-TEE version of StMM is still on-going project and I assume
> that this OP-TEE app will support the same set of functionality/APIs
> as StMM does.
Yes that's the goal 

> 
> > All further reading of non-volatile variables and all access to volatile
> > variables will be handled by the U-Boot internal variable cache.
> > 
> > For volatile variables I would assume OP-TEE to never see them. This
> > requires that the U-Boot variable cachek supports reading from and
> > writing to the cache at runtime.
> 
> No. As far as I correctly understand, StMM handles volatile
> variables as well as non-volatile variables.
> EDK2 on non-secure side will redirect user's request directly
> to secure side even without *caching* variable's values.
> 
Similar understanding here. The question is, will we have to think of something
for non-arm architectures?

> > StandaloneMmPkg seems to be the hardware independent part of the
> > solution. Where will the hardware driver reside in your OP-TEE solution?
It depends on where your hardware is. If you have a NOR flash directly connected
to the secure world the answer is yes. 
For starters we are going to use RPMB + U-Boot supplicant.

> > 
> > Is the EDK2 hardware store for variables of the MACCHIATObin here:
> > edk2-platforms/Silicon/Marvell/Drivers/Spi/MvFvbDxe/MvFvbDxe.c?
No idea, i can ask around.

> > 
> > Which hardware platform will you use for testing the U-Boot development
> > of you OP-TEE driver?
> 
> Ilias will be able to answer those questions.
- stm32mp1 ST board based on armv7 [1]
- Socionext DeveloperBox for armv8 [2]. This has a running EDKII implementation
of StMM in SPM. The underlying firmware should be irrelevant though since the 
whole functionality is contained within an OP-TEE TA (trusted application). If i
remember correctly that will need an extra driver in OP-TEE (if we port U-Boot
on that)
- TI AM6 board [3]. I don't have the board in my hands yet, so no details on it


[1] https://www.st.com/en/evaluation-tools/stm32mp157c-dk2.html
[2] https://www.96boards.org/product/developerbox/
[3] http://www.ti.com/tool/PROCESSOR-SDK-AM65X 


Regards
/Ilias

^ permalink raw reply	[flat|nested] 32+ messages in thread

* [U-Boot] [RFC 6/6] efi_loader: variable: support runtime variable access via cache
  2019-06-19  5:13                 ` Ilias Apalodimas
@ 2019-06-19  6:24                   ` Heinrich Schuchardt
  2019-06-19  7:07                     ` AKASHI Takahiro
  0 siblings, 1 reply; 32+ messages in thread
From: Heinrich Schuchardt @ 2019-06-19  6:24 UTC (permalink / raw)
  To: u-boot

On 6/19/19 7:13 AM, Ilias Apalodimas wrote:
> Heinrich,
>
> [...]
>>>>>>> Unfortunately, this is not practical right now because there is
>>>>>>> already some sort of assumption (and consensus) that we would re-use
>>>>>>> "Standalone MM services", which is already there in EDK2, as
>>>>>>> secure storage for UEFI variables.
>>>>>>> In the case, all the cache would be bypassed.
>>>>>>> In my old prototype, I utilized the cache but dropped that feature
>>>>>>> for several reasons.
>>>>>>
>>>>>> What has EDK2 code to do with it?
>>>>>
>>>>> Did you follow my comment below?
>>>>>>> Unfortunately, this is not practical right now because there is
>>>>>>> already some sort of assumption (and consensus) that we would re-use
>>>>>>> "Standalone MM services", which is already there in EDK2, as
>>>>>>> secure storage for UEFI variables.
>>>> We are already working towards having StandAloneMM as an early OP-TEE TA.
>>>> This will provide us with a secure variable storage for armv7/v8.
>>>
>>> What would this OP-TEE binary do? - This seems to be a source of
>>> misunderstanding when reviewing this patch.
>>
>> I and Ilias will give you more details offline, here's a short(?)
>> answer:
>>
>> Standalone MM services here means a SPD entity which provides
>> [Get|Set]Variable APIs to non-secure side firmware, that is
>> currently EDK2. So the source code of Standalone MM services
>> is included in EDK2 repository as a matter of fact.
>>
>> Here is one drawback: It won't allow for other entities running
>> concurrently on secure side. One example of useful secure feature
>> is (software-based) TPM. So Linaro is working on modifying/transforming
>> Standalone MM to one OP-TEE application, which Ilias mentioned above.
>>
> Exactly. The current StMM implementation exists for Armv8 *only* in SPM (Secure
> Partition Manager). The idea is to make it an OP-TEE application, so we can run
> it on on Armv7s as well. As Akashi-san mentions SPD (Secure Payload Dispatcher)
> and SPM are mutually exclusive so having everything as OP-TEE trusted
> applications gives us a number of advantages at the moment.
>
>>> My guess is that OP-TEE is used to read non-volatile variables only once
>>> when starting U-Boot and to write non-volatile variables whenever they
>>> are changed.
>>
>> So OP-TEE version of StMM is still on-going project and I assume
>> that this OP-TEE app will support the same set of functionality/APIs
>> as StMM does.
> Yes that's the goal

Do I understand it write:

In U-Boot we would have code that essentially provides the functionality
of EDK2's EFI_SMM_VARIABLE_PROTOCOL. Like
MdeModulePkg/Universal/Variable/RuntimeDxe/VariableSmm.c
this would talk via SMI calls with the hardware drivers, in this case
the OP-TEE app.

This would allow the OP-TEE app to be used both in U-Boot and in EDK2?

>
>>
>>> All further reading of non-volatile variables and all access to volatile
>>> variables will be handled by the U-Boot internal variable cache.
>>>
>>> For volatile variables I would assume OP-TEE to never see them. This
>>> requires that the U-Boot variable cachek supports reading from and
>>> writing to the cache at runtime.
>>
>> No. As far as I correctly understand, StMM handles volatile
>> variables as well as non-volatile variables.
>> EDK2 on non-secure side will redirect user's request directly
>> to secure side even without *caching* variable's values.
>>
> Similar understanding here. The question is, will we have to think of something
> for non-arm architectures?

The design should be open for other architectures. If we use the
EFI_SMM_VARIABLE_PROTOCOL as the interface we should be able to support
other architectures.

I am just wondering why does the OP-TEE module handle all the logic of
EFI_SMM_VARIABLE_PROTOCOL. Wouldn't something like the
EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL make more sense?

This protocol could also be used to implement CapsuleUpdate().

>
>>> StandaloneMmPkg seems to be the hardware independent part of the
>>> solution. Where will the hardware driver reside in your OP-TEE solution?
> It depends on where your hardware is. If you have a NOR flash directly connected
> to the secure world the answer is yes.
> For starters we are going to use RPMB + U-Boot supplicant.
>
>>>
>>> Is the EDK2 hardware store for variables of the MACCHIATObin here:
>>> edk2-platforms/Silicon/Marvell/Drivers/Spi/MvFvbDxe/MvFvbDxe.c?
> No idea, i can ask around.
>
>>>
>>> Which hardware platform will you use for testing the U-Boot development
>>> of you OP-TEE driver?
>>
>> Ilias will be able to answer those questions.
> - stm32mp1 ST board based on armv7 [1]
> - Socionext DeveloperBox for armv8 [2]. This has a running EDKII implementation
> of StMM in SPM. The underlying firmware should be irrelevant though since the
> whole functionality is contained within an OP-TEE TA (trusted application). If i
> remember correctly that will need an extra driver in OP-TEE (if we port U-Boot
> on that)
> - TI AM6 board [3]. I don't have the board in my hands yet, so no details on it

I have a MACCHIATObin. Would this also be usable for the testing?

Regards

Heinrich

>
>
> [1] https://www.st.com/en/evaluation-tools/stm32mp157c-dk2.html
> [2] https://www.96boards.org/product/developerbox/
> [3] http://www.ti.com/tool/PROCESSOR-SDK-AM65X
>
>
> Regards
> /Ilias
>

^ permalink raw reply	[flat|nested] 32+ messages in thread

* [U-Boot] [RFC 6/6] efi_loader: variable: support runtime variable access via cache
  2019-06-19  6:24                   ` Heinrich Schuchardt
@ 2019-06-19  7:07                     ` AKASHI Takahiro
  0 siblings, 0 replies; 32+ messages in thread
From: AKASHI Takahiro @ 2019-06-19  7:07 UTC (permalink / raw)
  To: u-boot

On Wed, Jun 19, 2019 at 08:24:50AM +0200, Heinrich Schuchardt wrote:
> On 6/19/19 7:13 AM, Ilias Apalodimas wrote:
> >Heinrich,
> >
> >[...]
> >>>>>>>Unfortunately, this is not practical right now because there is
> >>>>>>>already some sort of assumption (and consensus) that we would re-use
> >>>>>>>"Standalone MM services", which is already there in EDK2, as
> >>>>>>>secure storage for UEFI variables.
> >>>>>>>In the case, all the cache would be bypassed.
> >>>>>>>In my old prototype, I utilized the cache but dropped that feature
> >>>>>>>for several reasons.
> >>>>>>
> >>>>>>What has EDK2 code to do with it?
> >>>>>
> >>>>>Did you follow my comment below?
> >>>>>>>Unfortunately, this is not practical right now because there is
> >>>>>>>already some sort of assumption (and consensus) that we would re-use
> >>>>>>>"Standalone MM services", which is already there in EDK2, as
> >>>>>>>secure storage for UEFI variables.
> >>>>We are already working towards having StandAloneMM as an early OP-TEE TA.
> >>>>This will provide us with a secure variable storage for armv7/v8.
> >>>
> >>>What would this OP-TEE binary do? - This seems to be a source of
> >>>misunderstanding when reviewing this patch.
> >>
> >>I and Ilias will give you more details offline, here's a short(?)
> >>answer:
> >>
> >>Standalone MM services here means a SPD entity which provides
> >>[Get|Set]Variable APIs to non-secure side firmware, that is
> >>currently EDK2. So the source code of Standalone MM services
> >>is included in EDK2 repository as a matter of fact.
> >>
> >>Here is one drawback: It won't allow for other entities running
> >>concurrently on secure side. One example of useful secure feature
> >>is (software-based) TPM. So Linaro is working on modifying/transforming
> >>Standalone MM to one OP-TEE application, which Ilias mentioned above.
> >>
> >Exactly. The current StMM implementation exists for Armv8 *only* in SPM (Secure
> >Partition Manager). The idea is to make it an OP-TEE application, so we can run
> >it on on Armv7s as well. As Akashi-san mentions SPD (Secure Payload Dispatcher)
> >and SPM are mutually exclusive so having everything as OP-TEE trusted
> >applications gives us a number of advantages at the moment.
> >
> >>>My guess is that OP-TEE is used to read non-volatile variables only once
> >>>when starting U-Boot and to write non-volatile variables whenever they
> >>>are changed.
> >>
> >>So OP-TEE version of StMM is still on-going project and I assume
> >>that this OP-TEE app will support the same set of functionality/APIs
> >>as StMM does.
> >Yes that's the goal
> 
> Do I understand it write:
> 
> In U-Boot we would have code that essentially provides the functionality
> of EDK2's EFI_SMM_VARIABLE_PROTOCOL. Like
> MdeModulePkg/Universal/Variable/RuntimeDxe/VariableSmm.c
> this would talk via SMI calls with the hardware drivers, in this case
> the OP-TEE app.
> 
> This would allow the OP-TEE app to be used both in U-Boot and in EDK2?

I think so.

> >
> >>
> >>>All further reading of non-volatile variables and all access to volatile
> >>>variables will be handled by the U-Boot internal variable cache.
> >>>
> >>>For volatile variables I would assume OP-TEE to never see them. This
> >>>requires that the U-Boot variable cachek supports reading from and
> >>>writing to the cache at runtime.
> >>
> >>No. As far as I correctly understand, StMM handles volatile
> >>variables as well as non-volatile variables.
> >>EDK2 on non-secure side will redirect user's request directly
> >>to secure side even without *caching* variable's values.
> >>
> >Similar understanding here. The question is, will we have to think of something
> >for non-arm architectures?
> 
> The design should be open for other architectures. If we use the
> EFI_SMM_VARIABLE_PROTOCOL as the interface we should be able to support
> other architectures.

Yeah, but please note that EFI_SMM_VARIABLE_PROTOCOL is not part
of UEFI specification.

> I am just wondering why does the OP-TEE module handle all the logic of
> EFI_SMM_VARIABLE_PROTOCOL. Wouldn't something like the
> EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL make more sense?

It provides only read/write interfaces *without* protection.
As far as SetVariable API is concerned, you cannot compromise
any authenticated variables unless you can sign a given variable
with a trusted private key even if you can make a SMI call.

-Takahiro Akashi

> This protocol could also be used to implement CapsuleUpdate().
> 
> >
> >>>StandaloneMmPkg seems to be the hardware independent part of the
> >>>solution. Where will the hardware driver reside in your OP-TEE solution?
> >It depends on where your hardware is. If you have a NOR flash directly connected
> >to the secure world the answer is yes.
> >For starters we are going to use RPMB + U-Boot supplicant.
> >
> >>>
> >>>Is the EDK2 hardware store for variables of the MACCHIATObin here:
> >>>edk2-platforms/Silicon/Marvell/Drivers/Spi/MvFvbDxe/MvFvbDxe.c?
> >No idea, i can ask around.
> >
> >>>
> >>>Which hardware platform will you use for testing the U-Boot development
> >>>of you OP-TEE driver?
> >>
> >>Ilias will be able to answer those questions.
> >- stm32mp1 ST board based on armv7 [1]
> >- Socionext DeveloperBox for armv8 [2]. This has a running EDKII implementation
> >of StMM in SPM. The underlying firmware should be irrelevant though since the
> >whole functionality is contained within an OP-TEE TA (trusted application). If i
> >remember correctly that will need an extra driver in OP-TEE (if we port U-Boot
> >on that)
> >- TI AM6 board [3]. I don't have the board in my hands yet, so no details on it
> 
> I have a MACCHIATObin. Would this also be usable for the testing?

I think the answer will depend on what (feature) you want to test.

-Takahiro Akashi

> Regards
> 
> Heinrich
> 
> >
> >
> >[1] https://www.st.com/en/evaluation-tools/stm32mp157c-dk2.html
> >[2] https://www.96boards.org/product/developerbox/
> >[3] http://www.ti.com/tool/PROCESSOR-SDK-AM65X
> >
> >
> >Regards
> >/Ilias
> >
> 

^ permalink raw reply	[flat|nested] 32+ messages in thread

* [U-Boot] [RFC 3/6] efi_loader: support convert_pointer at runtime
  2019-06-17  5:41       ` Heinrich Schuchardt
@ 2019-07-13  6:16         ` Heinrich Schuchardt
  0 siblings, 0 replies; 32+ messages in thread
From: Heinrich Schuchardt @ 2019-07-13  6:16 UTC (permalink / raw)
  To: u-boot

On 6/17/19 7:41 AM, Heinrich Schuchardt wrote:
> On 6/17/19 3:15 AM, AKASHI Takahiro wrote:
>> On Sat, Jun 15, 2019 at 09:41:31PM +0200, Heinrich Schuchardt wrote:
>>> On 6/5/19 6:21 AM, AKASHI Takahiro wrote:
>>>> With this patch, ConvertPointer runtime service is enabled.
>>>> This function will be useful only after SetVirtualAddressMap is called
>>>> and before it exits according to UEFI specification.
>>>
>>> ConvertPointer() is called by drivers upon calling the notification
>>> functions of events registered as EVT_SIGNAL_VIRTUAL_ADDRESS_CHANGE.
>>>
>>> We still lack support for these events.
>>
>> So? What do you want me to do here?
>
> We will have to address this in a future patch.

Now that EVT_SIGNAL_VIRTUAL_ADDRESS_CHANGE is implemented and a unit
test for ConvertPointer() provided we should proceed with implementing
ConvertPointer().

Regards Heinrich

>
> Regards Heinrich
>
>>
>>>>
>>>> Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org>
>>>> ---
>>>>   lib/efi_loader/Kconfig       |  8 ++++
>>>>   lib/efi_loader/efi_runtime.c | 81
>>>> ++++++++++++++++++++++++++----------
>>>>   2 files changed, 66 insertions(+), 23 deletions(-)
>>>>
>>>> diff --git a/lib/efi_loader/Kconfig b/lib/efi_loader/Kconfig
>>>> index bb9c7582b14d..e2ef43157568 100644
>>>> --- a/lib/efi_loader/Kconfig
>>>> +++ b/lib/efi_loader/Kconfig
>>>> @@ -51,6 +51,14 @@ config EFI_RUNTIME_SET_VIRTUAL_ADDRESS_MAP
>>>>         Enable SetVirtualAddressMap runtime service. This API will be
>>>>         called by OS just before it enters into virtual address mode.
>>>>
>>>> +config EFI_RUNTIME_CONVERT_POINTER
>>>> +    bool "runtime service: ConvertPointer"
>>>> +    default n
>>>> +    help
>>>> +      Enable ConvertPointer runtime service. This API will be expected
>>>> +      to be called by UEFI drivers in relocating themselves to virtual
>>>> +      address space.
>>>> +
>>>>   config EFI_DEVICE_PATH_TO_TEXT
>>>>       bool "Device path to text protocol"
>>>>       default y
>>>> diff --git a/lib/efi_loader/efi_runtime.c
>>>> b/lib/efi_loader/efi_runtime.c
>>>> index cf202bb9ec07..ff3684a4b692 100644
>>>> --- a/lib/efi_loader/efi_runtime.c
>>>> +++ b/lib/efi_loader/efi_runtime.c
>>>> @@ -27,7 +27,6 @@ LIST_HEAD(efi_runtime_mmio);
>>>>
>>>>   static efi_status_t __efi_runtime EFIAPI efi_unimplemented(void);
>>>>   static efi_status_t __efi_runtime EFIAPI efi_device_error(void);
>>>> -static efi_status_t __efi_runtime EFIAPI efi_invalid_parameter(void);
>>>>
>>>>   /*
>>>>    * TODO(sjg at chromium.org): These defines and structures should
>>>> come from the ELF
>>>> @@ -108,6 +107,10 @@ efi_status_t efi_init_runtime_supported(void)
>>>>       efi_runtime_services_supported |=
>>>>                   EFI_RT_SUPPORTED_SET_VIRTUAL_ADDRESS_MAP;
>>>>   #endif
>>>> +#ifdef CONFIG_EFI_RUNTIME_CONVERT_POINTER
>>>> +    efi_runtime_services_supported |=
>>>> +                EFI_RT_SUPPORTED_CONVERT_POINTER;
>>>> +#endif
>>>>
>>>>       return EFI_CALL(efi_set_variable(L"RuntimeServicesSupported",
>>>>                        &efi_global_variable_guid,
>>>> @@ -392,6 +395,39 @@ efi_status_t __weak __efi_runtime EFIAPI
>>>> efi_set_time(struct efi_time *time)
>>>>       return EFI_UNSUPPORTED;
>>>>   }
>>>>
>>>> +#ifdef CONFIG_EFI_RUNTIME_CONVERT_POINTER
>>>> +static struct efi_mem_desc *efi_virtmap __efi_runtime_data;
>>>> +static int efi_virtmap_num __efi_runtime_data;
>>>> +
>>>
>>> Please, put a description of the function and its parameters here.
>>
>> Okay.
>>
>>>> +static efi_status_t __efi_runtime EFIAPI
>>>> efi_convert_pointer(unsigned long dbg,
>>>> +                                 void **address)
>>>> +{
>>>> +    struct efi_mem_desc *map;
>>>> +    efi_physical_addr_t addr;
>>>> +    int i;
>>>> +
>>>> +    if (!efi_virtmap)
>>>> +        return EFI_UNSUPPORTED;
>>>> +
>>>> +    if (!address)
>>>> +        return EFI_INVALID_PARAMETER;
>>>> +
>>>> +    for (i = 0, map = efi_virtmap; i < efi_virtmap_num; i++, map++) {
>>>> +        addr = (efi_physical_addr_t)*address;
>>> This line should be before the loop.
>>>
>>>> +        if (addr >= map->physical_start &&
>>>> +            (addr < (map->physical_start
>>>
>>> %s/(addr/addr/  No need for that extra parenthesis.
>>>
>>>> +                 + (map->num_pages << EFI_PAGE_SHIFT)))) {
>>>> +            *address = (void *)map->virtual_start;
>>>
>>> I guess on 32bit this will end in a warning? Wouldn't you need to
>>> convert to uintptr_t first?
>>>
>>>> +            *address += addr - map->physical_start;
>>>
>>> I think a single assignment is enough. Here you are updating a 32bit
>>> pointer with the difference of two u64. I would expect a warning.
>>
>> I will check.
>>
>>> *address = (void *)(uintptr_t)
>>> (addr - map->physical_start + map->virtual_start);
>>>
>>> Or do all calculation with addr before copying to *address.
>>>
>>>> +
>>>> +            return EFI_SUCCESS;
>>>> +        }
>>>> +    }
>>>> +
>>>> +    return EFI_NOT_FOUND;
>>>> +}
>>>> +#endif
>>>> +
>>>>   struct efi_runtime_detach_list_struct {
>>>>       void *ptr;
>>>>       void *patchto;
>>>> @@ -599,6 +635,10 @@ static efi_status_t EFIAPI
>>>> efi_set_virtual_address_map(
>>>>           return EFI_EXIT(EFI_INVALID_PARAMETER);
>>>>       }
>>>>
>>>> +    efi_virtmap = virtmap;
>>>> +    efi_virtmap_num = n;
>>>> +
>>>> +#if 0 /* FIXME: This code is fragile as calloc is used in
>>>> add_runtime_mmio */
>>>>       /* Rebind mmio pointers */
>>>>       for (i = 0; i < n; i++) {
>>>>           struct efi_mem_desc *map = (void*)virtmap +
>>>> @@ -622,14 +662,14 @@ static efi_status_t EFIAPI
>>>> efi_set_virtual_address_map(
>>>>                   *lmmio->ptr = (void *)new_addr;
>>>>               }
>>>>           }
>>>> -        if ((map_start <= (uintptr_t)systab.tables) &&
>>>> -            (map_end >= (uintptr_t)systab.tables)) {
>>>> -            char *ptr = (char *)systab.tables;
>>>> -
>>>> -            ptr += off;
>>>> -            systab.tables = (struct efi_configuration_table *)ptr;
>>>> -        }
>>>
>>> This looks like an unrelated change.
>>
>> It does.
>> This code should be replaced by:
>>>> +    /* FIXME */
>>>> +    efi_convert_pointer(0, (void **)&systab.tables);
>>
>> -Takahiro Akashi
>>
>>> Put it into a separate patch, please.
>>>
>>> Best regards
>>>
>>> Heinrich
>>>
>>>>       }
>>>> +#endif
>>>> +
>>>> +    /* FIXME */
>>>> +    efi_convert_pointer(0, (void **)&systab.tables);
>>>> +
>>>> +    /* All fixes must be done before this line */
>>>> +    efi_virtmap = NULL;
>>>>
>>>>       /* Move the actual runtime code over */
>>>>       for (i = 0; i < n; i++) {
>>>> @@ -644,6 +684,11 @@ static efi_status_t EFIAPI
>>>> efi_set_virtual_address_map(
>>>>               /* Once we're virtual, we can no longer handle
>>>>                  complex callbacks */
>>>>               efi_runtime_detach(new_offset);
>>>> +
>>>> +            /*
>>>> +             * FIXME:
>>>> +             * We can no longer update RuntimeServicesSupported.
>>>> +             */
>>>>               return EFI_EXIT(EFI_SUCCESS);
>>>>           }
>>>>       }
>>>> @@ -733,20 +778,6 @@ static efi_status_t __efi_runtime EFIAPI
>>>> efi_device_error(void)
>>>>       return EFI_DEVICE_ERROR;
>>>>   }
>>>>
>>>> -/**
>>>> - * efi_invalid_parameter() - replacement function, returns
>>>> EFI_INVALID_PARAMETER
>>>> - *
>>>> - * This function is used after SetVirtualAddressMap() is called as
>>>> replacement
>>>> - * for services that are not available anymore due to constraints
>>>> of the U-Boot
>>>> - * implementation.
>>>> - *
>>>> - * Return:    EFI_INVALID_PARAMETER
>>>> - */
>>>> -static efi_status_t __efi_runtime EFIAPI efi_invalid_parameter(void)
>>>> -{
>>>> -    return EFI_INVALID_PARAMETER;
>>>> -}
>>>> -
>>>>   /**
>>>>    * efi_update_capsule() - process information from operating system
>>>>    *
>>>> @@ -833,7 +864,11 @@ struct efi_runtime_services __efi_runtime_data
>>>> efi_runtime_services = {
>>>>   #else
>>>>       .set_virtual_address_map = (void *)&efi_unimplemented,
>>>>   #endif
>>>> -    .convert_pointer = (void *)&efi_invalid_parameter,
>>>> +#ifdef CONFIG_EFI_RUNTIME_CONVERT_POINTER
>>>> +    .convert_pointer = &efi_convert_pointer,
>>>> +#else
>>>> +    .convert_pointer = (void *)&efi_unimplemented,
>>>
>>> I feel uneasy using a function efi_unimplemented() with a different
>>> number of parameters here. Depending on the ABI this may lead to a
>>> crash.
>>>
>>> Best regards
>>>
>>> Heinrich
>>>
>>>> +#endif
>>>>       .get_variable = efi_get_variable,
>>>>       .get_next_variable_name = efi_get_next_variable_name,
>>>>       .set_variable = efi_set_variable,
>>>>
>>>
>>
>
>

^ permalink raw reply	[flat|nested] 32+ messages in thread

end of thread, other threads:[~2019-07-13  6:16 UTC | newest]

Thread overview: 32+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2019-06-05  4:21 [U-Boot] [RFC 0/6] efi_loader: support runtime variable access via cache AKASHI Takahiro
2019-06-05  4:21 ` [U-Boot] [RFC 1/6] efi_loader: runtime: make SetVirtualAddressMap configurable AKASHI Takahiro
2019-06-15 19:46   ` Heinrich Schuchardt
2019-06-15 21:14     ` Mark Kettenis
2019-06-16 21:52       ` Heinrich Schuchardt
2019-06-18 18:11         ` Mark Kettenis
2019-06-17  1:05     ` AKASHI Takahiro
2019-06-05  4:21 ` [U-Boot] [RFC 2/6] efi: add RuntimeServicesSupported variable AKASHI Takahiro
2019-06-13  5:56   ` Heinrich Schuchardt
2019-06-13  7:06     ` AKASHI Takahiro
2019-06-13  9:17       ` Heinrich Schuchardt
2019-06-13  9:21         ` Heinrich Schuchardt
2019-06-05  4:21 ` [U-Boot] [RFC 3/6] efi_loader: support convert_pointer at runtime AKASHI Takahiro
2019-06-15 19:41   ` Heinrich Schuchardt
2019-06-17  1:15     ` AKASHI Takahiro
2019-06-17  5:41       ` Heinrich Schuchardt
2019-07-13  6:16         ` Heinrich Schuchardt
2019-06-05  4:21 ` [U-Boot] [RFC 4/6] efi_loader: Patch non-runtime code out at ExitBootServices already AKASHI Takahiro
2019-06-05  4:21 ` [U-Boot] [RFC 5/6] cmd: efidebug: add "boot exit" sub-command AKASHI Takahiro
2019-06-05  4:21 ` [U-Boot] [RFC 6/6] efi_loader: variable: support runtime variable access via cache AKASHI Takahiro
2019-06-15 19:01   ` Heinrich Schuchardt
2019-06-17  1:51     ` AKASHI Takahiro
2019-06-17 19:52       ` Heinrich Schuchardt
2019-06-18  1:19         ` AKASHI Takahiro
2019-06-18  2:25           ` AKASHI Takahiro
2019-06-18 10:34           ` Ilias Apalodimas
2019-06-18 20:45             ` Heinrich Schuchardt
2019-06-19  1:25               ` AKASHI Takahiro
2019-06-19  5:13                 ` Ilias Apalodimas
2019-06-19  6:24                   ` Heinrich Schuchardt
2019-06-19  7:07                     ` AKASHI Takahiro
2019-06-05 10:34 ` [U-Boot] [RFC 0/6] efi_loader: " Heinrich Schuchardt

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.