1 /*
2 * Check decoding of KVM_* commands of ioctl syscall using /dev/kvm API.
3 * Based on kvmtest.c from https://lwn.net/Articles/658512/
4 *
5 * kvmtest.c author: Josh Triplett <josh@joshtriplett.org>
6 * Copyright (c) 2015 Intel Corporation
7 * Copyright (c) 2017-2018 The strace developers.
8 *
9 * Permission is hereby granted, free of charge, to any person obtaining a copy
10 * of this software and associated documentation files (the "Software"), to
11 * deal in the Software without restriction, including without limitation the
12 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
13 * sell copies of the Software, and to permit persons to whom the Software is
14 * furnished to do so, subject to the following conditions:
15 *
16 * The above copyright notice and this permission notice shall be included in
17 * all copies or substantial portions of the Software.
18 *
19 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
24 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
25 * IN THE SOFTWARE.
26 */
27
28 #include "tests.h"
29
30 #if defined HAVE_LINUX_KVM_H \
31 && defined HAVE_STRUCT_KVM_REGS \
32 && defined HAVE_STRUCT_KVM_SREGS \
33 && defined HAVE_STRUCT_KVM_USERSPACE_MEMORY_REGION \
34 &&(defined __x86_64__ || defined __i386__)
35
36 # include <fcntl.h>
37 # include <stdint.h>
38 # include <stdio.h>
39 # include <stdlib.h>
40 # include <string.h>
41 # include <sys/ioctl.h>
42 # include <sys/mman.h>
43 # include <linux/kvm.h>
44
45 static int
kvm_ioctl(int fd,unsigned long cmd,const char * cmd_str,void * arg)46 kvm_ioctl(int fd, unsigned long cmd, const char *cmd_str, void *arg)
47 {
48 int rc = ioctl(fd, cmd, arg);
49 if (rc < 0)
50 perror_msg_and_skip("%s", cmd_str);
51 return rc;
52 }
53
54 #define KVM_IOCTL(fd_, cmd_, arg_) \
55 kvm_ioctl((fd_), (cmd_), #cmd_, (arg_))
56
57 static const char dev[] = "/dev/kvm";
58 static const char vm_dev[] = "anon_inode:kvm-vm";
59 static const char vcpu_dev[] = "anon_inode:kvm-vcpu";
60 static size_t page_size;
61
62 extern const char code[];
63 extern const unsigned short code_size;
64
65 __asm__(
66 ".type code, @object \n"
67 "code: \n"
68 " mov $0xd80003f8, %edx \n"
69 " mov $'\n', %al \n"
70 " out %al, (%dx) \n"
71 " hlt \n"
72 ".size code, . - code \n"
73 ".type code_size, @object \n"
74 "code_size: \n"
75 " .short . - code \n"
76 ".size code_size, . - code_size \n"
77 );
78
79
80 static void
run_kvm(const int vcpu_fd,struct kvm_run * const run,const size_t mmap_size,void * const mem)81 run_kvm(const int vcpu_fd, struct kvm_run *const run, const size_t mmap_size,
82 void *const mem)
83 {
84 /* Initialize CS to point at 0, via a read-modify-write of sregs. */
85 struct kvm_sregs sregs;
86 KVM_IOCTL(vcpu_fd, KVM_GET_SREGS, &sregs);
87 printf("ioctl(%d<%s>, KVM_GET_SREGS, {cs={base=%#jx, limit=%u, selector=%u"
88 ", type=%u, present=%u, dpl=%u, db=%u, s=%u, l=%u, g=%u, avl=%u}"
89 ", ...}) = 0\n", vcpu_fd, vcpu_dev, (uintmax_t) sregs.cs.base,
90 sregs.cs.limit, sregs.cs.selector, sregs.cs.type,
91 sregs.cs.present, sregs.cs.dpl, sregs.cs.db, sregs.cs.s,
92 sregs.cs.l, sregs.cs.g, sregs.cs.avl);
93
94 sregs.cs.base = 0;
95 sregs.cs.selector = 0;
96 KVM_IOCTL(vcpu_fd, KVM_SET_SREGS, &sregs);
97 printf("ioctl(%d<%s>, KVM_SET_SREGS, {cs={base=%#jx, limit=%u"
98 ", selector=%u, type=%u, present=%u, dpl=%u, db=%u, s=%u"
99 ", l=%u, g=%u, avl=%u}, ...}) = 0\n",
100 vcpu_fd, vcpu_dev, (uintmax_t) sregs.cs.base,
101 sregs.cs.limit, sregs.cs.selector, sregs.cs.type,
102 sregs.cs.present, sregs.cs.dpl, sregs.cs.db, sregs.cs.s,
103 sregs.cs.l, sregs.cs.g, sregs.cs.avl);
104
105 /*
106 * Initialize registers: instruction pointer for our code, addends,
107 * and initial flags required by x86 architecture.
108 */
109 struct kvm_regs regs = {
110 .rip = page_size,
111 .rax = 2,
112 .rbx = 2,
113 .rflags = 0x2,
114 };
115 KVM_IOCTL(vcpu_fd, KVM_SET_REGS, ®s);
116 printf("ioctl(%d<%s>, KVM_SET_REGS, {rax=%#jx, ..."
117 ", rsp=%#jx, rbp=%#jx, ..., rip=%#jx, rflags=%#jx}) = 0\n",
118 vcpu_fd, vcpu_dev, (uintmax_t) regs.rax,
119 (uintmax_t) regs.rsp, (uintmax_t) regs.rbp,
120 (uintmax_t) regs.rip, (uintmax_t) regs.rflags);
121
122 /* Copy the code */
123 memcpy(mem, code, code_size);
124
125 const char *p = "\n";
126
127 /* Repeatedly run code and handle VM exits. */
128 for (;;) {
129 KVM_IOCTL(vcpu_fd, KVM_RUN, NULL);
130 printf("ioctl(%d<%s>, KVM_RUN, 0) = 0\n", vcpu_fd, vcpu_dev);
131
132 switch (run->exit_reason) {
133 case KVM_EXIT_HLT:
134 if (p)
135 error_msg_and_fail("premature KVM_EXIT_HLT");
136 return;
137 case KVM_EXIT_IO:
138 if (run->io.direction == KVM_EXIT_IO_OUT
139 && run->io.size == 1
140 && run->io.port == 0x03f8
141 && run->io.count == 1
142 && run->io.data_offset < mmap_size
143 && p && *p == ((char *) run)[run->io.data_offset])
144 p = NULL;
145 else
146 error_msg_and_fail("unhandled KVM_EXIT_IO");
147 break;
148 case KVM_EXIT_MMIO:
149 error_msg_and_fail("Got an unexpected MMIO exit:"
150 " phys_addr %#llx,"
151 " data %02x %02x %02x %02x"
152 " %02x %02x %02x %02x,"
153 " len %u, is_write %hhu",
154 (unsigned long long) run->mmio.phys_addr,
155 run->mmio.data[0], run->mmio.data[1],
156 run->mmio.data[2], run->mmio.data[3],
157 run->mmio.data[4], run->mmio.data[5],
158 run->mmio.data[6], run->mmio.data[7],
159 run->mmio.len, run->mmio.is_write);
160
161 default:
162 error_msg_and_fail("exit_reason = %#x",
163 run->exit_reason);
164 }
165 }
166 }
167
168 int
main(void)169 main(void)
170 {
171 skip_if_unavailable("/proc/self/fd/");
172
173 int kvm = open(dev, O_RDWR);
174 if (kvm < 0)
175 perror_msg_and_skip("open: %s", dev);
176
177 /* Make sure we have the stable version of the API */
178 int ret = KVM_IOCTL(kvm, KVM_GET_API_VERSION, 0);
179 if (ret != KVM_API_VERSION)
180 error_msg_and_skip("KVM_GET_API_VERSION returned %d"
181 ", KVM_API_VERSION is %d",
182 kvm, KVM_API_VERSION);
183 printf("ioctl(%d<%s>, KVM_GET_API_VERSION, 0) = %d\n",
184 kvm, dev, ret);
185
186 int vm_fd = KVM_IOCTL(kvm, KVM_CREATE_VM, 0);
187 printf("ioctl(%d<%s>, KVM_CREATE_VM, 0) = %d<%s>\n",
188 kvm, dev, vm_fd, vm_dev);
189
190 /* Allocate one aligned page of guest memory to hold the code. */
191 page_size = get_page_size();
192 void *const mem = mmap(NULL, page_size, PROT_READ | PROT_WRITE,
193 MAP_SHARED | MAP_ANONYMOUS, -1, 0);
194 if (mem == MAP_FAILED)
195 perror_msg_and_fail("mmap page");
196
197 /* Map it to the second page frame (to avoid the real-mode IDT at 0). */
198 struct kvm_userspace_memory_region region = {
199 .slot = 0,
200 .guest_phys_addr = page_size,
201 .memory_size = page_size,
202 .userspace_addr = (uintptr_t) mem,
203 };
204 KVM_IOCTL(vm_fd, KVM_SET_USER_MEMORY_REGION, ®ion);
205 printf("ioctl(%d<%s>, KVM_SET_USER_MEMORY_REGION"
206 ", {slot=0, flags=0, guest_phys_addr=%#lx, memory_size=%lu"
207 ", userspace_addr=%p}) = 0\n", vm_fd, vm_dev,
208 (unsigned long) page_size, (unsigned long) page_size, mem);
209
210 int vcpu_fd = KVM_IOCTL(vm_fd, KVM_CREATE_VCPU, NULL);
211 printf("ioctl(%d<%s>, KVM_CREATE_VCPU, 0) = %d<%s>\n",
212 vm_fd, vm_dev, vcpu_fd, vcpu_dev);
213
214 /* Map the shared kvm_run structure and following data. */
215 ret = KVM_IOCTL(kvm, KVM_GET_VCPU_MMAP_SIZE, NULL);
216 struct kvm_run *run;
217 if (ret < (int) sizeof(*run))
218 error_msg_and_fail("KVM_GET_VCPU_MMAP_SIZE returned %d < %d",
219 ret, (int) sizeof(*run));
220 printf("ioctl(%d<%s>, KVM_GET_VCPU_MMAP_SIZE, 0) = %d\n",
221 kvm, dev, ret);
222
223 const size_t mmap_size = (ret + page_size - 1) & -page_size;
224 run = mmap(NULL, mmap_size, PROT_READ | PROT_WRITE,
225 MAP_SHARED, vcpu_fd, 0);
226 if (run == MAP_FAILED)
227 perror_msg_and_fail("mmap vcpu");
228
229 run_kvm(vcpu_fd, run, mmap_size, mem);
230
231 puts("+++ exited with 0 +++");
232 return 0;
233 }
234
235 #else /* !HAVE_LINUX_KVM_H */
236
237 SKIP_MAIN_UNDEFINED("HAVE_LINUX_KVM_H && HAVE_STRUCT_KVM_REGS && "
238 "HAVE_STRUCT_KVM_SREGS && "
239 "HAVE_STRUCT_KVM_USERSPACE_MEMORY_REGION && "
240 "(__x86_64__ || __i386__)")
241
242 #endif
243