1 // SPDX-License-Identifier: GPL-2.0
2 #include <fcntl.h>
3 #include <stdio.h>
4 #include <stdlib.h>
5 #include <string.h>
6 #include <sys/ioctl.h>
7
8 #include "test_util.h"
9
10 #include "kvm_util.h"
11 #include "processor.h"
12
13 #define VCPU_ID 1
14
guest_ins_port80(uint8_t * buffer,unsigned int count)15 static void guest_ins_port80(uint8_t *buffer, unsigned int count)
16 {
17 unsigned long end;
18
19 if (count == 2)
20 end = (unsigned long)buffer + 1;
21 else
22 end = (unsigned long)buffer + 8192;
23
24 asm volatile("cld; rep; insb" : "+D"(buffer), "+c"(count) : "d"(0x80) : "memory");
25 GUEST_ASSERT_1(count == 0, count);
26 GUEST_ASSERT_2((unsigned long)buffer == end, buffer, end);
27 }
28
guest_code(void)29 static void guest_code(void)
30 {
31 uint8_t buffer[8192];
32 int i;
33
34 /*
35 * Special case tests. main() will adjust RCX 2 => 1 and 3 => 8192 to
36 * test that KVM doesn't explode when userspace modifies the "count" on
37 * a userspace I/O exit. KVM isn't required to play nice with the I/O
38 * itself as KVM doesn't support manipulating the count, it just needs
39 * to not explode or overflow a buffer.
40 */
41 guest_ins_port80(buffer, 2);
42 guest_ins_port80(buffer, 3);
43
44 /* Verify KVM fills the buffer correctly when not stuffing RCX. */
45 memset(buffer, 0, sizeof(buffer));
46 guest_ins_port80(buffer, 8192);
47 for (i = 0; i < 8192; i++)
48 GUEST_ASSERT_2(buffer[i] == 0xaa, i, buffer[i]);
49
50 GUEST_DONE();
51 }
52
main(int argc,char * argv[])53 int main(int argc, char *argv[])
54 {
55 struct kvm_regs regs;
56 struct kvm_run *run;
57 struct kvm_vm *vm;
58 struct ucall uc;
59 int rc;
60
61 /* Tell stdout not to buffer its content */
62 setbuf(stdout, NULL);
63
64 /* Create VM */
65 vm = vm_create_default(VCPU_ID, 0, guest_code);
66 run = vcpu_state(vm, VCPU_ID);
67
68 memset(®s, 0, sizeof(regs));
69
70 while (1) {
71 rc = _vcpu_run(vm, VCPU_ID);
72
73 TEST_ASSERT(rc == 0, "vcpu_run failed: %d\n", rc);
74 TEST_ASSERT(run->exit_reason == KVM_EXIT_IO,
75 "Unexpected exit reason: %u (%s),\n",
76 run->exit_reason,
77 exit_reason_str(run->exit_reason));
78
79 if (get_ucall(vm, VCPU_ID, &uc))
80 break;
81
82 TEST_ASSERT(run->io.port == 0x80,
83 "Expected I/O at port 0x80, got port 0x%x\n", run->io.port);
84
85 /*
86 * Modify the rep string count in RCX: 2 => 1 and 3 => 8192.
87 * Note, this abuses KVM's batching of rep string I/O to avoid
88 * getting stuck in an infinite loop. That behavior isn't in
89 * scope from a testing perspective as it's not ABI in any way,
90 * i.e. it really is abusing internal KVM knowledge.
91 */
92 vcpu_regs_get(vm, VCPU_ID, ®s);
93 if (regs.rcx == 2)
94 regs.rcx = 1;
95 if (regs.rcx == 3)
96 regs.rcx = 8192;
97 memset((void *)run + run->io.data_offset, 0xaa, 4096);
98 vcpu_regs_set(vm, VCPU_ID, ®s);
99 }
100
101 switch (uc.cmd) {
102 case UCALL_DONE:
103 break;
104 case UCALL_ABORT:
105 TEST_FAIL("%s at %s:%ld : argN+1 = 0x%lx, argN+2 = 0x%lx",
106 (const char *)uc.args[0], __FILE__, uc.args[1],
107 uc.args[2], uc.args[3]);
108 default:
109 TEST_FAIL("Unknown ucall %lu", uc.cmd);
110 }
111
112 kvm_vm_free(vm);
113 return 0;
114 }
115