1 /*
2 * Copyright (C) 2002-2021 The DOSBox Team
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License along
15 * with this program; if not, write to the Free Software Foundation, Inc.,
16 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 */
18
19 #include "paging.h"
20
21 #include <cassert>
22 #include <cstdlib>
23 #include <cstring>
24 #include <memory>
25
26 #include "mem.h"
27 #include "regs.h"
28 #include "lazyflags.h"
29 #include "cpu.h"
30 #include "debug.h"
31 #include "setup.h"
32
33 #define LINK_TOTAL (64*1024)
34
35 #define USERWRITE_PROHIBITED ((cpu.cpl&cpu.mpl)==3)
36
37
38 PagingBlock paging;
39
readb(PhysPt addr)40 uint8_t PageHandler::readb(PhysPt addr)
41 {
42 E_Exit("No byte handler for read from %d",addr);
43 return 0;
44 }
readw(PhysPt addr)45 uint16_t PageHandler::readw(PhysPt addr)
46 {
47 uint16_t ret = (readb(addr+0) << 0);
48 ret |= (readb(addr+1) << 8);
49 return ret;
50 }
readd(PhysPt addr)51 uint32_t PageHandler::readd(PhysPt addr)
52 {
53 uint32_t ret = (readb(addr+0) << 0);
54 ret |= (readb(addr+1) << 8);
55 ret |= (readb(addr+2) << 16);
56 ret |= (readb(addr+3) << 24);
57 return ret;
58 }
59
writeb(PhysPt addr,uint8_t)60 void PageHandler::writeb(PhysPt addr, uint8_t /*val*/)
61 {
62 E_Exit("No byte handler for write to %d",addr);
63 }
64
writew(PhysPt addr,uint16_t val)65 void PageHandler::writew(PhysPt addr, uint16_t val)
66 {
67 writeb(addr+0,(Bit8u) (val >> 0));
68 writeb(addr+1,(Bit8u) (val >> 8));
69 }
writed(PhysPt addr,uint32_t val)70 void PageHandler::writed(PhysPt addr, uint32_t val)
71 {
72 writeb(addr+0,(Bit8u) (val >> 0));
73 writeb(addr+1,(Bit8u) (val >> 8));
74 writeb(addr+2,(Bit8u) (val >> 16));
75 writeb(addr+3,(Bit8u) (val >> 24));
76 }
77
GetHostReadPt(Bitu)78 HostPt PageHandler::GetHostReadPt(Bitu /*phys_page*/) {
79 return 0;
80 }
81
GetHostWritePt(Bitu)82 HostPt PageHandler::GetHostWritePt(Bitu /*phys_page*/) {
83 return 0;
84 }
85
readb_checked(PhysPt addr,uint8_t * val)86 bool PageHandler::readb_checked(PhysPt addr, uint8_t *val)
87 {
88 *val = readb(addr);
89 return false;
90 }
readw_checked(PhysPt addr,uint16_t * val)91 bool PageHandler::readw_checked(PhysPt addr, uint16_t *val)
92 {
93 *val = readw(addr);
94 return false;
95 }
readd_checked(PhysPt addr,uint32_t * val)96 bool PageHandler::readd_checked(PhysPt addr, uint32_t *val)
97 {
98 *val = readd(addr);
99 return false;
100 }
writeb_checked(PhysPt addr,uint8_t val)101 bool PageHandler::writeb_checked(PhysPt addr, uint8_t val)
102 {
103 writeb(addr,val); return false;
104 }
writew_checked(PhysPt addr,uint16_t val)105 bool PageHandler::writew_checked(PhysPt addr, uint16_t val)
106 {
107 writew(addr,val); return false;
108 }
writed_checked(PhysPt addr,uint32_t val)109 bool PageHandler::writed_checked(PhysPt addr, uint32_t val)
110 {
111 writed(addr,val); return false;
112 }
113
114 struct PF_Entry {
115 Bitu cs;
116 Bitu eip;
117 Bitu page_addr;
118 Bitu mpl;
119 };
120
121 #define PF_QUEUESIZE 16
122 static struct {
123 uint8_t used = 0; // keeps track of number of entries
124 PF_Entry entries[PF_QUEUESIZE];
125 } pf_queue;
126
PageFaultCore(void)127 static Bits PageFaultCore(void) {
128 CPU_CycleLeft+=CPU_Cycles;
129 CPU_Cycles=1;
130 Bits ret=CPU_Core_Full_Run();
131 CPU_CycleLeft+=CPU_Cycles;
132 if (ret<0) E_Exit("Got a dosbox close machine in pagefault core?");
133 if (ret)
134 return ret;
135 if (!pf_queue.used) E_Exit("PF Core without PF");
136 PF_Entry * entry=&pf_queue.entries[pf_queue.used-1];
137 X86PageEntry pentry;
138 pentry.load=phys_readd(entry->page_addr);
139 if (pentry.block.p && entry->cs == SegValue(cs) && entry->eip==reg_eip) {
140 cpu.mpl=entry->mpl;
141 return -1;
142 }
143 return 0;
144 }
145
146 bool first=false;
147
PAGING_PageFault(PhysPt lin_addr,Bitu page_addr,uint32_t faultcode)148 void PAGING_PageFault(PhysPt lin_addr,Bitu page_addr,uint32_t faultcode) {
149 /* Save the state of the cpu cores */
150 LazyFlags old_lflags;
151 memcpy(&old_lflags,&lflags,sizeof(LazyFlags));
152 CPU_Decoder * old_cpudecoder;
153 old_cpudecoder=cpudecoder;
154 cpudecoder=&PageFaultCore;
155 paging.cr2=lin_addr;
156 PF_Entry * entry=&pf_queue.entries[pf_queue.used++];
157 LOG(LOG_PAGING, LOG_NORMAL)("PageFault at %X type [%x] queue %u", lin_addr, faultcode, pf_queue.used);
158 // LOG_MSG("EAX:%04X ECX:%04X EDX:%04X EBX:%04X",reg_eax,reg_ecx,reg_edx,reg_ebx);
159 // LOG_MSG("CS:%04X EIP:%08X SS:%04x SP:%08X",SegValue(cs),reg_eip,SegValue(ss),reg_esp);
160 entry->cs=SegValue(cs);
161 entry->eip=reg_eip;
162 entry->page_addr=page_addr;
163 entry->mpl=cpu.mpl;
164 cpu.mpl=3;
165
166 CPU_Exception(EXCEPTION_PF,faultcode);
167 DOSBOX_RunMachine();
168 pf_queue.used--;
169 LOG(LOG_PAGING, LOG_NORMAL)("Left PageFault for %x queue %u", lin_addr, pf_queue.used);
170 memcpy(&lflags,&old_lflags,sizeof(LazyFlags));
171 cpudecoder=old_cpudecoder;
172 // LOG_MSG("SS:%04x SP:%08X",SegValue(ss),reg_esp);
173 }
174
InitPageUpdateLink(Bitu relink,PhysPt addr)175 static inline void InitPageUpdateLink(Bitu relink,PhysPt addr) {
176 if (relink==0) return;
177 if (paging.links.used) {
178 if (paging.links.entries[paging.links.used-1]==(addr>>12)) {
179 paging.links.used--;
180 PAGING_UnlinkPages(addr>>12,1);
181 }
182 }
183 if (relink>1) PAGING_LinkPage_ReadOnly(addr>>12,relink);
184 }
185
InitPageCheckPresence(PhysPt lin_addr,bool writing,X86PageEntry & table,X86PageEntry & entry)186 static inline void InitPageCheckPresence(PhysPt lin_addr,bool writing,X86PageEntry& table,X86PageEntry& entry) {
187 Bitu lin_page=lin_addr >> 12;
188 Bitu d_index=lin_page >> 10;
189 Bitu t_index=lin_page & 0x3ff;
190 Bitu table_addr=(paging.base.page<<12)+d_index*4;
191 table.load=phys_readd(table_addr);
192 if (!table.block.p) {
193 LOG(LOG_PAGING,LOG_NORMAL)("NP Table");
194 PAGING_PageFault(lin_addr,table_addr,
195 (writing?0x02:0x00) | (((cpu.cpl&cpu.mpl)==0)?0x00:0x04));
196 table.load=phys_readd(table_addr);
197 if (GCC_UNLIKELY(!table.block.p))
198 E_Exit("Pagefault didn't correct table");
199 }
200 Bitu entry_addr=(table.block.base<<12)+t_index*4;
201 entry.load=phys_readd(entry_addr);
202 if (!entry.block.p) {
203 // LOG(LOG_PAGING,LOG_NORMAL)("NP Page");
204 PAGING_PageFault(lin_addr,entry_addr,
205 (writing?0x02:0x00) | (((cpu.cpl&cpu.mpl)==0)?0x00:0x04));
206 entry.load=phys_readd(entry_addr);
207 if (GCC_UNLIKELY(!entry.block.p))
208 E_Exit("Pagefault didn't correct page");
209 }
210 }
211
InitPageCheckPresence_CheckOnly(PhysPt lin_addr,bool writing,X86PageEntry & table,X86PageEntry & entry)212 static inline bool InitPageCheckPresence_CheckOnly(PhysPt lin_addr,bool writing,X86PageEntry& table,X86PageEntry& entry) {
213 Bitu lin_page=lin_addr >> 12;
214 Bitu d_index=lin_page >> 10;
215 Bitu t_index=lin_page & 0x3ff;
216 Bitu table_addr=(paging.base.page<<12)+d_index*4;
217 table.load=phys_readd(table_addr);
218 if (!table.block.p) {
219 paging.cr2=lin_addr;
220 cpu.exception.which=EXCEPTION_PF;
221 cpu.exception.error=(writing?0x02:0x00) | (((cpu.cpl&cpu.mpl)==0)?0x00:0x04);
222 return false;
223 }
224 Bitu entry_addr=(table.block.base<<12)+t_index*4;
225 entry.load=phys_readd(entry_addr);
226 if (!entry.block.p) {
227 paging.cr2=lin_addr;
228 cpu.exception.which=EXCEPTION_PF;
229 cpu.exception.error=(writing?0x02:0x00) | (((cpu.cpl&cpu.mpl)==0)?0x00:0x04);
230 return false;
231 }
232 return true;
233 }
234
235 // check if a user-level memory access would trigger a privilege page fault
InitPage_CheckUseraccess(Bitu u1,Bitu u2)236 static inline bool InitPage_CheckUseraccess(Bitu u1,Bitu u2) {
237 switch (CPU_ArchitectureType) {
238 case CPU_ARCHTYPE_MIXED:
239 case CPU_ARCHTYPE_386SLOW:
240 case CPU_ARCHTYPE_386FAST:
241 default:
242 return ((u1)==0) && ((u2)==0);
243 case CPU_ARCHTYPE_486OLDSLOW:
244 case CPU_ARCHTYPE_486NEWSLOW:
245 case CPU_ARCHTYPE_PENTIUMSLOW:
246 return ((u1)==0) || ((u2)==0);
247 }
248 }
249
250
251 class InitPageHandler final : public PageHandler {
252 public:
InitPageHandler()253 InitPageHandler() {
254 flags=PFLAG_INIT|PFLAG_NOCODE;
255 }
readb(PhysPt addr)256 uint8_t readb(PhysPt addr)
257 {
258 const auto needs_reset = InitPage(addr, false);
259 const auto val = mem_readb(addr);
260 InitPageUpdateLink(needs_reset, addr);
261 return val;
262 }
readw(PhysPt addr)263 uint16_t readw(PhysPt addr)
264 {
265 const auto needs_reset = InitPage(addr, false);
266 const auto val = mem_readw(addr);
267 InitPageUpdateLink(needs_reset, addr);
268 return val;
269 }
readd(PhysPt addr)270 uint32_t readd(PhysPt addr)
271 {
272 const auto needs_reset = InitPage(addr, false);
273 const auto val = mem_readd(addr);
274 InitPageUpdateLink(needs_reset, addr);
275 return val;
276 }
writeb(PhysPt addr,uint8_t val)277 void writeb(PhysPt addr, uint8_t val)
278 {
279 const auto needs_reset = InitPage(addr, true);
280 mem_writeb(addr, val);
281 InitPageUpdateLink(needs_reset,addr);
282 }
writew(PhysPt addr,uint16_t val)283 void writew(PhysPt addr, uint16_t val)
284 {
285 const auto needs_reset = InitPage(addr, true);
286 mem_writew(addr, val);
287 InitPageUpdateLink(needs_reset,addr);
288 }
writed(PhysPt addr,uint32_t val)289 void writed(PhysPt addr, uint32_t val)
290 {
291 const auto needs_reset = InitPage(addr, true);
292 mem_writed(addr, val);
293 InitPageUpdateLink(needs_reset,addr);
294 }
readb_checked(PhysPt addr,uint8_t * val)295 bool readb_checked(PhysPt addr, uint8_t *val)
296 {
297 if (InitPageCheckOnly(addr,false)) {
298 *val=mem_readb(addr);
299 return false;
300 } else return true;
301 }
readw_checked(PhysPt addr,uint16_t * val)302 bool readw_checked(PhysPt addr, uint16_t *val)
303 {
304 if (InitPageCheckOnly(addr,false)){
305 *val=mem_readw(addr);
306 return false;
307 } else return true;
308 }
readd_checked(PhysPt addr,uint32_t * val)309 bool readd_checked(PhysPt addr, uint32_t *val)
310 {
311 if (InitPageCheckOnly(addr,false)) {
312 *val=mem_readd(addr);
313 return false;
314 } else return true;
315 }
writeb_checked(PhysPt addr,uint8_t val)316 bool writeb_checked(PhysPt addr, uint8_t val)
317 {
318 if (InitPageCheckOnly(addr,true)) {
319 mem_writeb(addr,val);
320 return false;
321 } else return true;
322 }
writew_checked(PhysPt addr,uint16_t val)323 bool writew_checked(PhysPt addr, uint16_t val)
324 {
325 if (InitPageCheckOnly(addr,true)) {
326 mem_writew(addr,val);
327 return false;
328 } else return true;
329 }
writed_checked(PhysPt addr,uint32_t val)330 bool writed_checked(PhysPt addr, uint32_t val)
331 {
332 if (InitPageCheckOnly(addr,true)) {
333 mem_writed(addr,val);
334 return false;
335 } else return true;
336 }
InitPage(Bitu lin_addr,bool writing)337 Bitu InitPage(Bitu lin_addr,bool writing) {
338 Bitu lin_page=lin_addr >> 12;
339 Bitu phys_page;
340 if (paging.enabled) {
341 X86PageEntry table;
342 X86PageEntry entry;
343 InitPageCheckPresence(lin_addr,writing,table,entry);
344
345 // 0: no action
346 // 1: can (but currently does not) fail a user-level access privilege check
347 // 2: can (but currently does not) fail a write privilege check
348 // 3: fails a privilege check
349 Bitu priv_check=0;
350 if (InitPage_CheckUseraccess(entry.block.us,table.block.us)) {
351 if ((cpu.cpl&cpu.mpl)==3) priv_check=3;
352 else {
353 switch (CPU_ArchitectureType) {
354 case CPU_ARCHTYPE_MIXED:
355 case CPU_ARCHTYPE_386FAST:
356 default:
357 // priv_check=0; // default
358 break;
359 case CPU_ARCHTYPE_386SLOW:
360 case CPU_ARCHTYPE_486OLDSLOW:
361 case CPU_ARCHTYPE_486NEWSLOW:
362 case CPU_ARCHTYPE_PENTIUMSLOW:
363 priv_check=1;
364 break;
365 }
366 }
367 }
368 if ((entry.block.wr==0) || (table.block.wr==0)) {
369 // page is write-protected for user mode
370 if (priv_check==0) {
371 switch (CPU_ArchitectureType) {
372 case CPU_ARCHTYPE_MIXED:
373 case CPU_ARCHTYPE_386FAST:
374 default:
375 // priv_check=0; // default
376 break;
377 case CPU_ARCHTYPE_386SLOW:
378 case CPU_ARCHTYPE_486OLDSLOW:
379 case CPU_ARCHTYPE_486NEWSLOW:
380 case CPU_ARCHTYPE_PENTIUMSLOW:
381 priv_check=2;
382 break;
383 }
384 }
385 // check if actually failing the write-protected check
386 if (writing && USERWRITE_PROHIBITED) priv_check=3;
387 }
388 if (priv_check==3) {
389 LOG(LOG_PAGING, LOG_NORMAL)("Page access denied: cpl=%i, %x:%x:%x:%x",
390 static_cast<int>(cpu.cpl), entry.block.us, table.block.us, entry.block.wr, table.block.wr);
391 PAGING_PageFault(lin_addr,(table.block.base<<12)+(lin_page & 0x3ff)*4,0x05 | (writing?0x02:0x00));
392 priv_check=0;
393 }
394
395 if (!table.block.a) {
396 table.block.a=1; // set page table accessed
397 phys_writed((paging.base.page<<12)+(lin_page >> 10)*4,table.load);
398 }
399 if ((!entry.block.a) || (!entry.block.d)) {
400 entry.block.a=1; // set page accessed
401
402 // page is dirty if we're writing to it, or if we're reading but the
403 // page will be fully linked so we can't track later writes
404 if (writing || (priv_check==0)) entry.block.d=1; // mark page as dirty
405
406 phys_writed((table.block.base<<12)+(lin_page & 0x3ff)*4,entry.load);
407 }
408
409 phys_page=entry.block.base;
410
411 // now see how the page should be linked best, if we need to catch privilege
412 // checks later on it should be linked as read-only page
413 if (priv_check==0) {
414 // if reading we could link the page as read-only to later cacth writes,
415 // will slow down pretty much but allows catching all dirty events
416 PAGING_LinkPage(lin_page,phys_page);
417 } else {
418 if (priv_check==1) {
419 PAGING_LinkPage(lin_page,phys_page);
420 return 1;
421 } else if (writing) {
422 PageHandler * handler=MEM_GetPageHandler(phys_page);
423 PAGING_LinkPage(lin_page,phys_page);
424 if (!(handler->flags & PFLAG_READABLE)) return 1;
425 if (!(handler->flags & PFLAG_WRITEABLE)) return 1;
426 if (get_tlb_read(lin_addr)!=get_tlb_write(lin_addr)) return 1;
427 if (phys_page>1) return phys_page;
428 else return 1;
429 } else {
430 PAGING_LinkPage_ReadOnly(lin_page,phys_page);
431 }
432 }
433 } else {
434 if (lin_page<LINK_START) phys_page=paging.firstmb[lin_page];
435 else phys_page=lin_page;
436 PAGING_LinkPage(lin_page,phys_page);
437 }
438 return 0;
439 }
InitPageCheckOnly(Bitu lin_addr,bool writing)440 bool InitPageCheckOnly(Bitu lin_addr,bool writing) {
441 Bitu lin_page=lin_addr >> 12;
442 if (paging.enabled) {
443 X86PageEntry table;
444 X86PageEntry entry;
445 if (!InitPageCheckPresence_CheckOnly(lin_addr,writing,table,entry)) return false;
446
447 if (!USERWRITE_PROHIBITED) return true;
448
449 if (InitPage_CheckUseraccess(entry.block.us,table.block.us) ||
450 (((entry.block.wr==0) || (table.block.wr==0)) && writing)) {
451 LOG(LOG_PAGING, LOG_NORMAL)("Page access denied: cpl=%i, %x:%x:%x:%x",
452 static_cast<int>(cpu.cpl), entry.block.us, table.block.us, entry.block.wr, table.block.wr);
453 paging.cr2=lin_addr;
454 cpu.exception.which=EXCEPTION_PF;
455 cpu.exception.error=0x05 | (writing?0x02:0x00);
456 return false;
457 }
458 } else {
459 Bitu phys_page;
460 if (lin_page<LINK_START) phys_page=paging.firstmb[lin_page];
461 else phys_page=lin_page;
462 PAGING_LinkPage(lin_page,phys_page);
463 }
464 return true;
465 }
InitPageForced(Bitu lin_addr)466 void InitPageForced(Bitu lin_addr) {
467 Bitu lin_page=lin_addr >> 12;
468 Bitu phys_page;
469 if (paging.enabled) {
470 X86PageEntry table;
471 X86PageEntry entry;
472 InitPageCheckPresence(lin_addr,false,table,entry);
473
474 if (!table.block.a) {
475 table.block.a=1; //Set access
476 phys_writed((paging.base.page<<12)+(lin_page >> 10)*4,table.load);
477 }
478 if (!entry.block.a) {
479 entry.block.a=1; //Set access
480 phys_writed((table.block.base<<12)+(lin_page & 0x3ff)*4,entry.load);
481 }
482 phys_page=entry.block.base;
483 // maybe use read-only page here if possible
484 } else {
485 if (lin_page<LINK_START) phys_page=paging.firstmb[lin_page];
486 else phys_page=lin_page;
487 }
488 PAGING_LinkPage(lin_page,phys_page);
489 }
490 };
491
492 class InitPageUserROHandler final : public PageHandler {
493 public:
InitPageUserROHandler()494 InitPageUserROHandler() {
495 flags=PFLAG_INIT|PFLAG_NOCODE;
496 }
writeb(PhysPt addr,uint8_t val)497 void writeb(PhysPt addr, uint8_t val)
498 {
499 InitPage(addr, val);
500 host_writeb(get_tlb_read(addr) + addr, val);
501 }
writew(PhysPt addr,uint16_t val)502 void writew(PhysPt addr, uint16_t val)
503 {
504 InitPage(addr, val);
505 host_writew(get_tlb_read(addr) + addr, val);
506 }
writed(PhysPt addr,uint32_t val)507 void writed(PhysPt addr, uint32_t val)
508 {
509 InitPage(addr, val);
510 host_writed(get_tlb_read(addr) + addr, val);
511 }
writeb_checked(PhysPt addr,uint8_t val)512 bool writeb_checked(PhysPt addr, uint8_t val)
513 {
514 Bitu writecode = InitPageCheckOnly(addr, val);
515 if (writecode) {
516 HostPt tlb_addr;
517 if (writecode>1) tlb_addr=get_tlb_read(addr);
518 else tlb_addr=get_tlb_write(addr);
519 host_writeb(tlb_addr + addr, val);
520 return false;
521 }
522 return true;
523 }
writew_checked(PhysPt addr,uint16_t val)524 bool writew_checked(PhysPt addr, uint16_t val)
525 {
526 Bitu writecode = InitPageCheckOnly(addr, val);
527 if (writecode) {
528 HostPt tlb_addr;
529 if (writecode>1) tlb_addr=get_tlb_read(addr);
530 else tlb_addr=get_tlb_write(addr);
531 host_writew(tlb_addr + addr, val);
532 return false;
533 }
534 return true;
535 }
writed_checked(PhysPt addr,uint32_t val)536 bool writed_checked(PhysPt addr, uint32_t val)
537 {
538 Bitu writecode = InitPageCheckOnly(addr, val);
539 if (writecode) {
540 HostPt tlb_addr;
541 if (writecode>1) tlb_addr=get_tlb_read(addr);
542 else tlb_addr=get_tlb_write(addr);
543 host_writed(tlb_addr + addr, val);
544 return false;
545 }
546 return true;
547 }
InitPage(Bitu lin_addr,Bitu val)548 void InitPage(Bitu lin_addr, [[maybe_unused]] Bitu val) {
549 Bitu lin_page=lin_addr >> 12;
550 Bitu phys_page;
551 if (paging.enabled) {
552 if (!USERWRITE_PROHIBITED) return;
553
554 X86PageEntry table;
555 X86PageEntry entry;
556 InitPageCheckPresence(lin_addr,true,table,entry);
557
558 LOG(LOG_PAGING, LOG_NORMAL)("Page access denied: cpl=%i, %x:%x:%x:%x",
559 static_cast<int>(cpu.cpl), entry.block.us, table.block.us, entry.block.wr, table.block.wr);
560 PAGING_PageFault(lin_addr,(table.block.base<<12)+(lin_page & 0x3ff)*4,0x07);
561
562 if (!table.block.a) {
563 table.block.a=1; //Set access
564 phys_writed((paging.base.page<<12)+(lin_page >> 10)*4,table.load);
565 }
566 if ((!entry.block.a) || (!entry.block.d)) {
567 entry.block.a=1; //Set access
568 entry.block.d=1; //Set dirty
569 phys_writed((table.block.base<<12)+(lin_page & 0x3ff)*4,entry.load);
570 }
571 phys_page=entry.block.base;
572 PAGING_LinkPage(lin_page,phys_page);
573 } else {
574 if (lin_page<LINK_START) phys_page=paging.firstmb[lin_page];
575 else phys_page=lin_page;
576 PAGING_LinkPage(lin_page,phys_page);
577 }
578 }
InitPageCheckOnly(Bitu lin_addr,Bitu val)579 Bitu InitPageCheckOnly(Bitu lin_addr, [[maybe_unused]] Bitu val) {
580 Bitu lin_page=lin_addr >> 12;
581 if (paging.enabled) {
582 if (!USERWRITE_PROHIBITED) return 2;
583
584 X86PageEntry table;
585 X86PageEntry entry;
586 if (!InitPageCheckPresence_CheckOnly(lin_addr,true,table,entry)) return 0;
587
588 if (InitPage_CheckUseraccess(entry.block.us,table.block.us) || (((entry.block.wr==0) || (table.block.wr==0)))) {
589 LOG(LOG_PAGING, LOG_NORMAL)("Page access denied: cpl=%i, %x:%x:%x:%x",
590 static_cast<int>(cpu.cpl), entry.block.us, table.block.us, entry.block.wr, table.block.wr);
591 paging.cr2=lin_addr;
592 cpu.exception.which=EXCEPTION_PF;
593 cpu.exception.error=0x07;
594 return 0;
595 }
596 PAGING_LinkPage(lin_page,entry.block.base);
597 } else {
598 Bitu phys_page;
599 if (lin_page<LINK_START) phys_page=paging.firstmb[lin_page];
600 else phys_page=lin_page;
601 PAGING_LinkPage(lin_page,phys_page);
602 }
603 return 1;
604 }
InitPageForced(Bitu lin_addr)605 void InitPageForced(Bitu lin_addr) {
606 Bitu lin_page=lin_addr >> 12;
607 Bitu phys_page;
608 if (paging.enabled) {
609 X86PageEntry table;
610 X86PageEntry entry;
611 InitPageCheckPresence(lin_addr,true,table,entry);
612
613 if (!table.block.a) {
614 table.block.a=1; //Set access
615 phys_writed((paging.base.page<<12)+(lin_page >> 10)*4,table.load);
616 }
617 if (!entry.block.a) {
618 entry.block.a=1; //Set access
619 phys_writed((table.block.base<<12)+(lin_page & 0x3ff)*4,entry.load);
620 }
621 phys_page=entry.block.base;
622 } else {
623 if (lin_page<LINK_START) phys_page=paging.firstmb[lin_page];
624 else phys_page=lin_page;
625 }
626 PAGING_LinkPage(lin_page,phys_page);
627 }
628 };
629
630
PAGING_MakePhysPage(Bitu & page)631 bool PAGING_MakePhysPage(Bitu & page) {
632 if (paging.enabled) {
633 Bitu d_index=page >> 10;
634 Bitu t_index=page & 0x3ff;
635 X86PageEntry table;
636 table.load=phys_readd((paging.base.page<<12)+d_index*4);
637 if (!table.block.p) return false;
638 X86PageEntry entry;
639 entry.load=phys_readd((table.block.base<<12)+t_index*4);
640 if (!entry.block.p) return false;
641 page=entry.block.base;
642 } else {
643 if (page<LINK_START) page=paging.firstmb[page];
644 //Else keep it the same
645 }
646 return true;
647 }
648
649 static InitPageHandler init_page_handler;
650 static InitPageUserROHandler init_page_handler_userro;
651
652
PAGING_GetDirBase(void)653 Bitu PAGING_GetDirBase(void) {
654 return paging.cr3;
655 }
656
PAGING_ForcePageInit(Bitu lin_addr)657 bool PAGING_ForcePageInit(Bitu lin_addr) {
658 PageHandler * handler=get_tlb_readhandler(lin_addr);
659 if (handler==&init_page_handler) {
660 init_page_handler.InitPageForced(lin_addr);
661 return true;
662 } else if (handler==&init_page_handler_userro) {
663 PAGING_UnlinkPages(lin_addr>>12,1);
664 init_page_handler_userro.InitPageForced(lin_addr);
665 return true;
666 }
667 return false;
668 }
669
670 #if defined(USE_FULL_TLB)
PAGING_InitTLB(void)671 void PAGING_InitTLB(void) {
672 for (Bitu i=0;i<TLB_SIZE;i++) {
673 paging.tlb.read[i]=0;
674 paging.tlb.write[i]=0;
675 paging.tlb.readhandler[i]=&init_page_handler;
676 paging.tlb.writehandler[i]=&init_page_handler;
677 }
678 paging.links.used=0;
679 }
680
PAGING_ClearTLB(void)681 void PAGING_ClearTLB(void) {
682 Bit32u * entries=&paging.links.entries[0];
683 for (;paging.links.used>0;paging.links.used--) {
684 Bitu page=*entries++;
685 paging.tlb.read[page]=0;
686 paging.tlb.write[page]=0;
687 paging.tlb.readhandler[page]=&init_page_handler;
688 paging.tlb.writehandler[page]=&init_page_handler;
689 }
690 paging.links.used=0;
691 }
692
PAGING_UnlinkPages(Bitu lin_page,Bitu pages)693 void PAGING_UnlinkPages(Bitu lin_page,Bitu pages) {
694 for (;pages>0;pages--) {
695 paging.tlb.read[lin_page]=0;
696 paging.tlb.write[lin_page]=0;
697 paging.tlb.readhandler[lin_page]=&init_page_handler;
698 paging.tlb.writehandler[lin_page]=&init_page_handler;
699 lin_page++;
700 }
701 }
702
PAGING_MapPage(Bitu lin_page,Bitu phys_page)703 void PAGING_MapPage(Bitu lin_page,Bitu phys_page) {
704 if (lin_page<LINK_START) {
705 paging.firstmb[lin_page]=phys_page;
706 paging.tlb.read[lin_page]=0;
707 paging.tlb.write[lin_page]=0;
708 paging.tlb.readhandler[lin_page]=&init_page_handler;
709 paging.tlb.writehandler[lin_page]=&init_page_handler;
710 } else {
711 PAGING_LinkPage(lin_page,phys_page);
712 }
713 }
714
PAGING_LinkPage(Bitu lin_page,Bitu phys_page)715 void PAGING_LinkPage(Bitu lin_page,Bitu phys_page) {
716 PageHandler * handler=MEM_GetPageHandler(phys_page);
717 Bitu lin_base=lin_page << 12;
718 if (lin_page>=TLB_SIZE || phys_page>=TLB_SIZE)
719 E_Exit("Illegal page");
720
721 if (paging.links.used >= PAGING_LINKS) {
722 LOG(LOG_PAGING,LOG_NORMAL)("Not enough paging links, resetting cache");
723 PAGING_ClearTLB();
724 assert(paging.links.used == 0);
725 }
726
727 paging.tlb.phys_page[lin_page]=phys_page;
728 if (handler->flags & PFLAG_READABLE) paging.tlb.read[lin_page]=handler->GetHostReadPt(phys_page)-lin_base;
729 else paging.tlb.read[lin_page]=0;
730 if (handler->flags & PFLAG_WRITEABLE) paging.tlb.write[lin_page]=handler->GetHostWritePt(phys_page)-lin_base;
731 else paging.tlb.write[lin_page]=0;
732
733 paging.links.entries[paging.links.used++]=lin_page;
734 paging.tlb.readhandler[lin_page]=handler;
735 paging.tlb.writehandler[lin_page]=handler;
736 }
737
PAGING_LinkPage_ReadOnly(Bitu lin_page,Bitu phys_page)738 void PAGING_LinkPage_ReadOnly(Bitu lin_page,Bitu phys_page) {
739 PageHandler * handler=MEM_GetPageHandler(phys_page);
740 Bitu lin_base=lin_page << 12;
741 if (lin_page>=TLB_SIZE || phys_page>=TLB_SIZE)
742 E_Exit("Illegal page");
743
744 if (paging.links.used >= PAGING_LINKS) {
745 LOG(LOG_PAGING,LOG_NORMAL)("Not enough paging links, resetting cache");
746 PAGING_ClearTLB();
747 assert(paging.links.used == 0);
748 }
749
750 paging.tlb.phys_page[lin_page]=phys_page;
751 if (handler->flags & PFLAG_READABLE) paging.tlb.read[lin_page]=handler->GetHostReadPt(phys_page)-lin_base;
752 else paging.tlb.read[lin_page]=0;
753 paging.tlb.write[lin_page]=0;
754
755 paging.links.entries[paging.links.used++]=lin_page;
756 paging.tlb.readhandler[lin_page]=handler;
757 paging.tlb.writehandler[lin_page]=&init_page_handler_userro;
758 }
759
760 #else
761
InitTLBInt(tlb_entry * bank)762 static inline void InitTLBInt(tlb_entry *bank) {
763 for (Bitu i=0;i<TLB_SIZE;i++) {
764 bank[i].read=0;
765 bank[i].write=0;
766 bank[i].readhandler=&init_page_handler;
767 bank[i].writehandler=&init_page_handler;
768 }
769 }
770
PAGING_InitTLBBank(tlb_entry ** bank)771 void PAGING_InitTLBBank(tlb_entry **bank) {
772 *bank = (tlb_entry *)malloc(sizeof(tlb_entry)*TLB_SIZE);
773 if(!*bank) E_Exit("Out of Memory");
774 InitTLBInt(*bank);
775 }
776
PAGING_InitTLB(void)777 void PAGING_InitTLB(void) {
778 InitTLBInt(paging.tlbh);
779 paging.links.used=0;
780 }
781
PAGING_ClearTLB(void)782 void PAGING_ClearTLB(void) {
783 Bit32u * entries=&paging.links.entries[0];
784 for (;paging.links.used>0;paging.links.used--) {
785 Bitu page=*entries++;
786 tlb_entry *entry = get_tlb_entry(page<<12);
787 entry->read=0;
788 entry->write=0;
789 entry->readhandler=&init_page_handler;
790 entry->writehandler=&init_page_handler;
791 }
792 paging.links.used=0;
793 }
794
PAGING_UnlinkPages(Bitu lin_page,Bitu pages)795 void PAGING_UnlinkPages(Bitu lin_page,Bitu pages) {
796 for (;pages>0;pages--) {
797 tlb_entry *entry = get_tlb_entry(lin_page<<12);
798 entry->read=0;
799 entry->write=0;
800 entry->readhandler=&init_page_handler;
801 entry->writehandler=&init_page_handler;
802 lin_page++;
803 }
804 }
805
PAGING_MapPage(Bitu lin_page,Bitu phys_page)806 void PAGING_MapPage(Bitu lin_page,Bitu phys_page) {
807 if (lin_page<LINK_START) {
808 paging.firstmb[lin_page]=phys_page;
809 paging.tlbh[lin_page].read=0;
810 paging.tlbh[lin_page].write=0;
811 paging.tlbh[lin_page].readhandler=&init_page_handler;
812 paging.tlbh[lin_page].writehandler=&init_page_handler;
813 } else {
814 PAGING_LinkPage(lin_page,phys_page);
815 }
816 }
817
PAGING_LinkPage(Bitu lin_page,Bitu phys_page)818 void PAGING_LinkPage(Bitu lin_page,Bitu phys_page) {
819 PageHandler * handler=MEM_GetPageHandler(phys_page);
820 Bitu lin_base=lin_page << 12;
821 if (lin_page>=(TLB_SIZE*(TLB_BANKS+1)) || phys_page>=(TLB_SIZE*(TLB_BANKS+1)))
822 E_Exit("Illegal page");
823
824 if (paging.links.used>=PAGING_LINKS) {
825 LOG(LOG_PAGING,LOG_NORMAL)("Not enough paging links, resetting cache");
826 PAGING_ClearTLB();
827 }
828
829 tlb_entry *entry = get_tlb_entry(lin_base);
830 entry->phys_page=phys_page;
831 if (handler->flags & PFLAG_READABLE) entry->read=handler->GetHostReadPt(phys_page)-lin_base;
832 else entry->read=0;
833 if (handler->flags & PFLAG_WRITEABLE) entry->write=handler->GetHostWritePt(phys_page)-lin_base;
834 else entry->write=0;
835
836 paging.links.entries[paging.links.used++]=lin_page;
837 entry->readhandler=handler;
838 entry->writehandler=handler;
839 }
840
PAGING_LinkPage_ReadOnly(Bitu lin_page,Bitu phys_page)841 void PAGING_LinkPage_ReadOnly(Bitu lin_page,Bitu phys_page) {
842 PageHandler * handler=MEM_GetPageHandler(phys_page);
843 Bitu lin_base=lin_page << 12;
844 if (lin_page>=(TLB_SIZE*(TLB_BANKS+1)) || phys_page>=(TLB_SIZE*(TLB_BANKS+1)))
845 E_Exit("Illegal page");
846
847 if (paging.links.used>=PAGING_LINKS) {
848 LOG(LOG_PAGING,LOG_NORMAL)("Not enough paging links, resetting cache");
849 PAGING_ClearTLB();
850 }
851
852 tlb_entry *entry = get_tlb_entry(lin_base);
853 entry->phys_page=phys_page;
854 if (handler->flags & PFLAG_READABLE) entry->read=handler->GetHostReadPt(phys_page)-lin_base;
855 else entry->read=0;
856 entry->write=0;
857
858 paging.links.entries[paging.links.used++]=lin_page;
859 entry->readhandler=handler;
860 entry->writehandler=&init_page_handler_userro;
861 }
862
863 #endif
864
865
PAGING_SetDirBase(Bitu cr3)866 void PAGING_SetDirBase(Bitu cr3) {
867 paging.cr3=cr3;
868
869 paging.base.page=cr3 >> 12;
870 paging.base.addr=cr3 & ~4095;
871 // LOG(LOG_PAGING,LOG_NORMAL)("CR3:%X Base %X",cr3,paging.base.page);
872 if (paging.enabled) {
873 PAGING_ClearTLB();
874 }
875 }
876
PAGING_Enable(bool enabled)877 void PAGING_Enable(bool enabled) {
878 /* If paging is disabled, we work from a default paging table */
879 if (paging.enabled==enabled) return;
880 paging.enabled=enabled;
881 if (enabled) {
882 if (GCC_UNLIKELY(cpudecoder==CPU_Core_Simple_Run)) {
883 // LOG_MSG("CPU core simple won't run this game,switching to normal");
884 cpudecoder=CPU_Core_Normal_Run;
885 CPU_CycleLeft+=CPU_Cycles;
886 CPU_Cycles=0;
887 }
888 // LOG(LOG_PAGING,LOG_NORMAL)("Enabled");
889 PAGING_SetDirBase(paging.cr3);
890 }
891 PAGING_ClearTLB();
892 }
893
PAGING_Enabled(void)894 bool PAGING_Enabled(void) {
895 return paging.enabled;
896 }
897
898 class PAGING final : public Module_base{
899 public:
PAGING(Section * configuration)900 PAGING(Section* configuration):Module_base(configuration){
901 /* Setup default Page Directory, force it to update */
902 paging.enabled=false;
903 PAGING_InitTLB();
904 Bitu i;
905 for (i=0;i<LINK_START;i++) {
906 paging.firstmb[i]=i;
907 }
908 pf_queue.used=0;
909 }
910 };
911
912 static std::unique_ptr<PAGING> paging_instance = nullptr;
913
PAGING_Init(Section * sec)914 void PAGING_Init(Section *sec)
915 {
916 paging_instance = std::make_unique<PAGING>(sec);
917 }
918