Merge: [RHEL-10.3] Recent upstream fixes for IOMMU subsystem

MR: https://gitlab.com/redhat/centos-stream/src/kernel/centos-stream-10/-/merge_requests/2982

# Merge Request Required Information

JIRA: https://issues.redhat.com/browse/RHEL-213791
Upstream-Status: git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git

CVE: CVE-2026-53053
CVE: CVE-2026-53372
CVE: CVE-2026-64149
CVE: CVE-2026-53283
CVE: CVE-2026-53164
CVE: CVE-2026-64186
CVE: CVE-2026-64151
CVE: CVE-2026-64152

## Summary of Changes

Recent upstream fixes touching commits in RHEL10.

Signed-off-by: Jerry Snitselaar <jsnitsel@redhat.com>

Approved-by: Eder Zulian <ezulian@redhat.com>
Approved-by: Rafael Aquini <raquini@redhat.com>
Approved-by: Steve Best <sbest@redhat.com>
Approved-by: Jocelyn Falempe <jfalempe@redhat.com>
Approved-by: CKI KWF Bot <cki-ci-bot+kwf-gitlab-com@redhat.com>

Merged-by: CKI GitLab Kmaint Pipeline Bot <26919896-cki-kmaint-pipeline-bot@users.noreply.gitlab.com>
This commit is contained in:
CKI KWF Bot
2026-08-21 09:19:28 +00:00
45 changed files with 650 additions and 292 deletions
+1 -1
View File
@@ -198,7 +198,7 @@ static void tce_iommu_userspace_view_free(struct iommu_table *tbl)
static void tce_free_pSeries(struct iommu_table *tbl)
{
if (!tbl->it_userspace)
if (tbl->it_userspace)
tce_iommu_userspace_view_free(tbl);
}
+2
View File
@@ -470,6 +470,7 @@ static inline int snp_svsm_vtpm_send_command(u8 *buffer) { return -ENODEV; }
#ifdef CONFIG_KVM_AMD_SEV
bool snp_probe_rmptable_info(void);
int snp_rmptable_init(void);
int snp_lookup_rmpentry(u64 pfn, bool *assigned, int *level);
void snp_dump_hva_rmpentry(unsigned long address);
int psmash(u64 pfn);
@@ -484,6 +485,7 @@ static inline void snp_leak_pages(u64 pfn, unsigned int pages)
}
#else
static inline bool snp_probe_rmptable_info(void) { return false; }
static inline int snp_rmptable_init(void) { return -ENOSYS; }
static inline int snp_lookup_rmpentry(u64 pfn, bool *assigned, int *level) { return -ENODEV; }
static inline void snp_dump_hva_rmpentry(unsigned long address) {}
static inline int psmash(u64 pfn) { return -ENODEV; }
+7 -16
View File
@@ -505,19 +505,19 @@ static bool __init setup_rmptable(void)
* described in the SNP_INIT_EX firmware command description in the SNP
* firmware ABI spec.
*/
static int __init snp_rmptable_init(void)
int __init snp_rmptable_init(void)
{
unsigned int i;
u64 val;
if (!cc_platform_has(CC_ATTR_HOST_SEV_SNP))
return 0;
if (WARN_ON_ONCE(!cc_platform_has(CC_ATTR_HOST_SEV_SNP)))
return -ENOSYS;
if (!amd_iommu_snp_en)
goto nosnp;
if (WARN_ON_ONCE(!amd_iommu_snp_en))
return -ENOSYS;
if (!setup_rmptable())
goto nosnp;
return -ENOSYS;
/*
* Check if SEV-SNP is already enabled, this can happen in case of
@@ -530,7 +530,7 @@ static int __init snp_rmptable_init(void)
/* Zero out the RMP bookkeeping area */
if (!clear_rmptable_bookkeeping()) {
free_rmp_segment_table();
goto nosnp;
return -ENOSYS;
}
/* Zero out the RMP entries */
@@ -562,17 +562,8 @@ skip_enable:
crash_kexec_post_notifiers = true;
return 0;
nosnp:
cc_platform_clear(CC_ATTR_HOST_SEV_SNP);
return -ENOSYS;
}
/*
* This must be called after the IOMMU has been initialized.
*/
device_initcall(snp_rmptable_init);
static void set_rmp_segment_info(unsigned int segment_shift)
{
rmp_segment_shift = segment_shift;
+6 -3
View File
@@ -235,6 +235,7 @@ static const struct host1x_bo_ops tegra_bo_ops = {
static int tegra_bo_iommu_map(struct tegra_drm *tegra, struct tegra_bo *bo)
{
int prot = IOMMU_READ | IOMMU_WRITE;
ssize_t size;
int err;
if (bo->mm)
@@ -256,13 +257,15 @@ static int tegra_bo_iommu_map(struct tegra_drm *tegra, struct tegra_bo *bo)
bo->iova = bo->mm->start;
bo->size = iommu_map_sgtable(tegra->domain, bo->iova, bo->sgt, prot);
if (!bo->size) {
size = iommu_map_sgtable(tegra->domain, bo->iova, bo->sgt, prot);
if (size < 0) {
dev_err(tegra->drm->dev, "failed to map buffer\n");
err = -ENOMEM;
err = size;
goto remove;
}
bo->size = size;
mutex_unlock(&tegra->mm_lock);
return 0;
+5 -3
View File
@@ -235,6 +235,8 @@ static unsigned int pin_job(struct host1x *host, struct host1x_job *job)
}
if (host->domain) {
ssize_t map_err;
for_each_sgtable_sg(map->sgt, sg, j)
gather_size += sg->length;
@@ -248,11 +250,11 @@ static unsigned int pin_job(struct host1x *host, struct host1x_job *job)
goto put;
}
err = iommu_map_sgtable(host->domain, iova_dma_addr(&host->iova, alloc),
map_err = iommu_map_sgtable(host->domain, iova_dma_addr(&host->iova, alloc),
map->sgt, IOMMU_READ);
if (err == 0) {
if (map_err < 0) {
__free_iova(&host->iova, alloc);
err = -EINVAL;
err = map_err;
goto put;
}
+31 -31
View File
@@ -26,22 +26,20 @@ static ssize_t iommu_mmio_write(struct file *filp, const char __user *ubuf,
{
struct seq_file *m = filp->private_data;
struct amd_iommu *iommu = m->private;
int ret;
iommu->dbg_mmio_offset = -1;
int ret, dbg_mmio_offset = iommu->dbg_mmio_offset = -1;
if (cnt > OFS_IN_SZ)
return -EINVAL;
ret = kstrtou32_from_user(ubuf, cnt, 0, &iommu->dbg_mmio_offset);
ret = kstrtos32_from_user(ubuf, cnt, 0, &dbg_mmio_offset);
if (ret)
return ret;
if (iommu->dbg_mmio_offset > iommu->mmio_phys_end - sizeof(u64)) {
iommu->dbg_mmio_offset = -1;
if (dbg_mmio_offset < 0 || dbg_mmio_offset >
iommu->mmio_phys_end - sizeof(u64))
return -EINVAL;
}
iommu->dbg_mmio_offset = dbg_mmio_offset;
return cnt;
}
@@ -49,14 +47,16 @@ static int iommu_mmio_show(struct seq_file *m, void *unused)
{
struct amd_iommu *iommu = m->private;
u64 value;
int dbg_mmio_offset = iommu->dbg_mmio_offset;
if (iommu->dbg_mmio_offset < 0) {
if (dbg_mmio_offset < 0 || dbg_mmio_offset >
iommu->mmio_phys_end - sizeof(u64)) {
seq_puts(m, "Please provide mmio register's offset\n");
return 0;
}
value = readq(iommu->mmio_base + iommu->dbg_mmio_offset);
seq_printf(m, "Offset:0x%x Value:0x%016llx\n", iommu->dbg_mmio_offset, value);
value = readq(iommu->mmio_base + dbg_mmio_offset);
seq_printf(m, "Offset:0x%x Value:0x%016llx\n", dbg_mmio_offset, value);
return 0;
}
@@ -67,23 +67,20 @@ static ssize_t iommu_capability_write(struct file *filp, const char __user *ubuf
{
struct seq_file *m = filp->private_data;
struct amd_iommu *iommu = m->private;
int ret;
iommu->dbg_cap_offset = -1;
int ret, dbg_cap_offset = iommu->dbg_cap_offset = -1;
if (cnt > OFS_IN_SZ)
return -EINVAL;
ret = kstrtou32_from_user(ubuf, cnt, 0, &iommu->dbg_cap_offset);
ret = kstrtos32_from_user(ubuf, cnt, 0, &dbg_cap_offset);
if (ret)
return ret;
/* Capability register at offset 0x14 is the last IOMMU capability register. */
if (iommu->dbg_cap_offset > 0x14) {
iommu->dbg_cap_offset = -1;
if (dbg_cap_offset < 0 || dbg_cap_offset > 0x14)
return -EINVAL;
}
iommu->dbg_cap_offset = dbg_cap_offset;
return cnt;
}
@@ -91,21 +88,21 @@ static int iommu_capability_show(struct seq_file *m, void *unused)
{
struct amd_iommu *iommu = m->private;
u32 value;
int err;
int err, dbg_cap_offset = iommu->dbg_cap_offset;
if (iommu->dbg_cap_offset < 0) {
if (dbg_cap_offset < 0 || dbg_cap_offset > 0x14) {
seq_puts(m, "Please provide capability register's offset in the range [0x00 - 0x14]\n");
return 0;
}
err = pci_read_config_dword(iommu->dev, iommu->cap_ptr + iommu->dbg_cap_offset, &value);
err = pci_read_config_dword(iommu->dev, iommu->cap_ptr + dbg_cap_offset, &value);
if (err) {
seq_printf(m, "Not able to read capability register at 0x%x\n",
iommu->dbg_cap_offset);
dbg_cap_offset);
return 0;
}
seq_printf(m, "Offset:0x%x Value:0x%08x\n", iommu->dbg_cap_offset, value);
seq_printf(m, "Offset:0x%x Value:0x%08x\n", dbg_cap_offset, value);
return 0;
}
@@ -197,10 +194,11 @@ static ssize_t devid_write(struct file *filp, const char __user *ubuf,
static int devid_show(struct seq_file *m, void *unused)
{
u16 devid;
int sbdf_shadow = sbdf;
if (sbdf >= 0) {
devid = PCI_SBDF_TO_DEVID(sbdf);
seq_printf(m, "%04x:%02x:%02x.%x\n", PCI_SBDF_TO_SEGID(sbdf),
if (sbdf_shadow >= 0) {
devid = PCI_SBDF_TO_DEVID(sbdf_shadow);
seq_printf(m, "%04x:%02x:%02x.%x\n", PCI_SBDF_TO_SEGID(sbdf_shadow),
PCI_BUS_NUM(devid), PCI_SLOT(devid), PCI_FUNC(devid));
} else
seq_puts(m, "No or Invalid input provided\n");
@@ -237,13 +235,14 @@ static int iommu_devtbl_show(struct seq_file *m, void *unused)
{
struct amd_iommu_pci_seg *pci_seg;
u16 seg, devid;
int sbdf_shadow = sbdf;
if (sbdf < 0) {
if (sbdf_shadow < 0) {
seq_puts(m, "Enter a valid device ID to 'devid' file\n");
return 0;
}
seg = PCI_SBDF_TO_SEGID(sbdf);
devid = PCI_SBDF_TO_DEVID(sbdf);
seg = PCI_SBDF_TO_SEGID(sbdf_shadow);
devid = PCI_SBDF_TO_DEVID(sbdf_shadow);
for_each_pci_segment(pci_seg) {
if (pci_seg->id != seg)
@@ -336,19 +335,20 @@ static int iommu_irqtbl_show(struct seq_file *m, void *unused)
{
struct amd_iommu_pci_seg *pci_seg;
u16 devid, seg;
int sbdf_shadow = sbdf;
if (!irq_remapping_enabled) {
seq_puts(m, "Interrupt remapping is disabled\n");
return 0;
}
if (sbdf < 0) {
if (sbdf_shadow < 0) {
seq_puts(m, "Enter a valid device ID to 'devid' file\n");
return 0;
}
seg = PCI_SBDF_TO_SEGID(sbdf);
devid = PCI_SBDF_TO_DEVID(sbdf);
seg = PCI_SBDF_TO_SEGID(sbdf_shadow);
devid = PCI_SBDF_TO_DEVID(sbdf_shadow);
for_each_pci_segment(pci_seg) {
if (pci_seg->id != seg)
+32 -7
View File
@@ -1939,11 +1939,10 @@ static int __init init_iommu_one(struct amd_iommu *iommu, struct ivhd_header *h,
/* XT and GAM require GA mode. */
if ((h->efr_reg & (0x1 << IOMMU_EFR_GASUP_SHIFT)) == 0) {
amd_iommu_guest_ir = AMD_IOMMU_GUEST_IR_LEGACY;
break;
}
} else {
if (h->efr_reg & BIT(IOMMU_EFR_XTSUP_SHIFT))
amd_iommu_xt_mode = IRQ_REMAP_X2APIC_MODE;
}
if (h->efr_attr & BIT(IOMMU_IVHD_ATTR_HATDIS_SHIFT)) {
pr_warn_once("Host Address Translation is not supported.\n");
@@ -3365,7 +3364,7 @@ out:
return true;
}
static void iommu_snp_enable(void)
static __init void iommu_snp_enable(void)
{
#ifdef CONFIG_KVM_AMD_SEV
if (!cc_platform_has(CC_ATTR_HOST_SEV_SNP))
@@ -3390,6 +3389,14 @@ static void iommu_snp_enable(void)
goto disable_snp;
}
/*
* Enable host SNP support once SNP support is checked on IOMMU.
*/
if (snp_rmptable_init()) {
pr_warn("SNP: RMP initialization failed, SNP cannot be supported.\n");
goto disable_snp;
}
pr_info("IOMMU SNP support enabled.\n");
return;
@@ -3531,6 +3538,19 @@ static int __init iommu_go_to_state(enum iommu_init_state state)
ret = state_next();
}
/*
* SNP platform initilazation requires IOMMUs to be fully configured.
* If the SNP support on IOMMUs has NOT been checked, simply mark SNP
* as unsupported. If the SNP support on IOMMUs has been checked and
* host SNP support enabled but RMP enforcement has not been enabled
* in IOMMUs, then the system is in a half-baked state, but can limp
* along as all memory should be Hypervisor-Owned in the RMP. WARN,
* but leave SNP as "supported" to avoid confusing the kernel.
*/
if (ret && cc_platform_has(CC_ATTR_HOST_SEV_SNP) &&
!WARN_ON_ONCE(amd_iommu_snp_en))
cc_platform_clear(CC_ATTR_HOST_SEV_SNP);
return ret;
}
@@ -3638,18 +3658,23 @@ void __init amd_iommu_detect(void)
int ret;
if (no_iommu || (iommu_detected && !gart_iommu_aperture))
return;
goto disable_snp;
if (!amd_iommu_sme_check())
return;
goto disable_snp;
ret = iommu_go_to_state(IOMMU_IVRS_DETECTED);
if (ret)
return;
goto disable_snp;
amd_iommu_detected = true;
iommu_detected = 1;
x86_init.iommu.iommu_init = amd_iommu_init;
return;
disable_snp:
if (cc_platform_has(CC_ATTR_HOST_SEV_SNP))
cc_platform_clear(CC_ATTR_HOST_SEV_SNP);
}
/****************************************************************************
+27 -9
View File
@@ -351,7 +351,11 @@ static struct amd_iommu *__rlookup_amd_iommu(u16 seg, u16 devid)
struct amd_iommu_pci_seg *pci_seg;
for_each_pci_segment(pci_seg) {
if (pci_seg->id == seg)
if (pci_seg->id != seg)
continue;
/* IVRS may not describe every device on the bus */
if (devid > pci_seg->last_bdf)
return NULL;
return pci_seg->rlookup_table[devid];
}
return NULL;
@@ -403,11 +407,12 @@ struct iommu_dev_data *search_dev_data(struct amd_iommu *iommu, u16 devid)
return NULL;
}
static int clone_alias(struct pci_dev *pdev, u16 alias, void *data)
static int clone_alias(struct pci_dev *pdev_origin, u16 alias, void *data)
{
struct dev_table_entry new;
struct amd_iommu *iommu;
struct iommu_dev_data *dev_data, *alias_data;
struct pci_dev *pdev = data;
u16 devid = pci_dev_id(pdev);
int ret = 0;
@@ -454,9 +459,9 @@ static void clone_aliases(struct amd_iommu *iommu, struct device *dev)
* part of the PCI DMA aliases if it's bus differs
* from the original device.
*/
clone_alias(pdev, iommu->pci_seg->alias_table[pci_dev_id(pdev)], NULL);
clone_alias(pdev, iommu->pci_seg->alias_table[pci_dev_id(pdev)], pdev);
pci_for_each_dma_alias(pdev, clone_alias, NULL);
pci_for_each_dma_alias(pdev, clone_alias, pdev);
}
static void setup_aliases(struct amd_iommu *iommu, struct device *dev)
@@ -1769,7 +1774,8 @@ void amd_iommu_domain_flush_pages(struct protection_domain *domain,
{
lockdep_assert_held(&domain->lock);
if (likely(!amd_iommu_np_cache)) {
if (likely(!amd_iommu_np_cache) ||
size >= (1ULL<<52)) {
__domain_flush_pages(domain, address, size);
/* Wait until IOMMU TLB and all device IOTLB flushes are complete */
@@ -2148,7 +2154,8 @@ static void set_dte_passthrough(struct iommu_dev_data *dev_data,
new->data[0] |= DTE_FLAG_TV | DTE_FLAG_IR | DTE_FLAG_IW;
new->data[1] |= FIELD_PREP(DTE_DOMID_MASK, domain->id) |
(dev_data->ats_enabled) ? DTE_FLAG_IOTLB : 0;
(dev_data->ats_enabled ? DTE_FLAG_IOTLB : 0);
}
static void set_dte_entry(struct amd_iommu *iommu,
@@ -2909,8 +2916,21 @@ static struct iommu_domain blocked_domain = {
static struct protection_domain identity_domain;
static int amd_iommu_identity_attach(struct iommu_domain *dom, struct device *dev,
struct iommu_domain *old)
{
/*
* Don't allow attaching a device to the identity domain if SNP is
* enabled.
*/
if (amd_iommu_snp_en)
return -EINVAL;
return amd_iommu_attach_device(dom, dev, old);
}
static const struct iommu_domain_ops identity_domain_ops = {
.attach_dev = amd_iommu_attach_device,
.attach_dev = amd_iommu_identity_attach,
};
void amd_iommu_init_identity_domain(void)
@@ -2978,8 +2998,6 @@ static bool amd_iommu_capable(struct device *dev, enum iommu_cap cap)
return amdr_ivrs_remap_support;
case IOMMU_CAP_ENFORCE_CACHE_COHERENCY:
return true;
case IOMMU_CAP_DEFERRED_FLUSH:
return true;
case IOMMU_CAP_DIRTY_TRACKING: {
struct amd_iommu *iommu = get_amd_iommu_from_dev(dev);
+1 -1
View File
@@ -39,7 +39,7 @@ static const struct of_device_id qcom_smmu_actlr_client_of_match[] = {
.data = (const void *) (PREFETCH_DEEP | CPRE | CMTLB) },
{ .compatible = "qcom,adreno-smmu",
.data = (const void *) (PREFETCH_DEEP | CPRE | CMTLB) },
{ .compatible = "qcom,fastrpc",
{ .compatible = "qcom,fastrpc-compute-cb",
.data = (const void *) (PREFETCH_DEEP | CPRE | CMTLB) },
{ .compatible = "qcom,qcm2290-mdss",
.data = (const void *) (PREFETCH_SHALLOW | CPRE | CMTLB) },
+23 -5
View File
@@ -14,6 +14,7 @@
#include <linux/device.h>
#include <linux/dma-direct.h>
#include <linux/dma-map-ops.h>
#include <linux/generic_pt/iommu.h>
#include <linux/gfp.h>
#include <linux/huge_mm.h>
#include <linux/iommu.h>
@@ -648,6 +649,15 @@ static void iommu_dma_init_options(struct iommu_dma_options *options,
}
}
static bool iommu_domain_supports_fq(struct device *dev,
struct iommu_domain *domain)
{
/* iommupt always supports DMA-FQ */
if (iommupt_from_domain(domain))
return true;
return device_iommu_capable(dev, IOMMU_CAP_DEFERRED_FLUSH);
}
/**
* iommu_dma_init_domain - Initialise a DMA mapping domain
* @domain: IOMMU domain previously prepared by iommu_get_dma_cookie()
@@ -706,7 +716,8 @@ static int iommu_dma_init_domain(struct iommu_domain *domain, struct device *dev
/* If the FQ fails we can simply fall back to strict mode */
if (domain->type == IOMMU_DOMAIN_DMA_FQ &&
(!device_iommu_capable(dev, IOMMU_CAP_DEFERRED_FLUSH) || iommu_dma_init_fq(domain)))
(!iommu_domain_supports_fq(dev, domain) ||
iommu_dma_init_fq(domain)))
domain->type = IOMMU_DOMAIN_DMA;
return iova_reserve_iommu_regions(dev, domain);
@@ -1441,7 +1452,7 @@ int iommu_dma_map_sg(struct device *dev, struct scatterlist *sg, int nents,
*/
s->dma_address = pci_p2pdma_bus_addr_map(
p2pdma_state.mem, sg_phys(s));
sg_dma_len(s) = sg->length;
sg_dma_len(s) = s->length;
sg_dma_mark_bus_address(s);
continue;
default:
@@ -1894,12 +1905,18 @@ static int iommu_dma_iova_link_swiotlb(struct device *dev,
return 0;
}
/*
* After removing the partial head and tail, there may be no aligned
* middle left to map. The tail still gets bounced below.
*/
size -= iova_end_pad;
error = __dma_iova_link(dev, addr + mapped, phys + mapped, size, dir,
attrs);
if (size) {
error = __dma_iova_link(dev, addr + mapped, phys + mapped,
size, dir, attrs);
if (error)
goto out_unmap;
mapped += size;
}
if (iova_end_pad) {
error = iommu_dma_iova_bounce_and_link(dev, addr + mapped,
@@ -1912,7 +1929,8 @@ static int iommu_dma_iova_link_swiotlb(struct device *dev,
return 0;
out_unmap:
dma_iova_unlink(dev, state, 0, mapped, dir, attrs);
if (mapped)
dma_iova_unlink(dev, state, offset, mapped, dir, attrs);
return error;
}
+104 -77
View File
@@ -51,16 +51,27 @@ static void gather_range_pages(struct iommu_iotlb_gather *iotlb_gather,
iommu_pages_stop_incoherent_list(free_list,
iommu_table->iommu_device);
/*
* If running in DMA-FQ mode then the unmap will be followed by an IOTLB
* flush all so we need to optimize by never flushing the IOTLB here.
*
* For NO_GAPS the user gets to pick if flushing all or doing micro
* flushes is better for their work load by choosing DMA vs DMA-FQ
* operation. Drivers should also see shadow_on_flush.
*/
if (!iommu_iotlb_gather_queued(iotlb_gather)) {
if (pt_feature(common, PT_FEAT_FLUSH_RANGE_NO_GAPS) &&
iommu_iotlb_gather_is_disjoint(iotlb_gather, iova, len)) {
iommu_iotlb_sync(&iommu_table->domain, iotlb_gather);
/*
* Note that the sync frees the gather's free list, so we must
* not have any pages on that list that are covered by iova/len
* Note that the sync frees the gather's free list, so
* we must not have any pages on that list that are
* covered by iova/len
*/
}
iommu_iotlb_gather_add_range(iotlb_gather, iova, len);
}
iommu_pages_list_splice(free_list, &iotlb_gather->freelist);
}
@@ -466,6 +477,7 @@ struct pt_iommu_map_args {
pt_oaddr_t oa;
unsigned int leaf_pgsize_lg2;
unsigned int leaf_level;
pt_vaddr_t num_leaves;
};
/*
@@ -518,11 +530,17 @@ static int clear_contig(const struct pt_state *start_pts,
static int __map_range_leaf(struct pt_range *range, void *arg,
unsigned int level, struct pt_table_p *table)
{
struct pt_iommu *iommu_table = iommu_from_common(range->common);
struct pt_state pts = pt_init(range, level, table);
struct pt_iommu_map_args *map = arg;
unsigned int leaf_pgsize_lg2 = map->leaf_pgsize_lg2;
unsigned int leaves_avail;
unsigned int start_index;
pt_oaddr_t oa = map->oa;
pt_vaddr_t num_leaves;
unsigned int orig_end;
unsigned int step_lg2;
pt_vaddr_t last_va;
unsigned int step;
bool need_contig;
int ret = 0;
@@ -530,12 +548,25 @@ static int __map_range_leaf(struct pt_range *range, void *arg,
PT_WARN_ON(map->leaf_level != level);
PT_WARN_ON(!pt_can_have_leaf(&pts));
step = log2_to_int_t(unsigned int,
leaf_pgsize_lg2 - pt_table_item_lg2sz(&pts));
need_contig = leaf_pgsize_lg2 != pt_table_item_lg2sz(&pts);
step_lg2 = leaf_pgsize_lg2 - pt_table_item_lg2sz(&pts);
step = log2_to_int_t(unsigned int, step_lg2);
need_contig = step_lg2 != 0;
_pt_iter_first(&pts);
start_index = pts.index;
orig_end = pts.end_index;
leaves_avail =
log2_div_t(unsigned int, pts.end_index - pts.index, step_lg2);
if (map->num_leaves <= leaves_avail) {
/* Need to stop in the middle of the table to change sizes */
pts.end_index = pts.index + log2_mul(map->num_leaves, step_lg2);
num_leaves = 0;
} else {
num_leaves = map->num_leaves - leaves_avail;
}
PT_WARN_ON(
log2_mod_t(unsigned int, pts.end_index - pts.index, step_lg2));
do {
pts.type = pt_load_entry_raw(&pts);
if (pts.type != PT_ENTRY_EMPTY || need_contig) {
@@ -561,7 +592,40 @@ static int __map_range_leaf(struct pt_range *range, void *arg,
flush_writes_range(&pts, start_index, pts.index);
map->oa = oa;
map->num_leaves = num_leaves;
if (ret || num_leaves)
return ret;
/* range->va is not valid if we reached the end of the table */
pts.index -= step;
pt_index_to_va(&pts);
pts.index += step;
last_va = range->va + log2_to_int(leaf_pgsize_lg2);
if (last_va - 1 == range->last_va) {
PT_WARN_ON(pts.index != orig_end);
return 0;
}
/*
* Reached a point where the page size changed, compute the new
* parameters.
*/
map->leaf_pgsize_lg2 = pt_compute_best_pgsize(
iommu_table->domain.pgsize_bitmap, last_va, range->last_va, oa);
map->leaf_level =
pt_pgsz_lg2_to_level(range->common, map->leaf_pgsize_lg2);
map->num_leaves = pt_pgsz_count(iommu_table->domain.pgsize_bitmap,
last_va, range->last_va, oa,
map->leaf_pgsize_lg2);
/* Didn't finish this table level, caller will repeat it */
if (pts.index != orig_end) {
if (pts.index != start_index)
pt_index_to_va(&pts);
return -EAGAIN;
}
return 0;
}
static int __map_range(struct pt_range *range, void *arg, unsigned int level,
@@ -584,14 +648,9 @@ static int __map_range(struct pt_range *range, void *arg, unsigned int level,
if (pts.type != PT_ENTRY_EMPTY)
return -EADDRINUSE;
ret = pt_iommu_new_table(&pts, &map->attrs);
if (ret) {
/*
* Racing with another thread installing a table
*/
if (ret == -EAGAIN)
continue;
/* EAGAIN on a race will loop again */
if (ret)
return ret;
}
} else {
pts.table_lower = pt_table_ptr(&pts);
/*
@@ -615,10 +674,12 @@ static int __map_range(struct pt_range *range, void *arg, unsigned int level,
* The already present table can possibly be shared with another
* concurrent map.
*/
do {
if (map->leaf_level == level - 1)
ret = pt_descend(&pts, arg, __map_range_leaf);
else
ret = pt_descend(&pts, arg, __map_range);
} while (ret == -EAGAIN);
if (ret)
return ret;
@@ -626,6 +687,14 @@ static int __map_range(struct pt_range *range, void *arg, unsigned int level,
pt_index_to_va(&pts);
if (pts.index >= pts.end_index)
break;
/*
* This level is currently running __map_range_leaf() which is
* not correct if the target level has been updated to this
* level. Have the caller invoke __map_range_leaf.
*/
if (map->leaf_level == level)
return -EAGAIN;
} while (true);
return 0;
}
@@ -797,12 +866,13 @@ static int check_map_range(struct pt_iommu *iommu_table, struct pt_range *range,
static int do_map(struct pt_range *range, struct pt_common *common,
bool single_page, struct pt_iommu_map_args *map)
{
int ret;
/*
* The __map_single_page() fast path does not support DMA_INCOHERENT
* flushing to keep its .text small.
*/
if (single_page && !pt_feature(common, PT_FEAT_DMA_INCOHERENT)) {
int ret;
ret = pt_walk_range(range, __map_single_page, map);
if (ret != -EAGAIN)
@@ -810,50 +880,25 @@ static int do_map(struct pt_range *range, struct pt_common *common,
/* EAGAIN falls through to the full path */
}
do {
if (map->leaf_level == range->top_level)
return pt_walk_range(range, __map_range_leaf, map);
return pt_walk_range(range, __map_range, map);
ret = pt_walk_range(range, __map_range_leaf, map);
else
ret = pt_walk_range(range, __map_range, map);
} while (ret == -EAGAIN);
return ret;
}
/**
* map_pages() - Install translation for an IOVA range
* @domain: Domain to manipulate
* @iova: IO virtual address to start
* @paddr: Physical/Output address to start
* @pgsize: Length of each page
* @pgcount: Length of the range in pgsize units starting from @iova
* @prot: A bitmap of IOMMU_READ/WRITE/CACHE/NOEXEC/MMIO
* @gfp: GFP flags for any memory allocations
* @mapped: Total bytes successfully mapped
*
* The range starting at IOVA will have paddr installed into it. The caller
* must specify a valid pgsize and pgcount to segment the range into compatible
* blocks.
*
* On error the caller will probably want to invoke unmap on the range from iova
* up to the amount indicated by @mapped to return the table back to an
* unchanged state.
*
* Context: The caller must hold a write range lock that includes the whole
* range.
*
* Returns: -ERRNO on failure, 0 on success. The number of bytes of VA that were
* mapped are added to @mapped, @mapped is not zerod first.
*/
int DOMAIN_NS(map_pages)(struct iommu_domain *domain, unsigned long iova,
phys_addr_t paddr, size_t pgsize, size_t pgcount,
int prot, gfp_t gfp, size_t *mapped)
static int NS(map_range)(struct pt_iommu *iommu_table, dma_addr_t iova,
phys_addr_t paddr, dma_addr_t len, unsigned int prot,
gfp_t gfp, size_t *mapped)
{
struct pt_iommu *iommu_table =
container_of(domain, struct pt_iommu, domain);
pt_vaddr_t pgsize_bitmap = iommu_table->domain.pgsize_bitmap;
struct pt_common *common = common_from_iommu(iommu_table);
struct iommu_iotlb_gather iotlb_gather;
pt_vaddr_t len = pgsize * pgcount;
struct pt_iommu_map_args map = {
.iotlb_gather = &iotlb_gather,
.oa = paddr,
.leaf_pgsize_lg2 = vaffs(pgsize),
};
bool single_page = false;
struct pt_range range;
@@ -881,13 +926,13 @@ int DOMAIN_NS(map_pages)(struct iommu_domain *domain, unsigned long iova,
return ret;
/* Calculate target page size and level for the leaves */
if (pt_has_system_page_size(common) && pgsize == PAGE_SIZE &&
pgcount == 1) {
PT_WARN_ON(!(pgsize_bitmap & PAGE_SIZE));
if (pt_has_system_page_size(common) && len == PAGE_SIZE &&
likely(pgsize_bitmap & PAGE_SIZE)) {
if (log2_mod(iova | paddr, PAGE_SHIFT))
return -ENXIO;
map.leaf_pgsize_lg2 = PAGE_SHIFT;
map.leaf_level = 0;
map.num_leaves = 1;
single_page = true;
} else {
map.leaf_pgsize_lg2 = pt_compute_best_pgsize(
@@ -896,6 +941,9 @@ int DOMAIN_NS(map_pages)(struct iommu_domain *domain, unsigned long iova,
return -ENXIO;
map.leaf_level =
pt_pgsz_lg2_to_level(common, map.leaf_pgsize_lg2);
map.num_leaves = pt_pgsz_count(pgsize_bitmap, range.va,
range.last_va, paddr,
map.leaf_pgsize_lg2);
}
ret = check_map_range(iommu_table, &range, &map);
@@ -918,7 +966,6 @@ int DOMAIN_NS(map_pages)(struct iommu_domain *domain, unsigned long iova,
*mapped += map.oa - paddr;
return ret;
}
EXPORT_SYMBOL_NS_GPL(DOMAIN_NS(map_pages), "GENERIC_PT_IOMMU");
struct pt_unmap_args {
struct iommu_pages_list free_list;
@@ -1020,34 +1067,12 @@ start_oa:
return ret;
}
/**
* unmap_pages() - Make a range of IOVA empty/not present
* @domain: Domain to manipulate
* @iova: IO virtual address to start
* @pgsize: Length of each page
* @pgcount: Length of the range in pgsize units starting from @iova
* @iotlb_gather: Gather struct that must be flushed on return
*
* unmap_pages() will remove a translation created by map_pages(). It cannot
* subdivide a mapping created by map_pages(), so it should be called with IOVA
* ranges that match those passed to map_pages(). The IOVA range can aggregate
* contiguous map_pages() calls so long as no individual range is split.
*
* Context: The caller must hold a write range lock that includes
* the whole range.
*
* Returns: Number of bytes of VA unmapped. iova + res will be the point
* unmapping stopped.
*/
size_t DOMAIN_NS(unmap_pages)(struct iommu_domain *domain, unsigned long iova,
size_t pgsize, size_t pgcount,
static size_t NS(unmap_range)(struct pt_iommu *iommu_table, dma_addr_t iova,
dma_addr_t len,
struct iommu_iotlb_gather *iotlb_gather)
{
struct pt_iommu *iommu_table =
container_of(domain, struct pt_iommu, domain);
struct pt_unmap_args unmap = { .free_list = IOMMU_PAGES_LIST_INIT(
unmap.free_list) };
pt_vaddr_t len = pgsize * pgcount;
struct pt_range range;
int ret;
@@ -1062,7 +1087,6 @@ size_t DOMAIN_NS(unmap_pages)(struct iommu_domain *domain, unsigned long iova,
return unmap.unmapped;
}
EXPORT_SYMBOL_NS_GPL(DOMAIN_NS(unmap_pages), "GENERIC_PT_IOMMU");
static void NS(get_info)(struct pt_iommu *iommu_table,
struct pt_iommu_info *info)
@@ -1110,6 +1134,8 @@ static void NS(deinit)(struct pt_iommu *iommu_table)
}
static const struct pt_iommu_ops NS(ops) = {
.map_range = NS(map_range),
.unmap_range = NS(unmap_range),
#if IS_ENABLED(CONFIG_IOMMUFD_DRIVER) && defined(pt_entry_is_write_dirty) && \
IS_ENABLED(CONFIG_IOMMUFD_TEST) && defined(pt_entry_make_write_dirty)
.set_dirty = NS(set_dirty),
@@ -1172,6 +1198,7 @@ static int pt_iommu_init_domain(struct pt_iommu *iommu_table,
domain->type = __IOMMU_DOMAIN_PAGING;
domain->pgsize_bitmap = info.pgsize_bitmap;
domain->is_iommupt = true;
if (pt_feature(common, PT_FEAT_DYNAMIC_TOP))
range = _pt_top_range(common,
@@ -312,6 +312,17 @@ static void test_best_pgsize(struct kunit *test)
}
}
static void test_pgsz_count(struct kunit *test)
{
KUNIT_EXPECT_EQ(test,
pt_pgsz_count(SZ_4K, 0, SZ_1G - 1, 0, ilog2(SZ_4K)),
SZ_1G / SZ_4K);
KUNIT_EXPECT_EQ(test,
pt_pgsz_count(SZ_2M | SZ_4K, SZ_4K, SZ_1G - 1, SZ_4K,
ilog2(SZ_4K)),
(SZ_2M - SZ_4K) / SZ_4K);
}
/*
* Check that pt_install_table() and pt_table_pa() match
*/
@@ -770,6 +781,7 @@ static struct kunit_case generic_pt_test_cases[] = {
KUNIT_CASE_FMT(test_init),
KUNIT_CASE_FMT(test_bitops),
KUNIT_CASE_FMT(test_best_pgsize),
KUNIT_CASE_FMT(test_pgsz_count),
KUNIT_CASE_FMT(test_table_ptr),
KUNIT_CASE_FMT(test_max_va),
KUNIT_CASE_FMT(test_table_radix),
+22
View File
@@ -569,6 +569,28 @@ static inline unsigned int pt_compute_best_pgsize(pt_vaddr_t pgsz_bitmap,
return pgsz_lg2;
}
/*
* Return the number of pgsize_lg2 leaf entries that can be mapped for
* va to oa. This accounts for any requirement to reduce or increase the page
* size across the VA range.
*/
static inline pt_vaddr_t pt_pgsz_count(pt_vaddr_t pgsz_bitmap, pt_vaddr_t va,
pt_vaddr_t last_va, pt_oaddr_t oa,
unsigned int pgsize_lg2)
{
pt_vaddr_t len = last_va - va + 1;
pt_vaddr_t next_pgsizes = log2_set_mod(pgsz_bitmap, 0, pgsize_lg2 + 1);
if (next_pgsizes) {
unsigned int next_pgsize_lg2 = vaffs(next_pgsizes);
if (log2_mod(va ^ oa, next_pgsize_lg2) == 0)
len = min(len, log2_set_mod_max(va, next_pgsize_lg2) -
va + 1);
}
return log2_div(len, pgsize_lg2);
}
#define _PT_MAKE_CALL_LEVEL(fn) \
static __always_inline int fn(struct pt_range *range, void *arg, \
unsigned int level, \
+1 -2
View File
@@ -1314,7 +1314,6 @@ static int qi_check_fault(struct intel_iommu *iommu, int index, int wait_index)
if (fault & DMA_FSTS_ITE) {
head = readl(iommu->reg + DMAR_IQH_REG);
head = ((head >> shift) - 1 + QI_LENGTH) % QI_LENGTH;
head |= 1;
tail = readl(iommu->reg + DMAR_IQT_REG);
tail = ((tail >> shift) - 1 + QI_LENGTH) % QI_LENGTH;
@@ -1331,7 +1330,7 @@ static int qi_check_fault(struct intel_iommu *iommu, int index, int wait_index)
do {
if (qi->desc_status[head] == QI_IN_USE)
qi->desc_status[head] = QI_ABORT;
head = (head - 2 + QI_LENGTH) % QI_LENGTH;
head = (head - 1 + QI_LENGTH) % QI_LENGTH;
} while (head != tail);
/*
+7 -1
View File
@@ -157,7 +157,10 @@ static void device_rbtree_remove(struct device_domain_info *info)
unsigned long flags;
spin_lock_irqsave(&iommu->device_rbtree_lock, flags);
if (!RB_EMPTY_NODE(&info->node)) {
rb_erase(&info->node, &iommu->device_rbtree);
RB_CLEAR_NODE(&info->node);
}
spin_unlock_irqrestore(&iommu->device_rbtree_lock, flags);
}
@@ -3208,7 +3211,6 @@ static bool intel_iommu_capable(struct device *dev, enum iommu_cap cap)
switch (cap) {
case IOMMU_CAP_CACHE_COHERENCY:
case IOMMU_CAP_DEFERRED_FLUSH:
return true;
case IOMMU_CAP_PRE_BOOT_PROTECTION:
return dmar_platform_optin();
@@ -3251,6 +3253,7 @@ static struct iommu_device *intel_iommu_probe_device(struct device *dev)
info->dev = dev;
info->iommu = iommu;
RB_CLEAR_NODE(&info->node);
if (dev_is_pci(dev)) {
if (ecap_dev_iotlb_support(iommu->ecap) &&
pci_ats_supported(pdev) &&
@@ -3932,6 +3935,9 @@ static void quirk_iommu_igfx(struct pci_dev *dev)
disable_igfx_iommu = 1;
}
/* Q35 integrated gfx dmar support is totally busted. */
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x29b2, quirk_iommu_igfx);
/* G4x/GM45 integrated gfx dmar support is totally busted. */
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x2a40, quirk_iommu_igfx);
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x2e00, quirk_iommu_igfx);
+11
View File
@@ -1265,18 +1265,29 @@ void intel_iommu_disable_iopf(struct device *dev);
static inline int iopf_for_domain_set(struct iommu_domain *domain,
struct device *dev)
{
struct device_domain_info *info = dev_iommu_priv_get(dev);
if (!domain || !domain->iopf_handler)
return 0;
/* SVA with non-IOMMU/PRI IOPF handling is allowed. */
if (domain->type == IOMMU_DOMAIN_SVA && !info->pri_supported)
return 0;
return intel_iommu_enable_iopf(dev);
}
static inline void iopf_for_domain_remove(struct iommu_domain *domain,
struct device *dev)
{
struct device_domain_info *info = dev_iommu_priv_get(dev);
if (!domain || !domain->iopf_handler)
return;
if (domain->type == IOMMU_DOMAIN_SVA && !info->pri_supported)
return;
intel_iommu_disable_iopf(dev);
}
+5 -1
View File
@@ -148,6 +148,7 @@ static int intel_nested_set_dev_pasid(struct iommu_domain *domain,
{
struct device_domain_info *info = dev_iommu_priv_get(dev);
struct dmar_domain *dmar_domain = to_dmar_domain(domain);
struct iommu_domain *s2_domain = &dmar_domain->s2_domain->domain;
struct intel_iommu *iommu = info->iommu;
struct dev_pasid_info *dev_pasid;
int ret;
@@ -155,10 +156,13 @@ static int intel_nested_set_dev_pasid(struct iommu_domain *domain,
if (!pasid_supported(iommu) || dev_is_real_dma_subdevice(dev))
return -EOPNOTSUPP;
if (s2_domain->dirty_ops)
return -EINVAL;
if (context_copied(iommu, info->bus, info->devfn))
return -EBUSY;
ret = paging_domain_compatible(&dmar_domain->s2_domain->domain, dev);
ret = paging_domain_compatible(s2_domain, dev);
if (ret)
return ret;
+3 -1
View File
@@ -748,10 +748,12 @@ static void device_pasid_table_teardown(struct device *dev, u8 bus, u8 devfn)
}
did = context_domain_id(context);
context_clear_entry(context);
context_clear_present(context);
__iommu_flush_cache(iommu, context, sizeof(*context));
spin_unlock(&iommu->lock);
intel_context_flush_no_pasid(info, context, did);
context_clear_entry(context);
__iommu_flush_cache(iommu, context, sizeof(*context));
}
static int pci_pasid_table_teardown(struct pci_dev *pdev, u16 alias, void *data)
+1 -1
View File
@@ -137,7 +137,7 @@ static inline void iommu_pages_flush_incoherent(struct device *dma_dev,
void *virt, size_t offset,
size_t len)
{
dma_sync_single_for_device(dma_dev, (uintptr_t)virt + offset, len,
dma_sync_single_for_device(dma_dev, virt_to_phys(virt) + offset, len,
DMA_TO_DEVICE);
}
void iommu_pages_stop_incoherent_list(struct iommu_pages_list *list,
+80 -46
View File
@@ -35,6 +35,7 @@
#include <linux/sched/mm.h>
#include <linux/msi.h>
#include <uapi/linux/iommufd.h>
#include <linux/generic_pt/iommu.h>
#include "dma-iommu.h"
#include "iommu-priv.h"
@@ -2613,29 +2614,18 @@ out_set_count:
return pgsize;
}
int iommu_map_nosync(struct iommu_domain *domain, unsigned long iova,
phys_addr_t paddr, size_t size, int prot, gfp_t gfp)
static int __iommu_map_domain_pgtbl(struct iommu_domain *domain,
unsigned long iova, phys_addr_t paddr,
size_t size, int prot, gfp_t gfp,
size_t *mapped)
{
const struct iommu_domain_ops *ops = domain->ops;
unsigned long orig_iova = iova;
unsigned int min_pagesz;
size_t orig_size = size;
phys_addr_t orig_paddr = paddr;
int ret = 0;
might_sleep_if(gfpflags_allow_blocking(gfp));
if (unlikely(!(domain->type & __IOMMU_DOMAIN_PAGING)))
return -EINVAL;
if (WARN_ON(!ops->map_pages || domain->pgsize_bitmap == 0UL))
if (WARN_ON(!ops->map_pages))
return -ENODEV;
/* Discourage passing strange GFP flags */
if (WARN_ON_ONCE(gfp & (__GFP_COMP | __GFP_DMA | __GFP_DMA32 |
__GFP_HIGHMEM)))
return -EINVAL;
/* find out the minimum page size supported */
min_pagesz = 1 << __ffs(domain->pgsize_bitmap);
@@ -2653,36 +2643,27 @@ int iommu_map_nosync(struct iommu_domain *domain, unsigned long iova,
pr_debug("map: iova 0x%lx pa %pa size 0x%zx\n", iova, &paddr, size);
while (size) {
size_t pgsize, count, mapped = 0;
size_t pgsize, count, op_mapped = 0;
pgsize = iommu_pgsize(domain, iova, paddr, size, &count);
pr_debug("mapping: iova 0x%lx pa %pa pgsize 0x%zx count %zu\n",
iova, &paddr, pgsize, count);
ret = ops->map_pages(domain, iova, paddr, pgsize, count, prot,
gfp, &mapped);
gfp, &op_mapped);
/*
* Some pages may have been mapped, even if an error occurred,
* so we should account for those so they can be unmapped.
*/
size -= mapped;
*mapped += op_mapped;
if (ret)
break;
iova += mapped;
paddr += mapped;
}
/* unroll mapping in case something went wrong */
if (ret) {
iommu_unmap(domain, orig_iova, orig_size - size);
} else {
trace_map(orig_iova, orig_paddr, orig_size);
iommu_debug_map(domain, orig_paddr, orig_size);
}
return ret;
size -= op_mapped;
iova += op_mapped;
paddr += op_mapped;
}
return 0;
}
int iommu_sync_map(struct iommu_domain *domain, unsigned long iova, size_t size)
@@ -2694,6 +2675,38 @@ int iommu_sync_map(struct iommu_domain *domain, unsigned long iova, size_t size)
return ops->iotlb_sync_map(domain, iova, size);
}
int iommu_map_nosync(struct iommu_domain *domain, unsigned long iova,
phys_addr_t paddr, size_t size, int prot, gfp_t gfp)
{
struct pt_iommu *pt = iommupt_from_domain(domain);
size_t mapped = 0;
int ret;
might_sleep_if(gfpflags_allow_blocking(gfp));
/* Discourage passing strange GFP flags or illegal domains */
if (WARN_ON_ONCE(!(domain->type & __IOMMU_DOMAIN_PAGING) ||
!domain->pgsize_bitmap ||
(gfp & (__GFP_COMP | __GFP_DMA | __GFP_DMA32 |
__GFP_HIGHMEM))))
return -EINVAL;
if (pt)
ret = pt->ops->map_range(pt, iova, paddr, size, prot, gfp,
&mapped);
else
ret = __iommu_map_domain_pgtbl(domain, iova, paddr, size, prot,
gfp, &mapped);
trace_map(iova, paddr, mapped);
iommu_debug_map(domain, paddr, mapped);
if (ret) {
iommu_unmap(domain, iova, mapped);
return ret;
}
return 0;
}
int iommu_map(struct iommu_domain *domain, unsigned long iova,
phys_addr_t paddr, size_t size, int prot, gfp_t gfp)
{
@@ -2711,19 +2724,15 @@ int iommu_map(struct iommu_domain *domain, unsigned long iova,
}
EXPORT_SYMBOL_GPL(iommu_map);
static size_t __iommu_unmap(struct iommu_domain *domain,
unsigned long iova, size_t size,
struct iommu_iotlb_gather *iotlb_gather)
static size_t
__iommu_unmap_domain_pgtbl(struct iommu_domain *domain, unsigned long iova,
size_t size, struct iommu_iotlb_gather *iotlb_gather)
{
const struct iommu_domain_ops *ops = domain->ops;
size_t unmapped_page, unmapped = 0;
unsigned long orig_iova = iova;
unsigned int min_pagesz;
if (unlikely(!(domain->type & __IOMMU_DOMAIN_PAGING)))
return 0;
if (WARN_ON(!ops->unmap_pages || domain->pgsize_bitmap == 0UL))
if (WARN_ON(!ops->unmap_pages))
return 0;
/* find out the minimum page size supported */
@@ -2742,8 +2751,6 @@ static size_t __iommu_unmap(struct iommu_domain *domain,
pr_debug("unmap this: iova 0x%lx size 0x%zx\n", iova, size);
iommu_debug_unmap_begin(domain, iova, size);
/*
* Keep iterating until we either unmap 'size' bytes (or more)
* or we hit an area that isn't mapped.
@@ -2758,13 +2765,40 @@ static size_t __iommu_unmap(struct iommu_domain *domain,
pr_debug("unmapped: iova 0x%lx size 0x%zx\n",
iova, unmapped_page);
/*
* If the driver itself isn't using the gather, make sure
* it looks non-empty so iotlb_sync will still be called.
*/
if (iotlb_gather->start >= iotlb_gather->end)
iommu_iotlb_gather_add_range(iotlb_gather, iova, size);
iova += unmapped_page;
unmapped += unmapped_page;
}
trace_unmap(orig_iova, size, unmapped);
iommu_debug_unmap_end(domain, orig_iova, size, unmapped);
return unmapped;
}
static size_t __iommu_unmap(struct iommu_domain *domain, unsigned long iova,
size_t size,
struct iommu_iotlb_gather *iotlb_gather)
{
struct pt_iommu *pt = iommupt_from_domain(domain);
size_t unmapped;
if (WARN_ON_ONCE(!(domain->type & __IOMMU_DOMAIN_PAGING) ||
!domain->pgsize_bitmap))
return 0;
iommu_debug_unmap_begin(domain, iova, size);
if (pt)
unmapped = pt->ops->unmap_range(pt, iova, size, iotlb_gather);
else
unmapped = __iommu_unmap_domain_pgtbl(domain, iova, size,
iotlb_gather);
trace_unmap(iova, size, unmapped);
iommu_debug_unmap_end(domain, iova, size, unmapped);
return unmapped;
}
+7 -4
View File
@@ -149,15 +149,18 @@ int iommufd_viommu_report_event(struct iommufd_viommu *viommu,
goto out_unlock_veventqs;
}
/* Pre-allocate to avoid GFP_ATOMIC; use GFP_NOWAIT to avoid sleeping */
vevent = kzalloc(struct_size(vevent, event_data, data_len), GFP_NOWAIT);
if (!vevent) {
spin_lock(&veventq->common.lock);
if (veventq->num_events == veventq->depth) {
vevent = &veventq->lost_events_header;
rc = -ENOMEM;
goto out_set_header;
}
vevent = kzalloc(struct_size(vevent, event_data, data_len), GFP_ATOMIC);
if (!vevent) {
rc = -ENOMEM;
spin_lock(&veventq->common.lock);
if (veventq->num_events == veventq->depth) {
kfree(vevent);
vevent = &veventq->lost_events_header;
goto out_set_header;
}
+34 -7
View File
@@ -139,9 +139,14 @@ static ssize_t iommufd_fault_fops_read(struct file *filep, char __user *buf,
mutex_lock(&fault->mutex);
while ((group = iommufd_fault_deliver_fetch(fault))) {
size_t group_done = done;
if (done >= count ||
group->fault_count * fault_size > count - done) {
iommufd_fault_deliver_restore(fault, group);
/* Read count doesn't fit the first fault group */
if (done == 0)
rc = -EINVAL;
break;
}
@@ -157,14 +162,17 @@ static ssize_t iommufd_fault_fops_read(struct file *filep, char __user *buf,
iommufd_compose_fault_message(&iopf->fault,
&data, idev,
group->cookie);
if (copy_to_user(buf + done, &data, fault_size)) {
if (copy_to_user(buf + group_done, &data, fault_size)) {
xa_erase(&fault->response, group->cookie);
iommufd_fault_deliver_restore(fault, group);
rc = -EFAULT;
break;
}
done += fault_size;
group_done += fault_size;
}
if (rc)
break;
done = group_done;
}
mutex_unlock(&fault->mutex);
@@ -187,9 +195,10 @@ static ssize_t iommufd_fault_fops_write(struct file *filep, const char __user *b
mutex_lock(&fault->mutex);
while (count > done) {
rc = copy_from_user(&response, buf + done, response_size);
if (rc)
if (copy_from_user(&response, buf + done, response_size)) {
rc = -EFAULT;
break;
}
static_assert((int)IOMMUFD_PAGE_RESP_SUCCESS ==
(int)IOMMU_PAGE_RESP_SUCCESS);
@@ -263,9 +272,11 @@ iommufd_veventq_deliver_fetch(struct iommufd_veventq *veventq)
/* Make a copy of the lost_events_header for copy_to_user */
if (next == &veventq->lost_events_header) {
vevent = kzalloc(sizeof(*vevent), GFP_ATOMIC);
if (!vevent)
if (!vevent) {
vevent = ERR_PTR(-ENOMEM);
goto out_unlock;
}
}
list_del(&next->node);
if (vevent)
memcpy(vevent, next, sizeof(*vevent));
@@ -309,8 +320,17 @@ static ssize_t iommufd_veventq_fops_read(struct file *filep, char __user *buf,
if (*ppos)
return -ESPIPE;
/* Minimum read count is a vEVENT header */
if (count < sizeof(*hdr))
return -EINVAL;
while ((cur = iommufd_veventq_deliver_fetch(veventq))) {
if (IS_ERR(cur)) {
if (done == 0)
rc = PTR_ERR(cur);
break;
}
/* Validate the remaining bytes against the header size */
if (done >= count || sizeof(*hdr) > count - done) {
iommufd_veventq_deliver_restore(veventq, cur);
@@ -320,8 +340,11 @@ static ssize_t iommufd_veventq_fops_read(struct file *filep, char __user *buf,
/* If being a normal vEVENT, validate against the full size */
if (!vevent_for_lost_events_header(cur) &&
sizeof(hdr) + cur->data_len > count - done) {
sizeof(*hdr) + cur->data_len > count - done) {
iommufd_veventq_deliver_restore(veventq, cur);
/* Read count doesn't fit a single normal vEVENT */
if (done == 0)
rc = -EINVAL;
break;
}
@@ -335,6 +358,7 @@ static ssize_t iommufd_veventq_fops_read(struct file *filep, char __user *buf,
if (cur->data_len &&
copy_to_user(buf + done, cur->event_data, cur->data_len)) {
iommufd_veventq_deliver_restore(veventq, cur);
done -= sizeof(*hdr);
rc = -EFAULT;
break;
}
@@ -472,6 +496,9 @@ int iommufd_fault_iopf_handler(struct iopf_group *group)
static const struct file_operations iommufd_veventq_fops =
INIT_EVENTQ_FOPS(iommufd_veventq_fops_read, NULL);
/* An arbitrary upper bound for veventq_depth that fits all existing HWs */
#define VEVENTQ_MAX_DEPTH (1U << 19)
int iommufd_veventq_alloc(struct iommufd_ucmd *ucmd)
{
struct iommu_veventq_alloc *cmd = ucmd->cmd;
@@ -483,7 +510,7 @@ int iommufd_veventq_alloc(struct iommufd_ucmd *ucmd)
if (cmd->flags || cmd->__reserved ||
cmd->type == IOMMU_VEVENTQ_TYPE_DEFAULT)
return -EOPNOTSUPP;
if (!cmd->veventq_depth)
if (!cmd->veventq_depth || cmd->veventq_depth > VEVENTQ_MAX_DEPTH)
return -EINVAL;
viommu = iommufd_get_viommu(ucmd, cmd->viommu_id);
+10 -1
View File
@@ -489,6 +489,9 @@ int iommufd_hwpt_get_dirty_bitmap(struct iommufd_ucmd *ucmd)
return rc;
}
/* An arbitrary entry_num cap, far above any realistic invalidation batch */
#define IOMMU_HWPT_INVALIDATE_ENTRY_NUM_MAX (1U << 19)
int iommufd_hwpt_invalidate(struct iommufd_ucmd *ucmd)
{
struct iommu_hwpt_invalidate *cmd = ucmd->cmd;
@@ -507,7 +510,13 @@ int iommufd_hwpt_invalidate(struct iommufd_ucmd *ucmd)
goto out;
}
if (cmd->entry_num && (!cmd->data_uptr || !cmd->entry_len)) {
/*
* Bound entry_num and entry_len so a single call cannot pin the CPU;
* entry_len also caps the copy_struct_from_user() trailing-zero scan.
*/
if (cmd->entry_num &&
(!cmd->data_uptr || !cmd->entry_len || cmd->entry_len > PAGE_SIZE ||
cmd->entry_num > IOMMU_HWPT_INVALIDATE_ENTRY_NUM_MAX)) {
rc = -EINVAL;
goto out;
}
+10
View File
@@ -814,6 +814,16 @@ again:
unmapped_bytes += area_last - area_first + 1;
down_write(&iopt->iova_rwsem);
/*
* After releasing the iova_rwsem concurrent allocation could
* place new areas at IOVAs we have already unmapped. Keep
* moving the start of the search forward to ignore the area
* already unmapped.
*/
if (area_last >= last)
break;
start = area_last + 1;
}
out_unlock_iova:
+1 -1
View File
@@ -602,7 +602,7 @@ struct iommufd_vevent {
struct iommufd_vevent_header header;
struct list_head node; /* for iommufd_eventq::deliver */
ssize_t data_len;
u64 event_data[] __counted_by(data_len);
u8 event_data[] __counted_by(data_len);
};
#define vevent_for_lost_events_header(vevent) \
+4 -4
View File
@@ -1650,10 +1650,6 @@ void iopt_release_pages(struct kref *kref)
WARN_ON(!RB_EMPTY_ROOT(&pages->domains_itree.rb_root));
WARN_ON(pages->npinned);
WARN_ON(!xa_empty(&pages->pinned_pfns));
mmdrop(pages->source_mm);
mutex_destroy(&pages->mutex);
put_task_struct(pages->source_task);
free_uid(pages->source_user);
if (iopt_is_dmabuf(pages) && pages->dmabuf.attach) {
struct dma_buf *dmabuf = pages->dmabuf.attach->dmabuf;
@@ -1663,6 +1659,10 @@ void iopt_release_pages(struct kref *kref)
} else if (pages->type == IOPT_ADDRESS_FILE) {
fput(pages->file);
}
mmdrop(pages->source_mm);
mutex_destroy(&pages->mutex);
put_task_struct(pages->source_task);
free_uid(pages->source_user);
kfree(pages);
}
+2 -2
View File
@@ -605,7 +605,7 @@ static void mock_viommu_destroy(struct iommufd_viommu *viommu)
if (mock_viommu->mmap_offset)
iommufd_viommu_destroy_mmap(&mock_viommu->core,
mock_viommu->mmap_offset);
free_page((unsigned long)mock_viommu->page);
free_pages((unsigned long)mock_viommu->page, 1);
mutex_destroy(&mock_viommu->queue_mutex);
/* iommufd core frees mock_viommu and viommu */
@@ -839,7 +839,7 @@ err_destroy_mmap:
iommufd_viommu_destroy_mmap(&mock_viommu->core,
mock_viommu->mmap_offset);
err_free_page:
free_page((unsigned long)mock_viommu->page);
free_pages((unsigned long)mock_viommu->page, 1);
return rc;
}
+1 -1
View File
@@ -283,7 +283,7 @@ static int iommufd_vfio_check_extension(struct iommufd_ctx *ictx,
case VFIO_TYPE1_IOMMU:
case VFIO_TYPE1v2_IOMMU:
case VFIO_UNMAP_ALL:
return 1;
return !ictx->no_iommu_mode;
case VFIO_NOIOMMU_IOMMU:
return IS_ENABLED(CONFIG_VFIO_NOIOMMU);
+1
View File
@@ -611,6 +611,7 @@ static struct iova_magazine *iova_magazine_alloc(gfp_t flags)
static void iova_magazine_free(struct iova_magazine *mag)
{
if (mag)
kmem_cache_free(iova_magazine_cache, mag);
}
+17 -2
View File
@@ -158,7 +158,7 @@ static int __init __reserved_mem_reserve_reg(unsigned long node,
phys_addr_t base, size;
int len;
const __be32 *prop;
bool nomap;
bool nomap, default_cma;
prop = of_get_flat_dt_prop(node, "reg", &len);
if (!prop)
@@ -171,6 +171,12 @@ static int __init __reserved_mem_reserve_reg(unsigned long node,
}
nomap = of_get_flat_dt_prop(node, "no-map", NULL) != NULL;
default_cma = of_get_flat_dt_prop(node, "linux,cma-default", NULL);
if (default_cma && cma_skip_dt_default_reserved_mem()) {
pr_err("Skipping dt linux,cma-default for \"cma=\" kernel param.\n");
return -EINVAL;
}
while (len >= t_len) {
base = dt_mem_next_cell(dt_root_addr_cells, &prop);
@@ -256,12 +262,15 @@ void __init fdt_scan_reserved_mem_reg_nodes(void)
fdt_for_each_subnode(child, fdt, node) {
const char *uname;
bool default_cma = of_get_flat_dt_prop(child, "linux,cma-default", NULL);
prop = of_get_flat_dt_prop(child, "reg", &len);
if (!prop)
continue;
if (!of_fdt_device_is_available(fdt, child))
continue;
if (default_cma && cma_skip_dt_default_reserved_mem())
continue;
uname = fdt_get_name(fdt, child, NULL);
if (len && len % t_len != 0) {
@@ -406,7 +415,7 @@ static int __init __reserved_mem_alloc_size(unsigned long node, const char *unam
phys_addr_t base = 0, align = 0, size;
int len;
const __be32 *prop;
bool nomap;
bool nomap, default_cma;
int ret;
prop = of_get_flat_dt_prop(node, "size", &len);
@@ -430,6 +439,12 @@ static int __init __reserved_mem_alloc_size(unsigned long node, const char *unam
}
nomap = of_get_flat_dt_prop(node, "no-map", NULL) != NULL;
default_cma = of_get_flat_dt_prop(node, "linux,cma-default", NULL);
if (default_cma && cma_skip_dt_default_reserved_mem()) {
pr_err("Skipping dt linux,cma-default for \"cma=\" kernel param.\n");
return -EINVAL;
}
/* Need adjust the alignment to satisfy the CMA requirement */
if (IS_ENABLED(CONFIG_CMA)
+9
View File
@@ -57,6 +57,15 @@ extern bool cma_intersects(struct cma *cma, unsigned long start, unsigned long e
extern void cma_reserve_pages_on_error(struct cma *cma);
#ifdef CONFIG_DMA_CMA
extern bool cma_skip_dt_default_reserved_mem(void);
#else
static inline bool cma_skip_dt_default_reserved_mem(void)
{
return false;
}
#endif
#ifdef CONFIG_CMA
struct folio *cma_alloc_folio(struct cma *cma, int order, gfp_t gfp);
bool cma_free_folio(struct cma *cma, const struct folio *folio);
+2 -2
View File
@@ -248,8 +248,8 @@ static inline void *dma_alloc_attrs(struct device *dev, size_t size,
{
return NULL;
}
static void dma_free_attrs(struct device *dev, size_t size, void *cpu_addr,
dma_addr_t dma_handle, unsigned long attrs)
static inline void dma_free_attrs(struct device *dev, size_t size,
void *cpu_addr, dma_addr_t dma_handle, unsigned long attrs)
{
}
static inline void *dmam_alloc_attrs(struct device *dev, size_t size,
+58 -11
View File
@@ -66,6 +66,13 @@ struct pt_iommu {
struct device *iommu_device;
};
static inline struct pt_iommu *iommupt_from_domain(struct iommu_domain *domain)
{
if (!IS_ENABLED(CONFIG_IOMMU_PT) || !domain->is_iommupt)
return NULL;
return container_of(domain, struct pt_iommu, domain);
}
/**
* struct pt_iommu_info - Details about the IOMMU page table
*
@@ -80,6 +87,56 @@ struct pt_iommu_info {
};
struct pt_iommu_ops {
/**
* @map_range: Install translation for an IOVA range
* @iommu_table: Table to manipulate
* @iova: IO virtual address to start
* @paddr: Physical/Output address to start
* @len: Length of the range starting from @iova
* @prot: A bitmap of IOMMU_READ/WRITE/CACHE/NOEXEC/MMIO
* @gfp: GFP flags for any memory allocations
*
* The range starting at IOVA will have paddr installed into it. The
* rage is automatically segmented into optimally sized table entries,
* and can have any valid alignment.
*
* On error the caller will probably want to invoke unmap on the range
* from iova up to the amount indicated by @mapped to return the table
* back to an unchanged state.
*
* Context: The caller must hold a write range lock that includes
* the whole range.
*
* Returns: -ERRNO on failure, 0 on success. The number of bytes of VA
* that were mapped are added to @mapped, @mapped is not zerod first.
*/
int (*map_range)(struct pt_iommu *iommu_table, dma_addr_t iova,
phys_addr_t paddr, dma_addr_t len, unsigned int prot,
gfp_t gfp, size_t *mapped);
/**
* @unmap_range: Make a range of IOVA empty/not present
* @iommu_table: Table to manipulate
* @iova: IO virtual address to start
* @len: Length of the range starting from @iova
* @iotlb_gather: Gather struct that must be flushed on return
*
* unmap_range() will remove a translation created by map_range(). It
* cannot subdivide a mapping created by map_range(), so it should be
* called with IOVA ranges that match those passed to map_pages. The
* IOVA range can aggregate contiguous map_range() calls so long as no
* individual range is split.
*
* Context: The caller must hold a write range lock that includes
* the whole range.
*
* Returns: Number of bytes of VA unmapped. iova + res will be the
* point unmapping stopped.
*/
size_t (*unmap_range)(struct pt_iommu *iommu_table, dma_addr_t iova,
dma_addr_t len,
struct iommu_iotlb_gather *iotlb_gather);
/**
* @set_dirty: Make the iova write dirty
* @iommu_table: Table to manipulate
@@ -194,14 +251,6 @@ struct pt_iommu_cfg {
#define IOMMU_PROTOTYPES(fmt) \
phys_addr_t pt_iommu_##fmt##_iova_to_phys(struct iommu_domain *domain, \
dma_addr_t iova); \
int pt_iommu_##fmt##_map_pages(struct iommu_domain *domain, \
unsigned long iova, phys_addr_t paddr, \
size_t pgsize, size_t pgcount, \
int prot, gfp_t gfp, size_t *mapped); \
size_t pt_iommu_##fmt##_unmap_pages( \
struct iommu_domain *domain, unsigned long iova, \
size_t pgsize, size_t pgcount, \
struct iommu_iotlb_gather *iotlb_gather); \
int pt_iommu_##fmt##_read_and_clear_dirty( \
struct iommu_domain *domain, unsigned long iova, size_t size, \
unsigned long flags, struct iommu_dirty_bitmap *dirty); \
@@ -222,9 +271,7 @@ struct pt_iommu_cfg {
* iommu_pt
*/
#define IOMMU_PT_DOMAIN_OPS(fmt) \
.iova_to_phys = &pt_iommu_##fmt##_iova_to_phys, \
.map_pages = &pt_iommu_##fmt##_map_pages, \
.unmap_pages = &pt_iommu_##fmt##_unmap_pages
.iova_to_phys = &pt_iommu_##fmt##_iova_to_phys
#define IOMMU_PT_DIRTY_OPS(fmt) \
.read_and_clear_dirty = &pt_iommu_##fmt##_read_and_clear_dirty
+4 -1
View File
@@ -223,6 +223,7 @@ enum iommu_domain_cookie_type {
struct iommu_domain {
unsigned type;
enum iommu_domain_cookie_type cookie_type;
bool is_iommupt;
const struct iommu_domain_ops *ops;
const struct iommu_dirty_ops *dirty_ops;
const struct iommu_ops *owner; /* Whose domain_alloc we came from */
@@ -546,6 +547,7 @@ iommu_copy_struct_from_full_user_array(void *kdst, size_t kdst_entry_size,
user_array->entry_num *
user_array->entry_len))
return -EFAULT;
return 0;
}
/* Copy item by item */
@@ -982,7 +984,8 @@ static inline void iommu_flush_iotlb_all(struct iommu_domain *domain)
static inline void iommu_iotlb_sync(struct iommu_domain *domain,
struct iommu_iotlb_gather *iotlb_gather)
{
if (domain->ops->iotlb_sync)
if (domain->ops->iotlb_sync &&
likely(iotlb_gather->start < iotlb_gather->end))
domain->ops->iotlb_sync(domain, iotlb_gather);
iommu_iotlb_gather_init(iotlb_gather);
+1 -1
View File
@@ -335,7 +335,7 @@ static inline void sg_dma_mark_bus_address(struct scatterlist *sg)
}
/**
* sg_unmark_bus_address - Unmark the scatterlist entry as a bus address
* sg_dma_unmark_bus_address - Unmark the scatterlist entry as a bus address
* @sg: SG entry
*
* Description:
+8 -4
View File
@@ -224,13 +224,17 @@ struct iommu_ioas_map {
* @size: sizeof(struct iommu_ioas_map_file)
* @flags: same as for iommu_ioas_map
* @ioas_id: same as for iommu_ioas_map
* @fd: the memfd to map
* @start: byte offset from start of file to map from
* @fd: the memfd or supported dma-buf file to map
* @start: byte offset from start of the file to map from
* @length: same as for iommu_ioas_map
* @iova: same as for iommu_ioas_map
*
* Set an IOVA mapping from a memfd file. All other arguments and semantics
* match those of IOMMU_IOAS_MAP.
* Set an IOVA mapping from a memfd file. On kernels with dma-buf support,
* supported dma-buf files may also be accepted. This is not a generic
* dma-buf import path; currently supported dma-bufs include single-range
* VFIO PCI dma-bufs exported through VFIO_DEVICE_FEATURE_DMA_BUF, and
* other dma-bufs may be rejected. All other arguments and semantics match
* those of IOMMU_IOAS_MAP.
*/
struct iommu_ioas_map_file {
__u32 size;
+1
View File
@@ -29,6 +29,7 @@ struct map_benchmark {
__u32 dma_dir; /* DMA data direction */
__u32 dma_trans_ns; /* time for DMA transmission in ns */
__u32 granule; /* how many PAGE_SIZE will do map/unmap once a time */
__u8 expansion[76]; /* For future use */
};
#endif /* _UAPI_DMA_BENCHMARK_H */
+10 -6
View File
@@ -91,6 +91,16 @@ static int __init early_cma(char *p)
}
early_param("cma", early_cma);
/*
* cma_skip_dt_default_reserved_mem - This is called from the
* reserved_mem framework to detect if the default cma region is being
* set by the "cma=" kernel parameter.
*/
bool __init cma_skip_dt_default_reserved_mem(void)
{
return size_cmdline != -1;
}
#ifdef CONFIG_DMA_NUMA_CMA
static struct cma *dma_contiguous_numa_area[MAX_NUMNODES];
@@ -477,12 +487,6 @@ static int __init rmem_cma_setup(struct reserved_mem *rmem)
struct cma *cma;
int err;
if (size_cmdline != -1 && default_cma) {
pr_info("Reserved memory: bypass %s node, using cmdline CMA params instead\n",
rmem->name);
return -EBUSY;
}
if (!of_get_flat_dt_prop(node, "reusable", NULL) ||
of_get_flat_dt_prop(node, "no-map", NULL))
return -EINVAL;
+9 -2
View File
@@ -1250,7 +1250,14 @@ void debug_dma_map_phys(struct device *dev, phys_addr_t phys, size_t size,
entry->direction = direction;
entry->map_err_type = MAP_ERR_NOT_CHECKED;
if (!(attrs & DMA_ATTR_MMIO)) {
if (attrs & DMA_ATTR_MMIO) {
unsigned long pfn = PHYS_PFN(phys);
if (pfn_valid(pfn) && !PageReserved(pfn_to_page(pfn)))
err_printk(dev, entry,
"dma_map_resource called for RAM address %pa\n",
&phys);
} else {
check_for_stack(dev, phys);
if (!PhysHighMem(phys))
@@ -1548,7 +1555,7 @@ void debug_dma_sync_sg_for_device(struct device *dev, struct scatterlist *sg,
struct dma_debug_entry ref = {
.type = dma_debug_sg,
.dev = dev,
.paddr = sg_phys(sg),
.paddr = sg_phys(s),
.dev_addr = sg_dma_address(s),
.size = sg_dma_len(s),
.direction = direction,
+1 -1
View File
@@ -466,7 +466,7 @@ int dma_direct_map_sg(struct device *dev, struct scatterlist *sgl, int nents,
* must be mapped with CPU physical address and not PCI
* bus addresses.
*/
break;
fallthrough;
case PCI_P2PDMA_MAP_NONE:
sg->dma_address = dma_direct_map_phys(dev, sg_phys(sg),
sg->length, dir, attrs);
-4
View File
@@ -362,10 +362,6 @@ EXPORT_SYMBOL(dma_unmap_sg_attrs);
dma_addr_t dma_map_resource(struct device *dev, phys_addr_t phys_addr,
size_t size, enum dma_data_direction dir, unsigned long attrs)
{
if (IS_ENABLED(CONFIG_DMA_API_DEBUG) &&
WARN_ON_ONCE(pfn_valid(PHYS_PFN(phys_addr))))
return DMA_MAPPING_ERROR;
return dma_map_phys(dev, phys_addr, size, dir, attrs | DMA_ATTR_MMIO);
}
EXPORT_SYMBOL(dma_map_resource);
-2
View File
@@ -88,8 +88,6 @@ static void sg_split_phys(struct sg_splitter *splitters, const int nb_splits)
if (!j) {
out_sg->offset += split->skip_sg0;
out_sg->length -= split->skip_sg0;
} else {
out_sg->offset = 0;
}
sg_dma_address(out_sg) = 0;
sg_dma_len(out_sg) = 0;
+49 -2
View File
@@ -556,6 +556,21 @@ TEST_F(iommufd_ioas, alloc_hwpt_nested)
1, &num_inv);
assert(!num_inv);
/* Negative test: entry_len is bounded by PAGE_SIZE */
num_inv = 1;
test_err_hwpt_invalidate(EINVAL, nested_hwpt_id[0], inv_reqs,
IOMMU_HWPT_INVALIDATE_DATA_SELFTEST,
PAGE_SIZE + 1, &num_inv);
assert(!num_inv);
/* Negative test: entry_num is bounded */
#define IOMMU_HWPT_INVALIDATE_ENTRY_NUM_MAX (1U << 19)
num_inv = IOMMU_HWPT_INVALIDATE_ENTRY_NUM_MAX + 1;
test_err_hwpt_invalidate(EINVAL, nested_hwpt_id[0], inv_reqs,
IOMMU_HWPT_INVALIDATE_DATA_SELFTEST,
sizeof(*inv_reqs), &num_inv);
assert(!num_inv);
/* Negative test: invalid flag is passed */
num_inv = 1;
inv_reqs[0].flags = 0xffffffff;
@@ -2953,22 +2968,54 @@ TEST_F(iommufd_viommu, vdevice_alloc)
uint32_t veventq_id;
uint32_t veventq_fd;
int prev_seq = -1;
size_t hdr_size = sizeof(struct iommufd_vevent_header);
char vbuf[64];
if (dev_id) {
/* Must allocate vdevice before attaching to a nested hwpt */
test_err_mock_domain_replace(ENOENT, self->stdev_id,
self->nested_hwpt_id);
/* Test depth lower and upper bounds (mirrors kernel cap) */
#define VEVENTQ_MAX_DEPTH (1U << 19)
test_err_veventq_alloc(EINVAL, viommu_id,
IOMMU_VEVENTQ_TYPE_SELFTEST, 0, NULL,
NULL);
test_err_veventq_alloc(EINVAL, viommu_id,
IOMMU_VEVENTQ_TYPE_SELFTEST,
VEVENTQ_MAX_DEPTH + 1, NULL, NULL);
test_cmd_veventq_alloc(viommu_id, IOMMU_VEVENTQ_TYPE_SELFTEST,
VEVENTQ_MAX_DEPTH, &veventq_id,
&veventq_fd);
close(veventq_fd);
test_ioctl_destroy(veventq_id);
/* Allocate a vEVENTQ with veventq_depth=2 */
test_cmd_veventq_alloc(viommu_id, IOMMU_VEVENTQ_TYPE_SELFTEST,
&veventq_id, &veventq_fd);
2, &veventq_id, &veventq_fd);
test_err_veventq_alloc(EEXIST, viommu_id,
IOMMU_VEVENTQ_TYPE_SELFTEST, NULL, NULL);
IOMMU_VEVENTQ_TYPE_SELFTEST, 2, NULL,
NULL);
/* Invalid read counts on an empty vEVENTQ */
ASSERT_EQ(-1, read(veventq_fd, vbuf, 0));
ASSERT_EQ(EINVAL, errno);
ASSERT_EQ(-1, read(veventq_fd, vbuf, hdr_size - 1));
ASSERT_EQ(EINVAL, errno);
/* Set vdev_id to 0x99, unset it, and set to 0x88 */
test_cmd_vdevice_alloc(viommu_id, dev_id, 0x99, &vdev_id);
test_cmd_mock_domain_replace(self->stdev_id,
self->nested_hwpt_id);
test_cmd_trigger_vevents(dev_id, 1);
/* Invalid read counts on a non-empty vEVENTQ */
ASSERT_EQ(-1, read(veventq_fd, vbuf, 0));
ASSERT_EQ(EINVAL, errno);
/* header fits but the event's payload doesn't */
ASSERT_EQ(-1, read(veventq_fd, vbuf, hdr_size));
ASSERT_EQ(EINVAL, errno);
test_cmd_read_vevents(veventq_fd, 1, 0x99, &prev_seq);
test_err_vdevice_alloc(EEXIST, viommu_id, dev_id, 0x99,
&vdev_id);
@@ -712,7 +712,7 @@ TEST_FAIL_NTH(basic_fail_nth, device)
return -1;
if (_test_cmd_veventq_alloc(self->fd, viommu_id,
IOMMU_VEVENTQ_TYPE_SELFTEST, &veventq_id,
IOMMU_VEVENTQ_TYPE_SELFTEST, 2, &veventq_id,
&veventq_fd))
return -1;
close(veventq_fd);
@@ -1060,12 +1060,13 @@ static int _test_cmd_hw_queue_alloc(int fd, __u32 viommu_id, __u32 type,
base_addr, len, out_qid))
static int _test_cmd_veventq_alloc(int fd, __u32 viommu_id, __u32 type,
__u32 *veventq_id, __u32 *veventq_fd)
__u32 depth, __u32 *veventq_id,
__u32 *veventq_fd)
{
struct iommu_veventq_alloc cmd = {
.size = sizeof(cmd),
.type = type,
.veventq_depth = 2,
.veventq_depth = depth,
.viommu_id = viommu_id,
};
int ret;
@@ -1080,13 +1081,13 @@ static int _test_cmd_veventq_alloc(int fd, __u32 viommu_id, __u32 type,
return 0;
}
#define test_cmd_veventq_alloc(viommu_id, type, veventq_id, veventq_fd) \
ASSERT_EQ(0, _test_cmd_veventq_alloc(self->fd, viommu_id, type, \
#define test_cmd_veventq_alloc(viommu_id, type, depth, veventq_id, veventq_fd) \
ASSERT_EQ(0, _test_cmd_veventq_alloc(self->fd, viommu_id, type, depth, \
veventq_id, veventq_fd))
#define test_err_veventq_alloc(_errno, viommu_id, type, veventq_id, \
#define test_err_veventq_alloc(_errno, viommu_id, type, depth, veventq_id, \
veventq_fd) \
EXPECT_ERRNO(_errno, \
_test_cmd_veventq_alloc(self->fd, viommu_id, type, \
_test_cmd_veventq_alloc(self->fd, viommu_id, type, depth, \
veventq_id, veventq_fd))
static int _test_cmd_trigger_vevents(int fd, __u32 dev_id, __u32 nvevents)