Support for dynamic BAT modification (dynamic-bat).

Fundamentally, all this does is enforce the invariant that we always
translate effective addresses based on the current BAT registers and
page table before we do anything else with them.

This change can be logically divided into three parts.  The first part is
creating a table to represent the current BAT state, and keeping it up to
date (PowerPC::IBATUpdated, PowerPC::DBATUpdated, etc.).  This does
nothing by itself, but it's necessary for the other parts.

The second part (mostly in MMU.cpp) is simply removing all the hardcoded
checks for specific untranslated addresses, and consistently translating
addresses using the current BAT configuration. Very straightforward, but a
lot of code changes because we hardcoded assumptions all over the place.

The third part (mostly in Memmap.cpp) is making the fastmem arena reflect
the current BAT configuration.  We do this by redoing the mapping (calling
memmap()) based on the BAT table whenever it changes.

One additional minor change is that translation can fail in two ways:
either the segment is a direct store segment, or page table lookup failed.
The difference doesn't usually matter, but the difference affects cache
instructions, like dcbz.
This commit is contained in:
magumagu
2016-09-06 08:43:22 +02:00
committed by degasus
parent 3e0355e7f6
commit d557310371
20 changed files with 689 additions and 598 deletions
-115
View File
@@ -157,118 +157,3 @@ u8* MemArena::FindMemoryBase()
return static_cast<u8*>(base);
#endif
}
// yeah, this could also be done in like two bitwise ops...
#define SKIP(a_flags, b_flags) \
if (!(a_flags & MV_WII_ONLY) && (b_flags & MV_WII_ONLY)) \
continue; \
if (!(a_flags & MV_FAKE_VMEM) && (b_flags & MV_FAKE_VMEM)) \
continue;
static bool Memory_TryBase(u8* base, MemoryView* views, int num_views, u32 flags, MemArena* arena)
{
// OK, we know where to find free space. Now grab it!
// We just mimic the popular BAT setup.
int i;
for (i = 0; i < num_views; i++)
{
MemoryView* view = &views[i];
void* view_base;
bool use_sw_mirror;
SKIP(flags, view->flags);
#if _ARCH_64
// On 64-bit, we map the same file position multiple times, so we
// don't need the software fallback for the mirrors.
view_base = base + view->virtual_address;
use_sw_mirror = false;
#else
// On 32-bit, we don't have the actual address space to store all
// the mirrors, so we just map the fallbacks somewhere in our address
// space and use the software fallbacks for mirroring.
view_base = base + (view->virtual_address & 0x3FFFFFFF);
use_sw_mirror = true;
#endif
if (use_sw_mirror && (view->flags & MV_MIRROR_PREVIOUS))
{
view->view_ptr = views[i - 1].view_ptr;
}
else
{
view->mapped_ptr = arena->CreateView(view->shm_position, view->size, view_base);
view->view_ptr = view->mapped_ptr;
}
if (!view->view_ptr)
{
// Argh! ERROR! Free what we grabbed so far so we can try again.
MemoryMap_Shutdown(views, i + 1, flags, arena);
return false;
}
if (view->out_ptr)
*(view->out_ptr) = (u8*)view->view_ptr;
}
return true;
}
static u32 MemoryMap_InitializeViews(MemoryView* views, int num_views, u32 flags)
{
u32 shm_position = 0;
u32 last_position = 0;
for (int i = 0; i < num_views; i++)
{
// Zero all the pointers to be sure.
views[i].mapped_ptr = nullptr;
SKIP(flags, views[i].flags);
if (views[i].flags & MV_MIRROR_PREVIOUS)
shm_position = last_position;
views[i].shm_position = shm_position;
last_position = shm_position;
shm_position += views[i].size;
}
return shm_position;
}
u8* MemoryMap_Setup(MemoryView* views, int num_views, u32 flags, MemArena* arena)
{
u32 total_mem = MemoryMap_InitializeViews(views, num_views, flags);
arena->GrabSHMSegment(total_mem);
// Now, create views in high memory where there's plenty of space.
u8* base = MemArena::FindMemoryBase();
// This really shouldn't fail - in 64-bit, there will always be enough
// address space.
if (!Memory_TryBase(base, views, num_views, flags, arena))
{
PanicAlert("MemoryMap_Setup: Failed finding a memory base.");
exit(0);
return nullptr;
}
return base;
}
void MemoryMap_Shutdown(MemoryView* views, int num_views, u32 flags, MemArena* arena)
{
std::set<void*> freeset;
for (int i = 0; i < num_views; i++)
{
MemoryView* view = &views[i];
if (view->mapped_ptr && !freeset.count(view->mapped_ptr))
{
arena->ReleaseView(view->mapped_ptr, view->size);
freeset.insert(view->mapped_ptr);
view->mapped_ptr = nullptr;
}
}
}
-23
View File
@@ -35,26 +35,3 @@ private:
int fd;
#endif
};
enum
{
MV_MIRROR_PREVIOUS = 1,
MV_FAKE_VMEM = 2,
MV_WII_ONLY = 4,
};
struct MemoryView
{
u8** out_ptr;
u64 virtual_address;
u32 size;
u32 flags;
void* mapped_ptr;
void* view_ptr;
u32 shm_position;
};
// Uses a memory arena to set up an emulator-friendly memory map according to
// a passed-in list of MemoryView structures.
u8* MemoryMap_Setup(MemoryView* views, int num_views, u32 flags, MemArena* arena);
void MemoryMap_Shutdown(MemoryView* views, int num_views, u32 flags, MemArena* arena);
+4
View File
@@ -237,6 +237,8 @@ bool CBoot::Load_BS2(const std::string& _rBootROMFilename)
PowerPC::ppcState.spr[SPR_DBAT1L] = 0x0000002a;
PowerPC::ppcState.spr[SPR_DBAT3U] = 0xfff0001f;
PowerPC::ppcState.spr[SPR_DBAT3L] = 0xfff00001;
PowerPC::DBATUpdated();
PowerPC::IBATUpdated();
PC = 0x81200150;
return true;
}
@@ -377,6 +379,8 @@ bool CBoot::BootUp()
PowerPC::ppcState.spr[SPR_DBAT4L] = 0x10000002;
PowerPC::ppcState.spr[SPR_DBAT5U] = 0xd0001fff;
PowerPC::ppcState.spr[SPR_DBAT5L] = 0x1000002a;
PowerPC::DBATUpdated();
PowerPC::IBATUpdated();
dolLoader.Load();
PC = dolLoader.GetEntryPoint();
+8 -4
View File
@@ -52,6 +52,8 @@ bool CBoot::EmulatedBS2_GC(bool skipAppLoader)
PowerPC::ppcState.spr[SPR_DBAT0L] = 0x00000002;
PowerPC::ppcState.spr[SPR_DBAT1U] = 0xc0001fff;
PowerPC::ppcState.spr[SPR_DBAT1L] = 0x0000002a;
PowerPC::DBATUpdated();
PowerPC::IBATUpdated();
// Write necessary values
// Here we write values to memory that the apploader does not take care of. Game info goes
@@ -298,11 +300,11 @@ bool CBoot::SetupWiiMemory(DiscIO::Country country)
Memory::Write_U32(0x8179d500, 0x00003110); // Init
Memory::Write_U32(0x04000000, 0x00003118); // Unknown
Memory::Write_U32(0x04000000, 0x0000311c); // BAT
Memory::Write_U32(0x93400000, 0x00003120); // BAT
Memory::Write_U32(0x93600000, 0x00003120); // BAT
Memory::Write_U32(0x90000800, 0x00003124); // Init - MEM2 low
Memory::Write_U32(0x93ae0000, 0x00003128); // Init - MEM2 high
Memory::Write_U32(0x93ae0000, 0x00003130); // IOS MEM2 low
Memory::Write_U32(0x93b00000, 0x00003134); // IOS MEM2 high
Memory::Write_U32(0x935e0000, 0x00003128); // Init - MEM2 high
Memory::Write_U32(0x935e0000, 0x00003130); // IOS MEM2 low
Memory::Write_U32(0x93600000, 0x00003134); // IOS MEM2 high
Memory::Write_U32(0x00000012, 0x00003138); // Console type
// 40 is copied from 88 after running apploader
Memory::Write_U32(0x00090204, 0x00003140); // IOS revision (IOS9, v2.4)
@@ -371,6 +373,8 @@ bool CBoot::EmulatedBS2_Wii()
PowerPC::ppcState.spr[SPR_DBAT4L] = 0x10000002;
PowerPC::ppcState.spr[SPR_DBAT5U] = 0xd0001fff;
PowerPC::ppcState.spr[SPR_DBAT5L] = 0x1000002a;
PowerPC::DBATUpdated();
PowerPC::IBATUpdated();
Memory::Write_U32(0x4c000064, 0x00000300); // Write default DSI Handler: rfi
Memory::Write_U32(0x4c000064, 0x00000800); // Write default FPU Handler: rfi
@@ -410,6 +410,8 @@ void FifoPlayer::LoadMemory()
PowerPC::ppcState.spr[SPR_DBAT0L] = 0x00000002;
PowerPC::ppcState.spr[SPR_DBAT1U] = 0xc0001fff;
PowerPC::ppcState.spr[SPR_DBAT1L] = 0x0000002a;
PowerPC::DBATUpdated();
PowerPC::IBATUpdated();
SetupFifo();
+125 -32
View File
@@ -96,6 +96,30 @@ bool IsInitialized()
return m_IsInitialized;
}
namespace
{
enum
{
MV_FAKE_VMEM = 1,
MV_WII_ONLY = 2,
};
struct PhysicalMemoryRegion
{
u8** out_pointer;
u32 physical_address;
u32 size;
u32 flags;
u32 shm_position;
};
struct LogicalMemoryView
{
void* mapped_pointer;
u32 mapped_size;
};
}
// Dolphin allocates memory to represent four regions:
// - 32MB RAM (actually 24MB on hardware), available on Gamecube and Wii
// - 64MB "EXRAM", RAM only available on Wii
@@ -116,28 +140,12 @@ bool IsInitialized()
// [0x08000000, 0x0C000000) - EFB "mapping" (not handled here)
// [0x0C000000, 0x0E000000) - MMIO etc. (not handled here)
// [0x10000000, 0x14000000) - 64MB RAM (Wii-only; slightly slower)
//
// The 4GB starting at logical_base represents access from the CPU
// with address translation turned on. Instead of changing the mapping
// based on the BAT registers, we approximate the common BAT configuration
// used by games:
// [0x00000000, 0x02000000) - 32MB RAM, cached access, normally only mapped
// during startup by Wii WADs
// [0x02000000, 0x08000000) - Mirrors of 32MB RAM (not implemented here)
// [0x40000000, 0x50000000) - FakeVMEM
// [0x70000000, 0x80000000) - FakeVMEM
// [0x80000000, 0x82000000) - 32MB RAM, cached access
// [0x82000000, 0x88000000) - Mirrors of 32MB RAM (not implemented here)
// [0x90000000, 0x94000000) - 64MB RAM, Wii-only, cached access
// [0xC0000000, 0xC2000000) - 32MB RAM, uncached access
// [0xC2000000, 0xC8000000) - Mirrors of 32MB RAM (not implemented here)
// [0xC8000000, 0xCC000000) - EFB "mapping" (not handled here)
// [0xCC000000, 0xCE000000) - MMIO etc. (not handled here)
// [0xD0000000, 0xD4000000) - 64MB RAM, Wii-only, uncached access
// [0x7E000000, 0x80000000) - FakeVMEM
// [0xE0000000, 0xE0040000) - 256KB locked L1
//
// TODO: We shouldn't hardcode this mapping; we can generate it dynamically
// based on the BAT registers.
// The 4GB starting at logical_base represents access from the CPU
// with address translation turned on. This mapping is computed based
// on the BAT registers.
//
// Each of these 4GB regions is followed by 4GB of empty space so overflows
// in address computation in the JIT don't access the wrong memory.
@@ -152,18 +160,14 @@ bool IsInitialized()
//
// TODO: The actual size of RAM is REALRAM_SIZE (24MB); the other 8MB shouldn't
// be backed by actual memory.
static MemoryView views[] = {
static PhysicalMemoryRegion physical_regions[] = {
{&m_pRAM, 0x00000000, RAM_SIZE, 0},
{nullptr, 0x200000000, RAM_SIZE, MV_MIRROR_PREVIOUS},
{nullptr, 0x280000000, RAM_SIZE, MV_MIRROR_PREVIOUS},
{nullptr, 0x2C0000000, RAM_SIZE, MV_MIRROR_PREVIOUS},
{&m_pL1Cache, 0x2E0000000, L1_CACHE_SIZE, 0},
{&m_pFakeVMEM, 0x27E000000, FAKEVMEM_SIZE, MV_FAKE_VMEM},
{&m_pL1Cache, 0xE0000000, L1_CACHE_SIZE, 0},
{&m_pFakeVMEM, 0x7E000000, FAKEVMEM_SIZE, MV_FAKE_VMEM},
{&m_pEXRAM, 0x10000000, EXRAM_SIZE, MV_WII_ONLY},
{nullptr, 0x290000000, EXRAM_SIZE, MV_WII_ONLY | MV_MIRROR_PREVIOUS},
{nullptr, 0x2D0000000, EXRAM_SIZE, MV_WII_ONLY | MV_MIRROR_PREVIOUS},
};
static const int num_views = sizeof(views) / sizeof(MemoryView);
static std::vector<LogicalMemoryView> logical_mapped_entries;
void Init()
{
@@ -181,7 +185,34 @@ void Init()
flags |= MV_WII_ONLY;
if (bFakeVMEM)
flags |= MV_FAKE_VMEM;
physical_base = MemoryMap_Setup(views, num_views, flags, &g_arena);
u32 mem_size = 0;
for (PhysicalMemoryRegion& region : physical_regions)
{
if ((flags & region.flags) != region.flags)
continue;
region.shm_position = mem_size;
mem_size += region.size;
}
g_arena.GrabSHMSegment(mem_size);
physical_base = MemArena::FindMemoryBase();
for (PhysicalMemoryRegion& region : physical_regions)
{
if ((flags & region.flags) != region.flags)
continue;
u8* base = physical_base + region.physical_address;
*region.out_pointer = (u8*)g_arena.CreateView(region.shm_position, region.size, base);
if (!*region.out_pointer)
{
PanicAlert("MemoryMap_Setup: Failed finding a memory base.");
exit(0);
}
mem_size += region.size;
}
#ifndef _ARCH_32
logical_base = physical_base + 0x200000000;
#endif
@@ -197,6 +228,55 @@ void Init()
m_IsInitialized = true;
}
void UpdateLogicalMemory(u32* dbat_table)
{
for (auto& entry : logical_mapped_entries)
{
g_arena.ReleaseView(entry.mapped_pointer, entry.mapped_size);
}
logical_mapped_entries.clear();
for (unsigned i = 0; i < (1 << (32 - PowerPC::BAT_INDEX_SHIFT)); ++i)
{
if (dbat_table[i] & 1)
{
unsigned logical_address = i << PowerPC::BAT_INDEX_SHIFT;
// TODO: Merge adjacent mappings to make this faster.
unsigned logical_size = 1 << PowerPC::BAT_INDEX_SHIFT;
unsigned translated_address = dbat_table[i] & ~3;
for (PhysicalMemoryRegion& physical_region : physical_regions)
{
u32 mapping_address = physical_region.physical_address;
u32 mapping_end = mapping_address + physical_region.size;
u32 intersection_start = std::max(mapping_address, translated_address);
u32 intersection_end = std::min(mapping_end, translated_address + logical_size);
if (intersection_start < intersection_end)
{
// Found an overlapping region; map it.
// We only worry about one overlapping region; in theory, a logical
// region could translate to more than one physical region, but in
// practice, that doesn't happen.
u32 position = physical_region.shm_position;
if (intersection_start > mapping_address)
position += intersection_start - mapping_address;
u8* base = logical_base + logical_address;
if (intersection_start > translated_address)
base += intersection_start - translated_address;
u32 mapped_size = intersection_end - intersection_start;
void* mapped_pointer = g_arena.CreateView(position, mapped_size, base);
if (!mapped_pointer)
{
PanicAlert("MemoryMap_Setup: Failed finding a memory base.");
exit(0);
}
logical_mapped_entries.push_back({mapped_pointer, mapped_size});
break;
}
}
}
}
}
void DoState(PointerWrap& p)
{
bool wii = SConfig::GetInstance().bWii;
@@ -219,7 +299,18 @@ void Shutdown()
flags |= MV_WII_ONLY;
if (bFakeVMEM)
flags |= MV_FAKE_VMEM;
MemoryMap_Shutdown(views, num_views, flags, &g_arena);
for (PhysicalMemoryRegion& region : physical_regions)
{
if ((flags & region.flags) != region.flags)
continue;
g_arena.ReleaseView(*region.out_pointer, region.size);
*region.out_pointer = 0;
}
for (auto& entry : logical_mapped_entries)
{
g_arena.ReleaseView(entry.mapped_pointer, entry.mapped_size);
}
logical_mapped_entries.clear();
g_arena.ReleaseSHMSegment();
physical_base = nullptr;
logical_base = nullptr;
@@ -233,7 +324,9 @@ void Clear()
memset(m_pRAM, 0, RAM_SIZE);
if (m_pL1Cache)
memset(m_pL1Cache, 0, L1_CACHE_SIZE);
if (SConfig::GetInstance().bWii && m_pEXRAM)
if (m_pFakeVMEM)
memset(m_pFakeVMEM, 0, FAKEVMEM_SIZE);
if (m_pEXRAM)
memset(m_pEXRAM, 0, EXRAM_SIZE);
}
+2 -7
View File
@@ -57,13 +57,6 @@ enum
IO_SIZE = 0x00010000,
EXRAM_SIZE = 0x04000000,
EXRAM_MASK = EXRAM_SIZE - 1,
ADDR_MASK_HW_ACCESS = 0x0c000000,
ADDR_MASK_MEM1 = 0x20000000,
#if _ARCH_32
MEMVIEW32_MASK = 0x3FFFFFFF,
#endif
};
// MMIO mapping object.
@@ -75,6 +68,8 @@ void Init();
void Shutdown();
void DoState(PointerWrap& p);
void UpdateLogicalMemory(u32* dbat_table);
void Clear();
bool AreMemoryBreakpointsActivated();
@@ -962,6 +962,11 @@ IPCCommandResult CWII_IPC_HLE_Device_es::IOCtlV(u32 _CommandAddress)
if (pDolLoader->IsValid())
{
pDolLoader->Load(); // TODO: Check why sysmenu does not load the DOL correctly
// WADs start with address translation off at the given entry point.
//
// The state of other CPU registers (like the BAT registers) doesn't matter much
// because the WAD initializes everything itself anyway.
MSR = 0;
PC = pDolLoader->GetEntryPoint();
bSuccess = true;
}
@@ -297,6 +297,15 @@ void Interpreter::mtspr(UGeckoInstruction _inst)
// TODO: emulate locked cache and DMA bits.
break;
case SPR_HID4:
if (oldValue != rSPR(iIndex))
{
WARN_LOG(POWERPC, "HID4 updated %x %x", oldValue, rSPR(iIndex));
PowerPC::IBATUpdated();
PowerPC::DBATUpdated();
}
break;
case SPR_WPAR:
_assert_msg_(POWERPC, rGPR[_inst.RD] == 0x0C008000, "Gather pipe @ %08x", PC);
GPFifo::ResetGatherPipe();
@@ -354,6 +363,52 @@ void Interpreter::mtspr(UGeckoInstruction _inst)
case SPR_XER:
SetXER(rSPR(iIndex));
break;
case SPR_DBAT0L:
case SPR_DBAT0U:
case SPR_DBAT1L:
case SPR_DBAT1U:
case SPR_DBAT2L:
case SPR_DBAT2U:
case SPR_DBAT3L:
case SPR_DBAT3U:
case SPR_DBAT4L:
case SPR_DBAT4U:
case SPR_DBAT5L:
case SPR_DBAT5U:
case SPR_DBAT6L:
case SPR_DBAT6U:
case SPR_DBAT7L:
case SPR_DBAT7U:
if (oldValue != rSPR(iIndex))
{
WARN_LOG(POWERPC, "DBAT updated %d %x %x", iIndex, oldValue, rSPR(iIndex));
PowerPC::DBATUpdated();
}
break;
case SPR_IBAT0L:
case SPR_IBAT0U:
case SPR_IBAT1L:
case SPR_IBAT1U:
case SPR_IBAT2L:
case SPR_IBAT2U:
case SPR_IBAT3L:
case SPR_IBAT3U:
case SPR_IBAT4L:
case SPR_IBAT4U:
case SPR_IBAT5L:
case SPR_IBAT5U:
case SPR_IBAT6L:
case SPR_IBAT6U:
case SPR_IBAT7L:
case SPR_IBAT7U:
if (oldValue != rSPR(iIndex))
{
WARN_LOG(POWERPC, "IBAT updated %d %x %x", iIndex, oldValue, rSPR(iIndex));
PowerPC::IBATUpdated();
}
break;
}
}
@@ -365,38 +365,46 @@ void Jit64::dcbz(UGeckoInstruction inst)
int a = inst.RA;
int b = inst.RB;
u32 mem_mask = Memory::ADDR_MASK_HW_ACCESS;
// The following masks the region used by the GC/Wii virtual memory lib
mem_mask |= Memory::ADDR_MASK_MEM1;
MOV(32, R(RSCRATCH), gpr.R(b));
if (a)
ADD(32, R(RSCRATCH), gpr.R(a));
AND(32, R(RSCRATCH), Imm32(~31));
TEST(32, R(RSCRATCH), Imm32(mem_mask));
FixupBranch slow = J_CC(CC_NZ, true);
// Should this code ever run? I can't find any games that use DCBZ on non-physical addresses, but
// supposedly there are, at least for some MMU titles. Let's be careful and support it to be sure.
if (!UReg_MSR(MSR).DR)
{
// If address translation is turned off, just call the general-case code.
AND(32, R(RSCRATCH), Imm32(~31));
MOV(32, M(&PC), Imm32(jit->js.compilerPC));
BitSet32 registersInUse = CallerSavedRegistersInUse();
ABI_PushRegistersAndAdjustStack(registersInUse, 0);
ABI_CallFunctionR((void*)&PowerPC::ClearCacheLine, RSCRATCH);
ABI_PopRegistersAndAdjustStack(registersInUse, 0);
return;
}
// Perform lookup to see if we can use fast path.
MOV(32, R(RSCRATCH2), R(RSCRATCH));
SHR(32, R(RSCRATCH), Imm8(PowerPC::BAT_INDEX_SHIFT));
TEST(32, MScaled(RSCRATCH, SCALE_4, (u32)(u64)PowerPC::dbat_table), Imm32(2));
FixupBranch slow = J_CC(CC_Z, true);
// Fast path: compute full address, then zero out 32 bytes of memory.
AND(32, R(RSCRATCH2), Imm8(~31));
PXOR(XMM0, R(XMM0));
MOVAPS(MComplex(RMEM, RSCRATCH2, SCALE_1, 0), XMM0);
MOVAPS(MComplex(RMEM, RSCRATCH2, SCALE_1, 16), XMM0);
// Slow path: mask the address, then call the general-case code.
SwitchToFarCode();
SetJumpTarget(slow);
AND(32, R(RSCRATCH2), Imm32(~31));
MOV(32, M(&PC), Imm32(jit->js.compilerPC));
BitSet32 registersInUse = CallerSavedRegistersInUse();
ABI_PushRegistersAndAdjustStack(registersInUse, 0);
ABI_CallFunctionR(PowerPC::ClearCacheLine, RSCRATCH);
ABI_CallFunctionR(&PowerPC::ClearCacheLine, RSCRATCH2);
ABI_PopRegistersAndAdjustStack(registersInUse, 0);
FixupBranch exit = J(true);
FixupBranch end = J(true);
SwitchToNearCode();
// Mask out the address so we don't write to MEM1 out of bounds
// FIXME: Work out why the AGP disc writes out of bounds
if (!SConfig::GetInstance().bWii)
AND(32, R(RSCRATCH), Imm32(Memory::RAM_MASK));
PXOR(XMM0, R(XMM0));
MOVAPS(MComplex(RMEM, RSCRATCH, SCALE_1, 0), XMM0);
MOVAPS(MComplex(RMEM, RSCRATCH, SCALE_1, 16), XMM0);
SetJumpTarget(exit);
SetJumpTarget(end);
}
void Jit64::stX(UGeckoInstruction inst)
@@ -25,6 +25,9 @@ void Jit64::psq_stXX(UGeckoInstruction inst)
INSTRUCTION_START
JITDISABLE(bJITLoadStorePairedOff);
// For performance, the AsmCommon routines assume address translation is on.
FALLBACK_IF(!UReg_MSR(MSR).DR);
s32 offset = inst.SIMM_12;
bool indexed = inst.OPCD == 4;
bool update = (inst.OPCD == 61 && offset) || (inst.OPCD == 4 && !!(inst.SUBOP6 & 32));
@@ -112,6 +115,9 @@ void Jit64::psq_lXX(UGeckoInstruction inst)
INSTRUCTION_START
JITDISABLE(bJITLoadStorePairedOff);
// For performance, the AsmCommon routines assume address translation is on.
FALLBACK_IF(!UReg_MSR(MSR).DR);
s32 offset = inst.SIMM_12;
bool indexed = inst.OPCD == 4;
bool update = (inst.OPCD == 57 && offset) || (inst.OPCD == 4 && !!(inst.SUBOP6 & 32));
@@ -401,7 +401,8 @@ void QuantizedMemoryRoutines::GenQuantizedStore(bool single, EQuantizeType type,
}
}
int flags = isInline ? 0 : SAFE_LOADSTORE_NO_FASTMEM | SAFE_LOADSTORE_NO_PROLOG;
int flags =
isInline ? 0 : SAFE_LOADSTORE_NO_FASTMEM | SAFE_LOADSTORE_NO_PROLOG | SAFE_LOADSTORE_DR_ON;
if (!single)
flags |= SAFE_LOADSTORE_NO_SWAP;
@@ -459,7 +460,8 @@ void QuantizedMemoryRoutines::GenQuantizedLoad(bool single, EQuantizeType type,
if (jit->jo.memcheck)
{
BitSet32 regsToSave = QUANTIZED_REGS_TO_SAVE_LOAD;
int flags = isInline ? 0 : SAFE_LOADSTORE_NO_FASTMEM | SAFE_LOADSTORE_NO_PROLOG;
int flags =
isInline ? 0 : SAFE_LOADSTORE_NO_FASTMEM | SAFE_LOADSTORE_NO_PROLOG | SAFE_LOADSTORE_DR_ON;
SafeLoadToReg(RSCRATCH_EXTRA, R(RSCRATCH_EXTRA), size, 0, regsToSave, extend, flags);
if (!single && (type == QUANTIZE_U8 || type == QUANTIZE_S8))
{
@@ -582,7 +584,8 @@ void QuantizedMemoryRoutines::GenQuantizedLoadFloat(bool single, bool isInline)
if (jit->jo.memcheck)
{
BitSet32 regsToSave = QUANTIZED_REGS_TO_SAVE;
int flags = isInline ? 0 : SAFE_LOADSTORE_NO_FASTMEM | SAFE_LOADSTORE_NO_PROLOG;
int flags =
isInline ? 0 : SAFE_LOADSTORE_NO_FASTMEM | SAFE_LOADSTORE_NO_PROLOG | SAFE_LOADSTORE_DR_ON;
SafeLoadToReg(RSCRATCH_EXTRA, R(RSCRATCH_EXTRA), size, 0, regsToSave, extend, flags);
}
@@ -23,6 +23,9 @@ void JitArm64::psq_l(UGeckoInstruction inst)
JITDISABLE(bJITLoadStorePairedOff);
FALLBACK_IF(jo.memcheck || !jo.fastmem);
// The asm routines assume address translation is on.
FALLBACK_IF(!UReg_MSR(MSR).DR);
// X30 is LR
// X0 contains the scale
// X1 is the address
@@ -103,6 +106,9 @@ void JitArm64::psq_st(UGeckoInstruction inst)
JITDISABLE(bJITLoadStorePairedOff);
FALLBACK_IF(jo.memcheck || !jo.fastmem);
// The asm routines assume address translation is on.
FALLBACK_IF(!UReg_MSR(MSR).DR);
// X30 is LR
// X0 contains the scale
// X1 is the address
@@ -179,6 +185,7 @@ void JitArm64::psq_st(UGeckoInstruction inst)
UBFM(scale_reg, scale_reg, 8, 13); // Scale
// Inline address check
// FIXME: This doesn't correctly account for the BAT configuration.
TST(addr_reg, 6, 1);
FixupBranch pass = B(CC_EQ);
FixupBranch fail = B();
@@ -47,6 +47,16 @@ void JitArm64::GenerateAsm()
dispatcherNoCheck = GetCodePtr();
// Make sure MEM_REG is pointing at an appropriate register, based on MSR.DR.
LDR(INDEX_UNSIGNED, W0, PPC_REG, PPCSTATE_OFF(msr));
TST(W0, 28, 1);
FixupBranch physmem = B(CC_NEQ);
MOVI2R(MEM_REG, (u64)Memory::physical_base);
FixupBranch membaseend = B();
SetJumpTarget(physmem);
MOVI2R(MEM_REG, (u64)Memory::logical_base);
SetJumpTarget(membaseend);
bool assembly_dispatcher = true;
if (assembly_dispatcher)
+24 -40
View File
@@ -212,7 +212,7 @@ void EmuCodeBlock::MMIOLoadToReg(MMIO::Mapping* mmio, Gen::X64Reg reg_value,
}
FixupBranch EmuCodeBlock::CheckIfSafeAddress(const OpArg& reg_value, X64Reg reg_addr,
BitSet32 registers_in_use, u32 mem_mask)
BitSet32 registers_in_use)
{
registers_in_use[reg_addr] = true;
if (reg_value.IsSimpleReg())
@@ -227,29 +227,19 @@ FixupBranch EmuCodeBlock::CheckIfSafeAddress(const OpArg& reg_value, X64Reg reg_
else
scratch = reg_addr;
// On Gamecube games with MMU, do a little bit of extra work to make sure we're not accessing the
// 0x81800000 to 0x83FFFFFF range.
// It's okay to take a shortcut and not check this range on non-MMU games, since we're already
// assuming they'll never do an invalid memory access.
// The slightly more complex check needed for Wii games using the space just above MEM1 isn't
// implemented here yet, since there are no known working Wii MMU games to test it with.
if (jit->jo.memcheck && !SConfig::GetInstance().bWii)
{
if (scratch == reg_addr)
PUSH(scratch);
else
MOV(32, R(scratch), R(reg_addr));
AND(32, R(scratch), Imm32(0x3FFFFFFF));
CMP(32, R(scratch), Imm32(0x01800000));
if (scratch == reg_addr)
POP(scratch);
return J_CC(CC_AE, farcode.Enabled());
}
if (scratch == reg_addr)
PUSH(scratch);
else
{
TEST(32, R(reg_addr), Imm32(mem_mask));
return J_CC(CC_NZ, farcode.Enabled());
}
MOV(32, R(scratch), R(reg_addr));
// Perform lookup to see if we can use fast path.
SHR(32, R(scratch), Imm8(PowerPC::BAT_INDEX_SHIFT));
TEST(32, MScaled(scratch, SCALE_4, (u32)(u64)PowerPC::dbat_table), Imm32(2));
if (scratch == reg_addr)
POP(scratch);
return J_CC(CC_Z, farcode.Enabled());
}
void EmuCodeBlock::SafeLoadToReg(X64Reg reg_value, const Gen::OpArg& opAddress, int accessSize,
@@ -305,14 +295,11 @@ void EmuCodeBlock::SafeLoadToReg(X64Reg reg_value, const Gen::OpArg& opAddress,
}
FixupBranch exit;
if (!slowmem)
bool dr_set = (flags & SAFE_LOADSTORE_DR_ON) || UReg_MSR(MSR).DR;
bool fast_check_address = !slowmem && dr_set;
if (fast_check_address)
{
u32 mem_mask = Memory::ADDR_MASK_HW_ACCESS;
// The following masks the region used by the GC/Wii virtual memory lib
mem_mask |= Memory::ADDR_MASK_MEM1;
FixupBranch slow = CheckIfSafeAddress(R(reg_value), reg_addr, registersInUse, mem_mask);
FixupBranch slow = CheckIfSafeAddress(R(reg_value), reg_addr, registersInUse);
UnsafeLoadToReg(reg_value, R(reg_addr), accessSize, 0, signExtend);
if (farcode.Enabled())
SwitchToFarCode();
@@ -350,7 +337,7 @@ void EmuCodeBlock::SafeLoadToReg(X64Reg reg_value, const Gen::OpArg& opAddress,
MOVZX(64, accessSize, reg_value, R(ABI_RETURN));
}
if (!slowmem)
if (fast_check_address)
{
if (farcode.Enabled())
{
@@ -575,15 +562,12 @@ void EmuCodeBlock::SafeWriteRegToReg(OpArg reg_value, X64Reg reg_addr, int acces
}
}
FixupBranch slow, exit;
if (!slowmem)
FixupBranch exit;
bool dr_set = (flags & SAFE_LOADSTORE_DR_ON) || UReg_MSR(MSR).DR;
bool fast_check_address = !slowmem && dr_set;
if (fast_check_address)
{
u32 mem_mask = Memory::ADDR_MASK_HW_ACCESS;
// The following masks the region used by the GC/Wii virtual memory lib
mem_mask |= Memory::ADDR_MASK_MEM1;
slow = CheckIfSafeAddress(reg_value, reg_addr, registersInUse, mem_mask);
FixupBranch slow = CheckIfSafeAddress(reg_value, reg_addr, registersInUse);
UnsafeWriteRegToReg(reg_value, reg_addr, accessSize, 0, swap);
if (farcode.Enabled())
SwitchToFarCode();
@@ -629,7 +613,7 @@ void EmuCodeBlock::SafeWriteRegToReg(OpArg reg_value, X64Reg reg_addr, int acces
MemoryExceptionCheck();
if (!slowmem)
if (fast_check_address)
{
if (farcode.Enabled())
{
@@ -123,7 +123,7 @@ public:
}
Gen::FixupBranch CheckIfSafeAddress(const Gen::OpArg& reg_value, Gen::X64Reg reg_addr,
BitSet32 registers_in_use, u32 mem_mask);
BitSet32 registers_in_use);
void UnsafeLoadRegToReg(Gen::X64Reg reg_addr, Gen::X64Reg reg_value, int accessSize,
s32 offset = 0, bool signExtend = false);
void UnsafeLoadRegToRegNoSwap(Gen::X64Reg reg_addr, Gen::X64Reg reg_value, int accessSize,
@@ -154,6 +154,7 @@ public:
SAFE_LOADSTORE_CLOBBER_RSCRATCH_INSTEAD_OF_ADDR = 8,
// Force slowmem (used when generating fallbacks in trampolines)
SAFE_LOADSTORE_FORCE_SLOWMEM = 16,
SAFE_LOADSTORE_DR_ON = 32,
};
void SafeLoadToReg(Gen::X64Reg reg_value, const Gen::OpArg& opAddress, int accessSize, s32 offset,
@@ -12,6 +12,9 @@ void JitILBase::psq_st(UGeckoInstruction inst)
JITDISABLE(bJITLoadStorePairedOff);
FALLBACK_IF(jo.memcheck || inst.W);
// For performance, the AsmCommon routines assume address translation is on.
FALLBACK_IF(!UReg_MSR(MSR).DR);
IREmitter::InstLoc addr = ibuild.EmitIntConst(inst.SIMM_12);
IREmitter::InstLoc val;
@@ -32,6 +35,9 @@ void JitILBase::psq_l(UGeckoInstruction inst)
JITDISABLE(bJITLoadStorePairedOff);
FALLBACK_IF(jo.memcheck || inst.W);
// For performance, the AsmCommon routines assume address translation is on.
FALLBACK_IF(!UReg_MSR(MSR).DR);
IREmitter::InstLoc addr = ibuild.EmitIntConst(inst.SIMM_12);
IREmitter::InstLoc val;
File diff suppressed because it is too large Load Diff
-3
View File
@@ -84,9 +84,6 @@ void PPCSymbolDB::AddKnownSymbol(u32 startAddr, u32 size, const std::string& nam
Symbol* PPCSymbolDB::GetSymbolFromAddr(u32 addr)
{
if (!PowerPC::HostIsRAMAddress(addr))
return nullptr;
XFuncMap::iterator it = functions.find(addr);
if (it != functions.end())
{
+8
View File
@@ -265,6 +265,8 @@ void ClearCacheLine(const u32 address); // Zeroes 32 bytes; address should be 3
// TLB functions
void SDRUpdated();
void InvalidateTLBEntry(u32 address);
void DBATUpdated();
void IBATUpdated();
// Result changes based on the BAT registers and MSR.DR. Returns whether
// it's safe to optimize a read or write to this address to an unguarded
@@ -280,6 +282,12 @@ struct TranslateResult
u32 address;
};
TranslateResult JitCache_TranslateAddress(u32 address);
enum
{
BAT_INDEX_SHIFT = 17
};
extern u32 ibat_table[];
extern u32 dbat_table[];
} // namespace
enum CRBits