diff --git a/arch/arm64/include/asm/kvm_host.h b/arch/arm64/include/asm/kvm_host.h index 2f25c79af39a..bcdd9ae44d92 100644 --- a/arch/arm64/include/asm/kvm_host.h +++ b/arch/arm64/include/asm/kvm_host.h @@ -385,7 +385,7 @@ enum pkvm_iommu_pm_event { int pkvm_iommu_driver_init(enum pkvm_iommu_driver_id drv_id, void *data, size_t size); int pkvm_iommu_register(struct device *dev, enum pkvm_iommu_driver_id drv_id, - phys_addr_t pa, size_t size); + phys_addr_t pa, size_t size, struct device *parent); int pkvm_iommu_suspend(struct device *dev); int pkvm_iommu_resume(struct device *dev); diff --git a/arch/arm64/kvm/hyp/include/nvhe/iommu.h b/arch/arm64/kvm/hyp/include/nvhe/iommu.h index 70d9c9e67991..e9683d314938 100644 --- a/arch/arm64/kvm/hyp/include/nvhe/iommu.h +++ b/arch/arm64/kvm/hyp/include/nvhe/iommu.h @@ -26,6 +26,12 @@ struct pkvm_iommu_ops { */ int (*validate)(struct pkvm_iommu *dev); + /* + * Validation of a new child device that is being register by + * the parent device the child selected. Called with the host lock held. + */ + int (*validate_child)(struct pkvm_iommu *dev, struct pkvm_iommu *child); + /* * Callback to apply a host stage-2 mapping change at driver level. * Called before 'host_stage2_idmap_apply' with host lock held. @@ -57,7 +63,10 @@ struct pkvm_iommu_ops { }; struct pkvm_iommu { + struct pkvm_iommu *parent; struct list_head list; + struct list_head siblings; + struct list_head children; unsigned long id; const struct pkvm_iommu_ops *ops; phys_addr_t pa; @@ -71,6 +80,7 @@ int __pkvm_iommu_driver_init(enum pkvm_iommu_driver_id id, void *data, size_t si int __pkvm_iommu_register(unsigned long dev_id, enum pkvm_iommu_driver_id drv_id, phys_addr_t dev_pa, size_t dev_size, + unsigned long parent_id, void *kern_mem_va, size_t mem_size); int __pkvm_iommu_pm_notify(unsigned long dev_id, enum pkvm_iommu_pm_event event); diff --git a/arch/arm64/kvm/hyp/nvhe/hyp-main.c b/arch/arm64/kvm/hyp/nvhe/hyp-main.c index fae3dd83bbdf..30b3af4b1d25 100644 --- a/arch/arm64/kvm/hyp/nvhe/hyp-main.c +++ b/arch/arm64/kvm/hyp/nvhe/hyp-main.c @@ -991,11 +991,13 @@ static void handle___pkvm_iommu_register(struct kvm_cpu_context *host_ctxt) DECLARE_REG(enum pkvm_iommu_driver_id, drv_id, host_ctxt, 2); DECLARE_REG(phys_addr_t, dev_pa, host_ctxt, 3); DECLARE_REG(size_t, dev_size, host_ctxt, 4); - DECLARE_REG(void *, mem, host_ctxt, 5); - DECLARE_REG(size_t, mem_size, host_ctxt, 6); + DECLARE_REG(unsigned long, parent_id, host_ctxt, 5); + DECLARE_REG(void *, mem, host_ctxt, 6); + DECLARE_REG(size_t, mem_size, host_ctxt, 7); cpu_reg(host_ctxt, 1) = __pkvm_iommu_register(dev_id, drv_id, dev_pa, - dev_size, mem, mem_size); + dev_size, parent_id, + mem, mem_size); } static void handle___pkvm_iommu_pm_notify(struct kvm_cpu_context *host_ctxt) diff --git a/arch/arm64/kvm/hyp/nvhe/iommu.c b/arch/arm64/kvm/hyp/nvhe/iommu.c index 8b84966c55cc..4b77aba72b65 100644 --- a/arch/arm64/kvm/hyp/nvhe/iommu.c +++ b/arch/arm64/kvm/hyp/nvhe/iommu.c @@ -289,6 +289,7 @@ out: int __pkvm_iommu_register(unsigned long dev_id, enum pkvm_iommu_driver_id drv_id, phys_addr_t dev_pa, size_t dev_size, + unsigned long parent_id, void *kern_mem_va, size_t mem_size) { struct pkvm_iommu *dev = NULL; @@ -333,6 +334,7 @@ int __pkvm_iommu_register(unsigned long dev_id, /* Populate the new device entry. */ *dev = (struct pkvm_iommu){ + .children = LIST_HEAD_INIT(dev->children), .id = dev_id, .ops = drv->ops, .pa = dev_pa, @@ -344,6 +346,20 @@ int __pkvm_iommu_register(unsigned long dev_id, goto out; } + if (parent_id) { + dev->parent = find_iommu_by_id(parent_id); + if (!dev->parent) { + ret = -EINVAL; + goto out; + } + + if (dev->parent->ops->validate_child) { + ret = dev->parent->ops->validate_child(dev->parent, dev); + if (ret) + goto out; + } + } + if (dev->ops->validate) { ret = dev->ops->validate(dev); if (ret) @@ -369,6 +385,8 @@ int __pkvm_iommu_register(unsigned long dev_id, /* Register device and prevent host from mapping the MMIO range. */ list_add_tail(&dev->list, &iommu_list); + if (dev->parent) + list_add_tail(&dev->siblings, &dev->parent->children); out: if (ret) diff --git a/arch/arm64/kvm/iommu.c b/arch/arm64/kvm/iommu.c index e13d36ec57e7..01176199e08f 100644 --- a/arch/arm64/kvm/iommu.c +++ b/arch/arm64/kvm/iommu.c @@ -18,7 +18,7 @@ int pkvm_iommu_driver_init(enum pkvm_iommu_driver_id id, void *data, size_t size } int pkvm_iommu_register(struct device *dev, enum pkvm_iommu_driver_id drv_id, - phys_addr_t pa, size_t size) + phys_addr_t pa, size_t size, struct device *parent) { void *mem; int ret; @@ -29,14 +29,15 @@ int pkvm_iommu_register(struct device *dev, enum pkvm_iommu_driver_id drv_id, * We assume that hyp never allocates more than a page per hypcall. */ ret = kvm_call_hyp_nvhe(__pkvm_iommu_register, dev_to_id(dev), - drv_id, pa, size, NULL, 0); + drv_id, pa, size, dev_to_id(parent), NULL, 0); if (ret == -ENOMEM) { mem = (void *)__get_free_page(GFP_KERNEL); if (!mem) return -ENOMEM; ret = kvm_call_hyp_nvhe(__pkvm_iommu_register, dev_to_id(dev), - drv_id, pa, size, mem, PAGE_SIZE); + drv_id, pa, size, dev_to_id(parent), + mem, PAGE_SIZE); } return ret; } diff --git a/arch/arm64/kvm/iommu/s2mpu.c b/arch/arm64/kvm/iommu/s2mpu.c index be2b1ad09480..7d989afde0fb 100644 --- a/arch/arm64/kvm/iommu/s2mpu.c +++ b/arch/arm64/kvm/iommu/s2mpu.c @@ -81,6 +81,6 @@ int pkvm_iommu_s2mpu_register(struct device *dev, phys_addr_t addr) return ret; return pkvm_iommu_register(dev, PKVM_IOMMU_DRIVER_S2MPU, - addr, S2MPU_MMIO_SIZE); + addr, S2MPU_MMIO_SIZE, NULL); } EXPORT_SYMBOL_GPL(pkvm_iommu_s2mpu_register);