MBE (Mode Based Execution Control) emulation (#22)
* MBE (Mode Based Execution Control) emulation
This commit is contained in:
parent
fb09790846
commit
f44f4ae753
@ -1,5 +1,10 @@
|
||||
Changes after 2.7:
|
||||
|
||||
The Bochs source tree is transitioning from SVN to GIT hosted on github.
|
||||
We welcome every new contributor !
|
||||
|
||||
- Bugfixes for CPU emulation correctness (MONITOR/MWAIT, VMX/SVM fixes)
|
||||
! Implemented VMX MBE (Mode Based Execution Control) emulation required for Windows 11 guest
|
||||
- Fixed Voodoo device compilation for big endian architectures
|
||||
- Fixed memory handling in volatile BIOS write support
|
||||
- Fixed APIC MWAIT timer activation
|
||||
|
@ -4532,7 +4532,7 @@ public: // for now...
|
||||
BX_SMF bx_phy_address translate_linear_long_mode(bx_address laddr, Bit32u &lpf_mask, Bit32u &pkey, unsigned user, unsigned rw);
|
||||
#endif
|
||||
#if BX_SUPPORT_VMX >= 2
|
||||
BX_SMF bx_phy_address translate_guest_physical(bx_phy_address guest_paddr, bx_address guest_laddr, bool guest_laddr_valid, bool is_page_walk, unsigned rw, bool supervisor_shadow_stack = false);
|
||||
BX_SMF bx_phy_address translate_guest_physical(bx_phy_address guest_paddr, bx_address guest_laddr, bool guest_laddr_valid, bool is_page_walk, unsigned user_page, unsigned rw, bool supervisor_shadow_stack = false);
|
||||
BX_SMF void update_ept_access_dirty(bx_phy_address *entry_addr, Bit64u *entry, BxMemtype eptptr_memtype, unsigned leaf, unsigned write);
|
||||
BX_SMF bool is_eptptr_valid(Bit64u eptptr);
|
||||
BX_SMF bool spp_walk(bx_phy_address guest_paddr, bx_address guest_laddr, BxMemtype memtype);
|
||||
|
@ -252,7 +252,7 @@ Bit32u corei3_cnl_t::get_vmx_extensions_bitmask(void) const
|
||||
BX_VMX_VMCS_SHADOWING |
|
||||
BX_VMX_EPT_EXCEPTION |
|
||||
BX_VMX_SW_INTERRUPT_INJECTION_ILEN_0 |
|
||||
/* BX_VMX_MBE_CONTROL - not implemeted yet */
|
||||
BX_VMX_MBE_CONTROL |
|
||||
BX_VMX_TSC_SCALING;
|
||||
}
|
||||
|
||||
|
@ -261,7 +261,7 @@ Bit32u corei7_icelake_t::get_vmx_extensions_bitmask(void) const
|
||||
BX_VMX_PML |
|
||||
BX_VMX_SW_INTERRUPT_INJECTION_ILEN_0 |
|
||||
/* BX_VMX_POSTED_INSTERRUPTS - not implemented yet */
|
||||
/* BX_VMX_MBE_CONTROL - not implemeted yet */
|
||||
BX_VMX_MBE_CONTROL |
|
||||
BX_VMX_SPP |
|
||||
BX_VMX_TSC_SCALING;
|
||||
}
|
||||
|
@ -249,7 +249,7 @@ Bit32u corei7_skylake_x_t::get_vmx_extensions_bitmask(void) const
|
||||
BX_VMX_SW_INTERRUPT_INJECTION_ILEN_0 |
|
||||
BX_VMX_PML |
|
||||
/* BX_VMX_POSTED_INSTERRUPTS - not implemented yet */
|
||||
/* BX_VMX_MBE_CONTROL - not implemeted yet */
|
||||
BX_VMX_MBE_CONTROL |
|
||||
BX_VMX_TSC_SCALING;
|
||||
}
|
||||
|
||||
|
@ -265,7 +265,7 @@ Bit32u tigerlake_t::get_vmx_extensions_bitmask(void) const
|
||||
BX_VMX_PML |
|
||||
BX_VMX_SW_INTERRUPT_INJECTION_ILEN_0 |
|
||||
/* BX_VMX_POSTED_INSTERRUPTS - not implemented yet */
|
||||
/* BX_VMX_MBE_CONTROL - not implemeted yet */
|
||||
BX_VMX_MBE_CONTROL |
|
||||
BX_VMX_SPP |
|
||||
BX_VMX_TSC_SCALING;
|
||||
}
|
||||
|
@ -173,6 +173,7 @@ typedef bx_cpuid_t* (*bx_create_cpuid_method)(BX_CPU_C *cpu);
|
||||
#define BX_VMX_SPP (1 << 23) /* Sub Page Protection */
|
||||
#define BX_VMX_TSC_SCALING (1 << 24) /* TSC Scaling */
|
||||
#define BX_VMX_SW_INTERRUPT_INJECTION_ILEN_0 (1 << 25) /* Allow software interrupt injection with instruction length 0 */
|
||||
#define BX_VMX_MBE_CONTROL (1 << 26) /* Mode-Based Execution Control (XU/XS) */
|
||||
|
||||
// CPUID defines - STD features CPUID[0x00000001].EDX
|
||||
// ----------------------------
|
||||
|
@ -554,6 +554,8 @@ enum {
|
||||
BX_COMBINED_GLOBAL_PAGE = 0x100,
|
||||
};
|
||||
|
||||
#define IS_USER_PAGE(combined_access) !!((combined_access) & BX_COMBINED_ACCESS_USER)
|
||||
|
||||
#if BX_CPU_LEVEL >= 6
|
||||
|
||||
// Format of a Long Mode Non-Leaf Entry
|
||||
@ -648,7 +650,7 @@ int BX_CPU_C::check_entry_PAE(const char *s, Bit64u entry, Bit64u reserved, unsi
|
||||
}
|
||||
|
||||
if (entry & reserved) {
|
||||
BX_DEBUG(("PAE %s: reserved bit is set 0x" FMT_ADDRX64, s, entry));
|
||||
BX_DEBUG(("PAE %s: reserved bit is set 0x" FMT_ADDRX64 "(reserved: " FMT_ADDRX64 ")", s, entry, entry & reserved));
|
||||
return ERROR_RESERVED | ERROR_PROTECTION;
|
||||
}
|
||||
|
||||
@ -707,7 +709,7 @@ bx_phy_address BX_CPU_C::translate_linear_long_mode(bx_address laddr, Bit32u &lp
|
||||
#if BX_SUPPORT_VMX >= 2
|
||||
if (BX_CPU_THIS_PTR in_vmx_guest) {
|
||||
if (SECONDARY_VMEXEC_CONTROL(VMX_VM_EXEC_CTRL3_EPT_ENABLE))
|
||||
entry_addr[leaf] = translate_guest_physical(entry_addr[leaf], laddr, 1, 1, BX_READ);
|
||||
entry_addr[leaf] = translate_guest_physical(entry_addr[leaf], laddr, true /* laddr_valid */, true /* page walk */, IS_USER_PAGE(combined_access), BX_READ);
|
||||
}
|
||||
#endif
|
||||
#if BX_SUPPORT_SVM
|
||||
@ -904,7 +906,7 @@ bool BX_CPP_AttrRegparmN(1) BX_CPU_C::CheckPDPTR(bx_phy_address cr3_val)
|
||||
#if BX_SUPPORT_VMX >= 2
|
||||
if (BX_CPU_THIS_PTR in_vmx_guest) {
|
||||
if (SECONDARY_VMEXEC_CONTROL(VMX_VM_EXEC_CTRL3_EPT_ENABLE))
|
||||
cr3_val = translate_guest_physical(cr3_val, 0, 0, 1, BX_READ);
|
||||
cr3_val = translate_guest_physical(cr3_val, 0, false /* laddr_valid */, true /* page walk */, 0, BX_READ);
|
||||
}
|
||||
#endif
|
||||
|
||||
@ -1003,7 +1005,7 @@ bx_phy_address BX_CPU_C::translate_linear_PAE(bx_address laddr, Bit32u &lpf_mask
|
||||
#if BX_SUPPORT_VMX >= 2
|
||||
if (BX_CPU_THIS_PTR in_vmx_guest) {
|
||||
if (SECONDARY_VMEXEC_CONTROL(VMX_VM_EXEC_CTRL3_EPT_ENABLE))
|
||||
entry_addr[leaf] = translate_guest_physical(entry_addr[leaf], laddr, 1, 1, BX_READ);
|
||||
entry_addr[leaf] = translate_guest_physical(entry_addr[leaf], laddr, true /* laddr_valid */, true /* page walk */, IS_USER_PAGE(combined_access), BX_READ);
|
||||
}
|
||||
#endif
|
||||
#if BX_SUPPORT_SVM
|
||||
@ -1144,7 +1146,7 @@ bx_phy_address BX_CPU_C::translate_linear_legacy(bx_address laddr, Bit32u &lpf_m
|
||||
#if BX_SUPPORT_VMX >= 2
|
||||
if (BX_CPU_THIS_PTR in_vmx_guest) {
|
||||
if (SECONDARY_VMEXEC_CONTROL(VMX_VM_EXEC_CTRL3_EPT_ENABLE))
|
||||
entry_addr[leaf] = translate_guest_physical(entry_addr[leaf], laddr, 1, 1, BX_READ);
|
||||
entry_addr[leaf] = translate_guest_physical(entry_addr[leaf], laddr, true /* laddr_valid */, true /* page walk */, IS_USER_PAGE(combined_access), BX_READ);
|
||||
}
|
||||
#endif
|
||||
#if BX_SUPPORT_SVM
|
||||
@ -1375,7 +1377,7 @@ bx_phy_address BX_CPU_C::translate_linear(bx_TLB_entry *tlbEntry, bx_address lad
|
||||
#if BX_SUPPORT_VMX >= 2
|
||||
if (BX_CPU_THIS_PTR in_vmx_guest) {
|
||||
if (SECONDARY_VMEXEC_CONTROL(VMX_VM_EXEC_CTRL3_EPT_ENABLE)) {
|
||||
paddress = translate_guest_physical(paddress, laddr, 1, 0, rw, isShadowStack & !user);
|
||||
paddress = translate_guest_physical(paddress, laddr, true /* laddr_valid */, false /* page walk */, IS_USER_PAGE(combined_access), rw, isShadowStack & !user);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@ -1857,7 +1859,10 @@ bx_phy_address BX_CPU_C::nested_walk(bx_phy_address guest_paddr, unsigned rw, bo
|
||||
enum {
|
||||
BX_EPT_READ = 0x01,
|
||||
BX_EPT_WRITE = 0x02,
|
||||
BX_EPT_EXECUTE = 0x04
|
||||
BX_EPT_EXECUTE = 0x04,
|
||||
|
||||
BX_EPT_MBE_SUPERVISOR_EXECUTE = BX_EPT_EXECUTE,
|
||||
BX_EPT_MBE_USER_EXECUTE = 0x400
|
||||
};
|
||||
|
||||
/* EPT access mask */
|
||||
@ -1900,7 +1905,7 @@ const Bit64u BX_SUPERVISOR_SHADOW_STACK_PAGE = (BX_CONST64(1) << 60);
|
||||
|
||||
const Bit64u PAGING_EPT_RESERVED_BITS = BX_PAGING_PHY_ADDRESS_RESERVED_BITS;
|
||||
|
||||
bx_phy_address BX_CPU_C::translate_guest_physical(bx_phy_address guest_paddr, bx_address guest_laddr, bool guest_laddr_valid, bool is_page_walk, unsigned rw, bool supervisor_shadow_stack)
|
||||
bx_phy_address BX_CPU_C::translate_guest_physical(bx_phy_address guest_paddr, bx_address guest_laddr, bool guest_laddr_valid, bool is_page_walk, unsigned user_page, unsigned rw, bool supervisor_shadow_stack)
|
||||
{
|
||||
VMCS_CACHE *vm = &BX_CPU_THIS_PTR vmcs;
|
||||
bx_phy_address entry_addr[4], ppf = LPFOf(vm->eptptr);
|
||||
@ -1912,8 +1917,10 @@ bx_phy_address BX_CPU_C::translate_guest_physical(bx_phy_address guest_paddr, bx
|
||||
BxMemtype eptptr_memtype = BX_CPU_THIS_PTR cr0.get_CD() ? (BX_MEMTYPE_UC) : BxMemtype(vm->eptptr & 0x7);
|
||||
#endif
|
||||
|
||||
Bit32u combined_access = 0x7, access_mask = 0;
|
||||
Bit64u offset_mask = BX_CONST64(0x0000ffffffffffff);
|
||||
Bit32u combined_access = 0x7, access_mask = 0;
|
||||
if (SECONDARY_VMEXEC_CONTROL(VMX_VM_EXEC_CTRL3_MBE_CTRL))
|
||||
combined_access |= BX_EPT_MBE_USER_EXECUTE;
|
||||
|
||||
BX_DEBUG(("EPT walk for guest paddr 0x" FMT_PHY_ADDRX, guest_paddr));
|
||||
|
||||
@ -1921,7 +1928,14 @@ bx_phy_address BX_CPU_C::translate_guest_physical(bx_phy_address guest_paddr, bx
|
||||
if (BX_VMX_EPT_ACCESS_DIRTY_ENABLED && is_page_walk && guest_laddr_valid)
|
||||
rw = BX_WRITE;
|
||||
|
||||
if (rw == BX_EXECUTE) access_mask |= BX_EPT_EXECUTE;
|
||||
if (rw == BX_EXECUTE) {
|
||||
if (SECONDARY_VMEXEC_CONTROL(VMX_VM_EXEC_CTRL3_MBE_CTRL)) {
|
||||
access_mask |= user_page ? BX_EPT_MBE_USER_EXECUTE : BX_EPT_MBE_SUPERVISOR_EXECUTE;
|
||||
}
|
||||
else {
|
||||
access_mask |= BX_EPT_EXECUTE;
|
||||
}
|
||||
}
|
||||
if (rw & 1) access_mask |= BX_EPT_WRITE; // write or r-m-w
|
||||
if ((rw & 3) == BX_READ) access_mask |= BX_EPT_READ; // handle correctly shadow stack reads
|
||||
|
||||
@ -1935,6 +1949,9 @@ bx_phy_address BX_CPU_C::translate_guest_physical(bx_phy_address guest_paddr, bx
|
||||
offset_mask >>= 9;
|
||||
Bit64u curr_entry = entry[leaf];
|
||||
Bit32u curr_access_mask = curr_entry & 0x7;
|
||||
if (SECONDARY_VMEXEC_CONTROL(VMX_VM_EXEC_CTRL3_MBE_CTRL)) {
|
||||
curr_access_mask |= (curr_entry & BX_EPT_MBE_USER_EXECUTE);
|
||||
}
|
||||
|
||||
if (curr_access_mask == BX_EPT_ENTRY_NOT_PRESENT) {
|
||||
BX_DEBUG(("EPT %s: not present", bx_paging_level[leaf]));
|
||||
@ -1942,8 +1959,8 @@ bx_phy_address BX_CPU_C::translate_guest_physical(bx_phy_address guest_paddr, bx
|
||||
break;
|
||||
}
|
||||
|
||||
if (curr_access_mask == BX_EPT_ENTRY_WRITE_ONLY || curr_access_mask == BX_EPT_ENTRY_WRITE_EXECUTE) {
|
||||
BX_DEBUG(("EPT %s: EPT misconfiguration mask=%d", bx_paging_level[leaf], curr_access_mask));
|
||||
if ((curr_access_mask & (BX_EPT_READ | BX_EPT_WRITE)) == BX_EPT_ENTRY_WRITE_ONLY) {
|
||||
BX_DEBUG(("EPT %s: EPT misconfiguration access_mask=%x", bx_paging_level[leaf], curr_access_mask));
|
||||
vmexit_reason = VMX_VMEXIT_EPT_MISCONFIGURATION;
|
||||
break;
|
||||
}
|
||||
@ -1957,7 +1974,7 @@ bx_phy_address BX_CPU_C::translate_guest_physical(bx_phy_address guest_paddr, bx
|
||||
}
|
||||
|
||||
if (curr_entry & PAGING_EPT_RESERVED_BITS) {
|
||||
BX_DEBUG(("EPT %s: reserved bit is set 0x" FMT_ADDRX64, bx_paging_level[leaf], curr_entry));
|
||||
BX_DEBUG(("EPT %s: reserved bit is set 0x" FMT_ADDRX64 "(reserved: " FMT_ADDRX64 ")", bx_paging_level[leaf], curr_entry, curr_entry & PAGING_EPT_RESERVED_BITS));
|
||||
vmexit_reason = VMX_VMEXIT_EPT_MISCONFIGURATION;
|
||||
break;
|
||||
}
|
||||
@ -2038,6 +2055,12 @@ bx_phy_address BX_CPU_C::translate_guest_physical(bx_phy_address guest_paddr, bx
|
||||
if (vmexit_reason == VMX_VMEXIT_EPT_VIOLATION) {
|
||||
combined_access &= entry[leaf];
|
||||
vmexit_qualification = access_mask | (combined_access << 3);
|
||||
if (SECONDARY_VMEXEC_CONTROL(VMX_VM_EXEC_CTRL3_MBE_CTRL) && (rw == BX_EXECUTE)) {
|
||||
vmexit_qualification &= (0x3f); // reset all bit bits beyond [5:0]
|
||||
vmexit_qualification |= (1<<2); // bit2 indicate the operation was instruction fetch
|
||||
if (combined_access & BX_EPT_MBE_USER_EXECUTE)
|
||||
vmexit_qualification |= (1<<6);
|
||||
}
|
||||
if (guest_laddr_valid) {
|
||||
vmexit_qualification |= (1<<7);
|
||||
if (! is_page_walk) vmexit_qualification |= (1<<8);
|
||||
@ -2193,7 +2216,7 @@ void dbg_print_paging_pte(int level, Bit64u entry)
|
||||
}
|
||||
|
||||
#if BX_SUPPORT_VMX >= 2
|
||||
void dbg_print_ept_paging_pte(int level, Bit64u entry)
|
||||
void dbg_print_ept_paging_pte(int level, Bit64u entry, bool mbe)
|
||||
{
|
||||
dbg_printf("EPT %4s: 0x%08x%08x", bx_paging_level[level], GET32H(entry), GET32L(entry));
|
||||
|
||||
@ -2202,8 +2225,11 @@ void dbg_print_ept_paging_pte(int level, Bit64u entry)
|
||||
else
|
||||
dbg_printf(" ");
|
||||
|
||||
if (mbe)
|
||||
dbg_printf(" %s", (entry & 0x400) ? "XU" : "xu");
|
||||
|
||||
dbg_printf(" %s %s %s",
|
||||
(entry & 0x04) ? "E" : "e",
|
||||
(entry & 0x04) ? "X" : "x",
|
||||
(entry & 0x02) ? "W" : "w",
|
||||
(entry & 0x01) ? "R" : "r");
|
||||
|
||||
@ -2234,7 +2260,7 @@ bool BX_CPU_C::dbg_translate_guest_physical(bx_phy_address guest_paddr, bx_phy_a
|
||||
BX_MEM(0)->readPhysicalPage(BX_CPU_THIS, pt_address, 8, &pte);
|
||||
#if BX_DEBUGGER
|
||||
if (verbose)
|
||||
dbg_print_ept_paging_pte(level, pte);
|
||||
dbg_print_ept_paging_pte(level, pte, SECONDARY_VMEXEC_CONTROL(VMX_VM_EXEC_CTRL3_MBE_CTRL));
|
||||
#endif
|
||||
switch(pte & 7) {
|
||||
case BX_EPT_ENTRY_NOT_PRESENT:
|
||||
|
@ -712,7 +712,7 @@ void BX_CPU_C::init_secondary_proc_based_vmexec_ctrls(void)
|
||||
// [19] Reserved (must be '0)
|
||||
// [20] XSAVES Exiting
|
||||
// [21] Reserved (must be '0)
|
||||
// [22] Mode Based Execution Control (MBE) (not implemented yet)
|
||||
// [22] Mode Based Execution Control (MBE)
|
||||
// [23] Sub Page Protection
|
||||
// [24] Reserved (must be '0)
|
||||
// [25] Enable TSC Scaling
|
||||
@ -775,6 +775,11 @@ void BX_CPU_C::init_secondary_proc_based_vmexec_ctrls(void)
|
||||
}
|
||||
#endif
|
||||
#if BX_SUPPORT_VMX >= 2
|
||||
if (BX_SUPPORT_VMX_EXTENSION(BX_VMX_MBE_CONTROL)) {
|
||||
if (! BX_SUPPORT_VMX_EXTENSION(BX_VMX_EPT))
|
||||
BX_PANIC(("VMX MBE feature requires EPT support !"));
|
||||
cap->vmx_vmexec_ctrl2_supported_bits |= VMX_VM_EXEC_CTRL3_MBE_CTRL;
|
||||
}
|
||||
if (BX_SUPPORT_VMX_EXTENSION(BX_VMX_SPP)) {
|
||||
if (! BX_SUPPORT_VMX_EXTENSION(BX_VMX_EPT))
|
||||
BX_PANIC(("VMX SPP feature requires EPT support !"));
|
||||
|
@ -827,6 +827,13 @@ VMX_error_code BX_CPU_C::VMenterLoadCheckVmControls(void)
|
||||
}
|
||||
}
|
||||
|
||||
if (vm->vmexec_ctrls3 & VMX_VM_EXEC_CTRL3_MBE_CTRL) {
|
||||
if ((vm->vmexec_ctrls3 & VMX_VM_EXEC_CTRL3_EPT_ENABLE) == 0) {
|
||||
BX_ERROR(("VMFAIL: VMCS EXEC CTRL: MBE is enabled without EPT"));
|
||||
return VMXERR_VMENTRY_INVALID_VM_CONTROL_FIELD;
|
||||
}
|
||||
}
|
||||
|
||||
if (vm->vmexec_ctrls3 & VMX_VM_EXEC_CTRL3_XSAVES_XRSTORS)
|
||||
vm->xss_exiting_bitmap = VMread64(VMCS_64BIT_CONTROL_XSS_EXITING_BITMAP);
|
||||
else
|
||||
|
@ -746,7 +746,7 @@ typedef struct bx_VMCS
|
||||
#define VMX_VM_EXEC_CTRL3_EPT_VIOLATION_EXCEPTION (1 << 18) /* #VE Exception */
|
||||
#define VMX_VM_EXEC_CTRL3_SUPPRESS_GUEST_VMX_TRACE (1 << 19) /* Processor Trace (not implemented) */
|
||||
#define VMX_VM_EXEC_CTRL3_XSAVES_XRSTORS (1 << 20) /* XSAVES */
|
||||
#define VMX_VM_EXEC_CTRL3_MBE_CTRL (1 << 22) /* Mode Based Execution Control (not implemented yet) */
|
||||
#define VMX_VM_EXEC_CTRL3_MBE_CTRL (1 << 22) /* Mode Based Execution Control */
|
||||
#define VMX_VM_EXEC_CTRL3_SUBPAGE_WR_PROTECT_CTRL (1 << 23) /* Sub-Page Write Protection Control */
|
||||
#define VMX_VM_EXEC_CTRL3_PROCESSOR_TRACE_USE_GPA (1 << 24) /* Processor Trace (not implemented) */
|
||||
#define VMX_VM_EXEC_CTRL3_TSC_SCALING (1 << 25) /* TSC Scaling */
|
||||
|
Loading…
Reference in New Issue
Block a user