110e7a099SSean Christopherson // SPDX-License-Identifier: GPL-2.0
210e7a099SSean Christopherson #include <fcntl.h>
310e7a099SSean Christopherson #include <stdio.h>
410e7a099SSean Christopherson #include <stdlib.h>
510e7a099SSean Christopherson #include <string.h>
610e7a099SSean Christopherson #include <sys/ioctl.h>
710e7a099SSean Christopherson 
810e7a099SSean Christopherson #include "test_util.h"
910e7a099SSean Christopherson 
1010e7a099SSean Christopherson #include "kvm_util.h"
1110e7a099SSean Christopherson #include "processor.h"
1210e7a099SSean Christopherson 
1310e7a099SSean Christopherson static void guest_ins_port80(uint8_t *buffer, unsigned int count)
1410e7a099SSean Christopherson {
1510e7a099SSean Christopherson 	unsigned long end;
1610e7a099SSean Christopherson 
1710e7a099SSean Christopherson 	if (count == 2)
1810e7a099SSean Christopherson 		end = (unsigned long)buffer + 1;
1910e7a099SSean Christopherson 	else
2010e7a099SSean Christopherson 		end = (unsigned long)buffer + 8192;
2110e7a099SSean Christopherson 
2210e7a099SSean Christopherson 	asm volatile("cld; rep; insb" : "+D"(buffer), "+c"(count) : "d"(0x80) : "memory");
2310e7a099SSean Christopherson 	GUEST_ASSERT_1(count == 0, count);
2410e7a099SSean Christopherson 	GUEST_ASSERT_2((unsigned long)buffer == end, buffer, end);
2510e7a099SSean Christopherson }
2610e7a099SSean Christopherson 
2710e7a099SSean Christopherson static void guest_code(void)
2810e7a099SSean Christopherson {
2910e7a099SSean Christopherson 	uint8_t buffer[8192];
3010e7a099SSean Christopherson 	int i;
3110e7a099SSean Christopherson 
3210e7a099SSean Christopherson 	/*
3310e7a099SSean Christopherson 	 * Special case tests.  main() will adjust RCX 2 => 1 and 3 => 8192 to
3410e7a099SSean Christopherson 	 * test that KVM doesn't explode when userspace modifies the "count" on
3510e7a099SSean Christopherson 	 * a userspace I/O exit.  KVM isn't required to play nice with the I/O
3610e7a099SSean Christopherson 	 * itself as KVM doesn't support manipulating the count, it just needs
3710e7a099SSean Christopherson 	 * to not explode or overflow a buffer.
3810e7a099SSean Christopherson 	 */
3910e7a099SSean Christopherson 	guest_ins_port80(buffer, 2);
4010e7a099SSean Christopherson 	guest_ins_port80(buffer, 3);
4110e7a099SSean Christopherson 
4210e7a099SSean Christopherson 	/* Verify KVM fills the buffer correctly when not stuffing RCX. */
4310e7a099SSean Christopherson 	memset(buffer, 0, sizeof(buffer));
4410e7a099SSean Christopherson 	guest_ins_port80(buffer, 8192);
4510e7a099SSean Christopherson 	for (i = 0; i < 8192; i++)
4610e7a099SSean Christopherson 		GUEST_ASSERT_2(buffer[i] == 0xaa, i, buffer[i]);
4710e7a099SSean Christopherson 
4810e7a099SSean Christopherson 	GUEST_DONE();
4910e7a099SSean Christopherson }
5010e7a099SSean Christopherson 
5110e7a099SSean Christopherson int main(int argc, char *argv[])
5210e7a099SSean Christopherson {
53*ada1bf4dSSean Christopherson 	struct kvm_vcpu *vcpu;
5410e7a099SSean Christopherson 	struct kvm_regs regs;
5510e7a099SSean Christopherson 	struct kvm_run *run;
5610e7a099SSean Christopherson 	struct kvm_vm *vm;
5710e7a099SSean Christopherson 	struct ucall uc;
5810e7a099SSean Christopherson 
5910e7a099SSean Christopherson 	/* Tell stdout not to buffer its content */
6010e7a099SSean Christopherson 	setbuf(stdout, NULL);
6110e7a099SSean Christopherson 
62*ada1bf4dSSean Christopherson 	vm = vm_create_with_one_vcpu(&vcpu, guest_code);
63*ada1bf4dSSean Christopherson 	run = vcpu->run;
6410e7a099SSean Christopherson 
6510e7a099SSean Christopherson 	memset(&regs, 0, sizeof(regs));
6610e7a099SSean Christopherson 
6710e7a099SSean Christopherson 	while (1) {
68*ada1bf4dSSean Christopherson 		vcpu_run(vm, vcpu->id);
6910e7a099SSean Christopherson 
7010e7a099SSean Christopherson 		TEST_ASSERT(run->exit_reason == KVM_EXIT_IO,
7110e7a099SSean Christopherson 			    "Unexpected exit reason: %u (%s),\n",
7210e7a099SSean Christopherson 			    run->exit_reason,
7310e7a099SSean Christopherson 			    exit_reason_str(run->exit_reason));
7410e7a099SSean Christopherson 
75*ada1bf4dSSean Christopherson 		if (get_ucall(vm, vcpu->id, &uc))
7610e7a099SSean Christopherson 			break;
7710e7a099SSean Christopherson 
7810e7a099SSean Christopherson 		TEST_ASSERT(run->io.port == 0x80,
7910e7a099SSean Christopherson 			    "Expected I/O at port 0x80, got port 0x%x\n", run->io.port);
8010e7a099SSean Christopherson 
8110e7a099SSean Christopherson 		/*
8210e7a099SSean Christopherson 		 * Modify the rep string count in RCX: 2 => 1 and 3 => 8192.
8310e7a099SSean Christopherson 		 * Note, this abuses KVM's batching of rep string I/O to avoid
8410e7a099SSean Christopherson 		 * getting stuck in an infinite loop.  That behavior isn't in
8510e7a099SSean Christopherson 		 * scope from a testing perspective as it's not ABI in any way,
8610e7a099SSean Christopherson 		 * i.e. it really is abusing internal KVM knowledge.
8710e7a099SSean Christopherson 		 */
88*ada1bf4dSSean Christopherson 		vcpu_regs_get(vm, vcpu->id, &regs);
8910e7a099SSean Christopherson 		if (regs.rcx == 2)
9010e7a099SSean Christopherson 			regs.rcx = 1;
9110e7a099SSean Christopherson 		if (regs.rcx == 3)
9210e7a099SSean Christopherson 			regs.rcx = 8192;
9310e7a099SSean Christopherson 		memset((void *)run + run->io.data_offset, 0xaa, 4096);
94*ada1bf4dSSean Christopherson 		vcpu_regs_set(vm, vcpu->id, &regs);
9510e7a099SSean Christopherson 	}
9610e7a099SSean Christopherson 
9710e7a099SSean Christopherson 	switch (uc.cmd) {
9810e7a099SSean Christopherson 	case UCALL_DONE:
9910e7a099SSean Christopherson 		break;
10010e7a099SSean Christopherson 	case UCALL_ABORT:
10110e7a099SSean Christopherson 		TEST_FAIL("%s at %s:%ld : argN+1 = 0x%lx, argN+2 = 0x%lx",
10210e7a099SSean Christopherson 			  (const char *)uc.args[0], __FILE__, uc.args[1],
10310e7a099SSean Christopherson 			  uc.args[2], uc.args[3]);
10410e7a099SSean Christopherson 	default:
10510e7a099SSean Christopherson 		TEST_FAIL("Unknown ucall %lu", uc.cmd);
10610e7a099SSean Christopherson 	}
10710e7a099SSean Christopherson 
10810e7a099SSean Christopherson 	kvm_vm_free(vm);
10910e7a099SSean Christopherson 	return 0;
11010e7a099SSean Christopherson }
111