Bus: Protect main RAM buffer

Get rid of write-time checks.
This commit is contained in:
Stenzek 2023-10-07 18:10:09 +10:00
parent 79e1ae3e54
commit a062b00c1a
No known key found for this signature in database
12 changed files with 82 additions and 99 deletions

View file

@ -3009,7 +3009,7 @@ unsigned int Achievements::RAIntegration::RACallbackReadMemoryBlock(unsigned int
return 0;
const u32 copy_size = std::min<u32>(Bus::g_ram_size - nAddress, nBytes);
std::memcpy(pBuffer, Bus::g_ram + nAddress, copy_size);
std::memcpy(pBuffer, Bus::g_unprotected_ram + nAddress, copy_size);
return copy_size;
}

View file

@ -37,6 +37,7 @@ Log_SetChannel(Bus);
// TODO: Get rid of page code bits, instead use page faults to track SMC.
// Exports for external debugger access
#ifndef __ANDROID__
namespace Exports {
extern "C" {
@ -50,6 +51,7 @@ __attribute__((visibility("default"), used)) u32 RAM_SIZE, RAM_MASK;
}
} // namespace Exports
#endif
namespace Bus {
@ -105,6 +107,7 @@ static void* s_shmem_handle = nullptr;
std::bitset<RAM_8MB_CODE_PAGE_COUNT> g_ram_code_bits{};
u8* g_ram = nullptr;
u8* g_unprotected_ram = nullptr;
u32 g_ram_size = 0;
u32 g_ram_mask = 0;
u8* g_bios = nullptr;
@ -132,16 +135,13 @@ static std::vector<std::pair<u8*, size_t>> s_fastmem_ram_views;
#endif
static u8** s_fastmem_lut = nullptr;
static constexpr const std::array s_fastmem_ram_mirrors = {0x00000000u, 0x00200000u, 0x00400000u, 0x00600000u,
0x80000000u, 0x80200000u, 0x80400000u, 0x80600000u,
0xA0000000u, 0xA0200000u, 0xA0400000u, 0xA0600000u};
static void SetRAMSize(bool enable_8mb_ram);
static std::tuple<TickCount, TickCount, TickCount> CalculateMemoryTiming(MEMDELAY mem_delay, COMDELAY common_delay);
static void RecalculateMemoryTimings();
static void SetCodePageFastmemProtection(u32 page_index, bool writable);
static void SetRAMPageWritable(u32 page_index, bool writable);
static void SetHandlers();
@ -182,7 +182,9 @@ bool Bus::AllocateMemory()
g_ram = static_cast<u8*>(MemMap::MapSharedMemory(s_shmem_handle, MemoryMap::RAM_OFFSET, nullptr, MemoryMap::RAM_SIZE,
PageProtect::ReadWrite));
if (!g_ram)
g_unprotected_ram = static_cast<u8*>(MemMap::MapSharedMemory(s_shmem_handle, MemoryMap::RAM_OFFSET, nullptr,
MemoryMap::RAM_SIZE, PageProtect::ReadWrite));
if (!g_ram || !g_unprotected_ram)
{
Host::ReportErrorAsync("Error", "Failed to map memory for RAM");
ReleaseMemory();
@ -227,11 +229,21 @@ bool Bus::AllocateMemory()
Log_InfoPrintf("Fastmem base: %p", s_fastmem_arena.BasePointer());
#endif
#ifndef __ANDROID__
Exports::RAM = reinterpret_cast<uintptr_t>(g_unprotected_ram);
#endif
return true;
}
void Bus::ReleaseMemory()
{
#ifndef __ANDROID__
Exports::RAM = 0;
Exports::RAM_SIZE = 0;
Exports::RAM_MASK = 0;
#endif
#ifdef ENABLE_MMAP_FASTMEM
DebugAssert(s_fastmem_ram_views.empty());
s_fastmem_arena.Destroy();
@ -253,6 +265,12 @@ void Bus::ReleaseMemory()
g_bios = nullptr;
}
if (g_unprotected_ram)
{
MemMap::UnmapSharedMemory(g_unprotected_ram, MemoryMap::RAM_SIZE);
g_unprotected_ram = nullptr;
}
if (g_ram)
{
MemMap::UnmapSharedMemory(g_ram, MemoryMap::RAM_SIZE);
@ -278,9 +296,10 @@ void Bus::SetRAMSize(bool enable_8mb_ram)
g_ram_size = enable_8mb_ram ? RAM_8MB_SIZE : RAM_2MB_SIZE;
g_ram_mask = enable_8mb_ram ? RAM_8MB_MASK : RAM_2MB_MASK;
Exports::RAM = reinterpret_cast<uintptr_t>(g_ram);
#ifndef __ANDROID__
Exports::RAM_SIZE = g_ram_size;
Exports::RAM_MASK = g_ram_mask;
#endif
}
void Bus::Shutdown()
@ -291,9 +310,11 @@ void Bus::Shutdown()
g_ram_mask = 0;
g_ram_size = 0;
#ifndef __ANDROID__
Exports::RAM = 0;
Exports::RAM_SIZE = 0;
Exports::RAM_MASK = 0;
#endif
}
void Bus::Reset()
@ -509,13 +530,13 @@ void Bus::UpdateFastmemViews(CPUFastmemMode mode)
if (!s_fastmem_lut)
{
s_fastmem_lut = static_cast<u8**>(std::malloc(sizeof(u8*) * FASTMEM_LUT_NUM_SLOTS));
s_fastmem_lut = static_cast<u8**>(std::malloc(sizeof(u8*) * FASTMEM_LUT_SIZE));
Assert(s_fastmem_lut);
Log_InfoPrintf("Fastmem base (software): %p", s_fastmem_lut);
}
std::memset(s_fastmem_lut, 0, sizeof(u8*) * FASTMEM_LUT_NUM_SLOTS);
std::memset(s_fastmem_lut, 0, sizeof(u8*) * FASTMEM_LUT_SIZE);
auto MapRAM = [](u32 base_address) {
u8* ram_ptr = g_ram + (base_address & g_ram_mask);
@ -523,8 +544,6 @@ void Bus::UpdateFastmemViews(CPUFastmemMode mode)
{
const u32 lut_index = (base_address + address) >> FASTMEM_LUT_PAGE_SHIFT;
s_fastmem_lut[lut_index] = ram_ptr;
s_fastmem_lut[FASTMEM_LUT_NUM_PAGES + lut_index] =
g_ram_code_bits[address >> HOST_PAGE_SHIFT] ? nullptr : ram_ptr;
ram_ptr += FASTMEM_LUT_PAGE_SIZE;
}
};
@ -584,7 +603,7 @@ void Bus::SetRAMCodePage(u32 index)
// protect fastmem pages
g_ram_code_bits[index] = true;
SetCodePageFastmemProtection(index, false);
SetRAMPageWritable(index, false);
}
void Bus::ClearRAMCodePage(u32 index)
@ -594,11 +613,19 @@ void Bus::ClearRAMCodePage(u32 index)
// unprotect fastmem pages
g_ram_code_bits[index] = false;
SetCodePageFastmemProtection(index, true);
SetRAMPageWritable(index, true);
}
void Bus::SetCodePageFastmemProtection(u32 page_index, bool writable)
void Bus::SetRAMPageWritable(u32 page_index, bool writable)
{
if (!MemMap::MemProtect(&g_ram[page_index * HOST_PAGE_SIZE], HOST_PAGE_SIZE,
writable ? PageProtect::ReadWrite : PageProtect::ReadOnly)) [[unlikely]]
{
Log_ErrorFmt("Failed to set RAM host page {} ({}) to {}", page_index,
reinterpret_cast<const void*>(&g_ram[page_index * HOST_PAGE_SIZE]),
writable ? "read-write" : "read-only");
}
#ifdef ENABLE_MMAP_FASTMEM
if (s_fastmem_mode == CPUFastmemMode::MMap)
{
@ -608,7 +635,7 @@ void Bus::SetCodePageFastmemProtection(u32 page_index, bool writable)
for (const auto& it : s_fastmem_ram_views)
{
u8* page_address = it.first + (page_index * HOST_PAGE_SIZE);
if (!MemMap::MemProtect(page_address, HOST_PAGE_SIZE, protect))
if (!MemMap::MemProtect(page_address, HOST_PAGE_SIZE, protect)) [[unlikely]]
{
Log_ErrorPrintf("Failed to %s code page %u (0x%08X) @ %p", writable ? "unprotect" : "protect", page_index,
page_index * static_cast<u32>(HOST_PAGE_SIZE), page_address);
@ -618,30 +645,15 @@ void Bus::SetCodePageFastmemProtection(u32 page_index, bool writable)
return;
}
#endif
if (s_fastmem_mode == CPUFastmemMode::LUT)
{
// mirrors...
const u32 code_addr = page_index << HOST_PAGE_SHIFT;
for (u32 mirror_start : s_fastmem_ram_mirrors)
{
u32 lut_addr = mirror_start + code_addr;
u32 ram_offset = (lut_addr & g_ram_mask);
for (u32 j = 0; j < FASTMEM_LUT_PAGES_PER_CODE_PAGE; j++)
{
s_fastmem_lut[FASTMEM_LUT_NUM_PAGES + (lut_addr >> FASTMEM_LUT_PAGE_SHIFT)] =
writable ? &g_ram[ram_offset] : nullptr;
lut_addr += FASTMEM_LUT_PAGE_SIZE;
ram_offset += FASTMEM_LUT_PAGE_SIZE;
}
}
}
}
void Bus::ClearRAMCodePageFlags()
{
g_ram_code_bits.reset();
if (!MemMap::MemProtect(g_ram, RAM_8MB_SIZE, PageProtect::ReadWrite))
Log_ErrorPrint("Failed to restore RAM protection to read-write.");
#ifdef ENABLE_MMAP_FASTMEM
if (s_fastmem_mode == CPUFastmemMode::MMap)
{
@ -655,21 +667,6 @@ void Bus::ClearRAMCodePageFlags()
}
}
#endif
if (s_fastmem_mode == CPUFastmemMode::LUT)
{
for (u32 i = 0; i < static_cast<u32>(g_ram_code_bits.size()); i++)
{
u32 lut_addr = (i * HOST_PAGE_SIZE);
u32 ram_offset = (lut_addr & g_ram_mask);
for (u32 j = 0; j < FASTMEM_LUT_PAGES_PER_CODE_PAGE; j++)
{
s_fastmem_lut[FASTMEM_LUT_NUM_PAGES + (lut_addr >> FASTMEM_LUT_PAGE_SHIFT)] = &g_ram[ram_offset];
lut_addr += FASTMEM_LUT_PAGE_SIZE;
ram_offset += FASTMEM_LUT_PAGE_SIZE;
}
}
}
}
bool Bus::IsCodePageAddress(PhysicalMemoryAddress address)
@ -740,16 +737,16 @@ u8* Bus::GetMemoryRegionPointer(MemoryRegion region)
switch (region)
{
case MemoryRegion::RAM:
return g_ram;
return g_unprotected_ram;
case MemoryRegion::RAMMirror1:
return (g_ram + (RAM_2MB_SIZE & g_ram_mask));
return (g_unprotected_ram + (RAM_2MB_SIZE & g_ram_mask));
case MemoryRegion::RAMMirror2:
return (g_ram + ((RAM_2MB_SIZE * 2) & g_ram_mask));
return (g_unprotected_ram + ((RAM_2MB_SIZE * 2) & g_ram_mask));
case MemoryRegion::RAMMirror3:
return (g_ram + ((RAM_8MB_SIZE * 3) & g_ram_mask));
return (g_unprotected_ram + ((RAM_8MB_SIZE * 3) & g_ram_mask));
case MemoryRegion::EXP1:
return nullptr;
@ -932,9 +929,6 @@ template<MemoryAccessSize size>
void Bus::RAMWriteHandler(VirtualMemoryAddress address, u32 value)
{
const u32 offset = address & g_ram_mask;
const u32 page_index = offset / HOST_PAGE_SIZE;
if (g_ram_code_bits[page_index])
CPU::CodeCache::InvalidateBlocksWithPageIndex(page_index);
if constexpr (size == MemoryAccessSize::Byte)
{

View file

@ -96,10 +96,7 @@ enum : u32
FASTMEM_LUT_PAGE_SIZE = 4096,
FASTMEM_LUT_PAGE_MASK = FASTMEM_LUT_PAGE_SIZE - 1,
FASTMEM_LUT_PAGE_SHIFT = 12,
FASTMEM_LUT_PAGES_PER_CODE_PAGE = HOST_PAGE_SIZE / FASTMEM_LUT_PAGE_SIZE,
FASTMEM_LUT_NUM_PAGES = 0x100000, // 0x100000000 >> 12
FASTMEM_LUT_NUM_SLOTS = FASTMEM_LUT_NUM_PAGES * 2,
FASTMEM_LUT_SIZE = 0x100000, // 0x100000000 >> 12
};
#ifdef ENABLE_MMAP_FASTMEM
@ -135,10 +132,11 @@ bool CanUseFastmemForAddress(VirtualMemoryAddress address);
void SetExpansionROM(std::vector<u8> data);
extern std::bitset<RAM_8MB_CODE_PAGE_COUNT> g_ram_code_bits;
extern u8* g_ram; // 2MB-8MB RAM
extern u32 g_ram_size; // Active size of RAM.
extern u32 g_ram_mask; // Active address bits for RAM.
extern u8* g_bios; // 512K BIOS ROM
extern u8* g_ram; // 2MB-8MB RAM
extern u8* g_unprotected_ram; // RAM without page protection, use for debugger access.
extern u32 g_ram_size; // Active size of RAM.
extern u32 g_ram_mask; // Active address bits for RAM.
extern u8* g_bios; // 512K BIOS ROM
extern std::array<TickCount, 3> g_exp1_access_time;
extern std::array<TickCount, 3> g_exp2_access_time;
extern std::array<TickCount, 3> g_bios_access_time;

View file

@ -672,7 +672,19 @@ void CPU::CodeCache::ClearBlocks()
Common::PageFaultHandler::HandlerResult CPU::CodeCache::ExceptionHandler(void* exception_pc, void* fault_address,
bool is_write)
{
// TODO: Catch general RAM writes, not just fastmem
if (static_cast<const u8*>(fault_address) >= Bus::g_ram &&
static_cast<const u8*>(fault_address) < (Bus::g_ram + Bus::RAM_8MB_SIZE))
{
// Writing to protected RAM.
DebugAssert(is_write);
const u32 guest_address = static_cast<u32>(static_cast<const u8*>(fault_address) - Bus::g_ram);
const u32 page_index = Bus::GetRAMCodePageIndex(guest_address);
Log_DevFmt("Page fault on protected RAM @ 0x{:08X} (page #{}), invalidating code cache.", guest_address,
page_index);
InvalidateBlocksWithPageIndex(page_index);
return Common::PageFaultHandler::HandlerResult::ContinueExecution;
}
#ifdef ENABLE_RECOMPILER_SUPPORT
return HandleFastmemException(exception_pc, fault_address, is_write);
#else

View file

@ -38,16 +38,4 @@ void InvalidateBlocksWithPageIndex(u32 page_index);
/// Invalidates all blocks in the cache.
void InvalidateAllRAMBlocks();
/// Invalidates any code pages which overlap the specified range.
ALWAYS_INLINE void InvalidateCodePages(PhysicalMemoryAddress address, u32 word_count)
{
const u32 start_page = address / HOST_PAGE_SIZE;
const u32 end_page = (address + word_count * sizeof(u32) - sizeof(u32)) / HOST_PAGE_SIZE;
for (u32 page = start_page; page <= end_page; page++)
{
if (Bus::g_ram_code_bits[page])
CPU::CodeCache::InvalidateBlocksWithPageIndex(page);
}
}
} // namespace CPU::CodeCache

View file

@ -2740,17 +2740,17 @@ ALWAYS_INLINE bool CPU::DoSafeMemoryAccess(VirtualMemoryAddress address, u32& va
{
if constexpr (size == MemoryAccessSize::Byte)
{
value = g_ram[offset];
value = g_unprotected_ram[offset];
}
else if constexpr (size == MemoryAccessSize::HalfWord)
{
u16 temp;
std::memcpy(&temp, &g_ram[offset], sizeof(temp));
std::memcpy(&temp, &g_unprotected_ram[offset], sizeof(temp));
value = ZeroExtend32(temp);
}
else if constexpr (size == MemoryAccessSize::Word)
{
std::memcpy(&value, &g_ram[offset], sizeof(u32));
std::memcpy(&value, &g_unprotected_ram[offset], sizeof(u32));
}
}
else
@ -2759,9 +2759,9 @@ ALWAYS_INLINE bool CPU::DoSafeMemoryAccess(VirtualMemoryAddress address, u32& va
if constexpr (size == MemoryAccessSize::Byte)
{
if (g_ram[offset] != Truncate8(value))
if (g_unprotected_ram[offset] != Truncate8(value))
{
g_ram[offset] = Truncate8(value);
g_unprotected_ram[offset] = Truncate8(value);
if (g_ram_code_bits[page_index])
CPU::CodeCache::InvalidateBlocksWithPageIndex(page_index);
}
@ -2770,10 +2770,10 @@ ALWAYS_INLINE bool CPU::DoSafeMemoryAccess(VirtualMemoryAddress address, u32& va
{
const u16 new_value = Truncate16(value);
u16 old_value;
std::memcpy(&old_value, &g_ram[offset], sizeof(old_value));
std::memcpy(&old_value, &g_unprotected_ram[offset], sizeof(old_value));
if (old_value != new_value)
{
std::memcpy(&g_ram[offset], &new_value, sizeof(u16));
std::memcpy(&g_unprotected_ram[offset], &new_value, sizeof(u16));
if (g_ram_code_bits[page_index])
CPU::CodeCache::InvalidateBlocksWithPageIndex(page_index);
}
@ -2781,10 +2781,10 @@ ALWAYS_INLINE bool CPU::DoSafeMemoryAccess(VirtualMemoryAddress address, u32& va
else if constexpr (size == MemoryAccessSize::Word)
{
u32 old_value;
std::memcpy(&old_value, &g_ram[offset], sizeof(u32));
std::memcpy(&old_value, &g_unprotected_ram[offset], sizeof(u32));
if (old_value != value)
{
std::memcpy(&g_ram[offset], &value, sizeof(u32));
std::memcpy(&g_unprotected_ram[offset], &value, sizeof(u32));
if (g_ram_code_bits[page_index])
CPU::CodeCache::InvalidateBlocksWithPageIndex(page_index);
}
@ -2955,11 +2955,8 @@ void* CPU::GetDirectWriteMemoryPointer(VirtualMemoryAddress address, MemoryAcces
const PhysicalMemoryAddress paddr = address & PHYSICAL_MEMORY_ADDRESS_MASK;
#if 0
// Not enabled until we can protect code regions.
if (paddr < RAM_MIRROR_END)
return &g_ram[paddr & RAM_MASK];
#endif
return &g_ram[paddr & g_ram_mask];
if ((paddr & DCACHE_LOCATION_MASK) == DCACHE_LOCATION)
return &g_state.dcache[paddr & DCACHE_OFFSET_MASK];

View file

@ -109,8 +109,6 @@ public:
void EmitLoadGlobalAddress(HostReg host_reg, const void* ptr);
// Automatically generates an exception handler.
Value GetFastmemLoadBase();
Value GetFastmemStoreBase();
Value EmitLoadGuestMemory(Instruction instruction, const CodeCache::InstructionInfo& info, const Value& address,
const SpeculativeValue& address_spec, RegSize size);
void EmitLoadGuestRAMFastmem(const Value& address, RegSize size, Value& result);

View file

@ -1999,7 +1999,6 @@ void CodeGenerator::EmitStoreGuestMemoryFastmem(Instruction instruction, const C
{
m_emit->lsr(GetHostReg32(RARG1), GetHostReg32(address_reg), Bus::FASTMEM_LUT_PAGE_SHIFT);
m_emit->and_(GetHostReg32(RARG2), GetHostReg32(address_reg), Bus::FASTMEM_LUT_PAGE_MASK);
m_emit->add(GetHostReg64(RARG3), GetFastmemBasePtrReg(), Bus::FASTMEM_LUT_NUM_PAGES * sizeof(u32*));
m_emit->ldr(GetHostReg64(RARG1), a64::MemOperand(GetHostReg64(RARG3), GetHostReg32(RARG1), a64::LSL, 3));
host_pc = GetCurrentNearCodePointer();

View file

@ -63,7 +63,7 @@ Value CodeGenerator::EmitLoadGuestMemory(Instruction instruction, const CodeCach
Value result = m_register_cache.AllocateScratch(HostPointerSize);
const bool use_fastmem =
const bool use_fastmem = !g_settings.cpu_recompiler_memory_exceptions &&
(address_spec ? Bus::CanUseFastmemForAddress(*address_spec) : true) && !SpeculativeIsCacheIsolated();
if (address_spec)
{
@ -137,7 +137,7 @@ void CodeGenerator::EmitStoreGuestMemory(Instruction instruction, const CodeCach
}
}
const bool use_fastmem =
const bool use_fastmem = !g_settings.cpu_recompiler_memory_exceptions &&
(address_spec ? Bus::CanUseFastmemForAddress(*address_spec) : true) && !SpeculativeIsCacheIsolated();
if (address_spec)
{

View file

@ -2524,8 +2524,7 @@ void CodeGenerator::EmitStoreGuestMemoryFastmem(Instruction instruction, const C
m_emit->mov(GetHostReg32(RARG2), GetHostReg32(RARG1));
m_emit->shr(GetHostReg32(RARG1), Bus::FASTMEM_LUT_PAGE_SHIFT);
m_emit->and_(GetHostReg32(RARG2), Bus::FASTMEM_LUT_PAGE_MASK);
m_emit->mov(GetHostReg64(RARG1),
m_emit->qword[GetFastmemBasePtrReg() + GetHostReg64(RARG1) * 8 + (Bus::FASTMEM_LUT_NUM_PAGES * 8)]);
m_emit->mov(GetHostReg64(RARG1), m_emit->qword[GetFastmemBasePtrReg() + GetHostReg64(RARG1) * 8]);
host_pc = GetCurrentNearCodePointer();
switch (size)

View file

@ -739,7 +739,6 @@ TickCount DMA::TransferDeviceToMemory(Channel channel, u32 address, u32 incremen
const u32 terminator = UINT32_C(0xFFFFFF);
std::memcpy(&ram_pointer[address], &terminator, sizeof(terminator));
CPU::CodeCache::InvalidateCodePages(address, word_count);
return Bus::GetDMARAMTickCount(word_count);
}
@ -787,7 +786,6 @@ TickCount DMA::TransferDeviceToMemory(Channel channel, u32 address, u32 incremen
}
}
CPU::CodeCache::InvalidateCodePages(address, word_count);
return Bus::GetDMARAMTickCount(word_count);
}

View file

@ -3191,7 +3191,7 @@ bool System::DumpRAM(const char* filename)
if (!IsValid())
return false;
return FileSystem::WriteBinaryFile(filename, Bus::g_ram, Bus::g_ram_size);
return FileSystem::WriteBinaryFile(filename, Bus::g_unprotected_ram, Bus::g_ram_size);
}
bool System::DumpVRAM(const char* filename)