412 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * Test for x86 KVM_CAP_SYNC_REGS4 *5 * Copyright (C) 2018, Google LLC.6 *7 * Verifies expected behavior of x86 KVM_CAP_SYNC_REGS functionality,8 * including requesting an invalid register set, updates to/from values9 * in kvm_run.s.regs when kvm_valid_regs and kvm_dirty_regs are toggled.10 */11#include <fcntl.h>12#include <stdio.h>13#include <stdlib.h>14#include <string.h>15#include <sys/ioctl.h>16#include <pthread.h>17 18#include "kvm_test_harness.h"19#include "test_util.h"20#include "kvm_util.h"21#include "processor.h"22 23#define UCALL_PIO_PORT ((uint16_t)0x1000)24 25struct ucall uc_none = {26 .cmd = UCALL_NONE,27};28 29/*30 * ucall is embedded here to protect against compiler reshuffling registers31 * before calling a function. In this test we only need to get KVM_EXIT_IO32 * vmexit and preserve RBX, no additional information is needed.33 */34void guest_code(void)35{36 asm volatile("1: in %[port], %%al\n"37 "add $0x1, %%rbx\n"38 "jmp 1b"39 : : [port] "d" (UCALL_PIO_PORT), "D" (&uc_none)40 : "rax", "rbx");41}42 43KVM_ONE_VCPU_TEST_SUITE(sync_regs_test);44 45static void compare_regs(struct kvm_regs *left, struct kvm_regs *right)46{47#define REG_COMPARE(reg) \48 TEST_ASSERT(left->reg == right->reg, \49 "Register " #reg \50 " values did not match: 0x%llx, 0x%llx", \51 left->reg, right->reg)52 REG_COMPARE(rax);53 REG_COMPARE(rbx);54 REG_COMPARE(rcx);55 REG_COMPARE(rdx);56 REG_COMPARE(rsi);57 REG_COMPARE(rdi);58 REG_COMPARE(rsp);59 REG_COMPARE(rbp);60 REG_COMPARE(r8);61 REG_COMPARE(r9);62 REG_COMPARE(r10);63 REG_COMPARE(r11);64 REG_COMPARE(r12);65 REG_COMPARE(r13);66 REG_COMPARE(r14);67 REG_COMPARE(r15);68 REG_COMPARE(rip);69 REG_COMPARE(rflags);70#undef REG_COMPARE71}72 73static void compare_sregs(struct kvm_sregs *left, struct kvm_sregs *right)74{75}76 77static void compare_vcpu_events(struct kvm_vcpu_events *left,78 struct kvm_vcpu_events *right)79{80}81 82#define TEST_SYNC_FIELDS (KVM_SYNC_X86_REGS|KVM_SYNC_X86_SREGS|KVM_SYNC_X86_EVENTS)83#define INVALID_SYNC_FIELD 0x8000000084 85/*86 * Set an exception as pending *and* injected while KVM is processing events.87 * KVM is supposed to ignore/drop pending exceptions if userspace is also88 * requesting that an exception be injected.89 */90static void *race_events_inj_pen(void *arg)91{92 struct kvm_run *run = (struct kvm_run *)arg;93 struct kvm_vcpu_events *events = &run->s.regs.events;94 95 WRITE_ONCE(events->exception.nr, UD_VECTOR);96 97 for (;;) {98 WRITE_ONCE(run->kvm_dirty_regs, KVM_SYNC_X86_EVENTS);99 WRITE_ONCE(events->flags, 0);100 WRITE_ONCE(events->exception.injected, 1);101 WRITE_ONCE(events->exception.pending, 1);102 103 pthread_testcancel();104 }105 106 return NULL;107}108 109/*110 * Set an invalid exception vector while KVM is processing events. KVM is111 * supposed to reject any vector >= 32, as well as NMIs (vector 2).112 */113static void *race_events_exc(void *arg)114{115 struct kvm_run *run = (struct kvm_run *)arg;116 struct kvm_vcpu_events *events = &run->s.regs.events;117 118 for (;;) {119 WRITE_ONCE(run->kvm_dirty_regs, KVM_SYNC_X86_EVENTS);120 WRITE_ONCE(events->flags, 0);121 WRITE_ONCE(events->exception.nr, UD_VECTOR);122 WRITE_ONCE(events->exception.pending, 1);123 WRITE_ONCE(events->exception.nr, 255);124 125 pthread_testcancel();126 }127 128 return NULL;129}130 131/*132 * Toggle CR4.PAE while KVM is processing SREGS, EFER.LME=1 with CR4.PAE=0 is133 * illegal, and KVM's MMU heavily relies on vCPU state being valid.134 */135static noinline void *race_sregs_cr4(void *arg)136{137 struct kvm_run *run = (struct kvm_run *)arg;138 __u64 *cr4 = &run->s.regs.sregs.cr4;139 __u64 pae_enabled = *cr4;140 __u64 pae_disabled = *cr4 & ~X86_CR4_PAE;141 142 for (;;) {143 WRITE_ONCE(run->kvm_dirty_regs, KVM_SYNC_X86_SREGS);144 WRITE_ONCE(*cr4, pae_enabled);145 asm volatile(".rept 512\n\t"146 "nop\n\t"147 ".endr");148 WRITE_ONCE(*cr4, pae_disabled);149 150 pthread_testcancel();151 }152 153 return NULL;154}155 156static void race_sync_regs(struct kvm_vcpu *vcpu, void *racer)157{158 const time_t TIMEOUT = 2; /* seconds, roughly */159 struct kvm_x86_state *state;160 struct kvm_translation tr;161 struct kvm_run *run;162 pthread_t thread;163 time_t t;164 165 run = vcpu->run;166 167 run->kvm_valid_regs = KVM_SYNC_X86_SREGS;168 vcpu_run(vcpu);169 run->kvm_valid_regs = 0;170 171 /* Save state *before* spawning the thread that mucks with vCPU state. */172 state = vcpu_save_state(vcpu);173 174 /*175 * Selftests run 64-bit guests by default, both EFER.LME and CR4.PAE176 * should already be set in guest state.177 */178 TEST_ASSERT((run->s.regs.sregs.cr4 & X86_CR4_PAE) &&179 (run->s.regs.sregs.efer & EFER_LME),180 "vCPU should be in long mode, CR4.PAE=%d, EFER.LME=%d",181 !!(run->s.regs.sregs.cr4 & X86_CR4_PAE),182 !!(run->s.regs.sregs.efer & EFER_LME));183 184 TEST_ASSERT_EQ(pthread_create(&thread, NULL, racer, (void *)run), 0);185 186 for (t = time(NULL) + TIMEOUT; time(NULL) < t;) {187 /*188 * Reload known good state if the vCPU triple faults, e.g. due189 * to the unhandled #GPs being injected. VMX preserves state190 * on shutdown, but SVM synthesizes an INIT as the VMCB state191 * is architecturally undefined on triple fault.192 */193 if (!__vcpu_run(vcpu) && run->exit_reason == KVM_EXIT_SHUTDOWN)194 vcpu_load_state(vcpu, state);195 196 if (racer == race_sregs_cr4) {197 tr = (struct kvm_translation) { .linear_address = 0 };198 __vcpu_ioctl(vcpu, KVM_TRANSLATE, &tr);199 }200 }201 202 TEST_ASSERT_EQ(pthread_cancel(thread), 0);203 TEST_ASSERT_EQ(pthread_join(thread, NULL), 0);204 205 kvm_x86_state_cleanup(state);206}207 208KVM_ONE_VCPU_TEST(sync_regs_test, read_invalid, guest_code)209{210 struct kvm_run *run = vcpu->run;211 int rv;212 213 /* Request reading invalid register set from VCPU. */214 run->kvm_valid_regs = INVALID_SYNC_FIELD;215 rv = _vcpu_run(vcpu);216 TEST_ASSERT(rv < 0 && errno == EINVAL,217 "Invalid kvm_valid_regs did not cause expected KVM_RUN error: %d",218 rv);219 run->kvm_valid_regs = 0;220 221 run->kvm_valid_regs = INVALID_SYNC_FIELD | TEST_SYNC_FIELDS;222 rv = _vcpu_run(vcpu);223 TEST_ASSERT(rv < 0 && errno == EINVAL,224 "Invalid kvm_valid_regs did not cause expected KVM_RUN error: %d",225 rv);226 run->kvm_valid_regs = 0;227}228 229KVM_ONE_VCPU_TEST(sync_regs_test, set_invalid, guest_code)230{231 struct kvm_run *run = vcpu->run;232 int rv;233 234 /* Request setting invalid register set into VCPU. */235 run->kvm_dirty_regs = INVALID_SYNC_FIELD;236 rv = _vcpu_run(vcpu);237 TEST_ASSERT(rv < 0 && errno == EINVAL,238 "Invalid kvm_dirty_regs did not cause expected KVM_RUN error: %d",239 rv);240 run->kvm_dirty_regs = 0;241 242 run->kvm_dirty_regs = INVALID_SYNC_FIELD | TEST_SYNC_FIELDS;243 rv = _vcpu_run(vcpu);244 TEST_ASSERT(rv < 0 && errno == EINVAL,245 "Invalid kvm_dirty_regs did not cause expected KVM_RUN error: %d",246 rv);247 run->kvm_dirty_regs = 0;248}249 250KVM_ONE_VCPU_TEST(sync_regs_test, req_and_verify_all_valid, guest_code)251{252 struct kvm_run *run = vcpu->run;253 struct kvm_vcpu_events events;254 struct kvm_sregs sregs;255 struct kvm_regs regs;256 257 /* Request and verify all valid register sets. */258 /* TODO: BUILD TIME CHECK: TEST_ASSERT(KVM_SYNC_X86_NUM_FIELDS != 3); */259 run->kvm_valid_regs = TEST_SYNC_FIELDS;260 vcpu_run(vcpu);261 TEST_ASSERT_KVM_EXIT_REASON(vcpu, KVM_EXIT_IO);262 263 vcpu_regs_get(vcpu, ®s);264 compare_regs(®s, &run->s.regs.regs);265 266 vcpu_sregs_get(vcpu, &sregs);267 compare_sregs(&sregs, &run->s.regs.sregs);268 269 vcpu_events_get(vcpu, &events);270 compare_vcpu_events(&events, &run->s.regs.events);271}272 273KVM_ONE_VCPU_TEST(sync_regs_test, set_and_verify_various, guest_code)274{275 struct kvm_run *run = vcpu->run;276 struct kvm_vcpu_events events;277 struct kvm_sregs sregs;278 struct kvm_regs regs;279 280 /* Run once to get register set */281 run->kvm_valid_regs = TEST_SYNC_FIELDS;282 vcpu_run(vcpu);283 TEST_ASSERT_KVM_EXIT_REASON(vcpu, KVM_EXIT_IO);284 285 /* Set and verify various register values. */286 run->s.regs.regs.rbx = 0xBAD1DEA;287 run->s.regs.sregs.apic_base = 1 << 11;288 /* TODO run->s.regs.events.XYZ = ABC; */289 290 run->kvm_valid_regs = TEST_SYNC_FIELDS;291 run->kvm_dirty_regs = KVM_SYNC_X86_REGS | KVM_SYNC_X86_SREGS;292 vcpu_run(vcpu);293 TEST_ASSERT_KVM_EXIT_REASON(vcpu, KVM_EXIT_IO);294 TEST_ASSERT(run->s.regs.regs.rbx == 0xBAD1DEA + 1,295 "rbx sync regs value incorrect 0x%llx.",296 run->s.regs.regs.rbx);297 TEST_ASSERT(run->s.regs.sregs.apic_base == 1 << 11,298 "apic_base sync regs value incorrect 0x%llx.",299 run->s.regs.sregs.apic_base);300 301 vcpu_regs_get(vcpu, ®s);302 compare_regs(®s, &run->s.regs.regs);303 304 vcpu_sregs_get(vcpu, &sregs);305 compare_sregs(&sregs, &run->s.regs.sregs);306 307 vcpu_events_get(vcpu, &events);308 compare_vcpu_events(&events, &run->s.regs.events);309}310 311KVM_ONE_VCPU_TEST(sync_regs_test, clear_kvm_dirty_regs_bits, guest_code)312{313 struct kvm_run *run = vcpu->run;314 315 /* Clear kvm_dirty_regs bits, verify new s.regs values are316 * overwritten with existing guest values.317 */318 run->kvm_valid_regs = TEST_SYNC_FIELDS;319 run->kvm_dirty_regs = 0;320 run->s.regs.regs.rbx = 0xDEADBEEF;321 vcpu_run(vcpu);322 TEST_ASSERT_KVM_EXIT_REASON(vcpu, KVM_EXIT_IO);323 TEST_ASSERT(run->s.regs.regs.rbx != 0xDEADBEEF,324 "rbx sync regs value incorrect 0x%llx.",325 run->s.regs.regs.rbx);326}327 328KVM_ONE_VCPU_TEST(sync_regs_test, clear_kvm_valid_and_dirty_regs, guest_code)329{330 struct kvm_run *run = vcpu->run;331 struct kvm_regs regs;332 333 /* Run once to get register set */334 run->kvm_valid_regs = TEST_SYNC_FIELDS;335 vcpu_run(vcpu);336 TEST_ASSERT_KVM_EXIT_REASON(vcpu, KVM_EXIT_IO);337 338 /* Clear kvm_valid_regs bits and kvm_dirty_bits.339 * Verify s.regs values are not overwritten with existing guest values340 * and that guest values are not overwritten with kvm_sync_regs values.341 */342 run->kvm_valid_regs = 0;343 run->kvm_dirty_regs = 0;344 run->s.regs.regs.rbx = 0xAAAA;345 vcpu_regs_get(vcpu, ®s);346 regs.rbx = 0xBAC0;347 vcpu_regs_set(vcpu, ®s);348 vcpu_run(vcpu);349 TEST_ASSERT_KVM_EXIT_REASON(vcpu, KVM_EXIT_IO);350 TEST_ASSERT(run->s.regs.regs.rbx == 0xAAAA,351 "rbx sync regs value incorrect 0x%llx.",352 run->s.regs.regs.rbx);353 vcpu_regs_get(vcpu, ®s);354 TEST_ASSERT(regs.rbx == 0xBAC0 + 1,355 "rbx guest value incorrect 0x%llx.",356 regs.rbx);357}358 359KVM_ONE_VCPU_TEST(sync_regs_test, clear_kvm_valid_regs_bits, guest_code)360{361 struct kvm_run *run = vcpu->run;362 struct kvm_regs regs;363 364 /* Run once to get register set */365 run->kvm_valid_regs = TEST_SYNC_FIELDS;366 vcpu_run(vcpu);367 TEST_ASSERT_KVM_EXIT_REASON(vcpu, KVM_EXIT_IO);368 369 /* Clear kvm_valid_regs bits. Verify s.regs values are not overwritten370 * with existing guest values but that guest values are overwritten371 * with kvm_sync_regs values.372 */373 run->kvm_valid_regs = 0;374 run->kvm_dirty_regs = TEST_SYNC_FIELDS;375 run->s.regs.regs.rbx = 0xBBBB;376 vcpu_run(vcpu);377 TEST_ASSERT_KVM_EXIT_REASON(vcpu, KVM_EXIT_IO);378 TEST_ASSERT(run->s.regs.regs.rbx == 0xBBBB,379 "rbx sync regs value incorrect 0x%llx.",380 run->s.regs.regs.rbx);381 vcpu_regs_get(vcpu, ®s);382 TEST_ASSERT(regs.rbx == 0xBBBB + 1,383 "rbx guest value incorrect 0x%llx.",384 regs.rbx);385}386 387KVM_ONE_VCPU_TEST(sync_regs_test, race_cr4, guest_code)388{389 race_sync_regs(vcpu, race_sregs_cr4);390}391 392KVM_ONE_VCPU_TEST(sync_regs_test, race_exc, guest_code)393{394 race_sync_regs(vcpu, race_events_exc);395}396 397KVM_ONE_VCPU_TEST(sync_regs_test, race_inj_pen, guest_code)398{399 race_sync_regs(vcpu, race_events_inj_pen);400}401 402int main(int argc, char *argv[])403{404 int cap;405 406 cap = kvm_check_cap(KVM_CAP_SYNC_REGS);407 TEST_REQUIRE((cap & TEST_SYNC_FIELDS) == TEST_SYNC_FIELDS);408 TEST_REQUIRE(!(cap & INVALID_SYNC_FIELD));409 410 return test_harness_run(argc, argv);411}412