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(®s, 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, ®s); 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, ®s); 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