mirror of
https://gitlab.com/redhat/centos-stream/src/kernel/centos-stream-10.git
synced 2026-09-09 00:07:04 +08:00
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:
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
@@ -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)
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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) },
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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),
|
||||
|
||||
@@ -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, \
|
||||
|
||||
@@ -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);
|
||||
|
||||
/*
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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) \
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user