Skip to content

Commit

Permalink
Begin work on PSE (Page Size Extension) emulation. Limit the enable o…
Browse files Browse the repository at this point in the history
…nly to Pentium or higher. Reportedly Intel added it to the original Pentium but did not document it until the Pentium Pro or Pentium II
  • Loading branch information
joncampbell123 committed Dec 15, 2024
1 parent 3ea5256 commit a3d5073
Show file tree
Hide file tree
Showing 4 changed files with 95 additions and 55 deletions.
46 changes: 23 additions & 23 deletions include/paging.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
18 changes: 18 additions & 0 deletions src/cpu/cpu.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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:
Expand Down Expand Up @@ -3084,20 +3088,23 @@ 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) */
reg_ebx=0; /* Not Supported */
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 */
reg_ebx=0; /* Not Supported */
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. */
Expand All @@ -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) {
Expand All @@ -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 */
Expand Down Expand Up @@ -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");
Expand Down Expand Up @@ -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();

Expand Down
83 changes: 51 additions & 32 deletions src/cpu/paging.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -770,25 +786,28 @@ 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;
}

if (!dir_entry.block.p) {
// 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;
}

Expand All @@ -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?
}
Expand All @@ -835,22 +854,22 @@ 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);

// 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);

Expand Down
3 changes: 3 additions & 0 deletions src/dosbox.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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.");
Expand Down

0 comments on commit a3d5073

Please sign in to comment.