Implemented VMX 'Shadow Stack Prematurely Busy' and secondary VMEXIT controls

This commit is contained in:
Stanislav Shwartsman 2024-01-27 13:34:51 +02:00
parent e14b126198
commit 38b1bbf4ff
12 changed files with 169 additions and 43 deletions

View File

@ -8,6 +8,7 @@ Brief summary :
- Critical CPU emulation bugfixes for SHA and GFNI instructions, ADOX instruction (prevented boot of Win10)
! SVM: Implemented SVM VM_CR_MSR and INIT redirect (required for booting SMP with SVM)
! Implemented VMX MBE (Mode Based Execution Control) emulation required for Windows 11 guest
! Implemented VMX 'Shadow Stack Prematurely Busy' control
! Implemented MSR IA32_SPEC_CTRL Virtualization VMX extension
! Implemented Posted-Interrupt Processing VMX extension emulation
! Implemented Linear Address Separation (LASS) extension
@ -42,6 +43,7 @@ Detailed change log :
- SVM: Implemented SVM VM_CR_MSR and INIT redirect (required for booting SMP with SVM)
- Implemented VMX MBE (Mode Based Execution Control) emulation required for Windows 11 guest
- Implemented advanced VM-exit information for EPT violations (tied to MBE Control)
- Implemented VMX 'Shadow Stack Prematurely Busy' control
- Implemented MSR IA32_SPEC_CTRL Virtualization VMX extension
- Implemented Posted-Interrupt Processing VMX extension emulation
- Implemented Linear Address Separation (LASS) extension

View File

@ -2,7 +2,7 @@
// $Id$
/////////////////////////////////////////////////////////////////////////
//
// Copyright (c) 2005-2019 Stanislav Shwartsman
// Copyright (c) 2005-2024 Stanislav Shwartsman
// Written by Stanislav Shwartsman [sshwarts at sourceforge net]
//
// This library is free software; you can redistribute it and/or
@ -649,6 +649,11 @@ void BX_CPP_AttrRegparmN(1) BX_CPU_C::shadow_stack_switch(bx_address new_SSP)
void BX_CPP_AttrRegparmN(1) BX_CPU_C::call_far_shadow_stack_push(Bit16u cs, bx_address lip, bx_address old_ssp)
{
#if BX_SUPPORT_VMX
if (BX_CPU_THIS_PTR in_vmx_guest)
BX_CPU_THIS_PTR vmcs.shadow_stack_prematurely_busy = true;
#endif
if (SSP & 0x7) {
shadow_stack_write_dword(SSP-4, CPL, 0);
SSP &= ~BX_CONST64(0x7);
@ -657,7 +662,12 @@ void BX_CPP_AttrRegparmN(1) BX_CPU_C::call_far_shadow_stack_push(Bit16u cs, bx_a
shadow_stack_push_64(cs);
shadow_stack_push_64(lip);
shadow_stack_push_64(old_ssp);
}
#endif
#if BX_SUPPORT_VMX
if (BX_CPU_THIS_PTR in_vmx_guest)
BX_CPU_THIS_PTR vmcs.shadow_stack_prematurely_busy = false;
#endif
}
#endif // BX_SUPPORT_CET
#endif // BX_SUPPORT_X86_64

View File

@ -89,6 +89,15 @@ void BX_CPU_C::cpu_loop(void)
BX_CPU_THIS_PTR prev_rip = RIP; // commit new EIP
BX_CPU_THIS_PTR speculative_rsp = false;
#if BX_SUPPORT_CET && BX_SUPPORT_VMX
if (BX_CPU_THIS_PTR in_vmx_guest) {
VMCS_CACHE *vm = &BX_CPU_THIS_PTR vmcs;
if (vm->shadow_stack_prematurely_busy)
BX_PANIC(("Shadow stack prematurely busy is left set !"));
vm->shadow_stack_prematurely_busy = false; // for safety
}
#endif
while (1) {
// check on events which occurred for previous instructions (traps)

View File

@ -4944,6 +4944,7 @@ public: // for now...
BX_SMF void init_secondary_proc_based_vmexec_ctrls(void);
BX_SMF void init_tertiary_proc_based_vmexec_ctrls(void);
BX_SMF void init_vmexit_ctrls(void);
BX_SMF void init_secondary_vmexit_ctrls(void);
BX_SMF void init_vmentry_ctrls(void);
BX_SMF void init_VMCS(void);
BX_SMF bool vmcs_field_supported(Bit32u encoding);
@ -4974,7 +4975,7 @@ public: // for now...
BX_SMF void VMX_Self_IPI_Virtualization(Bit8u vector);
BX_SMF void VMX_Evaluate_Pending_Virtual_Interrupts(void);
BX_SMF void VMX_Deliver_Virtual_Interrupt(void);
BX_SMF void vmx_page_modification_logging(Bit64u guest_addr, unsigned dirty_update);
BX_SMF void vmx_page_modification_logging(Bit64u guest_laddr, Bit64u guest_paddr, unsigned dirty_update);
#endif
#if BX_SUPPORT_VMX >= 2
BX_SMF Bit16u VMread16_Shadow(unsigned encoding) BX_CPP_AttrRegparmN(1);

View File

@ -181,8 +181,6 @@ bxICacheEntry_c* BX_CPU_C::serveICacheMiss(Bit32u eipBiased, bx_phy_address pAdd
}
}
//BX_INFO(("commit trace %08x len=%d mask %08x", (Bit32u) entry->pAddr, entry->tlen, pageWriteStampTable.getFineGranularityMapping(entry->pAddr)));
entry->traceMask |= traceMask;
pageWriteStampTable.markICacheMask(pAddr, entry->traceMask);

View File

@ -387,6 +387,12 @@ bool BX_CPP_AttrRegparmN(2) BX_CPU_C::rdmsr(Bit32u index, Bit64u *msr)
case BX_MSR_VMX_VMEXIT_CTRLS:
val64 = VMX_MSR_VMX_VMEXIT_CTRLS;
break;
case BX_MSR_VMX_VMEXIT_CTRLS2:
if (BX_CPU_THIS_PTR vmx_cap.vmx_vmexit_ctrl2_supported_bits) {
val64 = VMX_MSR_VMX_VMEXIT_CTRLS2;
break;
}
return false;
case BX_MSR_VMX_VMENTRY_CTRLS:
val64 = VMX_MSR_VMX_VMENTRY_CTRLS;
break;
@ -1144,6 +1150,7 @@ bool BX_CPP_AttrRegparmN(2) BX_CPU_C::wrmsr(Bit32u index, Bit64u val_64)
case BX_MSR_VMX_PROCBASED_CTRLS2:
case BX_MSR_VMX_PROCBASED_CTRLS3:
case BX_MSR_VMX_VMEXIT_CTRLS:
case BX_MSR_VMX_VMEXIT_CTRLS2:
case BX_MSR_VMX_VMENTRY_CTRLS:
case BX_MSR_VMX_MISC:
case BX_MSR_VMX_CR0_FIXED0:

View File

@ -124,6 +124,7 @@ enum MSR_Register {
BX_MSR_VMX_TRUE_VMENTRY_CTRLS = 0x490,
BX_MSR_VMX_VMFUNC = 0x491,
BX_MSR_VMX_PROCBASED_CTRLS3 = 0x492,
BX_MSR_VMX_VMEXIT_CTRLS2 = 0x493,
BX_MSR_IA32_FEATURE_CONTROL = 0x03A,
BX_MSR_IA32_SMM_MONITOR_CTL = 0x09B,
#endif

View File

@ -921,8 +921,16 @@ void BX_CPU_C::update_access_dirty_PAE(bx_phy_address *entry_addr, Bit64u *entry
}
// Update A/D bits if needed
if (!(entry[leaf] & 0x20) || (write && !(entry[leaf] & 0x40))) {
entry[leaf] |= (0x20 | (write<<6)); // Update A and possibly D bits
// Specifically, a processor that supports CET will never set the dirty flag in a paging-structure entry in which the R/W flag is clear
bool set_dirty = write && !(entry[leaf] & 0x40);
if (BX_CPUID_SUPPORT_ISA_EXTENSION(BX_ISA_CET)) {
if (set_dirty && !(entry[leaf] & 0x02)) {
BX_PANIC(("PAE: asked to set dirty on paging leaf entry with R/W bit clear"));
}
}
if (!(entry[leaf] & 0x20) || set_dirty) {
entry[leaf] |= 0x20; // Update A and possibly D bits
if (set_dirty) entry[leaf] |= 0x40;
write_physical_qword(entry_addr[leaf], entry[leaf], entry_memtype[leaf], AccessReason(BX_PTE_ACCESS + leaf)); // should be done with locked RMW
}
}
@ -1227,8 +1235,16 @@ void BX_CPU_C::update_access_dirty(bx_phy_address *entry_addr, Bit32u *entry, Bx
}
// Update A/D bits if needed
if (!(entry[leaf] & 0x20) || (write && !(entry[leaf] & 0x40))) {
entry[leaf] |= (0x20 | (write<<6)); // Update A and possibly D bits
// Specifically, a processor that supports CET will never set the dirty flag in a paging-structure entry in which the R/W flag is clear
bool set_dirty = write && !(entry[leaf] & 0x40);
if (BX_CPUID_SUPPORT_ISA_EXTENSION(BX_ISA_CET)) {
if (set_dirty && !(entry[leaf] & 0x02)) {
BX_PANIC(("Legacy Paging: asked to set dirty on paging leaf entry with R/W bit clear"));
}
}
if (!(entry[leaf] & 0x20) || set_dirty) {
entry[leaf] |= 0x20; // Update A and possibly D bits
if (set_dirty) entry[leaf] |= 0x40;
write_physical_dword(entry_addr[leaf], entry[leaf], entry_memtype[leaf], AccessReason(BX_PTE_ACCESS + leaf)); // should be done with locked RMW
}
}
@ -2089,7 +2105,7 @@ bx_phy_address BX_CPU_C::translate_guest_physical(bx_phy_address guest_paddr, bx
// write access and Dirty-bit is not set in the leaf entry
unsigned dirty_update = (rw & 1) && !(entry[leaf] & 0x200);
if (SECONDARY_VMEXEC_CONTROL(VMX_VM_EXEC_CTRL2_PML_ENABLE))
vmx_page_modification_logging(guest_paddr, dirty_update);
vmx_page_modification_logging(guest_laddr, guest_paddr, dirty_update);
update_ept_access_dirty(entry_addr, entry, MEMTYPE(eptptr_memtype), leaf, rw & 1);
}

View File

@ -2,7 +2,7 @@
// $Id$
/////////////////////////////////////////////////////////////////////////
//
// Copyright (c) 2009-2023 Stanislav Shwartsman
// Copyright (c) 2009-2024 Stanislav Shwartsman
// Written by Stanislav Shwartsman [sshwarts at sourceforge net]
//
// This library is free software; you can redistribute it and/or
@ -398,6 +398,10 @@ bool BX_CPU_C::vmcs_field_supported(Bit32u encoding)
case VMCS_64BIT_CONTROL_TERTIARY_VMEXEC_CONTROLS_HI:
return BX_CPU_THIS_PTR vmx_cap.vmx_vmexec_ctrl3_supported_bits;
case VMCS_64BIT_CONTROL_SECONDARY_VMEXIT_CONTROLS:
case VMCS_64BIT_CONTROL_SECONDARY_VMEXIT_CONTROLS_HI:
return BX_CPU_THIS_PTR vmx_cap.vmx_vmexit_ctrl2_supported_bits;
case VMCS_64BIT_CONTROL_IA32_SPEC_CTRL_MASK:
case VMCS_64BIT_CONTROL_IA32_SPEC_CTRL_MASK_HI:
case VMCS_64BIT_CONTROL_IA32_SPEC_CTRL_SHADOW:
@ -565,6 +569,7 @@ void BX_CPU_C::init_vmx_capabilities(void)
init_tertiary_proc_based_vmexec_ctrls(); // must initialize in reverse order
init_secondary_proc_based_vmexec_ctrls();
init_primary_proc_based_vmexec_ctrls();
init_secondary_vmexit_ctrls(); // must initialize in reverse order
init_vmexit_ctrls();
init_vmentry_ctrls();
}
@ -881,6 +886,20 @@ void BX_CPU_C::init_tertiary_proc_based_vmexec_ctrls(void)
}
}
void BX_CPU_C::init_secondary_vmexit_ctrls(void)
{
struct bx_VMX_Cap *cap = &BX_CPU_THIS_PTR vmx_cap;
// secondary vmexit controls
// -----------------------------------------------------------
// [02] Prematurely busy shadow stack control
#if BX_SUPPORT_CET
if (BX_CPUID_SUPPORT_ISA_EXTENSION(BX_ISA_CET))
cap->vmx_vmexit_ctrl2_supported_bits |= VMX_VMEXIT_CTRL2_SHADOW_STACK_BUSY_CTRL;
#endif
}
void BX_CPU_C::init_vmexit_ctrls(void)
{
struct bx_VMX_Cap *cap = &BX_CPU_THIS_PTR vmx_cap;
@ -905,42 +924,46 @@ void BX_CPU_C::init_vmexit_ctrls(void)
// [28] Save host CET state on VMEXIT
// [29] Save host MSR_IA32_PKRS on VMEXIT
// [30] Save guest MSR_PERF_GLOBAL_CTRL on VMEXIT
// [31] Activate secondary VMEXIT controls
cap->vmx_vmexit_ctrl_supported_bits =
cap->vmx_vmexit_ctrl1_supported_bits =
VMX_VMEXIT_CTRL1_INTA_ON_VMEXIT | VMX_VMEXIT_CTRL1_SAVE_DBG_CTRLS;
#if BX_SUPPORT_X86_64
if (BX_CPUID_SUPPORT_ISA_EXTENSION(BX_ISA_LONG_MODE))
cap->vmx_vmexit_ctrl_supported_bits |= VMX_VMEXIT_CTRL1_HOST_ADDR_SPACE_SIZE;
cap->vmx_vmexit_ctrl1_supported_bits |= VMX_VMEXIT_CTRL1_HOST_ADDR_SPACE_SIZE;
#endif
if (BX_SUPPORT_VMX_EXTENSION(BX_VMX_PERF_GLOBAL_CTRL))
cap->vmx_vmexit_ctrl_supported_bits |= VMX_VMEXIT_CTRL1_LOAD_PERF_GLOBAL_CTRL_MSR;
cap->vmx_vmexit_ctrl1_supported_bits |= VMX_VMEXIT_CTRL1_LOAD_PERF_GLOBAL_CTRL_MSR;
#if BX_SUPPORT_VMX >= 2
if (BX_SUPPORT_VMX_EXTENSION(BX_VMX_PAT)) {
cap->vmx_vmexit_ctrl_supported_bits |=
cap->vmx_vmexit_ctrl1_supported_bits |=
VMX_VMEXIT_CTRL1_STORE_PAT_MSR | VMX_VMEXIT_CTRL1_LOAD_PAT_MSR;
}
if (BX_SUPPORT_VMX_EXTENSION(BX_VMX_EFER)) {
cap->vmx_vmexit_ctrl_supported_bits |=
cap->vmx_vmexit_ctrl1_supported_bits |=
VMX_VMEXIT_CTRL1_STORE_EFER_MSR | VMX_VMEXIT_CTRL1_LOAD_EFER_MSR;
}
if (BX_SUPPORT_VMX_EXTENSION(BX_VMX_PREEMPTION_TIMER))
cap->vmx_vmexit_ctrl_supported_bits |= VMX_VMEXIT_CTRL1_STORE_VMX_PREEMPTION_TIMER;
cap->vmx_vmexit_ctrl1_supported_bits |= VMX_VMEXIT_CTRL1_STORE_VMX_PREEMPTION_TIMER;
#endif
#if BX_SUPPORT_UINTR
if (BX_CPUID_SUPPORT_ISA_EXTENSION(BX_ISA_UINTR))
cap->vmx_vmexit_ctrl_supported_bits |= VMX_VMEXIT_CTRL1_CLEAR_UINV;
cap->vmx_vmexit_ctrl1_supported_bits |= VMX_VMEXIT_CTRL1_CLEAR_UINV;
#endif
#if BX_SUPPORT_CET
if (BX_CPUID_SUPPORT_ISA_EXTENSION(BX_ISA_CET))
cap->vmx_vmexit_ctrl_supported_bits |= VMX_VMEXIT_CTRL1_LOAD_HOST_CET_STATE;
cap->vmx_vmexit_ctrl1_supported_bits |= VMX_VMEXIT_CTRL1_LOAD_HOST_CET_STATE;
#endif
#if BX_SUPPORT_PKEYS
if (BX_CPUID_SUPPORT_ISA_EXTENSION(BX_ISA_PKS))
cap->vmx_vmexit_ctrl_supported_bits |= VMX_VMEXIT_CTRL1_LOAD_HOST_PKRS;
cap->vmx_vmexit_ctrl1_supported_bits |= VMX_VMEXIT_CTRL1_LOAD_HOST_PKRS;
#endif
if (BX_SUPPORT_VMX_EXTENSION(BX_VMX_PERF_GLOBAL_CTRL))
cap->vmx_vmexit_ctrl_supported_bits |= VMX_VMEXIT_CTRL1_SAVE_PERF_GLOBAL_CTRL;
cap->vmx_vmexit_ctrl1_supported_bits |= VMX_VMEXIT_CTRL1_SAVE_PERF_GLOBAL_CTRL;
if (cap->vmx_vmexit_ctrl2_supported_bits)
cap->vmx_vmexit_ctrl1_supported_bits |= VMX_VMEXIT_CTRL1_ACTIVATE_SECONDARY_CTRLS;
}
void BX_CPU_C::init_vmentry_ctrls(void)

View File

@ -2,7 +2,7 @@
// $Id$
/////////////////////////////////////////////////////////////////////////
//
// Copyright (c) 2009-2023 Stanislav Shwartsman
// Copyright (c) 2009-2024 Stanislav Shwartsman
// Written by Stanislav Shwartsman [sshwarts at sourceforge net]
//
// This library is free software; you can redistribute it and/or
@ -198,7 +198,7 @@ void BX_CPU_C::VMexit_ExtInterrupt(void)
if (PIN_VMEXIT(VMX_PIN_BASED_VMEXEC_CTRL_EXTERNAL_INTERRUPT_VMEXIT)) {
VMCS_CACHE *vm = &BX_CPU_THIS_PTR vmcs;
if (! (vm->vmexit_ctrls & VMX_VMEXIT_CTRL1_INTA_ON_VMEXIT)) {
if (! (vm->vmexit_ctrls1 & VMX_VMEXIT_CTRL1_INTA_ON_VMEXIT)) {
// interrupt wasn't acknowledged and still pending, interruption info is invalid
VMwrite32(VMCS_32BIT_VMEXIT_INTERRUPTION_INFO, 0);
VMexit(VMX_VMEXIT_EXTERNAL_INTERRUPT, 0);
@ -738,11 +738,16 @@ void BX_CPU_C::Virtualization_Exception(Bit64u qualification, Bit64u guest_physi
// - CR0.PE is set
// - the logical processor is not in the process of delivering an event through the IDT
// - the 32 bits at offset 4 in the virtualization-exception information area are all 0
// - the EPT violation cause a shadow stack to become prematurely busy
if (! BX_CPU_THIS_PTR cr0.get_PE() || BX_CPU_THIS_PTR in_event) return;
VMCS_CACHE *vm = &BX_CPU_THIS_PTR vmcs;
#if BX_SUPPORT_CET
if (vm->shadow_stack_prematurely_busy) return;
#endif
BxMemtype ve_info_memtype = BX_MEMTYPE_INVALID;
#if BX_SUPPORT_MEMTYPE
ve_info_memtype = resolve_memtype(vm->ve_info_addr);
@ -769,7 +774,7 @@ void BX_CPU_C::Virtualization_Exception(Bit64u qualification, Bit64u guest_physi
exception(BX_VE_EXCEPTION, 0);
}
void BX_CPU_C::vmx_page_modification_logging(Bit64u guest_paddr, unsigned dirty_update)
void BX_CPU_C::vmx_page_modification_logging(Bit64u guest_laddr, Bit64u guest_paddr, unsigned dirty_update)
{
VMCS_CACHE *vm = &BX_CPU_THIS_PTR vmcs;
@ -778,6 +783,9 @@ void BX_CPU_C::vmx_page_modification_logging(Bit64u guest_paddr, unsigned dirty_
if (BX_CPU_THIS_PTR nmi_unblocking_iret)
vmexit_qualification |= (1 << 12);
if (vm->vmexit_ctrls2 & VMX_VMEXIT_CTRL2_SHADOW_STACK_BUSY_CTRL) {
VMwrite_natural(VMCS_GUEST_LINEAR_ADDR, guest_laddr);
}
VMexit(VMX_VMEXIT_PML_LOGFULL, vmexit_qualification);
}

View File

@ -2,7 +2,7 @@
// $Id$
/////////////////////////////////////////////////////////////////////////
//
// Copyright (c) 2009-2023 Stanislav Shwartsman
// Copyright (c) 2009-2024 Stanislav Shwartsman
// Written by Stanislav Shwartsman [sshwarts at sourceforge net]
//
// This library is free software; you can redistribute it and/or
@ -736,7 +736,7 @@ VMX_error_code BX_CPU_C::VMenterLoadCheckVmControls(void)
BX_ERROR(("VMFAIL: VMCS EXEC CTRL: posted interrupts must be enabled together with virtual interrupt delivery"));
return VMXERR_VMENTRY_INVALID_VM_CONTROL_FIELD;
}
if (! (vm->vmexit_ctrls & VMX_VMEXIT_CTRL1_INTA_ON_VMEXIT)) {
if (! (vm->vmexit_ctrls1 & VMX_VMEXIT_CTRL1_INTA_ON_VMEXIT)) {
BX_ERROR(("VMFAIL: VMCS EXEC CTRL: posted interrupts must be enabled together 'ack interrupt on exit'"));
return VMXERR_VMENTRY_INVALID_VM_CONTROL_FIELD;
}
@ -887,7 +887,11 @@ VMX_error_code BX_CPU_C::VMenterLoadCheckVmControls(void)
// Load VM-exit control fields to VMCS Cache
//
vm->vmexit_ctrls = VMread32(VMCS_32BIT_CONTROL_VMEXIT_CONTROLS);
vm->vmexit_ctrls1 = VMread32(VMCS_32BIT_CONTROL_VMEXIT_CONTROLS);
if (vm->vmexit_ctrls1 & VMX_VMEXIT_CTRL1_ACTIVATE_SECONDARY_CTRLS)
vm->vmexit_ctrls2 = VMread64(VMCS_64BIT_CONTROL_SECONDARY_VMEXIT_CONTROLS);
else
vm->vmexit_ctrls2 = 0;
vm->vmexit_msr_store_cnt = VMread32(VMCS_32BIT_CONTROL_VMEXIT_MSR_STORE_COUNT);
vm->vmexit_msr_load_cnt = VMread32(VMCS_32BIT_CONTROL_VMEXIT_MSR_LOAD_COUNT);
@ -895,17 +899,22 @@ VMX_error_code BX_CPU_C::VMenterLoadCheckVmControls(void)
// Check VM-exit control fields
//
if (~vm->vmexit_ctrls & VMX_CHECKS_USE_MSR_VMX_VMEXIT_CTRLS_LO) {
if (~vm->vmexit_ctrls1 & VMX_CHECKS_USE_MSR_VMX_VMEXIT_CTRLS_LO) {
BX_ERROR(("VMFAIL: VMCS EXEC CTRL: VMX vmexit controls allowed 0-settings"));
return VMXERR_VMENTRY_INVALID_VM_CONTROL_FIELD;
}
if (vm->vmexit_ctrls & ~VMX_CHECKS_USE_MSR_VMX_VMEXIT_CTRLS_HI) {
BX_ERROR(("VMFAIL: VMCS EXEC CTRL: VMX vmexit controls allowed 1-settings [0x%08x]", vm->vmexit_ctrls & ~VMX_CHECKS_USE_MSR_VMX_VMEXIT_CTRLS_HI));
if (vm->vmexit_ctrls1 & ~VMX_CHECKS_USE_MSR_VMX_VMEXIT_CTRLS_HI) {
BX_ERROR(("VMFAIL: VMCS EXEC CTRL: VMX vmexit controls allowed 1-settings [0x%08x]", vm->vmexit_ctrls1 & ~VMX_CHECKS_USE_MSR_VMX_VMEXIT_CTRLS_HI));
return VMXERR_VMENTRY_INVALID_VM_CONTROL_FIELD;
}
if (vm->vmexit_ctrls2 & ~VMX_VMEXIT_CTRL2_SUPPORTED_BITS) {
BX_ERROR(("VMFAIL: VMCS EXEC CTRL: VMX vmexit secondary controls allowed 1-settings"));
return VMXERR_VMENTRY_INVALID_VM_CONTROL_FIELD;
}
#if BX_SUPPORT_VMX >= 2
if ((~vm->pin_vmexec_ctrls & VMX_PIN_BASED_VMEXEC_CTRL_VMX_PREEMPTION_TIMER_VMEXIT) && (vm->vmexit_ctrls & VMX_VMEXIT_CTRL1_STORE_VMX_PREEMPTION_TIMER)) {
if ((~vm->pin_vmexec_ctrls & VMX_PIN_BASED_VMEXEC_CTRL_VMX_PREEMPTION_TIMER_VMEXIT) && (vm->vmexit_ctrls1 & VMX_VMEXIT_CTRL1_STORE_VMX_PREEMPTION_TIMER)) {
BX_ERROR(("VMFAIL: save_VMX_preemption_timer VMEXIT control is set but VMX_preemption_timer VMEXEC control is clear"));
return VMXERR_VMENTRY_INVALID_VM_CONTROL_FIELD;
}
@ -939,6 +948,13 @@ VMX_error_code BX_CPU_C::VMenterLoadCheckVmControls(void)
}
}
if (PIN_VMEXIT(VMX_PIN_BASED_VMEXEC_CTRL_PROCESS_POSTED_INTERRUPTS)) {
if (! (vm->vmexit_ctrls1 & VMX_VMEXIT_CTRL1_INTA_ON_VMEXIT)) {
BX_ERROR(("VMFAIL: VMCS EXEC CTRL: posted interrupts must be enabled together 'ack interrupt on exit'"));
return VMXERR_VMENTRY_INVALID_VM_CONTROL_FIELD;
}
}
//
// Load VM-entry control fields to VMCS Cache
//
@ -1095,7 +1111,7 @@ VMX_error_code BX_CPU_C::VMenterLoadCheckHostState(void)
// VM Host State Checks Related to Address-Space Size
//
Bit32u vmexit_ctrls = vm->vmexit_ctrls;
Bit32u vmexit_ctrls = vm->vmexit_ctrls1;
if (vmexit_ctrls & VMX_VMEXIT_CTRL1_HOST_ADDR_SPACE_SIZE) {
x86_64_host = true;
}
@ -2444,7 +2460,7 @@ void BX_CPU_C::VMexitSaveGuestState(Bit32u reason, Bit32u vector)
}
#endif
if (vm->vmexit_ctrls & VMX_VMEXIT_CTRL1_SAVE_DBG_CTRLS)
if (vm->vmexit_ctrls1 & VMX_VMEXIT_CTRL1_SAVE_DBG_CTRLS)
VMwrite_natural(VMCS_GUEST_DR7, BX_CPU_THIS_PTR dr7.get32());
VMwrite_natural(VMCS_GUEST_RIP, RIP);
@ -2521,10 +2537,10 @@ void BX_CPU_C::VMexitSaveGuestState(Bit32u reason, Bit32u vector)
VMwrite32(VMCS_32BIT_GUEST_IA32_SYSENTER_CS_MSR, BX_CPU_THIS_PTR msr.sysenter_cs_msr);
#if BX_SUPPORT_VMX >= 2
if (vm->vmexit_ctrls & VMX_VMEXIT_CTRL1_STORE_PAT_MSR)
if (vm->vmexit_ctrls1 & VMX_VMEXIT_CTRL1_STORE_PAT_MSR)
VMwrite64(VMCS_64BIT_GUEST_IA32_PAT, BX_CPU_THIS_PTR msr.pat.u64);
#if BX_SUPPORT_X86_64
if (vm->vmexit_ctrls & VMX_VMEXIT_CTRL1_STORE_EFER_MSR)
if (vm->vmexit_ctrls1 & VMX_VMEXIT_CTRL1_STORE_EFER_MSR)
VMwrite64(VMCS_64BIT_GUEST_IA32_EFER, BX_CPU_THIS_PTR efer.get32());
#endif
#endif
@ -2606,7 +2622,7 @@ void BX_CPU_C::VMexitSaveGuestState(Bit32u reason, Bit32u vector)
BX_CPU_THIS_PTR lapic->deactivate_vmx_preemption_timer();
clear_event(BX_EVENT_VMX_PREEMPTION_TIMER_EXPIRED);
// Store back to VMCS
if (vm->vmexit_ctrls & VMX_VMEXIT_CTRL1_STORE_VMX_PREEMPTION_TIMER)
if (vm->vmexit_ctrls1 & VMX_VMEXIT_CTRL1_STORE_VMX_PREEMPTION_TIMER)
VMwrite32(VMCS_32BIT_GUEST_PREEMPTION_TIMER_VALUE, BX_CPU_THIS_PTR lapic->read_vmx_preemption_timer());
if (vm->vmexec_ctrls2 & VMX_VM_EXEC_CTRL2_VIRTUAL_INT_DELIVERY) {
@ -2626,7 +2642,7 @@ void BX_CPU_C::VMexitLoadHostState(void)
BX_CPU_THIS_PTR tsc_offset = 0;
#if BX_SUPPORT_X86_64
Bit32u vmexit_ctrls = BX_CPU_THIS_PTR vmcs.vmexit_ctrls;
Bit32u vmexit_ctrls = BX_CPU_THIS_PTR vmcs.vmexit_ctrls1;
if (vmexit_ctrls & VMX_VMEXIT_CTRL1_HOST_ADDR_SPACE_SIZE) {
BX_DEBUG(("VMEXIT to x86-64 host"));
x86_64_host = true;
@ -2837,6 +2853,20 @@ void BX_CPU_C::VMexit(Bit32u reason, Bit64u qualification)
// STEP 0: Update VMEXIT reason
//
// Extra vmexit reason bits:
// [25] Shadow stack prematurely busy
// [26] VMM bus lock detection (not implemented yet)
// [27] Enclave mode (not supported)
// [28] Pending MTF vmexit (only set by SMM VMEXIT which not implemented)
// [29] VmExit from VMX root operation (hannpen only in SMM VMEXIT which not implemented)
#if BX_SUPPORT_CET
if (vm->vmexit_ctrls2 & VMX_VMEXIT_CTRL2_SHADOW_STACK_BUSY_CTRL) {
if (vm->shadow_stack_prematurely_busy)
reason |= (1<<25);
}
vm->shadow_stack_prematurely_busy = false;
#endif
VMwrite32(VMCS_32BIT_VMEXIT_REASON, reason);
VMwrite_natural(VMCS_VMEXIT_QUALIFICATION, qualification);
@ -4184,7 +4214,8 @@ void BX_CPU_C::register_vmx_state(bx_param_c *parent)
bx_list_c *vmexit_ctrls = new bx_list_c(vmcache, "VMEXIT_CTRLS");
BXRS_HEX_PARAM_FIELD(vmexit_ctrls, vmexit_ctrls, BX_CPU_THIS_PTR vmcs.vmexit_ctrls);
BXRS_HEX_PARAM_FIELD(vmexit_ctrls, vmexit_ctrls, BX_CPU_THIS_PTR vmcs.vmexit_ctrls1);
BXRS_HEX_PARAM_FIELD(vmexit_ctrls, vmexit_ctrls2, BX_CPU_THIS_PTR vmcs.vmexit_ctrls2);
BXRS_DEC_PARAM_FIELD(vmexit_ctrls, vmexit_msr_store_cnt, BX_CPU_THIS_PTR vmcs.vmexit_msr_store_cnt);
BXRS_HEX_PARAM_FIELD(vmexit_ctrls, vmexit_msr_store_addr, BX_CPU_THIS_PTR vmcs.vmexit_msr_store_addr);
BXRS_DEC_PARAM_FIELD(vmexit_ctrls, vmexit_msr_load_cnt, BX_CPU_THIS_PTR vmcs.vmexit_msr_load_cnt);

View File

@ -2,7 +2,7 @@
// $Id$
/////////////////////////////////////////////////////////////////////////
//
// Copyright (c) 2009-2023 Stanislav Shwartsman
// Copyright (c) 2009-2024 Stanislav Shwartsman
// Written by Stanislav Shwartsman [sshwarts at sourceforge net]
//
// This library is free software; you can redistribute it and/or
@ -708,7 +708,8 @@ typedef struct bx_VMX_Cap
Bit32u vmx_proc_vmexec_ctrl_supported_bits;
Bit32u vmx_vmexec_ctrl2_supported_bits;
Bit64u vmx_vmexec_ctrl3_supported_bits;
Bit32u vmx_vmexit_ctrl_supported_bits;
Bit32u vmx_vmexit_ctrl1_supported_bits;
Bit32u vmx_vmexit_ctrl2_supported_bits;
Bit32u vmx_vmentry_ctrl_supported_bits;
#if BX_SUPPORT_VMX >= 2
Bit64u vmx_ept_vpid_cap_supported_bits;
@ -886,6 +887,10 @@ typedef struct bx_VMCS
Bit64u xss_exiting_bitmap;
#endif
#if BX_SUPPORT_CET
bool shadow_stack_prematurely_busy;
#endif
//
// VM-Exit Control Fields
//
@ -907,11 +912,19 @@ typedef struct bx_VMCS
#define VMX_VMEXIT_CTRL1_LOAD_HOST_CET_STATE (1 << 28) /* CET */
#define VMX_VMEXIT_CTRL1_LOAD_HOST_PKRS (1 << 29) /* Supervisor-Mode Protection Keys */
#define VMX_VMEXIT_CTRL1_SAVE_PERF_GLOBAL_CTRL (1 << 30) /* Save IA32_PERF_GLOBAL_CTRL on vmexit (not implemented) */
#define VMX_VMEXIT_CTRL1_ACTIVATE_SECONDARY_CTRLS (1 << 31)
#define VMX_VMEXIT_CTRL1_SUPPORTED_BITS \
(BX_CPU_THIS_PTR vmx_cap.vmx_vmexit_ctrl_supported_bits)
(BX_CPU_THIS_PTR vmx_cap.vmx_vmexit_ctrl1_supported_bits)
Bit32u vmexit_ctrls;
Bit32u vmexit_ctrls1;
#define VMX_VMEXIT_CTRL2_SHADOW_STACK_BUSY_CTRL (1 << 2) /* Shadow stack prematurely busy */
#define VMX_VMEXIT_CTRL2_SUPPORTED_BITS \
(BX_CPU_THIS_PTR vmx_cap.vmx_vmexit_ctrl2_supported_bits)
Bit64u vmexit_ctrls2;
Bit32u vmexit_msr_store_cnt;
bx_phy_address vmexit_msr_store_addr;
@ -1266,6 +1279,13 @@ const Bit32u VMX_MSR_VMX_PROCBASED_CTRLS2_LO = 0x00000000;
#define VMX_MSR_VMX_PROCBASED_CTRLS3 (VMX_VM_EXEC_CTRL3_SUPPORTED_BITS)
// IA32_VMX_EXIT_CTLS2 MSR (0x493)
// ---------------------
// Allowed 1-settings
#define VMX_MSR_VMX_VMEXIT_CTRLS2 (VMX_VMEXIT_CTRL2_SUPPORTED_BITS)
#if BX_SUPPORT_VMX >= 2
// IA32_VMX_EPT_VPID_CAP MSR (0x48c)