From a3d5073a2b622b98e5fd942f06a9aea66b075f5c Mon Sep 17 00:00:00 2001 From: Jonathan Campbell Date: Sun, 15 Dec 2024 08:51:31 -0800 Subject: [PATCH] Begin work on PSE (Page Size Extension) emulation. Limit the enable only to Pentium or higher. Reportedly Intel added it to the original Pentium but did not document it until the Pentium Pro or Pentium II --- include/paging.h | 46 ++++++++++++------------- src/cpu/cpu.cpp | 18 ++++++++++ src/cpu/paging.cpp | 83 ++++++++++++++++++++++++++++------------------ src/dosbox.cpp | 3 ++ 4 files changed, 95 insertions(+), 55 deletions(-) diff --git a/include/paging.h b/include/paging.h index 2ac7a084d1e..047ff498bba 100644 --- a/include/paging.h +++ b/include/paging.h @@ -223,31 +223,31 @@ void MEM_SetPageHandler(Bitu phys_page, Bitu pages, PageHandler * handler); #ifdef _MSC_VER #pragma pack (1) #endif -struct X86_PageEntryBlock{ +struct X86_PageEntryBlock{ // TODO: This is not exactly a page table entry, this is a mismash of both page table and page directory entry #ifdef WORDS_BIGENDIAN - uint32_t base:20; - uint32_t avl:3; - uint32_t g:1; - uint32_t pat:1; - uint32_t d:1; - uint32_t a:1; - uint32_t pcd:1; - uint32_t pwt:1; - uint32_t us:1; - uint32_t wr:1; - uint32_t p:1; + uint32_t base:20; // [31:12] PTE+PDE + uint32_t avl:3; // [11: 9] PTE=[11:9] PDE=[11:8] + uint32_t g:1; // [ 8: 8] PDE + uint32_t pat:1; // [ 7: 7] PTE PDE=PS(PageSize) + uint32_t d:1; // [ 6: 6] PTE PDE=AVL + uint32_t a:1; // [ 5: 5] PTE+PDE + uint32_t pcd:1; // [ 4: 4] PTE+PDE + uint32_t pwt:1; // [ 3: 3] PTE+PDE + uint32_t us:1; // [ 2: 2] PTE+PDE + uint32_t wr:1; // [ 1: 1] PTE+PDE + uint32_t p:1; // [ 0: 0] PTE+PDE #else - uint32_t p:1; - uint32_t wr:1; - uint32_t us:1; - uint32_t pwt:1; - uint32_t pcd:1; - uint32_t a:1; - uint32_t d:1; - uint32_t pat:1; - uint32_t g:1; - uint32_t avl:3; - uint32_t base:20; + uint32_t p:1; // [ 0: 0] + uint32_t wr:1; // [ 1: 1] + uint32_t us:1; // [ 2: 2] + uint32_t pwt:1; // [ 3: 3] + uint32_t pcd:1; // [ 4: 4] + uint32_t a:1; // [ 5: 5] + uint32_t d:1; // [ 6: 6] + uint32_t pat:1; // [ 7: 7] + uint32_t g:1; // [ 8: 8] + uint32_t avl:3; // [11: 9] + uint32_t base:20; // [31:12] #endif } GCC_ATTRIBUTE(packed); #ifdef _MSC_VER diff --git a/src/cpu/cpu.cpp b/src/cpu/cpu.cpp index 94fa427ef3a..50a993e7221 100644 --- a/src/cpu/cpu.cpp +++ b/src/cpu/cpu.cpp @@ -77,6 +77,9 @@ bool CPU_NMI_active = false; bool CPU_NMI_pending = false; bool do_seg_limits = false; +bool do_pse = false; +bool enable_pse = false; + bool enable_fpu = true; bool enable_msr = true; bool enable_syscall = true; @@ -2608,6 +2611,7 @@ void CPU_SET_CRX(Bitu cr,Bitu value) { PAGING_SetDirBase(value); break; case 4: + if (enable_pse) do_pse = !!(value & 0x10); cpu.cr4=value; break; default: @@ -3084,6 +3088,7 @@ bool CPU_CPUID(void) { reg_ecx=0; /* No features */ reg_edx=0x00000010|(enable_fpu?1:0); /* FPU+TimeStamp/RDTSC */ if (enable_msr) reg_edx |= 0x20; /* ModelSpecific/MSR */ + if (enable_pse) reg_edx |= 0x08; /* Page Size Extension */ if (enable_cmpxchg8b) reg_edx |= 0x100; /* CMPXCHG8B */ } else if (CPU_ArchitectureType == CPU_ARCHTYPE_PMMXSLOW) { reg_eax=0x543; /* intel pentium mmx (PMMX) */ @@ -3091,6 +3096,7 @@ bool CPU_CPUID(void) { reg_ecx=0; /* No features */ reg_edx=0x00800010|(enable_fpu?1:0); /* FPU+TimeStamp/RDTSC+MMX+ModelSpecific/MSR */ if (enable_msr) reg_edx |= 0x20; /* ModelSpecific/MSR */ + if (enable_pse) reg_edx |= 0x08; /* Page Size Extension */ if (enable_cmpxchg8b) reg_edx |= 0x100; /* CMPXCHG8B */ } else if (CPU_ArchitectureType == CPU_ARCHTYPE_PPROSLOW) { reg_eax=0x612; /* intel pentium pro */ @@ -3098,6 +3104,7 @@ bool CPU_CPUID(void) { reg_ecx=0; /* No features */ reg_edx=0x00008011; /* FPU+TimeStamp/RDTSC */ if (enable_msr) reg_edx |= 0x20; /* ModelSpecific/MSR */ + if (enable_pse) reg_edx |= 0x08; /* Page Size Extension */ if (enable_cmpxchg8b) reg_edx |= 0x100; /* CMPXCHG8B */ } else if (CPU_ArchitectureType == CPU_ARCHTYPE_PENTIUMII) { /* NTS: Most operating systems will not attempt SYSENTER/SYSEXIT unless this returns model 3, stepping 3, or higher. */ @@ -3120,6 +3127,7 @@ bool CPU_CPUID(void) { reg_ecx=0; /* No features */ reg_edx=0x00808011; /* FPU+TimeStamp/RDTSC */ if (enable_msr) reg_edx |= 0x20; /* ModelSpecific/MSR */ + if (enable_pse) reg_edx |= 0x08; /* Page Size Extension */ if (enable_cmpxchg8b) reg_edx |= 0x100; /* CMPXCHG8B */ reg_edx |= 0x800; /* SEP Fast System Call aka SYSENTER/SYSEXIT [SEE NOTES AT TOP OF THIS IF STATEMENT] */ } else if (CPU_ArchitectureType == CPU_ARCHTYPE_PENTIUMIII || CPU_ArchitectureType == CPU_ARCHTYPE_EXPERIMENTAL) { @@ -3128,6 +3136,7 @@ bool CPU_CPUID(void) { reg_ecx=0; /* No features */ reg_edx=0x03808011; /* FPU+TimeStamp/RDTSC+SSE+FXSAVE/FXRESTOR */ if (enable_msr) reg_edx |= 0x20; /* ModelSpecific/MSR */ + if (enable_pse) reg_edx |= 0x08; /* Page Size Extension */ if (enable_cmpxchg8b) reg_edx |= 0x100; /* CMPXCHG8B */ if (CPU_ArchitectureType == CPU_ARCHTYPE_PENTIUMIII && p3psn.enabled) reg_edx |= 0x40000; reg_edx |= 0x800; /* SEP Fast System Call aka SYSENTER/SYSEXIT */ @@ -3724,6 +3733,7 @@ class CPU: public Module_base { cpu_rep_max=section->Get_int("interruptible rep string op"); ignore_undefined_msr=section->Get_bool("ignore undefined msr"); enable_msr=section->Get_bool("enable msr"); + enable_pse=section->Get_bool("enable pse"); enable_syscall=section->Get_bool("enable syscall"); enable_cmpxchg8b=section->Get_bool("enable cmpxchg8b"); CPU_CycleUp=section->Get_int("cycleup"); @@ -4075,6 +4085,14 @@ class CPU: public Module_base { if (enable_cmpxchg8b && CPU_ArchitectureType >= CPU_ARCHTYPE_PENTIUM) LOG_MSG("Pentium CMPXCHG8B emulation is enabled"); + if (CPU_ArchitectureType >= CPU_ARCHTYPE_PENTIUM) { + do_pse = false; + cpu.cr4=0; + } + else { + enable_pse = false; + } + menu_update_core(); menu_update_cputype(); diff --git a/src/cpu/paging.cpp b/src/cpu/paging.cpp index 487281e4a87..ff3ab4d93ab 100644 --- a/src/cpu/paging.cpp +++ b/src/cpu/paging.cpp @@ -26,6 +26,9 @@ #include "cpu.h" #include "logging.h" +extern bool do_pse; +extern bool enable_pse; + extern bool dos_kernel_disabled; PagingBlock paging; @@ -436,28 +439,41 @@ class ExceptionPageHandler : public PageHandler { uint32_t phys_page = paging.tlb.phys_page[lin_page] & PHYSPAGE_ADDR; PageHandler* handler = MEM_GetPageHandler(phys_page); return handler; - } + } bool hack_check(PhysPt addr) { // First Encounters + // // They change the page attributes without clearing the TLB. // On a real 486 they get away with it because its TLB has only 32 or so // elements. The changed page attribs get overwritten and re-read before // the exception happens. Here we have gazillions of TLB entries so the // exception occurs if we don't check for it. - Bitu old_attirbs = paging.tlb.phys_page[addr>>12] >> 30; - X86PageEntry dir_entry, table_entry; - - dir_entry.load = phys_readd(GetPageDirectoryEntryAddr(addr)); - if (!dir_entry.block.p) return false; - table_entry.load = phys_readd(GetPageTableEntryAddr(addr, dir_entry)); - if (!table_entry.block.p) return false; - Bitu result = - translate_array[((dir_entry.load<<1)&0xc) | ((table_entry.load>>1)&0x3)]; - if (result != old_attirbs) return true; + // NTS 2024/12/15: Checking with 7-cpu.com, the Pentium also has 32, + // the Pentium II and III has 64. You would need to go to a pretty late + // level of CPU to find something that would probably cause this game to + // fail from page table laziness. However, to simplify this code, this + // check is skipped over if PSE or PAE are enabled because DOS games are + // unlikely to use those page table features. --J.C. + + if (!do_pse) { + Bitu old_attirbs = paging.tlb.phys_page[addr>>12] >> 30; + X86PageEntry dir_entry, table_entry; + + dir_entry.load = phys_readd(GetPageDirectoryEntryAddr(addr)); + if (!dir_entry.block.p) return false; + table_entry.load = phys_readd(GetPageTableEntryAddr(addr, dir_entry)); + if (!table_entry.block.p) return false; + + const Bitu result = + translate_array[((dir_entry.load<<1)&0xc) | ((table_entry.load>>1)&0x3)]; + + if (result != old_attirbs) return true; + } + return false; - } + } void Exception(PhysPt addr, bool writing, bool checked) { //PrintPageInfo("XCEPT",addr,writing, checked); @@ -770,7 +786,8 @@ class NewInitPageHandler : public PageHandler { dir_entry.load=phys_readd(dirEntryAddr); } else { - LOG(LOG_CPU,LOG_WARN)("Page directory access beyond end of memory, page %08x >= %08x",(unsigned int)(dirEntryAddr>>12u),(unsigned int)MEM_TotalPages()); + LOG(LOG_CPU,LOG_WARN)("Page directory access beyond end of memory, page %08x >= %08x", + (unsigned int)(dirEntryAddr>>12u),(unsigned int)MEM_TotalPages()); dir_entry.load=0xFFFFFFFF; } @@ -778,17 +795,19 @@ class NewInitPageHandler : public PageHandler { // table pointer is not present, do a page fault PAGING_NewPageFault(lin_addr, dirEntryAddr, prepare_only, (writing ? 2u : 0u) | (isUser ? 4u : 0u)); - + if (prepare_only) return true; else goto initpage_retry; // TODO maybe E_Exit after a few loops } + PhysPt tableEntryAddr = GetPageTableEntryAddr(lin_addr, dir_entry); // Range check to avoid emulator segfault: phys_readd() reads from MemBase+addr and does NOT range check. if ((tableEntryAddr+4) <= (MEM_TotalPages()<<12u)) { table_entry.load=phys_readd(tableEntryAddr); } else { - LOG(LOG_CPU,LOG_WARN)("Page table entry access beyond end of memory, page %08x >= %08x",(unsigned int)(tableEntryAddr>>12u),(unsigned int)MEM_TotalPages()); + LOG(LOG_CPU,LOG_WARN)("Page table entry access beyond end of memory, page %08x >= %08x", + (unsigned int)(tableEntryAddr>>12u),(unsigned int)MEM_TotalPages()); table_entry.load=0xFFFFFFFF; } @@ -797,33 +816,33 @@ class NewInitPageHandler : public PageHandler { if (!dir_entry.block.a) { dir_entry.block.a = 1; phys_writed(dirEntryAddr, dir_entry.load); - } + } if (!table_entry.block.p) { // physpage pointer is not present, do a page fault PAGING_NewPageFault(lin_addr, tableEntryAddr, prepare_only, - (writing ? 2u : 0u) | (isUser ? 4u : 0u)); - + (writing ? 2u : 0u) | (isUser ? 4u : 0u)); + if (prepare_only) return true; else goto initpage_retry; - } + } //PrintPageInfo("INI",lin_addr,writing,prepare_only); Bitu result = translate_array[((dir_entry.load<<1)&0xc) | ((table_entry.load>>1)&0x3)]; - + // If a page access right exception occurs we shouldn't change a or d // I'd prefer running into the prepared exception handler but we'd need // an additional handler that sets the 'a' bit - idea - foiler read? Bitu ft_index = result | (writing ? 8u : 0u) | (isUser ? 4u : 0u) | (paging.wp ? 16u : 0u); - + if (GCC_UNLIKELY(fault_table[ft_index])) { // exception error code format: // bit0 - protection violation, bit1 - writing, bit2 - user mode PAGING_NewPageFault(lin_addr, tableEntryAddr, prepare_only, - 1u | (writing ? 2u : 0u) | (isUser ? 4u : 0u)); - + 1u | (writing ? 2u : 0u) | (isUser ? 4u : 0u)); + if (prepare_only) return true; else goto initpage_retry; // unlikely to happen? } @@ -835,7 +854,7 @@ class NewInitPageHandler : public PageHandler { // set page accessed table_entry.block.a = 1; - + // update if needed if (table_load != table_entry.load) phys_writed(tableEntryAddr, table_entry.load); @@ -843,14 +862,14 @@ class NewInitPageHandler : public PageHandler { // if the page isn't dirty and we are reading we need to map the foiler // (dirty = false) bool dirty = table_entry.block.d? true:false; -/* - LOG_MSG("INIT %s LIN% 8x PHYS% 5x wr%x ch%x wp%x d%x c%x m%x a%x [%x/%x/%x]", - mtr[result], lin_addr, table_entry.block.base, - writing, prepare_only, paging.wp, - dirty, cpu.cpl, cpu.mpl, - ((dir_entry.load<<1)&0xc) | ((table_entry.load>>1)&0x3), - dirEntryAddr, tableEntryAddr, table_entry.load); -*/ + /* + LOG_MSG("INIT %s LIN% 8x PHYS% 5x wr%x ch%x wp%x d%x c%x m%x a%x [%x/%x/%x]", + mtr[result], lin_addr, table_entry.block.base, + writing, prepare_only, paging.wp, + dirty, cpu.cpl, cpu.mpl, + ((dir_entry.load<<1)&0xc) | ((table_entry.load>>1)&0x3), + dirEntryAddr, tableEntryAddr, table_entry.load); + */ // finally install the new page PAGING_LinkPageNew(lin_page, table_entry.block.base, result, dirty); diff --git a/src/dosbox.cpp b/src/dosbox.cpp index aaef2d20929..20510f096ae 100644 --- a/src/dosbox.cpp +++ b/src/dosbox.cpp @@ -2959,6 +2959,9 @@ void DOSBOX_SetupConfigSections(void) { Pbool->Set_help("Allow RDMSR/WRMSR instructions. This option is only meaningful when cputype=pentium.\n" "WARNING: Leaving this option enabled while installing Windows 95/98/ME can cause crashes."); + Pbool = secprop->Add_bool("enable pse",Property::Changeable::Always,false); + Pbool->Set_help("Allow PSE (Page Size Extensions) to paging"); + Pbool = secprop->Add_bool("enable cmpxchg8b",Property::Changeable::Always,true); Pbool->Set_help("Enable Pentium CMPXCHG8B instruction. Enable this explicitly if using software that uses this instruction.\n" "You must enable this option to run Windows ME because portions of the kernel rely on this instruction.");