[PATCH V3 2/4] efi/libstub: Introduce ExitBootServices helper

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



The spec allows ExitBootServices to fail with EFI_INVALID_PARAMETER if a
race condition has occurred where the EFI has updated the memory map after
the stub grabbed a reference to the map.  The spec defines a retry
proceedure with specific requirements to handle this scenario.

No current stub implementation correctly follows the spec in this regard,
so add a helper to the stub library that correctly adhears to the spec and
abstracts the complexity from stubs.

Signed-off-by: Jeffrey Hugo <jhugo@xxxxxxxxxxxxxx>
---
 drivers/firmware/efi/libstub/efi-stub-helper.c | 93 ++++++++++++++++++++++++++
 include/linux/efi.h                            | 19 ++++++
 2 files changed, 112 insertions(+)

diff --git a/drivers/firmware/efi/libstub/efi-stub-helper.c b/drivers/firmware/efi/libstub/efi-stub-helper.c
index 3071269..d5be0b5 100644
--- a/drivers/firmware/efi/libstub/efi-stub-helper.c
+++ b/drivers/firmware/efi/libstub/efi-stub-helper.c
@@ -720,3 +720,96 @@ char *efi_convert_cmdline(efi_system_table_t *sys_table_arg,
 	*cmd_line_len = options_bytes;
 	return (char *)cmdline_addr;
 }
+
+/*
+ * Handle calling ExitBootServices according to the requirements set out by the
+ * spec.  Obtains the current memory map, and returns that info after calling
+ * ExitBootServices.  Client has the option to specify a function to process the
+ * memory map data.  A client specific structure may be passed to the function
+ * via priv.  The client function may be called multiple times.
+ */
+efi_status_t efi_exit_boot_services(efi_system_table_t *sys_table,
+				    void *handle,
+				    efi_memory_desc_t **memory_map,
+				    unsigned long *map_size,
+				    unsigned long *desc_size,
+				    u32 *desc_ver,
+				    unsigned long *mmap_key,
+				    void *priv,
+				    efi_status_t (*priv_func)(
+						efi_system_table_t *sys_table,
+						void *handle,
+						efi_memory_desc_t *memory_map,
+						unsigned long *map_size,
+						unsigned long *desc_size,
+						u32 *desc_ver,
+						unsigned long *mmap_key,
+						unsigned long buff_size,
+						void *priv))
+{
+	efi_status_t status;
+	unsigned long buff_size;
+
+	status = efi_get_memory_map(sys_table, memory_map, map_size,
+				    desc_size, desc_ver, mmap_key, &buff_size);
+
+	if (status != EFI_SUCCESS)
+		goto fail;
+
+	if (priv_func) {
+		status = priv_func(sys_table, handle, *memory_map, map_size,
+				   desc_size, desc_ver, mmap_key, buff_size,
+				   priv);
+		if (status != EFI_SUCCESS)
+			goto free_map;
+	}
+
+	status = sys_table->boottime->exit_boot_services(handle, *mmap_key);
+
+	if (status == EFI_INVALID_PARAMETER) {
+		/*
+		 * The memory map changed between efi_get_memory_map() and
+		 * exit_boot_services().  Per the spec we need to get the
+		 * updated map, and try again.  The spec implies one retry
+		 * should be sufficent, which is confirmed against the EDK2
+		 * implementation.  Per the spec, we can only invoke
+		 * get_memory_map() and exit_boot_services() - we cannot alloc
+		 * so efi_get_memory_map() cannot be used, and we must reuse
+		 * the buffer.  For all practical purposes, the headroom in the
+		 * buffer should account for any changes in the map so the call
+		 * to get_memory_map() is expected to succeed here.
+		 */
+		*map_size = buff_size;
+		status = sys_table->boottime->get_memory_map(map_size,
+							     *memory_map,
+							     mmap_key,
+							     desc_size,
+							     desc_ver);
+		if (status != EFI_SUCCESS)
+			/* exit_boot_services() was called, thus cannot free*/
+			goto fail;
+
+		if (priv_func) {
+			status = priv_func(sys_table, handle, *memory_map,
+					   map_size, desc_size, desc_ver,
+					   mmap_key, buff_size, priv);
+			if (status != EFI_SUCCESS)
+				/* exit_boot_services() called, cannot free*/
+				goto fail;
+		}
+
+		status = sys_table->boottime->exit_boot_services(handle,
+								 *mmap_key);
+	}
+
+	if (status != EFI_SUCCESS)
+		/* exit_boot_services() was called, thus cannot free*/
+		goto fail;
+
+	return EFI_SUCCESS;
+
+free_map:
+	sys_table->boottime->free_pool(*memory_map);
+fail:
+	return status;
+}
diff --git a/include/linux/efi.h b/include/linux/efi.h
index c47fc5f..96f7b74 100644
--- a/include/linux/efi.h
+++ b/include/linux/efi.h
@@ -1466,4 +1466,23 @@ efi_status_t efi_setup_gop(efi_system_table_t *sys_table_arg,
 			   unsigned long size);
 
 bool efi_runtime_disabled(void);
+
+efi_status_t efi_exit_boot_services(efi_system_table_t *sys_table,
+				    void *handle,
+				    efi_memory_desc_t **memory_map,
+				    unsigned long *map_size,
+				    unsigned long *desc_size,
+				    u32 *desc_ver,
+				    unsigned long *mmap_key,
+				    void *priv,
+				    efi_status_t (*priv_func)(
+						efi_system_table_t *sys_table,
+						void *handle,
+						efi_memory_desc_t *memory_map,
+						unsigned long *map_size,
+						unsigned long *desc_size,
+						u32 *desc_ver,
+						unsigned long *mmap_key,
+						unsigned long buff_size,
+						void *priv));
 #endif /* _LINUX_EFI_H */
-- 
Qualcomm Datacenter Technologies as an affiliate of Qualcomm Technologies, Inc.
Qualcomm Technologies, Inc. is a member of the
Code Aurora Forum, a Linux Foundation Collaborative Project.

--
To unsubscribe from this list: send the line "unsubscribe linux-efi" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html



[Index of Archives]     [Linux ARM Kernel]     [Linux ARM]     [Linux Omap]     [Fedora ARM]     [IETF Annouce]     [Security]     [Bugtraq]     [Linux OMAP]     [Linux MIPS]     [ECOS]     [Asterisk Internet PBX]     [Linux API]

  Powered by Linux