system_counter_offset_test.c (2765B)
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#define VCPU_ID 0 18 19#ifdef __x86_64__ 20 21struct test_case { 22 uint64_t tsc_offset; 23}; 24 25static struct test_case test_cases[] = { 26 { 0 }, 27 { 180 * NSEC_PER_SEC }, 28 { -180 * NSEC_PER_SEC }, 29}; 30 31static void check_preconditions(struct kvm_vm *vm) 32{ 33 if (!_vcpu_has_device_attr(vm, VCPU_ID, KVM_VCPU_TSC_CTRL, KVM_VCPU_TSC_OFFSET)) 34 return; 35 36 print_skip("KVM_VCPU_TSC_OFFSET not supported; skipping test"); 37 exit(KSFT_SKIP); 38} 39 40static void setup_system_counter(struct kvm_vm *vm, struct test_case *test) 41{ 42 vcpu_access_device_attr(vm, VCPU_ID, KVM_VCPU_TSC_CTRL, 43 KVM_VCPU_TSC_OFFSET, &test->tsc_offset, true); 44} 45 46static uint64_t guest_read_system_counter(struct test_case *test) 47{ 48 return rdtsc(); 49} 50 51static uint64_t host_read_guest_system_counter(struct test_case *test) 52{ 53 return rdtsc() + test->tsc_offset; 54} 55 56#else /* __x86_64__ */ 57 58#error test not implemented for this architecture! 59 60#endif 61 62#define GUEST_SYNC_CLOCK(__stage, __val) \ 63 GUEST_SYNC_ARGS(__stage, __val, 0, 0, 0) 64 65static void guest_main(void) 66{ 67 int i; 68 69 for (i = 0; i < ARRAY_SIZE(test_cases); i++) { 70 struct test_case *test = &test_cases[i]; 71 72 GUEST_SYNC_CLOCK(i, guest_read_system_counter(test)); 73 } 74} 75 76static void handle_sync(struct ucall *uc, uint64_t start, uint64_t end) 77{ 78 uint64_t obs = uc->args[2]; 79 80 TEST_ASSERT(start <= obs && obs <= end, 81 "unexpected system counter value: %"PRIu64" expected range: [%"PRIu64", %"PRIu64"]", 82 obs, start, end); 83 84 pr_info("system counter value: %"PRIu64" expected range [%"PRIu64", %"PRIu64"]\n", 85 obs, start, end); 86} 87 88static void handle_abort(struct ucall *uc) 89{ 90 TEST_FAIL("%s at %s:%ld", (const char *)uc->args[0], 91 __FILE__, uc->args[1]); 92} 93 94static void enter_guest(struct kvm_vm *vm) 95{ 96 uint64_t start, end; 97 struct ucall uc; 98 int i; 99 100 for (i = 0; i < ARRAY_SIZE(test_cases); i++) { 101 struct test_case *test = &test_cases[i]; 102 103 setup_system_counter(vm, test); 104 start = host_read_guest_system_counter(test); 105 vcpu_run(vm, VCPU_ID); 106 end = host_read_guest_system_counter(test); 107 108 switch (get_ucall(vm, VCPU_ID, &uc)) { 109 case UCALL_SYNC: 110 handle_sync(&uc, start, end); 111 break; 112 case UCALL_ABORT: 113 handle_abort(&uc); 114 return; 115 default: 116 TEST_ASSERT(0, "unhandled ucall %ld\n", 117 get_ucall(vm, VCPU_ID, &uc)); 118 } 119 } 120} 121 122int main(void) 123{ 124 struct kvm_vm *vm; 125 126 vm = vm_create_default(VCPU_ID, 0, guest_main); 127 check_preconditions(vm); 128 ucall_init(vm, NULL); 129 130 enter_guest(vm); 131 kvm_vm_free(vm); 132}