mirror of
https://github.com/dingusdev/dingusppc.git
synced 2025-01-11 05:29:43 +00:00
ppcmmu: implement 601-style BAT.
This commit is contained in:
parent
e9fcc51b93
commit
22827642e4
@ -255,8 +255,8 @@ extern uint64_t exceptions_processed;
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Function prototypes
|
// Function prototypes
|
||||||
extern void ppc_cpu_init(MemCtrlBase* mem_ctrl, uint32_t proc_version);
|
extern void ppc_cpu_init(MemCtrlBase* mem_ctrl, uint32_t cpu_version);
|
||||||
extern void ppc_mmu_init();
|
extern void ppc_mmu_init(uint32_t cpu_version);
|
||||||
|
|
||||||
[[noreturn]] void ppc_illegalop();
|
[[noreturn]] void ppc_illegalop();
|
||||||
[[noreturn]] void ppc_fpu_off();
|
[[noreturn]] void ppc_fpu_off();
|
||||||
|
@ -736,7 +736,7 @@ void initialize_ppc_opcode_tables() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ppc_cpu_init(MemCtrlBase* mem_ctrl, uint32_t proc_version) {
|
void ppc_cpu_init(MemCtrlBase* mem_ctrl, uint32_t cpu_version) {
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
mem_ctrl_instance = mem_ctrl;
|
mem_ctrl_instance = mem_ctrl;
|
||||||
@ -787,9 +787,9 @@ void ppc_cpu_init(MemCtrlBase* mem_ctrl, uint32_t proc_version) {
|
|||||||
ppc_state.spr[i] = 0;
|
ppc_state.spr[i] = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
ppc_state.spr[SPR::PVR] = proc_version;
|
ppc_state.spr[SPR::PVR] = cpu_version;
|
||||||
|
|
||||||
if ((proc_version & 0xFFFF0000) == 0x00010000) {
|
if ((cpu_version & 0xFFFF0000) == 0x00010000) {
|
||||||
/* MPC601 sets MSR[ME] bit during hard reset / Power-On */
|
/* MPC601 sets MSR[ME] bit during hard reset / Power-On */
|
||||||
ppc_state.msr = 0x1040;
|
ppc_state.msr = 0x1040;
|
||||||
} else {
|
} else {
|
||||||
@ -797,7 +797,7 @@ void ppc_cpu_init(MemCtrlBase* mem_ctrl, uint32_t proc_version) {
|
|||||||
ppc_state.spr[SPR::DEC] = 0xFFFFFFFFUL;
|
ppc_state.spr[SPR::DEC] = 0xFFFFFFFFUL;
|
||||||
}
|
}
|
||||||
|
|
||||||
ppc_mmu_init();
|
ppc_mmu_init(cpu_version);
|
||||||
|
|
||||||
/* redirect code execution to reset vector */
|
/* redirect code execution to reset vector */
|
||||||
ppc_state.pc = 0xFFF00100;
|
ppc_state.pc = 0xFFF00100;
|
||||||
|
@ -21,26 +21,29 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|||||||
|
|
||||||
/** @file PowerPC Memory Management Unit emulation. */
|
/** @file PowerPC Memory Management Unit emulation. */
|
||||||
|
|
||||||
/* TODO:
|
|
||||||
- implement 601-style BATs
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "ppcmmu.h"
|
#include "ppcmmu.h"
|
||||||
#include "devices/memctrlbase.h"
|
#include "devices/memctrlbase.h"
|
||||||
#include "memaccess.h"
|
#include "memaccess.h"
|
||||||
#include "ppcemu.h"
|
#include "ppcemu.h"
|
||||||
#include <array>
|
#include <array>
|
||||||
#include <cinttypes>
|
#include <cinttypes>
|
||||||
|
#include <functional>
|
||||||
#include <loguru.hpp>
|
#include <loguru.hpp>
|
||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
|
|
||||||
/* pointer to exception handler to be called when a MMU exception is occured. */
|
/* pointer to exception handler to be called when a MMU exception is occured. */
|
||||||
void (*mmu_exception_handler)(Except_Type exception_type, uint32_t srr1_bits);
|
void (*mmu_exception_handler)(Except_Type exception_type, uint32_t srr1_bits);
|
||||||
|
|
||||||
|
/* pointers to BAT update functions. */
|
||||||
|
std::function<void(uint32_t bat_reg)> ibat_update;
|
||||||
|
std::function<void(uint32_t bat_reg)> dbat_update;
|
||||||
|
|
||||||
/** PowerPC-style MMU BAT arrays (NULL initialization isn't prescribed). */
|
/** PowerPC-style MMU BAT arrays (NULL initialization isn't prescribed). */
|
||||||
PPC_BAT_entry ibat_array[4] = {{0}};
|
PPC_BAT_entry ibat_array[4] = {{0}};
|
||||||
PPC_BAT_entry dbat_array[4] = {{0}};
|
PPC_BAT_entry dbat_array[4] = {{0}};
|
||||||
|
|
||||||
|
bool is_601_MMU = false;
|
||||||
|
|
||||||
//#define MMU_PROFILING // uncomment this to enable MMU profiling
|
//#define MMU_PROFILING // uncomment this to enable MMU profiling
|
||||||
//#define TLB_PROFILING // uncomment this to enable SoftTLB profiling
|
//#define TLB_PROFILING // uncomment this to enable SoftTLB profiling
|
||||||
|
|
||||||
@ -85,6 +88,49 @@ void ppc_set_cur_instruction(const uint8_t* ptr) {
|
|||||||
ppc_cur_instruction = READ_DWORD_BE_A(ptr);
|
ppc_cur_instruction = READ_DWORD_BE_A(ptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** 601-style block address translation. */
|
||||||
|
static BATResult mpc601_block_address_translation(uint32_t la)
|
||||||
|
{
|
||||||
|
uint32_t pa; // translated physical address
|
||||||
|
uint8_t prot; // protection bits for the translated address
|
||||||
|
unsigned key;
|
||||||
|
|
||||||
|
bool bat_hit = false;
|
||||||
|
unsigned msr_pr = !!(ppc_state.msr & 0x4000);
|
||||||
|
|
||||||
|
// I/O controller interface takes precedence over BAT in 601
|
||||||
|
// Report BAT miss if T bit is set in the corresponding SR
|
||||||
|
if (ppc_state.sr[(la >> 28) & 0x0F] & 0x80000000) {
|
||||||
|
return BATResult{false, 0, 0};
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int bat_index = 0; bat_index < 4; bat_index++) {
|
||||||
|
PPC_BAT_entry* bat_entry = &ibat_array[bat_index];
|
||||||
|
|
||||||
|
if (bat_entry->valid && ((la & bat_entry->hi_mask) == bat_entry->bepi)) {
|
||||||
|
bat_hit = true;
|
||||||
|
|
||||||
|
key = (((bat_entry->access & 1) & msr_pr) |
|
||||||
|
(((bat_entry->access >> 1) & 1) & (msr_pr ^ 1)));
|
||||||
|
|
||||||
|
// remapping BAT access from 601-style to PowerPC-style
|
||||||
|
static uint8_t access_conv[8] = {2, 2, 2, 1, 0, 1, 2, 1};
|
||||||
|
|
||||||
|
prot = access_conv[(key << 2) | bat_entry->prot];
|
||||||
|
|
||||||
|
#ifdef MMU_PROFILING
|
||||||
|
bat_transl_total++;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// logical to physical translation
|
||||||
|
pa = bat_entry->phys_hi | (la & ~bat_entry->hi_mask);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return BATResult{bat_hit, prot, pa};
|
||||||
|
}
|
||||||
|
|
||||||
/** PowerPC-style block address translation. */
|
/** PowerPC-style block address translation. */
|
||||||
template <const BATType type>
|
template <const BATType type>
|
||||||
static BATResult ppc_block_address_translation(uint32_t la)
|
static BATResult ppc_block_address_translation(uint32_t la)
|
||||||
@ -193,7 +239,16 @@ static PATResult page_address_translation(uint32_t la, bool is_instr_fetch,
|
|||||||
|
|
||||||
sr_val = ppc_state.sr[(la >> 28) & 0x0F];
|
sr_val = ppc_state.sr[(la >> 28) & 0x0F];
|
||||||
if (sr_val & 0x80000000) {
|
if (sr_val & 0x80000000) {
|
||||||
ABORT_F("Direct-store segments not supported, LA=0x%X\n", la);
|
// check for 601-specific memory-forced I/O segments
|
||||||
|
if (((sr_val >> 20) & 0x1FF) == 0x7F) {
|
||||||
|
return PATResult{
|
||||||
|
(la & 0x0FFFFFFF) | (sr_val << 28),
|
||||||
|
0, // prot = read/write
|
||||||
|
1 // no C bit updates
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
ABORT_F("Direct-store segments not supported, LA=0x%X\n", la);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* instruction fetch from a no-execute segment will cause ISI exception */
|
/* instruction fetch from a no-execute segment will cause ISI exception */
|
||||||
@ -253,54 +308,6 @@ static PATResult page_address_translation(uint32_t la, bool is_instr_fetch,
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/** PowerPC-style MMU data address translation. */
|
|
||||||
static uint32_t ppc_mmu_addr_translate(uint32_t la, int is_write)
|
|
||||||
{
|
|
||||||
uint32_t pa; /* translated physical address */
|
|
||||||
|
|
||||||
bool bat_hit = false;
|
|
||||||
unsigned msr_pr = !!(ppc_state.msr & 0x4000);
|
|
||||||
|
|
||||||
// Format: %XY
|
|
||||||
// X - supervisor access bit, Y - problem/user access bit
|
|
||||||
// Those bits are mutually exclusive
|
|
||||||
unsigned access_bits = ((msr_pr ^ 1) << 1) | msr_pr;
|
|
||||||
|
|
||||||
for (int bat_index = 0; bat_index < 4; bat_index++) {
|
|
||||||
PPC_BAT_entry* bat_entry = &dbat_array[bat_index];
|
|
||||||
|
|
||||||
if ((bat_entry->access & access_bits) && ((la & bat_entry->hi_mask) == bat_entry->bepi)) {
|
|
||||||
bat_hit = true;
|
|
||||||
|
|
||||||
#ifdef MMU_PROFILING
|
|
||||||
bat_transl_total++;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
if (!bat_entry->prot || ((bat_entry->prot & 1) && is_write)) {
|
|
||||||
ppc_state.spr[SPR::DSISR] = 0x08000000 | (is_write << 25);
|
|
||||||
ppc_state.spr[SPR::DAR] = la;
|
|
||||||
mmu_exception_handler(Except_Type::EXC_DSI, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
// logical to physical translation
|
|
||||||
pa = bat_entry->phys_hi | (la & ~bat_entry->hi_mask);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* page address translation */
|
|
||||||
if (!bat_hit) {
|
|
||||||
PATResult pat_res = page_address_translation(la, false, msr_pr, is_write);
|
|
||||||
pa = pat_res.phys;
|
|
||||||
|
|
||||||
#ifdef MMU_PROFILING
|
|
||||||
ptab_transl_total++;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
return pa;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint8_t* mmu_get_dma_mem(uint32_t addr, uint32_t size)
|
uint8_t* mmu_get_dma_mem(uint32_t addr, uint32_t size)
|
||||||
{
|
{
|
||||||
if (addr >= last_dma_area.start && (addr + size) <= last_dma_area.end) {
|
if (addr >= last_dma_area.start && (addr + size) <= last_dma_area.end) {
|
||||||
@ -347,7 +354,7 @@ uint32_t tlb_size_mask = TLB_SIZE - 1;
|
|||||||
|
|
||||||
// fake TLB entry for handling of unmapped memory accesses
|
// fake TLB entry for handling of unmapped memory accesses
|
||||||
uint64_t UnmappedVal = -1ULL;
|
uint64_t UnmappedVal = -1ULL;
|
||||||
TLBEntry UnmappedMem = {TLB_INVALID_TAG, 0, 0, 0};
|
TLBEntry UnmappedMem = {TLB_INVALID_TAG, TLBFlags::PAGE_NOPHYS, 0, 0};
|
||||||
|
|
||||||
uint8_t CurITLBMode = {0xFF}; // current ITLB mode
|
uint8_t CurITLBMode = {0xFF}; // current ITLB mode
|
||||||
uint8_t CurDTLBMode = {0xFF}; // current DTLB mode
|
uint8_t CurDTLBMode = {0xFF}; // current DTLB mode
|
||||||
@ -477,6 +484,7 @@ static TLBEntry* tlb2_target_entry(uint32_t gp_va)
|
|||||||
|
|
||||||
static TLBEntry* itlb2_refill(uint32_t guest_va)
|
static TLBEntry* itlb2_refill(uint32_t guest_va)
|
||||||
{
|
{
|
||||||
|
BATResult bat_res;
|
||||||
uint32_t phys_addr;
|
uint32_t phys_addr;
|
||||||
TLBEntry *tlb_entry;
|
TLBEntry *tlb_entry;
|
||||||
uint16_t flags = 0;
|
uint16_t flags = 0;
|
||||||
@ -484,7 +492,11 @@ static TLBEntry* itlb2_refill(uint32_t guest_va)
|
|||||||
/* instruction address translation if enabled */
|
/* instruction address translation if enabled */
|
||||||
if (ppc_state.msr & 0x20) {
|
if (ppc_state.msr & 0x20) {
|
||||||
// attempt block address translation first
|
// attempt block address translation first
|
||||||
BATResult bat_res = ppc_block_address_translation<BATType::IBAT>(guest_va);
|
if (is_601_MMU) {
|
||||||
|
bat_res = mpc601_block_address_translation(guest_va);
|
||||||
|
} else {
|
||||||
|
bat_res = ppc_block_address_translation<BATType::IBAT>(guest_va);
|
||||||
|
}
|
||||||
if (bat_res.hit) {
|
if (bat_res.hit) {
|
||||||
// check block protection
|
// check block protection
|
||||||
// only PP = 0 (no access) causes ISI exception
|
// only PP = 0 (no access) causes ISI exception
|
||||||
@ -526,6 +538,7 @@ static TLBEntry* itlb2_refill(uint32_t guest_va)
|
|||||||
|
|
||||||
static TLBEntry* dtlb2_refill(uint32_t guest_va, int is_write)
|
static TLBEntry* dtlb2_refill(uint32_t guest_va, int is_write)
|
||||||
{
|
{
|
||||||
|
BATResult bat_res;
|
||||||
uint32_t phys_addr;
|
uint32_t phys_addr;
|
||||||
uint16_t flags = 0;
|
uint16_t flags = 0;
|
||||||
TLBEntry *tlb_entry;
|
TLBEntry *tlb_entry;
|
||||||
@ -535,7 +548,11 @@ static TLBEntry* dtlb2_refill(uint32_t guest_va, int is_write)
|
|||||||
/* data address translation if enabled */
|
/* data address translation if enabled */
|
||||||
if (ppc_state.msr & 0x10) {
|
if (ppc_state.msr & 0x10) {
|
||||||
// attempt block address translation first
|
// attempt block address translation first
|
||||||
BATResult bat_res = ppc_block_address_translation<BATType::DBAT>(guest_va);
|
if (is_601_MMU) {
|
||||||
|
bat_res = mpc601_block_address_translation(guest_va);
|
||||||
|
} else {
|
||||||
|
bat_res = ppc_block_address_translation<BATType::DBAT>(guest_va);
|
||||||
|
}
|
||||||
if (bat_res.hit) {
|
if (bat_res.hit) {
|
||||||
// check block protection
|
// check block protection
|
||||||
if (!bat_res.prot || ((bat_res.prot & 1) && is_write)) {
|
if (!bat_res.prot || ((bat_res.prot & 1) && is_write)) {
|
||||||
@ -588,9 +605,7 @@ static TLBEntry* dtlb2_refill(uint32_t guest_va, int is_write)
|
|||||||
}
|
}
|
||||||
return tlb_entry;
|
return tlb_entry;
|
||||||
} else {
|
} else {
|
||||||
LOG_F(ERROR, "Read from unmapped memory at 0x%08X!\n", phys_addr);
|
LOG_F(ERROR, "Access to unmapped physical memory, phys_addr=0x%08X\n", phys_addr);
|
||||||
UnmappedMem.tag = tag;
|
|
||||||
UnmappedMem.host_va_offset = (int64_t)(&UnmappedVal) - guest_va;
|
|
||||||
return &UnmappedMem;
|
return &UnmappedMem;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -714,6 +729,9 @@ inline T mmu_read_vmem(uint32_t guest_va)
|
|||||||
// secondary TLB miss ->
|
// secondary TLB miss ->
|
||||||
// perform full address translation and refill the secondary TLB
|
// perform full address translation and refill the secondary TLB
|
||||||
tlb2_entry = dtlb2_refill(guest_va, 0);
|
tlb2_entry = dtlb2_refill(guest_va, 0);
|
||||||
|
if (tlb2_entry->flags & PAGE_NOPHYS) {
|
||||||
|
return UnmappedVal;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
#ifdef TLB_PROFILING
|
#ifdef TLB_PROFILING
|
||||||
else {
|
else {
|
||||||
@ -809,6 +827,9 @@ inline void mmu_write_vmem(uint32_t guest_va, T value)
|
|||||||
// secondary TLB miss ->
|
// secondary TLB miss ->
|
||||||
// perform full address translation and refill the secondary TLB
|
// perform full address translation and refill the secondary TLB
|
||||||
tlb2_entry = dtlb2_refill(guest_va, 1);
|
tlb2_entry = dtlb2_refill(guest_va, 1);
|
||||||
|
if (tlb2_entry->flags & PAGE_NOPHYS) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
#ifdef TLB_PROFILING
|
#ifdef TLB_PROFILING
|
||||||
else {
|
else {
|
||||||
@ -1047,7 +1068,44 @@ void tlb_flush_pat_entries()
|
|||||||
gTLBFlushPatEntries = false;
|
gTLBFlushPatEntries = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ibat_update(uint32_t bat_reg)
|
static void mpc601_bat_update(uint32_t bat_reg)
|
||||||
|
{
|
||||||
|
PPC_BAT_entry *ibat_entry, *dbat_entry;
|
||||||
|
uint32_t bsm, hi_mask;
|
||||||
|
uint8_t key, pp, prot;
|
||||||
|
int upper_reg_num;
|
||||||
|
|
||||||
|
upper_reg_num = bat_reg & 0xFFFFFFFE;
|
||||||
|
|
||||||
|
ibat_entry = &ibat_array[(bat_reg - 528) >> 1];
|
||||||
|
dbat_entry = &dbat_array[(bat_reg - 528) >> 1];
|
||||||
|
|
||||||
|
if (ppc_state.spr[bat_reg | 1] & 0x40) {
|
||||||
|
bsm = ppc_state.spr[upper_reg_num + 1] & 0x3F;
|
||||||
|
hi_mask = ~((bsm << 17) | 0x1FFFF);
|
||||||
|
|
||||||
|
ibat_entry->valid = true;
|
||||||
|
ibat_entry->access = (ppc_state.spr[upper_reg_num] >> 2) & 3;
|
||||||
|
ibat_entry->prot = ppc_state.spr[upper_reg_num] & 3;
|
||||||
|
ibat_entry->hi_mask = hi_mask;
|
||||||
|
ibat_entry->phys_hi = ppc_state.spr[upper_reg_num + 1] & hi_mask;
|
||||||
|
ibat_entry->bepi = ppc_state.spr[upper_reg_num] & hi_mask;
|
||||||
|
|
||||||
|
// copy IBAT entry to DBAT entry
|
||||||
|
*dbat_entry = *ibat_entry;
|
||||||
|
} else {
|
||||||
|
// disable the corresponding BAT paars
|
||||||
|
ibat_entry->valid = false;
|
||||||
|
dbat_entry->valid = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!gTLBFlushBatEntries) {
|
||||||
|
gTLBFlushBatEntries = true;
|
||||||
|
add_ctx_sync_action(&tlb_flush_bat_entries);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ppc_ibat_update(uint32_t bat_reg)
|
||||||
{
|
{
|
||||||
int upper_reg_num;
|
int upper_reg_num;
|
||||||
uint32_t bl, hi_mask;
|
uint32_t bl, hi_mask;
|
||||||
@ -1073,7 +1131,7 @@ void ibat_update(uint32_t bat_reg)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void dbat_update(uint32_t bat_reg)
|
static void ppc_dbat_update(uint32_t bat_reg)
|
||||||
{
|
{
|
||||||
int upper_reg_num;
|
int upper_reg_num;
|
||||||
uint32_t bl, hi_mask;
|
uint32_t bl, hi_mask;
|
||||||
@ -1345,6 +1403,54 @@ static inline void write_phys_mem(AddressMapEntry *mru_rgn, uint32_t addr, T val
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** PowerPC-style MMU data address translation. */
|
||||||
|
static uint32_t ppc_mmu_addr_translate(uint32_t la, int is_write)
|
||||||
|
{
|
||||||
|
uint32_t pa; /* translated physical address */
|
||||||
|
|
||||||
|
bool bat_hit = false;
|
||||||
|
unsigned msr_pr = !!(ppc_state.msr & 0x4000);
|
||||||
|
|
||||||
|
// Format: %XY
|
||||||
|
// X - supervisor access bit, Y - problem/user access bit
|
||||||
|
// Those bits are mutually exclusive
|
||||||
|
unsigned access_bits = ((msr_pr ^ 1) << 1) | msr_pr;
|
||||||
|
|
||||||
|
for (int bat_index = 0; bat_index < 4; bat_index++) {
|
||||||
|
PPC_BAT_entry* bat_entry = &dbat_array[bat_index];
|
||||||
|
|
||||||
|
if ((bat_entry->access & access_bits) && ((la & bat_entry->hi_mask) == bat_entry->bepi)) {
|
||||||
|
bat_hit = true;
|
||||||
|
|
||||||
|
#ifdef MMU_PROFILING
|
||||||
|
bat_transl_total++;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (!bat_entry->prot || ((bat_entry->prot & 1) && is_write)) {
|
||||||
|
ppc_state.spr[SPR::DSISR] = 0x08000000 | (is_write << 25);
|
||||||
|
ppc_state.spr[SPR::DAR] = la;
|
||||||
|
mmu_exception_handler(Except_Type::EXC_DSI, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// logical to physical translation
|
||||||
|
pa = bat_entry->phys_hi | (la & ~bat_entry->hi_mask);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* page address translation */
|
||||||
|
if (!bat_hit) {
|
||||||
|
PATResult pat_res = page_address_translation(la, false, msr_pr, is_write);
|
||||||
|
pa = pat_res.phys;
|
||||||
|
|
||||||
|
#ifdef MMU_PROFILING
|
||||||
|
ptab_transl_total++;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
return pa;
|
||||||
|
}
|
||||||
|
|
||||||
static void mem_write_unaligned(uint32_t addr, uint32_t value, uint32_t size) {
|
static void mem_write_unaligned(uint32_t addr, uint32_t value, uint32_t size) {
|
||||||
#ifdef MMU_DEBUG
|
#ifdef MMU_DEBUG
|
||||||
LOG_F(WARNING, "Attempt to write unaligned %d bytes to 0x%08X\n", size, addr);
|
LOG_F(WARNING, "Attempt to write unaligned %d bytes to 0x%08X\n", size, addr);
|
||||||
@ -1733,9 +1839,21 @@ uint64_t mem_read_dbg(uint32_t virt_addr, uint32_t size) {
|
|||||||
return ret_val;
|
return ret_val;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ppc_mmu_init() {
|
void ppc_mmu_init(uint32_t cpu_version)
|
||||||
|
{
|
||||||
mmu_exception_handler = ppc_exception_handler;
|
mmu_exception_handler = ppc_exception_handler;
|
||||||
|
|
||||||
|
if ((cpu_version >> 16) == 1) {
|
||||||
|
// use 601-style BATs
|
||||||
|
ibat_update = &mpc601_bat_update;
|
||||||
|
is_601_MMU = true;
|
||||||
|
} else {
|
||||||
|
// use PPC-style BATs
|
||||||
|
ibat_update = &ppc_ibat_update;
|
||||||
|
dbat_update = &ppc_dbat_update;
|
||||||
|
is_601_MMU = false;
|
||||||
|
}
|
||||||
|
|
||||||
// invalidate all IDTLB entries
|
// invalidate all IDTLB entries
|
||||||
for (auto &tlb_el : itlb1_mode1) {
|
for (auto &tlb_el : itlb1_mode1) {
|
||||||
tlb_el.tag = TLB_INVALID_TAG;
|
tlb_el.tag = TLB_INVALID_TAG;
|
||||||
|
@ -26,6 +26,7 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|||||||
|
|
||||||
#include <array>
|
#include <array>
|
||||||
#include <cinttypes>
|
#include <cinttypes>
|
||||||
|
#include <functional>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include "devices/memctrlbase.h"
|
#include "devices/memctrlbase.h"
|
||||||
|
|
||||||
@ -34,11 +35,12 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|||||||
|
|
||||||
/** generic PowerPC BAT descriptor (MMU internal state) */
|
/** generic PowerPC BAT descriptor (MMU internal state) */
|
||||||
typedef struct PPC_BAT_entry {
|
typedef struct PPC_BAT_entry {
|
||||||
uint8_t access; /* copy of Vs | Vp bits */
|
bool valid; /* BAT entry valid for MPC601 */
|
||||||
uint8_t prot; /* copy of PP bits */
|
uint8_t access; /* copy of Vs | Vp bits */
|
||||||
uint32_t phys_hi; /* high-order bits for physical address generation */
|
uint8_t prot; /* copy of PP bits */
|
||||||
uint32_t hi_mask; /* mask for high-order logical address bits */
|
uint32_t phys_hi; /* high-order bits for physical address generation */
|
||||||
uint32_t bepi; /* copy of Block effective page index */
|
uint32_t hi_mask; /* mask for high-order logical address bits */
|
||||||
|
uint32_t bepi; /* copy of Block effective page index */
|
||||||
} PPC_BAT_entry;
|
} PPC_BAT_entry;
|
||||||
|
|
||||||
/** Block address translation types. */
|
/** Block address translation types. */
|
||||||
@ -85,14 +87,15 @@ typedef struct TLBEntry {
|
|||||||
enum TLBFlags : uint16_t {
|
enum TLBFlags : uint16_t {
|
||||||
PAGE_MEM = 1 << 0, // memory page backed by host memory
|
PAGE_MEM = 1 << 0, // memory page backed by host memory
|
||||||
PAGE_IO = 1 << 1, // memory mapped I/O page
|
PAGE_IO = 1 << 1, // memory mapped I/O page
|
||||||
TLBE_FROM_BAT = 1 << 2, // TLB entry has been translated with BAT
|
PAGE_NOPHYS = 1 << 2, // no physical storage for this page (unmapped)
|
||||||
TLBE_FROM_PAT = 1 << 3, // TLB entry has been translated with PAT
|
TLBE_FROM_BAT = 1 << 3, // TLB entry has been translated with BAT
|
||||||
PAGE_WRITABLE = 1 << 4, // page is writable
|
TLBE_FROM_PAT = 1 << 4, // TLB entry has been translated with PAT
|
||||||
PTE_SET_C = 1 << 5, // tells if C bit of the PTE needs to be updated
|
PAGE_WRITABLE = 1 << 5, // page is writable
|
||||||
|
PTE_SET_C = 1 << 6, // tells if C bit of the PTE needs to be updated
|
||||||
};
|
};
|
||||||
|
|
||||||
extern void ibat_update(uint32_t bat_reg);
|
extern std::function<void(uint32_t bat_reg)> ibat_update;
|
||||||
extern void dbat_update(uint32_t bat_reg);
|
extern std::function<void(uint32_t bat_reg)> dbat_update;
|
||||||
|
|
||||||
extern uint8_t* mmu_get_dma_mem(uint32_t addr, uint32_t size);
|
extern uint8_t* mmu_get_dma_mem(uint32_t addr, uint32_t size);
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user