Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 #include <fcntl.h>
0003 #include <stdio.h>
0004 #include <stdlib.h>
0005 #include <string.h>
0006 #include <sys/ioctl.h>
0007 
0008 #include "test_util.h"
0009 
0010 #include "kvm_util.h"
0011 #include "processor.h"
0012 
0013 static void guest_ins_port80(uint8_t *buffer, unsigned int count)
0014 {
0015     unsigned long end;
0016 
0017     if (count == 2)
0018         end = (unsigned long)buffer + 1;
0019     else
0020         end = (unsigned long)buffer + 8192;
0021 
0022     asm volatile("cld; rep; insb" : "+D"(buffer), "+c"(count) : "d"(0x80) : "memory");
0023     GUEST_ASSERT_1(count == 0, count);
0024     GUEST_ASSERT_2((unsigned long)buffer == end, buffer, end);
0025 }
0026 
0027 static void guest_code(void)
0028 {
0029     uint8_t buffer[8192];
0030     int i;
0031 
0032     /*
0033      * Special case tests.  main() will adjust RCX 2 => 1 and 3 => 8192 to
0034      * test that KVM doesn't explode when userspace modifies the "count" on
0035      * a userspace I/O exit.  KVM isn't required to play nice with the I/O
0036      * itself as KVM doesn't support manipulating the count, it just needs
0037      * to not explode or overflow a buffer.
0038      */
0039     guest_ins_port80(buffer, 2);
0040     guest_ins_port80(buffer, 3);
0041 
0042     /* Verify KVM fills the buffer correctly when not stuffing RCX. */
0043     memset(buffer, 0, sizeof(buffer));
0044     guest_ins_port80(buffer, 8192);
0045     for (i = 0; i < 8192; i++)
0046         GUEST_ASSERT_2(buffer[i] == 0xaa, i, buffer[i]);
0047 
0048     GUEST_DONE();
0049 }
0050 
0051 int main(int argc, char *argv[])
0052 {
0053     struct kvm_vcpu *vcpu;
0054     struct kvm_regs regs;
0055     struct kvm_run *run;
0056     struct kvm_vm *vm;
0057     struct ucall uc;
0058 
0059     /* Tell stdout not to buffer its content */
0060     setbuf(stdout, NULL);
0061 
0062     vm = vm_create_with_one_vcpu(&vcpu, guest_code);
0063     run = vcpu->run;
0064 
0065     memset(&regs, 0, sizeof(regs));
0066 
0067     while (1) {
0068         vcpu_run(vcpu);
0069 
0070         TEST_ASSERT(run->exit_reason == KVM_EXIT_IO,
0071                 "Unexpected exit reason: %u (%s),\n",
0072                 run->exit_reason,
0073                 exit_reason_str(run->exit_reason));
0074 
0075         if (get_ucall(vcpu, &uc))
0076             break;
0077 
0078         TEST_ASSERT(run->io.port == 0x80,
0079                 "Expected I/O at port 0x80, got port 0x%x\n", run->io.port);
0080 
0081         /*
0082          * Modify the rep string count in RCX: 2 => 1 and 3 => 8192.
0083          * Note, this abuses KVM's batching of rep string I/O to avoid
0084          * getting stuck in an infinite loop.  That behavior isn't in
0085          * scope from a testing perspective as it's not ABI in any way,
0086          * i.e. it really is abusing internal KVM knowledge.
0087          */
0088         vcpu_regs_get(vcpu, &regs);
0089         if (regs.rcx == 2)
0090             regs.rcx = 1;
0091         if (regs.rcx == 3)
0092             regs.rcx = 8192;
0093         memset((void *)run + run->io.data_offset, 0xaa, 4096);
0094         vcpu_regs_set(vcpu, &regs);
0095     }
0096 
0097     switch (uc.cmd) {
0098     case UCALL_DONE:
0099         break;
0100     case UCALL_ABORT:
0101         REPORT_GUEST_ASSERT_2(uc, "argN+1 = 0x%lx, argN+2 = 0x%lx");
0102     default:
0103         TEST_FAIL("Unknown ucall %lu", uc.cmd);
0104     }
0105 
0106     kvm_vm_free(vm);
0107     return 0;
0108 }