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 45static int 46kvm_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 57static const char dev[] = "/dev/kvm"; 58static const char vm_dev[] = "anon_inode:kvm-vm"; 59static const char vcpu_dev[] = "anon_inode:kvm-vcpu"; 60static size_t page_size; 61 62extern const char code[]; 63extern 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 80static void 81run_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 168int 169main(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 237SKIP_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