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

Re: [Xen-devel] [PATCH RFC] EFI: allow retry of ExitBootServices() call



On Fri, Nov 14, 2014 at 12:37:30PM +0000, Jan Beulich wrote:
> 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.

s/reportedly/in fact/
> 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.

Wow. Talk about timing. We saw this and were going to see
doing something similar.


> 
> 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@xxxxxxxx>
> 
> --- 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_memo
>  
>      /* 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;
> --- a/xen/common/efi/boot.c
> +++ b/xen/common/efi/boot.c
> @@ -703,7 +703,7 @@ efi_start(EFI_HANDLE ImageHandle, EFI_SY
>      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_SY
>      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;
> +    }

Any reason for just doing the loop at max twice? Could we iterate
more than those (say forever?) with an printk at suitable intervals
to notify the user?

>  
> -    status = efi_bs->ExitBootServices(ImageHandle, map_key);
>      if ( EFI_ERROR(status) )
>          PrintErrMesg(L"Cannot exit boot services", status);
>  
> 
> 

_______________________________________________
Xen-devel mailing list
Xen-devel@xxxxxxxxxxxxx
http://lists.xen.org/xen-devel


 


Rackspace

Lists.xenproject.org is hosted with RackSpace, monitoring our
servers 24x7x365 and backed by RackSpace's Fanatical Support®.