Commit 03b47505 authored by Sean Christopherson's avatar Sean Christopherson

KVM: selftests: Make arm64's MMIO ucall multi-VM friendly

Fix a mostly-theoretical bug where ARM's ucall MMIO setup could result in
different VMs stomping on each other by cloberring the global pointer.

Fix the most obvious issue by saving the MMIO gpa into the VM.

A more subtle bug is that creating VMs in parallel (on multiple tasks)
could result in a VM using the wrong address.  Synchronizing a global to
a guest effectively snapshots the value on a per-VM basis, i.e. the
"global" is already prepped to work with multiple VMs, but setting the
global in the host is not thread-safe.  To fix that bug, add
write_guest_global() to allow stuffing a VM's copy of a "global" without
modifying the host value.
Reviewed-by: default avatarAndrew Jones <andrew.jones@linux.dev>
Tested-by: default avatarPeter Gonda <pgonda@google.com>
Signed-off-by: default avatarSean Christopherson <seanjc@google.com>
Link: https://lore.kernel.org/r/20221006003409.649993-6-seanjc@google.com
parent cf4694be
...@@ -16,6 +16,7 @@ ...@@ -16,6 +16,7 @@
#include <linux/kvm.h> #include <linux/kvm.h>
#include "linux/rbtree.h" #include "linux/rbtree.h"
#include <asm/atomic.h>
#include <sys/ioctl.h> #include <sys/ioctl.h>
...@@ -81,6 +82,7 @@ struct kvm_vm { ...@@ -81,6 +82,7 @@ struct kvm_vm {
struct sparsebit *vpages_mapped; struct sparsebit *vpages_mapped;
bool has_irqchip; bool has_irqchip;
bool pgd_created; bool pgd_created;
vm_paddr_t ucall_mmio_addr;
vm_paddr_t pgd; vm_paddr_t pgd;
vm_vaddr_t gdt; vm_vaddr_t gdt;
vm_vaddr_t tss; vm_vaddr_t tss;
...@@ -722,6 +724,19 @@ kvm_userspace_memory_region_find(struct kvm_vm *vm, uint64_t start, ...@@ -722,6 +724,19 @@ kvm_userspace_memory_region_find(struct kvm_vm *vm, uint64_t start,
memcpy(&(g), _p, sizeof(g)); \ memcpy(&(g), _p, sizeof(g)); \
}) })
/*
* Write a global value, but only in the VM's (guest's) domain. Primarily used
* for "globals" that hold per-VM values (VMs always duplicate code and global
* data into their own region of physical memory), but can be used anytime it's
* undesirable to change the host's copy of the global.
*/
#define write_guest_global(vm, g, val) ({ \
typeof(g) *_p = addr_gva2hva(vm, (vm_vaddr_t)&(g)); \
typeof(g) _val = val; \
\
memcpy(_p, &(_val), sizeof(g)); \
})
void assert_on_unhandled_exception(struct kvm_vcpu *vcpu); void assert_on_unhandled_exception(struct kvm_vcpu *vcpu);
void vcpu_arch_dump(FILE *stream, struct kvm_vcpu *vcpu, void vcpu_arch_dump(FILE *stream, struct kvm_vcpu *vcpu,
......
...@@ -6,20 +6,29 @@ ...@@ -6,20 +6,29 @@
*/ */
#include "kvm_util.h" #include "kvm_util.h"
/*
* ucall_exit_mmio_addr holds per-VM values (global data is duplicated by each
* VM), it must not be accessed from host code.
*/
static vm_vaddr_t *ucall_exit_mmio_addr; static vm_vaddr_t *ucall_exit_mmio_addr;
static void ucall_set_mmio_addr(struct kvm_vm *vm, vm_paddr_t mmio_gpa)
{
vm->ucall_mmio_addr = mmio_gpa;
write_guest_global(vm, ucall_exit_mmio_addr, (vm_vaddr_t *)mmio_gpa);
}
void ucall_arch_init(struct kvm_vm *vm, vm_paddr_t mmio_gpa) void ucall_arch_init(struct kvm_vm *vm, vm_paddr_t mmio_gpa)
{ {
virt_pg_map(vm, mmio_gpa, mmio_gpa); virt_pg_map(vm, mmio_gpa, mmio_gpa);
ucall_exit_mmio_addr = (vm_vaddr_t *)mmio_gpa; ucall_set_mmio_addr(vm, mmio_gpa);
sync_global_to_guest(vm, ucall_exit_mmio_addr);
} }
void ucall_arch_uninit(struct kvm_vm *vm) void ucall_arch_uninit(struct kvm_vm *vm)
{ {
ucall_exit_mmio_addr = 0; ucall_set_mmio_addr(vm, (vm_paddr_t)NULL);
sync_global_to_guest(vm, ucall_exit_mmio_addr);
} }
void ucall_arch_do_ucall(vm_vaddr_t uc) void ucall_arch_do_ucall(vm_vaddr_t uc)
...@@ -32,7 +41,7 @@ void *ucall_arch_get_ucall(struct kvm_vcpu *vcpu) ...@@ -32,7 +41,7 @@ void *ucall_arch_get_ucall(struct kvm_vcpu *vcpu)
struct kvm_run *run = vcpu->run; struct kvm_run *run = vcpu->run;
if (run->exit_reason == KVM_EXIT_MMIO && if (run->exit_reason == KVM_EXIT_MMIO &&
run->mmio.phys_addr == (uint64_t)ucall_exit_mmio_addr) { run->mmio.phys_addr == vcpu->vm->ucall_mmio_addr) {
vm_vaddr_t gva; vm_vaddr_t gva;
TEST_ASSERT(run->mmio.is_write && run->mmio.len == 8, TEST_ASSERT(run->mmio.is_write && run->mmio.len == 8,
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment