[BUG/RFC] efi/libstub: Preserve the memory map pointed to by the FDT

Ard Biesheuvel ard.biesheuvel at linaro.org
Fri Dec 9 09:51:43 PST 2016


Hi James,

On 9 December 2016 at 17:15, James Morse <james.morse at arm.com> wrote:
> allocate_new_fdt_and_exit_boot() bakes a pointer to the UEFI memory map
> into the FDT for later use. The corresponding memory is then free()d
> and (hopefully) allocated again via efi_exit_boot_services() calling
> efi_get_memory_map() as part of the exit_boot_services() loop. The
> final copy is annotated with virtual mappings and used to create the
> runtime map.
>
> Linux expects the FDT to contain a pointer to the UEFI memory map
> with the virtual mapping annotations, which will only happen if
> AllocatePool() returns memory to the stub that was just FreePool()d
> by the stub. This behaviour doesn't appear to guaranteed by the spec.
>
> Platforms that don't do this will use the stale memory map (assuming
> no later owner of the freed() memory changed it) and fail to initialise
> runtime services.
>
> Instead, keep the memory map we baked info the FDT, and create a new
> 'exit_map' pointer for use in the exit_boot_services() loop. (This was
> already different, it just had the same name and we hoped it would be
> in the same place).
>
> Annotate the memory map pointed to by the FDT with virtual mappings.
> This assumes the final memory map (which may be different), has
> not moved any of the regions with the runtime attribute.
>
> (Take the opportunity to remove some comments that are stale since
>  commit ed9cc156c42f ("efi/libstub: Use efi_exit_boot_services() in FDT"))
>
> Signed-off-by: James Morse <james.morse at arm.com>
> Fixes: ed9cc156c42f ("efi/libstub: Use efi_exit_boot_services() in FDT")
> Cc: Jeffrey Hugo <jhugo at codeaurora.org>

 ---
> I haven't seen this causing problems on any real platforms, only kvmtool
> which I'm busily hacking up to have primitive UEFI support. Needless to
> say my AllocatePool() doesn't re-use memory.
>

Thanks for the report. This is obviously a serious bug, and should be
fixed asap.

However, the approach is not correct. The whole point of the memory
map dance is that *only* the final version is correct, and the code
Jeffrey added is to ensure that even if ExitBootServices() fails the
first time (which can occur when a timer event fires between
GetMemoryMap() and ExitBootServices()), the memory map is retrieved
again.

The only correct approach is to annotate the final version with
virtual addresses, and pass /that/ address to the kernel. So the bug
is arguably that we pass the wrong version of the memory map to the
OS.

I think we need to fix this by using fdt_setprop_inplace() to poke the
correct value into the FDT after ExitBootServices() returns.

-- 
Ard.


>  drivers/firmware/efi/libstub/fdt.c | 23 +++++++++++------------
>  1 file changed, 11 insertions(+), 12 deletions(-)
>
> diff --git a/drivers/firmware/efi/libstub/fdt.c b/drivers/firmware/efi/libstub/fdt.c
> index a6a93116a8f0..f623894c633c 100644
> --- a/drivers/firmware/efi/libstub/fdt.c
> +++ b/drivers/firmware/efi/libstub/fdt.c
> @@ -181,10 +181,6 @@ static efi_status_t exit_boot_func(efi_system_table_t *sys_table_arg,
>   * which are fixed at 4K bytes, so in most cases the first
>   * allocation should succeed.
>   * EFI boot services are exited at the end of this function.
> - * There must be no allocations between the get_memory_map()
> - * call and the exit_boot_services() call, so the exiting of
> - * boot services is very tightly tied to the creation of the FDT
> - * with the final memory map in it.
>   */
>
>  efi_status_t allocate_new_fdt_and_exit_boot(efi_system_table_t *sys_table,
> @@ -199,7 +195,7 @@ efi_status_t allocate_new_fdt_and_exit_boot(efi_system_table_t *sys_table,
>         unsigned long map_size, desc_size, buff_size;
>         u32 desc_ver;
>         unsigned long mmap_key;
> -       efi_memory_desc_t *memory_map, *runtime_map;
> +       efi_memory_desc_t *memory_map, *runtime_map, *exit_map;
>         unsigned long new_fdt_size;
>         efi_status_t status;
>         int runtime_entry_count = 0;
> @@ -243,11 +239,6 @@ efi_status_t allocate_new_fdt_and_exit_boot(efi_system_table_t *sys_table,
>                         goto fail;
>                 }
>
> -               /*
> -                * Now that we have done our final memory allocation (and free)
> -                * we can get the memory map key  needed for
> -                * exit_boot_services().
> -                */
>                 status = efi_get_memory_map(sys_table, &map);
>                 if (status != EFI_SUCCESS)
>                         goto fail_free_new_fdt;
> @@ -259,8 +250,16 @@ efi_status_t allocate_new_fdt_and_exit_boot(efi_system_table_t *sys_table,
>                                     memory_map, map_size, desc_size, desc_ver);
>
>                 /* Succeeding the first time is the expected case. */
> -               if (status == EFI_SUCCESS)
> +               if (status == EFI_SUCCESS) {
> +                       /*
> +                        * Annotate the memory_map in the FDT with virtual
> +                        * addresses. These shouldn't change as the placement
> +                        * of EFI_MEMORY_RUNTIME regions should be fixed.
> +                        */
> +                       efi_get_virtmap(memory_map, map_size, desc_size,
> +                                       runtime_map, &runtime_entry_count);
>                         break;
> +               }
>
>                 if (status == EFI_BUFFER_TOO_SMALL) {
>                         /*
> @@ -279,7 +278,7 @@ efi_status_t allocate_new_fdt_and_exit_boot(efi_system_table_t *sys_table,
>                 }
>         }
>
> -       sys_table->boottime->free_pool(memory_map);
> +       map.map = &exit_map;
>         priv.runtime_map = runtime_map;
>         priv.runtime_entry_count = &runtime_entry_count;
>         status = efi_exit_boot_services(sys_table, handle, &map, &priv,
> --
> 2.10.1
>



More information about the linux-arm-kernel mailing list