aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJan Beulich <jbeulich@suse.com>2014-11-17 15:07:03 +0100
committerJan Beulich <jbeulich@suse.com>2014-11-17 15:07:03 +0100
commit0540b854f6733759593e829bc3f13c9b45974e32 (patch)
treec0d425addde7d8d04791a27c04ae1e6f194f7541
parent404227138e1e49c8073e946649a8d4173b35625c (diff)
EFI: allow retry of ExitBootServices() call
The specification is kind of vague under what conditions ExitBootServices() may legitimately fail, requiring the OS loader to retry: "If MapKey value is incorrect, ExitBootServices() returns EFI_INVALID_PARAMETER and GetMemoryMap() with ExitBootServices() must be called again. Firmware implementation may choose to do a partial shutdown of the boot services during the first call to ExitBootServices(). EFI OS loader should not make calls to any boot service function other then GetMemoryMap() after the first call to ExitBootServices()." While our code guarantees the map key to be valid, there are systems where a firmware internal notification sent while processing ExitBootServices() reportedly results in changes to the memory map. In that case, make a best effort second try: Avoid any boot service calls other than the two named above, with the possible exception of error paths. Those aren't a problem, since if we end up needing to retry, we're hosed when something goes wrong as much as if we didn't make the retry attempt. For x86, a minimal adjustment to efi_arch_process_memory_map() is needed for it to cope with potentially being called a second time. For arm64, while efi_process_memory_map_bootinfo() is easy to verify that it can safely be called more than once without violating spec constraints, it's not so obvious for fdt_add_uefi_nodes(), hence a step by step approach: - deletion of memory nodes and memory reserve map entries: the 2nd pass shouldn't find any as the 1st one deleted them all, - a "chosen" node should be found as it got added in the 1st pass, - the various "linux,uefi-*" nodes all got added during the 1st pass and hence only their contents may get updated. Signed-off-by: Jan Beulich <jbeulich@suse.com> Reviewed-by: Roy Franz <roy.franz@linaro.org> Release-acked-by: Konrad Rzeszutek Wilk <konrad.wilk@oracle.com>
-rw-r--r--xen/arch/x86/efi/efi-boot.h2
-rw-r--r--xen/common/efi/boot.c24
2 files changed, 16 insertions, 10 deletions
diff --git a/xen/arch/x86/efi/efi-boot.h b/xen/arch/x86/efi/efi-boot.h
index 454ffb6fdb..3a3b4fe3b8 100644
--- a/xen/arch/x86/efi/efi-boot.h
+++ b/xen/arch/x86/efi/efi-boot.h
@@ -140,7 +140,7 @@ static void __init efi_arch_process_memory_map(EFI_SYSTEM_TABLE *SystemTable,
/* Populate E820 table and check trampoline area availability. */
e = e820map - 1;
- for ( i = 0; i < map_size; i += desc_size )
+ for ( e820nr = i = 0; i < map_size; i += desc_size )
{
EFI_MEMORY_DESCRIPTOR *desc = map + i;
u64 len = desc->NumberOfPages << EFI_PAGE_SHIFT;
diff --git a/xen/common/efi/boot.c b/xen/common/efi/boot.c
index 3c63c0a395..c68efdb2b5 100644
--- a/xen/common/efi/boot.c
+++ b/xen/common/efi/boot.c
@@ -703,7 +703,7 @@ efi_start(EFI_HANDLE ImageHandle, EFI_SYSTEM_TABLE *SystemTable)
EFI_GRAPHICS_OUTPUT_PROTOCOL *gop = NULL;
EFI_GRAPHICS_OUTPUT_MODE_INFORMATION *mode_info;
union string section = { NULL }, name;
- bool_t base_video = 0;
+ bool_t base_video = 0, retry;
char *option_str;
bool_t use_cfg_file;
@@ -1051,17 +1051,23 @@ efi_start(EFI_HANDLE ImageHandle, EFI_SYSTEM_TABLE *SystemTable)
if ( !efi_memmap )
blexit(L"Unable to allocate memory for EFI memory map");
- status = efi_bs->GetMemoryMap(&efi_memmap_size, efi_memmap, &map_key,
- &efi_mdesc_size, &mdesc_ver);
- if ( EFI_ERROR(status) )
- PrintErrMesg(L"Cannot obtain memory map", status);
+ for ( retry = 0; ; retry = 1 )
+ {
+ status = efi_bs->GetMemoryMap(&efi_memmap_size, efi_memmap, &map_key,
+ &efi_mdesc_size, &mdesc_ver);
+ if ( EFI_ERROR(status) )
+ PrintErrMesg(L"Cannot obtain memory map", status);
- efi_arch_process_memory_map(SystemTable, efi_memmap, efi_memmap_size,
- efi_mdesc_size, mdesc_ver);
+ efi_arch_process_memory_map(SystemTable, efi_memmap, efi_memmap_size,
+ efi_mdesc_size, mdesc_ver);
- efi_arch_pre_exit_boot();
+ efi_arch_pre_exit_boot();
+
+ status = efi_bs->ExitBootServices(ImageHandle, map_key);
+ if ( status != EFI_INVALID_PARAMETER || retry )
+ break;
+ }
- status = efi_bs->ExitBootServices(ImageHandle, map_key);
if ( EFI_ERROR(status) )
PrintErrMesg(L"Cannot exit boot services", status);