x86, efi: retry ExitBootServices() on failure
[linux-3.10.git] / arch / x86 / boot / compressed / eboot.c
index c205035..d606463 100644 (file)
@@ -992,18 +992,20 @@ static efi_status_t exit_boot(struct boot_params *boot_params,
        efi_memory_desc_t *mem_map;
        efi_status_t status;
        __u32 desc_version;
+       bool called_exit = false;
        u8 nr_entries;
        int i;
 
        size = sizeof(*mem_map) * 32;
 
 again:
-       size += sizeof(*mem_map);
+       size += sizeof(*mem_map) * 2;
        _size = size;
        status = low_alloc(size, 1, (unsigned long *)&mem_map);
        if (status != EFI_SUCCESS)
                return status;
 
+get_map:
        status = efi_call_phys5(sys_table->boottime->get_memory_map, &size,
                                mem_map, &key, &desc_size, &desc_version);
        if (status == EFI_BUFFER_TOO_SMALL) {
@@ -1029,8 +1031,20 @@ again:
        /* Might as well exit boot services now */
        status = efi_call_phys2(sys_table->boottime->exit_boot_services,
                                handle, key);
-       if (status != EFI_SUCCESS)
-               goto free_mem_map;
+       if (status != EFI_SUCCESS) {
+               /*
+                * ExitBootServices() will fail if any of the event
+                * handlers change the memory map. In which case, we
+                * must be prepared to retry, but only once so that
+                * we're guaranteed to exit on repeated failures instead
+                * of spinning forever.
+                */
+               if (called_exit)
+                       goto free_mem_map;
+
+               called_exit = true;
+               goto get_map;
+       }
 
        /* Historic? */
        boot_params->alt_mem_k = 32 * 1024;