ANDROID: KVM: arm64: iommu: Create parent/child relation
In preparation for adding new IOMMU devices that act as suppliers to others, add the notion of a parent IOMMU device. Such device must be registered after its parent and the driver of the parent device must validate the addition. The relation has no generic implications, it is up to drivers to make use of it. Bug: 190463801 Signed-off-by: David Brazdil <dbrazdil@google.com> Change-Id: I4ee3675e5529bb73ad4546fa32380f237f054177
This commit is contained in:
@@ -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_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,
|
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_suspend(struct device *dev);
|
||||||
int pkvm_iommu_resume(struct device *dev);
|
int pkvm_iommu_resume(struct device *dev);
|
||||||
|
|
||||||
|
|||||||
@@ -26,6 +26,12 @@ struct pkvm_iommu_ops {
|
|||||||
*/
|
*/
|
||||||
int (*validate)(struct pkvm_iommu *dev);
|
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.
|
* Callback to apply a host stage-2 mapping change at driver level.
|
||||||
* Called before 'host_stage2_idmap_apply' with host lock held.
|
* Called before 'host_stage2_idmap_apply' with host lock held.
|
||||||
@@ -57,7 +63,10 @@ struct pkvm_iommu_ops {
|
|||||||
};
|
};
|
||||||
|
|
||||||
struct pkvm_iommu {
|
struct pkvm_iommu {
|
||||||
|
struct pkvm_iommu *parent;
|
||||||
struct list_head list;
|
struct list_head list;
|
||||||
|
struct list_head siblings;
|
||||||
|
struct list_head children;
|
||||||
unsigned long id;
|
unsigned long id;
|
||||||
const struct pkvm_iommu_ops *ops;
|
const struct pkvm_iommu_ops *ops;
|
||||||
phys_addr_t pa;
|
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,
|
int __pkvm_iommu_register(unsigned long dev_id,
|
||||||
enum pkvm_iommu_driver_id drv_id,
|
enum pkvm_iommu_driver_id drv_id,
|
||||||
phys_addr_t dev_pa, size_t dev_size,
|
phys_addr_t dev_pa, size_t dev_size,
|
||||||
|
unsigned long parent_id,
|
||||||
void *kern_mem_va, size_t mem_size);
|
void *kern_mem_va, size_t mem_size);
|
||||||
int __pkvm_iommu_pm_notify(unsigned long dev_id,
|
int __pkvm_iommu_pm_notify(unsigned long dev_id,
|
||||||
enum pkvm_iommu_pm_event event);
|
enum pkvm_iommu_pm_event event);
|
||||||
|
|||||||
@@ -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(enum pkvm_iommu_driver_id, drv_id, host_ctxt, 2);
|
||||||
DECLARE_REG(phys_addr_t, dev_pa, host_ctxt, 3);
|
DECLARE_REG(phys_addr_t, dev_pa, host_ctxt, 3);
|
||||||
DECLARE_REG(size_t, dev_size, host_ctxt, 4);
|
DECLARE_REG(size_t, dev_size, host_ctxt, 4);
|
||||||
DECLARE_REG(void *, mem, host_ctxt, 5);
|
DECLARE_REG(unsigned long, parent_id, host_ctxt, 5);
|
||||||
DECLARE_REG(size_t, mem_size, host_ctxt, 6);
|
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,
|
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)
|
static void handle___pkvm_iommu_pm_notify(struct kvm_cpu_context *host_ctxt)
|
||||||
|
|||||||
@@ -289,6 +289,7 @@ out:
|
|||||||
int __pkvm_iommu_register(unsigned long dev_id,
|
int __pkvm_iommu_register(unsigned long dev_id,
|
||||||
enum pkvm_iommu_driver_id drv_id,
|
enum pkvm_iommu_driver_id drv_id,
|
||||||
phys_addr_t dev_pa, size_t dev_size,
|
phys_addr_t dev_pa, size_t dev_size,
|
||||||
|
unsigned long parent_id,
|
||||||
void *kern_mem_va, size_t mem_size)
|
void *kern_mem_va, size_t mem_size)
|
||||||
{
|
{
|
||||||
struct pkvm_iommu *dev = NULL;
|
struct pkvm_iommu *dev = NULL;
|
||||||
@@ -333,6 +334,7 @@ int __pkvm_iommu_register(unsigned long dev_id,
|
|||||||
|
|
||||||
/* Populate the new device entry. */
|
/* Populate the new device entry. */
|
||||||
*dev = (struct pkvm_iommu){
|
*dev = (struct pkvm_iommu){
|
||||||
|
.children = LIST_HEAD_INIT(dev->children),
|
||||||
.id = dev_id,
|
.id = dev_id,
|
||||||
.ops = drv->ops,
|
.ops = drv->ops,
|
||||||
.pa = dev_pa,
|
.pa = dev_pa,
|
||||||
@@ -344,6 +346,20 @@ int __pkvm_iommu_register(unsigned long dev_id,
|
|||||||
goto out;
|
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) {
|
if (dev->ops->validate) {
|
||||||
ret = dev->ops->validate(dev);
|
ret = dev->ops->validate(dev);
|
||||||
if (ret)
|
if (ret)
|
||||||
@@ -369,6 +385,8 @@ int __pkvm_iommu_register(unsigned long dev_id,
|
|||||||
|
|
||||||
/* Register device and prevent host from mapping the MMIO range. */
|
/* Register device and prevent host from mapping the MMIO range. */
|
||||||
list_add_tail(&dev->list, &iommu_list);
|
list_add_tail(&dev->list, &iommu_list);
|
||||||
|
if (dev->parent)
|
||||||
|
list_add_tail(&dev->siblings, &dev->parent->children);
|
||||||
|
|
||||||
out:
|
out:
|
||||||
if (ret)
|
if (ret)
|
||||||
|
|||||||
@@ -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,
|
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;
|
void *mem;
|
||||||
int ret;
|
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.
|
* We assume that hyp never allocates more than a page per hypcall.
|
||||||
*/
|
*/
|
||||||
ret = kvm_call_hyp_nvhe(__pkvm_iommu_register, dev_to_id(dev),
|
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) {
|
if (ret == -ENOMEM) {
|
||||||
mem = (void *)__get_free_page(GFP_KERNEL);
|
mem = (void *)__get_free_page(GFP_KERNEL);
|
||||||
if (!mem)
|
if (!mem)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
ret = kvm_call_hyp_nvhe(__pkvm_iommu_register, dev_to_id(dev),
|
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;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -81,6 +81,6 @@ int pkvm_iommu_s2mpu_register(struct device *dev, phys_addr_t addr)
|
|||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
return pkvm_iommu_register(dev, PKVM_IOMMU_DRIVER_S2MPU,
|
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);
|
EXPORT_SYMBOL_GPL(pkvm_iommu_s2mpu_register);
|
||||||
|
|||||||
Reference in New Issue
Block a user