[syslinux] [PATCH v2] efi: Call ExitBootServices at least twice

celelibi at gmail.com celelibi at gmail.com
Fri Nov 20 12:18:37 PST 2015


From: Sylvain Gault <sylvain.gault at gmail.com>

Some firmware implementations may need ExitBootServices to be called
twice. The second time with an updated memory map key.

Signed-off-by: Sylvain Gault <sylvain.gault at gmail.com>
---
 efi/main.c | 92 ++++++++++++++++++++++++++++++++++++++++++++++++++++++--------
 1 file changed, 81 insertions(+), 11 deletions(-)

diff --git a/efi/main.c b/efi/main.c
index fd95f5c..94353c7 100644
--- a/efi/main.c
+++ b/efi/main.c
@@ -1001,19 +1001,96 @@ static int handle_ramdisks(struct linux_header *hdr,
 	return 0;
 }
 
+/*
+ * Try to use the given map buffer when calling GetMemoryMap.
+ * Reallocate the buffer if if it's too small.
+ * Make sure there is always at least adddesc free descriptors in the buffer.
+ * Always set *allocsize to the size of the buffer.
+ */
+static EFI_MEMORY_DESCRIPTOR *
+get_memory_map_realloc(EFI_MEMORY_DESCRIPTOR *map, UINTN *allocsize,
+		       UINTN *nr_entries, UINTN *key, UINTN *desc_sz,
+		       UINT32 *desc_ver, UINTN nfreedesc)
+{
+	EFI_STATUS status;
+	UINTN size, doubleaddsize, constaddsize;
+
+	size = *allocsize;
+
+	/*
+	 * First try with the given buffer.
+	 * Also get the minimal buffer size needed and the size of a single
+	 * memory descriptor.
+	 */
+	status = uefi_call_wrapper(BS->GetMemoryMap, 5, &size, map, key,
+	                           desc_sz, desc_ver);
+
+	constaddsize = nfreedesc * *desc_sz;
+	doubleaddsize = *desc_sz;
+
+	while (status == EFI_BUFFER_TOO_SMALL ||
+	       (status == EFI_SUCCESS && size + constaddsize > *allocsize)) {
+		if (map)
+			FreePool(map);
+
+		doubleaddsize *= 2;
+		size += doubleaddsize + constaddsize;
+		*allocsize = size;
+		map = AllocatePool(size);
+
+		status = uefi_call_wrapper(BS->GetMemoryMap, 5, &size, map, key,
+		                           desc_sz, desc_ver);
+	}
+
+	if (status == EFI_SUCCESS) {
+		*nr_entries = size / *desc_sz;
+		return map;
+	}
+
+	if (map)
+		FreePool(map);
+	return NULL;
+}
+
 static int exit_boot(struct boot_params *bp)
 {
 	struct e820_entry *e820buf, *e;
 	EFI_MEMORY_DESCRIPTOR *map;
 	EFI_STATUS status;
 	uint32_t e820_type;
-	UINTN i, nr_entries, key, desc_sz;
+	UINTN i, nr_entries, key, desc_sz, allocsize, addsize;
 	UINT32 desc_ver;
+	int retry;
 
-	/* Build efi memory map */
-	map = get_memory_map(&nr_entries, &key, &desc_sz, &desc_ver);
-	if (!map)
+	/*
+	 * Build efi memory map and call ExitBootServices as soon after as
+	 * possible. Some implementations need two calls to ExitBootServices.
+	 */
+
+	map = NULL;
+	allocsize = 0;
+	addsize = 4;
+	retry = 0;
+	do {
+		map = get_memory_map_realloc(map, &allocsize, &nr_entries, &key,
+		                             &desc_sz, &desc_ver, addsize);
+		if (!map)
+			return -1;
+
+		/* Only request additional memory on the first try. */
+		addsize = 0;
+
+		status = uefi_call_wrapper(BS->ExitBootServices, 2,
+		                           image_handle, key);
+		retry++;
+	} while (status != EFI_SUCCESS && retry < 2);
+
+	if (status != EFI_SUCCESS) {
+		printf("Failed to exit boot services: 0x%016lx\n", status);
+		FreePool(map);
 		return -1;
+	}
+
 
 	bp->efi.memmap = (uint32_t)(unsigned long)map;
 	bp->efi.memmap_size = nr_entries * desc_sz;
@@ -1088,13 +1165,6 @@ static int exit_boot(struct boot_params *bp)
 
 	bp->e820_entries = e - e820buf;
 
-	status = uefi_call_wrapper(BS->ExitBootServices, 2, image_handle, key);
-	if (status != EFI_SUCCESS) {
-		printf("Failed to exit boot services: 0x%016lx\n", status);
-		FreePool(map);
-		return -1;
-	}
-
 	return 0;
 }
 
-- 
2.6.2



More information about the Syslinux mailing list