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