mirror of
https://github.com/linux-msm/laptops-kernel.git
synced 2026-08-13 14:19:53 -07:00
Merge branches 'arm/mediatek', 'arm/msm', 'arm/renesas', 'arm/rockchip', 'arm/smmu', 'x86/vt-d' and 'x86/amd' into next
This commit is contained in:
@@ -44,6 +44,10 @@ properties:
|
||||
- renesas,ipmmu-r8a77990 # R-Car E3
|
||||
- renesas,ipmmu-r8a77995 # R-Car D3
|
||||
- renesas,ipmmu-r8a779a0 # R-Car V3U
|
||||
- items:
|
||||
- enum:
|
||||
- renesas,ipmmu-r8a779f0 # R-Car S4-8
|
||||
- const: renesas,rcar-gen4-ipmmu-vmsa # R-Car Gen4
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
@@ -133,7 +133,7 @@ nvkm_device_tegra_probe_iommu(struct nvkm_device_tegra *tdev)
|
||||
* or equal to the system's PAGE_SIZE, with a preference if
|
||||
* both are equal.
|
||||
*/
|
||||
pgsize_bitmap = tdev->iommu.domain->ops->pgsize_bitmap;
|
||||
pgsize_bitmap = tdev->iommu.domain->pgsize_bitmap;
|
||||
if (pgsize_bitmap & PAGE_SIZE) {
|
||||
tdev->iommu.pgshift = PAGE_SHIFT;
|
||||
} else {
|
||||
|
||||
@@ -15,7 +15,6 @@ extern irqreturn_t amd_iommu_int_thread(int irq, void *data);
|
||||
extern irqreturn_t amd_iommu_int_handler(int irq, void *data);
|
||||
extern void amd_iommu_apply_erratum_63(u16 devid);
|
||||
extern void amd_iommu_restart_event_logging(struct amd_iommu *iommu);
|
||||
extern void amd_iommu_reset_cmd_buffer(struct amd_iommu *iommu);
|
||||
extern int amd_iommu_init_devices(void);
|
||||
extern void amd_iommu_uninit_devices(void);
|
||||
extern void amd_iommu_init_notifier(void);
|
||||
@@ -117,8 +116,7 @@ void amd_iommu_domain_clr_pt_root(struct protection_domain *domain)
|
||||
|
||||
|
||||
extern bool translation_pre_enabled(struct amd_iommu *iommu);
|
||||
extern bool amd_iommu_is_attach_deferred(struct iommu_domain *domain,
|
||||
struct device *dev);
|
||||
extern bool amd_iommu_is_attach_deferred(struct device *dev);
|
||||
extern int __init add_special_device(u8 type, u8 id, u16 *devid,
|
||||
bool cmd_line);
|
||||
|
||||
|
||||
@@ -671,7 +671,7 @@ void amd_iommu_restart_event_logging(struct amd_iommu *iommu)
|
||||
* This function resets the command buffer if the IOMMU stopped fetching
|
||||
* commands from it.
|
||||
*/
|
||||
void amd_iommu_reset_cmd_buffer(struct amd_iommu *iommu)
|
||||
static void amd_iommu_reset_cmd_buffer(struct amd_iommu *iommu)
|
||||
{
|
||||
iommu_feature_disable(iommu, CONTROL_CMDBUF_EN);
|
||||
|
||||
@@ -990,6 +990,7 @@ static bool copy_device_table(void)
|
||||
get_order(dev_table_size));
|
||||
if (old_dev_tbl_cpy == NULL) {
|
||||
pr_err("Failed to allocate memory for copying old device table!\n");
|
||||
memunmap(old_devtb);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -1020,6 +1021,7 @@ static bool copy_device_table(void)
|
||||
if ((int_ctl != DTE_IRQ_REMAP_INTCTL) ||
|
||||
(int_tab_len != DTE_INTTABLEN)) {
|
||||
pr_err("Wrong old irq remapping flag: %#x\n", devid);
|
||||
memunmap(old_devtb);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -1953,9 +1955,11 @@ static int __init amd_iommu_init_pci(void)
|
||||
|
||||
for_each_iommu(iommu) {
|
||||
ret = iommu_init_pci(iommu);
|
||||
if (ret)
|
||||
break;
|
||||
|
||||
if (ret) {
|
||||
pr_err("IOMMU%d: Failed to initialize IOMMU Hardware (error=%d)!\n",
|
||||
iommu->index, ret);
|
||||
goto out;
|
||||
}
|
||||
/* Need to setup range after PCI init */
|
||||
iommu_set_cwwb_range(iommu);
|
||||
}
|
||||
@@ -1971,6 +1975,11 @@ static int __init amd_iommu_init_pci(void)
|
||||
* active.
|
||||
*/
|
||||
ret = amd_iommu_init_api();
|
||||
if (ret) {
|
||||
pr_err("IOMMU: Failed to initialize IOMMU-API interface (error=%d)!\n",
|
||||
ret);
|
||||
goto out;
|
||||
}
|
||||
|
||||
init_device_table_dma();
|
||||
|
||||
@@ -1980,6 +1989,7 @@ static int __init amd_iommu_init_pci(void)
|
||||
if (!ret)
|
||||
print_iommu_info();
|
||||
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
+12
-11
@@ -2221,8 +2221,7 @@ static void amd_iommu_get_resv_regions(struct device *dev,
|
||||
list_add_tail(®ion->list, head);
|
||||
}
|
||||
|
||||
bool amd_iommu_is_attach_deferred(struct iommu_domain *domain,
|
||||
struct device *dev)
|
||||
bool amd_iommu_is_attach_deferred(struct device *dev)
|
||||
{
|
||||
struct iommu_dev_data *dev_data = dev_iommu_priv_get(dev);
|
||||
|
||||
@@ -2275,13 +2274,6 @@ static int amd_iommu_def_domain_type(struct device *dev)
|
||||
const struct iommu_ops amd_iommu_ops = {
|
||||
.capable = amd_iommu_capable,
|
||||
.domain_alloc = amd_iommu_domain_alloc,
|
||||
.domain_free = amd_iommu_domain_free,
|
||||
.attach_dev = amd_iommu_attach_device,
|
||||
.detach_dev = amd_iommu_detach_device,
|
||||
.map = amd_iommu_map,
|
||||
.iotlb_sync_map = amd_iommu_iotlb_sync_map,
|
||||
.unmap = amd_iommu_unmap,
|
||||
.iova_to_phys = amd_iommu_iova_to_phys,
|
||||
.probe_device = amd_iommu_probe_device,
|
||||
.release_device = amd_iommu_release_device,
|
||||
.probe_finalize = amd_iommu_probe_finalize,
|
||||
@@ -2290,9 +2282,18 @@ const struct iommu_ops amd_iommu_ops = {
|
||||
.put_resv_regions = generic_iommu_put_resv_regions,
|
||||
.is_attach_deferred = amd_iommu_is_attach_deferred,
|
||||
.pgsize_bitmap = AMD_IOMMU_PGSIZES,
|
||||
.flush_iotlb_all = amd_iommu_flush_iotlb_all,
|
||||
.iotlb_sync = amd_iommu_iotlb_sync,
|
||||
.def_domain_type = amd_iommu_def_domain_type,
|
||||
.default_domain_ops = &(const struct iommu_domain_ops) {
|
||||
.attach_dev = amd_iommu_attach_device,
|
||||
.detach_dev = amd_iommu_detach_device,
|
||||
.map = amd_iommu_map,
|
||||
.unmap = amd_iommu_unmap,
|
||||
.iotlb_sync_map = amd_iommu_iotlb_sync_map,
|
||||
.iova_to_phys = amd_iommu_iova_to_phys,
|
||||
.flush_iotlb_all = amd_iommu_flush_iotlb_all,
|
||||
.iotlb_sync = amd_iommu_iotlb_sync,
|
||||
.free = amd_iommu_domain_free,
|
||||
}
|
||||
};
|
||||
|
||||
/*****************************************************************************
|
||||
|
||||
@@ -24,7 +24,6 @@
|
||||
MODULE_LICENSE("GPL v2");
|
||||
MODULE_AUTHOR("Joerg Roedel <jroedel@suse.de>");
|
||||
|
||||
#define MAX_DEVICES 0x10000
|
||||
#define PRI_QUEUE_SIZE 512
|
||||
|
||||
struct pri_queue {
|
||||
@@ -71,7 +70,6 @@ struct fault {
|
||||
struct pasid_state *state;
|
||||
struct mm_struct *mm;
|
||||
u64 address;
|
||||
u16 devid;
|
||||
u32 pasid;
|
||||
u16 tag;
|
||||
u16 finish;
|
||||
@@ -125,6 +123,15 @@ static void free_device_state(struct device_state *dev_state)
|
||||
{
|
||||
struct iommu_group *group;
|
||||
|
||||
/* Get rid of any remaining pasid states */
|
||||
free_pasid_states(dev_state);
|
||||
|
||||
/*
|
||||
* Wait until the last reference is dropped before freeing
|
||||
* the device state.
|
||||
*/
|
||||
wait_event(dev_state->wq, !atomic_read(&dev_state->count));
|
||||
|
||||
/*
|
||||
* First detach device from domain - No more PRI requests will arrive
|
||||
* from that device after it is unbound from the IOMMUv2 domain.
|
||||
@@ -537,7 +544,7 @@ static int ppr_notifier(struct notifier_block *nb, unsigned long e, void *data)
|
||||
ret = NOTIFY_DONE;
|
||||
|
||||
/* In kdump kernel pci dev is not initialized yet -> send INVALID */
|
||||
if (amd_iommu_is_attach_deferred(NULL, &pdev->dev)) {
|
||||
if (amd_iommu_is_attach_deferred(&pdev->dev)) {
|
||||
amd_iommu_complete_ppr(pdev, iommu_fault->pasid,
|
||||
PPR_INVALID, tag);
|
||||
goto out;
|
||||
@@ -850,15 +857,7 @@ void amd_iommu_free_device(struct pci_dev *pdev)
|
||||
|
||||
spin_unlock_irqrestore(&state_lock, flags);
|
||||
|
||||
/* Get rid of any remaining pasid states */
|
||||
free_pasid_states(dev_state);
|
||||
|
||||
put_device_state(dev_state);
|
||||
/*
|
||||
* Wait until the last reference is dropped before freeing
|
||||
* the device state.
|
||||
*/
|
||||
wait_event(dev_state->wq, !atomic_read(&dev_state->count));
|
||||
free_device_state(dev_state);
|
||||
}
|
||||
EXPORT_SYMBOL(amd_iommu_free_device);
|
||||
@@ -955,8 +954,8 @@ out:
|
||||
|
||||
static void __exit amd_iommu_v2_exit(void)
|
||||
{
|
||||
struct device_state *dev_state;
|
||||
int i;
|
||||
struct device_state *dev_state, *next;
|
||||
unsigned long flags;
|
||||
|
||||
if (!amd_iommu_v2_supported())
|
||||
return;
|
||||
@@ -969,18 +968,18 @@ static void __exit amd_iommu_v2_exit(void)
|
||||
* The loop below might call flush_workqueue(), so call
|
||||
* destroy_workqueue() after it
|
||||
*/
|
||||
for (i = 0; i < MAX_DEVICES; ++i) {
|
||||
dev_state = get_device_state(i);
|
||||
|
||||
if (dev_state == NULL)
|
||||
continue;
|
||||
spin_lock_irqsave(&state_lock, flags);
|
||||
|
||||
list_for_each_entry_safe(dev_state, next, &state_list, list) {
|
||||
WARN_ON_ONCE(1);
|
||||
|
||||
put_device_state(dev_state);
|
||||
amd_iommu_free_device(dev_state->pdev);
|
||||
list_del(&dev_state->list);
|
||||
free_device_state(dev_state);
|
||||
}
|
||||
|
||||
spin_unlock_irqrestore(&state_lock, flags);
|
||||
|
||||
destroy_workqueue(iommu_wq);
|
||||
}
|
||||
|
||||
|
||||
@@ -765,15 +765,6 @@ static void apple_dart_get_resv_regions(struct device *dev,
|
||||
|
||||
static const struct iommu_ops apple_dart_iommu_ops = {
|
||||
.domain_alloc = apple_dart_domain_alloc,
|
||||
.domain_free = apple_dart_domain_free,
|
||||
.attach_dev = apple_dart_attach_dev,
|
||||
.detach_dev = apple_dart_detach_dev,
|
||||
.map_pages = apple_dart_map_pages,
|
||||
.unmap_pages = apple_dart_unmap_pages,
|
||||
.flush_iotlb_all = apple_dart_flush_iotlb_all,
|
||||
.iotlb_sync = apple_dart_iotlb_sync,
|
||||
.iotlb_sync_map = apple_dart_iotlb_sync_map,
|
||||
.iova_to_phys = apple_dart_iova_to_phys,
|
||||
.probe_device = apple_dart_probe_device,
|
||||
.release_device = apple_dart_release_device,
|
||||
.device_group = apple_dart_device_group,
|
||||
@@ -782,6 +773,17 @@ static const struct iommu_ops apple_dart_iommu_ops = {
|
||||
.get_resv_regions = apple_dart_get_resv_regions,
|
||||
.put_resv_regions = generic_iommu_put_resv_regions,
|
||||
.pgsize_bitmap = -1UL, /* Restricted during dart probe */
|
||||
.default_domain_ops = &(const struct iommu_domain_ops) {
|
||||
.attach_dev = apple_dart_attach_dev,
|
||||
.detach_dev = apple_dart_detach_dev,
|
||||
.map_pages = apple_dart_map_pages,
|
||||
.unmap_pages = apple_dart_unmap_pages,
|
||||
.flush_iotlb_all = apple_dart_flush_iotlb_all,
|
||||
.iotlb_sync = apple_dart_iotlb_sync,
|
||||
.iotlb_sync_map = apple_dart_iotlb_sync_map,
|
||||
.iova_to_phys = apple_dart_iova_to_phys,
|
||||
.free = apple_dart_domain_free,
|
||||
}
|
||||
};
|
||||
|
||||
static irqreturn_t apple_dart_irq(int irq, void *dev)
|
||||
|
||||
@@ -1558,6 +1558,7 @@ static irqreturn_t arm_smmu_evtq_thread(int irq, void *dev)
|
||||
dev_info(smmu->dev, "\t0x%016llx\n",
|
||||
(unsigned long long)evt[i]);
|
||||
|
||||
cond_resched();
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -2841,17 +2842,9 @@ static int arm_smmu_dev_disable_feature(struct device *dev,
|
||||
static struct iommu_ops arm_smmu_ops = {
|
||||
.capable = arm_smmu_capable,
|
||||
.domain_alloc = arm_smmu_domain_alloc,
|
||||
.domain_free = arm_smmu_domain_free,
|
||||
.attach_dev = arm_smmu_attach_dev,
|
||||
.map_pages = arm_smmu_map_pages,
|
||||
.unmap_pages = arm_smmu_unmap_pages,
|
||||
.flush_iotlb_all = arm_smmu_flush_iotlb_all,
|
||||
.iotlb_sync = arm_smmu_iotlb_sync,
|
||||
.iova_to_phys = arm_smmu_iova_to_phys,
|
||||
.probe_device = arm_smmu_probe_device,
|
||||
.release_device = arm_smmu_release_device,
|
||||
.device_group = arm_smmu_device_group,
|
||||
.enable_nesting = arm_smmu_enable_nesting,
|
||||
.of_xlate = arm_smmu_of_xlate,
|
||||
.get_resv_regions = arm_smmu_get_resv_regions,
|
||||
.put_resv_regions = generic_iommu_put_resv_regions,
|
||||
@@ -2865,6 +2858,16 @@ static struct iommu_ops arm_smmu_ops = {
|
||||
.page_response = arm_smmu_page_response,
|
||||
.pgsize_bitmap = -1UL, /* Restricted during device attach */
|
||||
.owner = THIS_MODULE,
|
||||
.default_domain_ops = &(const struct iommu_domain_ops) {
|
||||
.attach_dev = arm_smmu_attach_dev,
|
||||
.map_pages = arm_smmu_map_pages,
|
||||
.unmap_pages = arm_smmu_unmap_pages,
|
||||
.flush_iotlb_all = arm_smmu_flush_iotlb_all,
|
||||
.iotlb_sync = arm_smmu_iotlb_sync,
|
||||
.iova_to_phys = arm_smmu_iova_to_phys,
|
||||
.enable_nesting = arm_smmu_enable_nesting,
|
||||
.free = arm_smmu_domain_free,
|
||||
}
|
||||
};
|
||||
|
||||
/* Probing and initialisation functions */
|
||||
@@ -2911,32 +2914,20 @@ static int arm_smmu_init_one_queue(struct arm_smmu_device *smmu,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void arm_smmu_cmdq_free_bitmap(void *data)
|
||||
{
|
||||
unsigned long *bitmap = data;
|
||||
bitmap_free(bitmap);
|
||||
}
|
||||
|
||||
static int arm_smmu_cmdq_init(struct arm_smmu_device *smmu)
|
||||
{
|
||||
int ret = 0;
|
||||
struct arm_smmu_cmdq *cmdq = &smmu->cmdq;
|
||||
unsigned int nents = 1 << cmdq->q.llq.max_n_shift;
|
||||
atomic_long_t *bitmap;
|
||||
|
||||
atomic_set(&cmdq->owner_prod, 0);
|
||||
atomic_set(&cmdq->lock, 0);
|
||||
|
||||
bitmap = (atomic_long_t *)bitmap_zalloc(nents, GFP_KERNEL);
|
||||
if (!bitmap) {
|
||||
dev_err(smmu->dev, "failed to allocate cmdq bitmap\n");
|
||||
ret = -ENOMEM;
|
||||
} else {
|
||||
cmdq->valid_map = bitmap;
|
||||
devm_add_action(smmu->dev, arm_smmu_cmdq_free_bitmap, bitmap);
|
||||
}
|
||||
cmdq->valid_map = (atomic_long_t *)devm_bitmap_zalloc(smmu->dev, nents,
|
||||
GFP_KERNEL);
|
||||
if (!cmdq->valid_map)
|
||||
return -ENOMEM;
|
||||
|
||||
return ret;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int arm_smmu_init_queues(struct arm_smmu_device *smmu)
|
||||
@@ -2981,10 +2972,10 @@ static int arm_smmu_init_l1_strtab(struct arm_smmu_device *smmu)
|
||||
{
|
||||
unsigned int i;
|
||||
struct arm_smmu_strtab_cfg *cfg = &smmu->strtab_cfg;
|
||||
size_t size = sizeof(*cfg->l1_desc) * cfg->num_l1_ents;
|
||||
void *strtab = smmu->strtab_cfg.strtab;
|
||||
|
||||
cfg->l1_desc = devm_kzalloc(smmu->dev, size, GFP_KERNEL);
|
||||
cfg->l1_desc = devm_kcalloc(smmu->dev, cfg->num_l1_ents,
|
||||
sizeof(*cfg->l1_desc), GFP_KERNEL);
|
||||
if (!cfg->l1_desc)
|
||||
return -ENOMEM;
|
||||
|
||||
|
||||
@@ -807,7 +807,7 @@ static int arm_smmu_init_domain_context(struct iommu_domain *domain,
|
||||
* Request context fault interrupt. Do this last to avoid the
|
||||
* handler seeing a half-initialised domain state.
|
||||
*/
|
||||
irq = smmu->irqs[smmu->num_global_irqs + cfg->irptndx];
|
||||
irq = smmu->irqs[cfg->irptndx];
|
||||
|
||||
if (smmu->impl && smmu->impl->context_fault)
|
||||
context_fault = smmu->impl->context_fault;
|
||||
@@ -858,7 +858,7 @@ static void arm_smmu_destroy_domain_context(struct iommu_domain *domain)
|
||||
arm_smmu_write_context_bank(smmu, cfg->cbndx);
|
||||
|
||||
if (cfg->irptndx != ARM_SMMU_INVALID_IRPTNDX) {
|
||||
irq = smmu->irqs[smmu->num_global_irqs + cfg->irptndx];
|
||||
irq = smmu->irqs[cfg->irptndx];
|
||||
devm_free_irq(smmu->dev, irq, domain);
|
||||
}
|
||||
|
||||
@@ -1583,25 +1583,27 @@ static int arm_smmu_def_domain_type(struct device *dev)
|
||||
static struct iommu_ops arm_smmu_ops = {
|
||||
.capable = arm_smmu_capable,
|
||||
.domain_alloc = arm_smmu_domain_alloc,
|
||||
.domain_free = arm_smmu_domain_free,
|
||||
.attach_dev = arm_smmu_attach_dev,
|
||||
.map_pages = arm_smmu_map_pages,
|
||||
.unmap_pages = arm_smmu_unmap_pages,
|
||||
.flush_iotlb_all = arm_smmu_flush_iotlb_all,
|
||||
.iotlb_sync = arm_smmu_iotlb_sync,
|
||||
.iova_to_phys = arm_smmu_iova_to_phys,
|
||||
.probe_device = arm_smmu_probe_device,
|
||||
.release_device = arm_smmu_release_device,
|
||||
.probe_finalize = arm_smmu_probe_finalize,
|
||||
.device_group = arm_smmu_device_group,
|
||||
.enable_nesting = arm_smmu_enable_nesting,
|
||||
.set_pgtable_quirks = arm_smmu_set_pgtable_quirks,
|
||||
.of_xlate = arm_smmu_of_xlate,
|
||||
.get_resv_regions = arm_smmu_get_resv_regions,
|
||||
.put_resv_regions = generic_iommu_put_resv_regions,
|
||||
.def_domain_type = arm_smmu_def_domain_type,
|
||||
.pgsize_bitmap = -1UL, /* Restricted during device attach */
|
||||
.owner = THIS_MODULE,
|
||||
.default_domain_ops = &(const struct iommu_domain_ops) {
|
||||
.attach_dev = arm_smmu_attach_dev,
|
||||
.map_pages = arm_smmu_map_pages,
|
||||
.unmap_pages = arm_smmu_unmap_pages,
|
||||
.flush_iotlb_all = arm_smmu_flush_iotlb_all,
|
||||
.iotlb_sync = arm_smmu_iotlb_sync,
|
||||
.iova_to_phys = arm_smmu_iova_to_phys,
|
||||
.enable_nesting = arm_smmu_enable_nesting,
|
||||
.set_pgtable_quirks = arm_smmu_set_pgtable_quirks,
|
||||
.free = arm_smmu_domain_free,
|
||||
}
|
||||
};
|
||||
|
||||
static void arm_smmu_device_reset(struct arm_smmu_device *smmu)
|
||||
@@ -1951,8 +1953,8 @@ static int acpi_smmu_get_data(u32 model, struct arm_smmu_device *smmu)
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int arm_smmu_device_acpi_probe(struct platform_device *pdev,
|
||||
struct arm_smmu_device *smmu)
|
||||
static int arm_smmu_device_acpi_probe(struct arm_smmu_device *smmu,
|
||||
u32 *global_irqs, u32 *pmu_irqs)
|
||||
{
|
||||
struct device *dev = smmu->dev;
|
||||
struct acpi_iort_node *node =
|
||||
@@ -1968,7 +1970,8 @@ static int arm_smmu_device_acpi_probe(struct platform_device *pdev,
|
||||
return ret;
|
||||
|
||||
/* Ignore the configuration access interrupt */
|
||||
smmu->num_global_irqs = 1;
|
||||
*global_irqs = 1;
|
||||
*pmu_irqs = 0;
|
||||
|
||||
if (iort_smmu->flags & ACPI_IORT_SMMU_COHERENT_WALK)
|
||||
smmu->features |= ARM_SMMU_FEAT_COHERENT_WALK;
|
||||
@@ -1976,25 +1979,24 @@ static int arm_smmu_device_acpi_probe(struct platform_device *pdev,
|
||||
return 0;
|
||||
}
|
||||
#else
|
||||
static inline int arm_smmu_device_acpi_probe(struct platform_device *pdev,
|
||||
struct arm_smmu_device *smmu)
|
||||
static inline int arm_smmu_device_acpi_probe(struct arm_smmu_device *smmu,
|
||||
u32 *global_irqs, u32 *pmu_irqs)
|
||||
{
|
||||
return -ENODEV;
|
||||
}
|
||||
#endif
|
||||
|
||||
static int arm_smmu_device_dt_probe(struct platform_device *pdev,
|
||||
struct arm_smmu_device *smmu)
|
||||
static int arm_smmu_device_dt_probe(struct arm_smmu_device *smmu,
|
||||
u32 *global_irqs, u32 *pmu_irqs)
|
||||
{
|
||||
const struct arm_smmu_match_data *data;
|
||||
struct device *dev = &pdev->dev;
|
||||
struct device *dev = smmu->dev;
|
||||
bool legacy_binding;
|
||||
|
||||
if (of_property_read_u32(dev->of_node, "#global-interrupts",
|
||||
&smmu->num_global_irqs)) {
|
||||
dev_err(dev, "missing #global-interrupts property\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
if (of_property_read_u32(dev->of_node, "#global-interrupts", global_irqs))
|
||||
return dev_err_probe(dev, -ENODEV,
|
||||
"missing #global-interrupts property\n");
|
||||
*pmu_irqs = 0;
|
||||
|
||||
data = of_device_get_match_data(dev);
|
||||
smmu->version = data->version;
|
||||
@@ -2073,6 +2075,7 @@ static int arm_smmu_device_probe(struct platform_device *pdev)
|
||||
struct arm_smmu_device *smmu;
|
||||
struct device *dev = &pdev->dev;
|
||||
int num_irqs, i, err;
|
||||
u32 global_irqs, pmu_irqs;
|
||||
irqreturn_t (*global_fault)(int irq, void *dev);
|
||||
|
||||
smmu = devm_kzalloc(dev, sizeof(*smmu), GFP_KERNEL);
|
||||
@@ -2083,10 +2086,9 @@ static int arm_smmu_device_probe(struct platform_device *pdev)
|
||||
smmu->dev = dev;
|
||||
|
||||
if (dev->of_node)
|
||||
err = arm_smmu_device_dt_probe(pdev, smmu);
|
||||
err = arm_smmu_device_dt_probe(smmu, &global_irqs, &pmu_irqs);
|
||||
else
|
||||
err = arm_smmu_device_acpi_probe(pdev, smmu);
|
||||
|
||||
err = arm_smmu_device_acpi_probe(smmu, &global_irqs, &pmu_irqs);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
@@ -2105,31 +2107,25 @@ static int arm_smmu_device_probe(struct platform_device *pdev)
|
||||
if (IS_ERR(smmu))
|
||||
return PTR_ERR(smmu);
|
||||
|
||||
num_irqs = 0;
|
||||
while ((res = platform_get_resource(pdev, IORESOURCE_IRQ, num_irqs))) {
|
||||
num_irqs++;
|
||||
if (num_irqs > smmu->num_global_irqs)
|
||||
smmu->num_context_irqs++;
|
||||
}
|
||||
num_irqs = platform_irq_count(pdev);
|
||||
|
||||
if (!smmu->num_context_irqs) {
|
||||
dev_err(dev, "found %d interrupts but expected at least %d\n",
|
||||
num_irqs, smmu->num_global_irqs + 1);
|
||||
return -ENODEV;
|
||||
}
|
||||
smmu->num_context_irqs = num_irqs - global_irqs - pmu_irqs;
|
||||
if (smmu->num_context_irqs <= 0)
|
||||
return dev_err_probe(dev, -ENODEV,
|
||||
"found %d interrupts but expected at least %d\n",
|
||||
num_irqs, global_irqs + pmu_irqs + 1);
|
||||
|
||||
smmu->irqs = devm_kcalloc(dev, num_irqs, sizeof(*smmu->irqs),
|
||||
GFP_KERNEL);
|
||||
if (!smmu->irqs) {
|
||||
dev_err(dev, "failed to allocate %d irqs\n", num_irqs);
|
||||
return -ENOMEM;
|
||||
}
|
||||
smmu->irqs = devm_kcalloc(dev, smmu->num_context_irqs,
|
||||
sizeof(*smmu->irqs), GFP_KERNEL);
|
||||
if (!smmu->irqs)
|
||||
return dev_err_probe(dev, -ENOMEM, "failed to allocate %d irqs\n",
|
||||
smmu->num_context_irqs);
|
||||
|
||||
for (i = 0; i < num_irqs; ++i) {
|
||||
int irq = platform_get_irq(pdev, i);
|
||||
for (i = 0; i < smmu->num_context_irqs; i++) {
|
||||
int irq = platform_get_irq(pdev, global_irqs + pmu_irqs + i);
|
||||
|
||||
if (irq < 0)
|
||||
return -ENODEV;
|
||||
return irq;
|
||||
smmu->irqs[i] = irq;
|
||||
}
|
||||
|
||||
@@ -2165,17 +2161,18 @@ static int arm_smmu_device_probe(struct platform_device *pdev)
|
||||
else
|
||||
global_fault = arm_smmu_global_fault;
|
||||
|
||||
for (i = 0; i < smmu->num_global_irqs; ++i) {
|
||||
err = devm_request_irq(smmu->dev, smmu->irqs[i],
|
||||
global_fault,
|
||||
IRQF_SHARED,
|
||||
"arm-smmu global fault",
|
||||
smmu);
|
||||
if (err) {
|
||||
dev_err(dev, "failed to request global IRQ %d (%u)\n",
|
||||
i, smmu->irqs[i]);
|
||||
return err;
|
||||
}
|
||||
for (i = 0; i < global_irqs; i++) {
|
||||
int irq = platform_get_irq(pdev, i);
|
||||
|
||||
if (irq < 0)
|
||||
return irq;
|
||||
|
||||
err = devm_request_irq(dev, irq, global_fault, IRQF_SHARED,
|
||||
"arm-smmu global fault", smmu);
|
||||
if (err)
|
||||
return dev_err_probe(dev, err,
|
||||
"failed to request global IRQ %d (%u)\n",
|
||||
i, irq);
|
||||
}
|
||||
|
||||
err = iommu_device_sysfs_add(&smmu->iommu, smmu->dev, NULL,
|
||||
|
||||
@@ -318,11 +318,10 @@ struct arm_smmu_device {
|
||||
unsigned long pa_size;
|
||||
unsigned long pgsize_bitmap;
|
||||
|
||||
u32 num_global_irqs;
|
||||
u32 num_context_irqs;
|
||||
int num_context_irqs;
|
||||
int num_clks;
|
||||
unsigned int *irqs;
|
||||
struct clk_bulk_data *clks;
|
||||
int num_clks;
|
||||
|
||||
spinlock_t global_sync_lock;
|
||||
|
||||
|
||||
@@ -590,19 +590,21 @@ static int qcom_iommu_of_xlate(struct device *dev, struct of_phandle_args *args)
|
||||
static const struct iommu_ops qcom_iommu_ops = {
|
||||
.capable = qcom_iommu_capable,
|
||||
.domain_alloc = qcom_iommu_domain_alloc,
|
||||
.domain_free = qcom_iommu_domain_free,
|
||||
.attach_dev = qcom_iommu_attach_dev,
|
||||
.detach_dev = qcom_iommu_detach_dev,
|
||||
.map = qcom_iommu_map,
|
||||
.unmap = qcom_iommu_unmap,
|
||||
.flush_iotlb_all = qcom_iommu_flush_iotlb_all,
|
||||
.iotlb_sync = qcom_iommu_iotlb_sync,
|
||||
.iova_to_phys = qcom_iommu_iova_to_phys,
|
||||
.probe_device = qcom_iommu_probe_device,
|
||||
.release_device = qcom_iommu_release_device,
|
||||
.device_group = generic_device_group,
|
||||
.of_xlate = qcom_iommu_of_xlate,
|
||||
.pgsize_bitmap = SZ_4K | SZ_64K | SZ_1M | SZ_16M,
|
||||
.default_domain_ops = &(const struct iommu_domain_ops) {
|
||||
.attach_dev = qcom_iommu_attach_dev,
|
||||
.detach_dev = qcom_iommu_detach_dev,
|
||||
.map = qcom_iommu_map,
|
||||
.unmap = qcom_iommu_unmap,
|
||||
.flush_iotlb_all = qcom_iommu_flush_iotlb_all,
|
||||
.iotlb_sync = qcom_iommu_iotlb_sync,
|
||||
.iova_to_phys = qcom_iommu_iova_to_phys,
|
||||
.free = qcom_iommu_domain_free,
|
||||
}
|
||||
};
|
||||
|
||||
static int qcom_iommu_sec_ptbl_init(struct device *dev)
|
||||
@@ -827,20 +829,20 @@ static int qcom_iommu_device_probe(struct platform_device *pdev)
|
||||
ret = devm_of_platform_populate(dev);
|
||||
if (ret) {
|
||||
dev_err(dev, "Failed to populate iommu contexts\n");
|
||||
return ret;
|
||||
goto err_pm_disable;
|
||||
}
|
||||
|
||||
ret = iommu_device_sysfs_add(&qcom_iommu->iommu, dev, NULL,
|
||||
dev_name(dev));
|
||||
if (ret) {
|
||||
dev_err(dev, "Failed to register iommu in sysfs\n");
|
||||
return ret;
|
||||
goto err_pm_disable;
|
||||
}
|
||||
|
||||
ret = iommu_device_register(&qcom_iommu->iommu, &qcom_iommu_ops, dev);
|
||||
if (ret) {
|
||||
dev_err(dev, "Failed to register iommu\n");
|
||||
return ret;
|
||||
goto err_pm_disable;
|
||||
}
|
||||
|
||||
bus_set_iommu(&platform_bus_type, &qcom_iommu_ops);
|
||||
@@ -852,6 +854,10 @@ static int qcom_iommu_device_probe(struct platform_device *pdev)
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
err_pm_disable:
|
||||
pm_runtime_disable(dev);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int qcom_iommu_device_remove(struct platform_device *pdev)
|
||||
|
||||
@@ -525,6 +525,7 @@ static int iommu_dma_init_domain(struct iommu_domain *domain, dma_addr_t base,
|
||||
struct iommu_dma_cookie *cookie = domain->iova_cookie;
|
||||
unsigned long order, base_pfn;
|
||||
struct iova_domain *iovad;
|
||||
int ret;
|
||||
|
||||
if (!cookie || cookie->type != IOMMU_DMA_IOVA_COOKIE)
|
||||
return -EINVAL;
|
||||
@@ -559,6 +560,9 @@ static int iommu_dma_init_domain(struct iommu_domain *domain, dma_addr_t base,
|
||||
}
|
||||
|
||||
init_iova_domain(iovad, 1UL << order, base_pfn);
|
||||
ret = iova_domain_init_rcaches(iovad);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* If the FQ fails we can simply fall back to strict mode */
|
||||
if (domain->type == IOMMU_DOMAIN_DMA_FQ && iommu_dma_init_fq(domain))
|
||||
|
||||
@@ -1309,17 +1309,19 @@ static int exynos_iommu_of_xlate(struct device *dev,
|
||||
|
||||
static const struct iommu_ops exynos_iommu_ops = {
|
||||
.domain_alloc = exynos_iommu_domain_alloc,
|
||||
.domain_free = exynos_iommu_domain_free,
|
||||
.attach_dev = exynos_iommu_attach_device,
|
||||
.detach_dev = exynos_iommu_detach_device,
|
||||
.map = exynos_iommu_map,
|
||||
.unmap = exynos_iommu_unmap,
|
||||
.iova_to_phys = exynos_iommu_iova_to_phys,
|
||||
.device_group = generic_device_group,
|
||||
.probe_device = exynos_iommu_probe_device,
|
||||
.release_device = exynos_iommu_release_device,
|
||||
.pgsize_bitmap = SECT_SIZE | LPAGE_SIZE | SPAGE_SIZE,
|
||||
.of_xlate = exynos_iommu_of_xlate,
|
||||
.default_domain_ops = &(const struct iommu_domain_ops) {
|
||||
.attach_dev = exynos_iommu_attach_device,
|
||||
.detach_dev = exynos_iommu_detach_device,
|
||||
.map = exynos_iommu_map,
|
||||
.unmap = exynos_iommu_unmap,
|
||||
.iova_to_phys = exynos_iommu_iova_to_phys,
|
||||
.free = exynos_iommu_domain_free,
|
||||
}
|
||||
};
|
||||
|
||||
static int __init exynos_iommu_init(void)
|
||||
|
||||
@@ -453,13 +453,15 @@ static void fsl_pamu_release_device(struct device *dev)
|
||||
static const struct iommu_ops fsl_pamu_ops = {
|
||||
.capable = fsl_pamu_capable,
|
||||
.domain_alloc = fsl_pamu_domain_alloc,
|
||||
.domain_free = fsl_pamu_domain_free,
|
||||
.attach_dev = fsl_pamu_attach_device,
|
||||
.detach_dev = fsl_pamu_detach_device,
|
||||
.iova_to_phys = fsl_pamu_iova_to_phys,
|
||||
.probe_device = fsl_pamu_probe_device,
|
||||
.release_device = fsl_pamu_release_device,
|
||||
.device_group = fsl_pamu_device_group,
|
||||
.default_domain_ops = &(const struct iommu_domain_ops) {
|
||||
.attach_dev = fsl_pamu_attach_device,
|
||||
.detach_dev = fsl_pamu_detach_device,
|
||||
.iova_to_phys = fsl_pamu_iova_to_phys,
|
||||
.free = fsl_pamu_domain_free,
|
||||
}
|
||||
};
|
||||
|
||||
int __init pamu_domain_init(void)
|
||||
|
||||
@@ -344,15 +344,15 @@ static void pgtable_walk_level(struct seq_file *m, struct dma_pte *pde,
|
||||
|
||||
static int show_device_domain_translation(struct device *dev, void *data)
|
||||
{
|
||||
struct dmar_domain *domain = find_domain(dev);
|
||||
struct device_domain_info *info = dev_iommu_priv_get(dev);
|
||||
struct dmar_domain *domain = info->domain;
|
||||
struct seq_file *m = data;
|
||||
u64 path[6] = { 0 };
|
||||
|
||||
if (!domain)
|
||||
return 0;
|
||||
|
||||
seq_printf(m, "Device %s with pasid %d @0x%llx\n",
|
||||
dev_name(dev), domain->default_pasid,
|
||||
seq_printf(m, "Device %s @0x%llx\n", dev_name(dev),
|
||||
(u64)virt_to_phys(domain->pgd));
|
||||
seq_puts(m, "IOVA_PFN\t\tPML5E\t\t\tPML4E\t\t\tPDPE\t\t\tPDE\t\t\tPTE\n");
|
||||
|
||||
|
||||
@@ -66,8 +66,6 @@ static unsigned long dmar_seq_ids[BITS_TO_LONGS(DMAR_UNITS_SUPPORTED)];
|
||||
static int alloc_iommu(struct dmar_drhd_unit *drhd);
|
||||
static void free_iommu(struct intel_iommu *iommu);
|
||||
|
||||
extern const struct iommu_ops intel_iommu_ops;
|
||||
|
||||
static void dmar_register_drhd_unit(struct dmar_drhd_unit *drhd)
|
||||
{
|
||||
/*
|
||||
@@ -789,7 +787,8 @@ static int __init dmar_acpi_dev_scope_init(void)
|
||||
andd->device_name);
|
||||
continue;
|
||||
}
|
||||
if (acpi_bus_get_device(h, &adev)) {
|
||||
adev = acpi_fetch_acpi_dev(h);
|
||||
if (!adev) {
|
||||
pr_err("Failed to get device for ACPI object %s\n",
|
||||
andd->device_name);
|
||||
continue;
|
||||
|
||||
+133
-866
File diff suppressed because it is too large
Load Diff
+6
-167
@@ -150,7 +150,7 @@ int intel_pasid_alloc_table(struct device *dev)
|
||||
int size;
|
||||
|
||||
might_sleep();
|
||||
info = get_domain_info(dev);
|
||||
info = dev_iommu_priv_get(dev);
|
||||
if (WARN_ON(!info || !dev_is_pci(dev) || info->pasid_table))
|
||||
return -EINVAL;
|
||||
|
||||
@@ -197,7 +197,7 @@ void intel_pasid_free_table(struct device *dev)
|
||||
struct pasid_entry *table;
|
||||
int i, max_pde;
|
||||
|
||||
info = get_domain_info(dev);
|
||||
info = dev_iommu_priv_get(dev);
|
||||
if (!info || !dev_is_pci(dev) || !info->pasid_table)
|
||||
return;
|
||||
|
||||
@@ -223,7 +223,7 @@ struct pasid_table *intel_pasid_get_table(struct device *dev)
|
||||
{
|
||||
struct device_domain_info *info;
|
||||
|
||||
info = get_domain_info(dev);
|
||||
info = dev_iommu_priv_get(dev);
|
||||
if (!info)
|
||||
return NULL;
|
||||
|
||||
@@ -234,7 +234,7 @@ static int intel_pasid_get_dev_max_id(struct device *dev)
|
||||
{
|
||||
struct device_domain_info *info;
|
||||
|
||||
info = get_domain_info(dev);
|
||||
info = dev_iommu_priv_get(dev);
|
||||
if (!info || !info->pasid_table)
|
||||
return 0;
|
||||
|
||||
@@ -254,7 +254,7 @@ static struct pasid_entry *intel_pasid_get_entry(struct device *dev, u32 pasid)
|
||||
return NULL;
|
||||
|
||||
dir = pasid_table->table;
|
||||
info = get_domain_info(dev);
|
||||
info = dev_iommu_priv_get(dev);
|
||||
dir_index = pasid >> PASID_PDE_SHIFT;
|
||||
index = pasid & PASID_PTE_MASK;
|
||||
|
||||
@@ -487,7 +487,7 @@ devtlb_invalidation_with_pasid(struct intel_iommu *iommu,
|
||||
struct device_domain_info *info;
|
||||
u16 sid, qdep, pfsid;
|
||||
|
||||
info = get_domain_info(dev);
|
||||
info = dev_iommu_priv_get(dev);
|
||||
if (!info || !info->ats_enabled)
|
||||
return;
|
||||
|
||||
@@ -762,164 +762,3 @@ int intel_pasid_setup_pass_through(struct intel_iommu *iommu,
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
intel_pasid_setup_bind_data(struct intel_iommu *iommu, struct pasid_entry *pte,
|
||||
struct iommu_gpasid_bind_data_vtd *pasid_data)
|
||||
{
|
||||
/*
|
||||
* Not all guest PASID table entry fields are passed down during bind,
|
||||
* here we only set up the ones that are dependent on guest settings.
|
||||
* Execution related bits such as NXE, SMEP are not supported.
|
||||
* Other fields, such as snoop related, are set based on host needs
|
||||
* regardless of guest settings.
|
||||
*/
|
||||
if (pasid_data->flags & IOMMU_SVA_VTD_GPASID_SRE) {
|
||||
if (!ecap_srs(iommu->ecap)) {
|
||||
pr_err_ratelimited("No supervisor request support on %s\n",
|
||||
iommu->name);
|
||||
return -EINVAL;
|
||||
}
|
||||
pasid_set_sre(pte);
|
||||
/* Enable write protect WP if guest requested */
|
||||
if (pasid_data->flags & IOMMU_SVA_VTD_GPASID_WPE)
|
||||
pasid_set_wpe(pte);
|
||||
}
|
||||
|
||||
if (pasid_data->flags & IOMMU_SVA_VTD_GPASID_EAFE) {
|
||||
if (!ecap_eafs(iommu->ecap)) {
|
||||
pr_err_ratelimited("No extended access flag support on %s\n",
|
||||
iommu->name);
|
||||
return -EINVAL;
|
||||
}
|
||||
pasid_set_eafe(pte);
|
||||
}
|
||||
|
||||
/*
|
||||
* Memory type is only applicable to devices inside processor coherent
|
||||
* domain. Will add MTS support once coherent devices are available.
|
||||
*/
|
||||
if (pasid_data->flags & IOMMU_SVA_VTD_GPASID_MTS_MASK) {
|
||||
pr_warn_ratelimited("No memory type support %s\n",
|
||||
iommu->name);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* intel_pasid_setup_nested() - Set up PASID entry for nested translation.
|
||||
* This could be used for guest shared virtual address. In this case, the
|
||||
* first level page tables are used for GVA-GPA translation in the guest,
|
||||
* second level page tables are used for GPA-HPA translation.
|
||||
*
|
||||
* @iommu: IOMMU which the device belong to
|
||||
* @dev: Device to be set up for translation
|
||||
* @gpgd: FLPTPTR: First Level Page translation pointer in GPA
|
||||
* @pasid: PASID to be programmed in the device PASID table
|
||||
* @pasid_data: Additional PASID info from the guest bind request
|
||||
* @domain: Domain info for setting up second level page tables
|
||||
* @addr_width: Address width of the first level (guest)
|
||||
*/
|
||||
int intel_pasid_setup_nested(struct intel_iommu *iommu, struct device *dev,
|
||||
pgd_t *gpgd, u32 pasid,
|
||||
struct iommu_gpasid_bind_data_vtd *pasid_data,
|
||||
struct dmar_domain *domain, int addr_width)
|
||||
{
|
||||
struct pasid_entry *pte;
|
||||
struct dma_pte *pgd;
|
||||
int ret = 0;
|
||||
u64 pgd_val;
|
||||
int agaw;
|
||||
u16 did;
|
||||
|
||||
if (!ecap_nest(iommu->ecap)) {
|
||||
pr_err_ratelimited("IOMMU: %s: No nested translation support\n",
|
||||
iommu->name);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (!(domain->flags & DOMAIN_FLAG_NESTING_MODE)) {
|
||||
pr_err_ratelimited("Domain is not in nesting mode, %x\n",
|
||||
domain->flags);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
pte = intel_pasid_get_entry(dev, pasid);
|
||||
if (WARN_ON(!pte))
|
||||
return -EINVAL;
|
||||
|
||||
/*
|
||||
* Caller must ensure PASID entry is not in use, i.e. not bind the
|
||||
* same PASID to the same device twice.
|
||||
*/
|
||||
if (pasid_pte_is_present(pte))
|
||||
return -EBUSY;
|
||||
|
||||
pasid_clear_entry(pte);
|
||||
|
||||
/* Sanity checking performed by caller to make sure address
|
||||
* width matching in two dimensions:
|
||||
* 1. CPU vs. IOMMU
|
||||
* 2. Guest vs. Host.
|
||||
*/
|
||||
switch (addr_width) {
|
||||
#ifdef CONFIG_X86
|
||||
case ADDR_WIDTH_5LEVEL:
|
||||
if (!cpu_feature_enabled(X86_FEATURE_LA57) ||
|
||||
!cap_5lp_support(iommu->cap)) {
|
||||
dev_err_ratelimited(dev,
|
||||
"5-level paging not supported\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
pasid_set_flpm(pte, 1);
|
||||
break;
|
||||
#endif
|
||||
case ADDR_WIDTH_4LEVEL:
|
||||
pasid_set_flpm(pte, 0);
|
||||
break;
|
||||
default:
|
||||
dev_err_ratelimited(dev, "Invalid guest address width %d\n",
|
||||
addr_width);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* First level PGD is in GPA, must be supported by the second level */
|
||||
if ((uintptr_t)gpgd > domain->max_addr) {
|
||||
dev_err_ratelimited(dev,
|
||||
"Guest PGD %lx not supported, max %llx\n",
|
||||
(uintptr_t)gpgd, domain->max_addr);
|
||||
return -EINVAL;
|
||||
}
|
||||
pasid_set_flptr(pte, (uintptr_t)gpgd);
|
||||
|
||||
ret = intel_pasid_setup_bind_data(iommu, pte, pasid_data);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* Setup the second level based on the given domain */
|
||||
pgd = domain->pgd;
|
||||
|
||||
agaw = iommu_skip_agaw(domain, iommu, &pgd);
|
||||
if (agaw < 0) {
|
||||
dev_err_ratelimited(dev, "Invalid domain page table\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
pgd_val = virt_to_phys(pgd);
|
||||
pasid_set_slptr(pte, pgd_val);
|
||||
pasid_set_fault_enable(pte);
|
||||
|
||||
did = domain->iommu_did[iommu->seq_id];
|
||||
pasid_set_domain_id(pte, did);
|
||||
|
||||
pasid_set_address_width(pte, agaw);
|
||||
pasid_set_page_snoop(pte, !!ecap_smpwc(iommu->ecap));
|
||||
|
||||
pasid_set_translation_type(pte, PASID_ENTRY_PGTT_NESTED);
|
||||
pasid_set_present(pte);
|
||||
pasid_flush_caches(iommu, pte, pasid, did);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -118,10 +118,6 @@ int intel_pasid_setup_second_level(struct intel_iommu *iommu,
|
||||
int intel_pasid_setup_pass_through(struct intel_iommu *iommu,
|
||||
struct dmar_domain *domain,
|
||||
struct device *dev, u32 pasid);
|
||||
int intel_pasid_setup_nested(struct intel_iommu *iommu,
|
||||
struct device *dev, pgd_t *pgd, u32 pasid,
|
||||
struct iommu_gpasid_bind_data_vtd *pasid_data,
|
||||
struct dmar_domain *domain, int addr_width);
|
||||
void intel_pasid_tear_down_entry(struct intel_iommu *iommu,
|
||||
struct device *dev, u32 pasid,
|
||||
bool fault_ignore);
|
||||
|
||||
+3
-217
@@ -168,11 +168,6 @@ int intel_svm_finish_prq(struct intel_iommu *iommu)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline bool intel_svm_capable(struct intel_iommu *iommu)
|
||||
{
|
||||
return iommu->flags & VTD_FLAG_SVM_CAPABLE;
|
||||
}
|
||||
|
||||
void intel_svm_check(struct intel_iommu *iommu)
|
||||
{
|
||||
if (!pasid_supported(iommu))
|
||||
@@ -200,7 +195,7 @@ static void __flush_svm_range_dev(struct intel_svm *svm,
|
||||
unsigned long address,
|
||||
unsigned long pages, int ih)
|
||||
{
|
||||
struct device_domain_info *info = get_domain_info(sdev->dev);
|
||||
struct device_domain_info *info = dev_iommu_priv_get(sdev->dev);
|
||||
|
||||
if (WARN_ON(!pages))
|
||||
return;
|
||||
@@ -318,193 +313,6 @@ out:
|
||||
return 0;
|
||||
}
|
||||
|
||||
int intel_svm_bind_gpasid(struct iommu_domain *domain, struct device *dev,
|
||||
struct iommu_gpasid_bind_data *data)
|
||||
{
|
||||
struct intel_iommu *iommu = device_to_iommu(dev, NULL, NULL);
|
||||
struct intel_svm_dev *sdev = NULL;
|
||||
struct dmar_domain *dmar_domain;
|
||||
struct device_domain_info *info;
|
||||
struct intel_svm *svm = NULL;
|
||||
unsigned long iflags;
|
||||
int ret = 0;
|
||||
|
||||
if (WARN_ON(!iommu) || !data)
|
||||
return -EINVAL;
|
||||
|
||||
if (data->format != IOMMU_PASID_FORMAT_INTEL_VTD)
|
||||
return -EINVAL;
|
||||
|
||||
/* IOMMU core ensures argsz is more than the start of the union */
|
||||
if (data->argsz < offsetofend(struct iommu_gpasid_bind_data, vendor.vtd))
|
||||
return -EINVAL;
|
||||
|
||||
/* Make sure no undefined flags are used in vendor data */
|
||||
if (data->vendor.vtd.flags & ~(IOMMU_SVA_VTD_GPASID_LAST - 1))
|
||||
return -EINVAL;
|
||||
|
||||
if (!dev_is_pci(dev))
|
||||
return -ENOTSUPP;
|
||||
|
||||
/* VT-d supports devices with full 20 bit PASIDs only */
|
||||
if (pci_max_pasids(to_pci_dev(dev)) != PASID_MAX)
|
||||
return -EINVAL;
|
||||
|
||||
/*
|
||||
* We only check host PASID range, we have no knowledge to check
|
||||
* guest PASID range.
|
||||
*/
|
||||
if (data->hpasid <= 0 || data->hpasid >= PASID_MAX)
|
||||
return -EINVAL;
|
||||
|
||||
info = get_domain_info(dev);
|
||||
if (!info)
|
||||
return -EINVAL;
|
||||
|
||||
dmar_domain = to_dmar_domain(domain);
|
||||
|
||||
mutex_lock(&pasid_mutex);
|
||||
ret = pasid_to_svm_sdev(dev, data->hpasid, &svm, &sdev);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
if (sdev) {
|
||||
/*
|
||||
* Do not allow multiple bindings of the same device-PASID since
|
||||
* there is only one SL page tables per PASID. We may revisit
|
||||
* once sharing PGD across domains are supported.
|
||||
*/
|
||||
dev_warn_ratelimited(dev, "Already bound with PASID %u\n",
|
||||
svm->pasid);
|
||||
ret = -EBUSY;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (!svm) {
|
||||
/* We come here when PASID has never been bond to a device. */
|
||||
svm = kzalloc(sizeof(*svm), GFP_KERNEL);
|
||||
if (!svm) {
|
||||
ret = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
/* REVISIT: upper layer/VFIO can track host process that bind
|
||||
* the PASID. ioasid_set = mm might be sufficient for vfio to
|
||||
* check pasid VMM ownership. We can drop the following line
|
||||
* once VFIO and IOASID set check is in place.
|
||||
*/
|
||||
svm->mm = get_task_mm(current);
|
||||
svm->pasid = data->hpasid;
|
||||
if (data->flags & IOMMU_SVA_GPASID_VAL) {
|
||||
svm->gpasid = data->gpasid;
|
||||
svm->flags |= SVM_FLAG_GUEST_PASID;
|
||||
}
|
||||
pasid_private_add(data->hpasid, svm);
|
||||
INIT_LIST_HEAD_RCU(&svm->devs);
|
||||
mmput(svm->mm);
|
||||
}
|
||||
sdev = kzalloc(sizeof(*sdev), GFP_KERNEL);
|
||||
if (!sdev) {
|
||||
ret = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
sdev->dev = dev;
|
||||
sdev->sid = PCI_DEVID(info->bus, info->devfn);
|
||||
sdev->iommu = iommu;
|
||||
|
||||
/* Only count users if device has aux domains */
|
||||
if (iommu_dev_feature_enabled(dev, IOMMU_DEV_FEAT_AUX))
|
||||
sdev->users = 1;
|
||||
|
||||
/* Set up device context entry for PASID if not enabled already */
|
||||
ret = intel_iommu_enable_pasid(iommu, sdev->dev);
|
||||
if (ret) {
|
||||
dev_err_ratelimited(dev, "Failed to enable PASID capability\n");
|
||||
kfree(sdev);
|
||||
goto out;
|
||||
}
|
||||
|
||||
/*
|
||||
* PASID table is per device for better security. Therefore, for
|
||||
* each bind of a new device even with an existing PASID, we need to
|
||||
* call the nested mode setup function here.
|
||||
*/
|
||||
spin_lock_irqsave(&iommu->lock, iflags);
|
||||
ret = intel_pasid_setup_nested(iommu, dev,
|
||||
(pgd_t *)(uintptr_t)data->gpgd,
|
||||
data->hpasid, &data->vendor.vtd, dmar_domain,
|
||||
data->addr_width);
|
||||
spin_unlock_irqrestore(&iommu->lock, iflags);
|
||||
if (ret) {
|
||||
dev_err_ratelimited(dev, "Failed to set up PASID %llu in nested mode, Err %d\n",
|
||||
data->hpasid, ret);
|
||||
/*
|
||||
* PASID entry should be in cleared state if nested mode
|
||||
* set up failed. So we only need to clear IOASID tracking
|
||||
* data such that free call will succeed.
|
||||
*/
|
||||
kfree(sdev);
|
||||
goto out;
|
||||
}
|
||||
|
||||
svm->flags |= SVM_FLAG_GUEST_MODE;
|
||||
|
||||
init_rcu_head(&sdev->rcu);
|
||||
list_add_rcu(&sdev->list, &svm->devs);
|
||||
out:
|
||||
if (!IS_ERR_OR_NULL(svm) && list_empty(&svm->devs)) {
|
||||
pasid_private_remove(data->hpasid);
|
||||
kfree(svm);
|
||||
}
|
||||
|
||||
mutex_unlock(&pasid_mutex);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int intel_svm_unbind_gpasid(struct device *dev, u32 pasid)
|
||||
{
|
||||
struct intel_iommu *iommu = device_to_iommu(dev, NULL, NULL);
|
||||
struct intel_svm_dev *sdev;
|
||||
struct intel_svm *svm;
|
||||
int ret;
|
||||
|
||||
if (WARN_ON(!iommu))
|
||||
return -EINVAL;
|
||||
|
||||
mutex_lock(&pasid_mutex);
|
||||
ret = pasid_to_svm_sdev(dev, pasid, &svm, &sdev);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
if (sdev) {
|
||||
if (iommu_dev_feature_enabled(dev, IOMMU_DEV_FEAT_AUX))
|
||||
sdev->users--;
|
||||
if (!sdev->users) {
|
||||
list_del_rcu(&sdev->list);
|
||||
intel_pasid_tear_down_entry(iommu, dev,
|
||||
svm->pasid, false);
|
||||
intel_svm_drain_prq(dev, svm->pasid);
|
||||
kfree_rcu(sdev, rcu);
|
||||
|
||||
if (list_empty(&svm->devs)) {
|
||||
/*
|
||||
* We do not free the IOASID here in that
|
||||
* IOMMU driver did not allocate it.
|
||||
* Unlike native SVM, IOASID for guest use was
|
||||
* allocated prior to the bind call.
|
||||
* In any case, if the free call comes before
|
||||
* the unbind, IOMMU driver will get notified
|
||||
* and perform cleanup.
|
||||
*/
|
||||
pasid_private_remove(pasid);
|
||||
kfree(svm);
|
||||
}
|
||||
}
|
||||
}
|
||||
out:
|
||||
mutex_unlock(&pasid_mutex);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int intel_svm_alloc_pasid(struct device *dev, struct mm_struct *mm,
|
||||
unsigned int flags)
|
||||
{
|
||||
@@ -524,7 +332,7 @@ static struct iommu_sva *intel_svm_bind_mm(struct intel_iommu *iommu,
|
||||
struct mm_struct *mm,
|
||||
unsigned int flags)
|
||||
{
|
||||
struct device_domain_info *info = get_domain_info(dev);
|
||||
struct device_domain_info *info = dev_iommu_priv_get(dev);
|
||||
unsigned long iflags, sflags;
|
||||
struct intel_svm_dev *sdev;
|
||||
struct intel_svm *svm;
|
||||
@@ -732,7 +540,7 @@ static void intel_svm_drain_prq(struct device *dev, u32 pasid)
|
||||
u16 sid, did;
|
||||
int qdep;
|
||||
|
||||
info = get_domain_info(dev);
|
||||
info = dev_iommu_priv_get(dev);
|
||||
if (WARN_ON(!info || !dev_is_pci(dev)))
|
||||
return;
|
||||
|
||||
@@ -1125,28 +933,6 @@ int intel_svm_page_response(struct device *dev,
|
||||
goto out;
|
||||
}
|
||||
|
||||
/*
|
||||
* For responses from userspace, need to make sure that the
|
||||
* pasid has been bound to its mm.
|
||||
*/
|
||||
if (svm->flags & SVM_FLAG_GUEST_MODE) {
|
||||
struct mm_struct *mm;
|
||||
|
||||
mm = get_task_mm(current);
|
||||
if (!mm) {
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (mm != svm->mm) {
|
||||
ret = -ENODEV;
|
||||
mmput(mm);
|
||||
goto out;
|
||||
}
|
||||
|
||||
mmput(mm);
|
||||
}
|
||||
|
||||
/*
|
||||
* Per VT-d spec. v3.0 ch7.7, system software must respond
|
||||
* with page group response if private data is present (PDP)
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user