mirror of
https://github.com/joncampbell123/dosbox-x.git
synced 2025-05-09 20:01:19 +08:00
phys_read/write functions are not used often enough to worry too much about performance, so to avoid emulator segfaults, put the system memory range check in the phys_read/write functions themselves. Add comments about the meaning of MemBase and MemSize and how they will be used when DOSBox-X eventually supports memory sizes larger than 4GB
This commit is contained in:
parent
205480815e
commit
3f41eb6123
@ -39,6 +39,7 @@ typedef uint64_t PhysPt64; /* guest physical memory pointer */
|
||||
typedef int32_t MemHandle;
|
||||
|
||||
extern HostPt MemBase;
|
||||
extern size_t MemSize;
|
||||
|
||||
HostPt GetMemBase(void);
|
||||
bool MEM_A20_Enabled(void);
|
||||
@ -184,25 +185,59 @@ void phys_writes(PhysPt addr, const char* string, Bitu length);
|
||||
|
||||
/* WARNING: These will cause a segfault or out of bounds access IF
|
||||
* addr is beyond the end of memory */
|
||||
/* 2024/12/22: Looking across the DOSBox-X codebase, these functions
|
||||
* aren't used TOO often, and where they are used, some
|
||||
* code has memory range checks anyway. So it doesn't hurt
|
||||
* performance very much if at all to just put the range
|
||||
* check here instead, in order not to segfault if somehow
|
||||
* the address given is beyond end of system memory. --J.C.
|
||||
*
|
||||
* There is one more important detail here. These functions
|
||||
* take only a 32-bit physical address. Which means if more
|
||||
* than 32 address bits are enabled on the CPU and the OS
|
||||
* has PSE/PAE page tables enabled, these functions will not
|
||||
* be able to reach above 4GB. Given how memory will be
|
||||
* segmented between the 'below 4GB' and 'above 4GB' regions,
|
||||
* if emulating 4GB or more, that is perfectly fine. S3 XGA
|
||||
* and ISA DMA emulation will never reach above 4GB anyway.
|
||||
*
|
||||
* The way the range check is done is ideal for performance,
|
||||
* yet may fail to work correctly if MemSize is very close
|
||||
* to zero, low enough that subtraction would cause it to
|
||||
* wrap back around to the largest possible value. The code,
|
||||
* when MemBase is a valid pointer, will never set MemSize
|
||||
* that small. */
|
||||
|
||||
static INLINE void phys_writeb(const PhysPt addr,const uint8_t val) {
|
||||
if (addr < MemSize)
|
||||
host_writeb(MemBase+addr,val);
|
||||
}
|
||||
static INLINE void phys_writew(const PhysPt addr,const uint16_t val) {
|
||||
if (addr < (MemSize-1u))
|
||||
host_writew(MemBase+addr,val);
|
||||
}
|
||||
static INLINE void phys_writed(const PhysPt addr,const uint32_t val) {
|
||||
if (addr < (MemSize-3u))
|
||||
host_writed(MemBase+addr,val);
|
||||
}
|
||||
|
||||
static INLINE uint8_t phys_readb(const PhysPt addr) {
|
||||
if (addr < MemSize)
|
||||
return host_readb(MemBase+addr);
|
||||
else
|
||||
return 0xFF;
|
||||
}
|
||||
static INLINE uint16_t phys_readw(const PhysPt addr) {
|
||||
if (addr < (MemSize-1u))
|
||||
return host_readw(MemBase+addr);
|
||||
else
|
||||
return 0xFFFFu;
|
||||
}
|
||||
static INLINE uint32_t phys_readd(const PhysPt addr) {
|
||||
if (addr < (MemSize-3u))
|
||||
return host_readd(MemBase+addr);
|
||||
else
|
||||
return 0xFFFFFFFFu;
|
||||
}
|
||||
|
||||
/* These don't check for alignment, better be sure it's correct */
|
||||
|
@ -797,14 +797,8 @@ initpage_retry:
|
||||
PhysPt dirEntryAddr = GetPageDirectoryEntryAddr(lin_addr);
|
||||
// Range check to avoid emulator segfault: phys_readd() reads from MemBase+addr and does NOT range check.
|
||||
// Needed to avoid segfault when running 1999 demo "Void Main" in a bootable Windows 95 image in pure DOS mode.
|
||||
if ((dirEntryAddr+4) <= (MEM_TotalPages()<<12u)) {
|
||||
// 2024/12/22: phys_readx() does range checking now
|
||||
dir_entry.load=phys_readd(dirEntryAddr);
|
||||
}
|
||||
else {
|
||||
LOG(LOG_CPU,LOG_WARN)("Page directory access beyond end of memory, page %08x >= %08x",
|
||||
(unsigned int)(dirEntryAddr>>12u),(unsigned int)MEM_TotalPages());
|
||||
dir_entry.load=0xFFFFFFFF;
|
||||
}
|
||||
|
||||
if (!dir_entry.dirblock.p) {
|
||||
// table pointer is not present, do a page fault
|
||||
@ -851,14 +845,8 @@ initpage_retry:
|
||||
else {
|
||||
PhysPt tableEntryAddr = GetPageTableEntryAddr(lin_addr, dir_entry);
|
||||
// Range check to avoid emulator segfault: phys_readd() reads from MemBase+addr and does NOT range check.
|
||||
if ((tableEntryAddr+4) <= (MEM_TotalPages()<<12u)) {
|
||||
// 2024/12/22: phys_readx() does range checking now
|
||||
table_entry.load=phys_readd(tableEntryAddr);
|
||||
}
|
||||
else {
|
||||
LOG(LOG_CPU,LOG_WARN)("Page table entry access beyond end of memory, page %08x >= %08x",
|
||||
(unsigned int)(tableEntryAddr>>12u),(unsigned int)MEM_TotalPages());
|
||||
table_entry.load=0xFFFFFFFF;
|
||||
}
|
||||
|
||||
// set page table accessed (IA manual: A is set whenever the entry is
|
||||
// used in a page translation)
|
||||
|
@ -215,7 +215,14 @@ uint32_t MEM_get_address_bits4GB() { /* some code cannot yet handle values large
|
||||
return memory.address_bits;
|
||||
}
|
||||
|
||||
/* WARNING: When DOSBox-X enables emulation of more than 4GB of RAM, this MemBase and MemSize will only reflect the memory below 4GB.
|
||||
* Which means phys_readx/writex(), which are limited to the first 4GB anyway (32-bit addresses), cannot be used to poke at
|
||||
* memory above 4GB. Instead of extending MemBase and the singular allocation block to 4GB or larger, the memory above 4GB
|
||||
* is a different block. The reason for this is that the gap that needs to be left open for PCI devices and the ROM BIOS is
|
||||
* large enough that such an arrangement would lead to the waste of about 64MB of emulator memory, which is significant, while
|
||||
* the 384KB wasted at the 8086 1MB limit is too small to worry about. */
|
||||
HostPt MemBase = NULL;
|
||||
size_t MemSize = 0;
|
||||
|
||||
class UnmappedPageHandler : public PageHandler {
|
||||
public:
|
||||
@ -1333,7 +1340,7 @@ void mem_writed(PhysPt address,uint32_t val) {
|
||||
}
|
||||
|
||||
void phys_writes(PhysPt addr, const char* string, Bitu length) {
|
||||
for(Bitu i = 0; i < length; i++) host_writeb(MemBase+addr+i,(uint8_t)string[i]);
|
||||
for(Bitu i = 0; i < length && (addr+i) < MemSize; i++) host_writeb(MemBase+addr+i,(uint8_t)string[i]);
|
||||
}
|
||||
|
||||
#include "control.h"
|
||||
@ -1882,6 +1889,7 @@ void ShutDownRAM(Section * sec) {
|
||||
#endif
|
||||
MemBase = NULL;
|
||||
}
|
||||
MemSize = 0;
|
||||
ACPI_free();
|
||||
}
|
||||
|
||||
@ -2014,6 +2022,7 @@ void Init_RAM() {
|
||||
#else // C_GAMELINK
|
||||
MemBase = new(std::nothrow) uint8_t[memory.pages*4096];
|
||||
#endif // C_GAMELINK
|
||||
MemSize = size_t(memory.pages*4096);
|
||||
if (!MemBase) E_Exit("Can't allocate main memory of %d KB",(int)memsizekb);
|
||||
/* Clear the memory, as new doesn't always give zeroed memory
|
||||
* (Visual C debug mode). We want zeroed memory though. */
|
||||
|
Loading…
x
Reference in New Issue
Block a user