1 /*
2  * mmio_warning_test
3  *
4  * Copyright (C) 2019, Google LLC.
5  *
6  * This work is licensed under the terms of the GNU GPL, version 2.
7  *
8  * Test that we don't get a kernel warning when we call KVM_RUN after a
9  * triple fault occurs.  To get the triple fault to occur we call KVM_RUN
10  * on a VCPU that hasn't been properly setup.
11  *
12  */
13 
14 #define _GNU_SOURCE
15 #include <fcntl.h>
16 #include <kvm_util.h>
17 #include <linux/kvm.h>
18 #include <processor.h>
19 #include <pthread.h>
20 #include <stdio.h>
21 #include <stdlib.h>
22 #include <string.h>
23 #include <sys/ioctl.h>
24 #include <sys/mman.h>
25 #include <sys/stat.h>
26 #include <sys/types.h>
27 #include <sys/wait.h>
28 #include <test_util.h>
29 #include <unistd.h>
30 
31 #define NTHREAD 4
32 #define NPROCESS 5
33 
34 struct thread_context {
35 	int kvmcpu;
36 	struct kvm_run *run;
37 };
38 
39 void *thr(void *arg)
40 {
41 	struct thread_context *tc = (struct thread_context *)arg;
42 	int res;
43 	int kvmcpu = tc->kvmcpu;
44 	struct kvm_run *run = tc->run;
45 
46 	res = ioctl(kvmcpu, KVM_RUN, 0);
47 	pr_info("ret1=%d exit_reason=%d suberror=%d\n",
48 		res, run->exit_reason, run->internal.suberror);
49 
50 	return 0;
51 }
52 
53 void test(void)
54 {
55 	int i, kvm, kvmvm, kvmcpu;
56 	pthread_t th[NTHREAD];
57 	struct kvm_run *run;
58 	struct thread_context tc;
59 
60 	kvm = open("/dev/kvm", O_RDWR);
61 	TEST_ASSERT(kvm != -1, "failed to open /dev/kvm");
62 	kvmvm = __kvm_ioctl(kvm, KVM_CREATE_VM, NULL);
63 	TEST_ASSERT(kvmvm > 0, KVM_IOCTL_ERROR(KVM_CREATE_VM, kvmvm));
64 	kvmcpu = ioctl(kvmvm, KVM_CREATE_VCPU, 0);
65 	TEST_ASSERT(kvmcpu != -1, KVM_IOCTL_ERROR(KVM_CREATE_VCPU, kvmcpu));
66 	run = (struct kvm_run *)mmap(0, 4096, PROT_READ|PROT_WRITE, MAP_SHARED,
67 				    kvmcpu, 0);
68 	tc.kvmcpu = kvmcpu;
69 	tc.run = run;
70 	srand(getpid());
71 	for (i = 0; i < NTHREAD; i++) {
72 		pthread_create(&th[i], NULL, thr, (void *)(uintptr_t)&tc);
73 		usleep(rand() % 10000);
74 	}
75 	for (i = 0; i < NTHREAD; i++)
76 		pthread_join(th[i], NULL);
77 }
78 
79 int get_warnings_count(void)
80 {
81 	int warnings;
82 	FILE *f;
83 
84 	f = popen("dmesg | grep \"WARNING:\" | wc -l", "r");
85 	if (fscanf(f, "%d", &warnings) < 1)
86 		warnings = 0;
87 	pclose(f);
88 
89 	return warnings;
90 }
91 
92 int main(void)
93 {
94 	int warnings_before, warnings_after;
95 
96 	TEST_REQUIRE(host_cpu_is_intel);
97 
98 	TEST_REQUIRE(!vm_is_unrestricted_guest(NULL));
99 
100 	warnings_before = get_warnings_count();
101 
102 	for (int i = 0; i < NPROCESS; ++i) {
103 		int status;
104 		int pid = fork();
105 
106 		if (pid < 0)
107 			exit(1);
108 		if (pid == 0) {
109 			test();
110 			exit(0);
111 		}
112 		while (waitpid(pid, &status, __WALL) != pid)
113 			;
114 	}
115 
116 	warnings_after = get_warnings_count();
117 	TEST_ASSERT(warnings_before == warnings_after,
118 		   "Warnings found in kernel.  Run 'dmesg' to inspect them.");
119 
120 	return 0;
121 }
122