Skip to content

Commit 83dbc15

Browse files
authored
Merge pull request #1855 from nicolasnoble/psxmsan
Adding "msan"-like feature.
2 parents fe5a246 + 1359143 commit 83dbc15

File tree

6 files changed

+283
-4
lines changed

6 files changed

+283
-4
lines changed

src/core/debug.cc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ uint32_t PCSX::Debug::normalizeAddress(uint32_t address) {
5959

6060
bool PCSX::Debug::isInKernel(uint32_t address, bool biosIsKernel) {
6161
PSXAddress addr(address);
62+
if (addr.type == PSXAddress::Type::MSAN) return false;
6263
const bool ramExpansion = PCSX::g_emulator->settings.get<PCSX::Emulator::Setting8MB>();
6364
if (addr.type == PSXAddress::Type::ROM) return biosIsKernel;
6465
if (addr.type != PSXAddress::Type::RAM) return false;

src/core/psxhw.cc

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -360,7 +360,17 @@ uint32_t PCSX::HW::read32(uint32_t add) {
360360
case 0x1f802080:
361361
hard = 0x58534350;
362362
break;
363-
363+
case 0x1f80208c: {
364+
uint32_t size = g_emulator->m_cpu->m_regs.GPR.n.a0;
365+
hard = g_emulator->m_mem->msanAlloc(size);
366+
break;
367+
}
368+
case 0x1f802090: {
369+
uint32_t ptr = g_emulator->m_cpu->m_regs.GPR.n.a0;
370+
uint32_t size = g_emulator->m_cpu->m_regs.GPR.n.a1;
371+
hard = g_emulator->m_mem->msanRealloc(ptr, size);
372+
break;
373+
}
364374
default: {
365375
uint32_t *ptr = (uint32_t *)&g_emulator->m_mem->m_hard[hwadd & 0xffff];
366376
hard = SWAP_LEu32(*ptr);
@@ -445,7 +455,9 @@ void PCSX::HW::write8(uint32_t add, uint32_t rawvalue) {
445455
case 0x1f802088:
446456
g_emulator->m_debug->m_checkKernel = value;
447457
break;
448-
458+
case 0x1f802089:
459+
g_emulator->m_mem->initMsan(value);
460+
break;
449461
default:
450462
if (addressInRegisterSpace(hwadd)) {
451463
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) {
789801
g_system->message("%s", memFile->gets<false>());
790802
break;
791803
}
804+
case 0x1f80208c: {
805+
g_emulator->m_mem->msanFree(value);
806+
break;
807+
}
792808
default: {
793809
if ((hwadd >= 0x1f801c00) && (hwadd < 0x1f801e00)) {
794810
write16(add, value & 0xffff);

src/core/psxmem.cc

Lines changed: 221 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,18 @@ static const std::map<uint32_t, std::string_view> s_knownBioses = {
8585
#endif
8686
};
8787

88+
PCSX::Memory::Memory() : m_listener(g_system->m_eventBus) {
89+
m_listener.listen<Events::ExecutionFlow::Reset>([this](auto &) {
90+
free(m_msanRAM);
91+
free(m_msanBitmap);
92+
free(m_msanWrittenBitmap);
93+
m_msanRAM = nullptr;
94+
m_msanBitmap = nullptr;
95+
m_msanWrittenBitmap = nullptr;
96+
m_msanAllocs.clear();
97+
});
98+
}
99+
88100
int PCSX::Memory::init() {
89101
m_readLUT = (uint8_t **)calloc(0x10000, sizeof(void *));
90102
m_writeLUT = (uint8_t **)calloc(0x10000, sizeof(void *));
@@ -140,7 +152,7 @@ bool PCSX::Memory::loadEXP1FromFile(std::filesystem::path rom_path) {
140152
memset(m_exp1, 0xff, exp1_size);
141153
f->read(m_exp1, rom_size);
142154
f->close();
143-
PCSX::g_system->printf(_("Loaded %i bytes to EXP1 from file: %s\n"), rom_size, exp1Path.string());
155+
g_system->printf(_("Loaded %i bytes to EXP1 from file: %s\n"), rom_size, exp1Path.string());
144156
result = true;
145157
}
146158
} else {
@@ -222,7 +234,7 @@ The distributed OpenBIOS.bin file can be an appropriate BIOS replacement.
222234
f->read(m_bios, bios_size);
223235
}
224236
f->close();
225-
PCSX::g_system->printf(_("Loaded BIOS: %s\n"), biosPath.string());
237+
g_system->printf(_("Loaded BIOS: %s\n"), biosPath.string());
226238
}
227239
}
228240

@@ -250,6 +262,14 @@ void PCSX::Memory::shutdown() {
250262

251263
free(m_readLUT);
252264
free(m_writeLUT);
265+
266+
free(m_msanRAM);
267+
free(m_msanBitmap);
268+
free(m_msanWrittenBitmap);
269+
m_msanRAM = nullptr;
270+
m_msanBitmap = nullptr;
271+
m_msanWrittenBitmap = nullptr;
272+
m_msanAllocs.clear();
253273
}
254274

255275
uint8_t PCSX::Memory::read8(uint32_t address) {
@@ -261,6 +281,20 @@ uint8_t PCSX::Memory::read8(uint32_t address) {
261281
if (pointer != nullptr) {
262282
const uint32_t offset = address & 0xffff;
263283
return *(pointer + offset);
284+
} else if (msanInitialized() && (address >= 0x20000000) && (address < (0x20000000 + c_msanSize))) {
285+
uint32_t msanAddress = address - 0x20000000;
286+
if ((m_msanWrittenBitmap[msanAddress / 8] & (1 << (msanAddress % 8))) == 0) {
287+
g_system->log(LogClass::CPU, _("8-bit read from usable but uninitialized msan memory: %8.8lx\n"), address);
288+
g_system->pause();
289+
return 0;
290+
}
291+
if (m_msanBitmap[msanAddress / 8] & (1 << (msanAddress % 8))) {
292+
return m_msanRAM[msanAddress];
293+
} else {
294+
g_system->log(LogClass::CPU, _("8-bit read from unsable msan memory: %8.8lx\n"), address);
295+
g_system->pause();
296+
return 0;
297+
}
264298
} else if (page == 0x1f80 || page == 0x9f80 || page == 0xbf80) {
265299
if ((address & 0xffff) < 0x400) {
266300
return m_hard[address & 0x3ff];
@@ -296,6 +330,23 @@ uint16_t PCSX::Memory::read16(uint32_t address) {
296330
if (pointer != nullptr) {
297331
const uint32_t offset = address & 0xffff;
298332
return SWAP_LEu16(*(uint16_t *)(pointer + offset));
333+
} else if (msanInitialized() && (address >= 0x20000000) && (address < (0x20000000 + c_msanSize))) {
334+
uint32_t msanAddress = address - 0x20000000;
335+
for (unsigned offset = 0; offset < 2; offset++) {
336+
if ((m_msanWrittenBitmap[(msanAddress + offset) / 8] & (1 << ((msanAddress + offset) % 8))) == 0) {
337+
g_system->log(LogClass::CPU, _("16-bit read from usable but uninitialized msan memory: %8.8lx\n"),
338+
address);
339+
g_system->pause();
340+
return 0;
341+
}
342+
}
343+
if (m_msanBitmap[msanAddress / 8] & (1 << (msanAddress % 8))) {
344+
return *(uint16_t *)&m_msanRAM[msanAddress];
345+
} else {
346+
g_system->log(LogClass::CPU, _("16-bit read from unsable msan memory: %8.8lx\n"), address);
347+
g_system->pause();
348+
return 0;
349+
}
299350
} else if (page == 0x1f80 || page == 0x9f80 || page == 0xbf80) {
300351
if ((address & 0xffff) < 0x400) {
301352
uint16_t *ptr = (uint16_t *)&m_hard[address & 0x3ff];
@@ -328,6 +379,23 @@ uint32_t PCSX::Memory::read32(uint32_t address, ReadType readType) {
328379
if (pointer != nullptr) {
329380
const uint32_t offset = address & 0xffff;
330381
return SWAP_LEu32(*(uint32_t *)(pointer + offset));
382+
} else if (msanInitialized() && (address >= 0x20000000) && (address < (0x20000000 + c_msanSize))) {
383+
uint32_t msanAddress = address - 0x20000000;
384+
for (unsigned offset = 0; offset < 4; offset++) {
385+
if ((m_msanWrittenBitmap[(msanAddress + offset) / 8] & (1 << ((msanAddress + offset) % 8))) == 0) {
386+
g_system->log(LogClass::CPU, _("32-bit read from usable but uninitialized msan memory: %8.8lx\n"),
387+
address);
388+
g_system->pause();
389+
return 0;
390+
}
391+
}
392+
if (m_msanBitmap[msanAddress / 8] & (1 << (msanAddress % 8))) {
393+
return *(uint32_t *)&m_msanRAM[msanAddress];
394+
} else {
395+
g_system->log(LogClass::CPU, _("32-bit read from unsable msan memory: %8.8lx\n"), address);
396+
g_system->pause();
397+
return 0;
398+
}
331399
} else if (page == 0x1f80 || page == 0x9f80 || page == 0xbf80) {
332400
if ((address & 0xffff) < 0x400) {
333401
uint32_t *ptr = (uint32_t *)&m_hard[address & 0x3ff];
@@ -437,6 +505,15 @@ void PCSX::Memory::write8(uint32_t address, uint32_t value) {
437505
const uint32_t offset = address & 0xffff;
438506
*(pointer + offset) = static_cast<uint8_t>(value);
439507
g_emulator->m_cpu->Clear((address & (~3)), 1);
508+
} else if (msanInitialized() && (address >= 0x20000000) && (address < (0x20000000 + c_msanSize))) {
509+
uint32_t msanAddress = address - 0x20000000;
510+
if (m_msanBitmap[msanAddress / 8] & (1 << (msanAddress % 8))) {
511+
m_msanWrittenBitmap[msanAddress / 8] |= (1 << (msanAddress % 8));
512+
m_msanRAM[msanAddress] = value;
513+
} else {
514+
g_system->log(LogClass::CPU, _("8-bit write to unsable msan memory: %8.8lx\n"), address);
515+
g_system->pause();
516+
}
440517
} else if (page == 0x1f80 || page == 0x9f80 || page == 0xbf80) {
441518
if ((address & 0xffff) < 0x400) {
442519
m_hard[address & 0x3ff] = value;
@@ -465,6 +542,17 @@ void PCSX::Memory::write16(uint32_t address, uint32_t value) {
465542
const uint32_t offset = address & 0xffff;
466543
*(uint16_t *)(pointer + offset) = SWAP_LEu16(static_cast<uint16_t>(value));
467544
g_emulator->m_cpu->Clear((address & (~3)), 1);
545+
} else if (msanInitialized() && (address >= 0x20000000) && (address < (0x20000000 + c_msanSize))) {
546+
uint32_t msanAddress = address - 0x20000000;
547+
if (m_msanBitmap[msanAddress / 8] & (1 << (msanAddress % 8))) {
548+
for (unsigned offset = 0; offset < 2; offset++) {
549+
m_msanWrittenBitmap[(msanAddress + offset) / 8] |= (1 << ((msanAddress + offset) % 8));
550+
}
551+
*(uint16_t *)&m_msanRAM[msanAddress] = value;
552+
} else {
553+
g_system->log(LogClass::CPU, _("16-bit write to unsable msan memory: %8.8lx\n"), address);
554+
g_system->pause();
555+
}
468556
} else if (page == 0x1f80 || page == 0x9f80 || page == 0xbf80) {
469557
if ((address & 0xffff) < 0x400) {
470558
uint16_t *ptr = (uint16_t *)&m_hard[address & 0x3ff];
@@ -494,6 +582,17 @@ void PCSX::Memory::write32(uint32_t address, uint32_t value) {
494582
const uint32_t offset = address & 0xffff;
495583
*(uint32_t *)(pointer + offset) = SWAP_LEu32(value);
496584
g_emulator->m_cpu->Clear((address & (~3)), 1);
585+
} else if (msanInitialized() && (address >= 0x20000000) && (address < (0x20000000 + c_msanSize))) {
586+
uint32_t msanAddress = address - 0x20000000;
587+
if (m_msanBitmap[msanAddress / 8] & (1 << (msanAddress % 8))) {
588+
for (unsigned offset = 0; offset < 4; offset++) {
589+
m_msanWrittenBitmap[(msanAddress + offset) / 8] |= (1 << ((msanAddress + offset) % 8));
590+
}
591+
*(uint32_t *)&m_msanRAM[msanAddress] = value;
592+
} else {
593+
g_system->log(LogClass::CPU, _("32-bit write to unsable msan memory: %8.8lx\n"), address);
594+
g_system->pause();
595+
}
497596
} else if (page == 0x1f80 || page == 0x9f80 || page == 0xbf80) {
498597
if ((address & 0xffff) < 0x400) {
499598
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
717816
auto toCopy = std::min(size, c_blockSize - offset);
718817
memcpy(block + offset, src, toCopy);
719818
}
819+
820+
void PCSX::Memory::initMsan(bool reset) {
821+
if (reset) {
822+
free(m_msanRAM);
823+
free(m_msanBitmap);
824+
free(m_msanWrittenBitmap);
825+
m_msanRAM = nullptr;
826+
m_msanBitmap = nullptr;
827+
m_msanWrittenBitmap = nullptr;
828+
m_msanAllocs.clear();
829+
}
830+
if (msanInitialized()) {
831+
g_system->printf(_("MSAN system was already initialized.\n"));
832+
g_system->pause();
833+
return;
834+
}
835+
836+
// 1.5GB of RAM, with 384MB worth of bitmap, between 0x20000000 and 0x80000000
837+
m_msanRAM = (uint8_t *)calloc(c_msanSize, 1);
838+
m_msanBitmap = (uint8_t *)calloc(c_msanSize / 8, 1);
839+
m_msanWrittenBitmap = (uint8_t *)calloc(c_msanSize / 8, 1);
840+
m_msanPtr = 1024;
841+
}
842+
843+
uint32_t PCSX::Memory::msanAlloc(uint32_t size) {
844+
// Allocate 1kB more than requested, to redzone the allocation.
845+
// This is to detect out-of-bounds accesses.
846+
uint32_t actualSize = size + 1 * 1024;
847+
// Then round up to the next 16-byte boundary.
848+
actualSize = actualSize + 15 & ~15;
849+
850+
// Check if we still have enough memory.
851+
if (m_msanPtr + actualSize > c_msanSize) {
852+
g_system->printf(_("Out of memory in MsanAlloc\n"));
853+
g_system->pause();
854+
return 0;
855+
}
856+
857+
// Allocate the memory.
858+
uint32_t ptr = m_msanPtr;
859+
m_msanPtr += actualSize;
860+
// Mark the allocation as usable.
861+
for (uint32_t i = 0; i < size; i++) {
862+
m_msanBitmap[(ptr + i) / 8] |= 1 << ((ptr + i) % 8);
863+
}
864+
865+
// Insert the allocation into the list of allocations.
866+
m_msanAllocs.insert({ptr, size});
867+
return ptr + 0x20000000;
868+
}
869+
870+
void PCSX::Memory::msanFree(uint32_t ptr) {
871+
if (ptr == 0) {
872+
return;
873+
}
874+
// Check if the pointer is valid.
875+
if (ptr < 0x20000000 || ptr >= 0x20000000 + c_msanSize) {
876+
g_system->printf(_("Invalid pointer passed to MsanFree: %08x\n"), ptr);
877+
g_system->pause();
878+
return;
879+
}
880+
ptr -= 0x20000000;
881+
auto it = m_msanAllocs.find(ptr);
882+
if (it == m_msanAllocs.end()) {
883+
g_system->printf(_("Invalid pointer passed to MsanFree: %08x\n"), ptr);
884+
g_system->pause();
885+
return;
886+
}
887+
// Mark the allocation as unusable.
888+
for (uint32_t i = 0; i < m_msanAllocs[ptr]; i++) {
889+
m_msanBitmap[(ptr + i) / 8] &= ~(1 << ((ptr + i) % 8));
890+
}
891+
// Remove the allocation from the list of allocations.
892+
m_msanAllocs.erase(ptr);
893+
}
894+
895+
uint32_t PCSX::Memory::msanRealloc(uint32_t ptr, uint32_t size) {
896+
if (ptr == 0) {
897+
return msanAlloc(size);
898+
}
899+
if (size == 0) {
900+
msanFree(ptr);
901+
return 0;
902+
}
903+
// Check if the pointer is valid.
904+
if (ptr < 0x20000000 || ptr >= 0x20000000 + c_msanSize) {
905+
g_system->printf(_("Invalid pointer passed to MsanRealloc: %08x\n"), ptr);
906+
g_system->pause();
907+
return 0;
908+
}
909+
ptr -= 0x20000000;
910+
auto it = m_msanAllocs.find(ptr);
911+
if (it == m_msanAllocs.end()) {
912+
g_system->printf(_("Invalid pointer passed to MsanRealloc: %08x\n"), ptr);
913+
g_system->pause();
914+
return 0;
915+
}
916+
auto oldSize = it->second;
917+
918+
// Allocate new memory.
919+
uint32_t newPtr = msanAlloc(size);
920+
if (!newPtr) return 0;
921+
newPtr -= 0x20000000;
922+
923+
// Copy the old memory to the new memory.
924+
memcpy(m_msanRAM + newPtr, m_msanRAM + ptr, std::min(size, oldSize));
925+
926+
// Mark the old allocation as unusable
927+
for (uint32_t i = 0; i < oldSize; i++) {
928+
m_msanBitmap[(ptr + i) / 8] &= ~(1 << ((ptr + i) % 8));
929+
}
930+
// Mark the new allocation as written to
931+
auto toCopy = std::min(size, oldSize);
932+
for (uint32_t i = 0; i < toCopy; i++) {
933+
m_msanWrittenBitmap[(newPtr + i) / 8] |= 1 << ((newPtr + i) % 8);
934+
}
935+
// Remove the allocation from the list of allocations.
936+
m_msanAllocs.erase(ptr);
937+
return newPtr + 0x20000000;
938+
}

src/core/psxmem.h

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,11 @@
2020
#pragma once
2121

2222
#include <string_view>
23+
#include <unordered_map>
2324
#include <vector>
2425

2526
#include "core/psxemulator.h"
27+
#include "support/eventbus.h"
2628
#include "support/polyfills.h"
2729
#include "support/sharedmem.h"
2830

@@ -47,6 +49,7 @@ namespace PCSX {
4749

4850
class Memory {
4951
public:
52+
Memory();
5053
int init();
5154
void reset();
5255
void shutdown();
@@ -74,6 +77,12 @@ class Memory {
7477
static constexpr uint16_t DMA_PCR = 0x10f0;
7578
static constexpr uint16_t DMA_ICR = 0x10f4;
7679

80+
void initMsan(bool reset);
81+
bool msanInitialized() { return m_msanRAM != nullptr; }
82+
uint32_t msanAlloc(uint32_t size);
83+
void msanFree(uint32_t ptr);
84+
uint32_t msanRealloc(uint32_t ptr, uint32_t size);
85+
7786
template <unsigned n>
7887
void dmaInterrupt() {
7988
uint32_t icr = readHardwareRegister<DMA_ICR>();
@@ -249,6 +258,15 @@ class Memory {
249258
uint8_t **m_writeLUT = nullptr;
250259
uint8_t **m_readLUT = nullptr;
251260

261+
static constexpr uint32_t c_msanSize = 1'610'612'736;
262+
uint8_t *m_msanRAM = nullptr;
263+
uint8_t *m_msanBitmap = nullptr;
264+
uint8_t *m_msanWrittenBitmap = nullptr;
265+
uint32_t m_msanPtr = 1024;
266+
EventBus::Listener m_listener;
267+
268+
std::unordered_map<uint32_t, uint32_t> m_msanAllocs;
269+
252270
template <typename T = void>
253271
T *getPointer(uint32_t address) {
254272
auto lut = m_readLUT[address >> 16];

0 commit comments

Comments
 (0)