Paolo Bonzini | 6089ae0 | 2018-03-28 09:45:34 +0200 | [diff] [blame] | 1 | /* |
| 2 | * Test for x86 KVM_CAP_SYNC_REGS |
| 3 | * |
| 4 | * Copyright (C) 2018, Google LLC. |
| 5 | * |
| 6 | * This work is licensed under the terms of the GNU GPL, version 2. |
| 7 | * |
| 8 | * Verifies expected behavior of x86 KVM_CAP_SYNC_REGS functionality, |
| 9 | * including requesting an invalid register set, updates to/from values |
| 10 | * in kvm_run.s.regs when kvm_valid_regs and kvm_dirty_regs are toggled. |
| 11 | */ |
| 12 | |
| 13 | #define _GNU_SOURCE /* for program_invocation_short_name */ |
| 14 | #include <fcntl.h> |
| 15 | #include <stdio.h> |
| 16 | #include <stdlib.h> |
| 17 | #include <string.h> |
| 18 | #include <sys/ioctl.h> |
| 19 | |
| 20 | #include "test_util.h" |
| 21 | #include "kvm_util.h" |
| 22 | #include "x86.h" |
| 23 | |
| 24 | #define VCPU_ID 5 |
| 25 | #define PORT_HOST_SYNC 0x1000 |
| 26 | |
| 27 | static void __exit_to_l0(uint16_t port, uint64_t arg0, uint64_t arg1) |
| 28 | { |
| 29 | __asm__ __volatile__("in %[port], %%al" |
| 30 | : |
| 31 | : [port]"d"(port), "D"(arg0), "S"(arg1) |
| 32 | : "rax"); |
| 33 | } |
| 34 | |
| 35 | #define exit_to_l0(_port, _arg0, _arg1) \ |
| 36 | __exit_to_l0(_port, (uint64_t) (_arg0), (uint64_t) (_arg1)) |
| 37 | |
| 38 | #define GUEST_ASSERT(_condition) do { \ |
| 39 | if (!(_condition)) \ |
| 40 | exit_to_l0(PORT_ABORT, "Failed guest assert: " #_condition, 0);\ |
| 41 | } while (0) |
| 42 | |
| 43 | void guest_code(void) |
| 44 | { |
| 45 | for (;;) { |
| 46 | exit_to_l0(PORT_HOST_SYNC, "hello", 0); |
| 47 | asm volatile ("inc %r11"); |
| 48 | } |
| 49 | } |
| 50 | |
| 51 | static void compare_regs(struct kvm_regs *left, struct kvm_regs *right) |
| 52 | { |
| 53 | #define REG_COMPARE(reg) \ |
| 54 | TEST_ASSERT(left->reg == right->reg, \ |
| 55 | "Register " #reg \ |
| 56 | " values did not match: 0x%llx, 0x%llx\n", \ |
| 57 | left->reg, right->reg) |
| 58 | REG_COMPARE(rax); |
| 59 | REG_COMPARE(rbx); |
| 60 | REG_COMPARE(rcx); |
| 61 | REG_COMPARE(rdx); |
| 62 | REG_COMPARE(rsi); |
| 63 | REG_COMPARE(rdi); |
| 64 | REG_COMPARE(rsp); |
| 65 | REG_COMPARE(rbp); |
| 66 | REG_COMPARE(r8); |
| 67 | REG_COMPARE(r9); |
| 68 | REG_COMPARE(r10); |
| 69 | REG_COMPARE(r11); |
| 70 | REG_COMPARE(r12); |
| 71 | REG_COMPARE(r13); |
| 72 | REG_COMPARE(r14); |
| 73 | REG_COMPARE(r15); |
| 74 | REG_COMPARE(rip); |
| 75 | REG_COMPARE(rflags); |
| 76 | #undef REG_COMPARE |
| 77 | } |
| 78 | |
| 79 | static void compare_sregs(struct kvm_sregs *left, struct kvm_sregs *right) |
| 80 | { |
| 81 | } |
| 82 | |
| 83 | static void compare_vcpu_events(struct kvm_vcpu_events *left, |
| 84 | struct kvm_vcpu_events *right) |
| 85 | { |
| 86 | } |
| 87 | |
| 88 | int main(int argc, char *argv[]) |
| 89 | { |
| 90 | struct kvm_vm *vm; |
| 91 | struct kvm_run *run; |
| 92 | struct kvm_regs regs; |
| 93 | struct kvm_sregs sregs; |
| 94 | struct kvm_vcpu_events events; |
| 95 | int rv, cap; |
| 96 | |
| 97 | /* Tell stdout not to buffer its content */ |
| 98 | setbuf(stdout, NULL); |
| 99 | |
| 100 | cap = kvm_check_cap(KVM_CAP_SYNC_REGS); |
| 101 | TEST_ASSERT((unsigned long)cap == KVM_SYNC_X86_VALID_FIELDS, |
| 102 | "KVM_CAP_SYNC_REGS (0x%x) != KVM_SYNC_X86_VALID_FIELDS (0x%lx)\n", |
| 103 | cap, KVM_SYNC_X86_VALID_FIELDS); |
| 104 | |
| 105 | /* Create VM */ |
| 106 | vm = vm_create_default(VCPU_ID, guest_code); |
| 107 | |
| 108 | run = vcpu_state(vm, VCPU_ID); |
| 109 | |
| 110 | /* Request reading invalid register set from VCPU. */ |
| 111 | run->kvm_valid_regs = KVM_SYNC_X86_VALID_FIELDS << 1; |
| 112 | rv = _vcpu_run(vm, VCPU_ID); |
| 113 | TEST_ASSERT(rv < 0 && errno == EINVAL, |
| 114 | "Invalid kvm_valid_regs did not cause expected KVM_RUN error: %d\n", |
| 115 | rv); |
| 116 | vcpu_state(vm, VCPU_ID)->kvm_valid_regs = 0; |
| 117 | |
| 118 | /* Request setting invalid register set into VCPU. */ |
| 119 | run->kvm_dirty_regs = KVM_SYNC_X86_VALID_FIELDS << 1; |
| 120 | rv = _vcpu_run(vm, VCPU_ID); |
| 121 | TEST_ASSERT(rv < 0 && errno == EINVAL, |
| 122 | "Invalid kvm_dirty_regs did not cause expected KVM_RUN error: %d\n", |
| 123 | rv); |
| 124 | vcpu_state(vm, VCPU_ID)->kvm_dirty_regs = 0; |
| 125 | |
| 126 | /* Request and verify all valid register sets. */ |
| 127 | /* TODO: BUILD TIME CHECK: TEST_ASSERT(KVM_SYNC_X86_NUM_FIELDS != 3); */ |
| 128 | run->kvm_valid_regs = KVM_SYNC_X86_VALID_FIELDS; |
| 129 | rv = _vcpu_run(vm, VCPU_ID); |
| 130 | TEST_ASSERT(run->exit_reason == KVM_EXIT_IO, |
| 131 | "Unexpected exit reason: %u (%s),\n", |
| 132 | run->exit_reason, |
| 133 | exit_reason_str(run->exit_reason)); |
| 134 | |
| 135 | vcpu_regs_get(vm, VCPU_ID, ®s); |
| 136 | compare_regs(®s, &run->s.regs.regs); |
| 137 | |
| 138 | vcpu_sregs_get(vm, VCPU_ID, &sregs); |
| 139 | compare_sregs(&sregs, &run->s.regs.sregs); |
| 140 | |
| 141 | vcpu_events_get(vm, VCPU_ID, &events); |
| 142 | compare_vcpu_events(&events, &run->s.regs.events); |
| 143 | |
| 144 | /* Set and verify various register values. */ |
| 145 | run->s.regs.regs.r11 = 0xBAD1DEA; |
| 146 | run->s.regs.sregs.apic_base = 1 << 11; |
| 147 | /* TODO run->s.regs.events.XYZ = ABC; */ |
| 148 | |
| 149 | run->kvm_valid_regs = KVM_SYNC_X86_VALID_FIELDS; |
| 150 | run->kvm_dirty_regs = KVM_SYNC_X86_REGS | KVM_SYNC_X86_SREGS; |
| 151 | rv = _vcpu_run(vm, VCPU_ID); |
| 152 | TEST_ASSERT(run->exit_reason == KVM_EXIT_IO, |
| 153 | "Unexpected exit reason: %u (%s),\n", |
| 154 | run->exit_reason, |
| 155 | exit_reason_str(run->exit_reason)); |
| 156 | TEST_ASSERT(run->s.regs.regs.r11 == 0xBAD1DEA + 1, |
| 157 | "r11 sync regs value incorrect 0x%llx.", |
| 158 | run->s.regs.regs.r11); |
| 159 | TEST_ASSERT(run->s.regs.sregs.apic_base == 1 << 11, |
| 160 | "apic_base sync regs value incorrect 0x%llx.", |
| 161 | run->s.regs.sregs.apic_base); |
| 162 | |
| 163 | vcpu_regs_get(vm, VCPU_ID, ®s); |
| 164 | compare_regs(®s, &run->s.regs.regs); |
| 165 | |
| 166 | vcpu_sregs_get(vm, VCPU_ID, &sregs); |
| 167 | compare_sregs(&sregs, &run->s.regs.sregs); |
| 168 | |
| 169 | vcpu_events_get(vm, VCPU_ID, &events); |
| 170 | compare_vcpu_events(&events, &run->s.regs.events); |
| 171 | |
| 172 | /* Clear kvm_dirty_regs bits, verify new s.regs values are |
| 173 | * overwritten with existing guest values. |
| 174 | */ |
| 175 | run->kvm_valid_regs = KVM_SYNC_X86_VALID_FIELDS; |
| 176 | run->kvm_dirty_regs = 0; |
| 177 | run->s.regs.regs.r11 = 0xDEADBEEF; |
| 178 | rv = _vcpu_run(vm, VCPU_ID); |
| 179 | TEST_ASSERT(run->exit_reason == KVM_EXIT_IO, |
| 180 | "Unexpected exit reason: %u (%s),\n", |
| 181 | run->exit_reason, |
| 182 | exit_reason_str(run->exit_reason)); |
| 183 | TEST_ASSERT(run->s.regs.regs.r11 != 0xDEADBEEF, |
| 184 | "r11 sync regs value incorrect 0x%llx.", |
| 185 | run->s.regs.regs.r11); |
| 186 | |
| 187 | /* Clear kvm_valid_regs bits and kvm_dirty_bits. |
| 188 | * Verify s.regs values are not overwritten with existing guest values |
| 189 | * and that guest values are not overwritten with kvm_sync_regs values. |
| 190 | */ |
| 191 | run->kvm_valid_regs = 0; |
| 192 | run->kvm_dirty_regs = 0; |
| 193 | run->s.regs.regs.r11 = 0xAAAA; |
| 194 | regs.r11 = 0xBAC0; |
| 195 | vcpu_regs_set(vm, VCPU_ID, ®s); |
| 196 | rv = _vcpu_run(vm, VCPU_ID); |
| 197 | TEST_ASSERT(run->exit_reason == KVM_EXIT_IO, |
| 198 | "Unexpected exit reason: %u (%s),\n", |
| 199 | run->exit_reason, |
| 200 | exit_reason_str(run->exit_reason)); |
| 201 | TEST_ASSERT(run->s.regs.regs.r11 == 0xAAAA, |
| 202 | "r11 sync regs value incorrect 0x%llx.", |
| 203 | run->s.regs.regs.r11); |
| 204 | vcpu_regs_get(vm, VCPU_ID, ®s); |
| 205 | TEST_ASSERT(regs.r11 == 0xBAC0 + 1, |
| 206 | "r11 guest value incorrect 0x%llx.", |
| 207 | regs.r11); |
| 208 | |
| 209 | /* Clear kvm_valid_regs bits. Verify s.regs values are not overwritten |
| 210 | * with existing guest values but that guest values are overwritten |
| 211 | * with kvm_sync_regs values. |
| 212 | */ |
| 213 | run->kvm_valid_regs = 0; |
| 214 | run->kvm_dirty_regs = KVM_SYNC_X86_VALID_FIELDS; |
| 215 | run->s.regs.regs.r11 = 0xBBBB; |
| 216 | rv = _vcpu_run(vm, VCPU_ID); |
| 217 | TEST_ASSERT(run->exit_reason == KVM_EXIT_IO, |
| 218 | "Unexpected exit reason: %u (%s),\n", |
| 219 | run->exit_reason, |
| 220 | exit_reason_str(run->exit_reason)); |
| 221 | TEST_ASSERT(run->s.regs.regs.r11 == 0xBBBB, |
| 222 | "r11 sync regs value incorrect 0x%llx.", |
| 223 | run->s.regs.regs.r11); |
| 224 | vcpu_regs_get(vm, VCPU_ID, ®s); |
| 225 | TEST_ASSERT(regs.r11 == 0xBBBB + 1, |
| 226 | "r11 guest value incorrect 0x%llx.", |
| 227 | regs.r11); |
| 228 | |
| 229 | kvm_vm_free(vm); |
| 230 | |
| 231 | return 0; |
| 232 | } |