Skip to content

Commit e357778

Browse files
Ackerley Tngbonzini
authored andcommitted
KVM: selftests: Test KVM exit behavior for private memory/access
"Testing private access when memslot gets deleted" tests the behavior of KVM when a private memslot gets deleted while the VM is using the private memslot. When KVM looks up the deleted (slot = NULL) memslot, KVM should exit to userspace with KVM_EXIT_MEMORY_FAULT. In the second test, upon a private access to non-private memslot, KVM should also exit to userspace with KVM_EXIT_MEMORY_FAULT. Intentionally don't take a requirement on KVM_CAP_GUEST_MEMFD, KVM_CAP_MEMORY_FAULT_INFO, KVM_MEMORY_ATTRIBUTE_PRIVATE, etc., as it's a KVM bug to advertise KVM_X86_SW_PROTECTED_VM without its prerequisites. Signed-off-by: Ackerley Tng <ackerleytng@google.com> [sean: call out the similarities with set_memory_region_test] Signed-off-by: Sean Christopherson <seanjc@google.com> Message-Id: <20231027182217.3615211-36-seanjc@google.com> Reviewed-by: Fuad Tabba <tabba@google.com> Tested-by: Fuad Tabba <tabba@google.com> Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
1 parent 8a89efd commit e357778

2 files changed

Lines changed: 121 additions & 0 deletions

File tree

tools/testing/selftests/kvm/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,7 @@ TEST_GEN_PROGS_x86_64 += x86_64/nested_exceptions_test
9292
TEST_GEN_PROGS_x86_64 += x86_64/platform_info_test
9393
TEST_GEN_PROGS_x86_64 += x86_64/pmu_event_filter_test
9494
TEST_GEN_PROGS_x86_64 += x86_64/private_mem_conversions_test
95+
TEST_GEN_PROGS_x86_64 += x86_64/private_mem_kvm_exits_test
9596
TEST_GEN_PROGS_x86_64 += x86_64/set_boot_cpu_id
9697
TEST_GEN_PROGS_x86_64 += x86_64/set_sregs_test
9798
TEST_GEN_PROGS_x86_64 += x86_64/smaller_maxphyaddr_emulation_test
Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
// SPDX-License-Identifier: GPL-2.0-only
2+
/*
3+
* Copyright (C) 2023, Google LLC.
4+
*/
5+
#include <linux/kvm.h>
6+
#include <pthread.h>
7+
#include <stdint.h>
8+
9+
#include "kvm_util.h"
10+
#include "processor.h"
11+
#include "test_util.h"
12+
13+
/* Arbitrarily selected to avoid overlaps with anything else */
14+
#define EXITS_TEST_GVA 0xc0000000
15+
#define EXITS_TEST_GPA EXITS_TEST_GVA
16+
#define EXITS_TEST_NPAGES 1
17+
#define EXITS_TEST_SIZE (EXITS_TEST_NPAGES * PAGE_SIZE)
18+
#define EXITS_TEST_SLOT 10
19+
20+
static uint64_t guest_repeatedly_read(void)
21+
{
22+
volatile uint64_t value;
23+
24+
while (true)
25+
value = *((uint64_t *) EXITS_TEST_GVA);
26+
27+
return value;
28+
}
29+
30+
static uint32_t run_vcpu_get_exit_reason(struct kvm_vcpu *vcpu)
31+
{
32+
int r;
33+
34+
r = _vcpu_run(vcpu);
35+
if (r) {
36+
TEST_ASSERT(errno == EFAULT, KVM_IOCTL_ERROR(KVM_RUN, r));
37+
TEST_ASSERT_EQ(vcpu->run->exit_reason, KVM_EXIT_MEMORY_FAULT);
38+
}
39+
return vcpu->run->exit_reason;
40+
}
41+
42+
const struct vm_shape protected_vm_shape = {
43+
.mode = VM_MODE_DEFAULT,
44+
.type = KVM_X86_SW_PROTECTED_VM,
45+
};
46+
47+
static void test_private_access_memslot_deleted(void)
48+
{
49+
struct kvm_vm *vm;
50+
struct kvm_vcpu *vcpu;
51+
pthread_t vm_thread;
52+
void *thread_return;
53+
uint32_t exit_reason;
54+
55+
vm = vm_create_shape_with_one_vcpu(protected_vm_shape, &vcpu,
56+
guest_repeatedly_read);
57+
58+
vm_userspace_mem_region_add(vm, VM_MEM_SRC_ANONYMOUS,
59+
EXITS_TEST_GPA, EXITS_TEST_SLOT,
60+
EXITS_TEST_NPAGES,
61+
KVM_MEM_GUEST_MEMFD);
62+
63+
virt_map(vm, EXITS_TEST_GVA, EXITS_TEST_GPA, EXITS_TEST_NPAGES);
64+
65+
/* Request to access page privately */
66+
vm_mem_set_private(vm, EXITS_TEST_GPA, EXITS_TEST_SIZE);
67+
68+
pthread_create(&vm_thread, NULL,
69+
(void *(*)(void *))run_vcpu_get_exit_reason,
70+
(void *)vcpu);
71+
72+
vm_mem_region_delete(vm, EXITS_TEST_SLOT);
73+
74+
pthread_join(vm_thread, &thread_return);
75+
exit_reason = (uint32_t)(uint64_t)thread_return;
76+
77+
TEST_ASSERT_EQ(exit_reason, KVM_EXIT_MEMORY_FAULT);
78+
TEST_ASSERT_EQ(vcpu->run->memory_fault.flags, KVM_MEMORY_EXIT_FLAG_PRIVATE);
79+
TEST_ASSERT_EQ(vcpu->run->memory_fault.gpa, EXITS_TEST_GPA);
80+
TEST_ASSERT_EQ(vcpu->run->memory_fault.size, EXITS_TEST_SIZE);
81+
82+
kvm_vm_free(vm);
83+
}
84+
85+
static void test_private_access_memslot_not_private(void)
86+
{
87+
struct kvm_vm *vm;
88+
struct kvm_vcpu *vcpu;
89+
uint32_t exit_reason;
90+
91+
vm = vm_create_shape_with_one_vcpu(protected_vm_shape, &vcpu,
92+
guest_repeatedly_read);
93+
94+
/* Add a non-private memslot (flags = 0) */
95+
vm_userspace_mem_region_add(vm, VM_MEM_SRC_ANONYMOUS,
96+
EXITS_TEST_GPA, EXITS_TEST_SLOT,
97+
EXITS_TEST_NPAGES, 0);
98+
99+
virt_map(vm, EXITS_TEST_GVA, EXITS_TEST_GPA, EXITS_TEST_NPAGES);
100+
101+
/* Request to access page privately */
102+
vm_mem_set_private(vm, EXITS_TEST_GPA, EXITS_TEST_SIZE);
103+
104+
exit_reason = run_vcpu_get_exit_reason(vcpu);
105+
106+
TEST_ASSERT_EQ(exit_reason, KVM_EXIT_MEMORY_FAULT);
107+
TEST_ASSERT_EQ(vcpu->run->memory_fault.flags, KVM_MEMORY_EXIT_FLAG_PRIVATE);
108+
TEST_ASSERT_EQ(vcpu->run->memory_fault.gpa, EXITS_TEST_GPA);
109+
TEST_ASSERT_EQ(vcpu->run->memory_fault.size, EXITS_TEST_SIZE);
110+
111+
kvm_vm_free(vm);
112+
}
113+
114+
int main(int argc, char *argv[])
115+
{
116+
TEST_REQUIRE(kvm_check_cap(KVM_CAP_VM_TYPES) & BIT(KVM_X86_SW_PROTECTED_VM));
117+
118+
test_private_access_memslot_deleted();
119+
test_private_access_memslot_not_private();
120+
}

0 commit comments

Comments
 (0)