Implement support for LA57 and 5-level paging

This commit is contained in:
Stanislav Shwartsman 2023-12-29 14:38:00 +02:00
parent 157d3a9189
commit e3612c30f8
10 changed files with 75 additions and 30 deletions

View File

@ -8,9 +8,10 @@ Brief summary :
! Implemented VMX MBE (Mode Based Execution Control) emulation required for Windows 11 guest
! Implemented Posted-Interrupt Processing VMX extension emulation
! Implemented Linear Address Separation (LASS) extension
! Implemented 57-bit Linear Address and 5-Level Paging support
! Implemented User-Level Interrupt (UINTR) extension
! Implemented recently published Intel instruction sets:
- MOVDIRI/MOVDIR64B, AVX512 BF16, AVX IFMA52, VNNI-INT8/VNNI-INT16, AVX-NE-CONVERT, CMPCCXADD, SM3, SM4, SHA512, WRMSRNS, MSRLIST, WAITPKG, SERIALIZE
- MOVDIRI/MOVDIR64B, AVX512 BF16, AVX IFMA52, VNNI-INT8/VNNI-INT16, AVX-NE-CONVERT, CMPCCXADD, SM3/SM4, SHA512, WRMSRNS, MSRLIST, WAITPKG, SERIALIZE
- Improved 64-bit guest support in Bochs internal debugger, added new internal debugger commands
- Bochs debugger enhanced with new commands (setpmem, loadmem, deref, ...)
Enhanced magic breakpoint capabilities. Refer to user documentation for more details.
@ -35,9 +36,10 @@ Detailed change log :
- Implemented VMX MBE (Mode Based Execution Control) emulation required for Windows 11 guest
- Implemented Posted-Interrupt Processing VMX extension emulation
- Implemented Linear Address Separation (LASS) extension
- Implemented 57-bit Linear Address and 5-Level Paging support
- Implemented User-Level Interrupt (UINTR) extension
- Implemented recently published Intel instruction sets:
- MOVDIRI/MOVDIR64B, AVX512 BF16, AVX IFMA52, VNNI-INT8/VNNI-INT16, AVX-NE-CONVERT, CMPCCXADD, SM3, SM4, SHA512, WRMSRNS, MSRLIST, WAITPKG, SERIALIZE
- MOVDIRI/MOVDIR64B, AVX512 BF16, AVX IFMA52, VNNI-INT8/VNNI-INT16, AVX-NE-CONVERT, CMPCCXADD, SM3/SM4, SHA512, WRMSRNS, MSRLIST, WAITPKG, SERIALIZE
- Bochs Debugger and Instrumentation
- Updated Bochs instrumentation examples for new disassembler introduced in Bochs 2.7 release.

View File

@ -717,10 +717,12 @@ void bx_dbg_phy_memory_access(unsigned cpu, bx_phy_address phy, unsigned len, un
"PDE",
"PDPTE",
"PML4E",
"PML5E",
"EPT PTE",
"EPT PDE",
"EPT PDPTE",
"EPT PML4E",
"EPT PML5E", // place holder
"EPT SPP PTE",
"EPT SPP PDE",
"EPT SPP PDPTE",

View File

@ -475,12 +475,6 @@ typedef Bit32u bx_address;
// define physical and linear address types
typedef bx_address bx_lin_address;
#if BX_SUPPORT_X86_64
#define BX_LIN_ADDRESS_WIDTH 48
#else
#define BX_LIN_ADDRESS_WIDTH 32
#endif
#if BX_PHY_ADDRESS_LONG
typedef Bit64u bx_phy_address;
#if BX_CPU_LEVEL == 5

View File

@ -284,10 +284,12 @@ enum AccessReason {
BX_PDE_ACCESS,
BX_PDTE_ACCESS,
BX_PML4E_ACCESS,
BX_PML5E_ACCESS,
BX_EPT_PTE_ACCESS,
BX_EPT_PDE_ACCESS,
BX_EPT_PDTE_ACCESS,
BX_EPT_PML4E_ACCESS,
BX_EPT_PML5E_ACCESS, // place holder
BX_EPT_SPP_PTE_ACCESS,
BX_EPT_SPP_PDE_ACCESS,
BX_EPT_SPP_PDTE_ACCESS,
@ -357,10 +359,13 @@ const unsigned BX_MSR_MAX_INDEX = 0x1000;
extern const char* cpu_mode_string(unsigned cpu_mode);
#if BX_SUPPORT_X86_64
BX_CPP_INLINE bool IsCanonical(bx_address offset)
BX_CPP_INLINE bool IsCanonicalToWidth(bx_address addr, unsigned LIN_ADDRESS_WIDTH)
{
return ((Bit64u)((((Bit64s)(offset)) >> (BX_LIN_ADDRESS_WIDTH-1)) + 1) < 2);
return ((Bit64u)((((Bit64s)(addr)) >> (LIN_ADDRESS_WIDTH-1)) + 1) < 2);
}
BX_CPP_INLINE bool IsCanonical48(bx_address addr) { return IsCanonicalToWidth(addr, 48); }
BX_CPP_INLINE bool IsCanonical57(bx_address addr) { return IsCanonicalToWidth(addr, 57); }
#endif
BX_CPP_INLINE bool IsValidPhyAddr(bx_phy_address addr)
@ -944,6 +949,9 @@ public: // for now...
#if BX_CPU_LEVEL >= 5
bx_cr4_t cr4;
Bit32u cr4_suppmask;
#if BX_SUPPORT_X86_64
unsigned linaddr_width;
#endif
bx_efer_t efer;
Bit32u efer_suppmask;
@ -4147,6 +4155,10 @@ public: // for now...
BX_CPU_THIS_PTR espPageWindowSize = 0;
}
#if BX_SUPPORT_X86_64
BX_SMF BX_CPP_INLINE bool IsCanonical(bx_address addr) { return IsCanonicalToWidth(addr, BX_CPU_THIS_PTR linaddr_width); }
#endif
BX_SMF bool write_virtual_checks(bx_segment_reg_t *seg, Bit32u offset, unsigned len, bool align = false) BX_CPP_AttrRegparmN(4);
BX_SMF bool read_virtual_checks(bx_segment_reg_t *seg, Bit32u offset, unsigned len, bool align = false) BX_CPP_AttrRegparmN(4);
BX_SMF bool execute_virtual_checks(bx_segment_reg_t *seg, Bit32u offset, unsigned len) BX_CPP_AttrRegparmN(3);

View File

@ -927,7 +927,13 @@ Bit32u bx_cpuid_t::get_std_cpuid_leaf_7_ecx(Bit32u extra) const
#endif
// [15:15] reserved
// [16:16] LA57: LA57 and 5-level paging - not supported
// [16:16] LA57: LA57 and 5-level paging
#if BX_SUPPORT_X86_64
if (is_cpu_extension_supported(BX_ISA_LA57))
ecx |= BX_CPUID_STD7_SUBLEAF0_ECX_LA57;
#endif
// [17:17] reserved
// [18:18] reserved
// [19:19] reserved
@ -1152,7 +1158,8 @@ void bx_cpuid_t::get_ext_cpuid_leaf_8(cpuid_function_t *leaf) const
// virtual & phys address size in low 2 bytes of EAX.
// TODO: physical address width should be 32-bit when no PSE-36 is supported
Bit32u phy_addr_width = BX_PHY_ADDRESS_WIDTH;
Bit32u lin_addr_width = is_cpu_extension_supported(BX_ISA_LONG_MODE) ? BX_LIN_ADDRESS_WIDTH : 32;
Bit32u lin_addr_width = is_cpu_extension_supported(BX_ISA_LONG_MODE) ?
(is_cpu_extension_supported(BX_ISA_LA57) ? 57 : 48) : 32;
leaf->eax = phy_addr_width | (lin_addr_width << 8);

View File

@ -1152,6 +1152,10 @@ bool BX_CPU_C::SetCR0(bxInstruction_c *i, bx_address val)
#endif
}
#if BX_SUPPORT_X86_64
BX_CPU_THIS_PTR linaddr_width = BX_CPU_THIS_PTR cr4.get_LA57() ? 57 : 48;
#endif
return true;
}
@ -1177,7 +1181,7 @@ Bit32u BX_CPU_C::get_cr4_allow_mask(void)
// [15] Reserved, Must be Zero
// [14] SMXE: SMX Extensions R/W
// [13] VMXE: VMX Extensions R/W
// [12] Reserved, Must be Zero
// [12] LA57, 57-bit Linear Address and 5-level paging
// [11] UMIP: User Mode Instruction Prevention R/W
// [10] OSXMMEXCPT: Operating System Unmasked Exception Support R/W
// [9] OSFXSR: Operating System FXSAVE/FXRSTOR Support R/W
@ -1260,6 +1264,11 @@ Bit32u BX_CPU_C::get_cr4_allow_mask(void)
if (is_cpu_extension_supported(BX_ISA_UMIP))
allowMask |= BX_CR4_UMIP_MASK;
#if BX_SUPPORT_X86_64
if (is_cpu_extension_supported(BX_ISA_LA57))
allowMask |= BX_CR4_LA57_MASK;
#endif
#if BX_SUPPORT_CET
if (is_cpu_extension_supported(BX_ISA_CET))
allowMask |= BX_CR4_CET_MASK;
@ -1299,6 +1308,11 @@ bool BX_CPP_AttrRegparmN(1) BX_CPU_C::check_CR4(bx_address cr4_val)
BX_ERROR(("check_CR4(): attempt to clear CR4.PAE when EFER.LMA=1"));
return false;
}
if(! temp_cr4.get_LA57()) {
BX_ERROR(("check_CR4(): attempt to clear CR4.LA57 when EFER.LMA=1"));
return false;
}
}
else {
if (temp_cr4.get_PCIDE()) {
@ -1388,6 +1402,10 @@ bool BX_CPU_C::SetCR4(bxInstruction_c *i, bx_address val)
set_PKeys(BX_CPU_THIS_PTR pkru, BX_CPU_THIS_PTR pkrs);
#endif
#if BX_SUPPORT_X86_64
BX_CPU_THIS_PTR linaddr_width = BX_CPU_THIS_PTR cr4.get_LA57() ? 57 : 48;
#endif
return true;
}
#endif // BX_CPU_LEVEL >= 5

View File

@ -153,7 +153,7 @@ struct bx_cr4_t {
BX_CPP_INLINE void set32(Bit32u val) { val32 = val; }
};
const Bit32u BX_CR4_FLUSH_TLB_MASK = (BX_CR4_PSE_MASK | BX_CR4_PAE_MASK | BX_CR4_PGE_MASK | BX_CR4_PCIDE_MASK | BX_CR4_SMEP_MASK | BX_CR4_SMAP_MASK | BX_CR4_PKE_MASK | BX_CR4_CET_MASK | BX_CR4_PKS_MASK | BX_CR4_LASS_MASK);
const Bit32u BX_CR4_FLUSH_TLB_MASK = (BX_CR4_PSE_MASK | BX_CR4_PAE_MASK | BX_CR4_PGE_MASK | BX_CR4_LA57_MASK | BX_CR4_PCIDE_MASK | BX_CR4_SMEP_MASK | BX_CR4_SMAP_MASK | BX_CR4_PKE_MASK | BX_CR4_CET_MASK | BX_CR4_PKS_MASK | BX_CR4_LASS_MASK);
#endif // #if BX_CPU_LEVEL >= 5
@ -398,7 +398,7 @@ typedef struct msr {
switch(type) {
#if BX_SUPPORT_X86_64
case BX_LIN_ADDRESS_MSR:
if (! IsCanonical(new_val)) return 0;
if (! IsCanonical48(new_val)) return 0;
break;
#endif
case BX_PHY_ADDRESS_MSR:

View File

@ -140,6 +140,7 @@ x86_feature(BX_ISA_WRMSRNS, "wrmsrns") /* Non-S
x86_feature(BX_ISA_CMPCCXADD, "cmpccxadd") /* CMPccXADD instructions */
x86_feature(BX_ISA_SERIALIZE, "serialize") /* SERIALIZE instruction */
x86_feature(BX_ISA_LASS, "lass") /* Linear Address Space Separation support */
x86_feature(BX_ISA_LA57, "la57") /* 57-bit Virtual Address and 5-level paging support */
x86_feature(BX_ISA_UINTR, "uintr") /* User Level Interrupts support */
x86_feature(BX_ISA_MOVDIRI, "movdiri") /* MOVDIRI instruction support */
x86_feature(BX_ISA_MOVDIR64B, "movdir64b") /* MOVDIR64B instruction support */

View File

@ -688,6 +688,10 @@ void BX_CPU_C::after_restore_state(void)
set_PKeys(BX_CPU_THIS_PTR pkru, BX_CPU_THIS_PTR pkrs);
#endif
#if BX_SUPPORT_X86_64
BX_CPU_THIS_PTR linaddr_width = BX_CPU_THIS_PTR cr4.get_LA57() ? 57 : 48;
#endif
handleCpuContextChange();
assert_checks();
@ -895,6 +899,9 @@ void BX_CPU_C::reset(unsigned source)
#if BX_CPU_LEVEL >= 5
BX_CPU_THIS_PTR cr4.set32(0);
BX_CPU_THIS_PTR cr4_suppmask = get_cr4_allow_mask();
#if BX_SUPPORT_X86_64
BX_CPU_THIS_PTR linaddr_width = 48;
#endif
#endif
#if BX_CPU_LEVEL >= 6

View File

@ -533,13 +533,14 @@ void BX_CPU_C::page_fault(unsigned fault, bx_address laddr, unsigned user, unsig
}
enum {
BX_LEVEL_PML5 = 4,
BX_LEVEL_PML4 = 3,
BX_LEVEL_PDPTE = 2,
BX_LEVEL_PDE = 1,
BX_LEVEL_PTE = 0
};
static const char *bx_paging_level[4] = { "PTE", "PDE", "PDPE", "PML4" }; // keep it 4 letters
static const char *bx_paging_level[5] = { "PTE", "PDE", "PDPE", "PML4", "PML5" }; // keep it 4 letters
// combined_access legend:
// -----------------------
@ -697,9 +698,8 @@ bx_phy_address BX_CPU_C::translate_linear_long_mode(bx_address laddr, Bit32u &lp
BxMemtype entry_memtype[4] = { 0 };
bool nx_fault = false;
int leaf;
Bit64u offset_mask = BX_CONST64(0x0000ffffffffffff);
Bit64u offset_mask = ((BX_CONST64(1) << BX_CPU_THIS_PTR linaddr_width) - 1);
lpf_mask = 0xfff;
Bit32u combined_access = (BX_COMBINED_ACCESS_WRITE | BX_COMBINED_ACCESS_USER);
Bit64u curr_entry = BX_CPU_THIS_PTR cr3;
@ -708,7 +708,9 @@ bx_phy_address BX_CPU_C::translate_linear_long_mode(bx_address laddr, Bit32u &lp
if (! BX_CPU_THIS_PTR efer.get_NXE())
reserved |= PAGE_DIRECTORY_NX_BIT;
for (leaf = BX_LEVEL_PML4;; --leaf) {
int start_leaf = BX_CPU_THIS_PTR cr4.get_LA57() ? BX_LEVEL_PML5 : BX_LEVEL_PML4, leaf = start_leaf;
for (;; --leaf) {
entry_addr[leaf] = ppf + ((laddr >> (9 + 9*leaf)) & 0xff8);
#if BX_SUPPORT_VMX >= 2
if (BX_CPU_THIS_PTR in_vmx_guest) {
@ -854,7 +856,7 @@ bx_phy_address BX_CPU_C::translate_linear_long_mode(bx_address laddr, Bit32u &lp
#endif
// Update A/D bits if needed
update_access_dirty_PAE(entry_addr, entry, entry_memtype, BX_LEVEL_PML4, leaf, isWrite);
update_access_dirty_PAE(entry_addr, entry, entry_memtype, start_leaf, leaf, isWrite);
return (ppf | combined_access);
}
@ -1654,19 +1656,20 @@ bx_phy_address BX_CPU_C::nested_walk_long_mode(bx_phy_address guest_paddr, unsig
Bit64u entry[4];
BxMemtype entry_memtype[4] = { BX_MEMTYPE_INVALID };
bool nx_fault = false;
int leaf;
SVM_CONTROLS *ctrls = &BX_CPU_THIS_PTR vmcb.ctrls;
SVM_HOST_STATE *host_state = &BX_CPU_THIS_PTR vmcb.host_state;
bx_phy_address ppf = ctrls->ncr3 & BX_CR3_PAGING_MASK;
Bit64u offset_mask = BX_CONST64(0x0000ffffffffffff);
Bit64u offset_mask = ((BX_CONST64(1) << BX_CPU_THIS_PTR linaddr_width) - 1);
unsigned combined_access = BX_COMBINED_ACCESS_WRITE | BX_COMBINED_ACCESS_USER;
Bit64u reserved = PAGING_PAE_RESERVED_BITS;
if (! host_state->efer.get_NXE())
reserved |= PAGE_DIRECTORY_NX_BIT;
for (leaf = BX_LEVEL_PML4;; --leaf) {
int start_leaf = BX_CPU_THIS_PTR cr4.get_LA57() ? BX_LEVEL_PML5 : BX_LEVEL_PML4, leaf = start_leaf;
for (;; --leaf) {
entry_addr[leaf] = ppf + ((guest_paddr >> (9 + 9*leaf)) & 0xff8);
entry[leaf] = read_physical_qword(entry_addr[leaf], BX_MEMTYPE_INVALID, AccessReason(BX_PTE_ACCESS + leaf));
offset_mask >>= 9;
@ -1705,7 +1708,7 @@ bx_phy_address BX_CPU_C::nested_walk_long_mode(bx_phy_address guest_paddr, unsig
nested_page_fault(ERROR_PROTECTION, guest_paddr, rw, is_page_walk);
// Update A/D bits if needed
update_access_dirty_PAE(entry_addr, entry, entry_memtype, BX_LEVEL_PML4, leaf, isWrite);
update_access_dirty_PAE(entry_addr, entry, entry_memtype, start_leaf, leaf, isWrite);
// Make up the physical page frame address
return ppf | (bx_phy_address)(guest_paddr & offset_mask);
@ -2268,7 +2271,7 @@ bool BX_CPU_C::dbg_translate_guest_physical_ept(bx_phy_address guest_paddr, bx_p
bx_phy_address pt_address = LPFOf(vm->eptptr);
Bit64u offset_mask = BX_CONST64(0x0000ffffffffffff);
for (int level = 3; level >= 0; --level) {
for (int level = BX_LEVEL_PML4; level >= 0; --level) {
Bit64u pte;
pt_address += ((guest_paddr >> (9 + 9*level)) & 0xff8);
offset_mask >>= 9;
@ -2335,9 +2338,8 @@ bool BX_CPU_C::dbg_xlate_linear2phy(bx_address laddr, bx_phy_address *phy, bx_ad
#if BX_CPU_LEVEL >= 6
if (BX_CPU_THIS_PTR cr4.get_PAE()) {
offset_mask = BX_CONST64(0x0000ffffffffffff);
int level = 3;
int level = BX_CPU_THIS_PTR cr4.get_LA57() ? BX_LEVEL_PML5 : BX_LEVEL_PML4;
offset_mask = ((BX_CONST64(1) << BX_CPU_THIS_PTR linaddr_width) - 1);
if (! long_mode()) {
pt_address = BX_CPU_THIS_PTR PDPTR_CACHE.entry[(laddr >> 30) & 3];
if (! (pt_address & 0x1)) {
@ -2346,7 +2348,7 @@ bool BX_CPU_C::dbg_xlate_linear2phy(bx_address laddr, bx_phy_address *phy, bx_ad
}
offset_mask >>= 18;
pt_address &= BX_CONST64(0x000ffffffffff000);
level = 1;
level = BX_LEVEL_PDE;
}
for (; level >= 0; --level) {
@ -2394,7 +2396,7 @@ bool BX_CPU_C::dbg_xlate_linear2phy(bx_address laddr, bx_phy_address *phy, bx_ad
#endif
{
offset_mask = 0xfff;
for (int level = 1; level >= 0; --level) {
for (int level = BX_LEVEL_PDE; level >= 0; --level) {
Bit32u pte;
pt_address += ((laddr >> (10 + 10*level)) & 0xffc);
#if BX_SUPPORT_VMX >= 2