Commit ae20eef5 authored by Peter Gonda's avatar Peter Gonda Committed by Sean Christopherson
Browse files

KVM: selftests: Add library for creating and interacting with SEV guests



Add a library/APIs for creating and interfacing with SEV guests, all of
which need some amount of common functionality, e.g. an open file handle
for the SEV driver (/dev/sev), ioctl() wrappers to pass said file handle
to KVM, tracking of the C-bit, etc.

Add an x86-specific hook to initialize address properties, a.k.a. the
location of the C-bit.  An arch specific hook is rather gross, but x86
already has a dedicated #ifdef-protected kvm_get_cpu_address_width() hook,
i.e. the ugliest code already exists.

Cc: Paolo Bonzini <pbonzini@redhat.com>
Cc: Sean Christopherson <seanjc@google.com>
Cc: Vishal Annapurve <vannapurve@google.com>
Cc: Ackerly Tng <ackerleytng@google.com>
cc: Andrew Jones <andrew.jones@linux.dev>
Cc: Tom Lendacky <thomas.lendacky@amd.com>
Cc: Michael Roth <michael.roth@amd.com>
Tested-by: default avatarCarlos Bilbao <carlos.bilbao@amd.com>
Originally-by: default avatarMichael Roth <michael.roth@amd.com>
Signed-off-by: default avatarPeter Gonda <pgonda@google.com>
Co-developed-by: default avatarSean Christopherson <seanjc@google.com>
Link: https://lore.kernel.org/r/20240223004258.3104051-9-seanjc@google.com


Signed-off-by: default avatarSean Christopherson <seanjc@google.com>
parent be1bd4c5
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -37,6 +37,7 @@ LIBKVM_x86_64 += lib/x86_64/handlers.S
LIBKVM_x86_64 += lib/x86_64/hyperv.c
LIBKVM_x86_64 += lib/x86_64/memstress.c
LIBKVM_x86_64 += lib/x86_64/processor.c
LIBKVM_x86_64 += lib/x86_64/sev.c
LIBKVM_x86_64 += lib/x86_64/svm.c
LIBKVM_x86_64 += lib/x86_64/ucall.c
LIBKVM_x86_64 += lib/x86_64/vmx.c
+2 −0
Original line number Diff line number Diff line
@@ -8,6 +8,8 @@
struct kvm_vm_arch {
	uint64_t c_bit;
	uint64_t s_bit;
	int sev_fd;
	bool is_pt_protected;
};

static inline bool __vm_arch_has_protected_memory(struct kvm_vm_arch *arch)
+8 −0
Original line number Diff line number Diff line
@@ -23,6 +23,12 @@
extern bool host_cpu_is_intel;
extern bool host_cpu_is_amd;

enum vm_guest_x86_subtype {
	VM_SUBTYPE_NONE = 0,
	VM_SUBTYPE_SEV,
	VM_SUBTYPE_SEV_ES,
};

#define NMI_VECTOR		0x02

#define X86_EFLAGS_FIXED	 (1u << 1)
@@ -273,6 +279,7 @@ struct kvm_x86_cpu_property {
#define X86_PROPERTY_MAX_EXT_LEAF		KVM_X86_CPU_PROPERTY(0x80000000, 0, EAX, 0, 31)
#define X86_PROPERTY_MAX_PHY_ADDR		KVM_X86_CPU_PROPERTY(0x80000008, 0, EAX, 0, 7)
#define X86_PROPERTY_MAX_VIRT_ADDR		KVM_X86_CPU_PROPERTY(0x80000008, 0, EAX, 8, 15)
#define X86_PROPERTY_SEV_C_BIT			KVM_X86_CPU_PROPERTY(0x8000001F, 0, EBX, 0, 5)
#define X86_PROPERTY_PHYS_ADDR_REDUCTION	KVM_X86_CPU_PROPERTY(0x8000001F, 0, EBX, 6, 11)

#define X86_PROPERTY_MAX_CENTAUR_LEAF		KVM_X86_CPU_PROPERTY(0xC0000000, 0, EAX, 0, 31)
@@ -1059,6 +1066,7 @@ do { \
} while (0)

void kvm_get_cpu_address_width(unsigned int *pa_bits, unsigned int *va_bits);
void kvm_init_vm_address_properties(struct kvm_vm *vm);
bool vm_is_unrestricted_guest(struct kvm_vm *vm);

struct ex_regs {
+105 −0
Original line number Diff line number Diff line
/* SPDX-License-Identifier: GPL-2.0-only */
/*
 * Helpers used for SEV guests
 *
 */
#ifndef SELFTEST_KVM_SEV_H
#define SELFTEST_KVM_SEV_H

#include <stdint.h>
#include <stdbool.h>

#include "linux/psp-sev.h"

#include "kvm_util.h"
#include "svm_util.h"
#include "processor.h"

enum sev_guest_state {
	SEV_GUEST_STATE_UNINITIALIZED = 0,
	SEV_GUEST_STATE_LAUNCH_UPDATE,
	SEV_GUEST_STATE_LAUNCH_SECRET,
	SEV_GUEST_STATE_RUNNING,
};

#define SEV_POLICY_NO_DBG	(1UL << 0)
#define SEV_POLICY_ES		(1UL << 2)

void sev_vm_launch(struct kvm_vm *vm, uint32_t policy);
void sev_vm_launch_measure(struct kvm_vm *vm, uint8_t *measurement);
void sev_vm_launch_finish(struct kvm_vm *vm);

struct kvm_vm *vm_sev_create_with_one_vcpu(uint32_t policy, void *guest_code,
					   struct kvm_vcpu **cpu);

kvm_static_assert(SEV_RET_SUCCESS == 0);

/*
 * The KVM_MEMORY_ENCRYPT_OP uAPI is utter garbage and takes an "unsigned long"
 * instead of a proper struct.  The size of the parameter is embedded in the
 * ioctl number, i.e. is ABI and thus immutable.  Hack around the mess by
 * creating an overlay to pass in an "unsigned long" without a cast (casting
 * will make the compiler unhappy due to dereferencing an aliased pointer).
 */
#define __vm_sev_ioctl(vm, cmd, arg)					\
({									\
	int r;								\
									\
	union {								\
		struct kvm_sev_cmd c;					\
		unsigned long raw;					\
	} sev_cmd = { .c = {						\
		.id = (cmd),						\
		.data = (uint64_t)(arg),				\
		.sev_fd = (vm)->arch.sev_fd,				\
	} };								\
									\
	r = __vm_ioctl(vm, KVM_MEMORY_ENCRYPT_OP, &sev_cmd.raw);	\
	r ?: sev_cmd.c.error;						\
})

#define vm_sev_ioctl(vm, cmd, arg)					\
({									\
	int ret = __vm_sev_ioctl(vm, cmd, arg);				\
									\
	__TEST_ASSERT_VM_VCPU_IOCTL(!ret, #cmd,	ret, vm);		\
})

static inline void sev_vm_init(struct kvm_vm *vm)
{
	vm->arch.sev_fd = open_sev_dev_path_or_exit();

	vm_sev_ioctl(vm, KVM_SEV_INIT, NULL);
}


static inline void sev_es_vm_init(struct kvm_vm *vm)
{
	vm->arch.sev_fd = open_sev_dev_path_or_exit();

	vm_sev_ioctl(vm, KVM_SEV_ES_INIT, NULL);
}

static inline void sev_register_encrypted_memory(struct kvm_vm *vm,
						 struct userspace_mem_region *region)
{
	struct kvm_enc_region range = {
		.addr = region->region.userspace_addr,
		.size = region->region.memory_size,
	};

	vm_ioctl(vm, KVM_MEMORY_ENCRYPT_REG_REGION, &range);
}

static inline void sev_launch_update_data(struct kvm_vm *vm, vm_paddr_t gpa,
					  uint64_t size)
{
	struct kvm_sev_launch_update_data update_data = {
		.uaddr = (unsigned long)addr_gpa2hva(vm, gpa),
		.len = size,
	};

	vm_sev_ioctl(vm, KVM_SEV_LAUNCH_UPDATE_DATA, &update_data);
}

#endif /* SELFTEST_KVM_SEV_H */
+1 −0
Original line number Diff line number Diff line
@@ -266,6 +266,7 @@ struct kvm_vm *____vm_create(struct vm_shape shape)
	case VM_MODE_PXXV48_4K:
#ifdef __x86_64__
		kvm_get_cpu_address_width(&vm->pa_bits, &vm->va_bits);
		kvm_init_vm_address_properties(vm);
		/*
		 * Ignore KVM support for 5-level paging (vm->va_bits == 57),
		 * it doesn't take effect unless a CR4.LA57 is set, which it
Loading