[PATCH v2 4/5] tests/ioctl_kvm_run_auxstr_vcpu.c: add a case for testing vcpu auxstr

Masatake YAMATO yamato at redhat.com
Sun May 20 20:27:09 UTC 2018


* ioctl_kvm_run_common.c: New file sharing between
ioctl_kvm_run.c and ioctl_kvm_run_auxstr_vcpu.c. Its contents
are mostly moved from ioctl_kvm_run.c. The exceptions are
KVM_RUN_CODE (must) and KVM_NO_CPUID_CALLBACK (optional) macros.
* ioctl_kvm_run.c (KVM_RUN_CODE): New definition.
Move the code to ioctl_kvm_run_common.c. This file
uses the the original code by including the new file.
* ioctl_kvm_run_auxstr_vcpu.c: New file. It does the same as
ioctl_kvm_run.c but prints aux string for kvm vcpu exit reason.
* tests/gen_tests.in: Add ioctl_kvm_run_auxstr_vcpu.
* tests/pure_executables.list: Add ioctl_kvm_run_auxstr_vcpu.

A change in v2:
* Skip the test case if cpuid information is not available in
  /proc/$pid/fd. It means the kernel is too old.
Suggested by ldv.

Signed-off-by: Masatake YAMATO <yamato at redhat.com>
---
 tests/gen_tests.in                |   1 +
 tests/ioctl_kvm_run.c             | 272 +----------------------------
 tests/ioctl_kvm_run_auxstr_vcpu.c |  14 ++
 tests/ioctl_kvm_run_common.c      | 273 ++++++++++++++++++++++++++++++
 tests/pure_executables.list       |   1 +
 5 files changed, 292 insertions(+), 269 deletions(-)
 create mode 100644 tests/ioctl_kvm_run_auxstr_vcpu.c
 create mode 100644 tests/ioctl_kvm_run_common.c

diff --git a/tests/gen_tests.in b/tests/gen_tests.in
index faf315cd..6097f6b0 100644
--- a/tests/gen_tests.in
+++ b/tests/gen_tests.in
@@ -134,6 +134,7 @@ ioctl_dm-v	+ioctl.test -v -s9
 ioctl_evdev	+ioctl.test
 ioctl_evdev-v	+ioctl.test -v
 ioctl_kvm_run	+ioctl.test -a36 -y
+ioctl_kvm_run_auxstr_vcpu	+ioctl.test -a36 -y -K
 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..6b671382 100644
--- a/tests/ioctl_kvm_run.c
+++ b/tests/ioctl_kvm_run.c
@@ -1,269 +1,3 @@
-/*
- * 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"
-	);
-
-
-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)
-{
-	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';
-
-	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
+#define KVM_RUN_CODE(FD,DEV,REASON) \
+  printf("ioctl(%d<%s>, KVM_RUN, 0) = 0\n", FD, DEV)
+#include "ioctl_kvm_run_common.c"
diff --git a/tests/ioctl_kvm_run_auxstr_vcpu.c b/tests/ioctl_kvm_run_auxstr_vcpu.c
new file mode 100644
index 00000000..45b2f58e
--- /dev/null
+++ b/tests/ioctl_kvm_run_auxstr_vcpu.c
@@ -0,0 +1,14 @@
+#define CASE_ENTRY(R) case R: reason_str = #R; break
+#define KVM_RUN_CODE(FD,DEV,EXIT_REASON)				\
+	do {								\
+		const char *reason_str = "???";				\
+		switch (EXIT_REASON) {					\
+			CASE_ENTRY(KVM_EXIT_HLT);			\
+			CASE_ENTRY(KVM_EXIT_IO);			\
+			CASE_ENTRY(KVM_EXIT_MMIO);			\
+		}							\
+		printf("ioctl(%d<%s>, KVM_RUN, 0) = 0 (%s)\n", FD, DEV, reason_str); \
+	} while (0)
+#define KVM_NO_CPUID_CALLBACK \
+	error_msg_and_skip("newer kernel (>= 4.16) is needed")
+#include "ioctl_kvm_run_common.c"
diff --git a/tests/ioctl_kvm_run_common.c b/tests/ioctl_kvm_run_common.c
new file mode 100644
index 00000000..3c0e0b86
--- /dev/null
+++ b/tests/ioctl_kvm_run_common.c
@@ -0,0 +1,273 @@
+/*
+ * 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"
+	);
+
+
+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);
+		KVM_RUN_CODE(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__)")
+
+#endif
diff --git a/tests/pure_executables.list b/tests/pure_executables.list
index 588861c1..42fa2c9a 100755
--- a/tests/pure_executables.list
+++ b/tests/pure_executables.list
@@ -108,6 +108,7 @@ ioctl_block
 ioctl_dm
 ioctl_evdev
 ioctl_kvm_run
+ioctl_kvm_run_auxstr_vcpu
 ioctl_loop
 ioctl_mtd
 ioctl_rtc
-- 
2.17.0



More information about the Strace-devel mailing list