system_counter_offset_test.c 2.7 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
// 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)
{
33
	if (!__vcpu_has_device_attr(vm, VCPU_ID, KVM_VCPU_TSC_CTRL, KVM_VCPU_TSC_OFFSET))
34 35 36 37 38 39 40 41
		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)
{
42 43
	vcpu_device_attr_set(vm, VCPU_ID, KVM_VCPU_TSC_CTRL,
			     KVM_VCPU_TSC_OFFSET, &test->tsc_offset);
44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132
}

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);
}