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