-arm7: Added rudimentary TLB support. Allows HP Jornada 720 to boot further. [Ryan Holtz]

This commit is contained in:
Ryan Holtz 2020-12-26 11:30:12 +01:00
parent d7ef8a48f2
commit e6d4824a8d
3 changed files with 514 additions and 131 deletions

View File

@ -41,8 +41,9 @@ TODO:
#define LOG_COPRO_WRITES (1 << 3)
#define LOG_COPRO_UNKNOWN (1 << 4)
#define LOG_COPRO_RESERVED (1 << 5)
#define LOG_TLB (1 << 6)
#define VERBOSE (0) // (LOG_MMU | LOG_COPRO_READS | LOG_COPRO_WRITES | LOG_COPRO_UNKNOWN | LOG_COPRO_RESERVED)
#define VERBOSE (0) // (LOG_COPRO_READS | LOG_COPRO_WRITES | LOG_COPRO_UNKNOWN | LOG_COPRO_RESERVED)
#include "logmacro.h"
#define PRINT_HAPYFSH2 (0)
@ -103,6 +104,8 @@ arm7_cpu_device::arm7_cpu_device(const machine_config &mconfig, device_type type
memset(m_insn_prefetch_valid, 0, sizeof(bool) * 3);
m_insn_prefetch_count = 0;
m_insn_prefetch_index = 0;
m_tlb_log = 0;
m_actual_log = 0;
}
@ -347,7 +350,7 @@ enum
// COARSE, desc_level1, vaddr
uint32_t arm7_cpu_device::arm7_tlb_get_second_level_descriptor( uint32_t granularity, uint32_t first_desc, uint32_t vaddr )
uint32_t arm7_cpu_device::get_lvl2_desc_from_page_table( uint32_t granularity, uint32_t first_desc, uint32_t vaddr )
{
uint32_t desc_lvl2 = vaddr;
@ -355,9 +358,13 @@ uint32_t arm7_cpu_device::arm7_tlb_get_second_level_descriptor( uint32_t granula
{
case TLB_COARSE:
desc_lvl2 = (first_desc & COPRO_TLB_CFLD_ADDR_MASK) | ((vaddr & COPRO_TLB_VADDR_CSLTI_MASK) >> COPRO_TLB_VADDR_CSLTI_MASK_SHIFT);
if (m_tlb_log)
LOGMASKED(LOG_TLB, "%s: get_lvl2_desc_from_page_table: coarse descriptor, lvl2 address is %08x\n", machine().describe_context(), desc_lvl2);
break;
case TLB_FINE:
desc_lvl2 = (first_desc & COPRO_TLB_FPTB_ADDR_MASK) | ((vaddr & COPRO_TLB_VADDR_FSLTI_MASK) >> COPRO_TLB_VADDR_FSLTI_MASK_SHIFT);
if (m_tlb_log)
LOGMASKED(LOG_TLB, "%s: get_lvl2_desc_from_page_table: fine descriptor, lvl2 address is %08x\n", machine().describe_context(), desc_lvl2);
break;
default:
// We shouldn't be here
@ -444,148 +451,376 @@ int arm7_cpu_device::detect_fault(int desc_lvl1, int ap, int flags)
return FAULT_NONE;
}
bool arm7_cpu_device::arm7_tlb_translate(offs_t &addr, int flags)
arm7_cpu_device::tlb_entry *arm7_cpu_device::tlb_map_entry(const offs_t vaddr, const int flags)
{
if (addr < 0x2000000)
{
addr += m_pid_offset;
}
const uint32_t bucket = (vaddr >> (COPRO_TLB_VADDR_FLTI_MASK_SHIFT + 2)) & 0x1F;
tlb_entry *entries = (flags & ARM7_TLB_ABORT_D) ? m_dtlb_entries : m_itlb_entries;
const uint32_t start = (flags & ARM7_TLB_ABORT_D) ? m_dtlb_entry_start[bucket] : m_itlb_entry_start[bucket];
uint32_t index = (flags & ARM7_TLB_ABORT_D) ? m_dtlb_entry_index[bucket] : m_itlb_entry_index[bucket];
uint32_t desc_lvl1 = m_program->read_dword(m_tlb_base_mask | ((addr & COPRO_TLB_VADDR_FLTI_MASK) >> COPRO_TLB_VADDR_FLTI_MASK_SHIFT));
bool entry_found = false;
#if ARM7_MMU_ENABLE_HACK
if ((m_r[eR15] == (m_mmu_enable_addr + 4)) || (m_r[eR15] == (m_mmu_enable_addr + 8)))
for (uint32_t i = 0; i < 2; i++)
{
LOGMASKED(LOG_MMU, "ARM7: fetch flat, PC = %08x, vaddr = %08x\n", m_r[eR15], addr);
return true;
}
else
{
m_mmu_enable_addr = 1;
}
#endif
uint8_t tlb_type = desc_lvl1 & 3;
if (tlb_type == COPRO_TLB_SECTION_TABLE)
{
// Entry is a section
int fault = detect_fault(desc_lvl1, (desc_lvl1 >> 10) & 3, flags);
if (fault == FAULT_NONE)
index = (index + 1) & 1;
if (!entries[start + index].valid)
{
addr = ( desc_lvl1 & COPRO_TLB_SECTION_PAGE_MASK ) | ( addr & ~COPRO_TLB_SECTION_PAGE_MASK );
entry_found = true;
break;
}
}
if (!entry_found)
{
index = (index + 1) & 1;
}
if (flags & ARM7_TLB_ABORT_D)
m_dtlb_entry_index[bucket] = index;
else
m_itlb_entry_index[bucket] = index;
return &entries[start + index];
}
arm7_cpu_device::tlb_entry *arm7_cpu_device::tlb_probe(const offs_t vaddr, const int flags)
{
const uint32_t bucket = (vaddr >> (COPRO_TLB_VADDR_FLTI_MASK_SHIFT + 2)) & 0x1F;
tlb_entry *entries = (flags & ARM7_TLB_ABORT_D) ? m_dtlb_entries : m_itlb_entries;
const uint32_t start = (flags & ARM7_TLB_ABORT_D) ? m_dtlb_entry_start[bucket] : m_itlb_entry_start[bucket];
uint32_t index = (flags & ARM7_TLB_ABORT_D) ? m_dtlb_entry_index[bucket] : m_itlb_entry_index[bucket];
for (uint32_t i = 0; i < 2; i++)
{
uint32_t position = start + index;
if (entries[position].valid)
{
switch (entries[position].type)
{
case COPRO_TLB_TYPE_SECTION:
if (entries[position].table_bits == (vaddr & COPRO_TLB_STABLE_MASK))
return &entries[position];
break;
case COPRO_TLB_TYPE_LARGE:
case COPRO_TLB_TYPE_SMALL:
if (entries[position].table_bits == (vaddr & COPRO_TLB_LSTABLE_MASK))
return &entries[position];
break;
case COPRO_TLB_TYPE_TINY:
if (entries[position].table_bits == (vaddr & COPRO_TLB_TTABLE_MASK))
return &entries[position];
break;
}
}
index = (index - 1) & 1;
}
return nullptr;
}
uint32_t arm7_cpu_device::get_fault_from_permissions(const uint8_t access, const uint8_t domain, const uint8_t type, int flags)
{
const uint8_t domain_bits = m_decoded_access_control[domain];
switch (domain_bits)
{
case COPRO_DOMAIN_NO_ACCESS:
if (type == COPRO_TLB_TYPE_SECTION)
return (domain << 4) | COPRO_FAULT_DOMAIN_SECTION;
return (domain << 4) | COPRO_FAULT_DOMAIN_PAGE;
case COPRO_DOMAIN_CLIENT:
{
const uint32_t mode = GET_CPSR & 0xF;
switch (access)
{
case 0: // Check System/ROM bit
{
const uint32_t sr = (COPRO_CTRL >> COPRO_CTRL_SYSTEM_SHIFT) & 3;
switch (sr)
{
case 0: // No Access
if (type == COPRO_TLB_TYPE_SECTION)
return (domain << 4) | COPRO_FAULT_PERM_SECTION;
return (domain << 4) | COPRO_FAULT_PERM_PAGE;
case 1: // No User Access, Read-Only System Access
if (mode == 0 || (flags & ARM7_TLB_WRITE))
{
if (type == COPRO_TLB_TYPE_SECTION)
return (domain << 4) | COPRO_FAULT_PERM_SECTION;
return (domain << 4) | COPRO_FAULT_PERM_PAGE;
}
return COPRO_FAULT_NONE;
case 2: // Read-Only Access
if (flags & ARM7_TLB_WRITE)
{
if (type == COPRO_TLB_TYPE_SECTION)
return (domain << 4) | COPRO_FAULT_PERM_SECTION;
return (domain << 4) | COPRO_FAULT_PERM_PAGE;
}
return COPRO_FAULT_NONE;
case 3: // Unpredictable Access
LOGMASKED(LOG_MMU, "%s: get_fault_from_permissions: Unpredictable access permissions (AP bits are 0, SR bits are 3).", machine().describe_context());
return COPRO_FAULT_NONE;
}
return COPRO_FAULT_NONE;
}
case 1: // No User Access
if (mode != 0)
return COPRO_FAULT_NONE;
if (type == COPRO_TLB_TYPE_SECTION)
return (domain << 4) | COPRO_FAULT_PERM_SECTION;
return (domain << 4) | COPRO_FAULT_PERM_PAGE;
case 2: // Read-Only User Access
if (mode != 0 || (flags & ARM7_TLB_READ))
return COPRO_FAULT_NONE;
if (type == COPRO_TLB_TYPE_SECTION)
return (domain << 4) | COPRO_FAULT_PERM_SECTION;
return (domain << 4) | COPRO_FAULT_PERM_PAGE;
case 3: // Full Access
return COPRO_FAULT_NONE;
}
return COPRO_FAULT_NONE;
}
case COPRO_DOMAIN_RESV:
LOGMASKED(LOG_MMU, "%s: get_fault_from_permissions: Domain type marked as Reserved.\n", machine().describe_context());
return COPRO_FAULT_NONE;
default:
return COPRO_FAULT_NONE;
}
}
uint32_t arm7_cpu_device::tlb_check_permissions(tlb_entry *entry, const int flags)
{
return get_fault_from_permissions(entry->access, entry->domain, entry->type, flags);
}
offs_t arm7_cpu_device::tlb_translate(tlb_entry *entry, const offs_t vaddr)
{
switch (entry->type)
{
case COPRO_TLB_TYPE_SECTION:
return entry->base_addr | (vaddr & ~COPRO_TLB_SECTION_PAGE_MASK);
case COPRO_TLB_TYPE_LARGE:
return entry->base_addr | (vaddr & ~COPRO_TLB_LARGE_PAGE_MASK);
case COPRO_TLB_TYPE_SMALL:
return entry->base_addr | (vaddr & ~COPRO_TLB_SMALL_PAGE_MASK);
case COPRO_TLB_TYPE_TINY:
return entry->base_addr | (vaddr & ~COPRO_TLB_TINY_PAGE_MASK);
default:
return 0;
}
}
bool arm7_cpu_device::page_table_finish_translation(offs_t &vaddr, const uint8_t type, const uint32_t lvl1, const uint32_t lvl2, const int flags, const uint32_t lvl1a, const uint32_t lvl2a)
{
const uint8_t domain = (uint8_t)(lvl1 >> 5) & 0xF;
uint8_t access = 0;
uint32_t table_bits = 0;
switch (type)
{
case COPRO_TLB_TYPE_SECTION:
access = (uint8_t)((lvl2 >> 10) & 3);
table_bits = vaddr & COPRO_TLB_STABLE_MASK;
break;
case COPRO_TLB_TYPE_LARGE:
{
const uint8_t subpage_shift = 4 + (uint8_t)((vaddr >> 13) & 6);
access = (uint8_t)((lvl2 >> subpage_shift) & 3);
table_bits = vaddr & COPRO_TLB_LSTABLE_MASK;
break;
}
case COPRO_TLB_TYPE_SMALL:
{
const uint8_t subpage_shift = 4 + (uint8_t)((vaddr >> 9) & 6);
access = (uint8_t)((lvl2 >> subpage_shift) & 3);
table_bits = vaddr & COPRO_TLB_LSTABLE_MASK;
break;
}
case COPRO_TLB_TYPE_TINY:
access = (uint8_t)((lvl2 >> 4) & 3);
table_bits = vaddr & COPRO_TLB_TTABLE_MASK;
break;
}
const uint32_t access_result = get_fault_from_permissions(access, domain, type, flags);
if (access_result != 0)
{
if (flags & ARM7_TLB_ABORT_P)
{
LOGMASKED(LOG_MMU, "ARM7: Page walk, Potential prefetch abort, vaddr = %08x, lvl1A = %08x, lvl1D = %08x, lvl2A = %08x, lvl2D = %08x\n", vaddr, lvl1a, lvl1, lvl2a, lvl2);
}
else if (flags & ARM7_TLB_ABORT_D)
{
uint8_t domain = (desc_lvl1 >> 5) & 0xF;
LOGMASKED(LOG_MMU, "ARM7: Section Table, Section %s fault on virtual address, vaddr = %08x, PC = %08x\n", (fault == FAULT_DOMAIN) ? "domain" : "permission", addr, m_r[eR15]);
m_faultStatus[0] = ((fault == FAULT_DOMAIN) ? (9 << 0) : (13 << 0)) | (domain << 4); // 9 = section domain fault, 13 = section permission fault
m_faultAddress = addr;
LOGMASKED(LOG_MMU, "ARM7: Page walk, Data abort, vaddr = %08x, lvl1A = %08x, lvl1D = %08x, lvl2A = %08x, lvl2D = %08x\n", vaddr, lvl1a, lvl1, lvl2a, lvl2);
LOGMASKED(LOG_MMU, "access: %d, domain: %d, type: %d\n", access, domain, type);
m_faultStatus[0] = access_result;
m_faultAddress = vaddr;
m_pendingAbtD = true;
update_irq_state();
LOGMASKED(LOG_MMU, "vaddr %08X desc_lvl1 %08X domain %d permission %d ap %d s %d r %d mode %d read %d write %d\n",
addr, desc_lvl1, domain, (m_domainAccessControl >> ((desc_lvl1 >> 4) & 0x1e)) & 3, (desc_lvl1 >> 10) & 3, (m_control & COPRO_CTRL_SYSTEM) ? 1 : 0, (m_control & COPRO_CTRL_ROM) ? 1 : 0,
m_r[eCPSR] & MODE_FLAG, flags & ARM7_TLB_READ ? 1 : 0, flags & ARM7_TLB_WRITE ? 1 : 0);
}
return false;
}
static const uint32_t s_page_masks[4] = { COPRO_TLB_SECTION_PAGE_MASK, COPRO_TLB_LARGE_PAGE_MASK, COPRO_TLB_SMALL_PAGE_MASK, COPRO_TLB_TINY_PAGE_MASK };
const uint32_t base_addr = lvl2 & s_page_masks[type];
const uint32_t paddr = base_addr | (vaddr & ~s_page_masks[type]);
if (flags)
{
tlb_entry *entry = tlb_map_entry(vaddr, flags);
entry->valid = true;
entry->domain = domain;
entry->access = access;
entry->table_bits = table_bits;
entry->base_addr = base_addr;
entry->type = type;
}
vaddr = paddr;
return true;
}
bool arm7_cpu_device::page_table_translate(offs_t &vaddr, const int flags)
{
const uint32_t lvl1_addr = m_tlb_base_mask | ((vaddr & COPRO_TLB_VADDR_FLTI_MASK) >> COPRO_TLB_VADDR_FLTI_MASK_SHIFT);
const uint32_t lvl1_desc = m_program->read_dword(lvl1_addr);
switch (lvl1_desc & 3)
{
case 0: // Unmapped
if (flags & ARM7_TLB_ABORT_D)
{
LOGMASKED(LOG_MMU, "ARM7: Page Table Translation failed (D), PC %08x, lvl1 unmapped, vaddr = %08x, lvl1A = %08x, lvl1D = %08x\n", m_r[eR15], vaddr, lvl1_addr, lvl1_desc);
m_faultStatus[0] = COPRO_FAULT_TRANSLATE_SECTION;
m_faultAddress = vaddr;
m_pendingAbtD = true;
update_irq_state();
}
else if (flags & ARM7_TLB_ABORT_P)
{
LOGMASKED(LOG_MMU, "ARM7: Page Table Translation failed (P), PC %08x, lvl1 unmapped, vaddr = %08x, lvl1A = %08x, lvl1D = %08x\n", m_r[eR15], vaddr, lvl1_addr, lvl1_desc);
}
return false;
case 1: // Coarse Table
{
const uint32_t lvl2_addr = (lvl1_desc & COPRO_TLB_CFLD_ADDR_MASK) | ((vaddr & COPRO_TLB_VADDR_CSLTI_MASK) >> COPRO_TLB_VADDR_CSLTI_MASK_SHIFT);
const uint32_t lvl2_desc = m_program->read_dword(lvl2_addr);
switch (lvl2_desc & 3)
{
case 0: // Unmapped
if (flags & ARM7_TLB_ABORT_D)
{
LOGMASKED(LOG_MMU, "ARM7: Page Table Translation failed (D), coarse lvl2 unmapped, PC %08x, vaddr = %08x, lvl1A = %08x, lvl1D = %08x, lvl2A = %08x, lvl2D = %08x\n", m_r[eR15], vaddr, lvl1_addr, lvl1_desc, lvl2_addr, lvl2_desc);
m_faultStatus[0] = ((lvl1_desc >> 1) & 0xF0) | COPRO_FAULT_TRANSLATE_PAGE;
m_faultAddress = vaddr;
m_pendingAbtD = true;
update_irq_state();
}
else if (flags & ARM7_TLB_ABORT_P)
{
LOGMASKED(LOG_MMU, "ARM7: Page Table Translation failed (P), coarse lvl2 unmapped, PC %08x, vaddr = %08x, lvl1A = %08x, lvl1D = %08x, lvl2A = %08x, lvl2D = %08x\n", m_r[eR15], vaddr, lvl1_addr, lvl1_desc, lvl2_addr, lvl2_desc);
}
return false;
case 1: // Large Page
return page_table_finish_translation(vaddr, COPRO_TLB_TYPE_LARGE, lvl1_desc, lvl2_desc, flags, lvl1_addr, lvl2_addr);
case 2: // Small Page
return page_table_finish_translation(vaddr, COPRO_TLB_TYPE_SMALL, lvl1_desc, lvl2_desc, flags, lvl1_addr, lvl2_addr);
case 3: // Tiny Page (invalid)
LOGMASKED(LOG_MMU, "ARM7: Page Table Translation failed, tiny page present in coarse lvl2 table, PC %08x, vaddr = %08x, lvl1A = %08x, lvl1D = %08x, lvl2A = %08x, lvl2D = %08x\n", m_r[eR15], vaddr, lvl1_addr, lvl1_desc, lvl2_addr, lvl2_desc);
return false;
}
return false;
}
else if (flags & ARM7_TLB_ABORT_P)
case 2: // Section Descriptor
return page_table_finish_translation(vaddr, COPRO_TLB_TYPE_SECTION, lvl1_desc, lvl1_desc, flags, lvl1_addr, lvl1_addr);
case 3: // Fine Table
{
LOGMASKED(LOG_MMU, "ARM7: Section Table, Section %s fault on virtual address, vaddr = %08x, PC = %08x\n", (fault == FAULT_DOMAIN) ? "domain" : "permission", addr, m_r[eR15]);
const uint32_t lvl2_addr = (lvl1_desc & COPRO_TLB_FPTB_ADDR_MASK) | ((vaddr & COPRO_TLB_VADDR_FSLTI_MASK) >> COPRO_TLB_VADDR_FSLTI_MASK_SHIFT);
const uint32_t lvl2_desc = m_program->read_dword(lvl2_addr);
switch (lvl2_desc & 3)
{
case 0: // Unmapped
if (flags & ARM7_TLB_ABORT_D)
{
LOGMASKED(LOG_MMU, "ARM7: Page Table Translation failed (D), fine lvl2 unmapped, PC %08x, vaddr = %08x, lvl1A = %08x, lvl1D = %08x, lvl2A = %08x, lvl2D = %08x\n", m_r[eR15], vaddr, lvl1_addr, lvl1_desc, lvl2_addr, lvl2_desc);
m_faultStatus[0] = ((lvl1_desc >> 1) & 0xF0) | COPRO_FAULT_TRANSLATE_PAGE;
m_faultAddress = vaddr;
m_pendingAbtD = true;
update_irq_state();
}
else if (flags & ARM7_TLB_ABORT_P)
{
LOGMASKED(LOG_MMU, "ARM7: Page Table Translation failed (P), fine lvl2 unmapped, PC %08x, vaddr = %08x, lvl1A = %08x, lvl1D = %08x, lvl2A = %08x, lvl2D = %08x\n", m_r[eR15], vaddr, lvl1_addr, lvl1_desc, lvl2_addr, lvl2_desc);
}
return false;
case 1: // Large Page
return page_table_finish_translation(vaddr, COPRO_TLB_TYPE_LARGE, lvl1_desc, lvl2_desc, flags, lvl1_addr, lvl2_addr);
case 2: // Small Page
return page_table_finish_translation(vaddr, COPRO_TLB_TYPE_SMALL, lvl1_desc, lvl2_desc, flags, lvl1_addr, lvl2_addr);
case 3: // Tiny Page
return page_table_finish_translation(vaddr, COPRO_TLB_TYPE_TINY, lvl1_desc, lvl2_desc, flags, lvl1_addr, lvl2_addr);
}
return false;
}
}
else if (tlb_type == COPRO_TLB_UNMAPPED)
return false;
}
bool arm7_cpu_device::translate_vaddr_to_paddr(offs_t &vaddr, const int flags)
{
if (m_tlb_log)
LOGMASKED(LOG_TLB, "%s: translate_vaddr_to_paddr: vaddr %08x, flags %08x\n", machine().describe_context(), vaddr, flags);
if (vaddr < 0x2000000)
{
// Unmapped, generate a translation fault
if (flags & ARM7_TLB_ABORT_D)
vaddr += m_pid_offset;
if (m_tlb_log)
LOGMASKED(LOG_TLB, "%s: translate_vaddr_to_paddr: vaddr < 32M, adding PID (%08x) = %08x\n", machine().describe_context(), m_pid_offset, vaddr);
}
tlb_entry *entry = tlb_probe(vaddr, flags);
if (entry)
{
const uint32_t access_result = tlb_check_permissions(entry, flags);
if (access_result == 0)
{
LOGMASKED(LOG_MMU, "ARM7: Translation fault on unmapped virtual address (D), PC = %08x, vaddr = %08x\n", m_r[eR15], addr);
m_faultStatus[0] = (5 << 0); // 5 = section translation fault
m_faultAddress = addr;
m_pendingAbtD = true;
update_irq_state();
vaddr = tlb_translate(entry, vaddr);
return true;
}
else if (flags & ARM7_TLB_ABORT_P)
{
LOGMASKED(LOG_MMU, "ARM7: Translation fault on unmapped virtual address (P), PC = %08x, vaddr = %08x\n", m_r[eR15], addr);
LOGMASKED(LOG_MMU, "ARM7: TLB, Potential prefetch abort, vaddr = %08x\n", vaddr);
}
else if (flags & ARM7_TLB_ABORT_D)
{
LOGMASKED(LOG_MMU, "ARM7: TLB, Data abort, vaddr = %08x\n", vaddr);
m_faultStatus[0] = access_result;
m_faultAddress = vaddr;
m_pendingAbtD = true;
update_irq_state();
}
return false;
}
else
{
// Entry is the physical address of a coarse second-level table
uint8_t permission = (m_domainAccessControl >> ((desc_lvl1 >> 4) & 0x1e)) & 3;
uint32_t desc_lvl2 = arm7_tlb_get_second_level_descriptor( (desc_lvl1 & 3) == COPRO_TLB_COARSE_TABLE ? TLB_COARSE : TLB_FINE, desc_lvl1, addr );
if ((permission != 1) && (permission != 3))
{
uint8_t domain = (desc_lvl1 >> 5) & 0xF;
fatalerror("ARM7: Not Yet Implemented: Coarse Table, Section Domain fault on virtual address, vaddr = %08x, domain = %08x, PC = %08x\n", addr, domain, m_r[eR15]);
}
switch (desc_lvl2 & 3)
{
case COPRO_TLB_UNMAPPED:
// Unmapped, generate a translation fault
if (flags & ARM7_TLB_ABORT_D)
{
uint8_t domain = (desc_lvl1 >> 5) & 0xF;
LOGMASKED(LOG_MMU, "ARM7: Translation fault on unmapped virtual address (D), lvl2, vaddr = %08x, PC %08X\n", addr, m_r[eR15]);
m_faultStatus[0] = (7 << 0) | (domain << 4); // 7 = page translation fault
m_faultAddress = addr;
m_pendingAbtD = true;
update_irq_state();
}
else if (flags & ARM7_TLB_ABORT_P)
{
LOGMASKED(LOG_MMU, "ARM7: Translation fault on unmapped virtual address (P), lvl2, vaddr = %08x, PC %08X\n", addr, m_r[eR15]);
}
return false;
case COPRO_TLB_LARGE_PAGE:
// Large page descriptor
addr = ( desc_lvl2 & COPRO_TLB_LARGE_PAGE_MASK ) | ( addr & ~COPRO_TLB_LARGE_PAGE_MASK );
break;
case COPRO_TLB_SMALL_PAGE:
// Small page descriptor
{
uint8_t ap = ((((desc_lvl2 >> 4) & 0xFF) >> (((addr >> 10) & 3) << 1)) & 3);
int fault = detect_fault(desc_lvl1, ap, flags);
if (fault == FAULT_NONE)
{
addr = (desc_lvl2 & COPRO_TLB_SMALL_PAGE_MASK) | (addr & ~COPRO_TLB_SMALL_PAGE_MASK);
break;
}
else if (flags & ARM7_TLB_ABORT_D)
{
uint8_t domain = (desc_lvl1 >> 5) & 0xF;
// hapyfish expects a data abort when something tries to write to a read-only memory location from user mode
LOGMASKED(LOG_MMU, "ARM7: Page Table, Section %s fault on virtual address, vaddr = %08x, PC = %08x\n", (fault == FAULT_DOMAIN) ? "domain" : "permission", addr, m_r[eR15]);
m_faultStatus[0] = ((fault == FAULT_DOMAIN) ? (11 << 0) : (15 << 0)) | (domain << 4); // 11 = page domain fault, 15 = page permission fault
m_faultAddress = addr;
m_pendingAbtD = true;
update_irq_state();
LOGMASKED(LOG_MMU, "vaddr %08X desc_lvl2 %08X domain %d permission %d ap %d s %d r %d mode %d read %d write %d\n",
addr, desc_lvl2, domain, permission, ap, (m_control & COPRO_CTRL_SYSTEM) ? 1 : 0, (m_control & COPRO_CTRL_ROM) ? 1 : 0,
m_r[eCPSR] & MODE_FLAG, flags & ARM7_TLB_READ ? 1 : 0, flags & ARM7_TLB_WRITE ? 1 : 0);
}
else if (flags & ARM7_TLB_ABORT_P)
{
LOGMASKED(LOG_MMU, "ARM7: Page Table, Section %s fault on virtual address, vaddr = %08x, PC = %08x\n", (fault == FAULT_DOMAIN) ? "domain" : "permission", addr, m_r[eR15]);
}
return false;
}
break;
case COPRO_TLB_TINY_PAGE:
// Tiny page descriptor
if ((desc_lvl1 & 3) == 1)
{
LOGMASKED(LOG_MMU, "ARM7: It would appear that we're looking up a tiny page from a coarse TLB lookup. This is bad. vaddr = %08x\n", addr);
}
addr = (desc_lvl2 & COPRO_TLB_TINY_PAGE_MASK) | (addr & ~COPRO_TLB_TINY_PAGE_MASK);
break;
}
return page_table_translate(vaddr, flags);
}
return true;
}
@ -594,7 +829,28 @@ bool arm7_cpu_device::memory_translate(int spacenum, int intention, offs_t &addr
/* only applies to the program address space and only does something if the MMU's enabled */
if (spacenum == AS_PROGRAM && (m_control & COPRO_CTRL_MMU_EN))
{
return arm7_tlb_translate(address, 0);
int intention_type = intention & TRANSLATE_TYPE_MASK;
const int flags = (intention_type & TRANSLATE_FETCH) ? ARM7_TLB_ABORT_P : ARM7_TLB_ABORT_D;
if (address < 0x2000000)
address += m_pid_offset;
tlb_entry *entry = tlb_probe(address, flags);
if (entry)
{
const uint32_t access_result = tlb_check_permissions(entry, flags);
if (access_result == 0)
{
address = tlb_translate(entry, address);
return true;
}
return false;
}
else
{
return page_table_translate(address, 0);
}
}
return true;
}
@ -632,6 +888,8 @@ void arm7_cpu_device::device_start()
save_item(NAME(m_insn_prefetch_buffer));
save_item(NAME(m_insn_prefetch_address));
save_item(NAME(m_insn_prefetch_valid));
save_item(NAME(m_tlb_log));
save_item(NAME(m_actual_log));
save_item(NAME(m_r));
save_item(NAME(m_pendingIrq));
save_item(NAME(m_pendingFiq));
@ -649,8 +907,28 @@ void arm7_cpu_device::device_start()
save_item(NAME(m_pid_offset));
save_item(NAME(m_domainAccessControl));
save_item(NAME(m_decoded_access_control));
save_item(STRUCT_MEMBER(m_dtlb_entries, valid));
save_item(STRUCT_MEMBER(m_dtlb_entries, domain));
save_item(STRUCT_MEMBER(m_dtlb_entries, access));
save_item(STRUCT_MEMBER(m_dtlb_entries, table_bits));
save_item(STRUCT_MEMBER(m_dtlb_entries, base_addr));
save_item(STRUCT_MEMBER(m_dtlb_entries, type));
save_item(STRUCT_MEMBER(m_itlb_entries, valid));
save_item(STRUCT_MEMBER(m_itlb_entries, domain));
save_item(STRUCT_MEMBER(m_itlb_entries, access));
save_item(STRUCT_MEMBER(m_itlb_entries, table_bits));
save_item(STRUCT_MEMBER(m_itlb_entries, base_addr));
save_item(STRUCT_MEMBER(m_itlb_entries, type));
save_item(NAME(m_dtlb_entry_index));
save_item(NAME(m_itlb_entry_index));
machine().save().register_postload(save_prepost_delegate(FUNC(arm7_cpu_device::postload), this));
for (uint32_t i = 0; i < 32; i++)
{
m_dtlb_entry_start[i] = i * 2;
m_itlb_entry_start[i] = i * 2;
}
set_icountptr(m_icount);
state_add( ARM7_PC, "PC", m_pc).callexport().formatstr("%08X");
@ -700,6 +978,7 @@ void arm7_cpu_device::device_start()
state_add( ARM7_UR13, "UR13", m_r[eR13_UND] ).formatstr("%08X");
state_add( ARM7_UR14, "UR14", m_r[eR14_UND] ).formatstr("%08X");
state_add( ARM7_USPSR, "UR16", m_r[eSPSR_UND]).formatstr("%08X");
state_add( ARM7_LOGTLB, "LOGTLB", m_actual_log).formatstr("%01X");
state_add(STATE_GENFLAGS, "GENFLAGS", m_r[eCPSR]).formatstr("%13s").noshow();
}
@ -780,6 +1059,11 @@ void arm7_cpu_device::device_reset()
m_r[eR15] = 0 | m_vectorbase;
m_impstate.cache_dirty = true;
memset(m_dtlb_entries, 0, sizeof(tlb_entry) * ARRAY_LENGTH(m_dtlb_entries));
memset(m_itlb_entries, 0, sizeof(tlb_entry) * ARRAY_LENGTH(m_itlb_entries));
memset(m_dtlb_entry_index, 0, ARRAY_LENGTH(m_dtlb_entry_index));
memset(m_itlb_entry_index, 0, ARRAY_LENGTH(m_itlb_entry_index));
}
void arm1176jzf_s_cpu_device::device_reset()
@ -814,7 +1098,7 @@ void arm7_cpu_device::update_insn_prefetch(uint32_t curr_pc)
uint32_t index = (i + start_index) % m_insn_prefetch_depth;
m_insn_prefetch_valid[index] = true;
offs_t physical_pc = pc;
if ((m_control & COPRO_CTRL_MMU_EN) && !arm7_tlb_translate(physical_pc, ARM7_TLB_ABORT_P | ARM7_TLB_READ))
if ((m_control & COPRO_CTRL_MMU_EN) && !translate_vaddr_to_paddr(physical_pc, ARM7_TLB_ABORT_P | ARM7_TLB_READ))
{
m_insn_prefetch_valid[index] = false;
break;
@ -854,6 +1138,8 @@ bool arm7_cpu_device::insn_fetch_arm(uint32_t pc, uint32_t &out_insn)
void arm7_cpu_device::execute_run()
{
m_tlb_log = m_actual_log;
uint32_t insn;
do
@ -1054,7 +1340,9 @@ void arm7_cpu_device::execute_run()
#endif
update_insn_prefetch(pc);
m_tlb_log = 0;
debugger_instruction_hook(pc);
m_tlb_log = m_actual_log;
/* handle Thumb instructions if active */
if (T_IS_SET(m_r[eCPSR]))
@ -1173,6 +1461,8 @@ skip_exec:
/* All instructions remove 3 cycles.. Others taking less / more will have adjusted this # prior to here */
m_icount -= 3;
} while (m_icount > 0);
m_tlb_log = 0;
}
@ -1314,7 +1604,7 @@ uint32_t arm7_cpu_device::arm7_rt_r_callback(offs_t offset)
data = COPRO_DOMAIN_ACCESS_CONTROL;
break;
case 5: // Fault Status
LOGMASKED(LOG_COPRO_READS, "arm7_rt_r_callback, Fault Status, PC = %08x\n", m_r[eR15]);
LOGMASKED(LOG_COPRO_READS, "arm7_rt_r_callback, Fault Status, PC = %08x, op3 %d, FSR0 = %08x, FSR1 = %08x\n", m_r[eR15], op3, COPRO_FAULT_STATUS_D, COPRO_FAULT_STATUS_P);
switch (op3)
{
case 0: data = COPRO_FAULT_STATUS_D; break;
@ -1322,7 +1612,7 @@ uint32_t arm7_cpu_device::arm7_rt_r_callback(offs_t offset)
}
break;
case 6: // Fault Address
LOGMASKED(LOG_COPRO_READS, "arm7_rt_r_callback, Fault Address, PC = %08x\n", m_r[eR15]);
LOGMASKED(LOG_COPRO_READS, "arm7_rt_r_callback, Fault Address, PC = %08x, FAR = %08x\n", m_r[eR15], COPRO_FAULT_ADDRESS);
data = COPRO_FAULT_ADDRESS;
break;
case 13: // Read Process ID (PID)
@ -1394,7 +1684,7 @@ void arm7_cpu_device::arm7_rt_w_callback(offs_t offset, uint32_t data)
}
if (((data & COPRO_CTRL_MMU_EN) == 0) && ((COPRO_CTRL & COPRO_CTRL_MMU_EN) != 0))
{
if (!arm7_tlb_translate( R15, 0))
if (!translate_vaddr_to_paddr( R15, 0))
{
fatalerror("ARM7_MMU_ENABLE_HACK translate failed\n");
}
@ -1432,6 +1722,54 @@ void arm7_cpu_device::arm7_rt_w_callback(offs_t offset, uint32_t data)
break;
case 8: // TLB Operations
LOGMASKED(LOG_COPRO_WRITES, "arm7_rt_w_callback TLB Ops = %08x (%d) (%d), PC = %08x\n", data, op2, op3, m_r[eR15]);
switch (op2)
{
case 0:
switch (op3)
{
case 5:
// Flush I
for (uint32_t i = 0; i < 64; i++)
{
m_itlb_entries[i].valid = false;
}
break;
case 6:
// Flush D
for (uint32_t i = 0; i < 64; i++)
{
m_dtlb_entries[i].valid = false;
}
break;
case 7:
// Flush I+D
for (uint32_t i = 0; i < 64; i++)
{
m_dtlb_entries[i].valid = false;
m_itlb_entries[i].valid = false;
}
break;
default:
LOGMASKED(LOG_COPRO_WRITES, "arm7_rt_w_callback Unsupported TLB Op\n");
break;
}
break;
case 1:
if (op3 == 6)
{
// Flush D single entry
tlb_entry *entry = tlb_map_entry(m_r[op3], ARM7_TLB_ABORT_D);
if (entry)
{
entry->valid = false;
}
}
else
{
LOGMASKED(LOG_COPRO_WRITES, "arm7_rt_w_callback Unsupported TLB Op\n");
}
break;
}
break;
case 9: // Read Buffer Operations
LOGMASKED(LOG_COPRO_WRITES, "arm7_rt_w_callback Read Buffer Ops = %08x (%d) (%d), PC = %08x\n", data, op2, op3, m_r[eR15]);
@ -1801,7 +2139,7 @@ void arm7_cpu_device::arm7_cpu_write32(uint32_t addr, uint32_t data)
{
if( COPRO_CTRL & COPRO_CTRL_MMU_EN )
{
if (!arm7_tlb_translate( addr, ARM7_TLB_ABORT_D | ARM7_TLB_WRITE ))
if (!translate_vaddr_to_paddr( addr, ARM7_TLB_ABORT_D | ARM7_TLB_WRITE ))
{
return;
}
@ -1816,7 +2154,7 @@ void arm7_cpu_device::arm7_cpu_write16(uint32_t addr, uint16_t data)
{
if( COPRO_CTRL & COPRO_CTRL_MMU_EN )
{
if (!arm7_tlb_translate( addr, ARM7_TLB_ABORT_D | ARM7_TLB_WRITE ))
if (!translate_vaddr_to_paddr( addr, ARM7_TLB_ABORT_D | ARM7_TLB_WRITE ))
{
return;
}
@ -1830,7 +2168,7 @@ void arm7_cpu_device::arm7_cpu_write8(uint32_t addr, uint8_t data)
{
if( COPRO_CTRL & COPRO_CTRL_MMU_EN )
{
if (!arm7_tlb_translate( addr, ARM7_TLB_ABORT_D | ARM7_TLB_WRITE ))
if (!translate_vaddr_to_paddr( addr, ARM7_TLB_ABORT_D | ARM7_TLB_WRITE ))
{
return;
}
@ -1845,7 +2183,7 @@ uint32_t arm7_cpu_device::arm7_cpu_read32(uint32_t addr)
if( COPRO_CTRL & COPRO_CTRL_MMU_EN )
{
if (!arm7_tlb_translate( addr, ARM7_TLB_ABORT_D | ARM7_TLB_READ ))
if (!translate_vaddr_to_paddr( addr, ARM7_TLB_ABORT_D | ARM7_TLB_READ ))
{
return 0;
}
@ -1870,7 +2208,7 @@ uint32_t arm7_cpu_device::arm7_cpu_read16(uint32_t addr)
if( COPRO_CTRL & COPRO_CTRL_MMU_EN )
{
if (!arm7_tlb_translate( addr, ARM7_TLB_ABORT_D | ARM7_TLB_READ ))
if (!translate_vaddr_to_paddr( addr, ARM7_TLB_ABORT_D | ARM7_TLB_READ ))
{
return 0;
}
@ -1890,7 +2228,7 @@ uint8_t arm7_cpu_device::arm7_cpu_read8(uint32_t addr)
{
if( COPRO_CTRL & COPRO_CTRL_MMU_EN )
{
if (!arm7_tlb_translate( addr, ARM7_TLB_ABORT_D | ARM7_TLB_READ ))
if (!translate_vaddr_to_paddr( addr, ARM7_TLB_ABORT_D | ARM7_TLB_READ ))
{
return 0;
}

View File

@ -158,6 +158,24 @@ protected:
bool m_insn_prefetch_valid[3];
const uint32_t m_prefetch_word0_shift;
const uint32_t m_prefetch_word1_shift;
int m_tlb_log;
int m_actual_log;
struct tlb_entry
{
bool valid;
uint8_t domain;
uint8_t access;
uint32_t table_bits;
uint32_t base_addr;
uint8_t type;
};
tlb_entry m_dtlb_entries[64];
tlb_entry m_itlb_entries[64];
uint8_t m_dtlb_entry_start[32];
uint8_t m_itlb_entry_start[32];
uint8_t m_dtlb_entry_index[32];
uint8_t m_itlb_entry_index[32];
bool m_pendingIrq;
bool m_pendingFiq;
@ -235,8 +253,15 @@ protected:
void arm9ops_e(uint32_t insn);
void set_cpsr(uint32_t val);
bool arm7_tlb_translate(offs_t &addr, int flags);
uint32_t arm7_tlb_get_second_level_descriptor( uint32_t granularity, uint32_t first_desc, uint32_t vaddr );
bool translate_vaddr_to_paddr(offs_t &addr, const int flags);
bool page_table_finish_translation(offs_t &vaddr, const uint8_t type, const uint32_t lvl1, const uint32_t lvl2, const int flags, const uint32_t lvl1a, const uint32_t lvl2a);
bool page_table_translate(offs_t &vaddr, const int flags);
tlb_entry *tlb_map_entry(const offs_t vaddr, const int flags);
tlb_entry *tlb_probe(const offs_t vaddr, const int flags);
uint32_t get_fault_from_permissions(const uint8_t access, const uint8_t domain, const uint8_t type, const int flags);
uint32_t tlb_check_permissions(tlb_entry *entry, const int flags);
offs_t tlb_translate(tlb_entry *entry, const offs_t vaddr);
uint32_t get_lvl2_desc_from_page_table(uint32_t granularity, uint32_t first_desc, uint32_t vaddr);
int detect_fault(int desc_lvl1, int ap, int flags);
void arm7_check_irq_state();
void update_irq_state();

View File

@ -48,7 +48,7 @@ enum
ARM7_R8, ARM7_R9, ARM7_R10, ARM7_R11, ARM7_R12, ARM7_R13, ARM7_R14, ARM7_R15,
ARM7_FR8, ARM7_FR9, ARM7_FR10, ARM7_FR11, ARM7_FR12, ARM7_FR13, ARM7_FR14,
ARM7_IR13, ARM7_IR14, ARM7_SR13, ARM7_SR14, ARM7_FSPSR, ARM7_ISPSR, ARM7_SSPSR,
ARM7_CPSR, ARM7_AR13, ARM7_AR14, ARM7_ASPSR, ARM7_UR13, ARM7_UR14, ARM7_USPSR
ARM7_CPSR, ARM7_AR13, ARM7_AR14, ARM7_ASPSR, ARM7_UR13, ARM7_UR14, ARM7_USPSR, ARM7_LOGTLB
};
/* There are 36 Unique - 32 bit processor registers */
@ -83,6 +83,19 @@ enum
};
/* Coprocessor-related macros */
#define COPRO_DOMAIN_NO_ACCESS 0
#define COPRO_DOMAIN_CLIENT 1
#define COPRO_DOMAIN_RESV 2
#define COPRO_DOMAIN_MANAGER 3
#define COPRO_FAULT_NONE 0
#define COPRO_FAULT_TRANSLATE_SECTION 5
#define COPRO_FAULT_TRANSLATE_PAGE 7
#define COPRO_FAULT_DOMAIN_SECTION 9
#define COPRO_FAULT_DOMAIN_PAGE 11
#define COPRO_FAULT_PERM_SECTION 13
#define COPRO_FAULT_PERM_PAGE 15
#define COPRO_TLB_BASE m_tlbBase
#define COPRO_TLB_BASE_MASK 0xffffc000
#define COPRO_TLB_VADDR_FLTI_MASK 0xfff00000
@ -99,6 +112,9 @@ enum
#define COPRO_TLB_LARGE_PAGE_MASK 0xffff0000
#define COPRO_TLB_SMALL_PAGE_MASK 0xfffff000
#define COPRO_TLB_TINY_PAGE_MASK 0xfffffc00
#define COPRO_TLB_STABLE_MASK 0xfff00000
#define COPRO_TLB_LSTABLE_MASK 0xfffff000
#define COPRO_TLB_TTABLE_MASK 0xfffffc00
#define COPRO_TLB_UNMAPPED 0
#define COPRO_TLB_LARGE_PAGE 1
#define COPRO_TLB_SMALL_PAGE 2
@ -106,6 +122,10 @@ enum
#define COPRO_TLB_COARSE_TABLE 1
#define COPRO_TLB_SECTION_TABLE 2
#define COPRO_TLB_FINE_TABLE 3
#define COPRO_TLB_TYPE_SECTION 0
#define COPRO_TLB_TYPE_LARGE 1
#define COPRO_TLB_TYPE_SMALL 2
#define COPRO_TLB_TYPE_TINY 3
#define COPRO_CTRL m_control
#define COPRO_CTRL_MMU_EN 0x00000001