[PATCH v3 4/4] tests: check decoding of vcpu auxstr

Masatake YAMATO yamato at redhat.com
Sat Jul 7 07:49:13 UTC 2018


* tests/ioctl_kvm_run_common.c: Rename from ioctl_kvm_run.c.
(run_kvm): Parametrize printing of KVM_RUN ioctl with print_KVM_RUN
invocation.
(main): Invoke optional KVM_NO_CPUID_CALLBACK macro when the old kernel
behavior is detected.
* tests/Makefile.am (EXTRA_DIST): Add ioctl_kvm_run_common.c.
* tests/ioctl_kvm_run.c: New file, a wrapper around
ioctl_kvm_run_common.c.
* ioctl_kvm_run_auxstr_vcpu.c: Likewise.
* tests/gen_tests.in (ioctl_kvm_run_auxstr_vcpu): New test.
* tests/pure_executables.list: Add ioctl_kvm_run_auxstr_vcpu.
* tests/.gitignore: Likewise.

Signed-off-by: Masatake YAMATO <yamato at redhat.com>
Co-Authored-by: Dmitry V. Levin <ldv at altlinux.org>
---
 tests/.gitignore                  |   1 +
 tests/Makefile.am                 |   1 +
 tests/gen_tests.in                |   1 +
 tests/ioctl_kvm_run.c             | 266 +---------------------------
 tests/ioctl_kvm_run_auxstr_vcpu.c |  24 +++
 tests/ioctl_kvm_run_common.c      | 279 ++++++++++++++++++++++++++++++
 tests/pure_executables.list       |   1 +
 7 files changed, 311 insertions(+), 262 deletions(-)
 create mode 100644 tests/ioctl_kvm_run_auxstr_vcpu.c
 create mode 100644 tests/ioctl_kvm_run_common.c

diff --git a/tests/.gitignore b/tests/.gitignore
index 2285a357..f15f5b03 100644
--- a/tests/.gitignore
+++ b/tests/.gitignore
@@ -145,6 +145,7 @@ ioctl_evdev
 ioctl_evdev-v
 ioctl_inotify
 ioctl_kvm_run
+ioctl_kvm_run_auxstr_vcpu
 ioctl_loop
 ioctl_loop-nv
 ioctl_loop-v
diff --git a/tests/Makefile.am b/tests/Makefile.am
index 34b08a9f..41643f01 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -386,6 +386,7 @@ EXTRA_DIST = \
 	init.sh \
 	init_delete_module.h \
 	ipc.sh \
+	ioctl_kvm_run_common.c \
 	ksysent.sed \
 	lstatx.c \
 	match.awk \
diff --git a/tests/gen_tests.in b/tests/gen_tests.in
index 24e0510e..869c087e 100644
--- a/tests/gen_tests.in
+++ b/tests/gen_tests.in
@@ -140,6 +140,7 @@ ioctl_evdev	+ioctl.test
 ioctl_evdev-v	+ioctl.test -v
 ioctl_inotify	+ioctl.test
 ioctl_kvm_run	+ioctl.test -a36 -y
+ioctl_kvm_run_auxstr_vcpu	+ioctl.test -a36 -y -e kvm=vcpu
 ioctl_loop	+ioctl.test
 ioctl_loop-nv	+ioctl.test -a22 -e verbose=none
 ioctl_loop-v	+ioctl.test -v
diff --git a/tests/ioctl_kvm_run.c b/tests/ioctl_kvm_run.c
index e1bef579..602507fa 100644
--- a/tests/ioctl_kvm_run.c
+++ b/tests/ioctl_kvm_run.c
@@ -1,269 +1,11 @@
-/*
- * Check decoding of KVM_* commands of ioctl syscall using /dev/kvm API.
- * Based on kvmtest.c from https://lwn.net/Articles/658512/
- *
- * kvmtest.c author: Josh Triplett <josh at joshtriplett.org>
- * Copyright (c) 2015 Intel Corporation
- * Copyright (c) 2017-2018 The strace developers.
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to
- * deal in the Software without restriction, including without limitation the
- * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
- * sell copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
- * IN THE SOFTWARE.
- */
-
-#include "tests.h"
-
-#if defined HAVE_LINUX_KVM_H				\
- && defined HAVE_STRUCT_KVM_REGS			\
- && defined HAVE_STRUCT_KVM_SREGS			\
- && defined HAVE_STRUCT_KVM_USERSPACE_MEMORY_REGION	\
- &&(defined __x86_64__ || defined __i386__)
-
-# include <fcntl.h>
-# include <stdint.h>
-# include <stdio.h>
-# include <stdlib.h>
-# include <string.h>
-# include <sys/ioctl.h>
-# include <sys/mman.h>
-# include <unistd.h>
-# include <linux/kvm.h>
-
-static int
-kvm_ioctl(int fd, unsigned long cmd, const char *cmd_str, void *arg)
-{
-	int rc = ioctl(fd, cmd, arg);
-	if (rc < 0)
-		perror_msg_and_skip("%s", cmd_str);
-	return rc;
-}
-
-#define KVM_IOCTL(fd_, cmd_, arg_)	\
-	kvm_ioctl((fd_), (cmd_), #cmd_, (arg_))
-
-static const char dev[] = "/dev/kvm";
-static const char vm_dev[] = "anon_inode:kvm-vm";
-static char vcpu_dev[] = "anon_inode:kvm-vcpu:0";
-static size_t page_size;
-
-extern const char code[];
-extern const unsigned short code_size;
-
-__asm__(
-	".type code, @object		\n"
-	"code:				\n"
-	"	mov $0xd80003f8, %edx	\n"
-	"	mov $'\n', %al		\n"
-	"	out %al, (%dx)		\n"
-	"	hlt			\n"
-	".size code, . - code		\n"
-	".type code_size, @object	\n"
-	"code_size:			\n"
-	"	.short . - code		\n"
-	".size code_size, . - code_size	\n"
-	);
+#include "ioctl_kvm_run_common.c"
 
+#if need_print_KVM_RUN
 
 static void
-run_kvm(const int vcpu_fd, struct kvm_run *const run, const size_t mmap_size,
-	void *const mem)
-{
-	/* Initialize CS to point at 0, via a read-modify-write of sregs. */
-	struct kvm_sregs sregs;
-	KVM_IOCTL(vcpu_fd, KVM_GET_SREGS, &sregs);
-	printf("ioctl(%d<%s>, KVM_GET_SREGS, {cs={base=%#jx, limit=%u, selector=%u"
-	       ", type=%u, present=%u, dpl=%u, db=%u, s=%u, l=%u, g=%u, avl=%u}"
-	       ", ...}) = 0\n", vcpu_fd, vcpu_dev, (uintmax_t) sregs.cs.base,
-	       sregs.cs.limit, sregs.cs.selector, sregs.cs.type,
-	       sregs.cs.present, sregs.cs.dpl, sregs.cs.db, sregs.cs.s,
-	       sregs.cs.l, sregs.cs.g, sregs.cs.avl);
-
-	sregs.cs.base = 0;
-	sregs.cs.selector = 0;
-	KVM_IOCTL(vcpu_fd, KVM_SET_SREGS, &sregs);
-	printf("ioctl(%d<%s>, KVM_SET_SREGS, {cs={base=%#jx, limit=%u"
-	       ", selector=%u, type=%u, present=%u, dpl=%u, db=%u, s=%u"
-	       ", l=%u, g=%u, avl=%u}, ...}) = 0\n",
-	       vcpu_fd, vcpu_dev, (uintmax_t) sregs.cs.base,
-	       sregs.cs.limit, sregs.cs.selector, sregs.cs.type,
-	       sregs.cs.present, sregs.cs.dpl, sregs.cs.db, sregs.cs.s,
-	       sregs.cs.l, sregs.cs.g, sregs.cs.avl);
-
-	/*
-	 * Initialize registers: instruction pointer for our code, addends,
-	 * and initial flags required by x86 architecture.
-	 */
-	struct kvm_regs regs = {
-		.rip = page_size,
-		.rax = 2,
-		.rbx = 2,
-		.rflags = 0x2,
-	};
-	KVM_IOCTL(vcpu_fd, KVM_SET_REGS, &regs);
-	printf("ioctl(%d<%s>, KVM_SET_REGS, {rax=%#jx, ..."
-	       ", rsp=%#jx, rbp=%#jx, ..., rip=%#jx, rflags=%#jx}) = 0\n",
-	       vcpu_fd, vcpu_dev, (uintmax_t) regs.rax,
-	       (uintmax_t) regs.rsp, (uintmax_t) regs.rbp,
-	       (uintmax_t) regs.rip, (uintmax_t) regs.rflags);
-
-	/* Copy the code */
-	memcpy(mem, code, code_size);
-
-	const char *p = "\n";
-
-	/* Repeatedly run code and handle VM exits. */
-	for (;;) {
-		KVM_IOCTL(vcpu_fd, KVM_RUN, NULL);
-		printf("ioctl(%d<%s>, KVM_RUN, 0) = 0\n", vcpu_fd, vcpu_dev);
-
-		switch (run->exit_reason) {
-		case KVM_EXIT_HLT:
-			if (p)
-				error_msg_and_fail("premature KVM_EXIT_HLT");
-			return;
-		case KVM_EXIT_IO:
-			if (run->io.direction == KVM_EXIT_IO_OUT
-			    && run->io.size == 1
-			    && run->io.port == 0x03f8
-			    && run->io.count == 1
-			    && run->io.data_offset < mmap_size
-			    && p && *p == ((char *) run)[run->io.data_offset])
-				p = NULL;
-			else
-				error_msg_and_fail("unhandled KVM_EXIT_IO");
-			break;
-		case KVM_EXIT_MMIO:
-			error_msg_and_fail("Got an unexpected MMIO exit:"
-					   " phys_addr %#llx,"
-					   " data %02x %02x %02x %02x"
-						" %02x %02x %02x %02x,"
-					   " len %u, is_write %hhu",
-					   (unsigned long long) run->mmio.phys_addr,
-					   run->mmio.data[0], run->mmio.data[1],
-					   run->mmio.data[2], run->mmio.data[3],
-					   run->mmio.data[4], run->mmio.data[5],
-					   run->mmio.data[6], run->mmio.data[7],
-					   run->mmio.len, run->mmio.is_write);
-
-		default:
-			error_msg_and_fail("exit_reason = %#x",
-					   run->exit_reason);
-		}
-	}
-}
-
-static int
-vcpu_dev_should_have_cpuid(int fd)
+print_KVM_RUN(const int fd, const char *const dev, const unsigned int reason)
 {
-	int r = 0;
-	char *filename = NULL;
-	char buf[sizeof(vcpu_dev)];
-
-	if (asprintf(&filename, "/proc/%d/fd/%d", getpid(), fd) < 0)
-		error_msg_and_fail("asprintf");
-
-	if (readlink(filename, buf, sizeof(buf)) == sizeof(buf) - 1
-	    && (memcmp(buf, vcpu_dev, sizeof(buf) - 1) == 0))
-		r = 1;
-	free(filename);
-	return r;
+	printf("ioctl(%d<%s>, KVM_RUN, 0) = 0\n", fd, dev);
 }
 
-int
-main(void)
-{
-	skip_if_unavailable("/proc/self/fd/");
-
-	int kvm = open(dev, O_RDWR);
-	if (kvm < 0)
-		perror_msg_and_skip("open: %s", dev);
-
-	/* Make sure we have the stable version of the API */
-	int ret = KVM_IOCTL(kvm, KVM_GET_API_VERSION, 0);
-	if (ret != KVM_API_VERSION)
-		error_msg_and_skip("KVM_GET_API_VERSION returned %d"
-				   ", KVM_API_VERSION is %d",
-				   kvm, KVM_API_VERSION);
-	printf("ioctl(%d<%s>, KVM_GET_API_VERSION, 0) = %d\n",
-	       kvm, dev, ret);
-
-	int vm_fd = KVM_IOCTL(kvm, KVM_CREATE_VM, 0);
-	printf("ioctl(%d<%s>, KVM_CREATE_VM, 0) = %d<%s>\n",
-	       kvm, dev, vm_fd, vm_dev);
-
-	/* Allocate one aligned page of guest memory to hold the code. */
-	page_size = get_page_size();
-	void *const mem = mmap(NULL, page_size, PROT_READ | PROT_WRITE,
-				  MAP_SHARED | MAP_ANONYMOUS, -1, 0);
-	if (mem == MAP_FAILED)
-		perror_msg_and_fail("mmap page");
-
-	/* Map it to the second page frame (to avoid the real-mode IDT at 0). */
-	struct kvm_userspace_memory_region region = {
-		.slot = 0,
-		.guest_phys_addr = page_size,
-		.memory_size = page_size,
-		.userspace_addr = (uintptr_t) mem,
-	};
-	KVM_IOCTL(vm_fd, KVM_SET_USER_MEMORY_REGION, &region);
-	printf("ioctl(%d<%s>, KVM_SET_USER_MEMORY_REGION"
-	       ", {slot=0, flags=0, guest_phys_addr=%#lx, memory_size=%lu"
-	       ", userspace_addr=%p}) = 0\n", vm_fd, vm_dev,
-	       (unsigned long) page_size, (unsigned long) page_size, mem);
-
-	int vcpu_fd = KVM_IOCTL(vm_fd, KVM_CREATE_VCPU, NULL);
-	if (!vcpu_dev_should_have_cpuid(vcpu_fd))
-		/*
-		 * This is an older kernel that doesn't place a cpuid
-		 * at the end of the dentry associated with vcpu_fd.
-		 * Trim the cpuid part of vcpu_dev like:
-		 * "anon_inode:kvm-vcpu:0" -> "anon_inode:kvm-vcpu"
-		 */
-		vcpu_dev[strlen (vcpu_dev) - 2] = '\0';
-
-	printf("ioctl(%d<%s>, KVM_CREATE_VCPU, 0) = %d<%s>\n",
-	       vm_fd, vm_dev, vcpu_fd, vcpu_dev);
-
-	/* Map the shared kvm_run structure and following data. */
-	ret = KVM_IOCTL(kvm, KVM_GET_VCPU_MMAP_SIZE, NULL);
-	struct kvm_run *run;
-	if (ret < (int) sizeof(*run))
-		error_msg_and_fail("KVM_GET_VCPU_MMAP_SIZE returned %d < %d",
-				   ret, (int) sizeof(*run));
-	printf("ioctl(%d<%s>, KVM_GET_VCPU_MMAP_SIZE, 0) = %d\n",
-	       kvm, dev, ret);
-
-	const size_t mmap_size = (ret + page_size - 1) & -page_size;
-	run = mmap(NULL, mmap_size, PROT_READ | PROT_WRITE,
-		   MAP_SHARED, vcpu_fd, 0);
-	if (run == MAP_FAILED)
-		perror_msg_and_fail("mmap vcpu");
-
-	run_kvm(vcpu_fd, run, mmap_size, mem);
-
-	puts("+++ exited with 0 +++");
-	return 0;
-}
-
-#else /* !HAVE_LINUX_KVM_H */
-
-SKIP_MAIN_UNDEFINED("HAVE_LINUX_KVM_H && HAVE_STRUCT_KVM_REGS && "
-		    "HAVE_STRUCT_KVM_SREGS && "
-		    "HAVE_STRUCT_KVM_USERSPACE_MEMORY_REGION && "
-		    "(__x86_64__ || __i386__)")
-
 #endif
diff --git a/tests/ioctl_kvm_run_auxstr_vcpu.c b/tests/ioctl_kvm_run_auxstr_vcpu.c
new file mode 100644
index 00000000..16af293f
--- /dev/null
+++ b/tests/ioctl_kvm_run_auxstr_vcpu.c
@@ -0,0 +1,24 @@
+#define KVM_NO_CPUID_CALLBACK	\
+	error_msg_and_skip("newer kernel (>= 4.16) is needed")
+
+#include "ioctl_kvm_run_common.c"
+
+#if need_print_KVM_RUN
+
+static void
+print_KVM_RUN(const int fd, const char *const dev, const unsigned int reason)
+{
+	const char *str;
+
+# define CASE_ENTRY(R) case R: str = #R; break
+	switch (reason) {
+		CASE_ENTRY(KVM_EXIT_HLT);
+		CASE_ENTRY(KVM_EXIT_IO);
+		CASE_ENTRY(KVM_EXIT_MMIO);
+		default: str = "???";
+	}
+
+	printf("ioctl(%d<%s>, KVM_RUN, 0) = 0 (%s)\n", fd, dev, str);
+}
+
+#endif
diff --git a/tests/ioctl_kvm_run_common.c b/tests/ioctl_kvm_run_common.c
new file mode 100644
index 00000000..8925a677
--- /dev/null
+++ b/tests/ioctl_kvm_run_common.c
@@ -0,0 +1,279 @@
+/*
+ * Check decoding of KVM_* commands of ioctl syscall using /dev/kvm API.
+ * Based on kvmtest.c from https://lwn.net/Articles/658512/
+ *
+ * kvmtest.c author: Josh Triplett <josh at joshtriplett.org>
+ * Copyright (c) 2015 Intel Corporation
+ * Copyright (c) 2017-2018 The strace developers.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+#include "tests.h"
+
+#if defined HAVE_LINUX_KVM_H				\
+ && defined HAVE_STRUCT_KVM_REGS			\
+ && defined HAVE_STRUCT_KVM_SREGS			\
+ && defined HAVE_STRUCT_KVM_USERSPACE_MEMORY_REGION	\
+ &&(defined __x86_64__ || defined __i386__)
+
+# include <fcntl.h>
+# include <stdint.h>
+# include <stdio.h>
+# include <stdlib.h>
+# include <string.h>
+# include <sys/ioctl.h>
+# include <sys/mman.h>
+# include <unistd.h>
+# include <linux/kvm.h>
+
+static int
+kvm_ioctl(int fd, unsigned long cmd, const char *cmd_str, void *arg)
+{
+	int rc = ioctl(fd, cmd, arg);
+	if (rc < 0)
+		perror_msg_and_skip("%s", cmd_str);
+	return rc;
+}
+
+#define KVM_IOCTL(fd_, cmd_, arg_)	\
+	kvm_ioctl((fd_), (cmd_), #cmd_, (arg_))
+
+static const char dev[] = "/dev/kvm";
+static const char vm_dev[] = "anon_inode:kvm-vm";
+static char vcpu_dev[] = "anon_inode:kvm-vcpu:0";
+static size_t page_size;
+
+extern const char code[];
+extern const unsigned short code_size;
+
+__asm__(
+	".type code, @object		\n"
+	"code:				\n"
+	"	mov $0xd80003f8, %edx	\n"
+	"	mov $'\n', %al		\n"
+	"	out %al, (%dx)		\n"
+	"	hlt			\n"
+	".size code, . - code		\n"
+	".type code_size, @object	\n"
+	"code_size:			\n"
+	"	.short . - code		\n"
+	".size code_size, . - code_size	\n"
+	);
+
+# define need_print_KVM_RUN 1
+
+static void
+print_KVM_RUN(const int fd, const char *const dev, const unsigned int reason);
+
+static void
+run_kvm(const int vcpu_fd, struct kvm_run *const run, const size_t mmap_size,
+	void *const mem)
+{
+	/* Initialize CS to point at 0, via a read-modify-write of sregs. */
+	struct kvm_sregs sregs;
+	KVM_IOCTL(vcpu_fd, KVM_GET_SREGS, &sregs);
+	printf("ioctl(%d<%s>, KVM_GET_SREGS, {cs={base=%#jx, limit=%u, selector=%u"
+	       ", type=%u, present=%u, dpl=%u, db=%u, s=%u, l=%u, g=%u, avl=%u}"
+	       ", ...}) = 0\n", vcpu_fd, vcpu_dev, (uintmax_t) sregs.cs.base,
+	       sregs.cs.limit, sregs.cs.selector, sregs.cs.type,
+	       sregs.cs.present, sregs.cs.dpl, sregs.cs.db, sregs.cs.s,
+	       sregs.cs.l, sregs.cs.g, sregs.cs.avl);
+
+	sregs.cs.base = 0;
+	sregs.cs.selector = 0;
+	KVM_IOCTL(vcpu_fd, KVM_SET_SREGS, &sregs);
+	printf("ioctl(%d<%s>, KVM_SET_SREGS, {cs={base=%#jx, limit=%u"
+	       ", selector=%u, type=%u, present=%u, dpl=%u, db=%u, s=%u"
+	       ", l=%u, g=%u, avl=%u}, ...}) = 0\n",
+	       vcpu_fd, vcpu_dev, (uintmax_t) sregs.cs.base,
+	       sregs.cs.limit, sregs.cs.selector, sregs.cs.type,
+	       sregs.cs.present, sregs.cs.dpl, sregs.cs.db, sregs.cs.s,
+	       sregs.cs.l, sregs.cs.g, sregs.cs.avl);
+
+	/*
+	 * Initialize registers: instruction pointer for our code, addends,
+	 * and initial flags required by x86 architecture.
+	 */
+	struct kvm_regs regs = {
+		.rip = page_size,
+		.rax = 2,
+		.rbx = 2,
+		.rflags = 0x2,
+	};
+	KVM_IOCTL(vcpu_fd, KVM_SET_REGS, &regs);
+	printf("ioctl(%d<%s>, KVM_SET_REGS, {rax=%#jx, ..."
+	       ", rsp=%#jx, rbp=%#jx, ..., rip=%#jx, rflags=%#jx}) = 0\n",
+	       vcpu_fd, vcpu_dev, (uintmax_t) regs.rax,
+	       (uintmax_t) regs.rsp, (uintmax_t) regs.rbp,
+	       (uintmax_t) regs.rip, (uintmax_t) regs.rflags);
+
+	/* Copy the code */
+	memcpy(mem, code, code_size);
+
+	const char *p = "\n";
+
+	/* Repeatedly run code and handle VM exits. */
+	for (;;) {
+		KVM_IOCTL(vcpu_fd, KVM_RUN, NULL);
+		print_KVM_RUN(vcpu_fd, vcpu_dev, run->exit_reason);
+
+		switch (run->exit_reason) {
+		case KVM_EXIT_HLT:
+			if (p)
+				error_msg_and_fail("premature KVM_EXIT_HLT");
+			return;
+		case KVM_EXIT_IO:
+			if (run->io.direction == KVM_EXIT_IO_OUT
+			    && run->io.size == 1
+			    && run->io.port == 0x03f8
+			    && run->io.count == 1
+			    && run->io.data_offset < mmap_size
+			    && p && *p == ((char *) run)[run->io.data_offset])
+				p = NULL;
+			else
+				error_msg_and_fail("unhandled KVM_EXIT_IO");
+			break;
+		case KVM_EXIT_MMIO:
+			error_msg_and_fail("Got an unexpected MMIO exit:"
+					   " phys_addr %#llx,"
+					   " data %02x %02x %02x %02x"
+						" %02x %02x %02x %02x,"
+					   " len %u, is_write %hhu",
+					   (unsigned long long) run->mmio.phys_addr,
+					   run->mmio.data[0], run->mmio.data[1],
+					   run->mmio.data[2], run->mmio.data[3],
+					   run->mmio.data[4], run->mmio.data[5],
+					   run->mmio.data[6], run->mmio.data[7],
+					   run->mmio.len, run->mmio.is_write);
+
+		default:
+			error_msg_and_fail("exit_reason = %#x",
+					   run->exit_reason);
+		}
+	}
+}
+
+static int
+vcpu_dev_should_have_cpuid(int fd)
+{
+	int r = 0;
+	char *filename = NULL;
+	char buf[sizeof(vcpu_dev)];
+
+	if (asprintf(&filename, "/proc/%d/fd/%d", getpid(), fd) < 0)
+		error_msg_and_fail("asprintf");
+
+	if (readlink(filename, buf, sizeof(buf)) == sizeof(buf) - 1
+	    && (memcmp(buf, vcpu_dev, sizeof(buf) - 1) == 0))
+		r = 1;
+	free(filename);
+	return r;
+}
+
+int
+main(void)
+{
+	skip_if_unavailable("/proc/self/fd/");
+
+	int kvm = open(dev, O_RDWR);
+	if (kvm < 0)
+		perror_msg_and_skip("open: %s", dev);
+
+	/* Make sure we have the stable version of the API */
+	int ret = KVM_IOCTL(kvm, KVM_GET_API_VERSION, 0);
+	if (ret != KVM_API_VERSION)
+		error_msg_and_skip("KVM_GET_API_VERSION returned %d"
+				   ", KVM_API_VERSION is %d",
+				   kvm, KVM_API_VERSION);
+	printf("ioctl(%d<%s>, KVM_GET_API_VERSION, 0) = %d\n",
+	       kvm, dev, ret);
+
+	int vm_fd = KVM_IOCTL(kvm, KVM_CREATE_VM, 0);
+	printf("ioctl(%d<%s>, KVM_CREATE_VM, 0) = %d<%s>\n",
+	       kvm, dev, vm_fd, vm_dev);
+
+	/* Allocate one aligned page of guest memory to hold the code. */
+	page_size = get_page_size();
+	void *const mem = mmap(NULL, page_size, PROT_READ | PROT_WRITE,
+				  MAP_SHARED | MAP_ANONYMOUS, -1, 0);
+	if (mem == MAP_FAILED)
+		perror_msg_and_fail("mmap page");
+
+	/* Map it to the second page frame (to avoid the real-mode IDT at 0). */
+	struct kvm_userspace_memory_region region = {
+		.slot = 0,
+		.guest_phys_addr = page_size,
+		.memory_size = page_size,
+		.userspace_addr = (uintptr_t) mem,
+	};
+	KVM_IOCTL(vm_fd, KVM_SET_USER_MEMORY_REGION, &region);
+	printf("ioctl(%d<%s>, KVM_SET_USER_MEMORY_REGION"
+	       ", {slot=0, flags=0, guest_phys_addr=%#lx, memory_size=%lu"
+	       ", userspace_addr=%p}) = 0\n", vm_fd, vm_dev,
+	       (unsigned long) page_size, (unsigned long) page_size, mem);
+
+	int vcpu_fd = KVM_IOCTL(vm_fd, KVM_CREATE_VCPU, NULL);
+	if (!vcpu_dev_should_have_cpuid(vcpu_fd)) {
+		/*
+		 * This is an older kernel that doesn't place a cpuid
+		 * at the end of the dentry associated with vcpu_fd.
+		 * Trim the cpuid part of vcpu_dev like:
+		 * "anon_inode:kvm-vcpu:0" -> "anon_inode:kvm-vcpu"
+		 */
+		vcpu_dev[strlen (vcpu_dev) - 2] = '\0';
+#ifdef KVM_NO_CPUID_CALLBACK
+		KVM_NO_CPUID_CALLBACK;
+#endif
+	}
+
+	printf("ioctl(%d<%s>, KVM_CREATE_VCPU, 0) = %d<%s>\n",
+	       vm_fd, vm_dev, vcpu_fd, vcpu_dev);
+
+	/* Map the shared kvm_run structure and following data. */
+	ret = KVM_IOCTL(kvm, KVM_GET_VCPU_MMAP_SIZE, NULL);
+	struct kvm_run *run;
+	if (ret < (int) sizeof(*run))
+		error_msg_and_fail("KVM_GET_VCPU_MMAP_SIZE returned %d < %d",
+				   ret, (int) sizeof(*run));
+	printf("ioctl(%d<%s>, KVM_GET_VCPU_MMAP_SIZE, 0) = %d\n",
+	       kvm, dev, ret);
+
+	const size_t mmap_size = (ret + page_size - 1) & -page_size;
+	run = mmap(NULL, mmap_size, PROT_READ | PROT_WRITE,
+		   MAP_SHARED, vcpu_fd, 0);
+	if (run == MAP_FAILED)
+		perror_msg_and_fail("mmap vcpu");
+
+	run_kvm(vcpu_fd, run, mmap_size, mem);
+
+	puts("+++ exited with 0 +++");
+	return 0;
+}
+
+#else /* !HAVE_LINUX_KVM_H */
+
+SKIP_MAIN_UNDEFINED("HAVE_LINUX_KVM_H && HAVE_STRUCT_KVM_REGS && "
+		    "HAVE_STRUCT_KVM_SREGS && "
+		    "HAVE_STRUCT_KVM_USERSPACE_MEMORY_REGION && "
+		    "(__x86_64__ || __i386__)")
+
+# define need_print_KVM_RUN 0
+
+#endif
diff --git a/tests/pure_executables.list b/tests/pure_executables.list
index 39565d0c..16efce7d 100755
--- a/tests/pure_executables.list
+++ b/tests/pure_executables.list
@@ -114,6 +114,7 @@ ioctl_dm
 ioctl_evdev
 ioctl_inotify
 ioctl_kvm_run
+ioctl_kvm_run_auxstr_vcpu
 ioctl_loop
 ioctl_mtd
 ioctl_rtc
-- 
2.17.1



More information about the Strace-devel mailing list