@@ -1137,68 +1137,6 @@ TranslateResult JitCache_TranslateAddress(u32 address)
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/

#define PPC_EXC_DSISR_PAGE (1 << 30)
#define PPC_EXC_DSISR_PROT (1 << 27)
#define PPC_EXC_DSISR_STORE (1 << 25)

#define SDR1_HTABORG(v) (((v) >> 16) & 0xffff)
#define SDR1_HTABMASK(v) ((v)&0x1ff)
#define SDR1_PAGETABLE_BASE(v) ((v)&0xffff)
#define SR_T (1 << 31)
#define SR_Ks (1 << 30)
#define SR_Kp (1 << 29)
#define SR_N (1 << 28)
#define SR_VSID(v) ((v)&0xffffff)
#define SR_BUID(v) (((v) >> 20) & 0x1ff)
#define SR_CNTRL_SPEC(v) ((v)&0xfffff)

#define EA_SR(v) (((v) >> 28) & 0xf)
#define EA_PageIndex(v) (((v) >> 12) & 0xffff)
#define EA_Offset(v) ((v)&0xfff)
#define EA_API(v) (((v) >> 22) & 0x3f)

#define PA_RPN(v) (((v) >> 12) & 0xfffff)
#define PA_Offset(v) ((v)&0xfff)

#define PTE1_V (1 << 31)
#define PTE1_VSID(v) (((v) >> 7) & 0xffffff)
#define PTE1_H (1 << 6)
#define PTE1_API(v) ((v)&0x3f)

#define PTE2_RPN(v) ((v)&0xfffff000)
#define PTE2_R (1 << 8)
#define PTE2_C (1 << 7)
#define PTE2_WIMG(v) (((v) >> 3) & 0xf)
#define PTE2_PP(v) ((v)&3)

// Hey! these duplicate a structure in Gekko.h
union UPTE1
{
struct
{
u32 API : 6;
u32 H : 1;
u32 VSID : 24;
u32 V : 1;
};
u32 Hex;
};

union UPTE2
{
struct
{
u32 PP : 2;
u32 : 1;
u32 WIMG : 4;
u32 C : 1;
u32 R : 1;
u32 : 3;
u32 RPN : 20;
};
u32 Hex;
};

static void GenerateDSIException(u32 effective_address, bool write)
{
// DSI exceptions are only supported in MMU mode.
@@ -1209,14 +1147,17 @@ static void GenerateDSIException(u32 effective_address, bool write)
return;
}

if (effective_address)
PowerPC::ppcState.spr[SPR_DSISR] = PPC_EXC_DSISR_PAGE | PPC_EXC_DSISR_STORE;
constexpr u32 dsisr_page = 1U << 30;
constexpr u32 dsisr_store = 1U << 25;

if (effective_address != 0)
ppcState.spr[SPR_DSISR] = dsisr_page | dsisr_store;
else
PowerPC::ppcState.spr[SPR_DSISR] = PPC_EXC_DSISR_PAGE;
ppcState.spr[SPR_DSISR] = dsisr_page;

PowerPC::ppcState.spr[SPR_DAR] = effective_address;
ppcState.spr[SPR_DAR] = effective_address;

PowerPC::ppcState.Exceptions |= EXCEPTION_DSI;
ppcState.Exceptions |= EXCEPTION_DSI;
}

static void GenerateISIException(u32 effective_address)
@@ -1230,20 +1171,22 @@ static void GenerateISIException(u32 effective_address)

void SDRUpdated()
{
u32 htabmask = SDR1_HTABMASK(PowerPC::ppcState.spr[SPR_SDR]);
const auto sdr = UReg_SDR1{ppcState.spr[SPR_SDR]};
const u32 htabmask = sdr.htabmask;

if (!Common::IsValidLowMask(htabmask))
WARN_LOG_FMT(POWERPC, "Invalid HTABMASK: 0b{:032b}", htabmask);

// While 6xx_pem.pdf §7.6.1.1 mentions that the number of trailing zeros in HTABORG
// must be equal to the number of trailing ones in the mask (i.e. HTABORG must be
// properly aligned), this is actually not a hard requirement. Real hardware will just OR
// the base address anyway. Ignoring SDR changes would lead to incorrect emulation.
u32 htaborg = SDR1_HTABORG(PowerPC::ppcState.spr[SPR_SDR]);
if (htaborg & htabmask)
const u32 htaborg = sdr.htaborg;
if ((htaborg & htabmask) != 0)
WARN_LOG_FMT(POWERPC, "Invalid HTABORG: htaborg=0x{:08x} htabmask=0x{:08x}", htaborg, htabmask);

PowerPC::ppcState.pagetable_base = htaborg << 16;
PowerPC::ppcState.pagetable_hashmask = ((htabmask << 10) | 0x3ff);
ppcState.pagetable_base = htaborg << 16;
ppcState.pagetable_hashmask = ((htabmask << 10) | 0x3ff);
}

enum class TLBLookupResult
@@ -1261,16 +1204,15 @@ static TLBLookupResult LookupTLBPageAddress(const XCheckTLBFlag flag, const u32

if (tlbe.tag[0] == tag)
{
UPTE2 PTE2;
PTE2.Hex = tlbe.pte[0];
UPTE_Hi pte2(tlbe.pte[0]);

// Check if C bit requires updating
if (flag == XCheckTLBFlag::Write)
{
if (PTE2.C == 0)
if (pte2.C == 0)
{
PTE2.C = 1;
tlbe.pte[0] = PTE2.Hex;
pte2.C = 1;
tlbe.pte[0] = pte2.Hex;
return TLBLookupResult::UpdateC;
}
}
@@ -1279,22 +1221,21 @@ static TLBLookupResult LookupTLBPageAddress(const XCheckTLBFlag flag, const u32
tlbe.recent = 0;

*paddr = tlbe.paddr[0] | (vpa & 0xfff);
*wi = (PTE2.WIMG & 0b1100) != 0;
*wi = (pte2.WIMG & 0b1100) != 0;

return TLBLookupResult::Found;
}
if (tlbe.tag[1] == tag)
{
UPTE2 PTE2;
PTE2.Hex = tlbe.pte[1];
UPTE_Hi pte2(tlbe.pte[1]);

// Check if C bit requires updating
if (flag == XCheckTLBFlag::Write)
{
if (PTE2.C == 0)
if (pte2.C == 0)
{
PTE2.C = 1;
tlbe.pte[1] = PTE2.Hex;
pte2.C = 1;
tlbe.pte[1] = pte2.Hex;
return TLBLookupResult::UpdateC;
}
}
@@ -1303,83 +1244,96 @@ static TLBLookupResult LookupTLBPageAddress(const XCheckTLBFlag flag, const u32
tlbe.recent = 1;

*paddr = tlbe.paddr[1] | (vpa & 0xfff);
*wi = (PTE2.WIMG & 0b1100) != 0;
*wi = (pte2.WIMG & 0b1100) != 0;

return TLBLookupResult::Found;
}
return TLBLookupResult::NotFound;
}

static void UpdateTLBEntry(const XCheckTLBFlag flag, UPTE2 PTE2, const u32 address)
static void UpdateTLBEntry(const XCheckTLBFlag flag, UPTE_Hi pte2, const u32 address)
{
if (IsNoExceptionFlag(flag))
return;

const int tag = address >> HW_PAGE_INDEX_SHIFT;
const u32 tag = address >> HW_PAGE_INDEX_SHIFT;
TLBEntry& tlbe = ppcState.tlb[IsOpcodeFlag(flag)][tag & HW_PAGE_INDEX_MASK];
const int index = tlbe.recent == 0 && tlbe.tag[0] != TLBEntry::INVALID_TAG;
const u32 index = tlbe.recent == 0 && tlbe.tag[0] != TLBEntry::INVALID_TAG;
tlbe.recent = index;
tlbe.paddr[index] = PTE2.RPN << HW_PAGE_INDEX_SHIFT;
tlbe.pte[index] = PTE2.Hex;
tlbe.paddr[index] = pte2.RPN << HW_PAGE_INDEX_SHIFT;
tlbe.pte[index] = pte2.Hex;
tlbe.tag[index] = tag;
}

void InvalidateTLBEntry(u32 address)
{
const u32 entry_index = (address >> HW_PAGE_INDEX_SHIFT) & HW_PAGE_INDEX_MASK;

TLBEntry& tlbe = ppcState.tlb[0][entry_index];
tlbe.tag[0] = TLBEntry::INVALID_TAG;
tlbe.tag[1] = TLBEntry::INVALID_TAG;

TLBEntry& tlbe_i = ppcState.tlb[1][entry_index];
tlbe_i.tag[0] = TLBEntry::INVALID_TAG;
tlbe_i.tag[1] = TLBEntry::INVALID_TAG;
ppcState.tlb[0][entry_index].Invalidate();
ppcState.tlb[1][entry_index].Invalidate();
}

union EffectiveAddress
{
BitField<0, 12, u32> offset;
BitField<12, 16, u32> page_index;
BitField<22, 6, u32> API;
BitField<28, 4, u32> SR;

u32 Hex = 0;

EffectiveAddress() = default;
explicit EffectiveAddress(u32 address) : Hex{address} {}
};

// Page Address Translation
static TranslateAddressResult TranslatePageAddress(const u32 address, const XCheckTLBFlag flag,
bool* wi)
static TranslateAddressResult TranslatePageAddress(const EffectiveAddress address,
const XCheckTLBFlag flag, bool* wi)
{
// TLB cache
// This catches 99%+ of lookups in practice, so the actual page table entry code below doesn't
// benefit much from optimization.
u32 translatedAddress = 0;
TLBLookupResult res = LookupTLBPageAddress(flag, address, &translatedAddress, wi);
u32 translated_address = 0;
const TLBLookupResult res = LookupTLBPageAddress(flag, address.Hex, &translated_address, wi);
if (res == TLBLookupResult::Found)
{
return TranslateAddressResult{TranslateAddressResultEnum::PAGE_TABLE_TRANSLATED,
translatedAddress};
translated_address};
}

u32 sr = PowerPC::ppcState.sr[EA_SR(address)];
const auto sr = UReg_SR{ppcState.sr[address.SR]};

if (sr & 0x80000000)
if (sr.T != 0)
return TranslateAddressResult{TranslateAddressResultEnum::DIRECT_STORE_SEGMENT, 0};

// TODO: Handle KS/KP segment register flags.

// No-execute segment register flag.
if ((flag == XCheckTLBFlag::Opcode || flag == XCheckTLBFlag::OpcodeNoException) &&
(sr & 0x10000000))
if ((flag == XCheckTLBFlag::Opcode || flag == XCheckTLBFlag::OpcodeNoException) && sr.N != 0)
{
return TranslateAddressResult{TranslateAddressResultEnum::PAGE_FAULT, 0};
}

u32 offset = EA_Offset(address); // 12 bit
u32 page_index = EA_PageIndex(address); // 16 bit
u32 VSID = SR_VSID(sr); // 24 bit
u32 api = EA_API(address); // 6 bit (part of page_index)
const u32 offset = address.offset; // 12 bit
const u32 page_index = address.page_index; // 16 bit
const u32 VSID = sr.VSID; // 24 bit
const u32 api = address.API; // 6 bit (part of page_index)

// hash function no 1 "xor" .360
u32 hash = (VSID ^ page_index);
u32 pte1 = (VSID << 7) | api | PTE1_V;

UPTE_Lo pte1;
pte1.VSID = VSID;
pte1.API = api;
pte1.V = 1;

for (int hash_func = 0; hash_func < 2; hash_func++)
{
// hash function no 2 "not" .360
if (hash_func == 1)
{
hash = ~hash;
pte1 |= PTE1_H;
pte1.H = 1;
}

u32 pteg_addr =
@@ -1389,10 +1343,9 @@ static TranslateAddressResult TranslatePageAddress(const u32 address, const XChe
{
const u32 pteg = Memory::Read_U32(pteg_addr);

if (pte1 == pteg)
if (pte1.Hex == pteg)
{
UPTE2 PTE2;
PTE2.Hex = Memory::Read_U32(pteg_addr + 4);
UPTE_Hi pte2(Memory::Read_U32(pteg_addr + 4));

// set the access bits
switch (flag)
@@ -1401,30 +1354,30 @@ static TranslateAddressResult TranslatePageAddress(const u32 address, const XChe
case XCheckTLBFlag::OpcodeNoException:
break;
case XCheckTLBFlag::Read:
PTE2.R = 1;
pte2.R = 1;
break;
case XCheckTLBFlag::Write:
PTE2.R = 1;
PTE2.C = 1;
pte2.R = 1;
pte2.C = 1;
break;
case XCheckTLBFlag::Opcode:
PTE2.R = 1;
pte2.R = 1;
break;
}

if (!IsNoExceptionFlag(flag))
{
Memory::Write_U32(PTE2.Hex, pteg_addr + 4);
Memory::Write_U32(pte2.Hex, pteg_addr + 4);
}

// We already updated the TLB entry if this was caused by a C bit.
if (res != TLBLookupResult::UpdateC)
UpdateTLBEntry(flag, PTE2, address);
UpdateTLBEntry(flag, pte2, address.Hex);

*wi = (PTE2.WIMG & 0b1100) != 0;
*wi = (pte2.WIMG & 0b1100) != 0;

return TranslateAddressResult{TranslateAddressResultEnum::PAGE_TABLE_TRANSLATED,
(PTE2.RPN << 12) | offset};
(pte2.RPN << 12) | offset};
}
}
}
@@ -1590,7 +1543,7 @@ static TranslateAddressResult TranslateAddress(u32 address)
if (TranslateBatAddess(IsOpcodeFlag(flag) ? ibat_table : dbat_table, &address, &wi))
return TranslateAddressResult{TranslateAddressResultEnum::BAT_TRANSLATED, address, wi};

return TranslatePageAddress(address, flag, &wi);
return TranslatePageAddress(EffectiveAddress{address}, flag, &wi);
}

std::optional<u32> GetTranslatedAddress(u32 address)
@@ -3,6 +3,7 @@

#pragma once

#include <array>
#include <cstddef>
#include <iosfwd>
#include <tuple>
@@ -49,12 +50,16 @@ constexpr size_t TLB_WAYS = 2;

struct TLBEntry
{
using WayArray = std::array<u32, TLB_WAYS>;

static constexpr u32 INVALID_TAG = 0xffffffff;

u32 tag[TLB_WAYS] = {INVALID_TAG, INVALID_TAG};
u32 paddr[TLB_WAYS] = {};
u32 pte[TLB_WAYS] = {};
u8 recent = 0;
WayArray tag{INVALID_TAG, INVALID_TAG};
WayArray paddr{};
WayArray pte{};
u32 recent = 0;

void Invalidate() { tag.fill(INVALID_TAG); }
};

struct PairedSingle