mirror of
https://github.com/hrydgard/ppsspp.git
synced 2026-10-22 10:49:05 +02:00
ME: Let kernel code on the main CPU reach RAM and VRAM through kseg1
Yabause-PSP's me.prx ORs 0xA0000000 onto its own addresses to share a buffer with the ME uncached, which is 0xA8xxxxxx for a module in kernel RAM. The main CPU's space has no views there, so it crashed. All of kernel segment 1 (0xA0000000-0xBFFFFFFF) now counts as MMIO, and a device below the hardware registers maps RAM and VRAM onto their uncached mirrors at 0x40000000. The main CPU's native kernel loads and stores send the whole segment to that path; the ME keeps 0xBC000000 as the boundary, since its space maps the rest. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
1 parent
e906b9f700
commit
657e99fcbf
4 files changed
+54
-7
No files matched your search
@@ -91,6 +91,43 @@ public:
|
||||
|
||||
static BootPageDevice g_bootPage;
|
||||
|
||||
// Kernel code reaches RAM and VRAM uncached at 0xA8000000 and 0xA4000000 (Yabause's me.prx does, by
|
||||
// ORing 0xA0000000 onto its own addresses). The main CPU's space has no views there, so it goes
|
||||
// through the uncached mirrors at 0x40000000 instead. The Media Engine's space maps them.
|
||||
class KernelUncachedDevice : public MMIODevice {
|
||||
public:
|
||||
KernelUncachedDevice() : MMIODevice("kseg1 memory", 0xA0000000, 0x1C000000) {}
|
||||
|
||||
bool Read(MIPSState *mips, u32 offset, int size, u32 *value) override {
|
||||
const u32 address = 0x40000000 + offset;
|
||||
if ((mips && mips->IsMediaEngine()) || !Memory::IsValidRange(address, size))
|
||||
return false;
|
||||
switch (size) {
|
||||
case 1: *value = Memory::ReadUnchecked_U8(address); break;
|
||||
case 2: *value = Memory::ReadUnchecked_U16(address); break;
|
||||
default: *value = Memory::ReadUnchecked_U32(address); break;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Write(MIPSState *mips, u32 offset, int size, u32 value) override {
|
||||
const u32 address = 0x40000000 + offset;
|
||||
if ((mips && mips->IsMediaEngine()) || !Memory::IsValidRange(address, size))
|
||||
return false;
|
||||
switch (size) {
|
||||
case 1: Memory::WriteUnchecked_U8((u8)value, address); break;
|
||||
case 2: Memory::WriteUnchecked_U16((u16)value, address); break;
|
||||
default: Memory::WriteUnchecked_U32(value, address); break;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void Reset() override {}
|
||||
void DoState(PointerWrap &p) override {}
|
||||
};
|
||||
|
||||
static KernelUncachedDevice g_kernelUncached;
|
||||
|
||||
static std::vector<MMIODevice *> g_devices;
|
||||
|
||||
void Init() {
|
||||
@@ -106,6 +143,7 @@ void Init() {
|
||||
MeMMIO::ClockDevice(),
|
||||
MeMMIO::DdrDevice(),
|
||||
MeMMIO::AudioDevice(),
|
||||
&g_kernelUncached,
|
||||
};
|
||||
for (MMIODevice *device : g_devices) {
|
||||
device->Reset();
|
||||
|
||||
@@ -411,7 +411,8 @@ void Arm64JitBackend::CompIR_LoadShift(IRInst inst) {
|
||||
}
|
||||
|
||||
// Memory directly, or through the fixed code for a hardware register (0xBC000000-0xBFFFFFFF), where
|
||||
// the address may also be one the CPU has no memory at.
|
||||
// the address may also be one the CPU has no memory at. The main CPU has no views anywhere in kernel
|
||||
// segment 1 (0xA0000000-0xBFFFFFFF), so all of it goes there (see Memory::IsMMIOAccess).
|
||||
void Arm64JitBackend::CompIR_KernelLoadStore(IRInst inst) {
|
||||
CONDITIONAL_DISABLE;
|
||||
|
||||
@@ -420,8 +421,13 @@ void Arm64JitBackend::CompIR_KernelLoadStore(IRInst inst) {
|
||||
ADDI2R(SCRATCH1, regs_.R(inst.src1), inst.constant, SCRATCH2);
|
||||
const ARM64Reg reg = isStore ? regs_.R(inst.src3) : regs_.R(inst.dest);
|
||||
|
||||
LSR(SCRATCH2, SCRATCH1, 26);
|
||||
CMPI2R(SCRATCH2, 0xBC000000 >> 26);
|
||||
if (mips_->IsMediaEngine()) {
|
||||
LSR(SCRATCH2, SCRATCH1, 26);
|
||||
CMPI2R(SCRATCH2, 0xBC000000 >> 26);
|
||||
} else {
|
||||
LSR(SCRATCH2, SCRATCH1, 29);
|
||||
CMPI2R(SCRATCH2, 0xA0000000 >> 29);
|
||||
}
|
||||
FixupBranch memory = B(CC_NEQ);
|
||||
if (isStore)
|
||||
MOV(SCRATCH2, reg);
|
||||
|
||||
@@ -89,7 +89,8 @@ Gen::OpArg X64JitBackend::PrepareSrc1Address(IRInst inst) {
|
||||
|
||||
#if PPSSPP_ARCH(AMD64)
|
||||
// Memory directly, or through the fixed code for a hardware register (0xBC000000-0xBFFFFFFF), where
|
||||
// the address may also be one the CPU has no memory at.
|
||||
// the address may also be one the CPU has no memory at. The main CPU has no views anywhere in kernel
|
||||
// segment 1 (0xA0000000-0xBFFFFFFF), so all of it goes there (see Memory::IsMMIOAccess).
|
||||
void X64JitBackend::CompIR_KernelLoadStore(IRInst inst) {
|
||||
CONDITIONAL_DISABLE;
|
||||
|
||||
@@ -98,7 +99,7 @@ void X64JitBackend::CompIR_KernelLoadStore(IRInst inst) {
|
||||
LEA(32, SCRATCH1, MDisp(regs_.RX(inst.src1), inst.constant));
|
||||
const X64Reg reg = isStore ? regs_.RX(inst.src3) : regs_.RX(inst.dest);
|
||||
|
||||
CMP(32, R(SCRATCH1), Imm32(0xBC000000));
|
||||
CMP(32, R(SCRATCH1), Imm32(mips_->IsMediaEngine() ? 0xBC000000 : 0xA0000000));
|
||||
FixupBranch below = J_CC(CC_B);
|
||||
CMP(32, R(SCRATCH1), Imm32(0xC0000000));
|
||||
FixupBranch above = J_CC(CC_AE);
|
||||
|
||||
+4
-2
@@ -409,9 +409,11 @@ inline bool IsDepthTexVRAMAddress(const u32 address) {
|
||||
return ((address & 0xBFE00000) == 0x04200000) || ((address & 0xBFE00000) == 0x04600000);
|
||||
}
|
||||
|
||||
// TODO: To re-evaluate.
|
||||
// Kernel segment 1 (0xA0000000-0xBFFFFFFF): the hardware registers from 0xBC000000, and below them
|
||||
// uncached RAM and VRAM, which only the Media Engine's space maps. Kernel code reaches the rest
|
||||
// through MMIO::Read/Write.
|
||||
inline bool IsMMIOAccess(const u32 address) {
|
||||
return ((address & 0xFC000000) == 0xBC000000);
|
||||
return (address & 0xE0000000) == 0xA0000000;
|
||||
}
|
||||
|
||||
// 0x08000000 -> 0x08800000
|
||||
|
||||
Reference in new issue
Block a user