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
guest_ins_port80(uint8_t * buffer,unsigned int count)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");
23*417bfd0cSSean Christopherson GUEST_ASSERT_EQ(count, 0);
24*417bfd0cSSean Christopherson GUEST_ASSERT_EQ((unsigned long)buffer, end);
2510e7a099SSean Christopherson }
2610e7a099SSean Christopherson
guest_code(void)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++)
46*417bfd0cSSean Christopherson __GUEST_ASSERT(buffer[i] == 0xaa,
47*417bfd0cSSean Christopherson "Expected '0xaa', got '0x%x' at buffer[%u]",
48*417bfd0cSSean Christopherson buffer[i], i);
4910e7a099SSean Christopherson
5010e7a099SSean Christopherson GUEST_DONE();
5110e7a099SSean Christopherson }
5210e7a099SSean Christopherson
main(int argc,char * argv[])5310e7a099SSean Christopherson int main(int argc, char *argv[])
5410e7a099SSean Christopherson {
55ada1bf4dSSean Christopherson struct kvm_vcpu *vcpu;
5610e7a099SSean Christopherson struct kvm_regs regs;
5710e7a099SSean Christopherson struct kvm_run *run;
5810e7a099SSean Christopherson struct kvm_vm *vm;
5910e7a099SSean Christopherson struct ucall uc;
6010e7a099SSean Christopherson
61ada1bf4dSSean Christopherson vm = vm_create_with_one_vcpu(&vcpu, guest_code);
62ada1bf4dSSean Christopherson run = vcpu->run;
6310e7a099SSean Christopherson
6410e7a099SSean Christopherson memset(®s, 0, sizeof(regs));
6510e7a099SSean Christopherson
6610e7a099SSean Christopherson while (1) {
67768e9a61SSean Christopherson vcpu_run(vcpu);
68c96f57b0SVipin Sharma TEST_ASSERT_KVM_EXIT_REASON(vcpu, KVM_EXIT_IO);
6910e7a099SSean Christopherson
70768e9a61SSean Christopherson if (get_ucall(vcpu, &uc))
7110e7a099SSean Christopherson break;
7210e7a099SSean Christopherson
7310e7a099SSean Christopherson TEST_ASSERT(run->io.port == 0x80,
7410e7a099SSean Christopherson "Expected I/O at port 0x80, got port 0x%x\n", run->io.port);
7510e7a099SSean Christopherson
7610e7a099SSean Christopherson /*
7710e7a099SSean Christopherson * Modify the rep string count in RCX: 2 => 1 and 3 => 8192.
7810e7a099SSean Christopherson * Note, this abuses KVM's batching of rep string I/O to avoid
7910e7a099SSean Christopherson * getting stuck in an infinite loop. That behavior isn't in
8010e7a099SSean Christopherson * scope from a testing perspective as it's not ABI in any way,
8110e7a099SSean Christopherson * i.e. it really is abusing internal KVM knowledge.
8210e7a099SSean Christopherson */
83768e9a61SSean Christopherson vcpu_regs_get(vcpu, ®s);
8410e7a099SSean Christopherson if (regs.rcx == 2)
8510e7a099SSean Christopherson regs.rcx = 1;
8610e7a099SSean Christopherson if (regs.rcx == 3)
8710e7a099SSean Christopherson regs.rcx = 8192;
8810e7a099SSean Christopherson memset((void *)run + run->io.data_offset, 0xaa, 4096);
89768e9a61SSean Christopherson vcpu_regs_set(vcpu, ®s);
9010e7a099SSean Christopherson }
9110e7a099SSean Christopherson
9210e7a099SSean Christopherson switch (uc.cmd) {
9310e7a099SSean Christopherson case UCALL_DONE:
9410e7a099SSean Christopherson break;
9510e7a099SSean Christopherson case UCALL_ABORT:
96*417bfd0cSSean Christopherson REPORT_GUEST_ASSERT(uc);
9710e7a099SSean Christopherson default:
9810e7a099SSean Christopherson TEST_FAIL("Unknown ucall %lu", uc.cmd);
9910e7a099SSean Christopherson }
10010e7a099SSean Christopherson
10110e7a099SSean Christopherson kvm_vm_free(vm);
10210e7a099SSean Christopherson return 0;
10310e7a099SSean Christopherson }
104