mirror of
https://mirrors.bfsu.edu.cn/git/linux.git
synced 2024-11-14 15:54:15 +08:00
KVM: selftests: Add ucall pool based implementation
To play nice with guests whose stack memory is encrypted, e.g. AMD SEV, introduce a new "ucall pool" implementation that passes the ucall struct via dedicated memory (which can be mapped shared, a.k.a. as plain text). Because not all architectures have access to the vCPU index in the guest, use a bitmap with atomic accesses to track which entries in the pool are free/used. A list+lock could also work in theory, but synchronizing the individual pointers to the guest would be a mess. Note, there's no need to rewalk the bitmap to ensure success. If all vCPUs are simply allocating, success is guaranteed because there are enough entries for all vCPUs. If one or more vCPUs are freeing and then reallocating, success is guaranteed because vCPUs _always_ walk the bitmap from 0=>N; if vCPU frees an entry and then wins a race to re-allocate, then either it will consume the entry it just freed (bit is the first free bit), or the losing vCPU is guaranteed to see the freed bit (winner consumes an earlier bit, which the loser hasn't yet visited). Reviewed-by: Andrew Jones <andrew.jones@linux.dev> Signed-off-by: Peter Gonda <pgonda@google.com> Co-developed-by: Sean Christopherson <seanjc@google.com> Signed-off-by: Sean Christopherson <seanjc@google.com> Link: https://lore.kernel.org/r/20221006003409.649993-8-seanjc@google.com
This commit is contained in:
parent
28a65567ac
commit
426729b2cf
@ -22,6 +22,9 @@ enum {
|
|||||||
struct ucall {
|
struct ucall {
|
||||||
uint64_t cmd;
|
uint64_t cmd;
|
||||||
uint64_t args[UCALL_MAX_ARGS];
|
uint64_t args[UCALL_MAX_ARGS];
|
||||||
|
|
||||||
|
/* Host virtual address of this struct. */
|
||||||
|
struct ucall *hva;
|
||||||
};
|
};
|
||||||
|
|
||||||
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);
|
||||||
@ -30,11 +33,7 @@ void *ucall_arch_get_ucall(struct kvm_vcpu *vcpu);
|
|||||||
|
|
||||||
void ucall(uint64_t cmd, int nargs, ...);
|
void ucall(uint64_t cmd, int nargs, ...);
|
||||||
uint64_t get_ucall(struct kvm_vcpu *vcpu, struct ucall *uc);
|
uint64_t get_ucall(struct kvm_vcpu *vcpu, struct ucall *uc);
|
||||||
|
void ucall_init(struct kvm_vm *vm, vm_paddr_t mmio_gpa);
|
||||||
static inline void ucall_init(struct kvm_vm *vm, vm_paddr_t mmio_gpa)
|
|
||||||
{
|
|
||||||
ucall_arch_init(vm, mmio_gpa);
|
|
||||||
}
|
|
||||||
|
|
||||||
#define GUEST_SYNC_ARGS(stage, arg1, arg2, arg3, arg4) \
|
#define GUEST_SYNC_ARGS(stage, arg1, arg2, arg3, arg4) \
|
||||||
ucall(UCALL_SYNC, 6, "hello", stage, arg1, arg2, arg3, arg4)
|
ucall(UCALL_SYNC, 6, "hello", stage, arg1, arg2, arg3, arg4)
|
||||||
|
@ -32,12 +32,9 @@ void *ucall_arch_get_ucall(struct kvm_vcpu *vcpu)
|
|||||||
|
|
||||||
if (run->exit_reason == KVM_EXIT_MMIO &&
|
if (run->exit_reason == KVM_EXIT_MMIO &&
|
||||||
run->mmio.phys_addr == vcpu->vm->ucall_mmio_addr) {
|
run->mmio.phys_addr == vcpu->vm->ucall_mmio_addr) {
|
||||||
vm_vaddr_t gva;
|
TEST_ASSERT(run->mmio.is_write && run->mmio.len == sizeof(uint64_t),
|
||||||
|
|
||||||
TEST_ASSERT(run->mmio.is_write && run->mmio.len == 8,
|
|
||||||
"Unexpected ucall exit mmio address access");
|
"Unexpected ucall exit mmio address access");
|
||||||
memcpy(&gva, run->mmio.data, sizeof(gva));
|
return (void *)(*((uint64_t *)run->mmio.data));
|
||||||
return addr_gva2hva(vcpu->vm, gva);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return NULL;
|
return NULL;
|
||||||
|
@ -55,7 +55,7 @@ void *ucall_arch_get_ucall(struct kvm_vcpu *vcpu)
|
|||||||
run->riscv_sbi.extension_id == KVM_RISCV_SELFTESTS_SBI_EXT) {
|
run->riscv_sbi.extension_id == KVM_RISCV_SELFTESTS_SBI_EXT) {
|
||||||
switch (run->riscv_sbi.function_id) {
|
switch (run->riscv_sbi.function_id) {
|
||||||
case KVM_RISCV_SELFTESTS_SBI_UCALL:
|
case KVM_RISCV_SELFTESTS_SBI_UCALL:
|
||||||
return addr_gva2hva(vcpu->vm, run->riscv_sbi.args[0]);
|
return (void *)run->riscv_sbi.args[0];
|
||||||
case KVM_RISCV_SELFTESTS_SBI_UNEXP:
|
case KVM_RISCV_SELFTESTS_SBI_UNEXP:
|
||||||
vcpu_dump(stderr, vcpu, 2);
|
vcpu_dump(stderr, vcpu, 2);
|
||||||
TEST_ASSERT(0, "Unexpected trap taken by guest");
|
TEST_ASSERT(0, "Unexpected trap taken by guest");
|
||||||
|
@ -26,7 +26,7 @@ void *ucall_arch_get_ucall(struct kvm_vcpu *vcpu)
|
|||||||
(run->s390_sieic.ipb >> 16) == 0x501) {
|
(run->s390_sieic.ipb >> 16) == 0x501) {
|
||||||
int reg = run->s390_sieic.ipa & 0xf;
|
int reg = run->s390_sieic.ipa & 0xf;
|
||||||
|
|
||||||
return addr_gva2hva(vcpu->vm, run->s.regs.gprs[reg]);
|
return (void *)run->s.regs.gprs[reg];
|
||||||
}
|
}
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
@ -1,22 +1,86 @@
|
|||||||
// SPDX-License-Identifier: GPL-2.0-only
|
// SPDX-License-Identifier: GPL-2.0-only
|
||||||
#include "kvm_util.h"
|
#include "kvm_util.h"
|
||||||
|
#include "linux/types.h"
|
||||||
|
#include "linux/bitmap.h"
|
||||||
|
#include "linux/atomic.h"
|
||||||
|
|
||||||
|
struct ucall_header {
|
||||||
|
DECLARE_BITMAP(in_use, KVM_MAX_VCPUS);
|
||||||
|
struct ucall ucalls[KVM_MAX_VCPUS];
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* ucall_pool holds per-VM values (global data is duplicated by each VM), it
|
||||||
|
* must not be accessed from host code.
|
||||||
|
*/
|
||||||
|
static struct ucall_header *ucall_pool;
|
||||||
|
|
||||||
|
void ucall_init(struct kvm_vm *vm, vm_paddr_t mmio_gpa)
|
||||||
|
{
|
||||||
|
struct ucall_header *hdr;
|
||||||
|
struct ucall *uc;
|
||||||
|
vm_vaddr_t vaddr;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
vaddr = vm_vaddr_alloc(vm, sizeof(*hdr), KVM_UTIL_MIN_VADDR);
|
||||||
|
hdr = (struct ucall_header *)addr_gva2hva(vm, vaddr);
|
||||||
|
memset(hdr, 0, sizeof(*hdr));
|
||||||
|
|
||||||
|
for (i = 0; i < KVM_MAX_VCPUS; ++i) {
|
||||||
|
uc = &hdr->ucalls[i];
|
||||||
|
uc->hva = uc;
|
||||||
|
}
|
||||||
|
|
||||||
|
write_guest_global(vm, ucall_pool, (struct ucall_header *)vaddr);
|
||||||
|
|
||||||
|
ucall_arch_init(vm, mmio_gpa);
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct ucall *ucall_alloc(void)
|
||||||
|
{
|
||||||
|
struct ucall *uc;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
GUEST_ASSERT(ucall_pool);
|
||||||
|
|
||||||
|
for (i = 0; i < KVM_MAX_VCPUS; ++i) {
|
||||||
|
if (!atomic_test_and_set_bit(i, ucall_pool->in_use)) {
|
||||||
|
uc = &ucall_pool->ucalls[i];
|
||||||
|
memset(uc->args, 0, sizeof(uc->args));
|
||||||
|
return uc;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
GUEST_ASSERT(0);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ucall_free(struct ucall *uc)
|
||||||
|
{
|
||||||
|
/* Beware, here be pointer arithmetic. */
|
||||||
|
clear_bit(uc - ucall_pool->ucalls, ucall_pool->in_use);
|
||||||
|
}
|
||||||
|
|
||||||
void ucall(uint64_t cmd, int nargs, ...)
|
void ucall(uint64_t cmd, int nargs, ...)
|
||||||
{
|
{
|
||||||
struct ucall uc = {};
|
struct ucall *uc;
|
||||||
va_list va;
|
va_list va;
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
WRITE_ONCE(uc.cmd, cmd);
|
uc = ucall_alloc();
|
||||||
|
|
||||||
|
WRITE_ONCE(uc->cmd, cmd);
|
||||||
|
|
||||||
nargs = min(nargs, UCALL_MAX_ARGS);
|
nargs = min(nargs, UCALL_MAX_ARGS);
|
||||||
|
|
||||||
va_start(va, nargs);
|
va_start(va, nargs);
|
||||||
for (i = 0; i < nargs; ++i)
|
for (i = 0; i < nargs; ++i)
|
||||||
WRITE_ONCE(uc.args[i], va_arg(va, uint64_t));
|
WRITE_ONCE(uc->args[i], va_arg(va, uint64_t));
|
||||||
va_end(va);
|
va_end(va);
|
||||||
|
|
||||||
ucall_arch_do_ucall((vm_vaddr_t)&uc);
|
ucall_arch_do_ucall((vm_vaddr_t)uc->hva);
|
||||||
|
|
||||||
|
ucall_free(uc);
|
||||||
}
|
}
|
||||||
|
|
||||||
uint64_t get_ucall(struct kvm_vcpu *vcpu, struct ucall *uc)
|
uint64_t get_ucall(struct kvm_vcpu *vcpu, struct ucall *uc)
|
||||||
|
@ -26,7 +26,7 @@ void *ucall_arch_get_ucall(struct kvm_vcpu *vcpu)
|
|||||||
struct kvm_regs regs;
|
struct kvm_regs regs;
|
||||||
|
|
||||||
vcpu_regs_get(vcpu, ®s);
|
vcpu_regs_get(vcpu, ®s);
|
||||||
return addr_gva2hva(vcpu->vm, regs.rdi);
|
return (void *)regs.rdi;
|
||||||
}
|
}
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user