DOSBox-X
|
00001 /* 00002 * Copyright (C) 2002-2020 The DOSBox Team 00003 * 00004 * This program is free software; you can redistribute it and/or modify 00005 * it under the terms of the GNU General Public License as published by 00006 * the Free Software Foundation; either version 2 of the License, or 00007 * (at your option) any later version. 00008 * 00009 * This program is distributed in the hope that it will be useful, 00010 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00011 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00012 * GNU General Public License for more details. 00013 * 00014 * You should have received a copy of the GNU General Public License along 00015 * with this program; if not, write to the Free Software Foundation, Inc., 00016 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 00017 */ 00018 00019 00020 #ifndef DOSBOX_PAGING_H 00021 #define DOSBOX_PAGING_H 00022 00023 #ifndef DOSBOX_DOSBOX_H 00024 #include <iostream> 00025 #include "dosbox.h" 00026 #endif 00027 #ifndef DOSBOX_MEM_H 00028 #include "mem.h" 00029 #endif 00030 00031 // disable this to reduce the size of the TLB 00032 // NOTE: does not work with the dynamic core (dynrec is fine) 00033 #define USE_FULL_TLB 00034 00035 class PageHandler; 00036 class MEM_CalloutObject; 00037 00038 class PageDirectory; 00039 00040 typedef PageHandler* (MEM_CalloutHandler)(MEM_CalloutObject &co,Bitu phys_page); 00041 00042 void MEM_RegisterHandler(Bitu phys_page,PageHandler *handler,Bitu page_range=1); 00043 00044 void MEM_FreeHandler(Bitu phys_page,Bitu page_range=1); 00045 00046 void MEM_InvalidateCachedHandler(Bitu phys_page,Bitu range=1); 00047 00048 static const Bitu MEMMASK_ISA_20BIT = 0x000000FFU; /* ISA 20-bit decode (20 - 12 = 8) */ 00049 static const Bitu MEMMASK_ISA_24BIT = 0x00000FFFU; /* ISA 24-bit decode (24 - 12 = 12) */ 00050 static const Bitu MEMMASK_FULL = 0x000FFFFFU; /* full 32-bit decode (32 - 12 = 20) */ 00051 00052 /* WARNING: Will only produce a correct result if 'x' is a nonzero power of two. 00053 * For use with MEMMASK_Combine. 'x' is in units of PAGES not BYTES. 00054 */ 00055 static inline Bitu MEMMASK_Range(const Bitu x) { 00056 return ~(x - 1); 00057 } 00058 00059 /* combine range mask with MEMMASK value. 00060 */ 00061 static inline Bitu MEMMASK_Combine(const Bitu a,const Bitu b) { 00062 return a & b; 00063 } 00064 00065 #define MEM_PAGE_SIZE (4096) 00066 #define XMS_START (0x110) 00067 00068 #if defined(USE_FULL_TLB) 00069 #define TLB_SIZE (1024*1024) 00070 #else 00071 #define TLB_SIZE 65536 // This must a power of 2 and greater then LINK_START 00072 #define BANK_SHIFT 28 00073 #define BANK_MASK 0xffff // always the same as TLB_SIZE-1? 00074 #define TLB_BANKS ((1024*1024/TLB_SIZE)-1) 00075 #endif 00076 00077 #define PFLAG_READABLE 0x1u 00078 #define PFLAG_WRITEABLE 0x2u 00079 #define PFLAG_HASROM 0x4u 00080 #define PFLAG_HASCODE 0x8u //Page contains dynamic code 00081 #define PFLAG_NOCODE 0x10u //No dynamic code can be generated here 00082 #define PFLAG_INIT 0x20u //No dynamic code can be generated here 00083 00084 #define LINK_START ((1024+64)/4) //Start right after the HMA 00085 00086 //Allow 128 mb of memory to be linked 00087 #define PAGING_LINKS (128*1024/4) 00088 00089 class PageHandler { 00090 public: 00091 PageHandler(Bitu flg) : flags(flg) {} 00092 virtual ~PageHandler(void) { } 00093 virtual Bit8u readb(PhysPt addr); 00094 virtual Bit16u readw(PhysPt addr); 00095 virtual Bit32u readd(PhysPt addr); 00096 virtual void writeb(PhysPt addr,Bit8u val); 00097 virtual void writew(PhysPt addr,Bit16u val); 00098 virtual void writed(PhysPt addr,Bit32u val); 00099 virtual HostPt GetHostReadPt(Bitu phys_page); 00100 virtual HostPt GetHostWritePt(Bitu phys_page); 00101 virtual bool readb_checked(PhysPt addr,Bit8u * val); 00102 virtual bool readw_checked(PhysPt addr,Bit16u * val); 00103 virtual bool readd_checked(PhysPt addr,Bit32u * val); 00104 virtual bool writeb_checked(PhysPt addr,Bit8u val); 00105 virtual bool writew_checked(PhysPt addr,Bit16u val); 00106 virtual bool writed_checked(PhysPt addr,Bit32u val); 00107 00108 #if 0//ENABLE IF PORTING ADDITIONAL CODE WRITTEN AGAINST THE OLDER PAGE HANDLER readb/writeb PROTYPE. 00109 // DEPRECATED. THIS IS HERE TO MAKE ANY DERIVED CLASS NOT YET UPDATED BLOW UP WITH A COMPILER ERROR. 00110 // FIXME: DOES VISUAL STUDIO 2017 HAVE ANY PROBLEMS WITH THIS? CLANG/LLVM? 00111 virtual void writeb(PhysPt addr,Bitu val) final = delete; 00112 virtual void writew(PhysPt addr,Bitu val) final = delete; 00113 virtual void writed(PhysPt addr,Bitu val) final = delete; 00114 virtual void writeb_checked(PhysPt addr,Bitu val) final = delete; 00115 virtual void writew_checked(PhysPt addr,Bitu val) final = delete; 00116 virtual void writed_checked(PhysPt addr,Bitu val) final = delete; 00117 #endif 00118 00119 PageHandler(void) : flags(0) { } 00120 Bitu flags; 00121 Bitu getFlags() const { 00122 return flags; 00123 } 00124 void setFlags(const Bitu flagsNew) { 00125 flags = flagsNew; 00126 } 00127 00128 private: 00129 PageHandler(const PageHandler&); 00130 00131 }; 00132 00133 /* NTS: To explain the Install() method, the caller not only provides the IOMASK_.. value, but ANDs 00134 * the least significant bits to define the range of I/O ports to respond to. An ISA Sound Blaster 00135 * for example would set portmask = (IOMASK_ISA_10BIT & (~0xF)) in order to respond to 220h-22Fh, 00136 * 240h-24Fh, etc. At I/O callout time, the callout object is tested 00137 * if (cpu_ioport & io_mask) == (m_port & io_mask) 00138 * 00139 * This does not prevent emulation of devices that start on non-aligned ports or strange port ranges, 00140 * because the callout handler is free to decline the I/O request, leading the callout process to 00141 * move on to the next device or mark the I/O port as empty. */ 00142 class MEM_CalloutObject { 00143 public: 00144 MEM_CalloutObject() : installed(false), mem_mask(0xFFFFFFFFU), range_mask(0U), alias_mask(0xFFFFFFFFU), getcounter(0), m_handler(NULL), m_base(0), alloc(false) {}; 00145 void InvalidateCachedHandlers(void); 00146 void Install(Bitu page,Bitu pagemask/*MEMMASK_ISA_24BIT, etc.*/,MEM_CalloutHandler *handler); 00147 void Uninstall(); 00148 public: 00149 bool installed; 00150 Bitu mem_mask; 00151 Bitu range_mask; 00152 Bitu alias_mask; 00153 unsigned int getcounter; 00154 MEM_CalloutHandler *m_handler; 00155 Bitu m_base; 00156 bool alloc; 00157 public: 00158 inline bool MatchPage(const Bitu p) { 00159 /* (p & io_mask) == (m_port & io_mask) but this also works. 00160 * apparently modern x86 processors are faster at addition/subtraction than bitmasking. 00161 * for this to work, m_port must be a multiple of the I/O range. For example, if the I/O 00162 * range is 16 ports, then m_port must be a multiple of 16. */ 00163 return ((p - m_base) & mem_mask) == 0; 00164 } 00165 inline bool isInstalled(void) { 00166 return installed; 00167 } 00168 }; 00169 00170 enum MEM_Type_t { 00171 MEM_TYPE_NONE=0, 00172 MEM_TYPE_MIN=1, 00173 MEM_TYPE_ISA=1, 00174 MEM_TYPE_PCI, 00175 MEM_TYPE_MB, 00176 00177 MEM_TYPE_MAX 00178 }; 00179 00180 void MEM_InitCallouts(void); 00181 00182 typedef uint32_t MEM_Callout_t; 00183 00184 static inline uint32_t MEM_Callout_t_comb(const enum MEM_Type_t t,const uint32_t idx) { 00185 return ((uint32_t)t << (uint32_t)28) + idx; 00186 } 00187 00188 static inline enum MEM_Type_t MEM_Callout_t_type(const MEM_Callout_t t) { 00189 return (enum MEM_Type_t)(t >> 28); 00190 } 00191 00192 static inline uint32_t MEM_Callout_t_index(const MEM_Callout_t t) { 00193 return t & (((uint32_t)1 << (uint32_t)28) - (uint32_t)1); 00194 } 00195 00196 static const MEM_Callout_t MEM_Callout_t_none = (MEM_Callout_t)0; 00197 00198 MEM_Callout_t MEM_AllocateCallout(MEM_Type_t t); 00199 void MEM_FreeCallout(MEM_Callout_t c); 00200 MEM_CalloutObject *MEM_GetCallout(MEM_Callout_t c); 00201 void MEM_PutCallout(MEM_CalloutObject *obj); 00202 00203 /* Some other functions */ 00204 void PAGING_Enable(bool enabled); 00205 bool PAGING_Enabled(void); 00206 void PAGING_SetWP(bool wp); 00207 void PAGING_SwitchCPL(bool isUser); 00208 00209 Bitu PAGING_GetDirBase(void); 00210 void PAGING_SetDirBase(Bitu cr3); 00211 void PAGING_InitTLB(void); 00212 void PAGING_ClearTLB(void); 00213 00214 void PAGING_LinkPage(Bitu lin_page,Bitu phys_page); 00215 void PAGING_LinkPage_ReadOnly(Bitu lin_page,Bitu phys_page); 00216 void PAGING_UnlinkPages(Bitu lin_page,Bitu pages); 00217 /* This maps the page directly, only use when paging is disabled */ 00218 void PAGING_MapPage(Bitu lin_page,Bitu phys_page); 00219 bool PAGING_MakePhysPage(Bitu & page); 00220 00221 void MEM_SetLFB(Bitu page, Bitu pages, PageHandler *handler, PageHandler *mmiohandler); 00222 void MEM_SetPageHandler(Bitu phys_page, Bitu pages, PageHandler * handler); 00223 00224 00225 #ifdef _MSC_VER 00226 #pragma pack (1) 00227 #endif 00228 struct X86_PageEntryBlock{ 00229 #ifdef WORDS_BIGENDIAN 00230 Bit32u base:20; 00231 Bit32u avl:3; 00232 Bit32u g:1; 00233 Bit32u pat:1; 00234 Bit32u d:1; 00235 Bit32u a:1; 00236 Bit32u pcd:1; 00237 Bit32u pwt:1; 00238 Bit32u us:1; 00239 Bit32u wr:1; 00240 Bit32u p:1; 00241 #else 00242 Bit32u p:1; 00243 Bit32u wr:1; 00244 Bit32u us:1; 00245 Bit32u pwt:1; 00246 Bit32u pcd:1; 00247 Bit32u a:1; 00248 Bit32u d:1; 00249 Bit32u pat:1; 00250 Bit32u g:1; 00251 Bit32u avl:3; 00252 Bit32u base:20; 00253 #endif 00254 } GCC_ATTRIBUTE(packed); 00255 #ifdef _MSC_VER 00256 #pragma pack () 00257 #endif 00258 00259 00260 union X86PageEntry { 00261 Bit32u load; 00262 X86_PageEntryBlock block; 00263 }; 00264 00265 #if !defined(USE_FULL_TLB) 00266 typedef struct { 00267 HostPt read; 00268 HostPt write; 00269 PageHandler * readhandler; 00270 PageHandler * writehandler; 00271 Bit32u phys_page; 00272 } tlb_entry; 00273 #endif 00274 00275 struct PagingBlock { 00276 Bitu cr3; 00277 Bitu cr2; 00278 bool wp; 00279 struct { 00280 Bitu page; 00281 PhysPt addr; 00282 } base; 00283 #if defined(USE_FULL_TLB) 00284 struct { 00285 HostPt read[TLB_SIZE]; 00286 HostPt write[TLB_SIZE]; 00287 PageHandler * readhandler[TLB_SIZE]; 00288 PageHandler * writehandler[TLB_SIZE]; 00289 Bit32u phys_page[TLB_SIZE]; 00290 } tlb; 00291 #else 00292 tlb_entry tlbh[TLB_SIZE]; 00293 tlb_entry *tlbh_banks[TLB_BANKS]; 00294 #endif 00295 struct { 00296 Bitu used; 00297 Bit32u entries[PAGING_LINKS]; 00298 } links; 00299 struct { 00300 Bitu used; 00301 Bit32u entries[PAGING_LINKS]; 00302 } ur_links; 00303 struct { 00304 Bitu used; 00305 Bit32u entries[PAGING_LINKS]; 00306 } krw_links; 00307 struct { 00308 Bitu used; 00309 Bit32u entries[PAGING_LINKS]; 00310 } kr_links; // WP-only 00311 Bit32u firstmb[LINK_START]; 00312 bool enabled; 00313 }; 00314 00315 extern PagingBlock paging; 00316 00317 /* Some support functions */ 00318 00319 PageHandler * MEM_GetPageHandler(const Bitu phys_page); 00320 00321 00322 /* Unaligned address handlers */ 00323 Bit16u mem_unalignedreadw(const PhysPt address); 00324 Bit32u mem_unalignedreadd(const PhysPt address); 00325 void mem_unalignedwritew(const PhysPt address,const Bit16u val); 00326 void mem_unalignedwrited(const PhysPt address,const Bit32u val); 00327 00328 bool mem_unalignedreadw_checked(const PhysPt address,Bit16u * const val); 00329 bool mem_unalignedreadd_checked(const PhysPt address,Bit32u * const val); 00330 bool mem_unalignedwritew_checked(const PhysPt address,Bit16u const val); 00331 bool mem_unalignedwrited_checked(const PhysPt address,Bit32u const val); 00332 00333 #if defined(USE_FULL_TLB) 00334 00335 static INLINE HostPt get_tlb_read(const PhysPt address) { 00336 return paging.tlb.read[address>>12]; 00337 } 00338 static INLINE HostPt get_tlb_write(const PhysPt address) { 00339 return paging.tlb.write[address>>12]; 00340 } 00341 static INLINE PageHandler* get_tlb_readhandler(const PhysPt address) { 00342 return paging.tlb.readhandler[address>>12]; 00343 } 00344 static INLINE PageHandler* get_tlb_writehandler(const PhysPt address) { 00345 return paging.tlb.writehandler[address>>12]; 00346 } 00347 00348 /* Use these helper functions to access linear addresses in readX/writeX functions */ 00349 static INLINE PhysPt PAGING_GetPhysicalPage(const PhysPt linePage) { 00350 return (paging.tlb.phys_page[linePage>>12]<<12); 00351 } 00352 00353 static INLINE PhysPt PAGING_GetPhysicalAddress(const PhysPt linAddr) { 00354 return (paging.tlb.phys_page[linAddr>>12]<<12)|(linAddr&0xfff); 00355 } 00356 00357 #else 00358 00359 void PAGING_InitTLBBank(tlb_entry **bank); 00360 00361 static INLINE tlb_entry *get_tlb_entry(const PhysPt address) { 00362 const Bitu index=(address >> 12U); 00363 if (TLB_BANKS && (index >= TLB_SIZE)) { 00364 const Bitu bank=(address >> BANK_SHIFT) - 1U; 00365 if (!paging.tlbh_banks[bank]) 00366 PAGING_InitTLBBank(&paging.tlbh_banks[bank]); 00367 return &paging.tlbh_banks[bank][index & BANK_MASK]; 00368 } 00369 return &paging.tlbh[index]; 00370 } 00371 00372 static INLINE HostPt get_tlb_read(const PhysPt address) { 00373 return get_tlb_entry(address)->read; 00374 } 00375 static INLINE HostPt get_tlb_write(const PhysPt address) { 00376 return get_tlb_entry(address)->write; 00377 } 00378 static INLINE PageHandler* get_tlb_readhandler(const PhysPt address) { 00379 return get_tlb_entry(address)->readhandler; 00380 } 00381 static INLINE PageHandler* get_tlb_writehandler(const PhysPt address) { 00382 return get_tlb_entry(address)->writehandler; 00383 } 00384 00385 /* Use these helper functions to access linear addresses in readX/writeX functions */ 00386 static INLINE PhysPt PAGING_GetPhysicalPage(const PhysPt linePage) { 00387 tlb_entry *entry = get_tlb_entry(linePage); 00388 return (entry->phys_page<<12); 00389 } 00390 00391 static INLINE PhysPt PAGING_GetPhysicalAddress(const PhysPt linAddr) { 00392 tlb_entry *entry = get_tlb_entry(linAddr); 00393 return (entry->phys_page<<12)|(linAddr&0xfff); 00394 } 00395 #endif 00396 00397 /* Special inlined memory reading/writing */ 00398 00399 static INLINE Bit8u mem_readb_inline(const PhysPt address) { 00400 const HostPt tlb_addr=get_tlb_read(address); 00401 if (tlb_addr) return host_readb(tlb_addr+address); 00402 else return (Bit8u)(get_tlb_readhandler(address))->readb(address); 00403 } 00404 00405 static INLINE Bit16u mem_readw_inline(const PhysPt address) { 00406 if ((address & 0xfff)<0xfff) { 00407 const HostPt tlb_addr=get_tlb_read(address); 00408 if (tlb_addr) return host_readw(tlb_addr+address); 00409 else return (Bit16u)(get_tlb_readhandler(address))->readw(address); 00410 } else return mem_unalignedreadw(address); 00411 } 00412 00413 static INLINE Bit32u mem_readd_inline(const PhysPt address) { 00414 if ((address & 0xfff)<0xffd) { 00415 const HostPt tlb_addr=get_tlb_read(address); 00416 if (tlb_addr) return host_readd(tlb_addr+address); 00417 else return (Bit32u)(get_tlb_readhandler(address))->readd(address); 00418 } else return mem_unalignedreadd(address); 00419 } 00420 00421 static INLINE void mem_writeb_inline(const PhysPt address,const Bit8u val) { 00422 const HostPt tlb_addr=get_tlb_write(address); 00423 if (tlb_addr) host_writeb(tlb_addr+address,val); 00424 else (get_tlb_writehandler(address))->writeb(address,val); 00425 } 00426 00427 static INLINE void mem_writew_inline(const PhysPt address,const Bit16u val) { 00428 if ((address & 0xfffu)<0xfffu) { 00429 const HostPt tlb_addr=get_tlb_write(address); 00430 if (tlb_addr) host_writew(tlb_addr+address,val); 00431 else (get_tlb_writehandler(address))->writew(address,val); 00432 } else mem_unalignedwritew(address,val); 00433 } 00434 00435 static INLINE void mem_writed_inline(const PhysPt address,const Bit32u val) { 00436 if ((address & 0xfffu)<0xffdu) { 00437 const HostPt tlb_addr=get_tlb_write(address); 00438 if (tlb_addr) host_writed(tlb_addr+address,val); 00439 else (get_tlb_writehandler(address))->writed(address,val); 00440 } else mem_unalignedwrited(address,val); 00441 } 00442 00443 00444 static INLINE bool mem_readb_checked(const PhysPt address, Bit8u * const val) { 00445 const HostPt tlb_addr=get_tlb_read(address); 00446 if (tlb_addr) { 00447 *val=host_readb(tlb_addr+address); 00448 return false; 00449 } else return (get_tlb_readhandler(address))->readb_checked(address, val); 00450 } 00451 00452 static INLINE bool mem_readw_checked(const PhysPt address, Bit16u * const val) { 00453 if ((address & 0xfffu)<0xfffu) { 00454 const HostPt tlb_addr=get_tlb_read(address); 00455 if (tlb_addr) { 00456 *val=host_readw(tlb_addr+address); 00457 return false; 00458 } else return (get_tlb_readhandler(address))->readw_checked(address, val); 00459 } else return mem_unalignedreadw_checked(address, val); 00460 } 00461 00462 static INLINE bool mem_readd_checked(const PhysPt address, Bit32u * const val) { 00463 if ((address & 0xfffu)<0xffdu) { 00464 const HostPt tlb_addr=get_tlb_read(address); 00465 if (tlb_addr) { 00466 *val=host_readd(tlb_addr+address); 00467 return false; 00468 } else return (get_tlb_readhandler(address))->readd_checked(address, val); 00469 } else return mem_unalignedreadd_checked(address, val); 00470 } 00471 00472 static INLINE bool mem_writeb_checked(const PhysPt address,const Bit8u val) { 00473 const HostPt tlb_addr=get_tlb_write(address); 00474 if (tlb_addr) { 00475 host_writeb(tlb_addr+address,val); 00476 return false; 00477 } else return (get_tlb_writehandler(address))->writeb_checked(address,val); 00478 } 00479 00480 static INLINE bool mem_writew_checked(const PhysPt address,const Bit16u val) { 00481 if ((address & 0xfffu)<0xfffu) { 00482 const HostPt tlb_addr=get_tlb_write(address); 00483 if (tlb_addr) { 00484 host_writew(tlb_addr+address,val); 00485 return false; 00486 } else return (get_tlb_writehandler(address))->writew_checked(address,val); 00487 } else return mem_unalignedwritew_checked(address,val); 00488 } 00489 00490 static INLINE bool mem_writed_checked(const PhysPt address,const Bit32u val) { 00491 if ((address & 0xfffu)<0xffdu) { 00492 const HostPt tlb_addr=get_tlb_write(address); 00493 if (tlb_addr) { 00494 host_writed(tlb_addr+address,val); 00495 return false; 00496 } else return (get_tlb_writehandler(address))->writed_checked(address,val); 00497 } else return mem_unalignedwrited_checked(address,val); 00498 } 00499 00500 extern bool dosbox_allow_nonrecursive_page_fault; /* when set, do nonrecursive mode (when executing instruction) */ 00501 00502 #include <exception> 00503 00504 class GuestPageFaultException : public std::exception { 00505 public: 00506 virtual const char *what() const throw() { 00507 return "Guest page fault exception"; 00508 } 00509 GuestPageFaultException(PhysPt n_lin_addr, Bitu n_page_addr, Bitu n_faultcode) { 00510 lin_addr = n_lin_addr; 00511 page_addr = n_page_addr; 00512 faultcode = n_faultcode; 00513 } 00514 public: 00515 PhysPt lin_addr; 00516 Bitu page_addr; 00517 Bitu faultcode; 00518 }; 00519 00520 #endif