forked from torvalds/linux
-
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.
KVM: x86: declare Xen HVM shared info capability and add test case
Instead of adding a plethora of new KVM_CAP_XEN_FOO capabilities, just add bits to the return value of KVM_CAP_XEN_HVM. Signed-off-by: David Woodhouse <[email protected]>
- Loading branch information
Showing
4 changed files
with
175 additions
and
1 deletion.
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,169 @@ | ||
// SPDX-License-Identifier: GPL-2.0-only | ||
/* | ||
* svm_vmcall_test | ||
* | ||
* Copyright © 2021 Amazon.com, Inc. or its affiliates. | ||
* | ||
* Xen shared_info / pvclock testing | ||
*/ | ||
|
||
#include "test_util.h" | ||
#include "kvm_util.h" | ||
#include "processor.h" | ||
|
||
#include <stdint.h> | ||
#include <time.h> | ||
|
||
#define VCPU_ID 5 | ||
|
||
#define SHINFO_REGION_GPA 0xc0000000ULL | ||
#define SHINFO_REGION_SLOT 10 | ||
#define PAGE_SIZE 4096 | ||
|
||
#define PVTIME_ADDR (SHINFO_REGION_GPA + PAGE_SIZE) | ||
|
||
static struct kvm_vm *vm; | ||
|
||
#define XEN_HYPERCALL_MSR 0x40000000 | ||
|
||
struct pvclock_vcpu_time_info { | ||
u32 version; | ||
u32 pad0; | ||
u64 tsc_timestamp; | ||
u64 system_time; | ||
u32 tsc_to_system_mul; | ||
s8 tsc_shift; | ||
u8 flags; | ||
u8 pad[2]; | ||
} __attribute__((__packed__)); /* 32 bytes */ | ||
|
||
struct pvclock_wall_clock { | ||
u32 version; | ||
u32 sec; | ||
u32 nsec; | ||
} __attribute__((__packed__)); | ||
|
||
static void guest_code(void) | ||
{ | ||
GUEST_DONE(); | ||
} | ||
|
||
static int cmp_timespec(struct timespec *a, struct timespec *b) | ||
{ | ||
if (a->tv_sec > b->tv_sec) | ||
return 1; | ||
else if (a->tv_sec < b->tv_sec) | ||
return -1; | ||
else if (a->tv_nsec > b->tv_nsec) | ||
return 1; | ||
else if (a->tv_nsec < b->tv_nsec) | ||
return -1; | ||
else | ||
return 0; | ||
} | ||
|
||
int main(int argc, char *argv[]) | ||
{ | ||
struct timespec min_ts, max_ts, vm_ts; | ||
|
||
if (!(kvm_check_cap(KVM_CAP_XEN_HVM) & | ||
KVM_XEN_HVM_CONFIG_SHARED_INFO) ) { | ||
print_skip("KVM_XEN_HVM_CONFIG_SHARED_INFO not available"); | ||
exit(KSFT_SKIP); | ||
} | ||
|
||
clock_gettime(CLOCK_REALTIME, &min_ts); | ||
|
||
vm = vm_create_default(VCPU_ID, 0, (void *) guest_code); | ||
vcpu_set_cpuid(vm, VCPU_ID, kvm_get_supported_cpuid()); | ||
|
||
/* Map a region for the shared_info page */ | ||
vm_userspace_mem_region_add(vm, VM_MEM_SRC_ANONYMOUS, | ||
SHINFO_REGION_GPA, SHINFO_REGION_SLOT, | ||
2 * getpagesize(), 0); | ||
virt_map(vm, SHINFO_REGION_GPA, SHINFO_REGION_GPA, 2, 0); | ||
|
||
struct kvm_xen_hvm_config hvmc = { | ||
.flags = KVM_XEN_HVM_CONFIG_INTERCEPT_HCALL, | ||
.msr = XEN_HYPERCALL_MSR, | ||
}; | ||
vm_ioctl(vm, KVM_XEN_HVM_CONFIG, &hvmc); | ||
|
||
struct kvm_xen_hvm_attr lm = { | ||
.type = KVM_XEN_ATTR_TYPE_LONG_MODE, | ||
.u.long_mode = 1, | ||
}; | ||
vm_ioctl(vm, KVM_XEN_HVM_SET_ATTR, &lm); | ||
|
||
struct kvm_xen_hvm_attr ha = { | ||
.type = KVM_XEN_ATTR_TYPE_SHARED_INFO, | ||
.u.shared_info.gfn = SHINFO_REGION_GPA / PAGE_SIZE, | ||
}; | ||
vm_ioctl(vm, KVM_XEN_HVM_SET_ATTR, &ha); | ||
|
||
struct kvm_xen_vcpu_attr vi = { | ||
.type = KVM_XEN_VCPU_ATTR_TYPE_VCPU_INFO, | ||
.u.gpa = SHINFO_REGION_GPA + 40, | ||
}; | ||
vcpu_ioctl(vm, VCPU_ID, KVM_XEN_VCPU_SET_ATTR, &vi); | ||
|
||
struct kvm_xen_vcpu_attr pvclock = { | ||
.type = KVM_XEN_VCPU_ATTR_TYPE_VCPU_TIME_INFO, | ||
.u.gpa = PVTIME_ADDR, | ||
}; | ||
vcpu_ioctl(vm, VCPU_ID, KVM_XEN_VCPU_SET_ATTR, &pvclock); | ||
|
||
for (;;) { | ||
volatile struct kvm_run *run = vcpu_state(vm, VCPU_ID); | ||
struct ucall uc; | ||
|
||
vcpu_run(vm, VCPU_ID); | ||
|
||
TEST_ASSERT(run->exit_reason == KVM_EXIT_IO, | ||
"Got exit_reason other than KVM_EXIT_IO: %u (%s)\n", | ||
run->exit_reason, | ||
exit_reason_str(run->exit_reason)); | ||
|
||
switch (get_ucall(vm, VCPU_ID, &uc)) { | ||
case UCALL_ABORT: | ||
TEST_FAIL("%s", (const char *)uc.args[0]); | ||
/* NOT REACHED */ | ||
case UCALL_SYNC: | ||
break; | ||
case UCALL_DONE: | ||
goto done; | ||
default: | ||
TEST_FAIL("Unknown ucall 0x%lx.", uc.cmd); | ||
} | ||
} | ||
|
||
done: | ||
clock_gettime(CLOCK_REALTIME, &max_ts); | ||
|
||
/* | ||
* Just a *really* basic check that things are being put in the | ||
* right place. The actual calculations are much the same for | ||
* Xen as they are for the KVM variants, so no need to check. | ||
*/ | ||
struct pvclock_wall_clock *wc; | ||
struct pvclock_vcpu_time_info *ti, *ti2; | ||
|
||
wc = addr_gva2hva(vm, SHINFO_REGION_GPA + 0xc00); | ||
ti = addr_gva2hva(vm, SHINFO_REGION_GPA + 0x40 + 0x20); | ||
ti2 = addr_gva2hva(vm, PVTIME_ADDR); | ||
|
||
vm_ts.tv_sec = wc->sec; | ||
vm_ts.tv_nsec = wc->nsec; | ||
TEST_ASSERT(wc->version && !(wc->version & 1), | ||
"Bad wallclock version %x", wc->version); | ||
TEST_ASSERT(cmp_timespec(&min_ts, &vm_ts) <= 0, "VM time too old"); | ||
TEST_ASSERT(cmp_timespec(&max_ts, &vm_ts) >= 0, "VM time too new"); | ||
|
||
TEST_ASSERT(ti->version && !(ti->version & 1), | ||
"Bad time_info version %x", ti->version); | ||
TEST_ASSERT(ti2->version && !(ti2->version & 1), | ||
"Bad time_info version %x", ti->version); | ||
|
||
kvm_vm_free(vm); | ||
return 0; | ||
} |