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 */