1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * Copyright (C) 2021, Google LLC. |
4 | * |
5 | * Tests for adjusting the system counter from userspace |
6 | */ |
7 | #include <asm/kvm_para.h> |
8 | #include <stdint.h> |
9 | #include <string.h> |
10 | #include <sys/stat.h> |
11 | #include <time.h> |
12 | |
13 | #include "test_util.h" |
14 | #include "kvm_util.h" |
15 | #include "processor.h" |
16 | |
17 | #ifdef __x86_64__ |
18 | |
19 | struct test_case { |
20 | uint64_t tsc_offset; |
21 | }; |
22 | |
23 | static struct test_case test_cases[] = { |
24 | { 0 }, |
25 | { 180 * NSEC_PER_SEC }, |
26 | { -180 * NSEC_PER_SEC }, |
27 | }; |
28 | |
29 | static void check_preconditions(struct kvm_vcpu *vcpu) |
30 | { |
31 | __TEST_REQUIRE(!__vcpu_has_device_attr(vcpu, KVM_VCPU_TSC_CTRL, |
32 | KVM_VCPU_TSC_OFFSET), |
33 | "KVM_VCPU_TSC_OFFSET not supported; skipping test" ); |
34 | } |
35 | |
36 | static void setup_system_counter(struct kvm_vcpu *vcpu, struct test_case *test) |
37 | { |
38 | vcpu_device_attr_set(vcpu, KVM_VCPU_TSC_CTRL, KVM_VCPU_TSC_OFFSET, |
39 | &test->tsc_offset); |
40 | } |
41 | |
42 | static uint64_t guest_read_system_counter(struct test_case *test) |
43 | { |
44 | return rdtsc(); |
45 | } |
46 | |
47 | static uint64_t host_read_guest_system_counter(struct test_case *test) |
48 | { |
49 | return rdtsc() + test->tsc_offset; |
50 | } |
51 | |
52 | #else /* __x86_64__ */ |
53 | |
54 | #error test not implemented for this architecture! |
55 | |
56 | #endif |
57 | |
58 | #define GUEST_SYNC_CLOCK(__stage, __val) \ |
59 | GUEST_SYNC_ARGS(__stage, __val, 0, 0, 0) |
60 | |
61 | static void guest_main(void) |
62 | { |
63 | int i; |
64 | |
65 | for (i = 0; i < ARRAY_SIZE(test_cases); i++) { |
66 | struct test_case *test = &test_cases[i]; |
67 | |
68 | GUEST_SYNC_CLOCK(i, guest_read_system_counter(test)); |
69 | } |
70 | } |
71 | |
72 | static void handle_sync(struct ucall *uc, uint64_t start, uint64_t end) |
73 | { |
74 | uint64_t obs = uc->args[2]; |
75 | |
76 | TEST_ASSERT(start <= obs && obs <= end, |
77 | "unexpected system counter value: %" PRIu64" expected range: [%" PRIu64", %" PRIu64"]" , |
78 | obs, start, end); |
79 | |
80 | pr_info("system counter value: %" PRIu64" expected range [%" PRIu64", %" PRIu64"]\n" , |
81 | obs, start, end); |
82 | } |
83 | |
84 | static void handle_abort(struct ucall *uc) |
85 | { |
86 | REPORT_GUEST_ASSERT(*uc); |
87 | } |
88 | |
89 | static void enter_guest(struct kvm_vcpu *vcpu) |
90 | { |
91 | uint64_t start, end; |
92 | struct ucall uc; |
93 | int i; |
94 | |
95 | for (i = 0; i < ARRAY_SIZE(test_cases); i++) { |
96 | struct test_case *test = &test_cases[i]; |
97 | |
98 | setup_system_counter(vcpu, test); |
99 | start = host_read_guest_system_counter(test); |
100 | vcpu_run(vcpu); |
101 | end = host_read_guest_system_counter(test); |
102 | |
103 | switch (get_ucall(vcpu, &uc)) { |
104 | case UCALL_SYNC: |
105 | handle_sync(uc: &uc, start, end); |
106 | break; |
107 | case UCALL_ABORT: |
108 | handle_abort(uc: &uc); |
109 | return; |
110 | default: |
111 | TEST_ASSERT(0, "unhandled ucall %ld" , |
112 | get_ucall(vcpu, &uc)); |
113 | } |
114 | } |
115 | } |
116 | |
117 | int main(void) |
118 | { |
119 | struct kvm_vcpu *vcpu; |
120 | struct kvm_vm *vm; |
121 | |
122 | vm = vm_create_with_one_vcpu(&vcpu, guest_main); |
123 | check_preconditions(vcpu); |
124 | |
125 | enter_guest(vcpu); |
126 | kvm_vm_free(vm); |
127 | } |
128 | |