diff mbox series

[RFC,06/10] kvm: selftests: add ucall interfaces based around shared memory

Message ID 20211210164620.11636-7-michael.roth@amd.com (mailing list archive)
State New
Headers show
Series KVM: selftests: Add support for test-selectable ucall implementations | expand

Commit Message

Michael Roth Dec. 10, 2021, 4:46 p.m. UTC
One complication with SEV and likely other Confidential Computing
implementations is the fact that guest stacks might be encrypted, so
existing interfaces like ucall()/get_ucall() will not work as-is since
they allocate ucall structs on the guest stack dynamically.
Additionally, the basic task of communicating the location in guest
memory of these structs is complicated by the fact that guest register
state may also be also encrypted, so existing approaches like reading
vCPU register values to get these addresses would need to take this
into consideration.

One way to address both of these in a (hopefully) robust way is to
introduce a new set of ucall interfaces that rely on tests setting up
shared guest memory in advance so that these ucall struct addresses are
communicated between host/guest in advance, along with any work needed
to ensure the memory is shared/public. With that in place, a ucall
implementation only needs to trigger an exit back to host userspace to
allow for host/guest communication via this shared memory / ucall
struct.

Implement this approach by extending ucall_ops to allow for ucall
implementations based on shared memory, and introducing
ucall_shared()/get_ucall_shared() analogs to the existing
ucall()/get_ucall() interfaces.

Signed-off-by: Michael Roth <michael.roth@amd.com>
---
 .../selftests/kvm/include/ucall_common.h      |  5 +++
 .../testing/selftests/kvm/lib/ucall_common.c  | 38 +++++++++++++++++++
 2 files changed, 43 insertions(+)
diff mbox series

Patch

diff --git a/tools/testing/selftests/kvm/include/ucall_common.h b/tools/testing/selftests/kvm/include/ucall_common.h
index fcd32607dcff..ae0e8eec9734 100644
--- a/tools/testing/selftests/kvm/include/ucall_common.h
+++ b/tools/testing/selftests/kvm/include/ucall_common.h
@@ -34,6 +34,8 @@  struct ucall_ops {
 	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 (*send_cmd_shared)(struct ucall *uc);
+	uint64_t (*recv_cmd_shared)(struct kvm_vm *vm, uint32_t vcpu_id, struct ucall *uc);
 };
 
 void ucall_init(struct kvm_vm *vm, void *arg);
@@ -42,6 +44,9 @@  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);
+vm_vaddr_t ucall_shared_alloc(struct kvm_vm *vm, int count);
+void ucall_shared(struct ucall *uc, uint64_t cmd, int nargs, ...);
+uint64_t get_ucall_shared(struct kvm_vm *vm, uint32_t vcpu_id, struct ucall *uc);
 
 #define GUEST_SYNC_ARGS(stage, arg1, arg2, arg3, arg4)	\
 				ucall(UCALL_SYNC, 6, "hello", stage, arg1, arg2, arg3, arg4)
diff --git a/tools/testing/selftests/kvm/lib/ucall_common.c b/tools/testing/selftests/kvm/lib/ucall_common.c
index db0129edcbc1..8e5738241a7c 100644
--- a/tools/testing/selftests/kvm/lib/ucall_common.c
+++ b/tools/testing/selftests/kvm/lib/ucall_common.c
@@ -93,3 +93,41 @@  uint64_t get_ucall(struct kvm_vm *vm, uint32_t vcpu_id, struct ucall *uc)
 
 	return ucall_ops->recv_cmd(vm, vcpu_id, uc);
 }
+
+/* Allocate shared memory within a guest to for a shared ucall buffer. */
+vm_vaddr_t ucall_shared_alloc(struct kvm_vm *vm, int count)
+{
+	return vm_vaddr_alloc(vm, count * sizeof(struct ucall),
+			      vm_get_page_size(vm));
+}
+
+/*
+ * Populate a shared ucall buffer previously allocated by ucall_shared_alloc()
+ * and then generate an exit to host userspace.
+ */
+void ucall_shared(struct ucall *uc, uint64_t cmd, int nargs, ...)
+{
+	va_list va;
+
+	if (!ucall_ops->send_cmd_shared)
+		return;
+
+	va_start(va, nargs);
+	ucall_process_args(uc, cmd, nargs, va);
+	va_end(va);
+
+	ucall_ops->send_cmd_shared(uc);
+}
+
+/*
+ * Parse a ucall buffer that has been allocated by ucall_shared_alloc() and
+ * shared with the guest in advance to determine the ucall message/command that
+ * was sent by the guest.
+ */
+uint64_t get_ucall_shared(struct kvm_vm *vm, uint32_t vcpu_id, struct ucall *uc)
+{
+	if (!ucall_ops->recv_cmd_shared)
+		return UCALL_NOT_IMPLEMENTED;
+
+	return ucall_ops->recv_cmd_shared(vm, vcpu_id, uc);
+}