From: Sai Praneeth <sai.praneeth.prakhya@xxxxxxxxx> There may exist some buggy UEFI firmware implementations that access efi memory regions other than EFI_RUNTIME_SERVICES_<CODE/DATA> even after kernel has assumed control of the platform. This violates UEFI specification. Here, we provide a debug config option which when enabled detects and fixes up/recovers from page faults caused by buggy firmware. The above said illegal accesses trigger page fault in ring 0 because firmware executes at ring 0 and if unhandled it hangs the kernel. We provide an efi specific page fault handler to: 1. Avoid panics/hangs caused by buggy firmware. 2. Shout loud that the firmware is buggy and hence can save ourselves from being blamed for not a fault of ours. Depending on the illegally accessed efi region, the efi page fault handler handles illegal accesses differently. 1. If the illegally accessed region is EFI_BOOT_SERVICES_<CODE/DATA>, the page fault handler fixes it up by mapping the requested region. 2. If the illegally accessed region is any other efi region (like EFI_CONVENTIONAL_MEMORY or EFI_LOADER_<CODE/DATA>), the page fault handler exits firmware context and disables EFI Runtime Services, so that we will never again call buggy firmware. Page faults to efi regions are handled differently because, presently during kernel boot, EFI_BOOT_SERVICES_<CODE/DATA> regions are reserved by kernel and hence it's OK to dynamically map these regions in page fault handler. The same approach cannot be followed for other efi regions like EFI_CONVENTIONAL_MEMORY and EFI_LOADER_<CODE/DATA> as they are very huge in size and reserving them could make the kernel un-bootable. Hence, we take a different approach (exiting firmware context) while dealing with page faults to these regions. This also saves us from executing buggy firmware further. Exiting firmware context means that on every entry to firmware we save the kernel context before calling firmware and if the firmware misbehaves, in the page fault handler, we roll back to the saved kernel context. Saving kernel context means saving the stack pointer and the instruction that gets executed when firmware returns. In the page fault handler we fix up these two things (RIP and RSP) so that when returning from page fault handler it looks as if firmware has called RET. This issue was reported by Al Stone when he saw that reboot via EFI hangs the machine. Upon debugging, I found that it's efi_reset_system() that's touching memory regions which it shouldn't. To reproduce the same behavior, I have hacked OVMF and made efi_reset_system() buggy. Testing the patch set: ---------------------- 1. Download buggy firmware from here [1]. 2. Run a qemu instance with this buggy BIOS and boot mainline kernel. Add reboot=efi to the kernel command line arguments and after the kernel is up and running, type "reboot". The kernel should hang while rebooting. 3. With the same setup, boot kernel after applying patches and the reboot should work fine. Also please notice warning/error messages printed by kernel. Note: ----- Patch set based on "next" branch in efi tree. [1] https://drive.google.com/open?id=1tkvT7GaVX2zSlzy1HK1T4Tv8cT36GP6R Sai Praneeth (8): x86/efi: Remove __init attribute from memory mapping functions x86/efi: Permanently save the EFI_MEMORY_MAP passed by firmware x86/efi: Save kernel context before calling EFI Runtime Services x86/efi: Add page fault handler to fixup/recover from page faults caused by firmware x86/efi: If EFI_WARN_ON_ILLEGAL_ACCESSES is enabled don't call efi_free_boot_services() x86/efi: Map EFI_BOOT_SERVICES_<CODE/DATA> regions only when EFI_WARN_ON_ILLEGAL_ACCESSES is disabled x86/mm: If in_atomic(), allocate pages without sleeping x86/efi: Introduce EFI_WARN_ON_ILLEGAL_ACCESSES arch/x86/Kconfig | 17 +++ arch/x86/include/asm/efi.h | 42 ++++++- arch/x86/mm/fault.c | 9 ++ arch/x86/mm/pageattr.c | 16 ++- arch/x86/platform/efi/efi.c | 10 +- arch/x86/platform/efi/efi_32.c | 2 +- arch/x86/platform/efi/efi_64.c | 16 ++- arch/x86/platform/efi/efi_stub_64.S | 101 ++++++++++++++++- arch/x86/platform/efi/quirks.c | 193 ++++++++++++++++++++++++++++++++ drivers/firmware/efi/efi.c | 6 +- drivers/firmware/efi/runtime-wrappers.c | 6 + include/linux/efi.h | 16 ++- init/main.c | 3 +- 13 files changed, 415 insertions(+), 22 deletions(-) Signed-off-by: Sai Praneeth Prakhya <sai.praneeth.prakhya@xxxxxxxxx> Suggested-by: Matt Fleming <matt@xxxxxxxxxxxxxxxxxxx> Based-on-code-from: Ricardo Neri <ricardo.neri@xxxxxxxxx> Cc: Al Stone <astone@xxxxxxxxxx> Cc: Lee Chun-Yi <jlee@xxxxxxxx> Cc: Borislav Petkov <bp@xxxxxxxxx> Cc: Bhupesh Sharma <bhsharma@xxxxxxxxxx> Cc: Ard Biesheuvel <ard.biesheuvel@xxxxxxxxxx> -- 2.7.4 -- 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