• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3  * Test checking that memory of protected guests is wiped after teardown.
4  *
5  * Copyright (C) 2022, Google LLC.
6  */
7 
8 #define _GNU_SOURCE
9 
10 #include <err.h>
11 #include <errno.h>
12 #include <fcntl.h>
13 #include <stdio.h>
14 #include <stdint.h>
15 #include <stdlib.h>
16 #include <string.h>
17 #include <unistd.h>
18 
19 #include <linux/kvm.h>
20 #include <sys/ioctl.h>
21 #include <sys/mman.h>
22 
23 #include "kselftest.h"
24 
25 #define KVM_VM_TYPE_ARM_PROTECTED	(1UL << 31)
26 
27 #define REG_X(number)	(0x6030000000100000ULL + (number) * 2UL)
28 #define REG_PC		0x6030000000100040ULL
29 
set_one_reg(int vcpufd,uint64_t reg_id,uint64_t val)30 static void set_one_reg(int vcpufd, uint64_t reg_id, uint64_t val)
31 {
32 	uint64_t reg_data;
33 	struct kvm_one_reg reg;
34 	int ret;
35 
36 	reg.addr = (__u64) &reg_data;
37 	reg_data = val;
38 	reg.id = reg_id;
39 
40 	ret = ioctl(vcpufd, KVM_SET_ONE_REG, &reg);
41 	if (ret < 0)
42 		ksft_exit_fail_msg("Failed to set reg: %d\n", ret);
43 }
44 
get_kvm(void)45 static int get_kvm(void)
46 {
47 	size_t run_size;
48 	int kvm, ret;
49 
50 	kvm = open("/dev/kvm", O_RDWR | O_CLOEXEC);
51 	if (kvm < 0)
52 		ksft_exit_skip("KVM not supported\n");
53 
54 	ret = ioctl(kvm, KVM_GET_API_VERSION, NULL);
55 	if (ret != 12)
56 		ksft_exit_fail_msg("KVM_GET_API_VERSION %d, expected 12", ret);
57 
58 	run_size = ioctl(kvm, KVM_GET_VCPU_MMAP_SIZE, NULL);
59 	if (run_size < sizeof(struct kvm_run))
60 		ksft_exit_fail_msg("KVM_GET_VCPU_MMAP_SIZE unexpectedly small\n");
61 
62 	return kvm;
63 }
64 
create_protected_vm(int kvm)65 static int create_protected_vm(int kvm)
66 {
67 	int vmfd = ioctl(kvm, KVM_CREATE_VM, KVM_VM_TYPE_ARM_PROTECTED);
68 
69 	if (vmfd < 0)
70 		ksft_exit_skip("Protected guests not supported: %d\n", vmfd);
71 
72 	return vmfd;
73 }
74 
create_vcpu(int vmfd,struct kvm_run ** run)75 static int create_vcpu(int vmfd, struct kvm_run **run)
76 {
77 	struct kvm_vcpu_init vcpu_init;
78 	int vcpufd, ret;
79 
80 	ret = ioctl(vmfd, KVM_ARM_PREFERRED_TARGET, &vcpu_init);
81 	if (ret)
82 		ksft_exit_fail_msg("Failed to set kvm_vcpu_init %d\n", ret);
83 
84 	vcpufd = ioctl(vmfd, KVM_CREATE_VCPU, (unsigned long)0);
85 	if (vcpufd < 0)
86 		ksft_exit_fail_msg("Failed to create VCPU: %d\n", vcpufd);
87 
88 	*run = mmap(NULL, sizeof(**run), PROT_READ | PROT_WRITE, MAP_SHARED, vcpufd, 0);
89 	if (!run)
90 		ksft_exit_fail_msg("Failed to mmap vcpu_run struct\n");
91 
92 	ret = ioctl(vcpufd, KVM_ARM_VCPU_INIT, &vcpu_init);
93 	if (ret)
94 		ksft_exit_fail_msg("Failed to initialize VCPU %d\n", ret);
95 
96 	return vcpufd;
97 }
98 
teardown(int kvm,int vmfd,int vcpufd,struct kvm_run * run)99 static void teardown(int kvm, int vmfd, int vcpufd, struct kvm_run *run)
100 {
101 	int ret = munmap(run, sizeof(*run));
102 
103 	if (ret)
104 		ksft_exit_fail_msg("Failed to unmap vCPU run: %d\n", ret);
105 
106 	ret = close(vcpufd);
107 	if (ret)
108 		ksft_exit_fail_msg("Failed to destroy VCPU: %d\n", ret);
109 
110 	ret = close(vmfd);
111 	if (ret)
112 		ksft_exit_fail_msg("Failed to destroy VM: %d\n", ret);
113 
114 	ret = close(kvm);
115 	if (ret)
116 		ksft_exit_fail_msg("Failed to close KVM fd: %d\n", ret);
117 }
118 
main(void)119 int main(void)
120 {
121 	struct kvm_userspace_memory_region region;
122 	long page_size = sysconf(_SC_PAGESIZE);
123 	int ret, kvm, vmfd, vcpufd;
124 	uint32_t guest_code[2];
125 	struct kvm_run *run;
126 	uint8_t *guest_mem;
127 	size_t run_size;
128 
129 	kvm = get_kvm();
130 	vmfd = create_protected_vm(kvm);
131 	vcpufd = create_vcpu(vmfd, &run);
132 
133 	/* Create a one-page memslot for the guest */
134 	guest_mem = mmap(NULL, page_size, PROT_READ | PROT_WRITE,
135 			MAP_SHARED | MAP_ANONYMOUS, -1, 0);
136 	if (guest_mem == MAP_FAILED)
137 		ksft_exit_fail_msg("Failed to mmap guest memory\n");
138 	region = (struct kvm_userspace_memory_region) {
139 		.slot = 0,
140 		.guest_phys_addr = 1UL << 30,
141 		.memory_size = page_size,
142 		.userspace_addr = (uint64_t)guest_mem,
143 	};
144 
145 	/* Copy some code in guest memory. */
146 	guest_code[0] = 0xf9400001;	/* 1:  ldr	x1, [x0]  */
147 	guest_code[1] = 0x17ffffff;	/*     b	1b	  */
148 	memcpy(guest_mem, guest_code, sizeof(guest_code));
149 	ret = ioctl(vmfd, KVM_SET_USER_MEMORY_REGION, &region);
150 	if (ret)
151 		ksft_exit_fail_msg("Failed to set memory region: %d\n", ret);
152 
153 	/*
154 	 * Get the VCPU to run one instruction, to be sure the page containing
155 	 * the code has been faulted in.
156 	 */
157 	set_one_reg(vcpufd, REG_PC, region.guest_phys_addr);
158 	set_one_reg(vcpufd, REG_X(0), region.guest_phys_addr + region.memory_size);
159 	ret = ioctl(vcpufd, KVM_RUN, NULL);
160 	if (ret)
161 		ksft_exit_fail_msg("Failed to run vcpu: %d\n", ret);
162 	if (run->exit_reason != KVM_EXIT_MMIO)
163 		ksft_exit_fail_msg("Unexpected KVM exit reason: %u\n", run->exit_reason);
164 
165 	/*
166 	 * Tear the guest down, and check that the donated memory has been
167 	 * wiped by the hypervisor.
168 	 */
169 	teardown(kvm, vmfd, vcpufd, run);
170 	if (!memcmp(guest_mem, guest_code, sizeof(guest_code)))
171 		ksft_exit_fail_msg("Protected guest memory has not been poisoned\n");
172 
173 	ksft_exit_pass();
174 }
175