>From bbc527a48fe0481495d6a0a562815e3d33fb655b Mon Sep 17 00:00:00 2001 From: Sean Christopherson Date: Thu, 18 Aug 2022 15:22:55 -0700 Subject: [PATCH 10/13] KVM: selftests: Add macro to atomically sync per-VM "global" pointers Add atomic_sync_global_pointer_to_guest() to allow sync'ing "global" pointers that hold per-VM values, i.e. technically need to be handled in a thread-safe manner. Use the new macro to fix a mostly-theoretical bug where ARM's ucall MMIO setup could result in different VMs stomping on each other. Signed-off-by: Sean Christopherson --- .../testing/selftests/kvm/include/kvm_util_base.h | 15 +++++++++++++++ tools/testing/selftests/kvm/lib/aarch64/ucall.c | 15 +++++++++++---- 2 files changed, 26 insertions(+), 4 deletions(-) diff --git a/tools/testing/selftests/kvm/include/kvm_util_base.h b/tools/testing/selftests/kvm/include/kvm_util_base.h index 8ce9e5be70a3..f4a2622db53c 100644 --- a/tools/testing/selftests/kvm/include/kvm_util_base.h +++ b/tools/testing/selftests/kvm/include/kvm_util_base.h @@ -16,6 +16,7 @@ #include #include "linux/rbtree.h" +#include #include @@ -727,6 +728,20 @@ kvm_userspace_memory_region_find(struct kvm_vm *vm, uint64_t start, memcpy(&(g), _p, sizeof(g)); \ }) +/* + * Sync a global pointer to the guest that has a per-VM value, in which case + * writes to the host copy of the "global" must be serialized (in case a test + * is being truly crazy and spawning multiple VMs concurrently). + */ +#define atomic_sync_global_pointer_to_guest(vm, g, val) ({ \ + typeof(g) *_p = addr_gva2hva(vm, (vm_vaddr_t)&(g)); \ + \ + while (cmpxchg(&g, NULL, val)) \ + ; \ + memcpy(_p, &(g), sizeof(g)); \ + WRITE_ONCE(g, NULL); \ +}) + void assert_on_unhandled_exception(struct kvm_vcpu *vcpu); void vcpu_arch_dump(FILE *stream, struct kvm_vcpu *vcpu, diff --git a/tools/testing/selftests/kvm/lib/aarch64/ucall.c b/tools/testing/selftests/kvm/lib/aarch64/ucall.c index 132c0e98bf49..c30a6eacde34 100644 --- a/tools/testing/selftests/kvm/lib/aarch64/ucall.c +++ b/tools/testing/selftests/kvm/lib/aarch64/ucall.c @@ -6,8 +6,17 @@ */ #include "kvm_util.h" +/* + * This "global" holds different per-VM values, it must not be accessed from + * host code except to sync the guest value, and that must be done atomically. + */ static vm_vaddr_t *ucall_exit_mmio_addr; +static void ucall_set_mmio_addr(struct kvm_vm *vm, vm_vaddr_t *val) +{ + atomic_sync_global_pointer_to_guest(vm, ucall_exit_mmio_addr, val); +} + static bool ucall_mmio_init(struct kvm_vm *vm, vm_paddr_t gpa) { if (kvm_userspace_memory_region_find(vm, gpa, gpa + 1)) @@ -15,8 +24,7 @@ static bool ucall_mmio_init(struct kvm_vm *vm, vm_paddr_t gpa) virt_pg_map(vm, gpa, gpa); - ucall_exit_mmio_addr = (vm_vaddr_t *)gpa; - sync_global_to_guest(vm, ucall_exit_mmio_addr); + ucall_set_mmio_addr(vm, (vm_vaddr_t *)gpa); return true; } @@ -66,8 +74,7 @@ void ucall_arch_init(struct kvm_vm *vm, void *arg) void ucall_arch_uninit(struct kvm_vm *vm) { - ucall_exit_mmio_addr = 0; - sync_global_to_guest(vm, ucall_exit_mmio_addr); + ucall_set_mmio_addr(vm, (vm_vaddr_t *)NULL); } void ucall_arch_do_ucall(vm_vaddr_t uc) -- 2.37.1.595.g718a3a8f04-goog