Re: [PATCH v2 07/12] selftests: KVM: Introduce system counter offset test

Andrew Jones
2021-07-21T15:17:41Z

On Fri, Jul 16, 2021 at 09:26:24PM +0000, Oliver Upton wrote:
> Introduce a KVM selftest to verify that userspace manipulation of the
> TSC (via the new vCPU attribute) results in the correct behavior within
> the guest.
>
> Signed-off-by: Oliver Upton <oupton@google.com>
> ---
> tools/testing/selftests/kvm/.gitignore | 1 +
> tools/testing/selftests/kvm/Makefile | 1 +
> .../kvm/system_counter_offset_test.c | 133 ++++++++++++++++++
> 3 files changed, 135 insertions(+)
> create mode 100644 tools/testing/selftests/kvm/system_counter_offset_test.c
>
> diff --git a/tools/testing/selftests/kvm/.gitignore b/tools/testing/selftests/kvm/.gitignore
> index d0877d01e771..2752813d5090 100644
> --- a/tools/testing/selftests/kvm/.gitignore
> +++ b/tools/testing/selftests/kvm/.gitignore
> @@ -50,3 +50,4 @@
> /set_memory_region_test
> /steal_time
> /kvm_binary_stats_test
> +/system_counter_offset_test
> diff --git a/tools/testing/selftests/kvm/Makefile b/tools/testing/selftests/kvm/Makefile
> index f7e24f334c6e..7bf2e5fb1d5a 100644
> --- a/tools/testing/selftests/kvm/Makefile
> +++ b/tools/testing/selftests/kvm/Makefile
> @@ -83,6 +83,7 @@ TEST_GEN_PROGS_x86_64 += memslot_perf_test
> TEST_GEN_PROGS_x86_64 += set_memory_region_test
> TEST_GEN_PROGS_x86_64 += steal_time
> TEST_GEN_PROGS_x86_64 += kvm_binary_stats_test
> +TEST_GEN_PROGS_x86_64 += system_counter_offset_test
>
> TEST_GEN_PROGS_aarch64 += aarch64/debug-exceptions
> TEST_GEN_PROGS_aarch64 += aarch64/get-reg-list
> diff --git a/tools/testing/selftests/kvm/system_counter_offset_test.c b/tools/testing/selftests/kvm/system_counter_offset_test.c
> new file mode 100644
> index 000000000000..7e9015770759
> --- /dev/null
> +++ b/tools/testing/selftests/kvm/system_counter_offset_test.c
> @@ -0,0 +1,133 @@
> +// 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)
> +{
> + if (!_vcpu_has_device_attr(vm, VCPU_ID, KVM_VCPU_TSC_CTRL, KVM_VCPU_TSC_OFFSET))
> + 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)
> +{
> + vcpu_access_device_attr(vm, VCPU_ID, KVM_VCPU_TSC_CTRL,
> + KVM_VCPU_TSC_OFFSET, &test->tsc_offset, true);
> +}
> +
> +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));
> + }
> +
> + GUEST_DONE();
> +}
> +
> +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;
> + case UCALL_DONE:
> + return;
> + }
> + }
> +}
> +
> +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);
> +}
> --
> 2.32.0.402.g57bb445576-goog
>
> _______________________________________________
> kvmarm mailing list
> kvmarm@lists.cs.columbia.edu
> https://lists.cs.columbia.edu/mailman/listinfo/kvmarm
>

Reviewed-by: Andrew Jones <drjones@redhat.com>