104 lines · c
1// SPDX-License-Identifier: GPL-2.02#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 13static 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 else20 end = (unsigned long)buffer + 8192;21 22 asm volatile("cld; rep; insb" : "+D"(buffer), "+c"(count) : "d"(0x80) : "memory");23 GUEST_ASSERT_EQ(count, 0);24 GUEST_ASSERT_EQ((unsigned long)buffer, end);25}26 27static 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 to34 * test that KVM doesn't explode when userspace modifies the "count" on35 * a userspace I/O exit. KVM isn't required to play nice with the I/O36 * itself as KVM doesn't support manipulating the count, it just needs37 * 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(buffer[i] == 0xaa,47 "Expected '0xaa', got '0x%x' at buffer[%u]",48 buffer[i], i);49 50 GUEST_DONE();51}52 53int main(int argc, char *argv[])54{55 struct kvm_vcpu *vcpu;56 struct kvm_regs regs;57 struct kvm_run *run;58 struct kvm_vm *vm;59 struct ucall uc;60 61 vm = vm_create_with_one_vcpu(&vcpu, guest_code);62 run = vcpu->run;63 64 memset(®s, 0, sizeof(regs));65 66 while (1) {67 vcpu_run(vcpu);68 TEST_ASSERT_KVM_EXIT_REASON(vcpu, KVM_EXIT_IO);69 70 if (get_ucall(vcpu, &uc))71 break;72 73 TEST_ASSERT(run->io.port == 0x80,74 "Expected I/O at port 0x80, got port 0x%x", run->io.port);75 76 /*77 * Modify the rep string count in RCX: 2 => 1 and 3 => 8192.78 * Note, this abuses KVM's batching of rep string I/O to avoid79 * getting stuck in an infinite loop. That behavior isn't in80 * scope from a testing perspective as it's not ABI in any way,81 * i.e. it really is abusing internal KVM knowledge.82 */83 vcpu_regs_get(vcpu, ®s);84 if (regs.rcx == 2)85 regs.rcx = 1;86 if (regs.rcx == 3)87 regs.rcx = 8192;88 memset((void *)run + run->io.data_offset, 0xaa, 4096);89 vcpu_regs_set(vcpu, ®s);90 }91 92 switch (uc.cmd) {93 case UCALL_DONE:94 break;95 case UCALL_ABORT:96 REPORT_GUEST_ASSERT(uc);97 default:98 TEST_FAIL("Unknown ucall %lu", uc.cmd);99 }100 101 kvm_vm_free(vm);102 return 0;103}104