1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3 * Test shared zeropage handling (with/without storage keys)
4 *
5 * Copyright (C) 2024, Red Hat, Inc.
6 */
7 #include <linux/fs.h>
8
9 #include "test_util.h"
10 #include "kvm_syscalls.h"
11 #include "kvm_util.h"
12 #include "kselftest.h"
13 #include "ucall_common.h"
14
set_storage_key(void * addr,u8 skey)15 static void set_storage_key(void *addr, u8 skey)
16 {
17 asm volatile("sske %0,%1" : : "d" (skey), "a" (addr));
18 }
19
guest_code(void)20 static void guest_code(void)
21 {
22 /* Issue some storage key instruction. */
23 set_storage_key((void *)0, 0x98);
24 GUEST_DONE();
25 }
26
27 /*
28 * Returns 1 if the shared zeropage is mapped, 0 if something else is mapped.
29 * Returns < 0 on error or if nothing is mapped.
30 */
maps_shared_zeropage(int pagemap_fd,void * addr)31 static int maps_shared_zeropage(int pagemap_fd, void *addr)
32 {
33 struct page_region region;
34 struct pm_scan_arg arg = {
35 .start = (uintptr_t)addr,
36 .end = (uintptr_t)addr + 4096,
37 .vec = (uintptr_t)®ion,
38 .vec_len = 1,
39 .size = sizeof(struct pm_scan_arg),
40 .category_mask = PAGE_IS_PFNZERO,
41 .category_anyof_mask = PAGE_IS_PRESENT,
42 .return_mask = PAGE_IS_PFNZERO,
43 };
44 return ioctl(pagemap_fd, PAGEMAP_SCAN, &arg);
45 }
46
main(int argc,char * argv[])47 int main(int argc, char *argv[])
48 {
49 char *mem, *page0, *page1, *page2, tmp;
50 const size_t pagesize = getpagesize();
51 struct kvm_vcpu *vcpu;
52 struct kvm_vm *vm;
53 struct ucall uc;
54 int pagemap_fd;
55
56 ksft_print_header();
57 ksft_set_plan(3);
58
59 /*
60 * We'll use memory that is not mapped into the VM for simplicity.
61 * Shared zeropages are enabled/disabled per-process.
62 */
63 mem = mmap(0, 3 * pagesize, PROT_READ, MAP_PRIVATE | MAP_ANON, -1, 0);
64 TEST_ASSERT(mem != MAP_FAILED, "mmap() failed");
65
66 /* Disable THP. Ignore errors on older kernels. */
67 madvise(mem, 3 * pagesize, MADV_NOHUGEPAGE);
68
69 page0 = mem;
70 page1 = page0 + pagesize;
71 page2 = page1 + pagesize;
72
73 /* Can we even detect shared zeropages? */
74 pagemap_fd = open("/proc/self/pagemap", O_RDONLY);
75 TEST_REQUIRE(pagemap_fd >= 0);
76
77 tmp = *page0;
78 asm volatile("" : "+r" (tmp));
79 TEST_REQUIRE(maps_shared_zeropage(pagemap_fd, page0) == 1);
80
81 vm = vm_create_with_one_vcpu(&vcpu, guest_code);
82
83 /* Verify that we get the shared zeropage after VM creation. */
84 tmp = *page1;
85 asm volatile("" : "+r" (tmp));
86 ksft_test_result(maps_shared_zeropage(pagemap_fd, page1) == 1,
87 "Shared zeropages should be enabled\n");
88
89 /*
90 * Let our VM execute a storage key instruction that should
91 * unshare all shared zeropages.
92 */
93 vcpu_run(vcpu);
94 get_ucall(vcpu, &uc);
95 TEST_ASSERT_EQ(uc.cmd, UCALL_DONE);
96
97 /* Verify that we don't have a shared zeropage anymore. */
98 ksft_test_result(!maps_shared_zeropage(pagemap_fd, page1),
99 "Shared zeropage should be gone\n");
100
101 /* Verify that we don't get any new shared zeropages. */
102 tmp = *page2;
103 asm volatile("" : "+r" (tmp));
104 ksft_test_result(!maps_shared_zeropage(pagemap_fd, page2),
105 "Shared zeropages should be disabled\n");
106
107 kvm_vm_free(vm);
108
109 ksft_finished();
110 }
111