ANDROID: KVM: arm64: iommu: Suspend/resume callbacks

Add suspend/resume callbacks for IOMMU devices. The EL1 kernel driver
is expected to call these when the IOMMU device is powered on but is
about to be used or about to stop being used.

pkvm_iommu_suspend/resume are exported for use by kernel modules.

Bug: 190463801
Change-Id: Ia4ab37fe96879d451ce82f4278b3ff33a0b9685b
Signed-off-by: David Brazdil <dbrazdil@google.com>
(cherry picked from commit ca47ae70c7)
Signed-off-by: Mostafa Saleh <smostafa@google.com>
This commit is contained in:
David Brazdil
2022-02-14 20:58:22 +00:00
committed by Mostafa Saleh
parent 59feca1938
commit 770ec56a17
6 changed files with 77 additions and 0 deletions

View File

@@ -83,6 +83,7 @@ enum __kvm_host_smccc_func {
__KVM_HOST_SMCCC_FUNC___pkvm_vcpu_sync_state,
__KVM_HOST_SMCCC_FUNC___pkvm_iommu_driver_init,
__KVM_HOST_SMCCC_FUNC___pkvm_iommu_register,
__KVM_HOST_SMCCC_FUNC___pkvm_iommu_pm_notify,
};
#define DECLARE_KVM_VHE_SYM(sym) extern char sym[]

View File

@@ -397,9 +397,16 @@ enum pkvm_iommu_driver_id {
PKVM_IOMMU_NR_DRIVERS,
};
enum pkvm_iommu_pm_event {
PKVM_IOMMU_PM_SUSPEND,
PKVM_IOMMU_PM_RESUME,
};
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);
int pkvm_iommu_suspend(struct device *dev);
int pkvm_iommu_resume(struct device *dev);
struct vcpu_reset_state {
unsigned long pc;

View File

@@ -7,6 +7,8 @@
#include <nvhe/mem_protect.h>
struct pkvm_iommu;
struct pkvm_iommu_ops {
/*
* Global driver initialization called before devices are registered.
@@ -23,6 +25,10 @@ struct pkvm_iommu_ops {
*/
int (*validate)(phys_addr_t base, size_t size);
/* Power management callbacks. Called with host lock held. */
int (*suspend)(struct pkvm_iommu *dev);
int (*resume)(struct pkvm_iommu *dev);
/* Amount of memory allocated per-device for use by the driver. */
size_t data_size;
};
@@ -34,6 +40,7 @@ struct pkvm_iommu {
phys_addr_t pa;
void *va;
size_t size;
bool powered;
char data[];
};
@@ -42,6 +49,8 @@ int __pkvm_iommu_register(unsigned long dev_id,
enum pkvm_iommu_driver_id drv_id,
phys_addr_t dev_pa, size_t dev_size,
void *kern_mem_va, size_t mem_size);
int __pkvm_iommu_pm_notify(unsigned long dev_id,
enum pkvm_iommu_pm_event event);
int pkvm_iommu_host_stage2_adjust_range(phys_addr_t addr, phys_addr_t *start,
phys_addr_t *end);

View File

@@ -1136,6 +1136,14 @@ static void handle___pkvm_iommu_register(struct kvm_cpu_context *host_ctxt)
dev_size, mem, mem_size);
}
static void handle___pkvm_iommu_pm_notify(struct kvm_cpu_context *host_ctxt)
{
DECLARE_REG(unsigned long, dev_id, host_ctxt, 1);
DECLARE_REG(enum pkvm_iommu_pm_event, event, host_ctxt, 2);
cpu_reg(host_ctxt, 1) = __pkvm_iommu_pm_notify(dev_id, event);
}
typedef void (*hcall_t)(struct kvm_cpu_context *);
#define HANDLE_FUNC(x) [__KVM_HOST_SMCCC_FUNC_##x] = (hcall_t)handle_##x
@@ -1172,6 +1180,7 @@ static const hcall_t host_hcall[] = {
HANDLE_FUNC(__pkvm_vcpu_sync_state),
HANDLE_FUNC(__pkvm_iommu_driver_init),
HANDLE_FUNC(__pkvm_iommu_register),
HANDLE_FUNC(__pkvm_iommu_pm_notify),
};
static void handle_host_hcall(struct kvm_cpu_context *host_ctxt)

View File

@@ -166,6 +166,19 @@ static bool validate_against_existing_iommus(struct pkvm_iommu *dev)
return true;
}
static struct pkvm_iommu *find_iommu_by_id(unsigned long id)
{
struct pkvm_iommu *dev;
assert_host_component_locked();
list_for_each_entry(dev, &iommu_list, list) {
if (dev->id == id)
return dev;
}
return NULL;
}
/*
* Initialize EL2 IOMMU driver.
*
@@ -289,6 +302,30 @@ out:
return ret;
}
int __pkvm_iommu_pm_notify(unsigned long dev_id, enum pkvm_iommu_pm_event event)
{
struct pkvm_iommu *dev;
int ret;
host_lock_component();
dev = find_iommu_by_id(dev_id);
if (dev) {
if (event == PKVM_IOMMU_PM_SUSPEND) {
ret = dev->ops->suspend ? dev->ops->suspend(dev) : 0;
dev->powered = !!ret;
} else if (event == PKVM_IOMMU_PM_RESUME) {
ret = dev->ops->resume ? dev->ops->resume(dev) : 0;
dev->powered = !ret;
} else {
ret = -EINVAL;
}
} else {
ret = -ENODEV;
}
host_unlock_component();
return ret;
}
/*
* Check host memory access against IOMMUs' MMIO regions.
* Returns -EPERM if the address is within the bounds of a registered device.

View File

@@ -40,3 +40,17 @@ int pkvm_iommu_register(struct device *dev, enum pkvm_iommu_driver_id drv_id,
}
return ret;
}
int pkvm_iommu_suspend(struct device *dev)
{
return kvm_call_hyp_nvhe(__pkvm_iommu_pm_notify, dev_to_id(dev),
PKVM_IOMMU_PM_SUSPEND);
}
EXPORT_SYMBOL_GPL(pkvm_iommu_suspend);
int pkvm_iommu_resume(struct device *dev)
{
return kvm_call_hyp_nvhe(__pkvm_iommu_pm_notify, dev_to_id(dev),
PKVM_IOMMU_PM_RESUME);
}
EXPORT_SYMBOL_GPL(pkvm_iommu_resume);