diff options
author | Paolo Bonzini <pbonzini@redhat.com> | 2017-12-20 18:49:14 -0500 |
---|---|---|
committer | Paolo Bonzini <pbonzini@redhat.com> | 2017-12-21 06:59:54 -0500 |
commit | fae1a3e775cca8c3a9e0eb34443b310871a15a92 (patch) | |
tree | 19939a12170be0445651ab50b5b65af6970732d2 | |
parent | 43aabca38aa9668eee3c3c1206207034614c0901 (diff) |
kvm: x86: fix RSM when PCID is non-zero
rsm_load_state_64() and rsm_enter_protected_mode() load CR3, then
CR4 & ~PCIDE, then CR0, then CR4.
However, setting CR4.PCIDE fails if CR3[11:0] != 0. It's probably easier
in the long run to replace rsm_enter_protected_mode() with an emulator
callback that sets all the special registers (like KVM_SET_SREGS would
do). For now, set the PCID field of CR3 only after CR4.PCIDE is 1.
Reported-by: Laszlo Ersek <lersek@redhat.com>
Tested-by: Laszlo Ersek <lersek@redhat.com>
Fixes: 660a5d517aaab9187f93854425c4c63f4a09195c
Cc: stable@vger.kernel.org
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
-rw-r--r-- | arch/x86/kvm/emulate.c | 32 |
1 files changed, 25 insertions, 7 deletions
diff --git a/arch/x86/kvm/emulate.c b/arch/x86/kvm/emulate.c index abe74f779f9d..b514b2b2845a 100644 --- a/arch/x86/kvm/emulate.c +++ b/arch/x86/kvm/emulate.c | |||
@@ -2390,9 +2390,21 @@ static int rsm_load_seg_64(struct x86_emulate_ctxt *ctxt, u64 smbase, int n) | |||
2390 | } | 2390 | } |
2391 | 2391 | ||
2392 | static int rsm_enter_protected_mode(struct x86_emulate_ctxt *ctxt, | 2392 | static int rsm_enter_protected_mode(struct x86_emulate_ctxt *ctxt, |
2393 | u64 cr0, u64 cr4) | 2393 | u64 cr0, u64 cr3, u64 cr4) |
2394 | { | 2394 | { |
2395 | int bad; | 2395 | int bad; |
2396 | u64 pcid; | ||
2397 | |||
2398 | /* In order to later set CR4.PCIDE, CR3[11:0] must be zero. */ | ||
2399 | pcid = 0; | ||
2400 | if (cr4 & X86_CR4_PCIDE) { | ||
2401 | pcid = cr3 & 0xfff; | ||
2402 | cr3 &= ~0xfff; | ||
2403 | } | ||
2404 | |||
2405 | bad = ctxt->ops->set_cr(ctxt, 3, cr3); | ||
2406 | if (bad) | ||
2407 | return X86EMUL_UNHANDLEABLE; | ||
2396 | 2408 | ||
2397 | /* | 2409 | /* |
2398 | * First enable PAE, long mode needs it before CR0.PG = 1 is set. | 2410 | * First enable PAE, long mode needs it before CR0.PG = 1 is set. |
@@ -2411,6 +2423,12 @@ static int rsm_enter_protected_mode(struct x86_emulate_ctxt *ctxt, | |||
2411 | bad = ctxt->ops->set_cr(ctxt, 4, cr4); | 2423 | bad = ctxt->ops->set_cr(ctxt, 4, cr4); |
2412 | if (bad) | 2424 | if (bad) |
2413 | return X86EMUL_UNHANDLEABLE; | 2425 | return X86EMUL_UNHANDLEABLE; |
2426 | if (pcid) { | ||
2427 | bad = ctxt->ops->set_cr(ctxt, 3, cr3 | pcid); | ||
2428 | if (bad) | ||
2429 | return X86EMUL_UNHANDLEABLE; | ||
2430 | } | ||
2431 | |||
2414 | } | 2432 | } |
2415 | 2433 | ||
2416 | return X86EMUL_CONTINUE; | 2434 | return X86EMUL_CONTINUE; |
@@ -2421,11 +2439,11 @@ static int rsm_load_state_32(struct x86_emulate_ctxt *ctxt, u64 smbase) | |||
2421 | struct desc_struct desc; | 2439 | struct desc_struct desc; |
2422 | struct desc_ptr dt; | 2440 | struct desc_ptr dt; |
2423 | u16 selector; | 2441 | u16 selector; |
2424 | u32 val, cr0, cr4; | 2442 | u32 val, cr0, cr3, cr4; |
2425 | int i; | 2443 | int i; |
2426 | 2444 | ||
2427 | cr0 = GET_SMSTATE(u32, smbase, 0x7ffc); | 2445 | cr0 = GET_SMSTATE(u32, smbase, 0x7ffc); |
2428 | ctxt->ops->set_cr(ctxt, 3, GET_SMSTATE(u32, smbase, 0x7ff8)); | 2446 | cr3 = GET_SMSTATE(u32, smbase, 0x7ff8); |
2429 | ctxt->eflags = GET_SMSTATE(u32, smbase, 0x7ff4) | X86_EFLAGS_FIXED; | 2447 | ctxt->eflags = GET_SMSTATE(u32, smbase, 0x7ff4) | X86_EFLAGS_FIXED; |
2430 | ctxt->_eip = GET_SMSTATE(u32, smbase, 0x7ff0); | 2448 | ctxt->_eip = GET_SMSTATE(u32, smbase, 0x7ff0); |
2431 | 2449 | ||
@@ -2467,14 +2485,14 @@ static int rsm_load_state_32(struct x86_emulate_ctxt *ctxt, u64 smbase) | |||
2467 | 2485 | ||
2468 | ctxt->ops->set_smbase(ctxt, GET_SMSTATE(u32, smbase, 0x7ef8)); | 2486 | ctxt->ops->set_smbase(ctxt, GET_SMSTATE(u32, smbase, 0x7ef8)); |
2469 | 2487 | ||
2470 | return rsm_enter_protected_mode(ctxt, cr0, cr4); | 2488 | return rsm_enter_protected_mode(ctxt, cr0, cr3, cr4); |
2471 | } | 2489 | } |
2472 | 2490 | ||
2473 | static int rsm_load_state_64(struct x86_emulate_ctxt *ctxt, u64 smbase) | 2491 | static int rsm_load_state_64(struct x86_emulate_ctxt *ctxt, u64 smbase) |
2474 | { | 2492 | { |
2475 | struct desc_struct desc; | 2493 | struct desc_struct desc; |
2476 | struct desc_ptr dt; | 2494 | struct desc_ptr dt; |
2477 | u64 val, cr0, cr4; | 2495 | u64 val, cr0, cr3, cr4; |
2478 | u32 base3; | 2496 | u32 base3; |
2479 | u16 selector; | 2497 | u16 selector; |
2480 | int i, r; | 2498 | int i, r; |
@@ -2491,7 +2509,7 @@ static int rsm_load_state_64(struct x86_emulate_ctxt *ctxt, u64 smbase) | |||
2491 | ctxt->ops->set_dr(ctxt, 7, (val & DR7_VOLATILE) | DR7_FIXED_1); | 2509 | ctxt->ops->set_dr(ctxt, 7, (val & DR7_VOLATILE) | DR7_FIXED_1); |
2492 | 2510 | ||
2493 | cr0 = GET_SMSTATE(u64, smbase, 0x7f58); | 2511 | cr0 = GET_SMSTATE(u64, smbase, 0x7f58); |
2494 | ctxt->ops->set_cr(ctxt, 3, GET_SMSTATE(u64, smbase, 0x7f50)); | 2512 | cr3 = GET_SMSTATE(u64, smbase, 0x7f50); |
2495 | cr4 = GET_SMSTATE(u64, smbase, 0x7f48); | 2513 | cr4 = GET_SMSTATE(u64, smbase, 0x7f48); |
2496 | ctxt->ops->set_smbase(ctxt, GET_SMSTATE(u32, smbase, 0x7f00)); | 2514 | ctxt->ops->set_smbase(ctxt, GET_SMSTATE(u32, smbase, 0x7f00)); |
2497 | val = GET_SMSTATE(u64, smbase, 0x7ed0); | 2515 | val = GET_SMSTATE(u64, smbase, 0x7ed0); |
@@ -2519,7 +2537,7 @@ static int rsm_load_state_64(struct x86_emulate_ctxt *ctxt, u64 smbase) | |||
2519 | dt.address = GET_SMSTATE(u64, smbase, 0x7e68); | 2537 | dt.address = GET_SMSTATE(u64, smbase, 0x7e68); |
2520 | ctxt->ops->set_gdt(ctxt, &dt); | 2538 | ctxt->ops->set_gdt(ctxt, &dt); |
2521 | 2539 | ||
2522 | r = rsm_enter_protected_mode(ctxt, cr0, cr4); | 2540 | r = rsm_enter_protected_mode(ctxt, cr0, cr3, cr4); |
2523 | if (r != X86EMUL_CONTINUE) | 2541 | if (r != X86EMUL_CONTINUE) |
2524 | return r; | 2542 | return r; |
2525 | 2543 | ||