From 44f9c1036178abf2720c45b6e1d9f4bdeae35799 Mon Sep 17 00:00:00 2001 From: Mostafa Saleh Date: Mon, 8 Jul 2024 18:25:03 +0000 Subject: [PATCH] ANDROID: KVM: arm64: devices: Add reset handler for devices Many devices must be reset before and after assignment, and as the host can't be trusted to do this, we have to do it from the hypervisor, and as we only deal with MMIO(platform) devices, which has not standard interface, we provide a function to register reset handler to modules, that can be registered with the MMIO base address of the device with pkvm_device_register_reset. Bug: 357781595 Bug: 348382247 Change-Id: Ie700aacf440caa6f1ffee85086a1edecdc104a5a Signed-off-by: Mostafa Saleh --- arch/arm64/include/asm/kvm_pkvm_module.h | 17 +++++++++++ arch/arm64/kvm/hyp/include/nvhe/pkvm.h | 2 ++ arch/arm64/kvm/hyp/nvhe/device/device.c | 38 ++++++++++++++++++++++++ arch/arm64/kvm/hyp/nvhe/modules.c | 1 + include/kvm/device.h | 2 ++ 5 files changed, 60 insertions(+) diff --git a/arch/arm64/include/asm/kvm_pkvm_module.h b/arch/arm64/include/asm/kvm_pkvm_module.h index abe984438180..5d2ad2990458 100644 --- a/arch/arm64/include/asm/kvm_pkvm_module.h +++ b/arch/arm64/include/asm/kvm_pkvm_module.h @@ -11,6 +11,7 @@ typedef void (*dyn_hcall_t)(struct user_pt_regs *); struct kvm_hyp_iommu; struct iommu_iotlb_gather; struct kvm_hyp_iommu_domain; +struct pkvm_device; #ifdef CONFIG_MODULES enum pkvm_psci_notification { @@ -169,6 +170,20 @@ enum pkvm_psci_notification { * @iommu_donate_pages_atomic: Allocate memory from IOMMU identity pool. * @iommu_reclaim_pages_atomic: Reclaim memory from iommu_donate_pages_atomic() * @hyp_smp_processor_id: Current CPU id + * @device_register_reset: Register a reset callback for devices that is called + * before/after devices are assigned. Only one callback + * can be registered per device. + * Devices are identified by the base address of the MMIO + * as defined in the device tree. + * Reset is expected to clear any state/secrets on the + * device and put it in quiescent state, where it can't + * trigger any DMA. + * If reset fails at device assignment to guest, the + * device won't be assigned. + * Or if it fails on the guest teardown path, that would + * panic to avoid leaking any information. + * Direction of assignment can be deduced from pkvm_device::ctxt + * where NULL means host to guest and vice versa. */ struct pkvm_module_ops { int (*create_private_mapping)(phys_addr_t phys, size_t size, @@ -235,6 +250,8 @@ struct pkvm_module_ops { void * (*iommu_donate_pages_atomic)(u8 order); void (*iommu_reclaim_pages_atomic)(void *p, u8 order); int (*hyp_smp_processor_id)(void); + int (*device_register_reset)(u64 phys, void *cookie, + int (*cb)(void *cookie, bool host_to_guest)); ANDROID_KABI_RESERVE(1); ANDROID_KABI_RESERVE(2); ANDROID_KABI_RESERVE(3); diff --git a/arch/arm64/kvm/hyp/include/nvhe/pkvm.h b/arch/arm64/kvm/hyp/include/nvhe/pkvm.h index c2ffe70004fb..9ed649fe379c 100644 --- a/arch/arm64/kvm/hyp/include/nvhe/pkvm.h +++ b/arch/arm64/kvm/hyp/include/nvhe/pkvm.h @@ -190,5 +190,7 @@ int pkvm_init_devices(void); int pkvm_device_hyp_assign_mmio(u64 pfn, u64 nr_pages); int pkvm_device_reclaim_mmio(u64 pfn, u64 nr_pages); int pkvm_host_map_guest_mmio(struct pkvm_hyp_vcpu *hyp_vcpu, u64 pfn, u64 gfn); +int pkvm_device_register_reset(u64 phys, void *cookie, + int (*cb)(void *cookie, bool host_to_guest)); #endif /* __ARM64_KVM_NVHE_PKVM_H__ */ diff --git a/arch/arm64/kvm/hyp/nvhe/device/device.c b/arch/arm64/kvm/hyp/nvhe/device/device.c index f48b332a544c..b47a8b558f77 100644 --- a/arch/arm64/kvm/hyp/nvhe/device/device.c +++ b/arch/arm64/kvm/hyp/nvhe/device/device.c @@ -148,6 +148,17 @@ out_unlock: return ret; } +static int pkvm_device_reset(struct pkvm_device *dev, bool host_to_guest) +{ + hyp_assert_lock_held(&device_spinlock); + + /* Reset is mandatory. */ + if (!dev->reset_handler) + return -ENODEV; + + return dev->reset_handler(dev->cookie, host_to_guest); +} + static int __pkvm_device_assign(struct pkvm_device *dev, struct pkvm_hyp_vm *vm) { int i; @@ -163,6 +174,10 @@ static int __pkvm_device_assign(struct pkvm_device *dev, struct pkvm_hyp_vm *vm) return ret; } + ret = pkvm_device_reset(dev, true); + if (ret) + return ret; + dev->ctxt = vm; return 0; } @@ -326,6 +341,7 @@ void pkvm_devices_teardown(struct pkvm_hyp_vm *vm) if (dev->ctxt != vm) continue; + WARN_ON(pkvm_device_reset(dev, false)); dev->ctxt = NULL; pkvm_devices_reclaim_device(dev); } @@ -380,3 +396,25 @@ void pkvm_devices_put_context(u64 iommu_id, u32 endpoint_id) hyp_spin_unlock(&device_spinlock); } + +int pkvm_device_register_reset(u64 phys, void *cookie, + int (*cb)(void *cookie, bool host_to_guest)) +{ + struct pkvm_device *dev; + int ret = 0; + + dev = pkvm_get_device_by_addr(phys); + if (!dev) + return -ENODEV; + + hyp_spin_lock(&device_spinlock); + if (!dev->reset_handler) { + dev->reset_handler = cb; + dev->cookie = cookie; + } else { + ret = -EBUSY; + } + hyp_spin_unlock(&device_spinlock); + + return ret; +} diff --git a/arch/arm64/kvm/hyp/nvhe/modules.c b/arch/arm64/kvm/hyp/nvhe/modules.c index da625f1f8c06..3d0a3d733317 100644 --- a/arch/arm64/kvm/hyp/nvhe/modules.c +++ b/arch/arm64/kvm/hyp/nvhe/modules.c @@ -265,6 +265,7 @@ const struct pkvm_module_ops module_ops = { .iommu_donate_pages_atomic = kvm_iommu_donate_pages_atomic, .iommu_reclaim_pages_atomic = kvm_iommu_reclaim_pages_atomic, .hyp_smp_processor_id = __hyp_smp_processor_id, + .device_register_reset = pkvm_device_register_reset, }; static void *pkvm_module_hyp_va(struct pkvm_el2_module *mod, void *kern_va) diff --git a/include/kvm/device.h b/include/kvm/device.h index 058ccbce32c2..fc1ade96eac5 100644 --- a/include/kvm/device.h +++ b/include/kvm/device.h @@ -38,6 +38,8 @@ struct pkvm_device { u32 group_id; void *ctxt; /* Current context of the device*/ unsigned short refcount; + int (*reset_handler)(void *cookie, bool host_to_guest); + void *cookie; /* cookie from drivers. */ }; #endif /* #ifndef __KVM_DEVICE_H */