Home | History | Annotate | Download | only in tests
      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 (at) 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
     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
     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, &regs);
    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
    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, &region);
    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