-
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.
selftests: KVM: Introduce system counter offset test
Introduce a KVM selftest to verify that userspace manipulation of the TSC (via the new vCPU attribute) results in the correct behavior within the guest. Reviewed-by: Andrew Jones <drjones@redhat.com> Signed-off-by: Oliver Upton <oupton@google.com> Message-Id: <20210916181555.973085-6-oupton@google.com> Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
- Loading branch information
Oliver Upton
authored and
Paolo Bonzini
committed
Oct 18, 2021
1 parent
c895513
commit 3f9808c
Showing
4 changed files
with
161 additions
and
14 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
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -54,3 +54,4 @@ | |
/set_memory_region_test | ||
/steal_time | ||
/kvm_binary_stats_test | ||
/system_counter_offset_test |
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
132 changes: 132 additions & 0 deletions
132
tools/testing/selftests/kvm/system_counter_offset_test.c
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,132 @@ | ||
// SPDX-License-Identifier: GPL-2.0-only | ||
/* | ||
* Copyright (C) 2021, Google LLC. | ||
* | ||
* Tests for adjusting the system counter from userspace | ||
*/ | ||
#include <asm/kvm_para.h> | ||
#include <stdint.h> | ||
#include <string.h> | ||
#include <sys/stat.h> | ||
#include <time.h> | ||
|
||
#include "test_util.h" | ||
#include "kvm_util.h" | ||
#include "processor.h" | ||
|
||
#define VCPU_ID 0 | ||
|
||
#ifdef __x86_64__ | ||
|
||
struct test_case { | ||
uint64_t tsc_offset; | ||
}; | ||
|
||
static struct test_case test_cases[] = { | ||
{ 0 }, | ||
{ 180 * NSEC_PER_SEC }, | ||
{ -180 * NSEC_PER_SEC }, | ||
}; | ||
|
||
static void check_preconditions(struct kvm_vm *vm) | ||
{ | ||
if (!_vcpu_has_device_attr(vm, VCPU_ID, KVM_VCPU_TSC_CTRL, KVM_VCPU_TSC_OFFSET)) | ||
return; | ||
|
||
print_skip("KVM_VCPU_TSC_OFFSET not supported; skipping test"); | ||
exit(KSFT_SKIP); | ||
} | ||
|
||
static void setup_system_counter(struct kvm_vm *vm, struct test_case *test) | ||
{ | ||
vcpu_access_device_attr(vm, VCPU_ID, KVM_VCPU_TSC_CTRL, | ||
KVM_VCPU_TSC_OFFSET, &test->tsc_offset, true); | ||
} | ||
|
||
static uint64_t guest_read_system_counter(struct test_case *test) | ||
{ | ||
return rdtsc(); | ||
} | ||
|
||
static uint64_t host_read_guest_system_counter(struct test_case *test) | ||
{ | ||
return rdtsc() + test->tsc_offset; | ||
} | ||
|
||
#else /* __x86_64__ */ | ||
|
||
#error test not implemented for this architecture! | ||
|
||
#endif | ||
|
||
#define GUEST_SYNC_CLOCK(__stage, __val) \ | ||
GUEST_SYNC_ARGS(__stage, __val, 0, 0, 0) | ||
|
||
static void guest_main(void) | ||
{ | ||
int i; | ||
|
||
for (i = 0; i < ARRAY_SIZE(test_cases); i++) { | ||
struct test_case *test = &test_cases[i]; | ||
|
||
GUEST_SYNC_CLOCK(i, guest_read_system_counter(test)); | ||
} | ||
} | ||
|
||
static void handle_sync(struct ucall *uc, uint64_t start, uint64_t end) | ||
{ | ||
uint64_t obs = uc->args[2]; | ||
|
||
TEST_ASSERT(start <= obs && obs <= end, | ||
"unexpected system counter value: %"PRIu64" expected range: [%"PRIu64", %"PRIu64"]", | ||
obs, start, end); | ||
|
||
pr_info("system counter value: %"PRIu64" expected range [%"PRIu64", %"PRIu64"]\n", | ||
obs, start, end); | ||
} | ||
|
||
static void handle_abort(struct ucall *uc) | ||
{ | ||
TEST_FAIL("%s at %s:%ld", (const char *)uc->args[0], | ||
__FILE__, uc->args[1]); | ||
} | ||
|
||
static void enter_guest(struct kvm_vm *vm) | ||
{ | ||
uint64_t start, end; | ||
struct ucall uc; | ||
int i; | ||
|
||
for (i = 0; i < ARRAY_SIZE(test_cases); i++) { | ||
struct test_case *test = &test_cases[i]; | ||
|
||
setup_system_counter(vm, test); | ||
start = host_read_guest_system_counter(test); | ||
vcpu_run(vm, VCPU_ID); | ||
end = host_read_guest_system_counter(test); | ||
|
||
switch (get_ucall(vm, VCPU_ID, &uc)) { | ||
case UCALL_SYNC: | ||
handle_sync(&uc, start, end); | ||
break; | ||
case UCALL_ABORT: | ||
handle_abort(&uc); | ||
return; | ||
default: | ||
TEST_ASSERT(0, "unhandled ucall %ld\n", | ||
get_ucall(vm, VCPU_ID, &uc)); | ||
} | ||
} | ||
} | ||
|
||
int main(void) | ||
{ | ||
struct kvm_vm *vm; | ||
|
||
vm = vm_create_default(VCPU_ID, 0, guest_main); | ||
check_preconditions(vm); | ||
ucall_init(vm, NULL); | ||
|
||
enter_guest(vm); | ||
kvm_vm_free(vm); | ||
} |