194c4b76bSAndrew Jones // SPDX-License-Identifier: GPL-2.0
294c4b76bSAndrew Jones /*
394c4b76bSAndrew Jones * steal/stolen time test
494c4b76bSAndrew Jones *
594c4b76bSAndrew Jones * Copyright (C) 2020, Red Hat, Inc.
694c4b76bSAndrew Jones */
794c4b76bSAndrew Jones #define _GNU_SOURCE
894c4b76bSAndrew Jones #include <stdio.h>
994c4b76bSAndrew Jones #include <time.h>
1094c4b76bSAndrew Jones #include <sched.h>
1194c4b76bSAndrew Jones #include <pthread.h>
1294c4b76bSAndrew Jones #include <linux/kernel.h>
1394c4b76bSAndrew Jones #include <asm/kvm.h>
1494c4b76bSAndrew Jones #include <asm/kvm_para.h>
1594c4b76bSAndrew Jones
1694c4b76bSAndrew Jones #include "test_util.h"
1794c4b76bSAndrew Jones #include "kvm_util.h"
1894c4b76bSAndrew Jones #include "processor.h"
1994c4b76bSAndrew Jones
2094c4b76bSAndrew Jones #define NR_VCPUS 4
2194c4b76bSAndrew Jones #define ST_GPA_BASE (1 << 30)
2294c4b76bSAndrew Jones
2394c4b76bSAndrew Jones static void *st_gva[NR_VCPUS];
2494c4b76bSAndrew Jones static uint64_t guest_stolen_time[NR_VCPUS];
2594c4b76bSAndrew Jones
2694c4b76bSAndrew Jones #if defined(__x86_64__)
2794c4b76bSAndrew Jones
2894c4b76bSAndrew Jones /* steal_time must have 64-byte alignment */
2994c4b76bSAndrew Jones #define STEAL_TIME_SIZE ((sizeof(struct kvm_steal_time) + 63) & ~63)
3094c4b76bSAndrew Jones
check_status(struct kvm_steal_time * st)3194c4b76bSAndrew Jones static void check_status(struct kvm_steal_time *st)
3294c4b76bSAndrew Jones {
3394c4b76bSAndrew Jones GUEST_ASSERT(!(READ_ONCE(st->version) & 1));
34*3d9bd831SSean Christopherson GUEST_ASSERT_EQ(READ_ONCE(st->flags), 0);
35*3d9bd831SSean Christopherson GUEST_ASSERT_EQ(READ_ONCE(st->preempted), 0);
3694c4b76bSAndrew Jones }
3794c4b76bSAndrew Jones
guest_code(int cpu)3894c4b76bSAndrew Jones static void guest_code(int cpu)
3994c4b76bSAndrew Jones {
4094c4b76bSAndrew Jones struct kvm_steal_time *st = st_gva[cpu];
4194c4b76bSAndrew Jones uint32_t version;
4294c4b76bSAndrew Jones
43*3d9bd831SSean Christopherson GUEST_ASSERT_EQ(rdmsr(MSR_KVM_STEAL_TIME), ((uint64_t)st_gva[cpu] | KVM_MSR_ENABLED));
4494c4b76bSAndrew Jones
4594c4b76bSAndrew Jones memset(st, 0, sizeof(*st));
4694c4b76bSAndrew Jones GUEST_SYNC(0);
4794c4b76bSAndrew Jones
4894c4b76bSAndrew Jones check_status(st);
4994c4b76bSAndrew Jones WRITE_ONCE(guest_stolen_time[cpu], st->steal);
5094c4b76bSAndrew Jones version = READ_ONCE(st->version);
5194c4b76bSAndrew Jones check_status(st);
5294c4b76bSAndrew Jones GUEST_SYNC(1);
5394c4b76bSAndrew Jones
5494c4b76bSAndrew Jones check_status(st);
5594c4b76bSAndrew Jones GUEST_ASSERT(version < READ_ONCE(st->version));
5694c4b76bSAndrew Jones WRITE_ONCE(guest_stolen_time[cpu], st->steal);
5794c4b76bSAndrew Jones check_status(st);
5894c4b76bSAndrew Jones GUEST_DONE();
5994c4b76bSAndrew Jones }
6094c4b76bSAndrew Jones
is_steal_time_supported(struct kvm_vcpu * vcpu)6199801604SSean Christopherson static bool is_steal_time_supported(struct kvm_vcpu *vcpu)
6294c4b76bSAndrew Jones {
63601c067fSSean Christopherson return kvm_cpu_has(X86_FEATURE_KVM_STEAL_TIME);
6494c4b76bSAndrew Jones }
6594c4b76bSAndrew Jones
steal_time_init(struct kvm_vcpu * vcpu,uint32_t i)6699801604SSean Christopherson static void steal_time_init(struct kvm_vcpu *vcpu, uint32_t i)
6799801604SSean Christopherson {
6894c4b76bSAndrew Jones int ret;
6994c4b76bSAndrew Jones
7094c4b76bSAndrew Jones /* ST_GPA_BASE is identity mapped */
7194c4b76bSAndrew Jones st_gva[i] = (void *)(ST_GPA_BASE + i * STEAL_TIME_SIZE);
7299801604SSean Christopherson sync_global_to_guest(vcpu->vm, st_gva[i]);
7394c4b76bSAndrew Jones
74768e9a61SSean Christopherson ret = _vcpu_set_msr(vcpu, MSR_KVM_STEAL_TIME,
7599801604SSean Christopherson (ulong)st_gva[i] | KVM_STEAL_RESERVED_MASK);
7694c4b76bSAndrew Jones TEST_ASSERT(ret == 0, "Bad GPA didn't fail");
7794c4b76bSAndrew Jones
78768e9a61SSean Christopherson vcpu_set_msr(vcpu, MSR_KVM_STEAL_TIME, (ulong)st_gva[i] | KVM_MSR_ENABLED);
7994c4b76bSAndrew Jones }
8094c4b76bSAndrew Jones
steal_time_dump(struct kvm_vm * vm,uint32_t vcpu_idx)8199801604SSean Christopherson static void steal_time_dump(struct kvm_vm *vm, uint32_t vcpu_idx)
8294c4b76bSAndrew Jones {
8399801604SSean Christopherson struct kvm_steal_time *st = addr_gva2hva(vm, (ulong)st_gva[vcpu_idx]);
8494c4b76bSAndrew Jones int i;
8594c4b76bSAndrew Jones
8699801604SSean Christopherson pr_info("VCPU%d:\n", vcpu_idx);
8794c4b76bSAndrew Jones pr_info(" steal: %lld\n", st->steal);
8894c4b76bSAndrew Jones pr_info(" version: %d\n", st->version);
8994c4b76bSAndrew Jones pr_info(" flags: %d\n", st->flags);
9094c4b76bSAndrew Jones pr_info(" preempted: %d\n", st->preempted);
9194c4b76bSAndrew Jones pr_info(" u8_pad: ");
9294c4b76bSAndrew Jones for (i = 0; i < 3; ++i)
9394c4b76bSAndrew Jones pr_info("%d", st->u8_pad[i]);
9494c4b76bSAndrew Jones pr_info("\n pad: ");
9594c4b76bSAndrew Jones for (i = 0; i < 11; ++i)
9694c4b76bSAndrew Jones pr_info("%d", st->pad[i]);
9794c4b76bSAndrew Jones pr_info("\n");
9894c4b76bSAndrew Jones }
9994c4b76bSAndrew Jones
10094c4b76bSAndrew Jones #elif defined(__aarch64__)
10194c4b76bSAndrew Jones
10294c4b76bSAndrew Jones /* PV_TIME_ST must have 64-byte alignment */
10394c4b76bSAndrew Jones #define STEAL_TIME_SIZE ((sizeof(struct st_time) + 63) & ~63)
10494c4b76bSAndrew Jones
10594c4b76bSAndrew Jones #define SMCCC_ARCH_FEATURES 0x80000001
10694c4b76bSAndrew Jones #define PV_TIME_FEATURES 0xc5000020
10794c4b76bSAndrew Jones #define PV_TIME_ST 0xc5000021
10894c4b76bSAndrew Jones
10994c4b76bSAndrew Jones struct st_time {
11094c4b76bSAndrew Jones uint32_t rev;
11194c4b76bSAndrew Jones uint32_t attr;
11294c4b76bSAndrew Jones uint64_t st_time;
11394c4b76bSAndrew Jones };
11494c4b76bSAndrew Jones
smccc(uint32_t func,uint64_t arg)11501f91acbSOliver Upton static int64_t smccc(uint32_t func, uint64_t arg)
11694c4b76bSAndrew Jones {
117e918e2bcSOliver Upton struct arm_smccc_res res;
11894c4b76bSAndrew Jones
119e918e2bcSOliver Upton smccc_hvc(func, arg, 0, 0, 0, 0, 0, 0, &res);
120e918e2bcSOliver Upton return res.a0;
12194c4b76bSAndrew Jones }
12294c4b76bSAndrew Jones
check_status(struct st_time * st)12394c4b76bSAndrew Jones static void check_status(struct st_time *st)
12494c4b76bSAndrew Jones {
125*3d9bd831SSean Christopherson GUEST_ASSERT_EQ(READ_ONCE(st->rev), 0);
126*3d9bd831SSean Christopherson GUEST_ASSERT_EQ(READ_ONCE(st->attr), 0);
12794c4b76bSAndrew Jones }
12894c4b76bSAndrew Jones
guest_code(int cpu)12994c4b76bSAndrew Jones static void guest_code(int cpu)
13094c4b76bSAndrew Jones {
13194c4b76bSAndrew Jones struct st_time *st;
13294c4b76bSAndrew Jones int64_t status;
13394c4b76bSAndrew Jones
13494c4b76bSAndrew Jones status = smccc(SMCCC_ARCH_FEATURES, PV_TIME_FEATURES);
135*3d9bd831SSean Christopherson GUEST_ASSERT_EQ(status, 0);
13694c4b76bSAndrew Jones status = smccc(PV_TIME_FEATURES, PV_TIME_FEATURES);
137*3d9bd831SSean Christopherson GUEST_ASSERT_EQ(status, 0);
13894c4b76bSAndrew Jones status = smccc(PV_TIME_FEATURES, PV_TIME_ST);
139*3d9bd831SSean Christopherson GUEST_ASSERT_EQ(status, 0);
14094c4b76bSAndrew Jones
14194c4b76bSAndrew Jones status = smccc(PV_TIME_ST, 0);
142*3d9bd831SSean Christopherson GUEST_ASSERT_NE(status, -1);
143*3d9bd831SSean Christopherson GUEST_ASSERT_EQ(status, (ulong)st_gva[cpu]);
14494c4b76bSAndrew Jones
14594c4b76bSAndrew Jones st = (struct st_time *)status;
14694c4b76bSAndrew Jones GUEST_SYNC(0);
14794c4b76bSAndrew Jones
14894c4b76bSAndrew Jones check_status(st);
14994c4b76bSAndrew Jones WRITE_ONCE(guest_stolen_time[cpu], st->st_time);
15094c4b76bSAndrew Jones GUEST_SYNC(1);
15194c4b76bSAndrew Jones
15294c4b76bSAndrew Jones check_status(st);
15394c4b76bSAndrew Jones WRITE_ONCE(guest_stolen_time[cpu], st->st_time);
15494c4b76bSAndrew Jones GUEST_DONE();
15594c4b76bSAndrew Jones }
15694c4b76bSAndrew Jones
is_steal_time_supported(struct kvm_vcpu * vcpu)15799801604SSean Christopherson static bool is_steal_time_supported(struct kvm_vcpu *vcpu)
15894c4b76bSAndrew Jones {
15994c4b76bSAndrew Jones struct kvm_device_attr dev = {
16094c4b76bSAndrew Jones .group = KVM_ARM_VCPU_PVTIME_CTRL,
16194c4b76bSAndrew Jones .attr = KVM_ARM_VCPU_PVTIME_IPA,
16294c4b76bSAndrew Jones };
16394c4b76bSAndrew Jones
164768e9a61SSean Christopherson return !__vcpu_ioctl(vcpu, KVM_HAS_DEVICE_ATTR, &dev);
16594c4b76bSAndrew Jones }
16694c4b76bSAndrew Jones
steal_time_init(struct kvm_vcpu * vcpu,uint32_t i)16799801604SSean Christopherson static void steal_time_init(struct kvm_vcpu *vcpu, uint32_t i)
16899801604SSean Christopherson {
16999801604SSean Christopherson struct kvm_vm *vm = vcpu->vm;
17094c4b76bSAndrew Jones uint64_t st_ipa;
17199801604SSean Christopherson int ret;
17294c4b76bSAndrew Jones
17399801604SSean Christopherson struct kvm_device_attr dev = {
17499801604SSean Christopherson .group = KVM_ARM_VCPU_PVTIME_CTRL,
17599801604SSean Christopherson .attr = KVM_ARM_VCPU_PVTIME_IPA,
17699801604SSean Christopherson .addr = (uint64_t)&st_ipa,
17799801604SSean Christopherson };
17894c4b76bSAndrew Jones
179768e9a61SSean Christopherson vcpu_ioctl(vcpu, KVM_HAS_DEVICE_ATTR, &dev);
18094c4b76bSAndrew Jones
18194c4b76bSAndrew Jones /* ST_GPA_BASE is identity mapped */
18294c4b76bSAndrew Jones st_gva[i] = (void *)(ST_GPA_BASE + i * STEAL_TIME_SIZE);
18394c4b76bSAndrew Jones sync_global_to_guest(vm, st_gva[i]);
18494c4b76bSAndrew Jones
18594c4b76bSAndrew Jones st_ipa = (ulong)st_gva[i] | 1;
186768e9a61SSean Christopherson ret = __vcpu_ioctl(vcpu, KVM_SET_DEVICE_ATTR, &dev);
18794c4b76bSAndrew Jones TEST_ASSERT(ret == -1 && errno == EINVAL, "Bad IPA didn't report EINVAL");
18894c4b76bSAndrew Jones
18994c4b76bSAndrew Jones st_ipa = (ulong)st_gva[i];
190768e9a61SSean Christopherson vcpu_ioctl(vcpu, KVM_SET_DEVICE_ATTR, &dev);
19194c4b76bSAndrew Jones
192768e9a61SSean Christopherson ret = __vcpu_ioctl(vcpu, KVM_SET_DEVICE_ATTR, &dev);
19394c4b76bSAndrew Jones TEST_ASSERT(ret == -1 && errno == EEXIST, "Set IPA twice without EEXIST");
19494c4b76bSAndrew Jones }
19594c4b76bSAndrew Jones
steal_time_dump(struct kvm_vm * vm,uint32_t vcpu_idx)19699801604SSean Christopherson static void steal_time_dump(struct kvm_vm *vm, uint32_t vcpu_idx)
19794c4b76bSAndrew Jones {
19899801604SSean Christopherson struct st_time *st = addr_gva2hva(vm, (ulong)st_gva[vcpu_idx]);
19994c4b76bSAndrew Jones
20099801604SSean Christopherson pr_info("VCPU%d:\n", vcpu_idx);
20194c4b76bSAndrew Jones pr_info(" rev: %d\n", st->rev);
20294c4b76bSAndrew Jones pr_info(" attr: %d\n", st->attr);
20394c4b76bSAndrew Jones pr_info(" st_time: %ld\n", st->st_time);
20494c4b76bSAndrew Jones }
20594c4b76bSAndrew Jones
20694c4b76bSAndrew Jones #endif
20794c4b76bSAndrew Jones
do_steal_time(void * arg)20894c4b76bSAndrew Jones static void *do_steal_time(void *arg)
20994c4b76bSAndrew Jones {
21094c4b76bSAndrew Jones struct timespec ts, stop;
21194c4b76bSAndrew Jones
21294c4b76bSAndrew Jones clock_gettime(CLOCK_MONOTONIC, &ts);
21394c4b76bSAndrew Jones stop = timespec_add_ns(ts, MIN_RUN_DELAY_NS);
21494c4b76bSAndrew Jones
21594c4b76bSAndrew Jones while (1) {
21694c4b76bSAndrew Jones clock_gettime(CLOCK_MONOTONIC, &ts);
217bfcaa849SAndrew Jones if (timespec_to_ns(timespec_sub(ts, stop)) >= 0)
21894c4b76bSAndrew Jones break;
21994c4b76bSAndrew Jones }
22094c4b76bSAndrew Jones
22194c4b76bSAndrew Jones return NULL;
22294c4b76bSAndrew Jones }
22394c4b76bSAndrew Jones
run_vcpu(struct kvm_vcpu * vcpu)22499801604SSean Christopherson static void run_vcpu(struct kvm_vcpu *vcpu)
22594c4b76bSAndrew Jones {
22694c4b76bSAndrew Jones struct ucall uc;
22794c4b76bSAndrew Jones
228768e9a61SSean Christopherson vcpu_run(vcpu);
22994c4b76bSAndrew Jones
230768e9a61SSean Christopherson switch (get_ucall(vcpu, &uc)) {
23194c4b76bSAndrew Jones case UCALL_SYNC:
23294c4b76bSAndrew Jones case UCALL_DONE:
23394c4b76bSAndrew Jones break;
23494c4b76bSAndrew Jones case UCALL_ABORT:
235594a1c27SColton Lewis REPORT_GUEST_ASSERT(uc);
23694c4b76bSAndrew Jones default:
23794c4b76bSAndrew Jones TEST_ASSERT(false, "Unexpected exit: %s",
23899801604SSean Christopherson exit_reason_str(vcpu->run->exit_reason));
23994c4b76bSAndrew Jones }
24094c4b76bSAndrew Jones }
24194c4b76bSAndrew Jones
main(int ac,char ** av)24294c4b76bSAndrew Jones int main(int ac, char **av)
24394c4b76bSAndrew Jones {
24499801604SSean Christopherson struct kvm_vcpu *vcpus[NR_VCPUS];
24594c4b76bSAndrew Jones struct kvm_vm *vm;
24694c4b76bSAndrew Jones pthread_attr_t attr;
24794c4b76bSAndrew Jones pthread_t thread;
24894c4b76bSAndrew Jones cpu_set_t cpuset;
24994c4b76bSAndrew Jones unsigned int gpages;
25094c4b76bSAndrew Jones long stolen_time;
25194c4b76bSAndrew Jones long run_delay;
25294c4b76bSAndrew Jones bool verbose;
25394c4b76bSAndrew Jones int i;
25494c4b76bSAndrew Jones
25594c4b76bSAndrew Jones verbose = ac > 1 && (!strncmp(av[1], "-v", 3) || !strncmp(av[1], "--verbose", 10));
25694c4b76bSAndrew Jones
25794c4b76bSAndrew Jones /* Set CPU affinity so we can force preemption of the VCPU */
25894c4b76bSAndrew Jones CPU_ZERO(&cpuset);
25994c4b76bSAndrew Jones CPU_SET(0, &cpuset);
26094c4b76bSAndrew Jones pthread_attr_init(&attr);
26194c4b76bSAndrew Jones pthread_attr_setaffinity_np(&attr, sizeof(cpu_set_t), &cpuset);
26294c4b76bSAndrew Jones pthread_setaffinity_np(pthread_self(), sizeof(cpu_set_t), &cpuset);
26394c4b76bSAndrew Jones
26499801604SSean Christopherson /* Create a VM and an identity mapped memslot for the steal time structure */
26599801604SSean Christopherson vm = vm_create_with_vcpus(NR_VCPUS, guest_code, vcpus);
26694c4b76bSAndrew Jones gpages = vm_calc_num_guest_pages(VM_MODE_DEFAULT, STEAL_TIME_SIZE * NR_VCPUS);
26794c4b76bSAndrew Jones vm_userspace_mem_region_add(vm, VM_MEM_SRC_ANONYMOUS, ST_GPA_BASE, 1, gpages, 0);
2684307af73SSean Christopherson virt_map(vm, ST_GPA_BASE, ST_GPA_BASE, gpages);
26994c4b76bSAndrew Jones
2707ed397d1SSean Christopherson TEST_REQUIRE(is_steal_time_supported(vcpus[0]));
27194c4b76bSAndrew Jones
27294c4b76bSAndrew Jones /* Run test on each VCPU */
27394c4b76bSAndrew Jones for (i = 0; i < NR_VCPUS; ++i) {
27499801604SSean Christopherson steal_time_init(vcpus[i], i);
27599801604SSean Christopherson
276768e9a61SSean Christopherson vcpu_args_set(vcpus[i], 1, i);
27799801604SSean Christopherson
27894c4b76bSAndrew Jones /* First VCPU run initializes steal-time */
27999801604SSean Christopherson run_vcpu(vcpus[i]);
28094c4b76bSAndrew Jones
28194c4b76bSAndrew Jones /* Second VCPU run, expect guest stolen time to be <= run_delay */
28299801604SSean Christopherson run_vcpu(vcpus[i]);
28394c4b76bSAndrew Jones sync_global_from_guest(vm, guest_stolen_time[i]);
28494c4b76bSAndrew Jones stolen_time = guest_stolen_time[i];
28594c4b76bSAndrew Jones run_delay = get_run_delay();
28694c4b76bSAndrew Jones TEST_ASSERT(stolen_time <= run_delay,
28794c4b76bSAndrew Jones "Expected stolen time <= %ld, got %ld",
28894c4b76bSAndrew Jones run_delay, stolen_time);
28994c4b76bSAndrew Jones
29094c4b76bSAndrew Jones /* Steal time from the VCPU. The steal time thread has the same CPU affinity as the VCPUs. */
29194c4b76bSAndrew Jones run_delay = get_run_delay();
29294c4b76bSAndrew Jones pthread_create(&thread, &attr, do_steal_time, NULL);
29394c4b76bSAndrew Jones do
294bac0b135SAndrew Jones sched_yield();
29594c4b76bSAndrew Jones while (get_run_delay() - run_delay < MIN_RUN_DELAY_NS);
29694c4b76bSAndrew Jones pthread_join(thread, NULL);
29794c4b76bSAndrew Jones run_delay = get_run_delay() - run_delay;
29894c4b76bSAndrew Jones TEST_ASSERT(run_delay >= MIN_RUN_DELAY_NS,
29994c4b76bSAndrew Jones "Expected run_delay >= %ld, got %ld",
30094c4b76bSAndrew Jones MIN_RUN_DELAY_NS, run_delay);
30194c4b76bSAndrew Jones
30294c4b76bSAndrew Jones /* Run VCPU again to confirm stolen time is consistent with run_delay */
30399801604SSean Christopherson run_vcpu(vcpus[i]);
30494c4b76bSAndrew Jones sync_global_from_guest(vm, guest_stolen_time[i]);
30594c4b76bSAndrew Jones stolen_time = guest_stolen_time[i] - stolen_time;
30694c4b76bSAndrew Jones TEST_ASSERT(stolen_time >= run_delay,
30794c4b76bSAndrew Jones "Expected stolen time >= %ld, got %ld",
30894c4b76bSAndrew Jones run_delay, stolen_time);
30994c4b76bSAndrew Jones
31094c4b76bSAndrew Jones if (verbose) {
31194c4b76bSAndrew Jones pr_info("VCPU%d: total-stolen-time=%ld test-stolen-time=%ld", i,
31294c4b76bSAndrew Jones guest_stolen_time[i], stolen_time);
31394c4b76bSAndrew Jones if (stolen_time == run_delay)
31494c4b76bSAndrew Jones pr_info(" (BONUS: guest test-stolen-time even exactly matches test-run_delay)");
31594c4b76bSAndrew Jones pr_info("\n");
31694c4b76bSAndrew Jones steal_time_dump(vm, i);
31794c4b76bSAndrew Jones }
31894c4b76bSAndrew Jones }
31994c4b76bSAndrew Jones
32094c4b76bSAndrew Jones return 0;
32194c4b76bSAndrew Jones }
322