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 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_1(count == 0, count);
24 	GUEST_ASSERT_2((unsigned long)buffer == end, buffer, end);
25 }
26 
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_2(buffer[i] == 0xaa, i, buffer[i]);
47 
48 	GUEST_DONE();
49 }
50 
51 int main(int argc, char *argv[])
52 {
53 	struct kvm_vcpu *vcpu;
54 	struct kvm_regs regs;
55 	struct kvm_run *run;
56 	struct kvm_vm *vm;
57 	struct ucall uc;
58 
59 	vm = vm_create_with_one_vcpu(&vcpu, guest_code);
60 	run = vcpu->run;
61 
62 	memset(&regs, 0, sizeof(regs));
63 
64 	while (1) {
65 		vcpu_run(vcpu);
66 		TEST_ASSERT_KVM_EXIT_REASON(vcpu, KVM_EXIT_IO);
67 
68 		if (get_ucall(vcpu, &uc))
69 			break;
70 
71 		TEST_ASSERT(run->io.port == 0x80,
72 			    "Expected I/O at port 0x80, got port 0x%x\n", run->io.port);
73 
74 		/*
75 		 * Modify the rep string count in RCX: 2 => 1 and 3 => 8192.
76 		 * Note, this abuses KVM's batching of rep string I/O to avoid
77 		 * getting stuck in an infinite loop.  That behavior isn't in
78 		 * scope from a testing perspective as it's not ABI in any way,
79 		 * i.e. it really is abusing internal KVM knowledge.
80 		 */
81 		vcpu_regs_get(vcpu, &regs);
82 		if (regs.rcx == 2)
83 			regs.rcx = 1;
84 		if (regs.rcx == 3)
85 			regs.rcx = 8192;
86 		memset((void *)run + run->io.data_offset, 0xaa, 4096);
87 		vcpu_regs_set(vcpu, &regs);
88 	}
89 
90 	switch (uc.cmd) {
91 	case UCALL_DONE:
92 		break;
93 	case UCALL_ABORT:
94 		REPORT_GUEST_ASSERT_2(uc, "argN+1 = 0x%lx, argN+2 = 0x%lx");
95 	default:
96 		TEST_FAIL("Unknown ucall %lu", uc.cmd);
97 	}
98 
99 	kvm_vm_free(vm);
100 	return 0;
101 }
102