As per the UEFI specification, accesses to BOOT_SERVICES_* memory regions by the UEFI firmware are illegal after the OS has called ExitBootServices. However, buggy firmware implementations may still access these regions after such call. The current approach of the kernel is to reserve and map all the EFI_BOOT_SERVICES_* memory regions until efi_free_boot_services() is called so that the buggy firmware can freely access such regions. A good way to detect these illegal accesses is to not map (but only reserve) these regions so that the accesses generate a page fault that the kernel can detect. Upon detection, the fault is fixed-up (i.e., the region is mapped for the buggy firmware to use). As the pages are reserved, the fixup is safe. Thus, rather than just silently allowing the buggy firmware to proceed, we detect the access and complain about it. Of course, this function will be called by the page fault handler fixup code in a subsequent patch. Ideally, the new memory map with the just mapped region should be passed to the firmware. However, as per the UEFI specification, SetVirtualAddressMap may be called only once. Subsequent calls will return EFI_UNSUPPORTED. Thus, it is pointless to pass the new map. Furthermore, all the EFI_RUNTIME_SERVICES_* should already be mapped at this point. Also, at this point, the virtual address of the system table should have been found. Thus, there is no need to look for it in the just-mapped region. Finally, this new mapping will not impact a reboot from kexec, as kexec is only concerned about runtime memory regions. Signed-off-by: Ricardo Neri <ricardo.neri-calderon@xxxxxxxxxxxxxxx> --- arch/x86/platform/efi/efi.c | 29 +++++++++++++++++++++++++++++ include/linux/efi.h | 8 ++++++++ 2 files changed, 37 insertions(+) diff --git a/arch/x86/platform/efi/efi.c b/arch/x86/platform/efi/efi.c index d45decf..2e78083 100644 --- a/arch/x86/platform/efi/efi.c +++ b/arch/x86/platform/efi/efi.c @@ -954,3 +954,32 @@ static int __init parse_efi_cmdline(char *str) return 0; } early_param("efi", parse_efi_cmdline); + +#ifdef CONFIG_EFI_BOOT_SERVICES_WARN +static const char boot_services_warning[] = +"Fixing illegal access to BOOT_SERVICES_*. Bug in EFI firmware?\n"; + +int efi_boot_services_fixup(unsigned long phys_addr) +{ + efi_memory_desc_t *md; + + md = efi_memory_descriptor(phys_addr); + + if (!md) + return 0; + + if (md->type == EFI_BOOT_SERVICES_CODE || + md->type == EFI_BOOT_SERVICES_DATA) { + /* + * If the page fault was caused by an acccess to BOOT_SERVICES_* + * memory regions, just map the region... and warn about it. + * By now we should have found the virtual address of the system + * table. Thus, no need to update. + */ + pr_warn_once("%s", (char *)&boot_services_warning); + efi_map_region(md); + return 1; + } + return 0; +} +#endif diff --git a/include/linux/efi.h b/include/linux/efi.h index b36b588..fbdc412 100644 --- a/include/linux/efi.h +++ b/include/linux/efi.h @@ -875,6 +875,14 @@ extern void efi_initialize_iomem_resources(struct resource *code_resource, struct resource *data_resource, struct resource *bss_resource); extern void efi_get_time(struct timespec *now); extern void efi_reserve_boot_services(void); +#ifdef CONFIG_EFI_BOOT_SERVICES_WARN +extern int efi_boot_services_fixup(unsigned long phys_addr); +#else +static inline int efi_boot_services_fixup(unsigned long phys_addr) +{ + return 0; +} +#endif extern int efi_get_fdt_params(struct efi_fdt_params *params, int verbose); extern struct efi_memory_map memmap; -- 1.9.1 -- To unsubscribe from this list: send the line "unsubscribe linux-efi" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html