mirror of
git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2025-11-27 01:11:31 +00:00
IOMMU Fixes for Linux v4.11-rc3
A few fixes piled up:
* Fix a NULL-ptr dereference that happens in VT-d on some
platforms
* A fix for ARM MSI region reporting, so that a sane interface
makes it to a released kernel
* Fixes for leaf-checking in ARM io-page-table code
* Two fixes for IO/TLB flushing code on ARM Exynos platforms
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v2
iQIcBAABAgAGBQJY1QLfAAoJECvwRC2XARrjiSUQAMS6l5kg+Fo6c8uq5oGgJ27C
ddxjbI9NkvRAfnGzrHAVm80A7J9C15ik0/dp9hHVo9BjURWfhslxKXRp+9aBPjaY
TzH7UD1avU9kwXvg8E+RYBaZrHY5on51Wz5IYcuFC2JHE3LYXGJgeNYupbXll8KI
GM/sFhUfPcBODlwQwl+331fDqvSVL3/QaqojLsugGK2426dwYmovmNVrcFxT/01w
fUrzG3DcHKSMJr1e2743A6Are3+Z+E7b435vclStl0ebUf+TKKAsQlluA99jyJ/y
b0ZsddVK1OhJyoJafBuAbkQkcp5IuBV3R40MZORRv+IOqCOTe/8BkLnH+4TbRAui
JmRmpREIfq4fxZbYepVis88dpbQriVu6ONI1FnGEEqdjDquhfnsC66cSAmgoRYEH
Q913jMSvU4ehH0yRhBZdSi/giA5T48idEmtt2WjMOBx+Zq1ZLZkd02dq6rZH5j4x
maIvyclg5N7nAG/KO8XJdqhET8K/0M2Fdp3H6YoBLMNyF2u2D52XFahOvIwV3Rf7
4bW3fiy2Jv3sxlgeTy9yo8mtFwIGzCqjpB/PSvSRfrrE7G2EGNPd7sauTfALBU6w
8z7HIj4/5DncFGbYIeEK3crrr5oqN9WtlJJGlm2qPPaKZMSJ78dKmtc2TKAxdFhU
cqfL0qAZr3rRCYtZbLiq
=EcLT
-----END PGP SIGNATURE-----
Merge tag 'iommu-fixes-v4.11-rc3' of git://git.kernel.org/pub/scm/linux/kernel/git/joro/iommu
Pull IOMMU fixes from Joerg Roedel:
"A few fixes piled up:
- fix a NULL-ptr dereference that happens in VT-d on some platforms
- a fix for ARM MSI region reporting, so that a sane interface makes
it to a released kernel
- fixes for leaf-checking in ARM io-page-table code
- two fixes for IO/TLB flushing code on ARM Exynos platforms"
* tag 'iommu-fixes-v4.11-rc3' of git://git.kernel.org/pub/scm/linux/kernel/git/joro/iommu:
iommu: Disambiguate MSI region types
iommu/exynos: Workaround FLPD cache flush issues for SYSMMU v5
iommu/exynos: Block SYSMMU while invalidating FLPD cache
iommu/vt-d: Fix NULL pointer dereference in device_to_iommu
iommu/io-pgtable-arm-v7s: Check for leaf entry before dereferencing it
iommu/io-pgtable-arm: Check for leaf entry before dereferencing it
This commit is contained in:
commit
213e4eb2da
10 changed files with 41 additions and 19 deletions
|
|
@ -3202,7 +3202,7 @@ static void amd_iommu_get_resv_regions(struct device *dev,
|
||||||
|
|
||||||
region = iommu_alloc_resv_region(MSI_RANGE_START,
|
region = iommu_alloc_resv_region(MSI_RANGE_START,
|
||||||
MSI_RANGE_END - MSI_RANGE_START + 1,
|
MSI_RANGE_END - MSI_RANGE_START + 1,
|
||||||
0, IOMMU_RESV_RESERVED);
|
0, IOMMU_RESV_MSI);
|
||||||
if (!region)
|
if (!region)
|
||||||
return;
|
return;
|
||||||
list_add_tail(®ion->list, head);
|
list_add_tail(®ion->list, head);
|
||||||
|
|
|
||||||
|
|
@ -1888,7 +1888,7 @@ static void arm_smmu_get_resv_regions(struct device *dev,
|
||||||
int prot = IOMMU_WRITE | IOMMU_NOEXEC | IOMMU_MMIO;
|
int prot = IOMMU_WRITE | IOMMU_NOEXEC | IOMMU_MMIO;
|
||||||
|
|
||||||
region = iommu_alloc_resv_region(MSI_IOVA_BASE, MSI_IOVA_LENGTH,
|
region = iommu_alloc_resv_region(MSI_IOVA_BASE, MSI_IOVA_LENGTH,
|
||||||
prot, IOMMU_RESV_MSI);
|
prot, IOMMU_RESV_SW_MSI);
|
||||||
if (!region)
|
if (!region)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1608,7 +1608,7 @@ static void arm_smmu_get_resv_regions(struct device *dev,
|
||||||
int prot = IOMMU_WRITE | IOMMU_NOEXEC | IOMMU_MMIO;
|
int prot = IOMMU_WRITE | IOMMU_NOEXEC | IOMMU_MMIO;
|
||||||
|
|
||||||
region = iommu_alloc_resv_region(MSI_IOVA_BASE, MSI_IOVA_LENGTH,
|
region = iommu_alloc_resv_region(MSI_IOVA_BASE, MSI_IOVA_LENGTH,
|
||||||
prot, IOMMU_RESV_MSI);
|
prot, IOMMU_RESV_SW_MSI);
|
||||||
if (!region)
|
if (!region)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -512,7 +512,13 @@ static void sysmmu_tlb_invalidate_flpdcache(struct sysmmu_drvdata *data,
|
||||||
spin_lock_irqsave(&data->lock, flags);
|
spin_lock_irqsave(&data->lock, flags);
|
||||||
if (data->active && data->version >= MAKE_MMU_VER(3, 3)) {
|
if (data->active && data->version >= MAKE_MMU_VER(3, 3)) {
|
||||||
clk_enable(data->clk_master);
|
clk_enable(data->clk_master);
|
||||||
__sysmmu_tlb_invalidate_entry(data, iova, 1);
|
if (sysmmu_block(data)) {
|
||||||
|
if (data->version >= MAKE_MMU_VER(5, 0))
|
||||||
|
__sysmmu_tlb_invalidate(data);
|
||||||
|
else
|
||||||
|
__sysmmu_tlb_invalidate_entry(data, iova, 1);
|
||||||
|
sysmmu_unblock(data);
|
||||||
|
}
|
||||||
clk_disable(data->clk_master);
|
clk_disable(data->clk_master);
|
||||||
}
|
}
|
||||||
spin_unlock_irqrestore(&data->lock, flags);
|
spin_unlock_irqrestore(&data->lock, flags);
|
||||||
|
|
|
||||||
|
|
@ -916,7 +916,7 @@ static struct intel_iommu *device_to_iommu(struct device *dev, u8 *bus, u8 *devf
|
||||||
* which we used for the IOMMU lookup. Strictly speaking
|
* which we used for the IOMMU lookup. Strictly speaking
|
||||||
* we could do this for all PCI devices; we only need to
|
* we could do this for all PCI devices; we only need to
|
||||||
* get the BDF# from the scope table for ACPI matches. */
|
* get the BDF# from the scope table for ACPI matches. */
|
||||||
if (pdev->is_virtfn)
|
if (pdev && pdev->is_virtfn)
|
||||||
goto got_pdev;
|
goto got_pdev;
|
||||||
|
|
||||||
*bus = drhd->devices[i].bus;
|
*bus = drhd->devices[i].bus;
|
||||||
|
|
@ -5249,7 +5249,7 @@ static void intel_iommu_get_resv_regions(struct device *device,
|
||||||
|
|
||||||
reg = iommu_alloc_resv_region(IOAPIC_RANGE_START,
|
reg = iommu_alloc_resv_region(IOAPIC_RANGE_START,
|
||||||
IOAPIC_RANGE_END - IOAPIC_RANGE_START + 1,
|
IOAPIC_RANGE_END - IOAPIC_RANGE_START + 1,
|
||||||
0, IOMMU_RESV_RESERVED);
|
0, IOMMU_RESV_MSI);
|
||||||
if (!reg)
|
if (!reg)
|
||||||
return;
|
return;
|
||||||
list_add_tail(®->list, head);
|
list_add_tail(®->list, head);
|
||||||
|
|
|
||||||
|
|
@ -422,8 +422,12 @@ static int __arm_v7s_map(struct arm_v7s_io_pgtable *data, unsigned long iova,
|
||||||
pte |= ARM_V7S_ATTR_NS_TABLE;
|
pte |= ARM_V7S_ATTR_NS_TABLE;
|
||||||
|
|
||||||
__arm_v7s_set_pte(ptep, pte, 1, cfg);
|
__arm_v7s_set_pte(ptep, pte, 1, cfg);
|
||||||
} else {
|
} else if (ARM_V7S_PTE_IS_TABLE(pte, lvl)) {
|
||||||
cptep = iopte_deref(pte, lvl);
|
cptep = iopte_deref(pte, lvl);
|
||||||
|
} else {
|
||||||
|
/* We require an unmap first */
|
||||||
|
WARN_ON(!selftest_running);
|
||||||
|
return -EEXIST;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Rinse, repeat */
|
/* Rinse, repeat */
|
||||||
|
|
|
||||||
|
|
@ -335,8 +335,12 @@ static int __arm_lpae_map(struct arm_lpae_io_pgtable *data, unsigned long iova,
|
||||||
if (cfg->quirks & IO_PGTABLE_QUIRK_ARM_NS)
|
if (cfg->quirks & IO_PGTABLE_QUIRK_ARM_NS)
|
||||||
pte |= ARM_LPAE_PTE_NSTABLE;
|
pte |= ARM_LPAE_PTE_NSTABLE;
|
||||||
__arm_lpae_set_pte(ptep, pte, cfg);
|
__arm_lpae_set_pte(ptep, pte, cfg);
|
||||||
} else {
|
} else if (!iopte_leaf(pte, lvl)) {
|
||||||
cptep = iopte_deref(pte, data);
|
cptep = iopte_deref(pte, data);
|
||||||
|
} else {
|
||||||
|
/* We require an unmap first */
|
||||||
|
WARN_ON(!selftest_running);
|
||||||
|
return -EEXIST;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Rinse, repeat */
|
/* Rinse, repeat */
|
||||||
|
|
|
||||||
|
|
@ -72,6 +72,7 @@ static const char * const iommu_group_resv_type_string[] = {
|
||||||
[IOMMU_RESV_DIRECT] = "direct",
|
[IOMMU_RESV_DIRECT] = "direct",
|
||||||
[IOMMU_RESV_RESERVED] = "reserved",
|
[IOMMU_RESV_RESERVED] = "reserved",
|
||||||
[IOMMU_RESV_MSI] = "msi",
|
[IOMMU_RESV_MSI] = "msi",
|
||||||
|
[IOMMU_RESV_SW_MSI] = "msi",
|
||||||
};
|
};
|
||||||
|
|
||||||
#define IOMMU_GROUP_ATTR(_name, _mode, _show, _store) \
|
#define IOMMU_GROUP_ATTR(_name, _mode, _show, _store) \
|
||||||
|
|
@ -1743,8 +1744,8 @@ void iommu_put_resv_regions(struct device *dev, struct list_head *list)
|
||||||
}
|
}
|
||||||
|
|
||||||
struct iommu_resv_region *iommu_alloc_resv_region(phys_addr_t start,
|
struct iommu_resv_region *iommu_alloc_resv_region(phys_addr_t start,
|
||||||
size_t length,
|
size_t length, int prot,
|
||||||
int prot, int type)
|
enum iommu_resv_type type)
|
||||||
{
|
{
|
||||||
struct iommu_resv_region *region;
|
struct iommu_resv_region *region;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1182,8 +1182,7 @@ static struct vfio_group *find_iommu_group(struct vfio_domain *domain,
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool vfio_iommu_has_resv_msi(struct iommu_group *group,
|
static bool vfio_iommu_has_sw_msi(struct iommu_group *group, phys_addr_t *base)
|
||||||
phys_addr_t *base)
|
|
||||||
{
|
{
|
||||||
struct list_head group_resv_regions;
|
struct list_head group_resv_regions;
|
||||||
struct iommu_resv_region *region, *next;
|
struct iommu_resv_region *region, *next;
|
||||||
|
|
@ -1192,7 +1191,7 @@ static bool vfio_iommu_has_resv_msi(struct iommu_group *group,
|
||||||
INIT_LIST_HEAD(&group_resv_regions);
|
INIT_LIST_HEAD(&group_resv_regions);
|
||||||
iommu_get_group_resv_regions(group, &group_resv_regions);
|
iommu_get_group_resv_regions(group, &group_resv_regions);
|
||||||
list_for_each_entry(region, &group_resv_regions, list) {
|
list_for_each_entry(region, &group_resv_regions, list) {
|
||||||
if (region->type & IOMMU_RESV_MSI) {
|
if (region->type == IOMMU_RESV_SW_MSI) {
|
||||||
*base = region->start;
|
*base = region->start;
|
||||||
ret = true;
|
ret = true;
|
||||||
goto out;
|
goto out;
|
||||||
|
|
@ -1283,7 +1282,7 @@ static int vfio_iommu_type1_attach_group(void *iommu_data,
|
||||||
if (ret)
|
if (ret)
|
||||||
goto out_domain;
|
goto out_domain;
|
||||||
|
|
||||||
resv_msi = vfio_iommu_has_resv_msi(iommu_group, &resv_msi_base);
|
resv_msi = vfio_iommu_has_sw_msi(iommu_group, &resv_msi_base);
|
||||||
|
|
||||||
INIT_LIST_HEAD(&domain->group_list);
|
INIT_LIST_HEAD(&domain->group_list);
|
||||||
list_add(&group->next, &domain->group_list);
|
list_add(&group->next, &domain->group_list);
|
||||||
|
|
|
||||||
|
|
@ -125,9 +125,16 @@ enum iommu_attr {
|
||||||
};
|
};
|
||||||
|
|
||||||
/* These are the possible reserved region types */
|
/* These are the possible reserved region types */
|
||||||
#define IOMMU_RESV_DIRECT (1 << 0)
|
enum iommu_resv_type {
|
||||||
#define IOMMU_RESV_RESERVED (1 << 1)
|
/* Memory regions which must be mapped 1:1 at all times */
|
||||||
#define IOMMU_RESV_MSI (1 << 2)
|
IOMMU_RESV_DIRECT,
|
||||||
|
/* Arbitrary "never map this or give it to a device" address ranges */
|
||||||
|
IOMMU_RESV_RESERVED,
|
||||||
|
/* Hardware MSI region (untranslated) */
|
||||||
|
IOMMU_RESV_MSI,
|
||||||
|
/* Software-managed MSI translation window */
|
||||||
|
IOMMU_RESV_SW_MSI,
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* struct iommu_resv_region - descriptor for a reserved memory region
|
* struct iommu_resv_region - descriptor for a reserved memory region
|
||||||
|
|
@ -142,7 +149,7 @@ struct iommu_resv_region {
|
||||||
phys_addr_t start;
|
phys_addr_t start;
|
||||||
size_t length;
|
size_t length;
|
||||||
int prot;
|
int prot;
|
||||||
int type;
|
enum iommu_resv_type type;
|
||||||
};
|
};
|
||||||
|
|
||||||
#ifdef CONFIG_IOMMU_API
|
#ifdef CONFIG_IOMMU_API
|
||||||
|
|
@ -288,7 +295,8 @@ extern void iommu_get_resv_regions(struct device *dev, struct list_head *list);
|
||||||
extern void iommu_put_resv_regions(struct device *dev, struct list_head *list);
|
extern void iommu_put_resv_regions(struct device *dev, struct list_head *list);
|
||||||
extern int iommu_request_dm_for_dev(struct device *dev);
|
extern int iommu_request_dm_for_dev(struct device *dev);
|
||||||
extern struct iommu_resv_region *
|
extern struct iommu_resv_region *
|
||||||
iommu_alloc_resv_region(phys_addr_t start, size_t length, int prot, int type);
|
iommu_alloc_resv_region(phys_addr_t start, size_t length, int prot,
|
||||||
|
enum iommu_resv_type type);
|
||||||
extern int iommu_get_group_resv_regions(struct iommu_group *group,
|
extern int iommu_get_group_resv_regions(struct iommu_group *group,
|
||||||
struct list_head *head);
|
struct list_head *head);
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue