Skip to content

Commit ae20eef

Browse files
pgondasean-jc
authored andcommitted
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 <[email protected]> Cc: Sean Christopherson <[email protected]> Cc: Vishal Annapurve <[email protected]> Cc: Ackerly Tng <[email protected]> cc: Andrew Jones <[email protected]> Cc: Tom Lendacky <[email protected]> Cc: Michael Roth <[email protected]> Tested-by: Carlos Bilbao <[email protected]> Originally-by: Michael Roth <[email protected]> Signed-off-by: Peter Gonda <[email protected]> Co-developed-by: Sean Christopherson <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Sean Christopherson <[email protected]>
1 parent be1bd4c commit ae20eef

File tree

7 files changed

+244
-0
lines changed

7 files changed

+244
-0
lines changed

tools/testing/selftests/kvm/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ LIBKVM_x86_64 += lib/x86_64/handlers.S
3737
LIBKVM_x86_64 += lib/x86_64/hyperv.c
3838
LIBKVM_x86_64 += lib/x86_64/memstress.c
3939
LIBKVM_x86_64 += lib/x86_64/processor.c
40+
LIBKVM_x86_64 += lib/x86_64/sev.c
4041
LIBKVM_x86_64 += lib/x86_64/svm.c
4142
LIBKVM_x86_64 += lib/x86_64/ucall.c
4243
LIBKVM_x86_64 += lib/x86_64/vmx.c

tools/testing/selftests/kvm/include/x86_64/kvm_util_arch.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@
88
struct kvm_vm_arch {
99
uint64_t c_bit;
1010
uint64_t s_bit;
11+
int sev_fd;
12+
bool is_pt_protected;
1113
};
1214

1315
static inline bool __vm_arch_has_protected_memory(struct kvm_vm_arch *arch)

tools/testing/selftests/kvm/include/x86_64/processor.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,12 @@
2323
extern bool host_cpu_is_intel;
2424
extern bool host_cpu_is_amd;
2525

26+
enum vm_guest_x86_subtype {
27+
VM_SUBTYPE_NONE = 0,
28+
VM_SUBTYPE_SEV,
29+
VM_SUBTYPE_SEV_ES,
30+
};
31+
2632
#define NMI_VECTOR 0x02
2733

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

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

10611068
void kvm_get_cpu_address_width(unsigned int *pa_bits, unsigned int *va_bits);
1069+
void kvm_init_vm_address_properties(struct kvm_vm *vm);
10621070
bool vm_is_unrestricted_guest(struct kvm_vm *vm);
10631071

10641072
struct ex_regs {
Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
/* SPDX-License-Identifier: GPL-2.0-only */
2+
/*
3+
* Helpers used for SEV guests
4+
*
5+
*/
6+
#ifndef SELFTEST_KVM_SEV_H
7+
#define SELFTEST_KVM_SEV_H
8+
9+
#include <stdint.h>
10+
#include <stdbool.h>
11+
12+
#include "linux/psp-sev.h"
13+
14+
#include "kvm_util.h"
15+
#include "svm_util.h"
16+
#include "processor.h"
17+
18+
enum sev_guest_state {
19+
SEV_GUEST_STATE_UNINITIALIZED = 0,
20+
SEV_GUEST_STATE_LAUNCH_UPDATE,
21+
SEV_GUEST_STATE_LAUNCH_SECRET,
22+
SEV_GUEST_STATE_RUNNING,
23+
};
24+
25+
#define SEV_POLICY_NO_DBG (1UL << 0)
26+
#define SEV_POLICY_ES (1UL << 2)
27+
28+
void sev_vm_launch(struct kvm_vm *vm, uint32_t policy);
29+
void sev_vm_launch_measure(struct kvm_vm *vm, uint8_t *measurement);
30+
void sev_vm_launch_finish(struct kvm_vm *vm);
31+
32+
struct kvm_vm *vm_sev_create_with_one_vcpu(uint32_t policy, void *guest_code,
33+
struct kvm_vcpu **cpu);
34+
35+
kvm_static_assert(SEV_RET_SUCCESS == 0);
36+
37+
/*
38+
* The KVM_MEMORY_ENCRYPT_OP uAPI is utter garbage and takes an "unsigned long"
39+
* instead of a proper struct. The size of the parameter is embedded in the
40+
* ioctl number, i.e. is ABI and thus immutable. Hack around the mess by
41+
* creating an overlay to pass in an "unsigned long" without a cast (casting
42+
* will make the compiler unhappy due to dereferencing an aliased pointer).
43+
*/
44+
#define __vm_sev_ioctl(vm, cmd, arg) \
45+
({ \
46+
int r; \
47+
\
48+
union { \
49+
struct kvm_sev_cmd c; \
50+
unsigned long raw; \
51+
} sev_cmd = { .c = { \
52+
.id = (cmd), \
53+
.data = (uint64_t)(arg), \
54+
.sev_fd = (vm)->arch.sev_fd, \
55+
} }; \
56+
\
57+
r = __vm_ioctl(vm, KVM_MEMORY_ENCRYPT_OP, &sev_cmd.raw); \
58+
r ?: sev_cmd.c.error; \
59+
})
60+
61+
#define vm_sev_ioctl(vm, cmd, arg) \
62+
({ \
63+
int ret = __vm_sev_ioctl(vm, cmd, arg); \
64+
\
65+
__TEST_ASSERT_VM_VCPU_IOCTL(!ret, #cmd, ret, vm); \
66+
})
67+
68+
static inline void sev_vm_init(struct kvm_vm *vm)
69+
{
70+
vm->arch.sev_fd = open_sev_dev_path_or_exit();
71+
72+
vm_sev_ioctl(vm, KVM_SEV_INIT, NULL);
73+
}
74+
75+
76+
static inline void sev_es_vm_init(struct kvm_vm *vm)
77+
{
78+
vm->arch.sev_fd = open_sev_dev_path_or_exit();
79+
80+
vm_sev_ioctl(vm, KVM_SEV_ES_INIT, NULL);
81+
}
82+
83+
static inline void sev_register_encrypted_memory(struct kvm_vm *vm,
84+
struct userspace_mem_region *region)
85+
{
86+
struct kvm_enc_region range = {
87+
.addr = region->region.userspace_addr,
88+
.size = region->region.memory_size,
89+
};
90+
91+
vm_ioctl(vm, KVM_MEMORY_ENCRYPT_REG_REGION, &range);
92+
}
93+
94+
static inline void sev_launch_update_data(struct kvm_vm *vm, vm_paddr_t gpa,
95+
uint64_t size)
96+
{
97+
struct kvm_sev_launch_update_data update_data = {
98+
.uaddr = (unsigned long)addr_gpa2hva(vm, gpa),
99+
.len = size,
100+
};
101+
102+
vm_sev_ioctl(vm, KVM_SEV_LAUNCH_UPDATE_DATA, &update_data);
103+
}
104+
105+
#endif /* SELFTEST_KVM_SEV_H */

tools/testing/selftests/kvm/lib/kvm_util.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -266,6 +266,7 @@ struct kvm_vm *____vm_create(struct vm_shape shape)
266266
case VM_MODE_PXXV48_4K:
267267
#ifdef __x86_64__
268268
kvm_get_cpu_address_width(&vm->pa_bits, &vm->va_bits);
269+
kvm_init_vm_address_properties(vm);
269270
/*
270271
* Ignore KVM support for 5-level paging (vm->va_bits == 57),
271272
* it doesn't take effect unless a CR4.LA57 is set, which it

tools/testing/selftests/kvm/lib/x86_64/processor.c

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
#include "test_util.h"
1010
#include "kvm_util.h"
1111
#include "processor.h"
12+
#include "sev.h"
1213

1314
#ifndef NUM_INTERRUPTS
1415
#define NUM_INTERRUPTS 256
@@ -278,6 +279,9 @@ uint64_t *__vm_get_page_table_entry(struct kvm_vm *vm, uint64_t vaddr,
278279
{
279280
uint64_t *pml4e, *pdpe, *pde;
280281

282+
TEST_ASSERT(!vm->arch.is_pt_protected,
283+
"Walking page tables of protected guests is impossible");
284+
281285
TEST_ASSERT(*level >= PG_LEVEL_NONE && *level < PG_LEVEL_NUM,
282286
"Invalid PG_LEVEL_* '%d'", *level);
283287

@@ -573,6 +577,11 @@ void kvm_arch_vm_post_create(struct kvm_vm *vm)
573577
vm_create_irqchip(vm);
574578
sync_global_to_guest(vm, host_cpu_is_intel);
575579
sync_global_to_guest(vm, host_cpu_is_amd);
580+
581+
if (vm->subtype == VM_SUBTYPE_SEV)
582+
sev_vm_init(vm);
583+
else if (vm->subtype == VM_SUBTYPE_SEV_ES)
584+
sev_es_vm_init(vm);
576585
}
577586

578587
void vcpu_arch_set_entry_point(struct kvm_vcpu *vcpu, void *guest_code)
@@ -1061,6 +1070,14 @@ void kvm_get_cpu_address_width(unsigned int *pa_bits, unsigned int *va_bits)
10611070
}
10621071
}
10631072

1073+
void kvm_init_vm_address_properties(struct kvm_vm *vm)
1074+
{
1075+
if (vm->subtype == VM_SUBTYPE_SEV) {
1076+
vm->arch.c_bit = BIT_ULL(this_cpu_property(X86_PROPERTY_SEV_C_BIT));
1077+
vm->gpa_tag_mask = vm->arch.c_bit;
1078+
}
1079+
}
1080+
10641081
static void set_idt_entry(struct kvm_vm *vm, int vector, unsigned long addr,
10651082
int dpl, unsigned short selector)
10661083
{
Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
// SPDX-License-Identifier: GPL-2.0-only
2+
#define _GNU_SOURCE /* for program_invocation_short_name */
3+
#include <stdint.h>
4+
#include <stdbool.h>
5+
6+
#include "sev.h"
7+
8+
/*
9+
* sparsebit_next_clear() can return 0 if [x, 2**64-1] are all set, and the
10+
* -1 would then cause an underflow back to 2**64 - 1. This is expected and
11+
* correct.
12+
*
13+
* If the last range in the sparsebit is [x, y] and we try to iterate,
14+
* sparsebit_next_set() will return 0, and sparsebit_next_clear() will try
15+
* and find the first range, but that's correct because the condition
16+
* expression would cause us to quit the loop.
17+
*/
18+
static void encrypt_region(struct kvm_vm *vm, struct userspace_mem_region *region)
19+
{
20+
const struct sparsebit *protected_phy_pages = region->protected_phy_pages;
21+
const vm_paddr_t gpa_base = region->region.guest_phys_addr;
22+
const sparsebit_idx_t lowest_page_in_region = gpa_base >> vm->page_shift;
23+
sparsebit_idx_t i, j;
24+
25+
if (!sparsebit_any_set(protected_phy_pages))
26+
return;
27+
28+
sev_register_encrypted_memory(vm, region);
29+
30+
sparsebit_for_each_set_range(protected_phy_pages, i, j) {
31+
const uint64_t size = (j - i + 1) * vm->page_size;
32+
const uint64_t offset = (i - lowest_page_in_region) * vm->page_size;
33+
34+
sev_launch_update_data(vm, gpa_base + offset, size);
35+
}
36+
}
37+
38+
void sev_vm_launch(struct kvm_vm *vm, uint32_t policy)
39+
{
40+
struct kvm_sev_launch_start launch_start = {
41+
.policy = policy,
42+
};
43+
struct userspace_mem_region *region;
44+
struct kvm_sev_guest_status status;
45+
int ctr;
46+
47+
vm_sev_ioctl(vm, KVM_SEV_LAUNCH_START, &launch_start);
48+
vm_sev_ioctl(vm, KVM_SEV_GUEST_STATUS, &status);
49+
50+
TEST_ASSERT_EQ(status.policy, policy);
51+
TEST_ASSERT_EQ(status.state, SEV_GUEST_STATE_LAUNCH_UPDATE);
52+
53+
hash_for_each(vm->regions.slot_hash, ctr, region, slot_node)
54+
encrypt_region(vm, region);
55+
56+
vm->arch.is_pt_protected = true;
57+
}
58+
59+
void sev_vm_launch_measure(struct kvm_vm *vm, uint8_t *measurement)
60+
{
61+
struct kvm_sev_launch_measure launch_measure;
62+
struct kvm_sev_guest_status guest_status;
63+
64+
launch_measure.len = 256;
65+
launch_measure.uaddr = (__u64)measurement;
66+
vm_sev_ioctl(vm, KVM_SEV_LAUNCH_MEASURE, &launch_measure);
67+
68+
vm_sev_ioctl(vm, KVM_SEV_GUEST_STATUS, &guest_status);
69+
TEST_ASSERT_EQ(guest_status.state, SEV_GUEST_STATE_LAUNCH_SECRET);
70+
}
71+
72+
void sev_vm_launch_finish(struct kvm_vm *vm)
73+
{
74+
struct kvm_sev_guest_status status;
75+
76+
vm_sev_ioctl(vm, KVM_SEV_GUEST_STATUS, &status);
77+
TEST_ASSERT(status.state == SEV_GUEST_STATE_LAUNCH_UPDATE ||
78+
status.state == SEV_GUEST_STATE_LAUNCH_SECRET,
79+
"Unexpected guest state: %d", status.state);
80+
81+
vm_sev_ioctl(vm, KVM_SEV_LAUNCH_FINISH, NULL);
82+
83+
vm_sev_ioctl(vm, KVM_SEV_GUEST_STATUS, &status);
84+
TEST_ASSERT_EQ(status.state, SEV_GUEST_STATE_RUNNING);
85+
}
86+
87+
struct kvm_vm *vm_sev_create_with_one_vcpu(uint32_t policy, void *guest_code,
88+
struct kvm_vcpu **cpu)
89+
{
90+
struct vm_shape shape = {
91+
.type = VM_TYPE_DEFAULT,
92+
.mode = VM_MODE_DEFAULT,
93+
.subtype = VM_SUBTYPE_SEV,
94+
};
95+
struct kvm_vm *vm;
96+
struct kvm_vcpu *cpus[1];
97+
uint8_t measurement[512];
98+
99+
vm = __vm_create_with_vcpus(shape, 1, 0, guest_code, cpus);
100+
*cpu = cpus[0];
101+
102+
sev_vm_launch(vm, policy);
103+
104+
/* TODO: Validate the measurement is as expected. */
105+
sev_vm_launch_measure(vm, measurement);
106+
107+
sev_vm_launch_finish(vm);
108+
109+
return vm;
110+
}

0 commit comments

Comments
 (0)