Commit 894901b5 authored by Joerg Roedel's avatar Joerg Roedel

Merge branch 'iommu/fixes' into arm/mediatek

parents 46cc815d 2926a2aa
...@@ -4452,6 +4452,7 @@ static int amd_ir_set_vcpu_affinity(struct irq_data *data, void *vcpu_info) ...@@ -4452,6 +4452,7 @@ static int amd_ir_set_vcpu_affinity(struct irq_data *data, void *vcpu_info)
/* Setting */ /* Setting */
irte->hi.fields.ga_root_ptr = (pi_data->base >> 12); irte->hi.fields.ga_root_ptr = (pi_data->base >> 12);
irte->hi.fields.vector = vcpu_pi_info->vector; irte->hi.fields.vector = vcpu_pi_info->vector;
irte->lo.fields_vapic.ga_log_intr = 1;
irte->lo.fields_vapic.guest_mode = 1; irte->lo.fields_vapic.guest_mode = 1;
irte->lo.fields_vapic.ga_tag = pi_data->ga_tag; irte->lo.fields_vapic.ga_tag = pi_data->ga_tag;
......
...@@ -2440,11 +2440,11 @@ static int __init state_next(void) ...@@ -2440,11 +2440,11 @@ static int __init state_next(void)
break; break;
case IOMMU_ACPI_FINISHED: case IOMMU_ACPI_FINISHED:
early_enable_iommus(); early_enable_iommus();
register_syscore_ops(&amd_iommu_syscore_ops);
x86_platform.iommu_shutdown = disable_iommus; x86_platform.iommu_shutdown = disable_iommus;
init_state = IOMMU_ENABLED; init_state = IOMMU_ENABLED;
break; break;
case IOMMU_ENABLED: case IOMMU_ENABLED:
register_syscore_ops(&amd_iommu_syscore_ops);
ret = amd_iommu_init_pci(); ret = amd_iommu_init_pci();
init_state = ret ? IOMMU_INIT_ERROR : IOMMU_PCI_INIT; init_state = ret ? IOMMU_INIT_ERROR : IOMMU_PCI_INIT;
enable_iommus_v2(); enable_iommus_v2();
......
...@@ -574,7 +574,9 @@ struct amd_iommu { ...@@ -574,7 +574,9 @@ struct amd_iommu {
static inline struct amd_iommu *dev_to_amd_iommu(struct device *dev) static inline struct amd_iommu *dev_to_amd_iommu(struct device *dev)
{ {
return container_of(dev, struct amd_iommu, iommu.dev); struct iommu_device *iommu = dev_to_iommu_device(dev);
return container_of(iommu, struct amd_iommu, iommu);
} }
#define ACPIHID_UID_LEN 256 #define ACPIHID_UID_LEN 256
......
...@@ -400,6 +400,8 @@ struct arm_smmu_device { ...@@ -400,6 +400,8 @@ struct arm_smmu_device {
u32 cavium_id_base; /* Specific to Cavium */ u32 cavium_id_base; /* Specific to Cavium */
spinlock_t global_sync_lock;
/* IOMMU core code handle */ /* IOMMU core code handle */
struct iommu_device iommu; struct iommu_device iommu;
}; };
...@@ -436,7 +438,7 @@ struct arm_smmu_domain { ...@@ -436,7 +438,7 @@ struct arm_smmu_domain {
struct arm_smmu_cfg cfg; struct arm_smmu_cfg cfg;
enum arm_smmu_domain_stage stage; enum arm_smmu_domain_stage stage;
struct mutex init_mutex; /* Protects smmu pointer */ struct mutex init_mutex; /* Protects smmu pointer */
spinlock_t cb_lock; /* Serialises ATS1* ops */ spinlock_t cb_lock; /* Serialises ATS1* ops and TLB syncs */
struct iommu_domain domain; struct iommu_domain domain;
}; };
...@@ -602,9 +604,12 @@ static void __arm_smmu_tlb_sync(struct arm_smmu_device *smmu, ...@@ -602,9 +604,12 @@ static void __arm_smmu_tlb_sync(struct arm_smmu_device *smmu,
static void arm_smmu_tlb_sync_global(struct arm_smmu_device *smmu) static void arm_smmu_tlb_sync_global(struct arm_smmu_device *smmu)
{ {
void __iomem *base = ARM_SMMU_GR0(smmu); void __iomem *base = ARM_SMMU_GR0(smmu);
unsigned long flags;
spin_lock_irqsave(&smmu->global_sync_lock, flags);
__arm_smmu_tlb_sync(smmu, base + ARM_SMMU_GR0_sTLBGSYNC, __arm_smmu_tlb_sync(smmu, base + ARM_SMMU_GR0_sTLBGSYNC,
base + ARM_SMMU_GR0_sTLBGSTATUS); base + ARM_SMMU_GR0_sTLBGSTATUS);
spin_unlock_irqrestore(&smmu->global_sync_lock, flags);
} }
static void arm_smmu_tlb_sync_context(void *cookie) static void arm_smmu_tlb_sync_context(void *cookie)
...@@ -612,9 +617,12 @@ static void arm_smmu_tlb_sync_context(void *cookie) ...@@ -612,9 +617,12 @@ static void arm_smmu_tlb_sync_context(void *cookie)
struct arm_smmu_domain *smmu_domain = cookie; struct arm_smmu_domain *smmu_domain = cookie;
struct arm_smmu_device *smmu = smmu_domain->smmu; struct arm_smmu_device *smmu = smmu_domain->smmu;
void __iomem *base = ARM_SMMU_CB(smmu, smmu_domain->cfg.cbndx); void __iomem *base = ARM_SMMU_CB(smmu, smmu_domain->cfg.cbndx);
unsigned long flags;
spin_lock_irqsave(&smmu_domain->cb_lock, flags);
__arm_smmu_tlb_sync(smmu, base + ARM_SMMU_CB_TLBSYNC, __arm_smmu_tlb_sync(smmu, base + ARM_SMMU_CB_TLBSYNC,
base + ARM_SMMU_CB_TLBSTATUS); base + ARM_SMMU_CB_TLBSTATUS);
spin_unlock_irqrestore(&smmu_domain->cb_lock, flags);
} }
static void arm_smmu_tlb_sync_vmid(void *cookie) static void arm_smmu_tlb_sync_vmid(void *cookie)
...@@ -1511,6 +1519,12 @@ static int arm_smmu_add_device(struct device *dev) ...@@ -1511,6 +1519,12 @@ static int arm_smmu_add_device(struct device *dev)
if (using_legacy_binding) { if (using_legacy_binding) {
ret = arm_smmu_register_legacy_master(dev, &smmu); ret = arm_smmu_register_legacy_master(dev, &smmu);
/*
* If dev->iommu_fwspec is initally NULL, arm_smmu_register_legacy_master()
* will allocate/initialise a new one. Thus we need to update fwspec for
* later use.
*/
fwspec = dev->iommu_fwspec; fwspec = dev->iommu_fwspec;
if (ret) if (ret)
goto out_free; goto out_free;
...@@ -1550,15 +1564,15 @@ static int arm_smmu_add_device(struct device *dev) ...@@ -1550,15 +1564,15 @@ static int arm_smmu_add_device(struct device *dev)
ret = arm_smmu_master_alloc_smes(dev); ret = arm_smmu_master_alloc_smes(dev);
if (ret) if (ret)
goto out_free; goto out_cfg_free;
iommu_device_link(&smmu->iommu, dev); iommu_device_link(&smmu->iommu, dev);
return 0; return 0;
out_cfg_free:
kfree(cfg);
out_free: out_free:
if (fwspec)
kfree(fwspec->iommu_priv);
iommu_fwspec_free(dev); iommu_fwspec_free(dev);
return ret; return ret;
} }
...@@ -1925,6 +1939,7 @@ static int arm_smmu_device_cfg_probe(struct arm_smmu_device *smmu) ...@@ -1925,6 +1939,7 @@ static int arm_smmu_device_cfg_probe(struct arm_smmu_device *smmu)
smmu->num_mapping_groups = size; smmu->num_mapping_groups = size;
mutex_init(&smmu->stream_map_mutex); mutex_init(&smmu->stream_map_mutex);
spin_lock_init(&smmu->global_sync_lock);
if (smmu->version < ARM_SMMU_V2 || !(id & ID0_PTFS_NO_AARCH32)) { if (smmu->version < ARM_SMMU_V2 || !(id & ID0_PTFS_NO_AARCH32)) {
smmu->features |= ARM_SMMU_FEAT_FMT_AARCH32_L; smmu->features |= ARM_SMMU_FEAT_FMT_AARCH32_L;
......
...@@ -4736,7 +4736,9 @@ static void intel_disable_iommus(void) ...@@ -4736,7 +4736,9 @@ static void intel_disable_iommus(void)
static inline struct intel_iommu *dev_to_intel_iommu(struct device *dev) static inline struct intel_iommu *dev_to_intel_iommu(struct device *dev)
{ {
return container_of(dev, struct intel_iommu, iommu.dev); struct iommu_device *iommu_dev = dev_to_iommu_device(dev);
return container_of(iommu_dev, struct intel_iommu, iommu);
} }
static ssize_t intel_iommu_show_version(struct device *dev, static ssize_t intel_iommu_show_version(struct device *dev,
......
...@@ -479,6 +479,9 @@ static int arm_v7s_map(struct io_pgtable_ops *ops, unsigned long iova, ...@@ -479,6 +479,9 @@ static int arm_v7s_map(struct io_pgtable_ops *ops, unsigned long iova,
if (!(prot & (IOMMU_READ | IOMMU_WRITE))) if (!(prot & (IOMMU_READ | IOMMU_WRITE)))
return 0; return 0;
if (WARN_ON(upper_32_bits(iova) || upper_32_bits(paddr)))
return -ERANGE;
ret = __arm_v7s_map(data, iova, paddr, size, prot, 1, data->pgd); ret = __arm_v7s_map(data, iova, paddr, size, prot, 1, data->pgd);
/* /*
* Synchronise all PTE updates for the new mapping before there's * Synchronise all PTE updates for the new mapping before there's
...@@ -659,6 +662,9 @@ static int arm_v7s_unmap(struct io_pgtable_ops *ops, unsigned long iova, ...@@ -659,6 +662,9 @@ static int arm_v7s_unmap(struct io_pgtable_ops *ops, unsigned long iova,
struct arm_v7s_io_pgtable *data = io_pgtable_ops_to_data(ops); struct arm_v7s_io_pgtable *data = io_pgtable_ops_to_data(ops);
size_t unmapped; size_t unmapped;
if (WARN_ON(upper_32_bits(iova)))
return 0;
unmapped = __arm_v7s_unmap(data, iova, size, 1, data->pgd); unmapped = __arm_v7s_unmap(data, iova, size, 1, data->pgd);
if (unmapped) if (unmapped)
io_pgtable_tlb_sync(&data->iop); io_pgtable_tlb_sync(&data->iop);
......
...@@ -452,6 +452,10 @@ static int arm_lpae_map(struct io_pgtable_ops *ops, unsigned long iova, ...@@ -452,6 +452,10 @@ static int arm_lpae_map(struct io_pgtable_ops *ops, unsigned long iova,
if (!(iommu_prot & (IOMMU_READ | IOMMU_WRITE))) if (!(iommu_prot & (IOMMU_READ | IOMMU_WRITE)))
return 0; return 0;
if (WARN_ON(iova >= (1ULL << data->iop.cfg.ias) ||
paddr >= (1ULL << data->iop.cfg.oas)))
return -ERANGE;
prot = arm_lpae_prot_to_pte(data, iommu_prot); prot = arm_lpae_prot_to_pte(data, iommu_prot);
ret = __arm_lpae_map(data, iova, paddr, size, prot, lvl, ptep); ret = __arm_lpae_map(data, iova, paddr, size, prot, lvl, ptep);
/* /*
...@@ -610,6 +614,9 @@ static int arm_lpae_unmap(struct io_pgtable_ops *ops, unsigned long iova, ...@@ -610,6 +614,9 @@ static int arm_lpae_unmap(struct io_pgtable_ops *ops, unsigned long iova,
arm_lpae_iopte *ptep = data->pgd; arm_lpae_iopte *ptep = data->pgd;
int lvl = ARM_LPAE_START_LVL(data); int lvl = ARM_LPAE_START_LVL(data);
if (WARN_ON(iova >= (1ULL << data->iop.cfg.ias)))
return 0;
unmapped = __arm_lpae_unmap(data, iova, size, lvl, ptep); unmapped = __arm_lpae_unmap(data, iova, size, lvl, ptep);
if (unmapped) if (unmapped)
io_pgtable_tlb_sync(&data->iop); io_pgtable_tlb_sync(&data->iop);
......
...@@ -158,14 +158,12 @@ void free_io_pgtable_ops(struct io_pgtable_ops *ops); ...@@ -158,14 +158,12 @@ void free_io_pgtable_ops(struct io_pgtable_ops *ops);
* @fmt: The page table format. * @fmt: The page table format.
* @cookie: An opaque token provided by the IOMMU driver and passed back to * @cookie: An opaque token provided by the IOMMU driver and passed back to
* any callback routines. * any callback routines.
* @tlb_sync_pending: Private flag for optimising out redundant syncs.
* @cfg: A copy of the page table configuration. * @cfg: A copy of the page table configuration.
* @ops: The page table operations in use for this set of page tables. * @ops: The page table operations in use for this set of page tables.
*/ */
struct io_pgtable { struct io_pgtable {
enum io_pgtable_fmt fmt; enum io_pgtable_fmt fmt;
void *cookie; void *cookie;
bool tlb_sync_pending;
struct io_pgtable_cfg cfg; struct io_pgtable_cfg cfg;
struct io_pgtable_ops ops; struct io_pgtable_ops ops;
}; };
...@@ -175,22 +173,17 @@ struct io_pgtable { ...@@ -175,22 +173,17 @@ struct io_pgtable {
static inline void io_pgtable_tlb_flush_all(struct io_pgtable *iop) static inline void io_pgtable_tlb_flush_all(struct io_pgtable *iop)
{ {
iop->cfg.tlb->tlb_flush_all(iop->cookie); iop->cfg.tlb->tlb_flush_all(iop->cookie);
iop->tlb_sync_pending = true;
} }
static inline void io_pgtable_tlb_add_flush(struct io_pgtable *iop, static inline void io_pgtable_tlb_add_flush(struct io_pgtable *iop,
unsigned long iova, size_t size, size_t granule, bool leaf) unsigned long iova, size_t size, size_t granule, bool leaf)
{ {
iop->cfg.tlb->tlb_add_flush(iova, size, granule, leaf, iop->cookie); iop->cfg.tlb->tlb_add_flush(iova, size, granule, leaf, iop->cookie);
iop->tlb_sync_pending = true;
} }
static inline void io_pgtable_tlb_sync(struct io_pgtable *iop) static inline void io_pgtable_tlb_sync(struct io_pgtable *iop)
{ {
if (iop->tlb_sync_pending) { iop->cfg.tlb->tlb_sync(iop->cookie);
iop->cfg.tlb->tlb_sync(iop->cookie);
iop->tlb_sync_pending = false;
}
} }
/** /**
......
...@@ -62,32 +62,40 @@ int iommu_device_sysfs_add(struct iommu_device *iommu, ...@@ -62,32 +62,40 @@ int iommu_device_sysfs_add(struct iommu_device *iommu,
va_list vargs; va_list vargs;
int ret; int ret;
device_initialize(&iommu->dev); iommu->dev = kzalloc(sizeof(*iommu->dev), GFP_KERNEL);
if (!iommu->dev)
return -ENOMEM;
iommu->dev.class = &iommu_class; device_initialize(iommu->dev);
iommu->dev.parent = parent;
iommu->dev.groups = groups; iommu->dev->class = &iommu_class;
iommu->dev->parent = parent;
iommu->dev->groups = groups;
va_start(vargs, fmt); va_start(vargs, fmt);
ret = kobject_set_name_vargs(&iommu->dev.kobj, fmt, vargs); ret = kobject_set_name_vargs(&iommu->dev->kobj, fmt, vargs);
va_end(vargs); va_end(vargs);
if (ret) if (ret)
goto error; goto error;
ret = device_add(&iommu->dev); ret = device_add(iommu->dev);
if (ret) if (ret)
goto error; goto error;
dev_set_drvdata(iommu->dev, iommu);
return 0; return 0;
error: error:
put_device(&iommu->dev); put_device(iommu->dev);
return ret; return ret;
} }
void iommu_device_sysfs_remove(struct iommu_device *iommu) void iommu_device_sysfs_remove(struct iommu_device *iommu)
{ {
device_unregister(&iommu->dev); dev_set_drvdata(iommu->dev, NULL);
device_unregister(iommu->dev);
iommu->dev = NULL;
} }
/* /*
* IOMMU drivers can indicate a device is managed by a given IOMMU using * IOMMU drivers can indicate a device is managed by a given IOMMU using
...@@ -102,14 +110,14 @@ int iommu_device_link(struct iommu_device *iommu, struct device *link) ...@@ -102,14 +110,14 @@ int iommu_device_link(struct iommu_device *iommu, struct device *link)
if (!iommu || IS_ERR(iommu)) if (!iommu || IS_ERR(iommu))
return -ENODEV; return -ENODEV;
ret = sysfs_add_link_to_group(&iommu->dev.kobj, "devices", ret = sysfs_add_link_to_group(&iommu->dev->kobj, "devices",
&link->kobj, dev_name(link)); &link->kobj, dev_name(link));
if (ret) if (ret)
return ret; return ret;
ret = sysfs_create_link_nowarn(&link->kobj, &iommu->dev.kobj, "iommu"); ret = sysfs_create_link_nowarn(&link->kobj, &iommu->dev->kobj, "iommu");
if (ret) if (ret)
sysfs_remove_link_from_group(&iommu->dev.kobj, "devices", sysfs_remove_link_from_group(&iommu->dev->kobj, "devices",
dev_name(link)); dev_name(link));
return ret; return ret;
...@@ -121,5 +129,5 @@ void iommu_device_unlink(struct iommu_device *iommu, struct device *link) ...@@ -121,5 +129,5 @@ void iommu_device_unlink(struct iommu_device *iommu, struct device *link)
return; return;
sysfs_remove_link(&link->kobj, "iommu"); sysfs_remove_link(&link->kobj, "iommu");
sysfs_remove_link_from_group(&iommu->dev.kobj, "devices", dev_name(link)); sysfs_remove_link_from_group(&iommu->dev->kobj, "devices", dev_name(link));
} }
...@@ -129,6 +129,7 @@ static void mtk_iommu_tlb_add_flush_nosync(unsigned long iova, size_t size, ...@@ -129,6 +129,7 @@ static void mtk_iommu_tlb_add_flush_nosync(unsigned long iova, size_t size,
writel_relaxed(iova, data->base + REG_MMU_INVLD_START_A); writel_relaxed(iova, data->base + REG_MMU_INVLD_START_A);
writel_relaxed(iova + size - 1, data->base + REG_MMU_INVLD_END_A); writel_relaxed(iova + size - 1, data->base + REG_MMU_INVLD_END_A);
writel_relaxed(F_MMU_INV_RANGE, data->base + REG_MMU_INVALIDATE); writel_relaxed(F_MMU_INV_RANGE, data->base + REG_MMU_INVALIDATE);
data->tlb_flush_active = true;
} }
static void mtk_iommu_tlb_sync(void *cookie) static void mtk_iommu_tlb_sync(void *cookie)
...@@ -137,6 +138,10 @@ static void mtk_iommu_tlb_sync(void *cookie) ...@@ -137,6 +138,10 @@ static void mtk_iommu_tlb_sync(void *cookie)
int ret; int ret;
u32 tmp; u32 tmp;
/* Avoid timing out if there's nothing to wait for */
if (!data->tlb_flush_active)
return;
ret = readl_poll_timeout_atomic(data->base + REG_MMU_CPE_DONE, tmp, ret = readl_poll_timeout_atomic(data->base + REG_MMU_CPE_DONE, tmp,
tmp != 0, 10, 100000); tmp != 0, 10, 100000);
if (ret) { if (ret) {
...@@ -146,6 +151,7 @@ static void mtk_iommu_tlb_sync(void *cookie) ...@@ -146,6 +151,7 @@ static void mtk_iommu_tlb_sync(void *cookie)
} }
/* Clear the CPE status */ /* Clear the CPE status */
writel_relaxed(0, data->base + REG_MMU_CPE_DONE); writel_relaxed(0, data->base + REG_MMU_CPE_DONE);
data->tlb_flush_active = false;
} }
static const struct iommu_gather_ops mtk_iommu_gather_ops = { static const struct iommu_gather_ops mtk_iommu_gather_ops = {
......
...@@ -47,6 +47,7 @@ struct mtk_iommu_data { ...@@ -47,6 +47,7 @@ struct mtk_iommu_data {
struct iommu_group *m4u_group; struct iommu_group *m4u_group;
struct mtk_smi_iommu smi_imu; /* SMI larb iommu info */ struct mtk_smi_iommu smi_imu; /* SMI larb iommu info */
bool enable_4GB; bool enable_4GB;
bool tlb_flush_active;
struct iommu_device iommu; struct iommu_device iommu;
}; };
......
...@@ -240,7 +240,7 @@ struct iommu_device { ...@@ -240,7 +240,7 @@ struct iommu_device {
struct list_head list; struct list_head list;
const struct iommu_ops *ops; const struct iommu_ops *ops;
struct fwnode_handle *fwnode; struct fwnode_handle *fwnode;
struct device dev; struct device *dev;
}; };
int iommu_device_register(struct iommu_device *iommu); int iommu_device_register(struct iommu_device *iommu);
...@@ -265,6 +265,11 @@ static inline void iommu_device_set_fwnode(struct iommu_device *iommu, ...@@ -265,6 +265,11 @@ static inline void iommu_device_set_fwnode(struct iommu_device *iommu,
iommu->fwnode = fwnode; iommu->fwnode = fwnode;
} }
static inline struct iommu_device *dev_to_iommu_device(struct device *dev)
{
return (struct iommu_device *)dev_get_drvdata(dev);
}
#define IOMMU_GROUP_NOTIFY_ADD_DEVICE 1 /* Device added */ #define IOMMU_GROUP_NOTIFY_ADD_DEVICE 1 /* Device added */
#define IOMMU_GROUP_NOTIFY_DEL_DEVICE 2 /* Pre Device removed */ #define IOMMU_GROUP_NOTIFY_DEL_DEVICE 2 /* Pre Device removed */
#define IOMMU_GROUP_NOTIFY_BIND_DRIVER 3 /* Pre Driver bind */ #define IOMMU_GROUP_NOTIFY_BIND_DRIVER 3 /* Pre Driver bind */
...@@ -589,6 +594,11 @@ static inline void iommu_device_set_fwnode(struct iommu_device *iommu, ...@@ -589,6 +594,11 @@ static inline void iommu_device_set_fwnode(struct iommu_device *iommu,
{ {
} }
static inline struct iommu_device *dev_to_iommu_device(struct device *dev)
{
return NULL;
}
static inline void iommu_device_unregister(struct iommu_device *iommu) static inline void iommu_device_unregister(struct iommu_device *iommu)
{ {
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment