Skip to content

Commit

Permalink
efi: optionally call SetVirtualAddressMap()
Browse files Browse the repository at this point in the history
Some UEFI implementations are not happy about lack of
SetVirtualAddressMap() call. Likely abuse the address map change
notification to do things beyond the necessary ConvertPointer() calls.
Specifically, wihtout the SetVirtualAddressMap() call, some access
EfiBootServices{Code,Data}, or even totally unmapped areas. Example
crash of GetVariable() call on Thinkpad W540:

    Xen call trace:
       [<0000000000000080>] 0000000000000080
       [<8c2b0398e0000daa>] 8c2b0398e0000daa

    Pagetable walk from ffffffff858483a1:
       L4[0x1ff] = 0000000000000000 ffffffffffffffff

    ****************************************
    Panic on CPU 0:
    FATAL PAGE FAULT
    [error_code=0002]
    Faulting linear address: ffffffff858483a1
    ****************************************

Fix this by calling SetVirtualAddressMap() runtime service, giving it
1:1 map for areas marked as needed during runtime. The address space in
which EFI runtime services are called is unchanged, but UEFI view of it
may be.
Since it's fairly late in Xen 4.13 development cycle, disable it
by default and hide behind EXPERT.

Signed-off-by: Marek Marczykowski-Górecki <[email protected]>
Reviewed-by: Jan Beulich <[email protected]>
Release-acked-by: Juergen Gross <[email protected]>
  • Loading branch information
marmarek authored and jbeulich committed Oct 25, 2019
1 parent 86cf0ed commit 4945041
Show file tree
Hide file tree
Showing 2 changed files with 40 additions and 3 deletions.
10 changes: 10 additions & 0 deletions xen/common/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,16 @@ config KEXEC

If unsure, say Y.

config EFI_SET_VIRTUAL_ADDRESS_MAP
bool "EFI: call SetVirtualAddressMap()" if EXPERT = "y"
---help---
Call EFI SetVirtualAddressMap() runtime service to setup memory map for
further runtime services. According to UEFI spec, it isn't strictly
necessary, but many UEFI implementations misbehave when this call is
missing.

If unsure, say N.

config XENOPROF
def_bool y
prompt "Xen Oprofile Support" if EXPERT = "y"
Expand Down
33 changes: 30 additions & 3 deletions xen/common/efi/boot.c
Original file line number Diff line number Diff line change
Expand Up @@ -1060,11 +1060,17 @@ static void __init efi_set_gop_mode(EFI_GRAPHICS_OUTPUT_PROTOCOL *gop, UINTN gop
efi_arch_video_init(gop, info_size, mode_info);
}

#define INVALID_VIRTUAL_ADDRESS (0xBAAADUL << \
(EFI_PAGE_SHIFT + BITS_PER_LONG - 32))

static void __init efi_exit_boot(EFI_HANDLE ImageHandle, EFI_SYSTEM_TABLE *SystemTable)
{
EFI_STATUS status;
UINTN info_size = 0, map_key;
bool retry;
#ifdef CONFIG_EFI_SET_VIRTUAL_ADDRESS_MAP
unsigned int i;
#endif

efi_bs->GetMemoryMap(&info_size, NULL, &map_key,
&efi_mdesc_size, &mdesc_ver);
Expand Down Expand Up @@ -1098,6 +1104,26 @@ static void __init efi_exit_boot(EFI_HANDLE ImageHandle, EFI_SYSTEM_TABLE *Syste
if ( EFI_ERROR(status) )
PrintErrMesg(L"Cannot exit boot services", status);

#ifdef CONFIG_EFI_SET_VIRTUAL_ADDRESS_MAP
for ( i = 0; i < efi_memmap_size; i += efi_mdesc_size )
{
EFI_MEMORY_DESCRIPTOR *desc = efi_memmap + i;

if ( desc->Attribute & EFI_MEMORY_RUNTIME )
desc->VirtualStart = desc->PhysicalStart;
else
desc->VirtualStart = INVALID_VIRTUAL_ADDRESS;
}
status = efi_rs->SetVirtualAddressMap(efi_memmap_size, efi_mdesc_size,
mdesc_ver, efi_memmap);
if ( status != EFI_SUCCESS )
{
printk(XENLOG_ERR "EFI: SetVirtualAddressMap() failed (%#lx), disabling runtime services\n",
status);
__clear_bit(EFI_RS, &efi_flags);
}
#endif

/* Adjust pointers into EFI. */
efi_ct = (void *)efi_ct + DIRECTMAP_VIRT_START;
efi_memmap = (void *)efi_memmap + DIRECTMAP_VIRT_START;
Expand Down Expand Up @@ -1464,8 +1490,6 @@ static bool __init rt_range_valid(unsigned long smfn, unsigned long emfn)
return true;
}

#define INVALID_VIRTUAL_ADDRESS (0xBAAADUL << \
(EFI_PAGE_SHIFT + BITS_PER_LONG - 32))

void __init efi_init_memory(void)
{
Expand Down Expand Up @@ -1580,7 +1604,10 @@ void __init efi_init_memory(void)
return;
}

/* Set up 1:1 page tables to do runtime calls in "physical" mode. */
/*
* Set up 1:1 page tables for runtime calls. See SetVirtualAddressMap() in
* efi_exit_boot().
*/
efi_l4_pgtable = alloc_xen_pagetable();
BUG_ON(!efi_l4_pgtable);
clear_page(efi_l4_pgtable);
Expand Down

0 comments on commit 4945041

Please sign in to comment.