Commit 89a6079d authored by Lu Baolu's avatar Lu Baolu Committed by Mika Westerberg

iommu/vt-d: Force IOMMU on for platform opt in hint

Intel VT-d spec added a new DMA_CTRL_PLATFORM_OPT_IN_FLAG flag in DMAR
ACPI table [1] for BIOS to report compliance about platform initiated
DMA restricted to RMRR ranges when transferring control to the OS. This
means that during OS boot, before it enables IOMMU none of the connected
devices can bypass DMA protection for instance by overwriting the data
structures used by the IOMMU. The OS also treats this as a hint that the
IOMMU should be enabled to prevent DMA attacks from possible malicious
devices.

A use of this flag is Kernel DMA protection for Thunderbolt [2] which in
practice means that IOMMU should be enabled for PCIe devices connected
to the Thunderbolt ports. With IOMMU enabled for these devices, all DMA
operations are limited in the range reserved for it, thus the DMA
attacks are prevented. All these devices are enumerated in the PCI/PCIe
module and marked with an untrusted flag.

This forces IOMMU to be enabled if DMA_CTRL_PLATFORM_OPT_IN_FLAG is set
in DMAR ACPI table and there are PCIe devices marked as untrusted in the
system. This can be turned off by adding "intel_iommu=off" in the kernel
command line, if any problems are found.

[1] https://software.intel.com/sites/default/files/managed/c5/15/vt-directed-io-spec.pdf
[2] https://docs.microsoft.com/en-us/windows/security/information-protection/kernel-dma-protection-for-thunderbolt

Cc: Jacob Pan <jacob.jun.pan@linux.intel.com>
Cc: Sohil Mehta <sohil.mehta@intel.com>
Signed-off-by: default avatarLu Baolu <baolu.lu@linux.intel.com>
Signed-off-by: default avatarMika Westerberg <mika.westerberg@linux.intel.com>
Reviewed-by: default avatarAshok Raj <ashok.raj@intel.com>
Reviewed-by: default avatarJoerg Roedel <jroedel@suse.de>
Acked-by: default avatarJoerg Roedel <jroedel@suse.de>
parent 617654aa
...@@ -2042,3 +2042,28 @@ int dmar_device_remove(acpi_handle handle) ...@@ -2042,3 +2042,28 @@ int dmar_device_remove(acpi_handle handle)
{ {
return dmar_device_hotplug(handle, false); return dmar_device_hotplug(handle, false);
} }
/*
* dmar_platform_optin - Is %DMA_CTRL_PLATFORM_OPT_IN_FLAG set in DMAR table
*
* Returns true if the platform has %DMA_CTRL_PLATFORM_OPT_IN_FLAG set in
* the ACPI DMAR table. This means that the platform boot firmware has made
* sure no device can issue DMA outside of RMRR regions.
*/
bool dmar_platform_optin(void)
{
struct acpi_table_dmar *dmar;
acpi_status status;
bool ret;
status = acpi_get_table(ACPI_SIG_DMAR, 0,
(struct acpi_table_header **)&dmar);
if (ACPI_FAILURE(status))
return false;
ret = !!(dmar->flags & DMAR_PLATFORM_OPT_IN);
acpi_put_table((struct acpi_table_header *)dmar);
return ret;
}
EXPORT_SYMBOL_GPL(dmar_platform_optin);
...@@ -184,6 +184,7 @@ static int rwbf_quirk; ...@@ -184,6 +184,7 @@ static int rwbf_quirk;
*/ */
static int force_on = 0; static int force_on = 0;
int intel_iommu_tboot_noforce; int intel_iommu_tboot_noforce;
static int no_platform_optin;
#define ROOT_ENTRY_NR (VTD_PAGE_SIZE/sizeof(struct root_entry)) #define ROOT_ENTRY_NR (VTD_PAGE_SIZE/sizeof(struct root_entry))
...@@ -503,6 +504,7 @@ static int __init intel_iommu_setup(char *str) ...@@ -503,6 +504,7 @@ static int __init intel_iommu_setup(char *str)
pr_info("IOMMU enabled\n"); pr_info("IOMMU enabled\n");
} else if (!strncmp(str, "off", 3)) { } else if (!strncmp(str, "off", 3)) {
dmar_disabled = 1; dmar_disabled = 1;
no_platform_optin = 1;
pr_info("IOMMU disabled\n"); pr_info("IOMMU disabled\n");
} else if (!strncmp(str, "igfx_off", 8)) { } else if (!strncmp(str, "igfx_off", 8)) {
dmar_map_gfx = 0; dmar_map_gfx = 0;
...@@ -2895,6 +2897,13 @@ static int iommu_should_identity_map(struct device *dev, int startup) ...@@ -2895,6 +2897,13 @@ static int iommu_should_identity_map(struct device *dev, int startup)
if (device_is_rmrr_locked(dev)) if (device_is_rmrr_locked(dev))
return 0; return 0;
/*
* Prevent any device marked as untrusted from getting
* placed into the statically identity mapping domain.
*/
if (pdev->untrusted)
return 0;
if ((iommu_identity_mapping & IDENTMAP_AZALIA) && IS_AZALIA(pdev)) if ((iommu_identity_mapping & IDENTMAP_AZALIA) && IS_AZALIA(pdev))
return 1; return 1;
...@@ -4728,14 +4737,54 @@ const struct attribute_group *intel_iommu_groups[] = { ...@@ -4728,14 +4737,54 @@ const struct attribute_group *intel_iommu_groups[] = {
NULL, NULL,
}; };
static int __init platform_optin_force_iommu(void)
{
struct pci_dev *pdev = NULL;
bool has_untrusted_dev = false;
if (!dmar_platform_optin() || no_platform_optin)
return 0;
for_each_pci_dev(pdev) {
if (pdev->untrusted) {
has_untrusted_dev = true;
break;
}
}
if (!has_untrusted_dev)
return 0;
if (no_iommu || dmar_disabled)
pr_info("Intel-IOMMU force enabled due to platform opt in\n");
/*
* If Intel-IOMMU is disabled by default, we will apply identity
* map for all devices except those marked as being untrusted.
*/
if (dmar_disabled)
iommu_identity_mapping |= IDENTMAP_ALL;
dmar_disabled = 0;
#if defined(CONFIG_X86) && defined(CONFIG_SWIOTLB)
swiotlb = 0;
#endif
no_iommu = 0;
return 1;
}
int __init intel_iommu_init(void) int __init intel_iommu_init(void)
{ {
int ret = -ENODEV; int ret = -ENODEV;
struct dmar_drhd_unit *drhd; struct dmar_drhd_unit *drhd;
struct intel_iommu *iommu; struct intel_iommu *iommu;
/* VT-d is required for a TXT/tboot launch, so enforce that */ /*
force_on = tboot_force_iommu(); * Intel IOMMU is required for a TXT/tboot launch or platform
* opt in, so enforce that.
*/
force_on = tboot_force_iommu() || platform_optin_force_iommu();
if (iommu_init_mempool()) { if (iommu_init_mempool()) {
if (force_on) if (force_on)
......
...@@ -39,6 +39,7 @@ struct acpi_dmar_header; ...@@ -39,6 +39,7 @@ struct acpi_dmar_header;
/* DMAR Flags */ /* DMAR Flags */
#define DMAR_INTR_REMAP 0x1 #define DMAR_INTR_REMAP 0x1
#define DMAR_X2APIC_OPT_OUT 0x2 #define DMAR_X2APIC_OPT_OUT 0x2
#define DMAR_PLATFORM_OPT_IN 0x4
struct intel_iommu; struct intel_iommu;
...@@ -170,6 +171,8 @@ static inline int dmar_ir_hotplug(struct dmar_drhd_unit *dmaru, bool insert) ...@@ -170,6 +171,8 @@ static inline int dmar_ir_hotplug(struct dmar_drhd_unit *dmaru, bool insert)
{ return 0; } { return 0; }
#endif /* CONFIG_IRQ_REMAP */ #endif /* CONFIG_IRQ_REMAP */
extern bool dmar_platform_optin(void);
#else /* CONFIG_DMAR_TABLE */ #else /* CONFIG_DMAR_TABLE */
static inline int dmar_device_add(void *handle) static inline int dmar_device_add(void *handle)
...@@ -182,6 +185,11 @@ static inline int dmar_device_remove(void *handle) ...@@ -182,6 +185,11 @@ static inline int dmar_device_remove(void *handle)
return 0; return 0;
} }
static inline bool dmar_platform_optin(void)
{
return false;
}
#endif /* CONFIG_DMAR_TABLE */ #endif /* CONFIG_DMAR_TABLE */
struct irte { struct irte {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment