linux/tools/testing/selftests/kvm/x86/kvm_clock_test.c
Sean Christopherson 85819fa0e3 KVM: selftests: Drop "vaddr_" from APIs that allocate memory for a given VM
Now that KVM selftests use gva_t instead of vm_vaddr_t, drop "vaddr_" from
the core memory allocation APIs as the information is extraneous and does
more harm than good.  E.g. the APIs don't _just_ allocate virtual memory,
they allocate backing physical memory and install mappings in the guest
page tables.  And as proven by kmalloc() and malloc(), developers generally
expect that allocations come with a working virtual address.

Opportunistically clean up the function comment for vm_alloc(), and drop
the misleading and superfluous comments for its wrappers.

No functional change intended.

Link: https://patch.msgid.link/20260420212004.3938325-12-seanjc@google.com
Signed-off-by: Sean Christopherson <seanjc@google.com>
2026-04-20 14:54:17 -07:00

157 lines
3.5 KiB
C

// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (C) 2021, Google LLC.
*
* Tests for adjusting the KVM clock from userspace
*/
#include <asm/kvm_para.h>
#include <asm/pvclock.h>
#include <asm/pvclock-abi.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"
struct test_case {
u64 kvmclock_base;
s64 realtime_offset;
};
static struct test_case test_cases[] = {
{ .kvmclock_base = 0 },
{ .kvmclock_base = 180 * NSEC_PER_SEC },
{ .kvmclock_base = 0, .realtime_offset = -180 * NSEC_PER_SEC },
{ .kvmclock_base = 0, .realtime_offset = 180 * NSEC_PER_SEC },
};
#define GUEST_SYNC_CLOCK(__stage, __val) \
GUEST_SYNC_ARGS(__stage, __val, 0, 0, 0)
static void guest_main(gpa_t pvti_pa, struct pvclock_vcpu_time_info *pvti)
{
int i;
wrmsr(MSR_KVM_SYSTEM_TIME_NEW, pvti_pa | KVM_MSR_ENABLED);
for (i = 0; i < ARRAY_SIZE(test_cases); i++)
GUEST_SYNC_CLOCK(i, __pvclock_read_cycles(pvti, rdtsc()));
}
#define EXPECTED_FLAGS (KVM_CLOCK_REALTIME | KVM_CLOCK_HOST_TSC)
static inline void assert_flags(struct kvm_clock_data *data)
{
TEST_ASSERT((data->flags & EXPECTED_FLAGS) == EXPECTED_FLAGS,
"unexpected clock data flags: %x (want set: %x)",
data->flags, EXPECTED_FLAGS);
}
static void handle_sync(struct ucall *uc, struct kvm_clock_data *start,
struct kvm_clock_data *end)
{
u64 obs, exp_lo, exp_hi;
obs = uc->args[2];
exp_lo = start->clock;
exp_hi = end->clock;
assert_flags(start);
assert_flags(end);
TEST_ASSERT(exp_lo <= obs && obs <= exp_hi,
"unexpected kvm-clock value: %"PRIu64" expected range: [%"PRIu64", %"PRIu64"]",
obs, exp_lo, exp_hi);
pr_info("kvm-clock value: %"PRIu64" expected range [%"PRIu64", %"PRIu64"]\n",
obs, exp_lo, exp_hi);
}
static void handle_abort(struct ucall *uc)
{
REPORT_GUEST_ASSERT(*uc);
}
static void setup_clock(struct kvm_vm *vm, struct test_case *test_case)
{
struct kvm_clock_data data;
memset(&data, 0, sizeof(data));
data.clock = test_case->kvmclock_base;
if (test_case->realtime_offset) {
struct timespec ts;
int r;
data.flags |= KVM_CLOCK_REALTIME;
do {
r = clock_gettime(CLOCK_REALTIME, &ts);
if (!r)
break;
} while (errno == EINTR);
TEST_ASSERT(!r, "clock_gettime() failed: %d", r);
data.realtime = ts.tv_sec * NSEC_PER_SEC;
data.realtime += ts.tv_nsec;
data.realtime += test_case->realtime_offset;
}
vm_ioctl(vm, KVM_SET_CLOCK, &data);
}
static void enter_guest(struct kvm_vcpu *vcpu)
{
struct kvm_clock_data start, end;
struct kvm_vm *vm = vcpu->vm;
struct ucall uc;
int i;
for (i = 0; i < ARRAY_SIZE(test_cases); i++) {
setup_clock(vm, &test_cases[i]);
vm_ioctl(vm, KVM_GET_CLOCK, &start);
vcpu_run(vcpu);
vm_ioctl(vm, KVM_GET_CLOCK, &end);
TEST_ASSERT_KVM_EXIT_REASON(vcpu, KVM_EXIT_IO);
switch (get_ucall(vcpu, &uc)) {
case UCALL_SYNC:
handle_sync(&uc, &start, &end);
break;
case UCALL_ABORT:
handle_abort(&uc);
return;
default:
TEST_ASSERT(0, "unhandled ucall: %ld", uc.cmd);
}
}
}
int main(void)
{
struct kvm_vcpu *vcpu;
gva_t pvti_gva;
gpa_t pvti_gpa;
struct kvm_vm *vm;
int flags;
flags = kvm_check_cap(KVM_CAP_ADJUST_CLOCK);
TEST_REQUIRE(flags & KVM_CLOCK_REALTIME);
TEST_REQUIRE(sys_clocksource_is_based_on_tsc());
vm = vm_create_with_one_vcpu(&vcpu, guest_main);
pvti_gva = vm_alloc(vm, getpagesize(), 0x10000);
pvti_gpa = addr_gva2gpa(vm, pvti_gva);
vcpu_args_set(vcpu, 2, pvti_gpa, pvti_gva);
enter_guest(vcpu);
kvm_vm_free(vm);
}