/* * Copyright (C) 2002-2005 The DOSBox Team * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include #include #include #include "dosbox.h" #include "mem.h" #include "paging.h" #include "regs.h" #include "lazyflags.h" #include "cpu.h" #include "debug.h" #include "setup.h" #define LINK_TOTAL (64*1024) PagingBlock paging; Bitu PageHandler::readb(PhysPt addr) { E_Exit("No byte handler for read from %d",addr); return 0; } Bitu PageHandler::readw(PhysPt addr) { return (readb(addr+0) << 0) | (readb(addr+1) << 8); } Bitu PageHandler::readd(PhysPt addr) { return (readb(addr+0) << 0) | (readb(addr+1) << 8) | (readb(addr+2) << 16) | (readb(addr+3) << 24); } void PageHandler::writeb(PhysPt addr,Bitu val) { E_Exit("No byte handler for write to %d",addr); }; void PageHandler::writew(PhysPt addr,Bitu val) { writeb(addr+0,(Bit8u) (val >> 0)); writeb(addr+1,(Bit8u) (val >> 8)); } void PageHandler::writed(PhysPt addr,Bitu val) { writeb(addr+0,(Bit8u) (val >> 0)); writeb(addr+1,(Bit8u) (val >> 8)); writeb(addr+2,(Bit8u) (val >> 16)); writeb(addr+3,(Bit8u) (val >> 24)); }; HostPt PageHandler::GetHostReadPt(Bitu phys_page) { return 0; } HostPt PageHandler::GetHostWritePt(Bitu phys_page) { return 0; } bool PageHandler::readb_checked(PhysPt addr, Bitu * val) { *val=readb(addr); return false; } bool PageHandler::readw_checked(PhysPt addr, Bitu * val) { *val=readw(addr); return false; } bool PageHandler::readd_checked(PhysPt addr, Bitu * val) { *val=readd(addr); return false; } bool PageHandler::writeb_checked(PhysPt addr,Bitu val) { writeb(addr,val); return false; } bool PageHandler::writew_checked(PhysPt addr,Bitu val) { writew(addr,val); return false; } bool PageHandler::writed_checked(PhysPt addr,Bitu val) { writed(addr,val); return false; } struct PF_Entry { Bitu cs; Bitu eip; Bitu page_addr; }; #define PF_QUEUESIZE 16 static struct { Bitu used; PF_Entry entries[PF_QUEUESIZE]; } pf_queue; static Bits PageFaultCore(void) { CPU_CycleLeft+=CPU_Cycles; CPU_Cycles=1; Bitu ret=CPU_Core_Full_Run(); CPU_CycleLeft+=CPU_Cycles; if (ret<0) E_Exit("Got a dosbox close machine in pagefault core?"); if (ret) return ret; if (!pf_queue.used) E_Exit("PF Core without PF"); PF_Entry * entry=&pf_queue.entries[pf_queue.used-1]; X86PageEntry pentry; pentry.load=phys_readd(entry->page_addr); if (pentry.block.p && entry->cs == SegValue(cs) && entry->eip==reg_eip) return -1; return 0; } #if C_DEBUG Bitu DEBUG_EnableDebugger(void); #endif bool first=false; void PAGING_PageFault(PhysPt lin_addr,Bitu page_addr,bool writefault,Bitu faultcode) { /* Save the state of the cpu cores */ LazyFlags old_lflags; memcpy(&old_lflags,&lflags,sizeof(LazyFlags)); CPU_Decoder * old_cpudecoder; old_cpudecoder=cpudecoder; cpudecoder=&PageFaultCore; paging.cr2=lin_addr; PF_Entry * entry=&pf_queue.entries[pf_queue.used++]; LOG(LOG_PAGING,LOG_NORMAL)("PageFault at %X type [%x:%x] queue %d",lin_addr,writefault,faultcode,pf_queue.used); // LOG_MSG("EAX:%04X ECX:%04X EDX:%04X EBX:%04X",reg_eax,reg_ecx,reg_edx,reg_ebx); // LOG_MSG("CS:%04X EIP:%08X SS:%04x SP:%08X",SegValue(cs),reg_eip,SegValue(ss),reg_esp); entry->cs=SegValue(cs); entry->eip=reg_eip; entry->page_addr=page_addr; //Caused by a write by default? CPU_Exception(14,(writefault?0x02:0x00) | faultcode); #if C_DEBUG // DEBUG_EnableDebugger(); #endif DOSBOX_RunMachine(); pf_queue.used--; LOG(LOG_PAGING,LOG_NORMAL)("Left PageFault for %x queue %d",lin_addr,pf_queue.used); memcpy(&lflags,&old_lflags,sizeof(LazyFlags)); cpudecoder=old_cpudecoder; // LOG_MSG("SS:%04x SP:%08X",SegValue(ss),reg_esp); } class InitPageHandler : public PageHandler { public: InitPageHandler() {flags=PFLAG_INIT|PFLAG_NOCODE;} Bitu readb(PhysPt addr) { InitPage(addr,false); return mem_readb(addr); } Bitu readw(PhysPt addr) { InitPage(addr,false); return mem_readw(addr); } Bitu readd(PhysPt addr) { InitPage(addr,false); return mem_readd(addr); } void writeb(PhysPt addr,Bitu val) { InitPage(addr,true); mem_writeb(addr,val); } void writew(PhysPt addr,Bitu val) { InitPage(addr,true); mem_writew(addr,val); } void writed(PhysPt addr,Bitu val) { InitPage(addr,true); mem_writed(addr,val); } bool readb_checked(PhysPt addr, Bitu * val) { if (InitPage(addr,false,true)) { *val=mem_readb(addr); return false; } else return true; } bool readw_checked(PhysPt addr, Bitu * val) { if (InitPage(addr,false,true)){ *val=mem_readw(addr); return false; } else return true; } bool readd_checked(PhysPt addr, Bitu * val) { if (InitPage(addr,false,true)) { *val=mem_readd(addr); return false; } else return true; } bool writeb_checked(PhysPt addr,Bitu val) { if (InitPage(addr,true,true)) { mem_writeb(addr,val); return false; } else return true; } bool writew_checked(PhysPt addr,Bitu val) { if (InitPage(addr,true,true)) { mem_writew(addr,val); return false; } else return true; } bool writed_checked(PhysPt addr,Bitu val) { if (InitPage(addr,true,true)) { mem_writed(addr,val); return false; } else return true; } bool InitPage(Bitu lin_addr,bool writing,bool check_only=false) { Bitu lin_page=lin_addr >> 12; Bitu phys_page; if (paging.enabled) { Bitu d_index=lin_page >> 10; Bitu t_index=lin_page & 0x3ff; Bitu table_addr=(paging.base.page<<12)+d_index*4; X86PageEntry table; table.load=phys_readd(table_addr); if (!table.block.p) { if (check_only) { paging.cr2=lin_addr; cpu.exception.which=14; cpu.exception.error=writing?0x02:0x00; return false; } LOG(LOG_PAGING,LOG_NORMAL)("NP Table"); PAGING_PageFault(lin_addr,table_addr,false,writing?0x02:0x00); table.load=phys_readd(table_addr); if (!table.block.p) E_Exit("Pagefault didn't correct table"); } if (!table.block.a) { table.block.a=1; //Set access phys_writed(table_addr,table.load); } X86PageEntry entry; Bitu entry_addr=(table.block.base<<12)+t_index*4; entry.load=phys_readd(entry_addr); if (!entry.block.p) { if (check_only) { paging.cr2=lin_addr; cpu.exception.which=14; cpu.exception.error=writing?0x02:0x00; return false; } // LOG(LOG_PAGING,LOG_NORMAL)("NP Page"); PAGING_PageFault(lin_addr,entry_addr,false,writing?0x02:0x00); entry.load=phys_readd(entry_addr); if (!entry.block.p) E_Exit("Pagefault didn't correct page"); } if (cpu.cpl==3) { if ((entry.block.us==0) || (table.block.us==0) && (((entry.block.wr==0) || (table.block.wr==0)) && writing)) { LOG(LOG_PAGING,LOG_NORMAL)("Page access denied: cpl=%i, %x:%x:%x:%x",cpu.cpl,entry.block.us,table.block.us,entry.block.wr,table.block.wr); if (check_only) { paging.cr2=lin_addr; cpu.exception.which=14; cpu.exception.error=0x05 | (writing?0x02:0x00); return false; } PAGING_PageFault(lin_addr,entry_addr,writing,0x05 | (writing?0x02:0x00)); } } if (check_only) return true; if ((!entry.block.a) || (!entry.block.d)) { entry.block.a=1; //Set access entry.block.d=1; //Set dirty phys_writed(entry_addr,entry.load); } phys_page=entry.block.base; } else { if (lin_page> 10; Bitu t_index=page & 0x3ff; X86PageEntry table; table.load=phys_readd((paging.base.page<<12)+d_index*4); if (!table.block.p) return false; X86PageEntry entry; entry.load=phys_readd((table.block.base<<12)+t_index*4); if (!entry.block.p) return false; page=entry.block.base; } else { if (page0;paging.links.used--) { Bitu page=*entries++; paging.tlb.read[page]=0; paging.tlb.write[page]=0; paging.tlb.handler[page]=&init_page_handler; } paging.links.used=0; } void PAGING_UnlinkPages(Bitu lin_page,Bitu pages) { for (;pages>0;pages--) { paging.tlb.read[lin_page]=0; paging.tlb.write[lin_page]=0; paging.tlb.handler[lin_page]=&init_page_handler; } } void PAGING_LinkPage(Bitu lin_page,Bitu phys_page) { PageHandler * handler=MEM_GetPageHandler(phys_page); Bitu lin_base=lin_page << 12; if (lin_page>=TLB_SIZE || phys_page>=TLB_SIZE) E_Exit("Illegal page"); if (paging.links.used>=PAGING_LINKS) { LOG(LOG_PAGING,LOG_NORMAL)("Not enough paging links, resetting cache"); PAGING_ClearTLB(); } paging.tlb.phys_page[lin_page]=phys_page; if (handler->flags & PFLAG_READABLE) paging.tlb.read[lin_page]=handler->GetHostReadPt(phys_page)-lin_base; else paging.tlb.read[lin_page]=0; if (handler->flags & PFLAG_WRITEABLE) paging.tlb.write[lin_page]=handler->GetHostWritePt(phys_page)-lin_base; else paging.tlb.write[lin_page]=0; paging.links.entries[paging.links.used++]=lin_page; paging.tlb.handler[lin_page]=handler; } void PAGING_MapPage(Bitu lin_page,Bitu phys_page) { if (lin_page> 12; paging.base.addr=cr3 & ~4095; // LOG(LOG_PAGING,LOG_NORMAL)("CR3:%X Base %X",cr3,paging.base.page); if (paging.enabled) { PAGING_ClearTLB(); } } void PAGING_Enable(bool enabled) { /* If paging is disable we work from a default paging table */ if (paging.enabled==enabled) return; paging.enabled=enabled; if (!enabled) { // LOG(LOG_PAGING,LOG_NORMAL)("Disabled"); } else { if (cpudecoder==CPU_Core_Simple_Run) { LOG_MSG("CPU core simple won't run this game,switching to normal"); cpudecoder=CPU_Core_Normal_Run; CPU_CycleLeft+=CPU_Cycles; CPU_Cycles=0; } // LOG(LOG_PAGING,LOG_NORMAL)("Enabled"); PAGING_SetDirBase(paging.cr3); } PAGING_ClearTLB(); } bool PAGING_Enabled(void) { return paging.enabled; } class PAGING:public Module_base{ public: PAGING(Section* configuration):Module_base(configuration){ /* Setup default Page Directory, force it to update */ paging.enabled=false; PAGING_InitTLB(); Bitu i; for (i=0;i