DOSBox-X
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Defines
include/paging.h
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