| /* |
| * mmio_warning_test |
| * |
| * Copyright (C) 2019, Google LLC. |
| * |
| * This work is licensed under the terms of the GNU GPL, version 2. |
| * |
| * Test that we don't get a kernel warning when we call KVM_RUN after a |
| * triple fault occurs. To get the triple fault to occur we call KVM_RUN |
| * on a VCPU that hasn't been properly setup. |
| * |
| */ |
| |
| #define _GNU_SOURCE |
| #include <fcntl.h> |
| #include <kvm_util.h> |
| #include <linux/kvm.h> |
| #include <processor.h> |
| #include <pthread.h> |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <string.h> |
| #include <sys/ioctl.h> |
| #include <sys/mman.h> |
| #include <sys/stat.h> |
| #include <sys/types.h> |
| #include <sys/wait.h> |
| #include <test_util.h> |
| #include <unistd.h> |
| |
| #define NTHREAD 4 |
| #define NPROCESS 5 |
| |
| struct thread_context { |
| int kvmcpu; |
| struct kvm_run *run; |
| }; |
| |
| void *thr(void *arg) |
| { |
| struct thread_context *tc = (struct thread_context *)arg; |
| int res; |
| int kvmcpu = tc->kvmcpu; |
| struct kvm_run *run = tc->run; |
| |
| res = ioctl(kvmcpu, KVM_RUN, 0); |
| pr_info("ret1=%d exit_reason=%d suberror=%d\n", |
| res, run->exit_reason, run->internal.suberror); |
| |
| return 0; |
| } |
| |
| void test(void) |
| { |
| int i, kvm, kvmvm, kvmcpu; |
| pthread_t th[NTHREAD]; |
| struct kvm_run *run; |
| struct thread_context tc; |
| |
| kvm = open("/dev/kvm", O_RDWR); |
| TEST_ASSERT(kvm != -1, "failed to open /dev/kvm"); |
| kvmvm = ioctl(kvm, KVM_CREATE_VM, 0); |
| TEST_ASSERT(kvmvm != -1, "KVM_CREATE_VM failed"); |
| kvmcpu = ioctl(kvmvm, KVM_CREATE_VCPU, 0); |
| TEST_ASSERT(kvmcpu != -1, "KVM_CREATE_VCPU failed"); |
| run = (struct kvm_run *)mmap(0, 4096, PROT_READ|PROT_WRITE, MAP_SHARED, |
| kvmcpu, 0); |
| tc.kvmcpu = kvmcpu; |
| tc.run = run; |
| srand(getpid()); |
| for (i = 0; i < NTHREAD; i++) { |
| pthread_create(&th[i], NULL, thr, (void *)(uintptr_t)&tc); |
| usleep(rand() % 10000); |
| } |
| for (i = 0; i < NTHREAD; i++) |
| pthread_join(th[i], NULL); |
| } |
| |
| int get_warnings_count(void) |
| { |
| int warnings; |
| FILE *f; |
| |
| f = popen("dmesg | grep \"WARNING:\" | wc -l", "r"); |
| if (fscanf(f, "%d", &warnings) < 1) |
| warnings = 0; |
| pclose(f); |
| |
| return warnings; |
| } |
| |
| int main(void) |
| { |
| int warnings_before, warnings_after; |
| |
| if (!is_intel_cpu()) { |
| print_skip("Must be run on an Intel CPU"); |
| exit(KSFT_SKIP); |
| } |
| |
| if (vm_is_unrestricted_guest(NULL)) { |
| print_skip("Unrestricted guest must be disabled"); |
| exit(KSFT_SKIP); |
| } |
| |
| warnings_before = get_warnings_count(); |
| |
| for (int i = 0; i < NPROCESS; ++i) { |
| int status; |
| int pid = fork(); |
| |
| if (pid < 0) |
| exit(1); |
| if (pid == 0) { |
| test(); |
| exit(0); |
| } |
| while (waitpid(pid, &status, __WALL) != pid) |
| ; |
| } |
| |
| warnings_after = get_warnings_count(); |
| TEST_ASSERT(warnings_before == warnings_after, |
| "Warnings found in kernel. Run 'dmesg' to inspect them."); |
| |
| return 0; |
| } |