* tests/Makefile.am (check_PROGRAMS): Add ioctl_kvm. (DECODER_TESTS): Add ioctl_kvm.test. (EXTRA_DIST): Add ioctl_kvm.expected. * tests/ioctl_kvm.c: New test target file. Taken from https://lwn.net/Articles/658512/. * tests/ioctl_kvm.expected: New expected file. * tests/ioctl_kvm.test: New test driver.
Changes in v2: * Skip the test case if kvm.h is not available. * Skip the test case if opening /dev/kvm is failed. * Include sys/typtes.h first. All items are suggested by ldv. Highlights in v3: * Build the new test case when __x86_64__ is defined. * Don't use macros/functions in err.h. Use macros/functions in tests.h instead. * Remove redundant inclusion of header files. All above items are suggested by ldv. * Add .test and .expected file to Makefile.am. No Change in v4. --- tests/Makefile.am | 3 + tests/ioctl_kvm.c | 173 +++++++++++++++++++++++++++++++++++++++++++++++ tests/ioctl_kvm.expected | 11 +++ tests/ioctl_kvm.test | 12 ++++ 4 files changed, 199 insertions(+) create mode 100644 tests/ioctl_kvm.c create mode 100644 tests/ioctl_kvm.expected create mode 100755 tests/ioctl_kvm.test diff --git a/tests/Makefile.am b/tests/Makefile.am index f2109fd4..ba4c0e2b 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -102,6 +102,7 @@ check_PROGRAMS = $(PURE_EXECUTABLES) \ int_0x80 \ ioctl_dm-v \ ioctl_evdev-v \ + ioctl_kvm \ ioctl_loop-nv \ ioctl_loop-v \ ioctl_nsfs \ @@ -206,6 +207,7 @@ DECODER_TESTS = \ ioctl.test \ ioctl_dm-v.test \ ioctl_dm.test \ + ioctl_kvm.test \ ioctl_loop-nv.test \ ioctl_nsfs.test \ ioctl_sock_gifconf.test \ @@ -332,6 +334,7 @@ EXTRA_DIST = \ init.sh \ init_delete_module.h \ ioctl-v.sh \ + ioctl_kvm.expected \ ipc.sh \ ipc_msgbuf.expected \ ksysent.sed \ diff --git a/tests/ioctl_kvm.c b/tests/ioctl_kvm.c new file mode 100644 index 00000000..9ac1b640 --- /dev/null +++ b/tests/ioctl_kvm.c @@ -0,0 +1,173 @@ +/* Based on the program explained on the page, https://lwn.net/Articles/658512/ */ + +/* Sample code for /dev/kvm API + * + * Copyright (c) 2015 Intel Corporation + * Author: Josh Triplett <j...@joshtriplett.org> + * + * 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(__x86_64__) \ + && defined(HAVE_STRUCT_KVM_REGS) \ + && defined(HAVE_STRUCT_KVM_SREGS) \ + && defined(HAVE_STRUCT_KVM_USERSPACE_MEMORY_REGION) + +# include <fcntl.h> +# include <stdint.h> +# include <stdio.h> +# include <stdlib.h> +# include <string.h> +# include <sys/ioctl.h> +# include <sys/mman.h> +# include <linux/kvm.h> + +int main(void) +{ + int kvm, vmfd, vcpufd, ret; + const uint8_t code[] = { + 0xba, 0xf8, 0x03, /* mov $0x3f8, %dx */ + 0x00, 0xd8, /* add %bl, %al */ + 0x04, '0', /* add $'0', %al */ + 0xee, /* out %al, (%dx) */ + 0xb0, '\n', /* mov $'\n', %al */ + 0xee, /* out %al, (%dx) */ + 0xf4, /* hlt */ + }; + uint8_t *mem; + struct kvm_sregs sregs; + size_t mmap_size; + struct kvm_run *run; + + kvm = open("/dev/kvm", O_RDWR | O_CLOEXEC); + if (kvm == -1) + perror_msg_and_skip("open: %s", "/dev/kvm"); + + /* Make sure we have the stable version of the API */ + ret = ioctl(kvm, KVM_GET_API_VERSION, NULL); + if (ret == -1) + perror_msg_and_skip("Error at KVM_GET_API_VERSION"); + if (ret != KVM_API_VERSION) + error_msg_and_skip("Unexpected KVM_GET_API_VERSION value"); + + vmfd = ioctl(kvm, KVM_CREATE_VM, (unsigned long)0); + if (vmfd == -1) + perror_msg_and_skip("KVM_CREATE_VM"); + + /* Allocate one aligned page of guest memory to hold the code. */ + mem = mmap(NULL, 0x1000, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0); + if (mem == MAP_FAILED) + perror_msg_and_skip("allocating guest memory"); + memcpy(mem, code, sizeof(code)); + + /* 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 = 0x1000, + .memory_size = 0x1000, + .userspace_addr = (uintptr_t)mem, + }; + ret = ioctl(vmfd, KVM_SET_USER_MEMORY_REGION, ®ion); + if (ret == -1) + perror_msg_and_skip("KVM_SET_USER_MEMORY_REGION"); + + vcpufd = ioctl(vmfd, KVM_CREATE_VCPU, 0UL); + if (vcpufd == -1) + perror_msg_and_skip("KVM_CREATE_VCPU"); + + /* Map the shared kvm_run structure and following data. */ + ret = ioctl(kvm, KVM_GET_VCPU_MMAP_SIZE, NULL); + if (ret == -1) + perror_msg_and_skip("KVM_GET_VCPU_MMAP_SIZE"); + mmap_size = ret; + if (mmap_size < sizeof(*run)) + error_msg_and_skip("KVM_GET_VCPU_MMAP_SIZE unexpectedly small"); + run = mmap(NULL, mmap_size, PROT_READ | PROT_WRITE, MAP_SHARED, vcpufd, 0); + if (run == MAP_FAILED) + perror_msg_and_skip("mmap vcpu"); + + /* Initialize CS to point at 0, via a read-modify-write of sregs. */ + ret = ioctl(vcpufd, KVM_GET_SREGS, &sregs); + if (ret == -1) + perror_msg_and_skip("KVM_GET_SREGS"); + sregs.cs.base = 0; + sregs.cs.selector = 0; + ret = ioctl(vcpufd, KVM_SET_SREGS, &sregs); + if (ret == -1) + perror_msg_and_skip("KVM_SET_SREGS"); + + /* Initialize registers: instruction pointer for our code, addends, and + * initial flags required by x86 architecture. */ + struct kvm_regs regs = { + .rip = 0x1000, + .rax = 2, + .rbx = 2, + .rflags = 0x2, + }; + ret = ioctl(vcpufd, KVM_SET_REGS, ®s); + if (ret == -1) + perror_msg_and_skip("KVM_SET_REGS"); + + /* Repeatedly run code and handle VM exits. */ + while (1) { + ret = ioctl(vcpufd, KVM_RUN, NULL); + if (ret == -1) + perror_msg_and_skip("KVM_RUN"); + switch (run->exit_reason) { + case KVM_EXIT_HLT: + puts("KVM_EXIT_HLT"); + return 0; + case KVM_EXIT_IO: + if (run->io.direction == KVM_EXIT_IO_OUT + && run->io.size == 1 + && run->io.port == 0x3f8 + && run->io.count == 1) + putchar(*(((char *)run) + run->io.data_offset)); + else + error_msg_and_skip("unhandled KVM_EXIT_IO"); + break; + case KVM_EXIT_FAIL_ENTRY: + error_msg_and_skip("KVM_EXIT_FAIL_ENTRY: hardware_entry_failure_reason = 0x%llx", + (unsigned long long)run->fail_entry.hardware_entry_failure_reason); + case KVM_EXIT_INTERNAL_ERROR: + error_msg_and_skip("KVM_EXIT_INTERNAL_ERROR: suberror = 0x%x", + run->internal.suberror); + default: + error_msg_and_skip("exit_reason = 0x%x", run->exit_reason); + } + } +} +#else + +#if !defined(HAVE_LINUX_KVM_H) +SKIP_MAIN_UNDEFINED("HAVE_LINUX_KVM_H") +#elif !defined( __x86_64__) +SKIP_MAIN_UNDEFINED("__x86_64__") +#elif !defined(HAVE_STRUCT_KVM_REGS) +SKIP_MAIN_UNDEFINED("HAVE_STRUCT_KVM_REGS") +#elif !defined(HAVE_STRUCT_KVM_SREGS) +SKIP_MAIN_UNDEFINED("HAVE_STRUCT_KVM_SREGS") +#elif !defined(HAVE_STRUCT_KVM_USERSPACE_MEMORY_REGION) +SKIP_MAIN_UNDEFINED("HAVE_STRUCT_KVM_USERSPACE_MEMORY_REGION") +#endif + +#endif diff --git a/tests/ioctl_kvm.expected b/tests/ioctl_kvm.expected new file mode 100644 index 00000000..2a3df15c --- /dev/null +++ b/tests/ioctl_kvm.expected @@ -0,0 +1,11 @@ +ioctl\([0-9]+</dev/kvm>, KVM_GET_API_VERSION, 0\) *= 12 +ioctl\([0-9]+</dev/kvm>, KVM_CREATE_VM, 0\) *= [0-9]+<anon_inode:kvm-vm> +ioctl\([0-9]+<anon_inode:kvm-vm>, KVM_SET_USER_MEMORY_REGION, {slot=0, flags=0, guest_phys_addr=0x1000, memory_size=4096, userspace_addr=0x[0-9a-f]+\) *= 0 +ioctl\([0-9]+<anon_inode:kvm-vm>, KVM_CREATE_VCPU, 0\) *= [0-9]+<anon_inode:kvm-vcpu> +ioctl\([0-9]+</dev/kvm>, KVM_GET_VCPU_MMAP_SIZE, 0\) *= [0-9]+ +ioctl\([0-9]+<anon_inode:kvm-vcpu>, KVM_GET_SREGS, {cs={base=0x[0-9a-f]+, limit=[0-9]+, selector=[0-9]+, type=[0-9]+, present=[0-9]+, dpl=[0-9]+, db=[0-9]+, s=[0-9]+, l=[0-9]+, g=[0-9]+, avl=[0-9]+}, ...}\) *= 0 +ioctl\([0-9]+<anon_inode:kvm-vcpu>, KVM_SET_SREGS, {cs={base=0, limit=[0-9]+, selector=0, type=[0-9]+, present=[0-9]+, dpl=[0-9]+, db=[0-9]+, s=[0-9]+, l=[0-9]+, g=[0-9]+, avl=[0-9]+}, ...}\) *= 0 +ioctl\([0-9]+<anon_inode:kvm-vcpu>, KVM_SET_REGS, {rax=0x2, ..., rsp=0, rbp=0, ..., rip=0x1000, rflags=0x2}\) *= 0 +ioctl\([0-9]+<anon_inode:kvm-vcpu>, KVM_RUN, 0\) *= 0 +ioctl\([0-9]+<anon_inode:kvm-vcpu>, KVM_RUN, 0\) *= 0 +ioctl\([0-9]+<anon_inode:kvm-vcpu>, KVM_RUN, 0\) *= 0 diff --git a/tests/ioctl_kvm.test b/tests/ioctl_kvm.test new file mode 100755 index 00000000..32a40a99 --- /dev/null +++ b/tests/ioctl_kvm.test @@ -0,0 +1,12 @@ +#!/bin/sh + +# Check decoding of KVM* ioctls. + +. "${srcdir=.}/init.sh" + +check_prog grep +run_prog > /dev/null +run_strace -y -eioctl $args +match_grep + +exit 0 -- 2.13.6 ------------------------------------------------------------------------------ Check out the vibrant tech community on one of the world's most engaging tech sites, Slashdot.org! http://sdm.link/slashdot _______________________________________________ Strace-devel mailing list Strace-devel@lists.sourceforge.net https://lists.sourceforge.net/lists/listinfo/strace-devel