@@ -34,7 +34,7 @@ ifeq ($(ARCH),s390)
endif
LIBKVM = lib/assert.c lib/elf.c lib/io.c lib/kvm_util.c lib/rbtree.c lib/sparsebit.c lib/test_util.c lib/guest_modes.c lib/perf_test_util.c
-LIBKVM_x86_64 = lib/x86_64/apic.c lib/x86_64/processor.c lib/x86_64/vmx.c lib/x86_64/svm.c lib/x86_64/ucall.c lib/x86_64/handlers.S
+LIBKVM_x86_64 = lib/x86_64/apic.c lib/x86_64/processor.c lib/x86_64/vmx.c lib/x86_64/svm.c lib/x86_64/ucall.c lib/x86_64/handlers.S lib/ucall_common.c
LIBKVM_aarch64 = lib/aarch64/processor.c lib/aarch64/ucall.c lib/aarch64/handlers.S lib/aarch64/spinlock.c lib/aarch64/gic.c lib/aarch64/gic_v3.c lib/aarch64/vgic.c
LIBKVM_s390x = lib/s390x/processor.c lib/s390x/ucall.c lib/s390x/diag318_test_handler.c
@@ -8,6 +8,16 @@
#define SELFTEST_KVM_UTIL_H
#include "kvm_util_base.h"
+/*
+ * TODO: ucall.h contains arch-specific declarations along with
+ * ucall_common.h. For now only a subset of archs provide the
+ * new header. Once all archs implement the new header the #include for
+ * ucall_common.h can be dropped.
+ */
+#ifdef __x86_64__
+#include "ucall.h"
+#else
#include "ucall_common.h"
+#endif
#endif /* SELFTEST_KVM_UTIL_H */
@@ -1,8 +1,12 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
- * tools/testing/selftests/kvm/include/kvm_util.h
+ * Common interfaces related to ucall support.
+ *
+ * A ucall is a hypercall to userspace.
*
* Copyright (C) 2018, Google LLC.
+ * Copyright (C) 2018, Red Hat, Inc.
+ * Copyright (C) 2021, Advanced Micro Devices, Inc.
*/
#ifndef SELFTEST_KVM_UCALL_COMMON_H
#define SELFTEST_KVM_UCALL_COMMON_H
@@ -14,6 +18,7 @@ enum {
UCALL_ABORT,
UCALL_DONE,
UCALL_UNHANDLED,
+ UCALL_NOT_IMPLEMENTED,
};
#define UCALL_MAX_ARGS 6
@@ -23,8 +28,18 @@ struct ucall {
uint64_t args[UCALL_MAX_ARGS];
};
+struct ucall_ops {
+ const char *name;
+ void (*init)(struct kvm_vm *vm, void *arg);
+ void (*uninit)(struct kvm_vm *vm);
+ void (*send_cmd)(struct ucall *uc);
+ uint64_t (*recv_cmd)(struct kvm_vm *vm, uint32_t vcpu_id, struct ucall *uc);
+};
+
void ucall_init(struct kvm_vm *vm, void *arg);
void ucall_uninit(struct kvm_vm *vm);
+void ucall_init_ops(struct kvm_vm *vm, void *arg, const struct ucall_ops *ops);
+void ucall_uninit_ops(struct kvm_vm *vm);
void ucall(uint64_t cmd, int nargs, ...);
uint64_t get_ucall(struct kvm_vm *vm, uint32_t vcpu_id, struct ucall *uc);
new file mode 100644
@@ -0,0 +1,18 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Arch-specific ucall implementations.
+ *
+ * A ucall is a "hypercall to userspace".
+ *
+ * Copyright (C) 2021 Advanced Micro Devices
+ */
+#ifndef SELFTEST_KVM_UCALL_H
+#define SELFTEST_KVM_UCALL_H
+
+#include "ucall_common.h"
+
+extern const struct ucall_ops ucall_ops_pio;
+
+extern const struct ucall_ops ucall_ops_default;
+
+#endif /* SELFTEST_KVM_UCALL_H */
new file mode 100644
@@ -0,0 +1,95 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Common interfaces related to ucall support. A ucall is a hypercall to
+ * userspace.
+ *
+ * Copyright (C) 2018, Red Hat, Inc.
+ * Copyright (C) 2021, Advanced Micro Devices, Inc.
+ */
+#include "kvm_util_base.h"
+#include "ucall_common.h"
+
+extern const struct ucall_ops ucall_ops_default;
+
+/* Some archs rely on a default that is available even without ucall_init(). */
+#if defined(__x86_64__) || defined(__s390x__)
+static const struct ucall_ops *ucall_ops = &ucall_ops_default;
+#else
+static const struct ucall_ops *ucall_ops;
+#endif
+
+void ucall_init_ops(struct kvm_vm *vm, void *arg, const struct ucall_ops *ops)
+{
+ TEST_ASSERT(ops, "ucall ops must be specified");
+ ucall_ops = ops;
+ sync_global_to_guest(vm, ucall_ops);
+
+ if (ucall_ops->init)
+ ucall_ops->init(vm, arg);
+}
+
+void ucall_init(struct kvm_vm *vm, void *arg)
+{
+ ucall_init_ops(vm, arg, &ucall_ops_default);
+}
+
+void ucall_uninit_ops(struct kvm_vm *vm)
+{
+ if (ucall_ops && ucall_ops->uninit)
+ ucall_ops->uninit(vm);
+
+ ucall_ops = NULL;
+ sync_global_to_guest(vm, ucall_ops);
+}
+
+void ucall_uninit(struct kvm_vm *vm)
+{
+ ucall_uninit_ops(vm);
+}
+
+static void ucall_process_args(struct ucall *uc, uint64_t cmd, int nargs, va_list va_args)
+{
+ int i;
+
+ nargs = nargs <= UCALL_MAX_ARGS ? nargs : UCALL_MAX_ARGS;
+ uc->cmd = cmd;
+
+ for (i = 0; i < nargs; ++i)
+ uc->args[i] = va_arg(va_args, uint64_t);
+}
+
+/*
+ * Allocate/populate a ucall buffer from the guest's stack and then generate an
+ * exit to host userspace. ucall_ops->send_cmd should have some way of
+ * communicating the address of the ucall buffer to the host.
+ */
+void ucall(uint64_t cmd, int nargs, ...)
+{
+ struct ucall uc;
+ va_list va;
+
+ if (!ucall_ops->send_cmd)
+ return;
+
+ va_start(va, nargs);
+ ucall_process_args(&uc, cmd, nargs, va);
+ va_end(va);
+
+ ucall_ops->send_cmd(&uc);
+}
+
+/*
+ * Parse the ucall buffer allocated by the guest via ucall() to determine what
+ * ucall message/command was sent by the guest. If 'uc' is provided, copy the
+ * contents of the guest's ucall buffer into it.
+ */
+uint64_t get_ucall(struct kvm_vm *vm, uint32_t vcpu_id, struct ucall *uc)
+{
+ if (!ucall_ops->recv_cmd)
+ return UCALL_NOT_IMPLEMENTED;
+
+ if (uc)
+ memset(uc, 0, sizeof(*uc));
+
+ return ucall_ops->recv_cmd(vm, vcpu_id, uc);
+}
@@ -1,48 +1,28 @@
// SPDX-License-Identifier: GPL-2.0
/*
- * ucall support. A ucall is a "hypercall to userspace".
+ * Arch-specific ucall implementations.
+ *
+ * A ucall is a "hypercall to userspace".
*
* Copyright (C) 2018, Red Hat, Inc.
*/
-#include "kvm_util.h"
+#include "kvm_util_base.h"
+#include "ucall.h"
#define UCALL_PIO_PORT ((uint16_t)0x1000)
-void ucall_init(struct kvm_vm *vm, void *arg)
-{
-}
-
-void ucall_uninit(struct kvm_vm *vm)
-{
-}
-
-void ucall(uint64_t cmd, int nargs, ...)
+static void ucall_ops_pio_send_cmd(struct ucall *uc)
{
- struct ucall uc = {
- .cmd = cmd,
- };
- va_list va;
- int i;
-
- nargs = nargs <= UCALL_MAX_ARGS ? nargs : UCALL_MAX_ARGS;
-
- va_start(va, nargs);
- for (i = 0; i < nargs; ++i)
- uc.args[i] = va_arg(va, uint64_t);
- va_end(va);
-
asm volatile("in %[port], %%al"
- : : [port] "d" (UCALL_PIO_PORT), "D" (&uc) : "rax", "memory");
+ : : [port] "d" (UCALL_PIO_PORT), "D" (uc) : "rax", "memory");
}
-uint64_t get_ucall(struct kvm_vm *vm, uint32_t vcpu_id, struct ucall *uc)
+static uint64_t ucall_ops_pio_recv_cmd(struct kvm_vm *vm, uint32_t vcpu_id,
+ struct ucall *uc)
{
struct kvm_run *run = vcpu_state(vm, vcpu_id);
struct ucall ucall = {};
- if (uc)
- memset(uc, 0, sizeof(*uc));
-
if (run->exit_reason == KVM_EXIT_IO && run->io.port == UCALL_PIO_PORT) {
struct kvm_regs regs;
@@ -57,3 +37,11 @@ uint64_t get_ucall(struct kvm_vm *vm, uint32_t vcpu_id, struct ucall *uc)
return ucall.cmd;
}
+
+const struct ucall_ops ucall_ops_pio = {
+ .name = "PIO",
+ .send_cmd = ucall_ops_pio_send_cmd,
+ .recv_cmd = ucall_ops_pio_recv_cmd,
+};
+
+const struct ucall_ops ucall_ops_default = ucall_ops_pio;
To support SEV, x86 tests will need an alternative to using PIO instructions to handle ucall-related functionality since ucall structs are currently allocated on the guest stack, which will generally be encrypted memory that can't be accessed by tests through the normal mechanisms (along with some other complications which will requires some new ucall interfaces as well). To prepare for this, introduce a ucall_ops struct and supporting interfaces that can be used to define multiple ucall implementations that can be selected on a per-test basis, and re-work the existing PIO-based ucall implementation to make use of these changes. Subsequent patches will do the same for other archs as well, and then extend this ops interface to address complications when dealing with encrypted/private guest memory. Signed-off-by: Michael Roth <michael.roth@amd.com> --- tools/testing/selftests/kvm/Makefile | 2 +- .../testing/selftests/kvm/include/kvm_util.h | 10 ++ .../selftests/kvm/include/ucall_common.h | 17 +++- .../selftests/kvm/include/x86_64/ucall.h | 18 ++++ .../testing/selftests/kvm/lib/ucall_common.c | 95 +++++++++++++++++++ .../testing/selftests/kvm/lib/x86_64/ucall.c | 46 ++++----- 6 files changed, 157 insertions(+), 31 deletions(-) create mode 100644 tools/testing/selftests/kvm/include/x86_64/ucall.h create mode 100644 tools/testing/selftests/kvm/lib/ucall_common.c