mirror of
https://github.com/torvalds/linux.git
synced 2026-07-27 01:32:21 +02:00
IOMMU Fixes for Linux v7.2-rc5
- AMD: * Fix lockdep splat from nested domain allocation * Fix nested domain leak * Fix broken synchronisation of command completion * Fix OOB write in "ivrs_acpihid" command-line parsing - VT-d: * Prevent SVA for IOMMUs with non-coherent page-table walker * Fix OOB write in PMU driver. -----BEGIN PGP SIGNATURE----- iQFEBAABCgAuFiEEPxTL6PPUbjXGY88ct6xw3ITBYzQFAmpjXsoQHHdpbGxAa2Vy bmVsLm9yZwAKCRC3rHDchMFjNGzbB/9xlcRZhOY0aTlqbKglyjyOmqtnJrtNOyUp 6+gTqiUBcWp9fOXQxA/n+Ne0wYgyYJNgmI0K1Upn0u2ffbnbiT9TLNKEl6fJMW0s 3g5VNIsqP06SmPVt18eCuLspEP2+JPHvFw7kQY+IAF5odfyXZ4eZ4duk9PPLI5Ov ggk4+ePMhFn1pYdK+3MOMqZ/lqc1Q3gWWwvuLnV/FrmQ4dwmh3nE328cbn2yYSBl t5e0k+89qBq8ktCO5DbxJQStno757FMmCQ58qcYhpI5oMIbfXl4TYSEsXut88g0e jT//iXgyQywLYvbnoCZ5BF5dNI9IZwYolkgwZYaAbM+MN3sZSSwu =nAFJ -----END PGP SIGNATURE----- Merge tag 'iommu-fixes-v7.2-rc5' of git://git.kernel.org/pub/scm/linux/kernel/git/iommu/linux Pull iommu fixes from Will Deacon: "Joerg's away at the moment so I've been looking after the IOMMU tree in his absence. In the process of doing that, I've hoovered up a handful of fixes for the AMD and Intel drivers which address a combination of the usual out-of-bounds/locking/leak bugs as well as some logical issues around SVA and command completion. AMD: - Fix lockdep splat from nested domain allocation - Fix nested domain leak - Fix broken synchronisation of command completion - Fix OOB write in "ivrs_acpihid" command-line parsing VT-d: - Prevent SVA for IOMMUs with non-coherent page-table walker - Fix OOB write in PMU driver" * tag 'iommu-fixes-v7.2-rc5' of git://git.kernel.org/pub/scm/linux/kernel/git/iommu/linux: iommu/intel: Fix out-of-bounds memset in dmar_latency_disable() iommu/amd: Bound the early ACPI HID map iommu/vt-d: Disallow SVA if page walk is not coherent iommu/amd: Wait for completion instead of returning early in iommu_completion_wait() iommu/amd: Fix nested domain leak iommu/amd: Fix IRQ unsafe locking in gdom allocation
This commit is contained in:
commit
a93212b3d1
|
|
@ -3864,6 +3864,12 @@ static int __init parse_ivrs_acpihid(char *str)
|
|||
return 1;
|
||||
|
||||
found:
|
||||
if (early_acpihid_map_size == EARLY_MAP_SIZE) {
|
||||
pr_err("Early ACPI HID map overflow - ignoring ivrs_acpihid%s\n",
|
||||
str);
|
||||
return 1;
|
||||
}
|
||||
|
||||
p = acpiid;
|
||||
hid = strsep(&p, ":");
|
||||
uid = p;
|
||||
|
|
|
|||
|
|
@ -1450,11 +1450,23 @@ static int iommu_completion_wait(struct amd_iommu *iommu)
|
|||
int ret;
|
||||
u64 data;
|
||||
|
||||
if (!iommu->need_sync)
|
||||
return 0;
|
||||
|
||||
raw_spin_lock_irqsave(&iommu->lock, flags);
|
||||
|
||||
if (!iommu->need_sync) {
|
||||
/*
|
||||
* No command has been queued since the last completion-wait.
|
||||
* A concurrent CPU may have already queued that CWAIT and
|
||||
* cleared need_sync; need_sync == false only means a covering
|
||||
* CWAIT is queued, not that all prior commands have completed.
|
||||
* Wait for the last allocated sequence number so that any
|
||||
* command queued before this call (possibly on another CPU)
|
||||
* is guaranteed to have completed before returning.
|
||||
*/
|
||||
data = iommu->cmd_sem_val;
|
||||
raw_spin_unlock_irqrestore(&iommu->lock, flags);
|
||||
return wait_on_sem(iommu, data);
|
||||
}
|
||||
|
||||
data = get_cmdsem_val(iommu);
|
||||
build_completion_wait(&cmd, iommu, data);
|
||||
|
||||
|
|
@ -1464,9 +1476,7 @@ static int iommu_completion_wait(struct amd_iommu *iommu)
|
|||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = wait_on_sem(iommu, data);
|
||||
|
||||
return ret;
|
||||
return wait_on_sem(iommu, data);
|
||||
}
|
||||
|
||||
static void domain_flush_complete(struct protection_domain *domain)
|
||||
|
|
|
|||
|
|
@ -59,7 +59,9 @@ static int validate_gdte_nested(struct iommu_hwpt_amd_guest *gdte)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static void *gdom_info_load_or_alloc_locked(struct xarray *xa, unsigned long index)
|
||||
static void *gdom_info_load_or_alloc_locked(struct xarray *xa,
|
||||
unsigned long index,
|
||||
unsigned long *flags)
|
||||
{
|
||||
struct guest_domain_mapping_info *elm, *res;
|
||||
|
||||
|
|
@ -67,13 +69,13 @@ static void *gdom_info_load_or_alloc_locked(struct xarray *xa, unsigned long ind
|
|||
if (elm)
|
||||
return elm;
|
||||
|
||||
xa_unlock(xa);
|
||||
xa_unlock_irqrestore(xa, *flags);
|
||||
elm = kzalloc_obj(struct guest_domain_mapping_info);
|
||||
xa_lock(xa);
|
||||
xa_lock_irqsave(xa, *flags);
|
||||
if (!elm)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
res = __xa_cmpxchg(xa, index, NULL, elm, GFP_KERNEL);
|
||||
res = __xa_cmpxchg(xa, index, NULL, elm, GFP_ATOMIC);
|
||||
if (xa_is_err(res))
|
||||
res = ERR_PTR(xa_err(res));
|
||||
|
||||
|
|
@ -95,6 +97,7 @@ amd_iommu_alloc_domain_nested(struct iommufd_viommu *viommu, u32 flags,
|
|||
const struct iommu_user_data *user_data)
|
||||
{
|
||||
int ret;
|
||||
unsigned long irqflags;
|
||||
struct nested_domain *ndom;
|
||||
struct guest_domain_mapping_info *gdom_info;
|
||||
struct amd_iommu_viommu *aviommu = container_of(viommu, struct amd_iommu_viommu, core);
|
||||
|
|
@ -136,11 +139,12 @@ amd_iommu_alloc_domain_nested(struct iommufd_viommu *viommu, u32 flags,
|
|||
* keep track of the gDomID mapping. When the S2 is changed, the INVALIDATE_IOMMU_PAGES
|
||||
* command must be issued for each hDomID in the xarray.
|
||||
*/
|
||||
xa_lock(&aviommu->gdomid_array);
|
||||
xa_lock_irqsave(&aviommu->gdomid_array, irqflags);
|
||||
|
||||
gdom_info = gdom_info_load_or_alloc_locked(&aviommu->gdomid_array, ndom->gdom_id);
|
||||
gdom_info = gdom_info_load_or_alloc_locked(&aviommu->gdomid_array,
|
||||
ndom->gdom_id, &irqflags);
|
||||
if (IS_ERR(gdom_info)) {
|
||||
xa_unlock(&aviommu->gdomid_array);
|
||||
xa_unlock_irqrestore(&aviommu->gdomid_array, irqflags);
|
||||
ret = PTR_ERR(gdom_info);
|
||||
goto out_err;
|
||||
}
|
||||
|
|
@ -148,7 +152,7 @@ amd_iommu_alloc_domain_nested(struct iommufd_viommu *viommu, u32 flags,
|
|||
/* Check if gDomID exist */
|
||||
if (refcount_inc_not_zero(&gdom_info->users)) {
|
||||
ndom->gdom_info = gdom_info;
|
||||
xa_unlock(&aviommu->gdomid_array);
|
||||
xa_unlock_irqrestore(&aviommu->gdomid_array, irqflags);
|
||||
|
||||
pr_debug("%s: Found gdom_id=%#x, hdom_id=%#x\n",
|
||||
__func__, ndom->gdom_id, gdom_info->hdom_id);
|
||||
|
|
@ -161,7 +165,7 @@ amd_iommu_alloc_domain_nested(struct iommufd_viommu *viommu, u32 flags,
|
|||
if (gdom_info->hdom_id <= 0) {
|
||||
__xa_cmpxchg(&aviommu->gdomid_array,
|
||||
ndom->gdom_id, gdom_info, NULL, GFP_ATOMIC);
|
||||
xa_unlock(&aviommu->gdomid_array);
|
||||
xa_unlock_irqrestore(&aviommu->gdomid_array, irqflags);
|
||||
ret = -ENOSPC;
|
||||
goto out_err_gdom_info;
|
||||
}
|
||||
|
|
@ -169,7 +173,7 @@ amd_iommu_alloc_domain_nested(struct iommufd_viommu *viommu, u32 flags,
|
|||
ndom->gdom_info = gdom_info;
|
||||
refcount_set(&gdom_info->users, 1);
|
||||
|
||||
xa_unlock(&aviommu->gdomid_array);
|
||||
xa_unlock_irqrestore(&aviommu->gdomid_array, irqflags);
|
||||
|
||||
pr_debug("%s: Allocate gdom_id=%#x, hdom_id=%#x\n",
|
||||
__func__, ndom->gdom_id, gdom_info->hdom_id);
|
||||
|
|
@ -257,14 +261,15 @@ static int nested_attach_device(struct iommu_domain *dom, struct device *dev,
|
|||
|
||||
static void nested_domain_free(struct iommu_domain *dom)
|
||||
{
|
||||
unsigned long irqflags;
|
||||
struct guest_domain_mapping_info *curr;
|
||||
struct nested_domain *ndom = to_ndomain(dom);
|
||||
struct nested_domain *ndom __free(kfree) = to_ndomain(dom);
|
||||
struct amd_iommu_viommu *aviommu = ndom->viommu;
|
||||
|
||||
xa_lock(&aviommu->gdomid_array);
|
||||
xa_lock_irqsave(&aviommu->gdomid_array, irqflags);
|
||||
|
||||
if (!refcount_dec_and_test(&ndom->gdom_info->users)) {
|
||||
xa_unlock(&aviommu->gdomid_array);
|
||||
xa_unlock_irqrestore(&aviommu->gdomid_array, irqflags);
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -275,7 +280,7 @@ static void nested_domain_free(struct iommu_domain *dom)
|
|||
curr = __xa_cmpxchg(&aviommu->gdomid_array, ndom->gdom_id,
|
||||
ndom->gdom_info, NULL, GFP_ATOMIC);
|
||||
|
||||
xa_unlock(&aviommu->gdomid_array);
|
||||
xa_unlock_irqrestore(&aviommu->gdomid_array, irqflags);
|
||||
if (WARN_ON(!curr || xa_err(curr)))
|
||||
return;
|
||||
|
||||
|
|
@ -285,7 +290,6 @@ static void nested_domain_free(struct iommu_domain *dom)
|
|||
|
||||
amd_iommu_pdom_id_free(ndom->gdom_info->hdom_id);
|
||||
kfree(curr);
|
||||
kfree(ndom);
|
||||
}
|
||||
|
||||
static const struct iommu_domain_ops nested_domain_ops = {
|
||||
|
|
|
|||
|
|
@ -63,7 +63,7 @@ void dmar_latency_disable(struct intel_iommu *iommu, enum latency_type type)
|
|||
return;
|
||||
|
||||
spin_lock_irqsave(&latency_lock, flags);
|
||||
memset(&lstat[type], 0, sizeof(*lstat) * DMAR_LATENCY_NUM);
|
||||
memset(&lstat[type], 0, sizeof(*lstat));
|
||||
spin_unlock_irqrestore(&latency_lock, flags);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@
|
|||
|
||||
void intel_svm_check(struct intel_iommu *iommu)
|
||||
{
|
||||
if (!pasid_supported(iommu))
|
||||
if (!pasid_supported(iommu) || !ecap_smpwc(iommu->ecap))
|
||||
return;
|
||||
|
||||
if (cpu_feature_enabled(X86_FEATURE_GBPAGES) &&
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user