mirror of
https://github.com/torvalds/linux.git
synced 2026-05-28 09:04:39 +02:00
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>
157 lines
3.5 KiB
C
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);
|
|
}
|