diff --git a/src/core/debug.cc b/src/core/debug.cc index a2c27bc7e..a85ed7cc1 100644 --- a/src/core/debug.cc +++ b/src/core/debug.cc @@ -59,6 +59,7 @@ uint32_t PCSX::Debug::normalizeAddress(uint32_t address) { bool PCSX::Debug::isInKernel(uint32_t address, bool biosIsKernel) { PSXAddress addr(address); + if (addr.type == PSXAddress::Type::MSAN) return false; const bool ramExpansion = PCSX::g_emulator->settings.get(); if (addr.type == PSXAddress::Type::ROM) return biosIsKernel; if (addr.type != PSXAddress::Type::RAM) return false; diff --git a/src/core/psxhw.cc b/src/core/psxhw.cc index ccb69a48c..800c46362 100644 --- a/src/core/psxhw.cc +++ b/src/core/psxhw.cc @@ -360,7 +360,17 @@ uint32_t PCSX::HW::read32(uint32_t add) { case 0x1f802080: hard = 0x58534350; break; - + case 0x1f80208c: { + uint32_t size = g_emulator->m_cpu->m_regs.GPR.n.a0; + hard = g_emulator->m_mem->msanAlloc(size); + break; + } + case 0x1f802090: { + uint32_t ptr = g_emulator->m_cpu->m_regs.GPR.n.a0; + uint32_t size = g_emulator->m_cpu->m_regs.GPR.n.a1; + hard = g_emulator->m_mem->msanRealloc(ptr, size); + break; + } default: { uint32_t *ptr = (uint32_t *)&g_emulator->m_mem->m_hard[hwadd & 0xffff]; hard = SWAP_LEu32(*ptr); @@ -445,7 +455,9 @@ void PCSX::HW::write8(uint32_t add, uint32_t rawvalue) { case 0x1f802088: g_emulator->m_debug->m_checkKernel = value; break; - + case 0x1f802089: + g_emulator->m_mem->initMsan(value); + break; default: if (addressInRegisterSpace(hwadd)) { uint32_t *ptr = (uint32_t *)&g_emulator->m_mem->m_hard[hwadd & 0xffff]; @@ -789,6 +801,10 @@ void PCSX::HW::write32(uint32_t add, uint32_t value) { g_system->message("%s", memFile->gets()); break; } + case 0x1f80208c: { + g_emulator->m_mem->msanFree(value); + break; + } default: { if ((hwadd >= 0x1f801c00) && (hwadd < 0x1f801e00)) { write16(add, value & 0xffff); diff --git a/src/core/psxmem.cc b/src/core/psxmem.cc index beaa0794f..b43398400 100644 --- a/src/core/psxmem.cc +++ b/src/core/psxmem.cc @@ -85,6 +85,18 @@ static const std::map s_knownBioses = { #endif }; +PCSX::Memory::Memory() : m_listener(g_system->m_eventBus) { + m_listener.listen([this](auto &) { + free(m_msanRAM); + free(m_msanBitmap); + free(m_msanWrittenBitmap); + m_msanRAM = nullptr; + m_msanBitmap = nullptr; + m_msanWrittenBitmap = nullptr; + m_msanAllocs.clear(); + }); +} + int PCSX::Memory::init() { m_readLUT = (uint8_t **)calloc(0x10000, sizeof(void *)); m_writeLUT = (uint8_t **)calloc(0x10000, sizeof(void *)); @@ -140,7 +152,7 @@ bool PCSX::Memory::loadEXP1FromFile(std::filesystem::path rom_path) { memset(m_exp1, 0xff, exp1_size); f->read(m_exp1, rom_size); f->close(); - PCSX::g_system->printf(_("Loaded %i bytes to EXP1 from file: %s\n"), rom_size, exp1Path.string()); + g_system->printf(_("Loaded %i bytes to EXP1 from file: %s\n"), rom_size, exp1Path.string()); result = true; } } else { @@ -222,7 +234,7 @@ The distributed OpenBIOS.bin file can be an appropriate BIOS replacement. f->read(m_bios, bios_size); } f->close(); - PCSX::g_system->printf(_("Loaded BIOS: %s\n"), biosPath.string()); + g_system->printf(_("Loaded BIOS: %s\n"), biosPath.string()); } } @@ -250,6 +262,14 @@ void PCSX::Memory::shutdown() { free(m_readLUT); free(m_writeLUT); + + free(m_msanRAM); + free(m_msanBitmap); + free(m_msanWrittenBitmap); + m_msanRAM = nullptr; + m_msanBitmap = nullptr; + m_msanWrittenBitmap = nullptr; + m_msanAllocs.clear(); } uint8_t PCSX::Memory::read8(uint32_t address) { @@ -261,6 +281,20 @@ uint8_t PCSX::Memory::read8(uint32_t address) { if (pointer != nullptr) { const uint32_t offset = address & 0xffff; return *(pointer + offset); + } else if (msanInitialized() && (address >= 0x20000000) && (address < (0x20000000 + c_msanSize))) { + uint32_t msanAddress = address - 0x20000000; + if ((m_msanWrittenBitmap[msanAddress / 8] & (1 << (msanAddress % 8))) == 0) { + g_system->log(LogClass::CPU, _("8-bit read from usable but uninitialized msan memory: %8.8lx\n"), address); + g_system->pause(); + return 0; + } + if (m_msanBitmap[msanAddress / 8] & (1 << (msanAddress % 8))) { + return m_msanRAM[msanAddress]; + } else { + g_system->log(LogClass::CPU, _("8-bit read from unsable msan memory: %8.8lx\n"), address); + g_system->pause(); + return 0; + } } else if (page == 0x1f80 || page == 0x9f80 || page == 0xbf80) { if ((address & 0xffff) < 0x400) { return m_hard[address & 0x3ff]; @@ -296,6 +330,23 @@ uint16_t PCSX::Memory::read16(uint32_t address) { if (pointer != nullptr) { const uint32_t offset = address & 0xffff; return SWAP_LEu16(*(uint16_t *)(pointer + offset)); + } else if (msanInitialized() && (address >= 0x20000000) && (address < (0x20000000 + c_msanSize))) { + uint32_t msanAddress = address - 0x20000000; + for (unsigned offset = 0; offset < 2; offset++) { + if ((m_msanWrittenBitmap[(msanAddress + offset) / 8] & (1 << ((msanAddress + offset) % 8))) == 0) { + g_system->log(LogClass::CPU, _("16-bit read from usable but uninitialized msan memory: %8.8lx\n"), + address); + g_system->pause(); + return 0; + } + } + if (m_msanBitmap[msanAddress / 8] & (1 << (msanAddress % 8))) { + return *(uint16_t *)&m_msanRAM[msanAddress]; + } else { + g_system->log(LogClass::CPU, _("16-bit read from unsable msan memory: %8.8lx\n"), address); + g_system->pause(); + return 0; + } } else if (page == 0x1f80 || page == 0x9f80 || page == 0xbf80) { if ((address & 0xffff) < 0x400) { uint16_t *ptr = (uint16_t *)&m_hard[address & 0x3ff]; @@ -328,6 +379,23 @@ uint32_t PCSX::Memory::read32(uint32_t address, ReadType readType) { if (pointer != nullptr) { const uint32_t offset = address & 0xffff; return SWAP_LEu32(*(uint32_t *)(pointer + offset)); + } else if (msanInitialized() && (address >= 0x20000000) && (address < (0x20000000 + c_msanSize))) { + uint32_t msanAddress = address - 0x20000000; + for (unsigned offset = 0; offset < 4; offset++) { + if ((m_msanWrittenBitmap[(msanAddress + offset) / 8] & (1 << ((msanAddress + offset) % 8))) == 0) { + g_system->log(LogClass::CPU, _("32-bit read from usable but uninitialized msan memory: %8.8lx\n"), + address); + g_system->pause(); + return 0; + } + } + if (m_msanBitmap[msanAddress / 8] & (1 << (msanAddress % 8))) { + return *(uint32_t *)&m_msanRAM[msanAddress]; + } else { + g_system->log(LogClass::CPU, _("32-bit read from unsable msan memory: %8.8lx\n"), address); + g_system->pause(); + return 0; + } } else if (page == 0x1f80 || page == 0x9f80 || page == 0xbf80) { if ((address & 0xffff) < 0x400) { uint32_t *ptr = (uint32_t *)&m_hard[address & 0x3ff]; @@ -437,6 +505,15 @@ void PCSX::Memory::write8(uint32_t address, uint32_t value) { const uint32_t offset = address & 0xffff; *(pointer + offset) = static_cast(value); g_emulator->m_cpu->Clear((address & (~3)), 1); + } else if (msanInitialized() && (address >= 0x20000000) && (address < (0x20000000 + c_msanSize))) { + uint32_t msanAddress = address - 0x20000000; + if (m_msanBitmap[msanAddress / 8] & (1 << (msanAddress % 8))) { + m_msanWrittenBitmap[msanAddress / 8] |= (1 << (msanAddress % 8)); + m_msanRAM[msanAddress] = value; + } else { + g_system->log(LogClass::CPU, _("8-bit write to unsable msan memory: %8.8lx\n"), address); + g_system->pause(); + } } else if (page == 0x1f80 || page == 0x9f80 || page == 0xbf80) { if ((address & 0xffff) < 0x400) { m_hard[address & 0x3ff] = value; @@ -465,6 +542,17 @@ void PCSX::Memory::write16(uint32_t address, uint32_t value) { const uint32_t offset = address & 0xffff; *(uint16_t *)(pointer + offset) = SWAP_LEu16(static_cast(value)); g_emulator->m_cpu->Clear((address & (~3)), 1); + } else if (msanInitialized() && (address >= 0x20000000) && (address < (0x20000000 + c_msanSize))) { + uint32_t msanAddress = address - 0x20000000; + if (m_msanBitmap[msanAddress / 8] & (1 << (msanAddress % 8))) { + for (unsigned offset = 0; offset < 2; offset++) { + m_msanWrittenBitmap[(msanAddress + offset) / 8] |= (1 << ((msanAddress + offset) % 8)); + } + *(uint16_t *)&m_msanRAM[msanAddress] = value; + } else { + g_system->log(LogClass::CPU, _("16-bit write to unsable msan memory: %8.8lx\n"), address); + g_system->pause(); + } } else if (page == 0x1f80 || page == 0x9f80 || page == 0xbf80) { if ((address & 0xffff) < 0x400) { uint16_t *ptr = (uint16_t *)&m_hard[address & 0x3ff]; @@ -494,6 +582,17 @@ void PCSX::Memory::write32(uint32_t address, uint32_t value) { const uint32_t offset = address & 0xffff; *(uint32_t *)(pointer + offset) = SWAP_LEu32(value); g_emulator->m_cpu->Clear((address & (~3)), 1); + } else if (msanInitialized() && (address >= 0x20000000) && (address < (0x20000000 + c_msanSize))) { + uint32_t msanAddress = address - 0x20000000; + if (m_msanBitmap[msanAddress / 8] & (1 << (msanAddress % 8))) { + for (unsigned offset = 0; offset < 4; offset++) { + m_msanWrittenBitmap[(msanAddress + offset) / 8] |= (1 << ((msanAddress + offset) % 8)); + } + *(uint32_t *)&m_msanRAM[msanAddress] = value; + } else { + g_system->log(LogClass::CPU, _("32-bit write to unsable msan memory: %8.8lx\n"), address); + g_system->pause(); + } } else if (page == 0x1f80 || page == 0x9f80 || page == 0xbf80) { if ((address & 0xffff) < 0x400) { uint32_t *ptr = (uint32_t *)&m_hard[address & 0x3ff]; @@ -717,3 +816,123 @@ void PCSX::Memory::MemoryAsFile::writeBlock(const void *src, size_t size, size_t auto toCopy = std::min(size, c_blockSize - offset); memcpy(block + offset, src, toCopy); } + +void PCSX::Memory::initMsan(bool reset) { + if (reset) { + free(m_msanRAM); + free(m_msanBitmap); + free(m_msanWrittenBitmap); + m_msanRAM = nullptr; + m_msanBitmap = nullptr; + m_msanWrittenBitmap = nullptr; + m_msanAllocs.clear(); + } + if (msanInitialized()) { + g_system->printf(_("MSAN system was already initialized.\n")); + g_system->pause(); + return; + } + + // 1.5GB of RAM, with 384MB worth of bitmap, between 0x20000000 and 0x80000000 + m_msanRAM = (uint8_t *)calloc(c_msanSize, 1); + m_msanBitmap = (uint8_t *)calloc(c_msanSize / 8, 1); + m_msanWrittenBitmap = (uint8_t *)calloc(c_msanSize / 8, 1); + m_msanPtr = 1024; +} + +uint32_t PCSX::Memory::msanAlloc(uint32_t size) { + // Allocate 1kB more than requested, to redzone the allocation. + // This is to detect out-of-bounds accesses. + uint32_t actualSize = size + 1 * 1024; + // Then round up to the next 16-byte boundary. + actualSize = actualSize + 15 & ~15; + + // Check if we still have enough memory. + if (m_msanPtr + actualSize > c_msanSize) { + g_system->printf(_("Out of memory in MsanAlloc\n")); + g_system->pause(); + return 0; + } + + // Allocate the memory. + uint32_t ptr = m_msanPtr; + m_msanPtr += actualSize; + // Mark the allocation as usable. + for (uint32_t i = 0; i < size; i++) { + m_msanBitmap[(ptr + i) / 8] |= 1 << ((ptr + i) % 8); + } + + // Insert the allocation into the list of allocations. + m_msanAllocs.insert({ptr, size}); + return ptr + 0x20000000; +} + +void PCSX::Memory::msanFree(uint32_t ptr) { + if (ptr == 0) { + return; + } + // Check if the pointer is valid. + if (ptr < 0x20000000 || ptr >= 0x20000000 + c_msanSize) { + g_system->printf(_("Invalid pointer passed to MsanFree: %08x\n"), ptr); + g_system->pause(); + return; + } + ptr -= 0x20000000; + auto it = m_msanAllocs.find(ptr); + if (it == m_msanAllocs.end()) { + g_system->printf(_("Invalid pointer passed to MsanFree: %08x\n"), ptr); + g_system->pause(); + return; + } + // Mark the allocation as unusable. + for (uint32_t i = 0; i < m_msanAllocs[ptr]; i++) { + m_msanBitmap[(ptr + i) / 8] &= ~(1 << ((ptr + i) % 8)); + } + // Remove the allocation from the list of allocations. + m_msanAllocs.erase(ptr); +} + +uint32_t PCSX::Memory::msanRealloc(uint32_t ptr, uint32_t size) { + if (ptr == 0) { + return msanAlloc(size); + } + if (size == 0) { + msanFree(ptr); + return 0; + } + // Check if the pointer is valid. + if (ptr < 0x20000000 || ptr >= 0x20000000 + c_msanSize) { + g_system->printf(_("Invalid pointer passed to MsanRealloc: %08x\n"), ptr); + g_system->pause(); + return 0; + } + ptr -= 0x20000000; + auto it = m_msanAllocs.find(ptr); + if (it == m_msanAllocs.end()) { + g_system->printf(_("Invalid pointer passed to MsanRealloc: %08x\n"), ptr); + g_system->pause(); + return 0; + } + auto oldSize = it->second; + + // Allocate new memory. + uint32_t newPtr = msanAlloc(size); + if (!newPtr) return 0; + newPtr -= 0x20000000; + + // Copy the old memory to the new memory. + memcpy(m_msanRAM + newPtr, m_msanRAM + ptr, std::min(size, oldSize)); + + // Mark the old allocation as unusable + for (uint32_t i = 0; i < oldSize; i++) { + m_msanBitmap[(ptr + i) / 8] &= ~(1 << ((ptr + i) % 8)); + } + // Mark the new allocation as written to + auto toCopy = std::min(size, oldSize); + for (uint32_t i = 0; i < toCopy; i++) { + m_msanWrittenBitmap[(newPtr + i) / 8] |= 1 << ((newPtr + i) % 8); + } + // Remove the allocation from the list of allocations. + m_msanAllocs.erase(ptr); + return newPtr + 0x20000000; +} diff --git a/src/core/psxmem.h b/src/core/psxmem.h index 7b731524f..bff45f451 100644 --- a/src/core/psxmem.h +++ b/src/core/psxmem.h @@ -20,9 +20,11 @@ #pragma once #include +#include #include #include "core/psxemulator.h" +#include "support/eventbus.h" #include "support/polyfills.h" #include "support/sharedmem.h" @@ -47,6 +49,7 @@ namespace PCSX { class Memory { public: + Memory(); int init(); void reset(); void shutdown(); @@ -74,6 +77,12 @@ class Memory { static constexpr uint16_t DMA_PCR = 0x10f0; static constexpr uint16_t DMA_ICR = 0x10f4; + void initMsan(bool reset); + bool msanInitialized() { return m_msanRAM != nullptr; } + uint32_t msanAlloc(uint32_t size); + void msanFree(uint32_t ptr); + uint32_t msanRealloc(uint32_t ptr, uint32_t size); + template void dmaInterrupt() { uint32_t icr = readHardwareRegister(); @@ -249,6 +258,15 @@ class Memory { uint8_t **m_writeLUT = nullptr; uint8_t **m_readLUT = nullptr; + static constexpr uint32_t c_msanSize = 1'610'612'736; + uint8_t *m_msanRAM = nullptr; + uint8_t *m_msanBitmap = nullptr; + uint8_t *m_msanWrittenBitmap = nullptr; + uint32_t m_msanPtr = 1024; + EventBus::Listener m_listener; + + std::unordered_map m_msanAllocs; + template T *getPointer(uint32_t address) { auto lut = m_readLUT[address >> 16]; diff --git a/src/mips/common/hardware/pcsxhw.h b/src/mips/common/hardware/pcsxhw.h index 33f67830f..629a4c87e 100644 --- a/src/mips/common/hardware/pcsxhw.h +++ b/src/mips/common/hardware/pcsxhw.h @@ -35,5 +35,17 @@ static __inline__ void pcsx_exit(int code) { *((volatile int16_t* const)0x1f8020 static __inline__ void pcsx_message(const char* msg) { *((volatile const char** const)0x1f802084) = msg; } static __inline__ void pcsx_checkKernel(int enable) { *((volatile char*)0x1f802088) = enable; } static __inline__ int pcsx_isCheckingKernel() { return *((volatile char* const)0x1f802088) != 0; } +static __inline__ void pcsx_initMsan() { *((volatile char* const)0x1f802089) = 0; } +static __inline__ void pcsx_resetMsan() { *((volatile char* const)0x1f802089) = 1; } +static __inline__ void* pcsx_msanAlloc(uint32_t size) { + register uint32_t a0 asm("a0") = size; + return *((void* volatile* const)0x1f80208c); +} +static __inline__ void pcsx_msanFree(void* ptr) { *((void* volatile* const)0x1f80208c) = ptr; } +static __inline__ void* pcsx_msanRealloc(void* ptr, uint32_t size) { + register void* a0 asm("a0") = ptr; + register uint32_t a1 asm("a1") = size; + return *((void* volatile* const)0x1f802090); +} static __inline__ int pcsx_present() { return *((volatile uint32_t* const)0x1f802080) == 0x58534350; } diff --git a/src/supportpsx/memory.h b/src/supportpsx/memory.h index a5b38bb79..0776b754f 100644 --- a/src/supportpsx/memory.h +++ b/src/supportpsx/memory.h @@ -25,6 +25,12 @@ namespace PCSX { struct PSXAddress { explicit PSXAddress(uint32_t virt) { + if ((virt >= 0x20000000) && (virt < 0x80000000)) { + segment = Segment::MSAN; + type = Type::MSAN; + physical = virt - 0x20000000; + return; + } physical = virt & 0x1fffffff; if (virt == 0xfffe0130) { segment = Segment::Internal; @@ -96,8 +102,12 @@ struct PSXAddress { case Type::ROM: ret += 0x1fc00000; break; + case Type::MSAN: + ret += 0x20000000; + break; } switch (segment) { + case Segment::MSAN: case Segment::KUSEG: return ret; case Segment::KSEG0: @@ -105,6 +115,7 @@ struct PSXAddress { case Segment::KSEG1: return ret | 0xa0000000; } + return {}; } uint32_t physical = 0; @@ -117,6 +128,7 @@ struct PSXAddress { EXP3, ROM, Internal, + MSAN, } type = Type::RAM; enum class Segment { KUSEG, @@ -124,6 +136,7 @@ struct PSXAddress { KSEG1, Internal, Invalid, + MSAN, } segment = Segment::Invalid; };