-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Rework the guest exit to userspace code to generalize the concept into what it is, a "hypercall to userspace", and provide two implementations of it: the PortIO version currently used, but only useable by x86, and an MMIO version that other architectures (except s390) can use. Signed-off-by: Andrew Jones <drjones@redhat.com> Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
- Loading branch information
Andrew Jones
authored and
Paolo Bonzini
committed
Oct 16, 2018
1 parent
6c93026
commit 14c47b7
Showing
10 changed files
with
222 additions
and
85 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,144 @@ | ||
// SPDX-License-Identifier: GPL-2.0 | ||
/* | ||
* ucall support. A ucall is a "hypercall to userspace". | ||
* | ||
* Copyright (C) 2018, Red Hat, Inc. | ||
*/ | ||
#include "kvm_util.h" | ||
#include "kvm_util_internal.h" | ||
|
||
#define UCALL_PIO_PORT ((uint16_t)0x1000) | ||
|
||
static ucall_type_t ucall_type; | ||
static vm_vaddr_t *ucall_exit_mmio_addr; | ||
|
||
static bool ucall_mmio_init(struct kvm_vm *vm, vm_paddr_t gpa) | ||
{ | ||
if (kvm_userspace_memory_region_find(vm, gpa, gpa + 1)) | ||
return false; | ||
|
||
virt_pg_map(vm, gpa, gpa, 0); | ||
|
||
ucall_exit_mmio_addr = (vm_vaddr_t *)gpa; | ||
sync_global_to_guest(vm, ucall_exit_mmio_addr); | ||
|
||
return true; | ||
} | ||
|
||
void ucall_init(struct kvm_vm *vm, ucall_type_t type, void *arg) | ||
{ | ||
ucall_type = type; | ||
sync_global_to_guest(vm, ucall_type); | ||
|
||
if (type == UCALL_PIO) | ||
return; | ||
|
||
if (type == UCALL_MMIO) { | ||
vm_paddr_t gpa, start, end, step; | ||
bool ret; | ||
|
||
if (arg) { | ||
gpa = (vm_paddr_t)arg; | ||
ret = ucall_mmio_init(vm, gpa); | ||
TEST_ASSERT(ret, "Can't set ucall mmio address to %lx", gpa); | ||
return; | ||
} | ||
|
||
/* | ||
* Find an address within the allowed virtual address space, | ||
* that does _not_ have a KVM memory region associated with it. | ||
* Identity mapping an address like this allows the guest to | ||
* access it, but as KVM doesn't know what to do with it, it | ||
* will assume it's something userspace handles and exit with | ||
* KVM_EXIT_MMIO. Well, at least that's how it works for AArch64. | ||
* Here we start with a guess that the addresses around two | ||
* thirds of the VA space are unmapped and then work both down | ||
* and up from there in 1/6 VA space sized steps. | ||
*/ | ||
start = 1ul << (vm->va_bits * 2 / 3); | ||
end = 1ul << vm->va_bits; | ||
step = 1ul << (vm->va_bits / 6); | ||
for (gpa = start; gpa >= 0; gpa -= step) { | ||
if (ucall_mmio_init(vm, gpa & ~(vm->page_size - 1))) | ||
return; | ||
} | ||
for (gpa = start + step; gpa < end; gpa += step) { | ||
if (ucall_mmio_init(vm, gpa & ~(vm->page_size - 1))) | ||
return; | ||
} | ||
TEST_ASSERT(false, "Can't find a ucall mmio address"); | ||
} | ||
} | ||
|
||
void ucall_uninit(struct kvm_vm *vm) | ||
{ | ||
ucall_type = 0; | ||
sync_global_to_guest(vm, ucall_type); | ||
ucall_exit_mmio_addr = 0; | ||
sync_global_to_guest(vm, ucall_exit_mmio_addr); | ||
} | ||
|
||
static void ucall_pio_exit(struct ucall *uc) | ||
{ | ||
#ifdef __x86_64__ | ||
asm volatile("in %[port], %%al" | ||
: : [port] "d" (UCALL_PIO_PORT), "D" (uc) : "rax"); | ||
#endif | ||
} | ||
|
||
static void ucall_mmio_exit(struct ucall *uc) | ||
{ | ||
*ucall_exit_mmio_addr = (vm_vaddr_t)uc; | ||
} | ||
|
||
void ucall(uint64_t cmd, int nargs, ...) | ||
{ | ||
struct ucall uc = { | ||
.cmd = cmd, | ||
}; | ||
va_list va; | ||
int i; | ||
|
||
nargs = nargs <= UCALL_MAX_ARGS ? nargs : UCALL_MAX_ARGS; | ||
|
||
va_start(va, nargs); | ||
for (i = 0; i < nargs; ++i) | ||
uc.args[i] = va_arg(va, uint64_t); | ||
va_end(va); | ||
|
||
switch (ucall_type) { | ||
case UCALL_PIO: | ||
ucall_pio_exit(&uc); | ||
break; | ||
case UCALL_MMIO: | ||
ucall_mmio_exit(&uc); | ||
break; | ||
}; | ||
} | ||
|
||
uint64_t get_ucall(struct kvm_vm *vm, uint32_t vcpu_id, struct ucall *uc) | ||
{ | ||
struct kvm_run *run = vcpu_state(vm, vcpu_id); | ||
|
||
memset(uc, 0, sizeof(*uc)); | ||
|
||
#ifdef __x86_64__ | ||
if (ucall_type == UCALL_PIO && run->exit_reason == KVM_EXIT_IO && | ||
run->io.port == UCALL_PIO_PORT) { | ||
struct kvm_regs regs; | ||
vcpu_regs_get(vm, vcpu_id, ®s); | ||
memcpy(uc, addr_gva2hva(vm, (vm_vaddr_t)regs.rdi), sizeof(*uc)); | ||
return uc->cmd; | ||
} | ||
#endif | ||
if (ucall_type == UCALL_MMIO && run->exit_reason == KVM_EXIT_MMIO && | ||
run->mmio.phys_addr == (uint64_t)ucall_exit_mmio_addr) { | ||
vm_vaddr_t gva; | ||
TEST_ASSERT(run->mmio.is_write && run->mmio.len == 8, | ||
"Unexpected ucall exit mmio address access"); | ||
gva = *(vm_vaddr_t *)run->mmio.data; | ||
memcpy(uc, addr_gva2hva(vm, gva), sizeof(*uc)); | ||
} | ||
|
||
return uc->cmd; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.