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
guest_ins_port80(uint8_t * buffer,unsigned int count)13 static void guest_ins_port80(uint8_t *buffer, unsigned int count)
14 {
15 unsigned long end;
16
17 if (count == 2)
18 end = (unsigned long)buffer + 1;
19 else
20 end = (unsigned long)buffer + 8192;
21
22 asm volatile("cld; rep; insb" : "+D"(buffer), "+c"(count) : "d"(0x80) : "memory");
23 GUEST_ASSERT_EQ(count, 0);
24 GUEST_ASSERT_EQ((unsigned long)buffer, end);
25 }
26
guest_code(void)27 static void guest_code(void)
28 {
29 uint8_t buffer[8192];
30 int i;
31
32 /*
33 * Special case tests. main() will adjust RCX 2 => 1 and 3 => 8192 to
34 * test that KVM doesn't explode when userspace modifies the "count" on
35 * a userspace I/O exit. KVM isn't required to play nice with the I/O
36 * itself as KVM doesn't support manipulating the count, it just needs
37 * to not explode or overflow a buffer.
38 */
39 guest_ins_port80(buffer, 2);
40 guest_ins_port80(buffer, 3);
41
42 /* Verify KVM fills the buffer correctly when not stuffing RCX. */
43 memset(buffer, 0, sizeof(buffer));
44 guest_ins_port80(buffer, 8192);
45 for (i = 0; i < 8192; i++)
46 __GUEST_ASSERT(buffer[i] == 0xaa,
47 "Expected '0xaa', got '0x%x' at buffer[%u]",
48 buffer[i], i);
49
50 GUEST_DONE();
51 }
52
main(int argc,char * argv[])53 int main(int argc, char *argv[])
54 {
55 struct kvm_vcpu *vcpu;
56 struct kvm_regs regs;
57 struct kvm_run *run;
58 struct kvm_vm *vm;
59 struct ucall uc;
60
61 vm = vm_create_with_one_vcpu(&vcpu, guest_code);
62 run = vcpu->run;
63
64 memset(®s, 0, sizeof(regs));
65
66 while (1) {
67 vcpu_run(vcpu);
68 TEST_ASSERT_KVM_EXIT_REASON(vcpu, KVM_EXIT_IO);
69
70 if (get_ucall(vcpu, &uc))
71 break;
72
73 TEST_ASSERT(run->io.port == 0x80,
74 "Expected I/O at port 0x80, got port 0x%x", run->io.port);
75
76 /*
77 * Modify the rep string count in RCX: 2 => 1 and 3 => 8192.
78 * Note, this abuses KVM's batching of rep string I/O to avoid
79 * getting stuck in an infinite loop. That behavior isn't in
80 * scope from a testing perspective as it's not ABI in any way,
81 * i.e. it really is abusing internal KVM knowledge.
82 */
83 vcpu_regs_get(vcpu, ®s);
84 if (regs.rcx == 2)
85 regs.rcx = 1;
86 if (regs.rcx == 3)
87 regs.rcx = 8192;
88 memset((void *)run + run->io.data_offset, 0xaa, 4096);
89 vcpu_regs_set(vcpu, ®s);
90 }
91
92 switch (uc.cmd) {
93 case UCALL_DONE:
94 break;
95 case UCALL_ABORT:
96 REPORT_GUEST_ASSERT(uc);
97 default:
98 TEST_FAIL("Unknown ucall %lu", uc.cmd);
99 }
100
101 kvm_vm_free(vm);
102 return 0;
103 }
104