efi: Allow disabling PCI busmastering on bridges during boot
Subsystems (3)
Ard Biesheuvel
maintainerPATCH v3 1/1 efi: Allow disabling PCI busmastering on bridges during boot
From: Matthew Garrett <[email protected]>
Add an option to disable the busmaster bit in the control register on all PCI bridges calling ExitBootServices() and passing control to the runtime kernel. System firmware may configure the IOMMU to prevent malicious PCI devices from being able to attack the OS via DMA. However, since firmware can't guarantee that the OS is IOMMU-aware, it will tear down IOMMU configuration when ExitBootServices() is called. This leaves a window between where a hostile device could still cause damage before Linux configures the IOMMU again.
If CONFIG_EFI_DISABLE_PCI_DMA is enabled or "efi=disable_early_pci_dma" is passed on the command line, the EFI stub will clear the busmaster bit on all PCI bridges before ExitBootServices() is called. This will prevent any malicious PCI devices from being able to perform DMA until the kernel reenables busmastering after configuring the IOMMU.
This option may cause failures with some poorly behaved hardware and should not be enabled without testing. The kernel commandline options "efi=disable_early_pci_dma" or "efi=no_disable_early_pci_dma" may be used to override the default. Note that PCI devices downstream from PCI bridges are disconnected from their drivers first, using the UEFI driver model API, so that DMA can be disabled safely at the bridge level.
--- Documentation/admin-guide/kernel-parameters.txt | 4 + arch/x86/boot/compressed/eboot.c | 2 + drivers/firmware/efi/Kconfig | 22 ++++ drivers/firmware/efi/libstub/Makefile | 2 +- drivers/firmware/efi/libstub/arm-stub.c | 1 + drivers/firmware/efi/libstub/efi-stub-helper.c | 17 +++ drivers/firmware/efi/libstub/efistub.h | 1 + drivers/firmware/efi/libstub/pci.c | 118 ++++++++++++++++++++ include/linux/efi.h | 6 +- 9 files changed, 171 insertions(+), 2 deletions(-)
Documentation/admin-guide/kernel-parameters.txt +4 -0
@@ -1177,6 +1177,10 @@ claim. Specify efi=nosoftreserve to disable this reservation and treat the memory by its base type (i.e. EFI_CONVENTIONAL_MEMORY / "System RAM"). + disable_early_pci_dma: Disable the busmaster bit on all + PCI bridges while in the EFI boot stub + no_disable_early_pci_dma: Leave the busmaster bit set + on all PCI bridges while in the EFI boot stub efi_no_storage_paranoia [EFI; X86] Using this parameter you can use more than 50% of
arch/x86/boot/compressed/eboot.c +2 -0
@@ -166,6 +166,8 @@ static void setup_efi_pci(struct boot_params *params) free_handle:efi_bs_call (free_pool, pci_handle); + +efi_pci_disable_bridge_busmaster (); }static void retrieve_apple_device_properties (struct boot_params *boot_params)
drivers/firmware/efi/Kconfig +22 -0
@@ -215,6 +215,28 @@ config EFI_RCI2_TABLE Say Y here for Dell EMC PowerEdge systems. +config EFI_DISABLE_PCI_DMA + bool "Clear Busmaster bit on PCI bridges during ExitBootServices()" + help + Disable the busmaster bit in the control register on all PCI bridges + while calling ExitBootServices() and passing control to the runtime + kernel. System firmware may configure the IOMMU to prevent malicious + PCI devices from being able to attack the OS via DMA. However, since + firmware can't guarantee that the OS is IOMMU-aware, it will tear + down IOMMU configuration when ExitBootServices() is called. This + leaves a window between where a hostile device could still cause + damage before Linux configures the IOMMU again. + + If you say Y here, the EFI stub will clear the busmaster bit on all + PCI bridges before ExitBootServices() is called. This will prevent + any malicious PCI devices from being able to perform DMA until the + kernel reenables busmastering after configuring the IOMMU. + + This option will cause failures with some poorly behaved hardware + and should not be enabled without testing. The kernel commandline + options "efi=disable_early_pci_dma" or "efi=no_disable_early_pci_dma" + may be used to override this option. + endmenu config UEFI_CPER
drivers/firmware/efi/libstub/Makefile +1 -1
@@ -39,7 +39,7 @@ OBJECT_FILES_NON_STANDARD := y KCOV_INSTRUMENT := n lib-y := efi-stub-helper.o gop.o secureboot.o tpm.o \ - random.o + random.o pci.o # include the stub's generic dependencies from lib/ when building for ARM/arm64 arm-deps-y := fdt_rw.c fdt_ro.c fdt_wip.c fdt.c fdt_empty_tree.c fdt_sw.c
drivers/firmware/efi/libstub/arm-stub.c +1 -0
@@ -256,6 +256,7 @@ unsigned long efi_entry(void *handle, efi_system_table_t *sys_table_arg, }install_memreserve_table (); +efi_pci_disable_bridge_busmaster (); new_fdt_addr = fdt_addr; status = allocate_new_fdt_and_exit_boot(handle,
drivers/firmware/efi/libstub/efi-stub-helper.c +17 -0
@@ -33,6 +33,8 @@ static bool __efistub_global efi_nokaslr; static bool __efistub_global efi_quiet; static bool __efistub_global efi_novamap; static bool __efistub_global efi_nosoftreserve; +static bool __efistub_global efi_disable_pci_dma = +IS_ENABLED (CONFIG_EFI_DISABLE_PCI_DMA );bool __pure nokaslr (void ) {
@@ -51,6 +53,11 @@ bool __pure __efi_soft_reserve_enabled(void)return !efi_nosoftreserve; } +bool __pure disable_pci_dma (void ) +{ +return efi_disable_pci_dma; +} +#define EFI_MMAP_NR_SLACK_SLOTS 8struct file_info {
@@ -493,6 +500,16 @@ efi_status_t efi_parse_options(char const *cmdline) efi_nosoftreserve = true; } +if (!strncmp (str,"disable_early_pci_dma" ,21 )) { + str +=strlen ("disable_early_pci_dma" ); + efi_disable_pci_dma = true; + } + +if (!strncmp (str,"no_disable_early_pci_dma" ,24 )) { + str +=strlen ("no_disable_early_pci_dma" ); + efi_disable_pci_dma = false; + } +/* Group words together, delimited by "," */ while (*str && *str !=' ' && *str !=',' ) str++;
drivers/firmware/efi/libstub/efistub.h +1 -0
@@ -34,6 +34,7 @@extern bool __pure nokaslr (void );extern bool __pure is_quiet (void );extern bool __pure novamap (void ); +extern bool __pure disable_pci_dma (void );extern __pure efi_system_table_t *efi_system_table (void );
drivers/firmware/efi/libstub/pci.c new file +118 -0
118 lines not on this page · raw message
include/linux/efi.h +5 -1
@@ -319,7 +319,9 @@ typedef union {void *stall;void *set_watchdog_timer;void *connect_controller; -void *disconnect_controller; +efi_status_t (__efiapi *disconnect_controller)(efi_handle_t, + efi_handle_t, + efi_handle_t);void *open_protocol;void *close_protocol;void *open_protocol_information;
@@ -1692,4 +1694,6 @@ struct linux_efi_memreserve {#define EFI_MEMRESERVE_COUNT (size) (((size) - sizeof(struct linux_efi_memreserve)) \ / sizeof(((struct linux_efi_memreserve *)0)->entry[0])) +void efi_pci_disable_bridge_busmaster (void ); +#endif /* _LINUX_EFI_H */
Signature · 1 line
2.20.1
Quoted 2 lines · Ard Biesheuvel
From: Matthew Garrett <[email protected]>
(snip)
This version looks good to me, thanks!