ANDROID: KVM: arm64: iommu: Avoid mapping devices in host stage-2

Introduce a linked list of IOMMU devices and
'pkvm_iommu_host_stage2_adjust_range' called from host DABT handler.
The function will adjust the memory range that is about to be mapped
to avoid MMIO regions of all devices in the linked list. If the host
tried to access a device MMIO region, the access is declined.

The function replaces the existing call to
'kvm_iommu.ops.host_stage2_adjust_mmio_range' callback.

Bug: 190463801
Signed-off-by: David Brazdil <dbrazdil@google.com>
Change-Id: Ib38256f0005588810a4400efd9a85380d354be59
This commit is contained in:
David Brazdil
2022-02-14 18:13:49 +00:00
parent 1d9ae14c92
commit d7adab5f9f
3 changed files with 55 additions and 6 deletions

View File

@@ -18,7 +18,15 @@ struct pkvm_iommu_ops {
int (*init)(void *data, size_t size);
};
struct pkvm_iommu {
struct list_head list;
phys_addr_t pa;
size_t size;
};
int __pkvm_iommu_driver_init(enum pkvm_iommu_driver_id id, void *data, size_t size);
int pkvm_iommu_host_stage2_adjust_range(phys_addr_t addr, phys_addr_t *start,
phys_addr_t *end);
struct kvm_iommu_ops {
int (*init)(void);

View File

@@ -25,6 +25,14 @@ struct pkvm_iommu_driver {
static struct pkvm_iommu_driver iommu_drivers[PKVM_IOMMU_NR_DRIVERS];
/* IOMMU device list. Must only be accessed with host_kvm.lock held. */
static LIST_HEAD(iommu_list);
static void assert_host_component_locked(void)
{
hyp_assert_lock_held(&host_kvm.lock);
}
/*
* Find IOMMU driver by its ID. The input ID is treated as unstrusted
* and is properly validated.
@@ -101,3 +109,36 @@ out:
driver_release_init(drv, /*success=*/!ret);
return ret;
}
/*
* Check host memory access against IOMMUs' MMIO regions.
* Returns -EPERM if the address is within the bounds of a registered device.
* Otherwise returns zero and adjusts boundaries of the new mapping to avoid
* MMIO regions of registered IOMMUs.
*/
int pkvm_iommu_host_stage2_adjust_range(phys_addr_t addr, phys_addr_t *start,
phys_addr_t *end)
{
struct pkvm_iommu *dev;
phys_addr_t new_start = *start;
phys_addr_t new_end = *end;
phys_addr_t dev_start, dev_end;
assert_host_component_locked();
list_for_each_entry(dev, &iommu_list, list) {
dev_start = dev->pa;
dev_end = dev_start + dev->size;
if (addr < dev_start)
new_end = min(new_end, dev_start);
else if (addr >= dev_end)
new_start = max(new_start, dev_end);
else
return -EPERM;
}
*start = new_start;
*end = new_end;
return 0;
}

View File

@@ -562,13 +562,13 @@ static int host_stage2_idmap(u64 addr)
prot = is_memory ? PKVM_HOST_MEM_PROT : PKVM_HOST_MMIO_PROT;
/**
* Let device drivers adjust the permitted range first.
* host_stage2_adjust_range() should be last to also properly align it.
/*
* Adjust against IOMMU devices first. host_stage2_adjust_range() should
* be called last for proper alignment.
*/
if (!is_memory && kvm_iommu_ops.host_stage2_adjust_mmio_range) {
ret = kvm_iommu_ops.host_stage2_adjust_mmio_range(addr, &range.start,
&range.end);
if (!is_memory) {
ret = pkvm_iommu_host_stage2_adjust_range(addr, &range.start,
&range.end);
if (ret)
return ret;
}