forked from Minki/linux
thunderbolt: Changes for v4.21 merge window
-----BEGIN PGP SIGNATURE----- iQJUBAABCgA+FiEEVTdhRGBbNzLrSUBaAP2fSd+ZWKAFAlwOLrYgHG1pa2Eud2Vz dGVyYmVyZ0BsaW51eC5pbnRlbC5jb20ACgkQAP2fSd+ZWKCQyA//Zs4mAtgwa6ON 9gaYjVDkrAIs8jD1JHHBKpqCVvRdCZfnAtYT2Rk33SgRYFV0UmXoOL3HzMOHMTjO 2/S0SmO260UAQL7b4yPSb6tzCLSrW55rDS52EpQmJ8ncGD8l65tduwdA9gZEt+Kr AhM5TK6nEQagRIcAQSmBlJMkDWNy2wvxTOebQv3C9woGSK7TFMvhCfZaLV9hpi89 ThdQtGLsGnYyzSw9tvEAwrsX96mWr2sdMV392SIgXEs+P3NtphTPvNM33Jo48l36 aFbhQwHEy6vtV6sC1va8NC/XQgLCK3DSx9R2/s+dZnZTXF4w14X+7KvNhQM8YpPB OXPQXIsjpz/APBWULoPy6BX2TtzJUy0upGm/4B0kYBCFF1qmbFIeOi6beaXTFGzz 80qBpv6XUk/P/kGs3FTt3FfARjmHYnYuQhP90wFgoelMOHmBatz0YQYUYVeNe5ew 5itFeXgm3PkSnibxu0KBJCQe32SmNgMiGctgEKirAbK6Ibdp0bvj8NlZuCdoF5S/ 1z6L4GL9zoPrxTRKMZlxlvexe9Lr+qRqiG8fG+Xt/WRZFauD0k7l7RvTqzxDC+Di PnlrryDDNDEnSSlNjHdEqNMg8B1QrQ/5e0GAUoKNFVVTBLfc1oChaxAfzDjhRRgU 4l2irxVwQSP76ZCvPuF5NOfEfI58Buo= =tqRs -----END PGP SIGNATURE----- Merge tag 'thunderbolt-for-v4.21' of git://git.kernel.org/pub/scm/linux/kernel/git/westeri/thunderbolt into char-misc-next Mika writes: thunderbolt: Changes for v4.21 merge window * tag 'thunderbolt-for-v4.21' of git://git.kernel.org/pub/scm/linux/kernel/git/westeri/thunderbolt: thunderbolt: Export IOMMU based DMA protection support to userspace iommu/vt-d: Do not enable ATS for untrusted devices iommu/vt-d: Force IOMMU on for platform opt in hint PCI / ACPI: Identify untrusted PCI devices
This commit is contained in:
commit
c20c809f92
@ -21,6 +21,15 @@ Description: Holds a comma separated list of device unique_ids that
|
||||
If a device is authorized automatically during boot its
|
||||
boot attribute is set to 1.
|
||||
|
||||
What: /sys/bus/thunderbolt/devices/.../domainX/iommu_dma_protection
|
||||
Date: Mar 2019
|
||||
KernelVersion: 4.21
|
||||
Contact: thunderbolt-software@lists.01.org
|
||||
Description: This attribute tells whether the system uses IOMMU
|
||||
for DMA protection. Value of 1 means IOMMU is used 0 means
|
||||
it is not (DMA protection is solely based on Thunderbolt
|
||||
security levels).
|
||||
|
||||
What: /sys/bus/thunderbolt/devices/.../domainX/security
|
||||
Date: Sep 2017
|
||||
KernelVersion: 4.13
|
||||
|
@ -133,6 +133,26 @@ If the user still wants to connect the device they can either approve
|
||||
the device without a key or write a new key and write 1 to the
|
||||
``authorized`` file to get the new key stored on the device NVM.
|
||||
|
||||
DMA protection utilizing IOMMU
|
||||
------------------------------
|
||||
Recent systems from 2018 and forward with Thunderbolt ports may natively
|
||||
support IOMMU. This means that Thunderbolt security is handled by an IOMMU
|
||||
so connected devices cannot access memory regions outside of what is
|
||||
allocated for them by drivers. When Linux is running on such system it
|
||||
automatically enables IOMMU if not enabled by the user already. These
|
||||
systems can be identified by reading ``1`` from
|
||||
``/sys/bus/thunderbolt/devices/domainX/iommu_dma_protection`` attribute.
|
||||
|
||||
The driver does not do anything special in this case but because DMA
|
||||
protection is handled by the IOMMU, security levels (if set) are
|
||||
redundant. For this reason some systems ship with security level set to
|
||||
``none``. Other systems have security level set to ``user`` in order to
|
||||
support downgrade to older OS, so users who want to automatically
|
||||
authorize devices when IOMMU DMA protection is enabled can use the
|
||||
following ``udev`` rule::
|
||||
|
||||
ACTION=="add", SUBSYSTEM=="thunderbolt", ATTRS{iommu_dma_protection}=="1", ATTR{authorized}=="0", ATTR{authorized}="1"
|
||||
|
||||
Upgrading NVM on Thunderbolt device or host
|
||||
-------------------------------------------
|
||||
Since most of the functionality is handled in firmware running on a
|
||||
|
@ -24,6 +24,14 @@ static int acpi_data_get_property_array(const struct acpi_device_data *data,
|
||||
acpi_object_type type,
|
||||
const union acpi_object **obj);
|
||||
|
||||
/*
|
||||
* The GUIDs here are made equivalent to each other in order to avoid extra
|
||||
* complexity in the properties handling code, with the caveat that the
|
||||
* kernel will accept certain combinations of GUID and properties that are
|
||||
* not defined without a warning. For instance if any of the properties
|
||||
* from different GUID appear in a property list of another, it will be
|
||||
* accepted by the kernel. Firmware validation tools should catch these.
|
||||
*/
|
||||
static const guid_t prp_guids[] = {
|
||||
/* ACPI _DSD device properties GUID: daffd814-6eba-4d8c-8a91-bc9bbf4aa301 */
|
||||
GUID_INIT(0xdaffd814, 0x6eba, 0x4d8c,
|
||||
@ -31,6 +39,9 @@ static const guid_t prp_guids[] = {
|
||||
/* Hotplug in D3 GUID: 6211e2c0-58a3-4af3-90e1-927a4e0c55a4 */
|
||||
GUID_INIT(0x6211e2c0, 0x58a3, 0x4af3,
|
||||
0x90, 0xe1, 0x92, 0x7a, 0x4e, 0x0c, 0x55, 0xa4),
|
||||
/* External facing port GUID: efcc06cc-73ac-4bc3-bff0-76143807c389 */
|
||||
GUID_INIT(0xefcc06cc, 0x73ac, 0x4bc3,
|
||||
0xbf, 0xf0, 0x76, 0x14, 0x38, 0x07, 0xc3, 0x89),
|
||||
};
|
||||
|
||||
static const guid_t ads_guid =
|
||||
|
@ -2042,3 +2042,28 @@ int dmar_device_remove(acpi_handle handle)
|
||||
{
|
||||
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;
|
||||
*/
|
||||
static int force_on = 0;
|
||||
int intel_iommu_tboot_noforce;
|
||||
static int no_platform_optin;
|
||||
|
||||
#define ROOT_ENTRY_NR (VTD_PAGE_SIZE/sizeof(struct root_entry))
|
||||
|
||||
@ -503,6 +504,7 @@ static int __init intel_iommu_setup(char *str)
|
||||
pr_info("IOMMU enabled\n");
|
||||
} else if (!strncmp(str, "off", 3)) {
|
||||
dmar_disabled = 1;
|
||||
no_platform_optin = 1;
|
||||
pr_info("IOMMU disabled\n");
|
||||
} else if (!strncmp(str, "igfx_off", 8)) {
|
||||
dmar_map_gfx = 0;
|
||||
@ -1471,7 +1473,8 @@ static void iommu_enable_dev_iotlb(struct device_domain_info *info)
|
||||
if (info->pri_supported && !pci_reset_pri(pdev) && !pci_enable_pri(pdev, 32))
|
||||
info->pri_enabled = 1;
|
||||
#endif
|
||||
if (info->ats_supported && !pci_enable_ats(pdev, VTD_PAGE_SHIFT)) {
|
||||
if (!pdev->untrusted && info->ats_supported &&
|
||||
!pci_enable_ats(pdev, VTD_PAGE_SHIFT)) {
|
||||
info->ats_enabled = 1;
|
||||
domain_update_iotlb(info->domain);
|
||||
info->ats_qdep = pci_ats_queue_depth(pdev);
|
||||
@ -2895,6 +2898,13 @@ static int iommu_should_identity_map(struct device *dev, int startup)
|
||||
if (device_is_rmrr_locked(dev))
|
||||
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))
|
||||
return 1;
|
||||
|
||||
@ -4728,14 +4738,54 @@ const struct attribute_group *intel_iommu_groups[] = {
|
||||
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 ret = -ENODEV;
|
||||
struct dmar_drhd_unit *drhd;
|
||||
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 (force_on)
|
||||
|
@ -789,6 +789,24 @@ static void pci_acpi_optimize_delay(struct pci_dev *pdev,
|
||||
ACPI_FREE(obj);
|
||||
}
|
||||
|
||||
static void pci_acpi_set_untrusted(struct pci_dev *dev)
|
||||
{
|
||||
u8 val;
|
||||
|
||||
if (pci_pcie_type(dev) != PCI_EXP_TYPE_ROOT_PORT)
|
||||
return;
|
||||
if (device_property_read_u8(&dev->dev, "ExternalFacingPort", &val))
|
||||
return;
|
||||
|
||||
/*
|
||||
* These root ports expose PCIe (including DMA) outside of the
|
||||
* system so make sure we treat them and everything behind as
|
||||
* untrusted.
|
||||
*/
|
||||
if (val)
|
||||
dev->untrusted = 1;
|
||||
}
|
||||
|
||||
static void pci_acpi_setup(struct device *dev)
|
||||
{
|
||||
struct pci_dev *pci_dev = to_pci_dev(dev);
|
||||
@ -798,6 +816,7 @@ static void pci_acpi_setup(struct device *dev)
|
||||
return;
|
||||
|
||||
pci_acpi_optimize_delay(pci_dev, adev->handle);
|
||||
pci_acpi_set_untrusted(pci_dev);
|
||||
|
||||
pci_acpi_add_pm_notifier(adev, pci_dev);
|
||||
if (!adev->wakeup.flags.valid)
|
||||
|
@ -1378,6 +1378,19 @@ static void set_pcie_thunderbolt(struct pci_dev *dev)
|
||||
}
|
||||
}
|
||||
|
||||
static void set_pcie_untrusted(struct pci_dev *dev)
|
||||
{
|
||||
struct pci_dev *parent;
|
||||
|
||||
/*
|
||||
* If the upstream bridge is untrusted we treat this device
|
||||
* untrusted as well.
|
||||
*/
|
||||
parent = pci_upstream_bridge(dev);
|
||||
if (parent && parent->untrusted)
|
||||
dev->untrusted = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* pci_ext_cfg_is_aliased - Is ext config space just an alias of std config?
|
||||
* @dev: PCI device
|
||||
@ -1638,6 +1651,8 @@ int pci_setup_device(struct pci_dev *dev)
|
||||
/* Need to have dev->cfg_size ready */
|
||||
set_pcie_thunderbolt(dev);
|
||||
|
||||
set_pcie_untrusted(dev);
|
||||
|
||||
/* "Unknown power state" */
|
||||
dev->current_state = PCI_UNKNOWN;
|
||||
|
||||
|
@ -7,7 +7,9 @@
|
||||
*/
|
||||
|
||||
#include <linux/device.h>
|
||||
#include <linux/dmar.h>
|
||||
#include <linux/idr.h>
|
||||
#include <linux/iommu.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/slab.h>
|
||||
@ -236,6 +238,20 @@ err_free_str:
|
||||
}
|
||||
static DEVICE_ATTR_RW(boot_acl);
|
||||
|
||||
static ssize_t iommu_dma_protection_show(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
/*
|
||||
* Kernel DMA protection is a feature where Thunderbolt security is
|
||||
* handled natively using IOMMU. It is enabled when IOMMU is
|
||||
* enabled and ACPI DMAR table has DMAR_PLATFORM_OPT_IN set.
|
||||
*/
|
||||
return sprintf(buf, "%d\n",
|
||||
iommu_present(&pci_bus_type) && dmar_platform_optin());
|
||||
}
|
||||
static DEVICE_ATTR_RO(iommu_dma_protection);
|
||||
|
||||
static ssize_t security_show(struct device *dev, struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
@ -251,6 +267,7 @@ static DEVICE_ATTR_RO(security);
|
||||
|
||||
static struct attribute *domain_attrs[] = {
|
||||
&dev_attr_boot_acl.attr,
|
||||
&dev_attr_iommu_dma_protection.attr,
|
||||
&dev_attr_security.attr,
|
||||
NULL,
|
||||
};
|
||||
|
@ -39,6 +39,7 @@ struct acpi_dmar_header;
|
||||
/* DMAR Flags */
|
||||
#define DMAR_INTR_REMAP 0x1
|
||||
#define DMAR_X2APIC_OPT_OUT 0x2
|
||||
#define DMAR_PLATFORM_OPT_IN 0x4
|
||||
|
||||
struct intel_iommu;
|
||||
|
||||
@ -170,6 +171,8 @@ static inline int dmar_ir_hotplug(struct dmar_drhd_unit *dmaru, bool insert)
|
||||
{ return 0; }
|
||||
#endif /* CONFIG_IRQ_REMAP */
|
||||
|
||||
extern bool dmar_platform_optin(void);
|
||||
|
||||
#else /* CONFIG_DMAR_TABLE */
|
||||
|
||||
static inline int dmar_device_add(void *handle)
|
||||
@ -182,6 +185,11 @@ static inline int dmar_device_remove(void *handle)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline bool dmar_platform_optin(void)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
#endif /* CONFIG_DMAR_TABLE */
|
||||
|
||||
struct irte {
|
||||
|
@ -396,6 +396,14 @@ struct pci_dev {
|
||||
unsigned int is_hotplug_bridge:1;
|
||||
unsigned int shpc_managed:1; /* SHPC owned by shpchp */
|
||||
unsigned int is_thunderbolt:1; /* Thunderbolt controller */
|
||||
/*
|
||||
* Devices marked being untrusted are the ones that can potentially
|
||||
* execute DMA attacks and similar. They are typically connected
|
||||
* through external ports such as Thunderbolt but not limited to
|
||||
* that. When an IOMMU is enabled they should be getting full
|
||||
* mappings to make sure they cannot access arbitrary memory.
|
||||
*/
|
||||
unsigned int untrusted:1;
|
||||
unsigned int __aer_firmware_first_valid:1;
|
||||
unsigned int __aer_firmware_first:1;
|
||||
unsigned int broken_intx_masking:1; /* INTx masking can't be used */
|
||||
|
Loading…
Reference in New Issue
Block a user