mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2026-08-09 06:14:34 +02:00
Add a selftest to verify KVM's handling of {de,en}crypt debug ioctls,
specifically focusing on edge cases around the chunk (16 bytes) and page
(4096) sizes, where KVM had multiple bugs. E.g. KVM would fail to handle
small sizes that aren't naturally aligned and sized, would buffer overflow
if the destination was unaligned but the source was not, etc.
Attempt to strike a balance between an exhaustive test and a reasonable
runtime. On a system with both SEV and SEV-ES support, the current runtime
is under 45 seconds. Which isn't great, but it's tolerable, and it's not
obvious which of the combinations are "better" than the others.
Link: https://patch.msgid.link/20260501203537.2120074-3-seanjc@google.com
Signed-off-by: Sean Christopherson <seanjc@google.com>
172 lines
4.4 KiB
C
172 lines
4.4 KiB
C
/* 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)
|
|
|
|
#define SNP_POLICY_SMT (1ULL << 16)
|
|
#define SNP_POLICY_RSVD_MBO (1ULL << 17)
|
|
#define SNP_POLICY_DBG (1ULL << 19)
|
|
|
|
#define GHCB_MSR_TERM_REQ 0x100
|
|
|
|
static inline bool is_sev_snp_vm(struct kvm_vm *vm)
|
|
{
|
|
return vm->type == KVM_X86_SNP_VM;
|
|
}
|
|
|
|
static inline bool is_sev_es_vm(struct kvm_vm *vm)
|
|
{
|
|
return is_sev_snp_vm(vm) || vm->type == KVM_X86_SEV_ES_VM;
|
|
}
|
|
|
|
static inline bool is_sev_vm(struct kvm_vm *vm)
|
|
{
|
|
return is_sev_es_vm(vm) || vm->type == KVM_X86_SEV_VM;
|
|
}
|
|
|
|
void sev_vm_launch(struct kvm_vm *vm, u32 policy);
|
|
void sev_vm_launch_measure(struct kvm_vm *vm, u8 *measurement);
|
|
void sev_vm_launch_finish(struct kvm_vm *vm);
|
|
void snp_vm_launch_start(struct kvm_vm *vm, u64 policy);
|
|
void snp_vm_launch_update(struct kvm_vm *vm);
|
|
void snp_vm_launch_finish(struct kvm_vm *vm);
|
|
|
|
struct kvm_vm *vm_sev_create_with_one_vcpu(u32 type, void *guest_code,
|
|
struct kvm_vcpu **cpu);
|
|
void vm_sev_launch(struct kvm_vm *vm, u64 policy, u8 *measurement);
|
|
|
|
kvm_static_assert(SEV_RET_SUCCESS == 0);
|
|
|
|
/*
|
|
* A SEV-SNP VM requires the policy reserved bit to always be set.
|
|
* The SMT policy bit is also required to be set based on SMT being
|
|
* available and active on the system.
|
|
*/
|
|
static inline u64 snp_default_policy(void)
|
|
{
|
|
return SNP_POLICY_RSVD_MBO | (is_smt_on() ? SNP_POLICY_SMT : 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 = (u64)(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); \
|
|
})
|
|
|
|
void sev_vm_init(struct kvm_vm *vm);
|
|
void sev_es_vm_init(struct kvm_vm *vm);
|
|
void snp_vm_init(struct kvm_vm *vm);
|
|
|
|
static inline void vmgexit(void)
|
|
{
|
|
__asm__ __volatile__("rep; vmmcall");
|
|
}
|
|
|
|
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, gpa_t gpa,
|
|
u64 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);
|
|
}
|
|
|
|
static inline void snp_launch_update_data(struct kvm_vm *vm, gpa_t gpa,
|
|
u64 hva, u64 size, u8 type)
|
|
{
|
|
struct kvm_sev_snp_launch_update update_data = {
|
|
.uaddr = hva,
|
|
.gfn_start = gpa >> PAGE_SHIFT,
|
|
.len = size,
|
|
.type = type,
|
|
};
|
|
|
|
vm_sev_ioctl(vm, KVM_SEV_SNP_LAUNCH_UPDATE, &update_data);
|
|
}
|
|
|
|
static inline void sev_dbg_crypt_memory(struct kvm_vm *vm, unsigned int cmd,
|
|
void *dst, void *src, unsigned int len)
|
|
{
|
|
struct kvm_sev_dbg dbg = {
|
|
.src_uaddr = (unsigned long)src,
|
|
.dst_uaddr = (unsigned long)dst,
|
|
.len = len,
|
|
};
|
|
|
|
vm_sev_ioctl(vm, cmd, &dbg);
|
|
}
|
|
|
|
static inline void sev_decrypt_memory(struct kvm_vm *vm, void *dst, void *src,
|
|
unsigned int len)
|
|
{
|
|
sev_dbg_crypt_memory(vm, KVM_SEV_DBG_DECRYPT, dst, src, len);
|
|
}
|
|
|
|
static inline void sev_encrypt_memory(struct kvm_vm *vm, void *dst, void *src,
|
|
unsigned int len)
|
|
{
|
|
sev_dbg_crypt_memory(vm, KVM_SEV_DBG_ENCRYPT, dst, src, len);
|
|
}
|
|
|
|
#endif /* SELFTEST_KVM_SEV_H */
|