Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

KVM: x86: allow RSM from 64-bit mode

The SDM says that exiting system management mode from 64-bit mode
is invalid, but that would be too good to be true. But actually,
most of the code is already there to support exiting from compat
mode (EFER.LME=1, EFER.LMA=0). Getting all the way from 64-bit
mode to real mode only requires clearing CS.L and CR4.PCIDE.

Cc: stable@vger.kernel.org
Fixes: 660a5d517aaab9187f93854425c4c63f4a09195c
Tested-by: Laszlo Ersek <lersek@redhat.com>
Cc: Radim Krčmář <rkrcmar@redhat.com>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>

+25 -5
+25 -5
arch/x86/kvm/emulate.c
··· 2484 2484 2485 2485 /* 2486 2486 * Get back to real mode, to prepare a safe state in which to load 2487 - * CR0/CR3/CR4/EFER. 2488 - * 2489 - * CR4.PCIDE must be zero, because it is a 64-bit mode only feature. 2487 + * CR0/CR3/CR4/EFER. It's all a bit more complicated if the vCPU 2488 + * supports long mode. 2490 2489 */ 2490 + cr4 = ctxt->ops->get_cr(ctxt, 4); 2491 + if (emulator_has_longmode(ctxt)) { 2492 + struct desc_struct cs_desc; 2493 + 2494 + /* Zero CR4.PCIDE before CR0.PG. */ 2495 + if (cr4 & X86_CR4_PCIDE) { 2496 + ctxt->ops->set_cr(ctxt, 4, cr4 & ~X86_CR4_PCIDE); 2497 + cr4 &= ~X86_CR4_PCIDE; 2498 + } 2499 + 2500 + /* A 32-bit code segment is required to clear EFER.LMA. */ 2501 + memset(&cs_desc, 0, sizeof(cs_desc)); 2502 + cs_desc.type = 0xb; 2503 + cs_desc.s = cs_desc.g = cs_desc.p = 1; 2504 + ctxt->ops->set_segment(ctxt, 0, &cs_desc, 0, VCPU_SREG_CS); 2505 + } 2506 + 2507 + /* For the 64-bit case, this will clear EFER.LMA. */ 2491 2508 cr0 = ctxt->ops->get_cr(ctxt, 0); 2492 2509 if (cr0 & X86_CR0_PE) 2493 2510 ctxt->ops->set_cr(ctxt, 0, cr0 & ~(X86_CR0_PG | X86_CR0_PE)); 2494 - cr4 = ctxt->ops->get_cr(ctxt, 4); 2511 + 2512 + /* Now clear CR4.PAE (which must be done before clearing EFER.LME). */ 2495 2513 if (cr4 & X86_CR4_PAE) 2496 2514 ctxt->ops->set_cr(ctxt, 4, cr4 & ~X86_CR4_PAE); 2515 + 2516 + /* And finally go back to 32-bit mode. */ 2497 2517 efer = 0; 2498 2518 ctxt->ops->set_msr(ctxt, MSR_EFER, efer); 2499 2519 ··· 4474 4454 F(DstMem | SrcReg | Src2CL | ModRM, em_shld), N, N, 4475 4455 /* 0xA8 - 0xAF */ 4476 4456 I(Stack | Src2GS, em_push_sreg), I(Stack | Src2GS, em_pop_sreg), 4477 - II(No64 | EmulateOnUD | ImplicitOps, em_rsm, rsm), 4457 + II(EmulateOnUD | ImplicitOps, em_rsm, rsm), 4478 4458 F(DstMem | SrcReg | ModRM | BitOp | Lock | PageTable, em_bts), 4479 4459 F(DstMem | SrcReg | Src2ImmByte | ModRM, em_shrd), 4480 4460 F(DstMem | SrcReg | Src2CL | ModRM, em_shrd),