Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 /*
0003  * Test for x86 KVM_CAP_SYNC_REGS
0004  *
0005  * Copyright (C) 2018, Google LLC.
0006  *
0007  * Verifies expected behavior of x86 KVM_CAP_SYNC_REGS functionality,
0008  * including requesting an invalid register set, updates to/from values
0009  * in kvm_run.s.regs when kvm_valid_regs and kvm_dirty_regs are toggled.
0010  */
0011 
0012 #define _GNU_SOURCE /* for program_invocation_short_name */
0013 #include <fcntl.h>
0014 #include <stdio.h>
0015 #include <stdlib.h>
0016 #include <string.h>
0017 #include <sys/ioctl.h>
0018 
0019 #include "test_util.h"
0020 #include "kvm_util.h"
0021 #include "processor.h"
0022 
0023 #define UCALL_PIO_PORT ((uint16_t)0x1000)
0024 
0025 struct ucall uc_none = {
0026     .cmd = UCALL_NONE,
0027 };
0028 
0029 /*
0030  * ucall is embedded here to protect against compiler reshuffling registers
0031  * before calling a function. In this test we only need to get KVM_EXIT_IO
0032  * vmexit and preserve RBX, no additional information is needed.
0033  */
0034 void guest_code(void)
0035 {
0036     asm volatile("1: in %[port], %%al\n"
0037              "add $0x1, %%rbx\n"
0038              "jmp 1b"
0039              : : [port] "d" (UCALL_PIO_PORT), "D" (&uc_none)
0040              : "rax", "rbx");
0041 }
0042 
0043 static void compare_regs(struct kvm_regs *left, struct kvm_regs *right)
0044 {
0045 #define REG_COMPARE(reg) \
0046     TEST_ASSERT(left->reg == right->reg, \
0047             "Register " #reg \
0048             " values did not match: 0x%llx, 0x%llx\n", \
0049             left->reg, right->reg)
0050     REG_COMPARE(rax);
0051     REG_COMPARE(rbx);
0052     REG_COMPARE(rcx);
0053     REG_COMPARE(rdx);
0054     REG_COMPARE(rsi);
0055     REG_COMPARE(rdi);
0056     REG_COMPARE(rsp);
0057     REG_COMPARE(rbp);
0058     REG_COMPARE(r8);
0059     REG_COMPARE(r9);
0060     REG_COMPARE(r10);
0061     REG_COMPARE(r11);
0062     REG_COMPARE(r12);
0063     REG_COMPARE(r13);
0064     REG_COMPARE(r14);
0065     REG_COMPARE(r15);
0066     REG_COMPARE(rip);
0067     REG_COMPARE(rflags);
0068 #undef REG_COMPARE
0069 }
0070 
0071 static void compare_sregs(struct kvm_sregs *left, struct kvm_sregs *right)
0072 {
0073 }
0074 
0075 static void compare_vcpu_events(struct kvm_vcpu_events *left,
0076                 struct kvm_vcpu_events *right)
0077 {
0078 }
0079 
0080 #define TEST_SYNC_FIELDS   (KVM_SYNC_X86_REGS|KVM_SYNC_X86_SREGS|KVM_SYNC_X86_EVENTS)
0081 #define INVALID_SYNC_FIELD 0x80000000
0082 
0083 int main(int argc, char *argv[])
0084 {
0085     struct kvm_vcpu *vcpu;
0086     struct kvm_vm *vm;
0087     struct kvm_run *run;
0088     struct kvm_regs regs;
0089     struct kvm_sregs sregs;
0090     struct kvm_vcpu_events events;
0091     int rv, cap;
0092 
0093     /* Tell stdout not to buffer its content */
0094     setbuf(stdout, NULL);
0095 
0096     cap = kvm_check_cap(KVM_CAP_SYNC_REGS);
0097     TEST_REQUIRE((cap & TEST_SYNC_FIELDS) == TEST_SYNC_FIELDS);
0098     TEST_REQUIRE(!(cap & INVALID_SYNC_FIELD));
0099 
0100     vm = vm_create_with_one_vcpu(&vcpu, guest_code);
0101 
0102     run = vcpu->run;
0103 
0104     /* Request reading invalid register set from VCPU. */
0105     run->kvm_valid_regs = INVALID_SYNC_FIELD;
0106     rv = _vcpu_run(vcpu);
0107     TEST_ASSERT(rv < 0 && errno == EINVAL,
0108             "Invalid kvm_valid_regs did not cause expected KVM_RUN error: %d\n",
0109             rv);
0110     run->kvm_valid_regs = 0;
0111 
0112     run->kvm_valid_regs = INVALID_SYNC_FIELD | TEST_SYNC_FIELDS;
0113     rv = _vcpu_run(vcpu);
0114     TEST_ASSERT(rv < 0 && errno == EINVAL,
0115             "Invalid kvm_valid_regs did not cause expected KVM_RUN error: %d\n",
0116             rv);
0117     run->kvm_valid_regs = 0;
0118 
0119     /* Request setting invalid register set into VCPU. */
0120     run->kvm_dirty_regs = INVALID_SYNC_FIELD;
0121     rv = _vcpu_run(vcpu);
0122     TEST_ASSERT(rv < 0 && errno == EINVAL,
0123             "Invalid kvm_dirty_regs did not cause expected KVM_RUN error: %d\n",
0124             rv);
0125     run->kvm_dirty_regs = 0;
0126 
0127     run->kvm_dirty_regs = INVALID_SYNC_FIELD | TEST_SYNC_FIELDS;
0128     rv = _vcpu_run(vcpu);
0129     TEST_ASSERT(rv < 0 && errno == EINVAL,
0130             "Invalid kvm_dirty_regs did not cause expected KVM_RUN error: %d\n",
0131             rv);
0132     run->kvm_dirty_regs = 0;
0133 
0134     /* Request and verify all valid register sets. */
0135     /* TODO: BUILD TIME CHECK: TEST_ASSERT(KVM_SYNC_X86_NUM_FIELDS != 3); */
0136     run->kvm_valid_regs = TEST_SYNC_FIELDS;
0137     rv = _vcpu_run(vcpu);
0138     TEST_ASSERT(run->exit_reason == KVM_EXIT_IO,
0139             "Unexpected exit reason: %u (%s),\n",
0140             run->exit_reason,
0141             exit_reason_str(run->exit_reason));
0142 
0143     vcpu_regs_get(vcpu, &regs);
0144     compare_regs(&regs, &run->s.regs.regs);
0145 
0146     vcpu_sregs_get(vcpu, &sregs);
0147     compare_sregs(&sregs, &run->s.regs.sregs);
0148 
0149     vcpu_events_get(vcpu, &events);
0150     compare_vcpu_events(&events, &run->s.regs.events);
0151 
0152     /* Set and verify various register values. */
0153     run->s.regs.regs.rbx = 0xBAD1DEA;
0154     run->s.regs.sregs.apic_base = 1 << 11;
0155     /* TODO run->s.regs.events.XYZ = ABC; */
0156 
0157     run->kvm_valid_regs = TEST_SYNC_FIELDS;
0158     run->kvm_dirty_regs = KVM_SYNC_X86_REGS | KVM_SYNC_X86_SREGS;
0159     rv = _vcpu_run(vcpu);
0160     TEST_ASSERT(run->exit_reason == KVM_EXIT_IO,
0161             "Unexpected exit reason: %u (%s),\n",
0162             run->exit_reason,
0163             exit_reason_str(run->exit_reason));
0164     TEST_ASSERT(run->s.regs.regs.rbx == 0xBAD1DEA + 1,
0165             "rbx sync regs value incorrect 0x%llx.",
0166             run->s.regs.regs.rbx);
0167     TEST_ASSERT(run->s.regs.sregs.apic_base == 1 << 11,
0168             "apic_base sync regs value incorrect 0x%llx.",
0169             run->s.regs.sregs.apic_base);
0170 
0171     vcpu_regs_get(vcpu, &regs);
0172     compare_regs(&regs, &run->s.regs.regs);
0173 
0174     vcpu_sregs_get(vcpu, &sregs);
0175     compare_sregs(&sregs, &run->s.regs.sregs);
0176 
0177     vcpu_events_get(vcpu, &events);
0178     compare_vcpu_events(&events, &run->s.regs.events);
0179 
0180     /* Clear kvm_dirty_regs bits, verify new s.regs values are
0181      * overwritten with existing guest values.
0182      */
0183     run->kvm_valid_regs = TEST_SYNC_FIELDS;
0184     run->kvm_dirty_regs = 0;
0185     run->s.regs.regs.rbx = 0xDEADBEEF;
0186     rv = _vcpu_run(vcpu);
0187     TEST_ASSERT(run->exit_reason == KVM_EXIT_IO,
0188             "Unexpected exit reason: %u (%s),\n",
0189             run->exit_reason,
0190             exit_reason_str(run->exit_reason));
0191     TEST_ASSERT(run->s.regs.regs.rbx != 0xDEADBEEF,
0192             "rbx sync regs value incorrect 0x%llx.",
0193             run->s.regs.regs.rbx);
0194 
0195     /* Clear kvm_valid_regs bits and kvm_dirty_bits.
0196      * Verify s.regs values are not overwritten with existing guest values
0197      * and that guest values are not overwritten with kvm_sync_regs values.
0198      */
0199     run->kvm_valid_regs = 0;
0200     run->kvm_dirty_regs = 0;
0201     run->s.regs.regs.rbx = 0xAAAA;
0202     regs.rbx = 0xBAC0;
0203     vcpu_regs_set(vcpu, &regs);
0204     rv = _vcpu_run(vcpu);
0205     TEST_ASSERT(run->exit_reason == KVM_EXIT_IO,
0206             "Unexpected exit reason: %u (%s),\n",
0207             run->exit_reason,
0208             exit_reason_str(run->exit_reason));
0209     TEST_ASSERT(run->s.regs.regs.rbx == 0xAAAA,
0210             "rbx sync regs value incorrect 0x%llx.",
0211             run->s.regs.regs.rbx);
0212     vcpu_regs_get(vcpu, &regs);
0213     TEST_ASSERT(regs.rbx == 0xBAC0 + 1,
0214             "rbx guest value incorrect 0x%llx.",
0215             regs.rbx);
0216 
0217     /* Clear kvm_valid_regs bits. Verify s.regs values are not overwritten
0218      * with existing guest values but that guest values are overwritten
0219      * with kvm_sync_regs values.
0220      */
0221     run->kvm_valid_regs = 0;
0222     run->kvm_dirty_regs = TEST_SYNC_FIELDS;
0223     run->s.regs.regs.rbx = 0xBBBB;
0224     rv = _vcpu_run(vcpu);
0225     TEST_ASSERT(run->exit_reason == KVM_EXIT_IO,
0226             "Unexpected exit reason: %u (%s),\n",
0227             run->exit_reason,
0228             exit_reason_str(run->exit_reason));
0229     TEST_ASSERT(run->s.regs.regs.rbx == 0xBBBB,
0230             "rbx sync regs value incorrect 0x%llx.",
0231             run->s.regs.regs.rbx);
0232     vcpu_regs_get(vcpu, &regs);
0233     TEST_ASSERT(regs.rbx == 0xBBBB + 1,
0234             "rbx guest value incorrect 0x%llx.",
0235             regs.rbx);
0236 
0237     kvm_vm_free(vm);
0238 
0239     return 0;
0240 }