kvm: selftest: unify the guest port macros
Most of the tests are using the same way to do guest to host sync but the code is mostly duplicated. Generalize the guest port macros into the common header file and use it in different tests. Meanwhile provide "struct guest_args" and a helper "guest_args_read()" to hide the register details when playing with these port operations on RDI and RSI. Signed-off-by: Peter Xu <peterx@redhat.com> Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
This commit is contained in:
parent
07a262cc7c
commit
4e18bccc2e
6 changed files with 78 additions and 95 deletions
|
@ -23,20 +23,6 @@
|
|||
#define X86_FEATURE_OSXSAVE (1<<27)
|
||||
#define VCPU_ID 1
|
||||
|
||||
enum {
|
||||
GUEST_UPDATE_CR4 = 0x1000,
|
||||
GUEST_FAILED,
|
||||
GUEST_DONE,
|
||||
};
|
||||
|
||||
static void exit_to_hv(uint16_t port)
|
||||
{
|
||||
__asm__ __volatile__("in %[port], %%al"
|
||||
:
|
||||
: [port]"d"(port)
|
||||
: "rax");
|
||||
}
|
||||
|
||||
static inline bool cr4_cpuid_is_sync(void)
|
||||
{
|
||||
int func, subfunc;
|
||||
|
@ -64,17 +50,15 @@ static void guest_code(void)
|
|||
set_cr4(cr4);
|
||||
|
||||
/* verify CR4.OSXSAVE == CPUID.OSXSAVE */
|
||||
if (!cr4_cpuid_is_sync())
|
||||
exit_to_hv(GUEST_FAILED);
|
||||
GUEST_ASSERT(cr4_cpuid_is_sync());
|
||||
|
||||
/* notify hypervisor to change CR4 */
|
||||
exit_to_hv(GUEST_UPDATE_CR4);
|
||||
GUEST_SYNC(0);
|
||||
|
||||
/* check again */
|
||||
if (!cr4_cpuid_is_sync())
|
||||
exit_to_hv(GUEST_FAILED);
|
||||
GUEST_ASSERT(cr4_cpuid_is_sync());
|
||||
|
||||
exit_to_hv(GUEST_DONE);
|
||||
GUEST_DONE();
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
|
@ -104,16 +88,16 @@ int main(int argc, char *argv[])
|
|||
|
||||
if (run->exit_reason == KVM_EXIT_IO) {
|
||||
switch (run->io.port) {
|
||||
case GUEST_UPDATE_CR4:
|
||||
case GUEST_PORT_SYNC:
|
||||
/* emulate hypervisor clearing CR4.OSXSAVE */
|
||||
vcpu_sregs_get(vm, VCPU_ID, &sregs);
|
||||
sregs.cr4 &= ~X86_CR4_OSXSAVE;
|
||||
vcpu_sregs_set(vm, VCPU_ID, &sregs);
|
||||
break;
|
||||
case GUEST_FAILED:
|
||||
case GUEST_PORT_ABORT:
|
||||
TEST_ASSERT(false, "Guest CR4 bit (OSXSAVE) unsynchronized with CPUID bit.");
|
||||
break;
|
||||
case GUEST_DONE:
|
||||
case GUEST_PORT_DONE:
|
||||
goto done;
|
||||
default:
|
||||
TEST_ASSERT(false, "Unknown port 0x%x.",
|
||||
|
|
|
@ -144,4 +144,43 @@ allocate_kvm_dirty_log(struct kvm_userspace_memory_region *region);
|
|||
|
||||
int vm_create_device(struct kvm_vm *vm, struct kvm_create_device *cd);
|
||||
|
||||
#define GUEST_PORT_SYNC 0x1000
|
||||
#define GUEST_PORT_ABORT 0x1001
|
||||
#define GUEST_PORT_DONE 0x1002
|
||||
|
||||
static inline void __exit_to_l0(uint16_t port, uint64_t arg0, uint64_t arg1)
|
||||
{
|
||||
__asm__ __volatile__("in %[port], %%al"
|
||||
:
|
||||
: [port]"d"(port), "D"(arg0), "S"(arg1)
|
||||
: "rax");
|
||||
}
|
||||
|
||||
/*
|
||||
* Allows to pass three arguments to the host: port is 16bit wide,
|
||||
* arg0 & arg1 are 64bit wide
|
||||
*/
|
||||
#define GUEST_SYNC_ARGS(_port, _arg0, _arg1) \
|
||||
__exit_to_l0(_port, (uint64_t) (_arg0), (uint64_t) (_arg1))
|
||||
|
||||
#define GUEST_ASSERT(_condition) do { \
|
||||
if (!(_condition)) \
|
||||
GUEST_SYNC_ARGS(GUEST_PORT_ABORT, \
|
||||
"Failed guest assert: " \
|
||||
#_condition, __LINE__); \
|
||||
} while (0)
|
||||
|
||||
#define GUEST_SYNC(stage) GUEST_SYNC_ARGS(GUEST_PORT_SYNC, "hello", stage)
|
||||
|
||||
#define GUEST_DONE() GUEST_SYNC_ARGS(GUEST_PORT_DONE, 0, 0)
|
||||
|
||||
struct guest_args {
|
||||
uint64_t arg0;
|
||||
uint64_t arg1;
|
||||
uint16_t port;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
void guest_args_read(struct kvm_vm *vm, uint32_t vcpu_id,
|
||||
struct guest_args *args);
|
||||
|
||||
#endif /* SELFTEST_KVM_UTIL_H */
|
||||
|
|
|
@ -1536,3 +1536,17 @@ void *addr_gva2hva(struct kvm_vm *vm, vm_vaddr_t gva)
|
|||
{
|
||||
return addr_gpa2hva(vm, addr_gva2gpa(vm, gva));
|
||||
}
|
||||
|
||||
void guest_args_read(struct kvm_vm *vm, uint32_t vcpu_id,
|
||||
struct guest_args *args)
|
||||
{
|
||||
struct kvm_run *run = vcpu_state(vm, vcpu_id);
|
||||
struct kvm_regs regs;
|
||||
|
||||
memset(®s, 0, sizeof(regs));
|
||||
vcpu_regs_get(vm, vcpu_id, ®s);
|
||||
|
||||
args->port = run->io.port;
|
||||
args->arg0 = regs.rdi;
|
||||
args->arg1 = regs.rsi;
|
||||
}
|
||||
|
|
|
@ -21,28 +21,6 @@
|
|||
#include "vmx.h"
|
||||
|
||||
#define VCPU_ID 5
|
||||
#define PORT_SYNC 0x1000
|
||||
#define PORT_ABORT 0x1001
|
||||
#define PORT_DONE 0x1002
|
||||
|
||||
static inline void __exit_to_l0(uint16_t port, uint64_t arg0, uint64_t arg1)
|
||||
{
|
||||
__asm__ __volatile__("in %[port], %%al"
|
||||
:
|
||||
: [port]"d"(port), "D"(arg0), "S"(arg1)
|
||||
: "rax");
|
||||
}
|
||||
|
||||
#define exit_to_l0(_port, _arg0, _arg1) \
|
||||
__exit_to_l0(_port, (uint64_t) (_arg0), (uint64_t) (_arg1))
|
||||
|
||||
#define GUEST_ASSERT(_condition) do { \
|
||||
if (!(_condition)) \
|
||||
exit_to_l0(PORT_ABORT, "Failed guest assert: " #_condition, __LINE__);\
|
||||
} while (0)
|
||||
|
||||
#define GUEST_SYNC(stage) \
|
||||
exit_to_l0(PORT_SYNC, "hello", stage);
|
||||
|
||||
static bool have_nested_state;
|
||||
|
||||
|
@ -137,7 +115,7 @@ void guest_code(struct vmx_pages *vmx_pages)
|
|||
if (vmx_pages)
|
||||
l1_guest_code(vmx_pages);
|
||||
|
||||
exit_to_l0(PORT_DONE, 0, 0);
|
||||
GUEST_DONE();
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
|
@ -178,13 +156,13 @@ int main(int argc, char *argv[])
|
|||
memset(®s1, 0, sizeof(regs1));
|
||||
vcpu_regs_get(vm, VCPU_ID, ®s1);
|
||||
switch (run->io.port) {
|
||||
case PORT_ABORT:
|
||||
case GUEST_PORT_ABORT:
|
||||
TEST_ASSERT(false, "%s at %s:%d", (const char *) regs1.rdi,
|
||||
__FILE__, regs1.rsi);
|
||||
/* NOT REACHED */
|
||||
case PORT_SYNC:
|
||||
case GUEST_PORT_SYNC:
|
||||
break;
|
||||
case PORT_DONE:
|
||||
case GUEST_PORT_DONE:
|
||||
goto done;
|
||||
default:
|
||||
TEST_ASSERT(false, "Unknown port 0x%x.", run->io.port);
|
||||
|
|
|
@ -22,28 +22,11 @@
|
|||
#include "x86.h"
|
||||
|
||||
#define VCPU_ID 5
|
||||
#define PORT_HOST_SYNC 0x1000
|
||||
|
||||
static void __exit_to_l0(uint16_t port, uint64_t arg0, uint64_t arg1)
|
||||
{
|
||||
__asm__ __volatile__("in %[port], %%al"
|
||||
:
|
||||
: [port]"d"(port), "D"(arg0), "S"(arg1)
|
||||
: "rax");
|
||||
}
|
||||
|
||||
#define exit_to_l0(_port, _arg0, _arg1) \
|
||||
__exit_to_l0(_port, (uint64_t) (_arg0), (uint64_t) (_arg1))
|
||||
|
||||
#define GUEST_ASSERT(_condition) do { \
|
||||
if (!(_condition)) \
|
||||
exit_to_l0(PORT_ABORT, "Failed guest assert: " #_condition, 0);\
|
||||
} while (0)
|
||||
|
||||
void guest_code(void)
|
||||
{
|
||||
for (;;) {
|
||||
exit_to_l0(PORT_HOST_SYNC, "hello", 0);
|
||||
GUEST_SYNC(0);
|
||||
asm volatile ("inc %r11");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -62,27 +62,12 @@ struct kvm_single_msr {
|
|||
/* The virtual machine object. */
|
||||
static struct kvm_vm *vm;
|
||||
|
||||
#define exit_to_l0(_port, _arg) do_exit_to_l0(_port, (unsigned long) (_arg))
|
||||
static void do_exit_to_l0(uint16_t port, unsigned long arg)
|
||||
{
|
||||
__asm__ __volatile__("in %[port], %%al"
|
||||
:
|
||||
: [port]"d"(port), "D"(arg)
|
||||
: "rax");
|
||||
}
|
||||
|
||||
|
||||
#define GUEST_ASSERT(_condition) do { \
|
||||
if (!(_condition)) \
|
||||
exit_to_l0(PORT_ABORT, "Failed guest assert: " #_condition); \
|
||||
} while (0)
|
||||
|
||||
static void check_ia32_tsc_adjust(int64_t max)
|
||||
{
|
||||
int64_t adjust;
|
||||
|
||||
adjust = rdmsr(MSR_IA32_TSC_ADJUST);
|
||||
exit_to_l0(PORT_REPORT, adjust);
|
||||
GUEST_SYNC(adjust);
|
||||
GUEST_ASSERT(adjust <= max);
|
||||
}
|
||||
|
||||
|
@ -132,7 +117,7 @@ static void l1_guest_code(struct vmx_pages *vmx_pages)
|
|||
|
||||
check_ia32_tsc_adjust(-2 * TSC_ADJUST_VALUE);
|
||||
|
||||
exit_to_l0(PORT_DONE, 0);
|
||||
GUEST_DONE();
|
||||
}
|
||||
|
||||
void report(int64_t val)
|
||||
|
@ -161,26 +146,26 @@ int main(int argc, char *argv[])
|
|||
|
||||
for (;;) {
|
||||
volatile struct kvm_run *run = vcpu_state(vm, VCPU_ID);
|
||||
struct kvm_regs regs;
|
||||
struct guest_args args;
|
||||
|
||||
vcpu_run(vm, VCPU_ID);
|
||||
vcpu_regs_get(vm, VCPU_ID, ®s);
|
||||
guest_args_read(vm, VCPU_ID, &args);
|
||||
TEST_ASSERT(run->exit_reason == KVM_EXIT_IO,
|
||||
"Got exit_reason other than KVM_EXIT_IO: %u (%s), rip=%lx\n",
|
||||
"Got exit_reason other than KVM_EXIT_IO: %u (%s)\n",
|
||||
run->exit_reason,
|
||||
exit_reason_str(run->exit_reason), regs.rip);
|
||||
exit_reason_str(run->exit_reason));
|
||||
|
||||
switch (run->io.port) {
|
||||
case PORT_ABORT:
|
||||
TEST_ASSERT(false, "%s", (const char *) regs.rdi);
|
||||
switch (args.port) {
|
||||
case GUEST_PORT_ABORT:
|
||||
TEST_ASSERT(false, "%s", (const char *) args.arg0);
|
||||
/* NOT REACHED */
|
||||
case PORT_REPORT:
|
||||
report(regs.rdi);
|
||||
case GUEST_PORT_SYNC:
|
||||
report(args.arg1);
|
||||
break;
|
||||
case PORT_DONE:
|
||||
case GUEST_PORT_DONE:
|
||||
goto done;
|
||||
default:
|
||||
TEST_ASSERT(false, "Unknown port 0x%x.", run->io.port);
|
||||
TEST_ASSERT(false, "Unknown port 0x%x.", args.port);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue