Core: Postpone page table updates when DR is set

MMU::PageTableUpdated takes some time to run, so ideally we shouldn't
call it every time the game runs tlbie. And it is possible to delay
running MMU::PageTableUpdated, but only on the condition that no page
table mappings are currently visible through fastmem. (If a game has
removed a mapping and then ran tlbie, trying to access that mapping
through fastmem must not succeed. The reverse - a game adding a
mapping but it not being available through fastmem fast accesses - is
not a correctness problem since we can fall back to a slow access.)

In 7b885b8, we accomplished this in the case where DR is unset. DR being
unset means memory uses physical mappings (no BAT or page table
mappings), so the fact that we haven't ran MMU::PageTableUpdated yet is
invisible to the game. In this commit you're currently looking at, we
also accomplish it in the case where DR is set. This required some
additional work. Previously, we had two fastmem regions (4 GiB each):
One for physical mappings (DR unset) and one for logical mappings (DR
set). We now also have a third fastmem region: Logical mappings minus
page table mappings. When the game runs tlbie while DR is set, we swap
from the regular logical mappings region to the logical mappings minus
page table mappings region. Then the next time we get a fastmem fault,
we run MMU::PageTableUpdated and switch back to the regular logical
mappings region.

For reference, the Disney Trio of Destruction runs tlbie with DR unset,
and all GameCube games I've checked (admittedly not very many) run tlbie
with DR set.
This commit is contained in:
JosJuice
2026-04-30 20:25:19 +02:00
parent d19952cc11
commit 7d9c712f1b
9 changed files with 154 additions and 112 deletions

View File

@@ -183,8 +183,10 @@ bool MemoryManager::InitFastmemArena()
// the emulated system. This lets the JIT emulate PPC load/store instructions by translating a PPC
// address to a host address as follows and then using a regular load/store instruction:
//
// RMEM = ppcState.msr.DR ? m_logical_base : m_physical_base
// host_address = RMEM + u32(ppc_address_base + ppc_address_offset)
// logical_base = m_ppc_state.pagetable_update_pending ?
// m_logical_base_without_page_table : m_logical_base_with_page_table;
// RMEM = m_ppc_state.msr.DR ? m_logical_base : logical_base;
// host_address = RMEM + u32(ppc_address_base + ppc_address_offset);
//
// If the resulting host address is backed by real memory, the memory access will simply work.
// If not, a segfault handler will backpatch the JIT code to instead call functions in MMU.cpp.
@@ -199,19 +201,22 @@ bool MemoryManager::InitFastmemArena()
// 4 GiB range by at most 2 GiB in either direction. So, make sure we have 2 GiB of guard pages
// on each side of each 4 GiB range.
//
// We need two 4 GiB ranges, one for PPC addresses with address translation disabled
// (m_physical_base) and one for PPC addresses with address translation enabled (m_logical_base),
// so our memory map ends up looking like this:
// We need three 4 GiB ranges, one for PPC addresses with address translation disabled
// (m_physical_base) and two for PPC addresses with address translation enabled
// (m_logical_base_without_page_table and m_logical_base_with_page_table), so our memory map ends
// up looking like this:
//
// 2 GiB guard
// 4 GiB view for disabled address translation
// 2 GiB guard
// 4 GiB view for enabled address translation
// 4 GiB view for enabled address translation without page table
// 2 GiB guard
// 4 GiB view for enabled address translation with page table
// 2 GiB guard
constexpr size_t ppc_view_size = 0x1'0000'0000;
constexpr size_t guard_size = 0x8000'0000;
constexpr size_t memory_size = ppc_view_size * 2 + guard_size * 3;
constexpr size_t memory_size = ppc_view_size * 3 + guard_size * 4;
m_fastmem_arena = m_arena.ReserveMemoryRegion(memory_size);
if (!m_fastmem_arena)
@@ -221,7 +226,8 @@ bool MemoryManager::InitFastmemArena()
}
m_physical_base = m_fastmem_arena + guard_size;
m_logical_base = m_fastmem_arena + ppc_view_size + guard_size * 2;
m_logical_base_without_page_table = m_fastmem_arena + ppc_view_size + guard_size * 2;
m_logical_base_with_page_table = m_fastmem_arena + ppc_view_size * 2 + guard_size * 3;
for (const PhysicalMemoryRegion& region : m_physical_regions)
{
@@ -247,11 +253,17 @@ bool MemoryManager::InitFastmemArena()
void MemoryManager::UpdateDBATMappings(const PowerPC::BatTable& dbat_table)
{
for (const auto& [logical_address, entry] : m_dbat_mapped_entries)
for (const auto& [logical_address, entry] : m_dbat_mapped_entries_without_page_table)
{
m_arena.UnmapFromMemoryRegion(entry.mapped_pointer, entry.mapped_size);
}
m_dbat_mapped_entries.clear();
m_dbat_mapped_entries_without_page_table.clear();
for (const auto& [logical_address, entry] : m_dbat_mapped_entries_with_page_table)
{
m_arena.UnmapFromMemoryRegion(entry.mapped_pointer, entry.mapped_size);
}
m_dbat_mapped_entries_with_page_table.clear();
RemoveAllPageTableMappings();
@@ -298,18 +310,30 @@ void MemoryManager::UpdateDBATMappings(const PowerPC::BatTable& dbat_table)
if (m_is_fastmem_arena_initialized)
{
u32 position = physical_region.shm_position + intersection_start - mapping_address;
u8* base = m_logical_base + mapped_logical_address;
void* mapped_pointer = m_arena.MapInMemoryRegion(position, mapped_size, base, true);
if (!mapped_pointer)
u8* base_1 = m_logical_base_without_page_table + mapped_logical_address;
void* mapped_pointer_1 = m_arena.MapInMemoryRegion(position, mapped_size, base_1, true);
if (!mapped_pointer_1)
{
PanicAlertFmt("Memory::UpdateDBATMappings(): Failed to map memory region at 0x{:08X} "
"(size 0x{:08X}) into logical fastmem region at 0x{:08X}.",
"(size 0x{:08X}) into logical fastmem region 1 at 0x{:08X}.",
intersection_start, mapped_size, logical_address);
continue;
}
m_dbat_mapped_entries.emplace(logical_address,
LogicalMemoryView{mapped_pointer, mapped_size});
m_dbat_mapped_entries_without_page_table.emplace(
logical_address, LogicalMemoryView{mapped_pointer_1, mapped_size});
u8* base_2 = m_logical_base_with_page_table + mapped_logical_address;
void* mapped_pointer_2 = m_arena.MapInMemoryRegion(position, mapped_size, base_2, true);
if (!mapped_pointer_2)
{
PanicAlertFmt("Memory::UpdateDBATMappings(): Failed to map memory region at 0x{:08X} "
"(size 0x{:08X}) into logical fastmem region 2 at 0x{:08X}.",
intersection_start, mapped_size, logical_address);
continue;
}
m_dbat_mapped_entries_with_page_table.emplace(
logical_address, LogicalMemoryView{mapped_pointer_2, mapped_size});
}
u32 bat_index = mapped_logical_address / PowerPC::BAT_PAGE_SIZE;
@@ -401,7 +425,8 @@ void MemoryManager::AddHostPageTableMapping(u32 logical_address, u32 translated_
// Found an overlapping region; map it.
const u32 position = physical_region.shm_position + intersection_start - mapping_address;
u8* const base = m_logical_base + logical_address + intersection_start - translated_address;
u8* const base =
m_logical_base_with_page_table + logical_address + intersection_start - translated_address;
const u32 mapped_size = intersection_end - intersection_start;
const auto it = m_page_table_mapped_entries.find(logical_address);
@@ -573,11 +598,17 @@ void MemoryManager::ShutdownFastmemArena()
m_arena.UnmapFromMemoryRegion(base, region.size);
}
for (const auto& [logical_address, entry] : m_dbat_mapped_entries)
for (const auto& [logical_address, entry] : m_dbat_mapped_entries_without_page_table)
{
m_arena.UnmapFromMemoryRegion(entry.mapped_pointer, entry.mapped_size);
}
m_dbat_mapped_entries.clear();
m_dbat_mapped_entries_without_page_table.clear();
for (const auto& [logical_address, entry] : m_dbat_mapped_entries_with_page_table)
{
m_arena.UnmapFromMemoryRegion(entry.mapped_pointer, entry.mapped_size);
}
m_dbat_mapped_entries_with_page_table.clear();
for (const auto& [logical_address, entry] : m_page_table_mapped_entries)
{
@@ -593,7 +624,8 @@ void MemoryManager::ShutdownFastmemArena()
m_fastmem_arena = nullptr;
m_fastmem_arena_size = 0;
m_physical_base = nullptr;
m_logical_base = nullptr;
m_logical_base_without_page_table = nullptr;
m_logical_base_with_page_table = nullptr;
m_is_fastmem_arena_initialized = false;
}

View File

@@ -81,7 +81,8 @@ public:
bool IsAddressInFastmemArea(const u8* address) const;
u8* GetPhysicalBase() const { return m_physical_base; }
u8* GetLogicalBase() const { return m_logical_base; }
u8* GetLogicalBaseWithoutPageTable() const { return m_logical_base_without_page_table; }
u8* GetLogicalBaseWithPageTable() const { return m_logical_base_with_page_table; }
u8* GetPhysicalPageMappingsBase() const { return m_physical_page_mappings_base; }
u8* GetLogicalPageMappingsBase() const { return m_logical_page_mappings_base; }
@@ -181,10 +182,11 @@ private:
u8* m_fastmem_arena = nullptr;
size_t m_fastmem_arena_size = 0;
u8* m_physical_base = nullptr;
u8* m_logical_base = nullptr;
u8* m_logical_base_without_page_table = nullptr;
u8* m_logical_base_with_page_table = nullptr;
// This page table is used for a "soft MMU" implementation when
// setting up the full memory map in process memory isn't possible.
// This page table is used for a "soft MMU" implementation when setting up the full
// memory map in process memory isn't possible. Only BAT mappings are included.
u8* m_physical_page_mappings_base = nullptr;
u8* m_logical_page_mappings_base = nullptr;
@@ -252,9 +254,13 @@ private:
// [0x7E000000, 0x80000000) - FakeVMEM
// [0xE0000000, 0xE0040000) - 256KB locked L1
//
// The 4GB starting at m_logical_base represents access from the CPU
// with address translation turned on. This mapping is computed based
// on the BAT registers.
// The 4GB starting at m_logical_base_without_page_table represents access
// from the CPU with address translation turned on. This mapping is computed
// based on the BAT registers.
//
// The 4GB starting at m_logical_base_with_page_table is the same as
// m_logical_base_without_page_table, except mappings are computed based on
// the page table in addition to the BAT registers.
//
// Each of these 4GB regions is surrounded by 2GB of empty space so overflows
// in address computation in the JIT don't access unrelated memory.
@@ -270,7 +276,8 @@ private:
std::array<PhysicalMemoryRegion, 4> m_physical_regions{};
// The key is the logical address
std::map<u32, LogicalMemoryView> m_dbat_mapped_entries;
std::map<u32, LogicalMemoryView> m_dbat_mapped_entries_without_page_table;
std::map<u32, LogicalMemoryView> m_dbat_mapped_entries_with_page_table;
std::map<u32, LogicalMemoryView> m_page_table_mapped_entries;
std::array<void*, PowerPC::BAT_PAGE_COUNT> m_physical_page_mappings{};

View File

@@ -145,16 +145,26 @@ bool Jit64::HandleFault(uintptr_t access_address, SContext* ctx)
if (memory.IsAddressInFastmemArea(reinterpret_cast<u8*>(access_address)))
{
auto& ppc_state = m_system.GetPPCState();
const uintptr_t memory_base = reinterpret_cast<uintptr_t>(
ppc_state.msr.DR ? memory.GetLogicalBase() : memory.GetPhysicalBase());
m_ppc_state.msr.DR ?
(m_ppc_state.pagetable_update_pending ? memory.GetLogicalBaseWithoutPageTable() :
memory.GetLogicalBaseWithPageTable()) :
memory.GetPhysicalBase());
if (access_address < memory_base || access_address >= memory_base + 0x1'0000'0000)
{
WARN_LOG_FMT(DYNA_REC,
"Jit64 address calculation overflowed! Please report if this happens a lot. "
"PC {:#018x}, access address {:#018x}, memory base {:#018x}, MSR.DR {}",
ctx->CTX_PC, access_address, memory_base, ppc_state.msr.DR);
ctx->CTX_PC, access_address, memory_base, m_ppc_state.msr.DR);
}
if (m_ppc_state.msr.DR && m_ppc_state.pagetable_update_pending)
{
// Switch from logical base without page table to logical base with page table,
// then rerun the code that faulted.
m_system.GetMMU().PageTableUpdated();
return true;
}
return BackPatch(ctx);
@@ -373,7 +383,7 @@ void Jit64::FallBackToInterpreter(UGeckoInstruction inst)
// We must also update constant propagation
m_constant_propagation.ClearGPRs(js.op->regsOut);
if (js.op->opinfo->flags & FL_SET_MSR)
if (js.op->opinfo->flags & (FL_SET_MSR | FL_TLBIE))
EmitUpdateMembase();
if (js.op->canEndBlock)
@@ -520,11 +530,12 @@ void Jit64::MSRUpdated(const OpArg& msr, X64Reg scratch_reg)
if (msr.IsImm())
{
MOV(64, R(RMEM),
ImmPtr(UReg_MSR(msr.Imm32()).DR ? memory.GetLogicalBase() : memory.GetPhysicalBase()));
ImmPtr(UReg_MSR(msr.Imm32()).DR ? memory.GetLogicalBaseWithPageTable() :
memory.GetPhysicalBase()));
}
else
{
MOV(64, R(RMEM), ImmPtr(memory.GetLogicalBase()));
MOV(64, R(RMEM), ImmPtr(memory.GetLogicalBaseWithPageTable()));
MOV(64, R(scratch_reg), ImmPtr(memory.GetPhysicalBase()));
TEST(32, msr, Imm32(dr_bit));
CMOVcc(64, RMEM, R(scratch_reg), CC_Z);

View File

@@ -139,20 +139,33 @@ bool JitArm64::HandleFault(uintptr_t access_address, SContext* ctx)
if (memory.IsAddressInFastmemArea(reinterpret_cast<u8*>(access_address)))
{
const uintptr_t memory_base = reinterpret_cast<uintptr_t>(
m_ppc_state.msr.DR ? memory.GetLogicalBase() : memory.GetPhysicalBase());
m_ppc_state.msr.DR ?
(m_ppc_state.pagetable_update_pending ? memory.GetLogicalBaseWithoutPageTable() :
memory.GetLogicalBaseWithPageTable()) :
memory.GetPhysicalBase());
if (access_address < memory_base || access_address >= memory_base + 0x1'0000'0000)
{
ERROR_LOG_FMT(DYNA_REC,
"JitArm64 address calculation overflowed. This should never happen! "
"PC {:#018x}, access address {:#018x}, memory base {:#018x}, MSR.DR {}, "
"mem_ptr {}, pbase {}, lbase {}",
ctx->CTX_PC, access_address, memory_base, m_ppc_state.msr.DR,
fmt::ptr(m_ppc_state.mem_ptr), fmt::ptr(memory.GetPhysicalBase()),
fmt::ptr(memory.GetLogicalBase()));
ASSERT_MSG(DYNA_REC, false,
"JitArm64 address calculation overflowed!\n\n"
"PC {:#018x}, access address {:#018x}, memory base {:#018x}, MSR.DR {}, "
"pagetable update pending {}, mem_ptr {}, pbase {}, lbase1 {}, lbase2 {}",
ctx->CTX_PC, access_address, memory_base, m_ppc_state.msr.DR,
m_ppc_state.pagetable_update_pending, fmt::ptr(m_ppc_state.mem_ptr),
fmt::ptr(memory.GetPhysicalBase()),
fmt::ptr(memory.GetLogicalBaseWithoutPageTable()),
fmt::ptr(memory.GetLogicalBaseWithPageTable()));
}
else if (m_ppc_state.msr.DR && m_ppc_state.pagetable_update_pending)
{
// Switch from logical base without page table to logical base with page table,
// then rerun the code that faulted.
m_system.GetMMU().PageTableUpdated();
success = true;
}
else
{
// Backpatch the code that faulted.
success = HandleFastmemFault(ctx);
}
}
@@ -282,7 +295,7 @@ void JitArm64::FallBackToInterpreter(UGeckoInstruction inst)
// We must also update constant propagation
m_constant_propagation.ClearGPRs(js.op->regsOut);
if (js.op->opinfo->flags & FL_SET_MSR)
if (js.op->opinfo->flags & (FL_SET_MSR | FL_TLBIE))
EmitUpdateMembase();
if (js.op->canEndBlock)
@@ -429,10 +442,10 @@ void JitArm64::MSRUpdated(u32 msr)
{
// Update mem_ptr
auto& memory = m_system.GetMemory();
MOVP2R(MEM_REG,
UReg_MSR(msr).DR ?
(jo.fastmem ? memory.GetLogicalBase() : memory.GetLogicalPageMappingsBase()) :
(jo.fastmem ? memory.GetPhysicalBase() : memory.GetPhysicalPageMappingsBase()));
MOVP2R(MEM_REG, UReg_MSR(msr).DR ? (jo.fastmem ? memory.GetLogicalBaseWithPageTable() :
memory.GetLogicalPageMappingsBase()) :
(jo.fastmem ? memory.GetPhysicalBase() :
memory.GetPhysicalPageMappingsBase()));
STR(IndexType::Unsigned, MEM_REG, PPC_REG, PPCSTATE_OFF(mem_ptr));
// Update feature_flags
@@ -476,7 +489,8 @@ void JitArm64::MSRUpdated(ARM64Reg msr)
// Update mem_ptr
auto& memory = m_system.GetMemory();
MOVP2R(MEM_REG, jo.fastmem ? memory.GetLogicalBase() : memory.GetLogicalPageMappingsBase());
MOVP2R(MEM_REG,
jo.fastmem ? memory.GetLogicalBaseWithPageTable() : memory.GetLogicalPageMappingsBase());
MOVP2R(XA, jo.fastmem ? memory.GetPhysicalBase() : memory.GetPhysicalPageMappingsBase());
TST(msr, LogicalImm(1ULL << UReg_MSR{}.DR.StartBit(), GPRSize::B32));
CSEL(MEM_REG, MEM_REG, XA, CCFlags::CC_NEQ);

View File

@@ -102,8 +102,10 @@ void JitInterface::UpdateMembase()
#endif
if (ppc_state.msr.DR)
{
ppc_state.mem_ptr =
fastmem_arena ? memory.GetLogicalBase() : memory.GetLogicalPageMappingsBase();
ppc_state.mem_ptr = fastmem_arena ? (ppc_state.pagetable_update_pending ?
memory.GetLogicalBaseWithoutPageTable() :
memory.GetLogicalBaseWithPageTable()) :
memory.GetLogicalPageMappingsBase();
}
else
{

View File

@@ -1375,10 +1375,8 @@ void MMU::InvalidateTLBEntry(u32 address)
m_ppc_state.tlb[PowerPC::DATA_TLB_INDEX][entry_index].Invalidate();
m_ppc_state.tlb[PowerPC::INST_TLB_INDEX][entry_index].Invalidate();
if (m_ppc_state.msr.DR)
PageTableUpdated();
else
m_ppc_state.pagetable_update_pending = true;
m_ppc_state.pagetable_update_pending = true;
m_system.GetJitInterface().UpdateMembase();
}
void MMU::ClearPageTable()

View File

@@ -385,7 +385,7 @@ constexpr std::array<GekkoOPTemplate, 107> s_table31{{
{310, "eciwx", OpType::System, 1, FL_IN_A0B | FL_OUT_D | FL_LOADSTORE},
{438, "ecowx", OpType::System, 1, FL_IN_A0B | FL_IN_S | FL_LOADSTORE},
{854, "eieio", OpType::System, 1, 0},
{306, "tlbie", OpType::System, 1, FL_IN_B | FL_PROGRAMEXCEPTION},
{306, "tlbie", OpType::System, 1, FL_IN_B | FL_PROGRAMEXCEPTION | FL_TLBIE},
{566, "tlbsync", OpType::System, 1, FL_PROGRAMEXCEPTION},
}};

View File

@@ -68,6 +68,7 @@ enum InstructionFlags : u64
FL_SET_CRx = FL_SET_CR0 | FL_SET_CR1 | FL_SET_CRn | FL_SET_ALL_CR,
FL_READ_CRx = FL_READ_CRn | FL_READ_CR_BI | FL_READ_ALL_CR,
FL_SET_MSR = (1ull << 39),
FL_TLBIE = (1ull << 40),
};
enum class OpType

View File

@@ -64,7 +64,7 @@ public:
{
std::string logical_address;
auto& memory = Core::System::GetInstance().GetMemory();
auto logical_base = reinterpret_cast<uintptr_t>(memory.GetLogicalBase());
auto logical_base = reinterpret_cast<uintptr_t>(memory.GetLogicalBaseWithPageTable());
if (access_address >= logical_base && access_address < logical_base + 0x1'0000'0000)
logical_address = fmt::format(" (PPC {:#010x})", access_address - logical_base);
@@ -80,7 +80,8 @@ public:
// After we return from the signal handler, the memory access will happen again.
// Let it succeed this time so the signal handler won't get called over and over.
auto& memory = Core::System::GetInstance().GetMemory();
const uintptr_t logical_base = reinterpret_cast<uintptr_t>(memory.GetLogicalBase());
const uintptr_t logical_base =
reinterpret_cast<uintptr_t>(memory.GetLogicalBaseWithPageTable());
const u32 logical_address = static_cast<u32>(access_address - logical_base);
const u32 mask = s_minimum_mapping_size - 1;
for (u32 i = logical_address & mask; i < s_minimum_mapping_size; i += PowerPC::HW_PAGE_SIZE)
@@ -102,27 +103,6 @@ private:
StubBlockCache m_block_cache;
};
// This is used as a performance optimization. If several page table updates are performed while
// DR is disabled, MMU.cpp will only have to rescan the page table one time once DR is enabled again
// instead of after each page table update.
class DisableDR final
{
public:
DisableDR()
{
auto& system = Core::System::GetInstance();
system.GetPPCState().msr.DR = 0;
system.GetPowerPC().MSRUpdated();
}
~DisableDR()
{
auto& system = Core::System::GetInstance();
system.GetPPCState().msr.DR = 1;
system.GetPowerPC().MSRUpdated();
}
};
class PageTableHostMappingTest : public ::testing::Test
{
public:
@@ -240,9 +220,13 @@ public:
SCOPED_TRACE(
fmt::format("ExpectMapped({:#010x}, {:#010x})", logical_address, physical_address));
auto& memory = Core::System::GetInstance().GetMemory();
auto& system = Core::System::GetInstance();
if (system.GetPPCState().pagetable_update_pending)
system.GetMMU().PageTableUpdated();
auto& memory = system.GetMemory();
u8* physical_base = memory.GetPhysicalBase();
u8* logical_base = memory.GetLogicalBase();
u8* logical_base = memory.GetLogicalBaseWithPageTable();
auto* physical_ptr = reinterpret_cast<volatile u32*>(physical_base + physical_address);
auto* logical_ptr = reinterpret_cast<volatile u32*>(logical_base + logical_address);
@@ -267,9 +251,13 @@ public:
SCOPED_TRACE(
fmt::format("ExpectReadOnlyMapped({:#010x}, {:#010x})", logical_address, physical_address));
auto& memory = Core::System::GetInstance().GetMemory();
auto& system = Core::System::GetInstance();
if (system.GetPPCState().pagetable_update_pending)
system.GetMMU().PageTableUpdated();
auto& memory = system.GetMemory();
u8* physical_base = memory.GetPhysicalBase();
u8* logical_base = memory.GetLogicalBase();
u8* logical_base = memory.GetLogicalBaseWithPageTable();
auto* physical_ptr = reinterpret_cast<volatile u32*>(physical_base + physical_address);
auto* logical_ptr = reinterpret_cast<volatile u32*>(logical_base + logical_address);
@@ -293,8 +281,12 @@ public:
{
SCOPED_TRACE(fmt::format("ExpectNotMapped({:#010x})", logical_address));
auto& memory = Core::System::GetInstance().GetMemory();
u8* logical_base = memory.GetLogicalBase();
auto& system = Core::System::GetInstance();
if (system.GetPPCState().pagetable_update_pending)
system.GetMMU().PageTableUpdated();
auto& memory = system.GetMemory();
u8* logical_base = memory.GetLogicalBaseWithPageTable();
auto* logical_ptr = reinterpret_cast<volatile u32*>(logical_base + logical_address);
s_detection_address = logical_ptr;
@@ -382,7 +374,6 @@ public:
static void AddHostSizedMapping(u32 logical_address, u32 physical_address, u32 index)
{
DisableDR disable_dr;
for (u32 i = 0; i < s_minimum_mapping_size; i += PowerPC::HW_PAGE_SIZE)
AddMapping(logical_address + i, physical_address + i, index);
}
@@ -396,7 +387,6 @@ public:
static void RemoveHostSizedMapping(u32 logical_address, u32 physical_address, u32 index)
{
DisableDR disable_dr;
for (u32 i = 0; i < s_minimum_mapping_size; i += PowerPC::HW_PAGE_SIZE)
RemoveMapping(logical_address + i, physical_address + i, index);
}
@@ -438,23 +428,17 @@ TEST_F(PageTableHostMappingTest, Basic)
TEST_F(PageTableHostMappingTest, LargeHostPageMismatchedAddresses)
{
{
DisableDR disable_dr;
AddMapping(0x10110000, 0x00111000, 0);
for (u32 i = PowerPC::HW_PAGE_SIZE; i < s_minimum_mapping_size; i += PowerPC::HW_PAGE_SIZE)
AddMapping(0x10110000 + i, 0x00110000 + i, 0);
}
AddMapping(0x10110000, 0x00111000, 0);
for (u32 i = PowerPC::HW_PAGE_SIZE; i < s_minimum_mapping_size; i += PowerPC::HW_PAGE_SIZE)
AddMapping(0x10110000 + i, 0x00110000 + i, 0);
ExpectMappedOnlyIf4KHostPages(0x10110000, 0x00111000);
}
TEST_F(PageTableHostMappingTest, LargeHostPageMisalignedAddresses)
{
{
DisableDR disable_dr;
for (u32 i = 0; i < s_minimum_mapping_size * 2; i += PowerPC::HW_PAGE_SIZE)
AddMapping(0x10120000 + i, 0x00121000 + i, 0);
}
for (u32 i = 0; i < s_minimum_mapping_size * 2; i += PowerPC::HW_PAGE_SIZE)
AddMapping(0x10120000 + i, 0x00121000 + i, 0);
ExpectMappedOnlyIf4KHostPages(0x10120000, 0x00121000);
ExpectMappedOnlyIf4KHostPages(0x10120000 + s_minimum_mapping_size,
@@ -463,14 +447,11 @@ TEST_F(PageTableHostMappingTest, LargeHostPageMisalignedAddresses)
TEST_F(PageTableHostMappingTest, ChangeSR)
{
for (u32 i = 0; i < s_minimum_mapping_size; i += PowerPC::HW_PAGE_SIZE)
{
DisableDR disable_dr;
for (u32 i = 0; i < s_minimum_mapping_size; i += PowerPC::HW_PAGE_SIZE)
{
auto [pte1, pte2] = CreateMapping(0x20130000 + i, 0x00130000 + i);
pte1.VSID = 0xabc;
SetPTE(pte1, pte2, 0x20130000 + i, 0);
}
auto [pte1, pte2] = CreateMapping(0x20130000 + i, 0x00130000 + i);
pte1.VSID = 0xabc;
SetPTE(pte1, pte2, 0x20130000 + i, 0);
}
ExpectNotMapped(0x20130000);
@@ -635,14 +616,11 @@ TEST_F(PageTableHostMappingTest, WIMG)
{
for (u32 i = 0; i < 16; ++i)
{
for (u32 j = 0; j < s_minimum_mapping_size; j += PowerPC::HW_PAGE_SIZE)
{
DisableDR disable_dr;
for (u32 j = 0; j < s_minimum_mapping_size; j += PowerPC::HW_PAGE_SIZE)
{
auto [pte1, pte2] = CreateMapping(0x101e0000 + j, 0x001e0000 + j);
pte2.WIMG = i;
SetPTE(pte1, pte2, 0x101e0000 + j, 0);
}
auto [pte1, pte2] = CreateMapping(0x101e0000 + j, 0x001e0000 + j);
pte2.WIMG = i;
SetPTE(pte1, pte2, 0x101e0000 + j, 0);
}
if ((i & 0b1100) != 0)
@@ -657,7 +635,6 @@ TEST_F(PageTableHostMappingTest, RC)
auto& mmu = Core::System::GetInstance().GetMMU();
const auto set_up_mapping = [] {
DisableDR disable_dr;
for (u32 i = 0; i < s_minimum_mapping_size; i += PowerPC::HW_PAGE_SIZE)
{
auto [pte1, pte2] = CreateMapping(0x101f0000 + i, 0x001f0000 + i);