cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

kvm_clock_test.c (4712B)


      1// SPDX-License-Identifier: GPL-2.0-only
      2/*
      3 * Copyright (C) 2021, Google LLC.
      4 *
      5 * Tests for adjusting the KVM clock from userspace
      6 */
      7#include <asm/kvm_para.h>
      8#include <asm/pvclock.h>
      9#include <asm/pvclock-abi.h>
     10#include <stdint.h>
     11#include <string.h>
     12#include <sys/stat.h>
     13#include <time.h>
     14
     15#include "test_util.h"
     16#include "kvm_util.h"
     17#include "processor.h"
     18
     19#define VCPU_ID 0
     20
     21struct test_case {
     22	uint64_t kvmclock_base;
     23	int64_t realtime_offset;
     24};
     25
     26static struct test_case test_cases[] = {
     27	{ .kvmclock_base = 0 },
     28	{ .kvmclock_base = 180 * NSEC_PER_SEC },
     29	{ .kvmclock_base = 0, .realtime_offset = -180 * NSEC_PER_SEC },
     30	{ .kvmclock_base = 0, .realtime_offset = 180 * NSEC_PER_SEC },
     31};
     32
     33#define GUEST_SYNC_CLOCK(__stage, __val)			\
     34		GUEST_SYNC_ARGS(__stage, __val, 0, 0, 0)
     35
     36static void guest_main(vm_paddr_t pvti_pa, struct pvclock_vcpu_time_info *pvti)
     37{
     38	int i;
     39
     40	wrmsr(MSR_KVM_SYSTEM_TIME_NEW, pvti_pa | KVM_MSR_ENABLED);
     41	for (i = 0; i < ARRAY_SIZE(test_cases); i++)
     42		GUEST_SYNC_CLOCK(i, __pvclock_read_cycles(pvti, rdtsc()));
     43}
     44
     45#define EXPECTED_FLAGS (KVM_CLOCK_REALTIME | KVM_CLOCK_HOST_TSC)
     46
     47static inline void assert_flags(struct kvm_clock_data *data)
     48{
     49	TEST_ASSERT((data->flags & EXPECTED_FLAGS) == EXPECTED_FLAGS,
     50		    "unexpected clock data flags: %x (want set: %x)",
     51		    data->flags, EXPECTED_FLAGS);
     52}
     53
     54static void handle_sync(struct ucall *uc, struct kvm_clock_data *start,
     55			struct kvm_clock_data *end)
     56{
     57	uint64_t obs, exp_lo, exp_hi;
     58
     59	obs = uc->args[2];
     60	exp_lo = start->clock;
     61	exp_hi = end->clock;
     62
     63	assert_flags(start);
     64	assert_flags(end);
     65
     66	TEST_ASSERT(exp_lo <= obs && obs <= exp_hi,
     67		    "unexpected kvm-clock value: %"PRIu64" expected range: [%"PRIu64", %"PRIu64"]",
     68		    obs, exp_lo, exp_hi);
     69
     70	pr_info("kvm-clock value: %"PRIu64" expected range [%"PRIu64", %"PRIu64"]\n",
     71		obs, exp_lo, exp_hi);
     72}
     73
     74static void handle_abort(struct ucall *uc)
     75{
     76	TEST_FAIL("%s at %s:%ld", (const char *)uc->args[0],
     77		  __FILE__, uc->args[1]);
     78}
     79
     80static void setup_clock(struct kvm_vm *vm, struct test_case *test_case)
     81{
     82	struct kvm_clock_data data;
     83
     84	memset(&data, 0, sizeof(data));
     85
     86	data.clock = test_case->kvmclock_base;
     87	if (test_case->realtime_offset) {
     88		struct timespec ts;
     89		int r;
     90
     91		data.flags |= KVM_CLOCK_REALTIME;
     92		do {
     93			r = clock_gettime(CLOCK_REALTIME, &ts);
     94			if (!r)
     95				break;
     96		} while (errno == EINTR);
     97
     98		TEST_ASSERT(!r, "clock_gettime() failed: %d\n", r);
     99
    100		data.realtime = ts.tv_sec * NSEC_PER_SEC;
    101		data.realtime += ts.tv_nsec;
    102		data.realtime += test_case->realtime_offset;
    103	}
    104
    105	vm_ioctl(vm, KVM_SET_CLOCK, &data);
    106}
    107
    108static void enter_guest(struct kvm_vm *vm)
    109{
    110	struct kvm_clock_data start, end;
    111	struct kvm_run *run;
    112	struct ucall uc;
    113	int i, r;
    114
    115	run = vcpu_state(vm, VCPU_ID);
    116
    117	for (i = 0; i < ARRAY_SIZE(test_cases); i++) {
    118		setup_clock(vm, &test_cases[i]);
    119
    120		vm_ioctl(vm, KVM_GET_CLOCK, &start);
    121
    122		r = _vcpu_run(vm, VCPU_ID);
    123		vm_ioctl(vm, KVM_GET_CLOCK, &end);
    124
    125		TEST_ASSERT(!r, "vcpu_run failed: %d\n", r);
    126		TEST_ASSERT(run->exit_reason == KVM_EXIT_IO,
    127			    "unexpected exit reason: %u (%s)",
    128			    run->exit_reason, exit_reason_str(run->exit_reason));
    129
    130		switch (get_ucall(vm, VCPU_ID, &uc)) {
    131		case UCALL_SYNC:
    132			handle_sync(&uc, &start, &end);
    133			break;
    134		case UCALL_ABORT:
    135			handle_abort(&uc);
    136			return;
    137		default:
    138			TEST_ASSERT(0, "unhandled ucall: %ld\n", uc.cmd);
    139		}
    140	}
    141}
    142
    143#define CLOCKSOURCE_PATH "/sys/devices/system/clocksource/clocksource0/current_clocksource"
    144
    145static void check_clocksource(void)
    146{
    147	char *clk_name;
    148	struct stat st;
    149	FILE *fp;
    150
    151	fp = fopen(CLOCKSOURCE_PATH, "r");
    152	if (!fp) {
    153		pr_info("failed to open clocksource file: %d; assuming TSC.\n",
    154			errno);
    155		return;
    156	}
    157
    158	if (fstat(fileno(fp), &st)) {
    159		pr_info("failed to stat clocksource file: %d; assuming TSC.\n",
    160			errno);
    161		goto out;
    162	}
    163
    164	clk_name = malloc(st.st_size);
    165	TEST_ASSERT(clk_name, "failed to allocate buffer to read file\n");
    166
    167	if (!fgets(clk_name, st.st_size, fp)) {
    168		pr_info("failed to read clocksource file: %d; assuming TSC.\n",
    169			ferror(fp));
    170		goto out;
    171	}
    172
    173	TEST_ASSERT(!strncmp(clk_name, "tsc\n", st.st_size),
    174		    "clocksource not supported: %s", clk_name);
    175out:
    176	fclose(fp);
    177}
    178
    179int main(void)
    180{
    181	vm_vaddr_t pvti_gva;
    182	vm_paddr_t pvti_gpa;
    183	struct kvm_vm *vm;
    184	int flags;
    185
    186	flags = kvm_check_cap(KVM_CAP_ADJUST_CLOCK);
    187	if (!(flags & KVM_CLOCK_REALTIME)) {
    188		print_skip("KVM_CLOCK_REALTIME not supported; flags: %x",
    189			   flags);
    190		exit(KSFT_SKIP);
    191	}
    192
    193	check_clocksource();
    194
    195	vm = vm_create_default(VCPU_ID, 0, guest_main);
    196
    197	pvti_gva = vm_vaddr_alloc(vm, getpagesize(), 0x10000);
    198	pvti_gpa = addr_gva2gpa(vm, pvti_gva);
    199	vcpu_args_set(vm, VCPU_ID, 2, pvti_gpa, pvti_gva);
    200
    201	enter_guest(vm);
    202	kvm_vm_free(vm);
    203}