[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] Re: [Xen-devel] [PATCH] IOMMU: make DMA containment of quarantined devices optional
On Wed, Dec 11, 2019 at 01:53:00PM +0100, Jan Beulich wrote: > Containing still in flight DMA was introduced to work around certain > devices / systems hanging hard upon hitting an IOMMU fault. Passing > through (such) devices (on such systems) is inherently insecure (as > guests could easily arrange for IOMMU faults to occur). Defaulting to > a mode where admins may not even become aware of issues with devices can > be considered undesirable. Therefore convert this mode of operation to > an optional one, not one enabled by default. > > This involves resurrecting code commit ea38867831da ("x86 / iommu: set > up a scratch page in the quarantine domain") did remove, in a slightly > extended fashion. Here, instead of reintroducing a pretty pointless use > of "goto" in domain_context_unmap(), and instead of making the function > (at least temporarily) inconsistent, take the opportunity and replace > the other similarly pointless "goto" as well. > > In order to key the re-instated bypasses off of there (not) being a root > page table this further requires moving the allocate_domain_resources() > invocation from reassign_device() to amd_iommu_setup_domain_device() (or > else reassign_device() would allocate a root page table anyway); this is > benign to the second caller of the latter function. > > Signed-off-by: Jan Beulich <jbeulich@xxxxxxxx> > --- > I'm happy to take better suggestions to replace "full". > > --- a/docs/misc/xen-command-line.pandoc > +++ b/docs/misc/xen-command-line.pandoc > @@ -1232,7 +1232,7 @@ detection of systems known to misbehave > > Default: `new` unless directed-EOI is supported > > ### iommu > - = List of [ <bool>, verbose, debug, force, required, quarantine, > + = List of [ <bool>, verbose, debug, force, required, quarantine[=full], > sharept, intremap, intpost, crash-disable, > snoop, qinval, igfx, amd-iommu-perdev-intremap, > dom0-{passthrough,strict} ] > @@ -1270,11 +1270,13 @@ boolean (e.g. `iommu=no`) can override t > will prevent Xen from booting if IOMMUs aren't discovered and enabled > successfully. > > -* The `quarantine` boolean can be used to control Xen's behavior when > - de-assigning devices from guests. If enabled (the default), Xen always > - quarantines such devices; they must be explicitly assigned back to Dom0 > - before they can be used there again. If disabled, Xen will only > - quarantine devices the toolstack hass arranged for getting quarantined. > +* The `quarantine` option can be used to control Xen's behavior when > + de-assigning devices from guests. If set to true (the default), Xen > + always quarantines such devices; they must be explicitly assigned back > + to Dom0 before they can be used there again. If set to "full", still > + active DMA will additionally be directed to a "sink" page. If set to > + false, Xen will only quarantine devices the toolstack has arranged for > + getting quarantined. > > * The `sharept` boolean controls whether the IOMMU pagetables are shared > with the CPU-side HAP pagetables, or allocated separately. Sharing > --- a/xen/drivers/passthrough/amd/pci_amd_iommu.c > +++ b/xen/drivers/passthrough/amd/pci_amd_iommu.c > @@ -85,18 +85,36 @@ int get_dma_requestor_id(uint16_t seg, u > return req_id; > } > > -static void amd_iommu_setup_domain_device( > +static int __must_check allocate_domain_resources(struct domain_iommu *hd) > +{ > + int rc; > + > + spin_lock(&hd->arch.mapping_lock); > + rc = amd_iommu_alloc_root(hd); > + spin_unlock(&hd->arch.mapping_lock); > + > + return rc; > +} > + > +static int __must_check amd_iommu_setup_domain_device( > struct domain *domain, struct amd_iommu *iommu, > uint8_t devfn, struct pci_dev *pdev) > { > struct amd_iommu_dte *table, *dte; > unsigned long flags; > - int req_id, valid = 1; > + int req_id, valid = 1, rc; > u8 bus = pdev->bus; > - const struct domain_iommu *hd = dom_iommu(domain); > + struct domain_iommu *hd = dom_iommu(domain); > + > + /* dom_io is used as a sentinel for quarantined devices */ > + if ( domain == dom_io && !hd->arch.root_table ) This condition (and it's Intel counterpart) would be better in a macro IMO, so that vendor code regardless of the implementation can use the same macro (and to avoid having to add the same comment in all instances), ie: IS_DEVICE_QUARANTINED or some such would be fine IMO. > + return 0; > + > + BUG_ON(!hd->arch.paging_mode || !iommu->dev_table.buffer); > > - BUG_ON( !hd->arch.root_table || !hd->arch.paging_mode || > - !iommu->dev_table.buffer ); > + rc = allocate_domain_resources(hd); > + if ( rc ) > + return rc; > > if ( iommu_hwdom_passthrough && is_hardware_domain(domain) ) > valid = 0; > @@ -151,6 +169,8 @@ static void amd_iommu_setup_domain_devic > > amd_iommu_flush_iotlb(devfn, pdev, INV_IOMMU_ALL_PAGES_ADDRESS, 0); > } > + > + return 0; > } > > int __init acpi_ivrs_init(void) > @@ -220,17 +240,6 @@ int amd_iommu_alloc_root(struct domain_i > return 0; > } > > -static int __must_check allocate_domain_resources(struct domain_iommu *hd) > -{ > - int rc; > - > - spin_lock(&hd->arch.mapping_lock); > - rc = amd_iommu_alloc_root(hd); > - spin_unlock(&hd->arch.mapping_lock); > - > - return rc; > -} > - > int amd_iommu_get_paging_mode(unsigned long entries) > { > int level = 1; > @@ -287,6 +296,10 @@ static void amd_iommu_disable_domain_dev > int req_id; > u8 bus = pdev->bus; > > + /* dom_io is used as a sentinel for quarantined devices */ > + if ( domain == dom_io && !dom_iommu(domain)->arch.root_table ) > + return; > + > BUG_ON ( iommu->dev_table.buffer == NULL ); > req_id = get_dma_requestor_id(iommu->seg, PCI_BDF2(bus, devfn)); > table = iommu->dev_table.buffer; > @@ -333,7 +346,6 @@ static int reassign_device(struct domain > { > struct amd_iommu *iommu; > int bdf, rc; > - struct domain_iommu *t = dom_iommu(target); > > bdf = PCI_BDF2(pdev->bus, pdev->devfn); > iommu = find_iommu_for_device(pdev->seg, bdf); > @@ -354,11 +366,10 @@ static int reassign_device(struct domain > pdev->domain = target; > } > > - rc = allocate_domain_resources(t); > + rc = amd_iommu_setup_domain_device(target, iommu, devfn, pdev); > if ( rc ) > return rc; > > - amd_iommu_setup_domain_device(target, iommu, devfn, pdev); > AMD_IOMMU_DEBUG("Re-assign %04x:%02x:%02x.%u from dom%d to dom%d\n", > pdev->seg, pdev->bus, PCI_SLOT(devfn), PCI_FUNC(devfn), > source->domain_id, target->domain_id); > @@ -515,8 +526,7 @@ static int amd_iommu_add_device(u8 devfn > spin_unlock_irqrestore(&iommu->lock, flags); > } > > - amd_iommu_setup_domain_device(pdev->domain, iommu, devfn, pdev); > - return 0; > + return amd_iommu_setup_domain_device(pdev->domain, iommu, devfn, pdev); > } > > static int amd_iommu_remove_device(u8 devfn, struct pci_dev *pdev) > --- a/xen/drivers/passthrough/iommu.c > +++ b/xen/drivers/passthrough/iommu.c > @@ -30,13 +30,17 @@ bool_t __initdata iommu_enable = 1; > bool_t __read_mostly iommu_enabled; > bool_t __read_mostly force_iommu; > bool_t __read_mostly iommu_verbose; > -bool __read_mostly iommu_quarantine = true; > bool_t __read_mostly iommu_igfx = 1; > bool_t __read_mostly iommu_snoop = 1; > bool_t __read_mostly iommu_qinval = 1; > bool_t __read_mostly iommu_intremap = 1; > bool_t __read_mostly iommu_crash_disable; > > +#define IOMMU_quarantine_none false > +#define IOMMU_quarantine_basic true > +#define IOMMU_quarantine_full 2 > +uint8_t __read_mostly iommu_quarantine = IOMMU_quarantine_basic; I don't really like to use booleans with non-boolean variables. Wouldn't it be better to just use plain numbers, or even better an enum? > + > static bool __hwdom_initdata iommu_hwdom_none; > bool __hwdom_initdata iommu_hwdom_strict; > bool __read_mostly iommu_hwdom_passthrough; > @@ -81,6 +85,8 @@ static int __init parse_iommu_param(cons > force_iommu = val; > else if ( (val = parse_boolean("quarantine", s, ss)) >= 0 ) > iommu_quarantine = val; > + else if ( ss == s + 15 && !strncmp(s, "quarantine=full", 15) ) > + iommu_quarantine = IOMMU_quarantine_full; > else if ( (val = parse_boolean("igfx", s, ss)) >= 0 ) > iommu_igfx = val; > else if ( (val = parse_boolean("verbose", s, ss)) >= 0 ) > @@ -451,7 +457,7 @@ static int __init iommu_quarantine_init( > dom_io->options |= XEN_DOMCTL_CDF_iommu; > > rc = iommu_domain_init(dom_io, 0); > - if ( rc ) > + if ( rc || iommu_quarantine < IOMMU_quarantine_full ) > return rc; > > if ( !hd->platform_ops->quarantine_init ) > --- a/xen/drivers/passthrough/vtd/iommu.c > +++ b/xen/drivers/passthrough/vtd/iommu.c > @@ -1291,6 +1291,10 @@ int domain_context_mapping_one( > int agaw, rc, ret; > bool_t flush_dev_iotlb; > > + /* dom_io is used as a sentinel for quarantined devices */ > + if ( domain == dom_io && !hd->arch.pgd_maddr ) > + return 0; > + > ASSERT(pcidevs_locked()); > spin_lock(&iommu->lock); > maddr = bus_to_context_maddr(iommu, bus); > @@ -1537,6 +1541,10 @@ int domain_context_unmap_one( > int iommu_domid, rc, ret; > bool_t flush_dev_iotlb; > > + /* dom_io is used as a sentinel for quarantined devices */ > + if ( domain == dom_io && !dom_iommu(domain)->arch.pgd_maddr ) > + return 0; > + > ASSERT(pcidevs_locked()); > spin_lock(&iommu->lock); > > @@ -1598,7 +1606,7 @@ static int domain_context_unmap(struct d > { > struct acpi_drhd_unit *drhd; > struct vtd_iommu *iommu; > - int ret = 0; > + int ret; > u8 seg = pdev->seg, bus = pdev->bus, tmp_bus, tmp_devfn, secbus; > int found = 0; > > @@ -1614,14 +1622,12 @@ static int domain_context_unmap(struct d > printk(VTDPREFIX "d%d:Hostbridge: skip %04x:%02x:%02x.%u > unmap\n", > domain->domain_id, seg, bus, > PCI_SLOT(devfn), PCI_FUNC(devfn)); > - if ( !is_hardware_domain(domain) ) > - return -EPERM; > - goto out; > + return is_hardware_domain(domain) ? 0 : -EPERM; > > case DEV_TYPE_PCIe_BRIDGE: > case DEV_TYPE_PCIe2PCI_BRIDGE: > case DEV_TYPE_LEGACY_PCI_BRIDGE: > - goto out; > + return 0; > > case DEV_TYPE_PCIe_ENDPOINT: > if ( iommu_debug ) > @@ -1665,10 +1671,13 @@ static int domain_context_unmap(struct d > dprintk(XENLOG_ERR VTDPREFIX, "d%d:unknown(%u): %04x:%02x:%02x.%u\n", > domain->domain_id, pdev->type, > seg, bus, PCI_SLOT(devfn), PCI_FUNC(devfn)); > - ret = -EINVAL; > - goto out; > + return -EINVAL; > } > > + /* dom_io is used as a sentinel for quarantined devices */ > + if ( domain == dom_io && !dom_iommu(domain)->arch.pgd_maddr ) > + return ret; > + > /* > * if no other devices under the same iommu owned by this domain, > * clear iommu in iommu_bitmap and clear domain_id in domid_bitmp > @@ -1694,16 +1703,12 @@ static int domain_context_unmap(struct d > > iommu_domid = domain_iommu_domid(domain, iommu); > if ( iommu_domid == -1 ) > - { > - ret = -EINVAL; > - goto out; > - } > + return -EINVAL; > > clear_bit(iommu_domid, iommu->domid_bitmap); > iommu->domid_map[iommu_domid] = 0; > } > > -out: > return ret; > } > > --- a/xen/include/xen/iommu.h > +++ b/xen/include/xen/iommu.h > @@ -53,8 +53,9 @@ static inline bool_t dfn_eq(dfn_t x, dfn > } > > extern bool_t iommu_enable, iommu_enabled; > -extern bool force_iommu, iommu_quarantine, iommu_verbose, iommu_igfx; > +extern bool force_iommu, iommu_verbose, iommu_igfx; > extern bool_t iommu_snoop, iommu_qinval, iommu_intremap, iommu_intpost; > +extern uint8_t iommu_quarantine; Exporting this variable without the paired defines seems pointless, how are external callers supposed to figure out the quarantine mode without the IOMMU_quarantine_* defines? Thanks, Roger. _______________________________________________ Xen-devel mailing list Xen-devel@xxxxxxxxxxxxxxxxxxxx https://lists.xenproject.org/mailman/listinfo/xen-devel
|
Lists.xenproject.org is hosted with RackSpace, monitoring our |