nkeynes@550: /** nkeynes@586: * $Id$ nkeynes@826: * nkeynes@939: * SH4 MMU implementation based on address space page maps. This module nkeynes@939: * is responsible for all address decoding functions. nkeynes@550: * nkeynes@550: * Copyright (c) 2005 Nathan Keynes. nkeynes@550: * nkeynes@550: * This program is free software; you can redistribute it and/or modify nkeynes@550: * it under the terms of the GNU General Public License as published by nkeynes@550: * the Free Software Foundation; either version 2 of the License, or nkeynes@550: * (at your option) any later version. nkeynes@550: * nkeynes@550: * This program is distributed in the hope that it will be useful, nkeynes@550: * but WITHOUT ANY WARRANTY; without even the implied warranty of nkeynes@550: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the nkeynes@550: * GNU General Public License for more details. nkeynes@550: */ nkeynes@550: #define MODULE sh4_module nkeynes@550: nkeynes@550: #include nkeynes@915: #include nkeynes@550: #include "sh4/sh4mmio.h" nkeynes@550: #include "sh4/sh4core.h" nkeynes@669: #include "sh4/sh4trans.h" nkeynes@934: #include "dreamcast.h" nkeynes@550: #include "mem.h" nkeynes@931: #include "mmu.h" nkeynes@550: nkeynes@939: /* An entry is a 1K entry if it's one of the mmu_utlb_1k_pages entries */ nkeynes@939: #define IS_1K_PAGE_ENTRY(ent) ( ((uintptr_t)(((struct utlb_1k_entry *)ent) - &mmu_utlb_1k_pages[0])) < UTLB_ENTRY_COUNT ) nkeynes@586: nkeynes@939: /* Primary address space (used directly by SH4 cores) */ nkeynes@939: mem_region_fn_t *sh4_address_space; nkeynes@939: mem_region_fn_t *sh4_user_address_space; nkeynes@550: nkeynes@1217: /* External address space (usually the same as the global ext_address_space) */ nkeynes@1217: static mem_region_fn_t *sh4_ext_address_space; nkeynes@1217: nkeynes@939: /* Accessed from the UTLB accessor methods */ nkeynes@939: uint32_t mmu_urc; nkeynes@939: uint32_t mmu_urb; nkeynes@952: static gboolean mmu_urc_overflow; /* If true, urc was set >= urb */ nkeynes@939: nkeynes@939: /* Module globals */ nkeynes@550: static struct itlb_entry mmu_itlb[ITLB_ENTRY_COUNT]; nkeynes@550: static struct utlb_entry mmu_utlb[UTLB_ENTRY_COUNT]; nkeynes@939: static struct utlb_page_entry mmu_utlb_pages[UTLB_ENTRY_COUNT]; nkeynes@550: static uint32_t mmu_lrui; nkeynes@586: static uint32_t mmu_asid; // current asid nkeynes@946: static struct utlb_default_regions *mmu_user_storequeue_regions; nkeynes@550: nkeynes@939: /* Structures for 1K page handling */ nkeynes@939: static struct utlb_1k_entry mmu_utlb_1k_pages[UTLB_ENTRY_COUNT]; nkeynes@939: static int mmu_utlb_1k_free_list[UTLB_ENTRY_COUNT]; nkeynes@939: static int mmu_utlb_1k_free_index; nkeynes@915: nkeynes@550: nkeynes@939: /* Function prototypes */ nkeynes@550: static void mmu_invalidate_tlb(); nkeynes@939: static void mmu_utlb_register_all(); nkeynes@939: static void mmu_utlb_remove_entry(int); nkeynes@939: static void mmu_utlb_insert_entry(int); nkeynes@939: static void mmu_register_mem_region( uint32_t start, uint32_t end, mem_region_fn_t fn ); nkeynes@939: static void mmu_register_user_mem_region( uint32_t start, uint32_t end, mem_region_fn_t fn ); nkeynes@939: static void mmu_set_tlb_enabled( int tlb_on ); nkeynes@939: static void mmu_set_tlb_asid( uint32_t asid ); nkeynes@946: static void mmu_set_storequeue_protected( int protected, int tlb_on ); nkeynes@939: static gboolean mmu_utlb_map_pages( mem_region_fn_t priv_page, mem_region_fn_t user_page, sh4addr_t start_addr, int npages ); nkeynes@943: static void mmu_utlb_remap_pages( gboolean remap_priv, gboolean remap_user, int entryNo ); nkeynes@943: static gboolean mmu_utlb_unmap_pages( gboolean unmap_priv, gboolean unmap_user, sh4addr_t start_addr, int npages ); nkeynes@939: static gboolean mmu_ext_page_remapped( sh4addr_t page, mem_region_fn_t fn, void *user_data ); nkeynes@939: static void mmu_utlb_1k_init(); nkeynes@939: static struct utlb_1k_entry *mmu_utlb_1k_alloc(); nkeynes@939: static void mmu_utlb_1k_free( struct utlb_1k_entry *entry ); nkeynes@955: static int mmu_read_urc(); nkeynes@550: nkeynes@946: static void FASTCALL tlb_miss_read( sh4addr_t addr, void *exc ); nkeynes@939: static int32_t FASTCALL tlb_protected_read( sh4addr_t addr, void *exc ); nkeynes@939: static void FASTCALL tlb_protected_write( sh4addr_t addr, uint32_t val, void *exc ); nkeynes@975: static int32_t FASTCALL tlb_protected_read_for_write( sh4addr_t addr, void *exc ); nkeynes@939: static void FASTCALL tlb_initial_write( sh4addr_t addr, uint32_t val, void *exc ); nkeynes@975: static int32_t FASTCALL tlb_initial_read_for_write( sh4addr_t addr, void *exc ); nkeynes@939: static uint32_t get_tlb_size_mask( uint32_t flags ); nkeynes@939: static uint32_t get_tlb_size_pages( uint32_t flags ); nkeynes@586: nkeynes@946: #define DEFAULT_REGIONS 0 nkeynes@946: #define DEFAULT_STOREQUEUE_REGIONS 1 nkeynes@946: #define DEFAULT_STOREQUEUE_SQMD_REGIONS 2 nkeynes@946: nkeynes@946: static struct utlb_default_regions mmu_default_regions[3] = { nkeynes@946: { &mem_region_tlb_miss, &mem_region_tlb_protected, &mem_region_tlb_multihit }, nkeynes@946: { &p4_region_storequeue_miss, &p4_region_storequeue_protected, &p4_region_storequeue_multihit }, nkeynes@946: { &p4_region_storequeue_sqmd_miss, &p4_region_storequeue_sqmd_protected, &p4_region_storequeue_sqmd_multihit } }; nkeynes@946: nkeynes@946: #define IS_STOREQUEUE_PROTECTED() (mmu_user_storequeue_regions == &mmu_default_regions[DEFAULT_STOREQUEUE_SQMD_REGIONS]) nkeynes@550: nkeynes@1217: #ifndef SH4_TRANSLATOR nkeynes@1217: /* Dummy MMU vtable functions */ nkeynes@1217: void mmu_utlb_init_vtable( struct utlb_entry *ent, struct utlb_page_entry *page, gboolean writable ) nkeynes@1217: { nkeynes@1217: } nkeynes@1217: void mmu_utlb_init_storequeue_vtable( struct utlb_entry *ent, struct utlb_page_entry *page ) nkeynes@1217: { nkeynes@1217: } nkeynes@1217: void mmu_utlb_1k_init_vtable( struct utlb_1k_entry *entry ) nkeynes@1217: { nkeynes@1217: } nkeynes@1217: #endif nkeynes@1217: nkeynes@939: /*********************** Module public functions ****************************/ nkeynes@550: nkeynes@939: /** nkeynes@939: * Allocate memory for the address space maps, and initialize them according nkeynes@939: * to the default (reset) values. (TLB is disabled by default) nkeynes@939: */ nkeynes@939: nkeynes@826: void MMU_init() nkeynes@550: { nkeynes@1217: sh4_ext_address_space = ext_address_space; nkeynes@939: sh4_address_space = mem_alloc_pages( sizeof(mem_region_fn_t) * 256 ); nkeynes@939: sh4_user_address_space = mem_alloc_pages( sizeof(mem_region_fn_t) * 256 ); nkeynes@946: mmu_user_storequeue_regions = &mmu_default_regions[DEFAULT_STOREQUEUE_REGIONS]; nkeynes@939: nkeynes@939: mmu_set_tlb_enabled(0); nkeynes@939: mmu_register_user_mem_region( 0x80000000, 0x00000000, &mem_region_address_error ); nkeynes@946: mmu_register_user_mem_region( 0xE0000000, 0xE4000000, &p4_region_storequeue ); nkeynes@939: nkeynes@939: /* Setup P4 tlb/cache access regions */ nkeynes@939: mmu_register_mem_region( 0xE0000000, 0xE4000000, &p4_region_storequeue ); nkeynes@939: mmu_register_mem_region( 0xE4000000, 0xF0000000, &mem_region_unmapped ); nkeynes@939: mmu_register_mem_region( 0xF0000000, 0xF1000000, &p4_region_icache_addr ); nkeynes@939: mmu_register_mem_region( 0xF1000000, 0xF2000000, &p4_region_icache_data ); nkeynes@939: mmu_register_mem_region( 0xF2000000, 0xF3000000, &p4_region_itlb_addr ); nkeynes@939: mmu_register_mem_region( 0xF3000000, 0xF4000000, &p4_region_itlb_data ); nkeynes@939: mmu_register_mem_region( 0xF4000000, 0xF5000000, &p4_region_ocache_addr ); nkeynes@939: mmu_register_mem_region( 0xF5000000, 0xF6000000, &p4_region_ocache_data ); nkeynes@939: mmu_register_mem_region( 0xF6000000, 0xF7000000, &p4_region_utlb_addr ); nkeynes@939: mmu_register_mem_region( 0xF7000000, 0xF8000000, &p4_region_utlb_data ); nkeynes@939: mmu_register_mem_region( 0xF8000000, 0x00000000, &mem_region_unmapped ); nkeynes@939: nkeynes@939: /* Setup P4 control region */ nkeynes@939: mmu_register_mem_region( 0xFF000000, 0xFF001000, &mmio_region_MMU.fn ); nkeynes@939: mmu_register_mem_region( 0xFF100000, 0xFF101000, &mmio_region_PMM.fn ); nkeynes@939: mmu_register_mem_region( 0xFF200000, 0xFF201000, &mmio_region_UBC.fn ); nkeynes@939: mmu_register_mem_region( 0xFF800000, 0xFF801000, &mmio_region_BSC.fn ); nkeynes@939: mmu_register_mem_region( 0xFF900000, 0xFFA00000, &mem_region_unmapped ); // SDMR2 + SDMR3 nkeynes@939: mmu_register_mem_region( 0xFFA00000, 0xFFA01000, &mmio_region_DMAC.fn ); nkeynes@939: mmu_register_mem_region( 0xFFC00000, 0xFFC01000, &mmio_region_CPG.fn ); nkeynes@939: mmu_register_mem_region( 0xFFC80000, 0xFFC81000, &mmio_region_RTC.fn ); nkeynes@939: mmu_register_mem_region( 0xFFD00000, 0xFFD01000, &mmio_region_INTC.fn ); nkeynes@939: mmu_register_mem_region( 0xFFD80000, 0xFFD81000, &mmio_region_TMU.fn ); nkeynes@939: mmu_register_mem_region( 0xFFE00000, 0xFFE01000, &mmio_region_SCI.fn ); nkeynes@939: mmu_register_mem_region( 0xFFE80000, 0xFFE81000, &mmio_region_SCIF.fn ); nkeynes@939: mmu_register_mem_region( 0xFFF00000, 0xFFF01000, &mem_region_unmapped ); // H-UDI nkeynes@939: nkeynes@939: register_mem_page_remapped_hook( mmu_ext_page_remapped, NULL ); nkeynes@939: mmu_utlb_1k_init(); nkeynes@939: nkeynes@1173: /* Ensure the code regions are executable. Although it might nkeynes@960: * be more portable to mmap these at runtime rather than using static decls nkeynes@960: */ nkeynes@939: mem_unprotect( mmu_utlb_pages, sizeof(mmu_utlb_pages) ); nkeynes@939: mem_unprotect( mmu_utlb_1k_pages, sizeof(mmu_utlb_1k_pages) ); nkeynes@550: } nkeynes@550: nkeynes@550: void MMU_reset() nkeynes@550: { nkeynes@550: mmio_region_MMU_write( CCR, 0 ); nkeynes@586: mmio_region_MMU_write( MMUCR, 0 ); nkeynes@550: } nkeynes@550: nkeynes@550: void MMU_save_state( FILE *f ) nkeynes@550: { nkeynes@955: mmu_read_urc(); nkeynes@550: fwrite( &mmu_itlb, sizeof(mmu_itlb), 1, f ); nkeynes@550: fwrite( &mmu_utlb, sizeof(mmu_utlb), 1, f ); nkeynes@586: fwrite( &mmu_urc, sizeof(mmu_urc), 1, f ); nkeynes@586: fwrite( &mmu_urb, sizeof(mmu_urb), 1, f ); nkeynes@586: fwrite( &mmu_lrui, sizeof(mmu_lrui), 1, f ); nkeynes@586: fwrite( &mmu_asid, sizeof(mmu_asid), 1, f ); nkeynes@550: } nkeynes@550: nkeynes@550: int MMU_load_state( FILE *f ) nkeynes@550: { nkeynes@550: if( fread( &mmu_itlb, sizeof(mmu_itlb), 1, f ) != 1 ) { nkeynes@736: return 1; nkeynes@550: } nkeynes@550: if( fread( &mmu_utlb, sizeof(mmu_utlb), 1, f ) != 1 ) { nkeynes@736: return 1; nkeynes@550: } nkeynes@586: if( fread( &mmu_urc, sizeof(mmu_urc), 1, f ) != 1 ) { nkeynes@736: return 1; nkeynes@586: } nkeynes@586: if( fread( &mmu_urc, sizeof(mmu_urb), 1, f ) != 1 ) { nkeynes@736: return 1; nkeynes@586: } nkeynes@586: if( fread( &mmu_lrui, sizeof(mmu_lrui), 1, f ) != 1 ) { nkeynes@736: return 1; nkeynes@586: } nkeynes@586: if( fread( &mmu_asid, sizeof(mmu_asid), 1, f ) != 1 ) { nkeynes@736: return 1; nkeynes@586: } nkeynes@939: nkeynes@939: uint32_t mmucr = MMIO_READ(MMU,MMUCR); nkeynes@952: mmu_urc_overflow = mmu_urc >= mmu_urb; nkeynes@939: mmu_set_tlb_enabled(mmucr&MMUCR_AT); nkeynes@946: mmu_set_storequeue_protected(mmucr&MMUCR_SQMD, mmucr&MMUCR_AT); nkeynes@550: return 0; nkeynes@550: } nkeynes@550: nkeynes@550: /** nkeynes@550: * LDTLB instruction implementation. Copies PTEH, PTEL and PTEA into the UTLB nkeynes@550: * entry identified by MMUCR.URC. Does not modify MMUCR or the ITLB. nkeynes@550: */ nkeynes@550: void MMU_ldtlb() nkeynes@550: { nkeynes@955: int urc = mmu_read_urc(); nkeynes@1090: if( IS_TLB_ENABLED() && mmu_utlb[urc].flags & TLB_VALID ) nkeynes@955: mmu_utlb_remove_entry( urc ); nkeynes@955: mmu_utlb[urc].vpn = MMIO_READ(MMU, PTEH) & 0xFFFFFC00; nkeynes@955: mmu_utlb[urc].asid = MMIO_READ(MMU, PTEH) & 0x000000FF; nkeynes@955: mmu_utlb[urc].ppn = MMIO_READ(MMU, PTEL) & 0x1FFFFC00; nkeynes@955: mmu_utlb[urc].flags = MMIO_READ(MMU, PTEL) & 0x00001FF; nkeynes@955: mmu_utlb[urc].pcmcia = MMIO_READ(MMU, PTEA); nkeynes@955: mmu_utlb[urc].mask = get_tlb_size_mask(mmu_utlb[urc].flags); nkeynes@1090: if( IS_TLB_ENABLED() && mmu_utlb[urc].flags & TLB_VALID ) nkeynes@955: mmu_utlb_insert_entry( urc ); nkeynes@550: } nkeynes@550: nkeynes@939: nkeynes@939: MMIO_REGION_READ_FN( MMU, reg ) nkeynes@939: { nkeynes@939: reg &= 0xFFF; nkeynes@939: switch( reg ) { nkeynes@939: case MMUCR: nkeynes@955: return MMIO_READ( MMU, MMUCR) | (mmu_read_urc()<<10) | ((mmu_urb&0x3F)<<18) | (mmu_lrui<<26); nkeynes@939: default: nkeynes@939: return MMIO_READ( MMU, reg ); nkeynes@939: } nkeynes@939: } nkeynes@939: nkeynes@975: MMIO_REGION_READ_DEFSUBFNS(MMU) nkeynes@975: nkeynes@939: MMIO_REGION_WRITE_FN( MMU, reg, val ) nkeynes@939: { nkeynes@939: uint32_t tmp; nkeynes@939: reg &= 0xFFF; nkeynes@939: switch(reg) { nkeynes@939: case SH4VER: nkeynes@939: return; nkeynes@939: case PTEH: nkeynes@939: val &= 0xFFFFFCFF; nkeynes@939: if( (val & 0xFF) != mmu_asid ) { nkeynes@939: mmu_set_tlb_asid( val&0xFF ); nkeynes@939: } nkeynes@939: break; nkeynes@939: case PTEL: nkeynes@939: val &= 0x1FFFFDFF; nkeynes@939: break; nkeynes@939: case PTEA: nkeynes@939: val &= 0x0000000F; nkeynes@939: break; nkeynes@939: case TRA: nkeynes@939: val &= 0x000003FC; nkeynes@939: break; nkeynes@939: case EXPEVT: nkeynes@939: case INTEVT: nkeynes@939: val &= 0x00000FFF; nkeynes@939: break; nkeynes@939: case MMUCR: nkeynes@939: if( val & MMUCR_TI ) { nkeynes@939: mmu_invalidate_tlb(); nkeynes@939: } nkeynes@939: mmu_urc = (val >> 10) & 0x3F; nkeynes@939: mmu_urb = (val >> 18) & 0x3F; nkeynes@939: if( mmu_urb == 0 ) { nkeynes@939: mmu_urb = 0x40; nkeynes@952: } else if( mmu_urc >= mmu_urb ) { nkeynes@952: mmu_urc_overflow = TRUE; nkeynes@939: } nkeynes@939: mmu_lrui = (val >> 26) & 0x3F; nkeynes@939: val &= 0x00000301; nkeynes@939: tmp = MMIO_READ( MMU, MMUCR ); nkeynes@939: if( (val ^ tmp) & (MMUCR_SQMD) ) { nkeynes@946: mmu_set_storequeue_protected( val & MMUCR_SQMD, val&MMUCR_AT ); nkeynes@939: } nkeynes@939: if( (val ^ tmp) & (MMUCR_AT) ) { nkeynes@939: // AT flag has changed state - flush the xlt cache as all bets nkeynes@939: // are off now. We also need to force an immediate exit from the nkeynes@939: // current block nkeynes@939: mmu_set_tlb_enabled( val & MMUCR_AT ); nkeynes@939: MMIO_WRITE( MMU, MMUCR, val ); nkeynes@948: sh4_core_exit( CORE_EXIT_FLUSH_ICACHE ); nkeynes@948: xlat_flush_cache(); // If we're not running, flush the cache anyway nkeynes@939: } nkeynes@939: break; nkeynes@939: case CCR: nkeynes@939: CCN_set_cache_control( val ); nkeynes@939: val &= 0x81A7; nkeynes@939: break; nkeynes@939: case MMUUNK1: nkeynes@939: /* Note that if the high bit is set, this appears to reset the machine. nkeynes@939: * Not emulating this behaviour yet until we know why... nkeynes@939: */ nkeynes@939: val &= 0x00010007; nkeynes@939: break; nkeynes@939: case QACR0: nkeynes@939: case QACR1: nkeynes@939: val &= 0x0000001C; nkeynes@939: break; nkeynes@939: case PMCR1: nkeynes@939: PMM_write_control(0, val); nkeynes@939: val &= 0x0000C13F; nkeynes@939: break; nkeynes@939: case PMCR2: nkeynes@939: PMM_write_control(1, val); nkeynes@939: val &= 0x0000C13F; nkeynes@939: break; nkeynes@939: default: nkeynes@939: break; nkeynes@939: } nkeynes@939: MMIO_WRITE( MMU, reg, val ); nkeynes@939: } nkeynes@939: nkeynes@939: /********************** 1K Page handling ***********************/ nkeynes@939: /* Since we use 4K pages as our native page size, 1K pages need a bit of extra nkeynes@939: * effort to manage - we justify this on the basis that most programs won't nkeynes@939: * actually use 1K pages, so we may as well optimize for the common case. nkeynes@939: * nkeynes@939: * Implementation uses an intermediate page entry (the utlb_1k_entry) that nkeynes@939: * redirects requests to the 'real' page entry. These are allocated on an nkeynes@939: * as-needed basis, and returned to the pool when all subpages are empty. nkeynes@939: */ nkeynes@939: static void mmu_utlb_1k_init() nkeynes@939: { nkeynes@939: int i; nkeynes@939: for( i=0; i 0 ); nkeynes@939: mmu_utlb_1k_free_list[--mmu_utlb_1k_free_index] = entryNo; nkeynes@939: } nkeynes@939: nkeynes@939: nkeynes@939: /********************** Address space maintenance *************************/ nkeynes@939: nkeynes@1217: mem_region_fn_t *mmu_set_ext_address_space( mem_region_fn_t *ext ) nkeynes@1217: { nkeynes@1217: mem_region_fn_t *old_ext = sh4_ext_address_space; nkeynes@1217: sh4_ext_address_space = ext; nkeynes@1217: mmu_set_tlb_enabled(IS_TLB_ENABLED()); nkeynes@1217: return old_ext; nkeynes@1217: } nkeynes@1217: nkeynes@939: /** nkeynes@939: * MMU accessor functions just increment URC - fixup here if necessary nkeynes@939: */ nkeynes@955: static int mmu_read_urc() nkeynes@939: { nkeynes@952: if( mmu_urc_overflow ) { nkeynes@952: if( mmu_urc >= 0x40 ) { nkeynes@952: mmu_urc_overflow = FALSE; nkeynes@952: mmu_urc -= 0x40; nkeynes@952: mmu_urc %= mmu_urb; nkeynes@952: } nkeynes@952: } else { nkeynes@952: mmu_urc %= mmu_urb; nkeynes@952: } nkeynes@955: return mmu_urc; nkeynes@939: } nkeynes@939: nkeynes@939: static void mmu_register_mem_region( uint32_t start, uint32_t end, mem_region_fn_t fn ) nkeynes@939: { nkeynes@939: int count = (end - start) >> 12; nkeynes@939: mem_region_fn_t *ptr = &sh4_address_space[start>>12]; nkeynes@939: while( count-- > 0 ) { nkeynes@939: *ptr++ = fn; nkeynes@939: } nkeynes@939: } nkeynes@939: static void mmu_register_user_mem_region( uint32_t start, uint32_t end, mem_region_fn_t fn ) nkeynes@939: { nkeynes@939: int count = (end - start) >> 12; nkeynes@939: mem_region_fn_t *ptr = &sh4_user_address_space[start>>12]; nkeynes@939: while( count-- > 0 ) { nkeynes@939: *ptr++ = fn; nkeynes@939: } nkeynes@939: } nkeynes@939: nkeynes@939: static gboolean mmu_ext_page_remapped( sh4addr_t page, mem_region_fn_t fn, void *user_data ) nkeynes@939: { nkeynes@980: unsigned int i; nkeynes@939: if( (MMIO_READ(MMU,MMUCR)) & MMUCR_AT ) { nkeynes@939: /* TLB on */ nkeynes@939: sh4_address_space[(page|0x80000000)>>12] = fn; /* Direct map to P1 and P2 */ nkeynes@939: sh4_address_space[(page|0xA0000000)>>12] = fn; nkeynes@939: /* Scan UTLB and update any direct-referencing entries */ nkeynes@939: } else { nkeynes@939: /* Direct map to U0, P0, P1, P2, P3 */ nkeynes@939: for( i=0; i<= 0xC0000000; i+= 0x20000000 ) { nkeynes@939: sh4_address_space[(page|i)>>12] = fn; nkeynes@939: } nkeynes@939: for( i=0; i < 0x80000000; i+= 0x20000000 ) { nkeynes@939: sh4_user_address_space[(page|i)>>12] = fn; nkeynes@939: } nkeynes@939: } nkeynes@963: return TRUE; nkeynes@939: } nkeynes@939: nkeynes@939: static void mmu_set_tlb_enabled( int tlb_on ) nkeynes@939: { nkeynes@939: mem_region_fn_t *ptr, *uptr; nkeynes@939: int i; nkeynes@939: nkeynes@946: /* Reset the storequeue area */ nkeynes@946: nkeynes@939: if( tlb_on ) { nkeynes@939: mmu_register_mem_region(0x00000000, 0x80000000, &mem_region_tlb_miss ); nkeynes@939: mmu_register_mem_region(0xC0000000, 0xE0000000, &mem_region_tlb_miss ); nkeynes@939: mmu_register_user_mem_region(0x00000000, 0x80000000, &mem_region_tlb_miss ); nkeynes@946: nkeynes@946: /* Default SQ prefetch goes to TLB miss (?) */ nkeynes@946: mmu_register_mem_region( 0xE0000000, 0xE4000000, &p4_region_storequeue_miss ); nkeynes@946: mmu_register_user_mem_region( 0xE0000000, 0xE4000000, mmu_user_storequeue_regions->tlb_miss ); nkeynes@939: mmu_utlb_register_all(); nkeynes@939: } else { nkeynes@939: for( i=0, ptr = sh4_address_space; i<7; i++, ptr += LXDREAM_PAGE_TABLE_ENTRIES ) { nkeynes@1217: memcpy( ptr, sh4_ext_address_space, sizeof(mem_region_fn_t) * LXDREAM_PAGE_TABLE_ENTRIES ); nkeynes@939: } nkeynes@939: for( i=0, ptr = sh4_user_address_space; i<4; i++, ptr += LXDREAM_PAGE_TABLE_ENTRIES ) { nkeynes@1217: memcpy( ptr, sh4_ext_address_space, sizeof(mem_region_fn_t) * LXDREAM_PAGE_TABLE_ENTRIES ); nkeynes@939: } nkeynes@946: nkeynes@946: mmu_register_mem_region( 0xE0000000, 0xE4000000, &p4_region_storequeue ); nkeynes@946: if( IS_STOREQUEUE_PROTECTED() ) { nkeynes@946: mmu_register_user_mem_region( 0xE0000000, 0xE4000000, &p4_region_storequeue_sqmd ); nkeynes@946: } else { nkeynes@946: mmu_register_user_mem_region( 0xE0000000, 0xE4000000, &p4_region_storequeue ); nkeynes@946: } nkeynes@939: } nkeynes@946: nkeynes@939: } nkeynes@939: nkeynes@946: /** nkeynes@946: * Flip the SQMD switch - this is rather expensive, so will need to be changed if nkeynes@946: * anything expects to do this frequently. nkeynes@946: */ nkeynes@946: static void mmu_set_storequeue_protected( int protected, int tlb_on ) nkeynes@939: { nkeynes@946: mem_region_fn_t nontlb_region; nkeynes@946: int i; nkeynes@946: nkeynes@939: if( protected ) { nkeynes@946: mmu_user_storequeue_regions = &mmu_default_regions[DEFAULT_STOREQUEUE_SQMD_REGIONS]; nkeynes@946: nontlb_region = &p4_region_storequeue_sqmd; nkeynes@939: } else { nkeynes@946: mmu_user_storequeue_regions = &mmu_default_regions[DEFAULT_STOREQUEUE_REGIONS]; nkeynes@946: nontlb_region = &p4_region_storequeue; nkeynes@939: } nkeynes@946: nkeynes@946: if( tlb_on ) { nkeynes@946: mmu_register_user_mem_region( 0xE0000000, 0xE4000000, mmu_user_storequeue_regions->tlb_miss ); nkeynes@946: for( i=0; i> 12]; nkeynes@939: mem_region_fn_t *uptr = &sh4_user_address_space[start_addr >> 12]; nkeynes@946: struct utlb_default_regions *privdefs = &mmu_default_regions[DEFAULT_REGIONS]; nkeynes@946: struct utlb_default_regions *userdefs = privdefs; nkeynes@946: nkeynes@939: gboolean mapping_ok = TRUE; nkeynes@939: int i; nkeynes@939: nkeynes@939: if( (start_addr & 0xFC000000) == 0xE0000000 ) { nkeynes@939: /* Storequeue mapping */ nkeynes@946: privdefs = &mmu_default_regions[DEFAULT_STOREQUEUE_REGIONS]; nkeynes@946: userdefs = mmu_user_storequeue_regions; nkeynes@939: } else if( (start_addr & 0xE0000000) == 0xC0000000 ) { nkeynes@939: user_page = NULL; /* No user access to P3 region */ nkeynes@939: } else if( start_addr >= 0x80000000 ) { nkeynes@939: return TRUE; // No mapping - legal but meaningless nkeynes@939: } nkeynes@939: nkeynes@939: if( npages == 0 ) { nkeynes@939: struct utlb_1k_entry *ent; nkeynes@939: int i, idx = (start_addr >> 10) & 0x03; nkeynes@939: if( IS_1K_PAGE_ENTRY(*ptr) ) { nkeynes@939: ent = (struct utlb_1k_entry *)*ptr; nkeynes@939: } else { nkeynes@939: ent = mmu_utlb_1k_alloc(); nkeynes@939: /* New 1K struct - init to previous contents of region */ nkeynes@939: for( i=0; i<4; i++ ) { nkeynes@939: ent->subpages[i] = *ptr; nkeynes@939: ent->user_subpages[i] = *uptr; nkeynes@939: } nkeynes@939: *ptr = &ent->fn; nkeynes@939: *uptr = &ent->user_fn; nkeynes@939: } nkeynes@939: nkeynes@939: if( priv_page != NULL ) { nkeynes@946: if( ent->subpages[idx] == privdefs->tlb_miss ) { nkeynes@939: ent->subpages[idx] = priv_page; nkeynes@939: } else { nkeynes@939: mapping_ok = FALSE; nkeynes@946: ent->subpages[idx] = privdefs->tlb_multihit; nkeynes@939: } nkeynes@939: } nkeynes@939: if( user_page != NULL ) { nkeynes@946: if( ent->user_subpages[idx] == userdefs->tlb_miss ) { nkeynes@939: ent->user_subpages[idx] = user_page; nkeynes@939: } else { nkeynes@939: mapping_ok = FALSE; nkeynes@946: ent->user_subpages[idx] = userdefs->tlb_multihit; nkeynes@939: } nkeynes@939: } nkeynes@939: nkeynes@939: } else { nkeynes@943: if( priv_page != NULL ) { nkeynes@946: /* Privileged mapping only */ nkeynes@946: for( i=0; itlb_miss ) { nkeynes@946: *ptr++ = priv_page; nkeynes@946: } else { nkeynes@946: mapping_ok = FALSE; nkeynes@946: *ptr++ = privdefs->tlb_multihit; nkeynes@939: } nkeynes@939: } nkeynes@946: } nkeynes@946: if( user_page != NULL ) { nkeynes@943: /* User mapping only (eg ASID change remap w/ SV=1) */ nkeynes@939: for( i=0; itlb_miss ) { nkeynes@939: *uptr++ = user_page; nkeynes@939: } else { nkeynes@939: mapping_ok = FALSE; nkeynes@946: *uptr++ = userdefs->tlb_multihit; nkeynes@939: } nkeynes@939: } nkeynes@939: } nkeynes@939: } nkeynes@946: nkeynes@939: return mapping_ok; nkeynes@939: } nkeynes@939: nkeynes@939: /** nkeynes@943: * Remap any pages within the region covered by entryNo, but not including nkeynes@943: * entryNo itself. This is used to reestablish pages that were previously nkeynes@943: * covered by a multi-hit exception region when one of the pages is removed. nkeynes@943: */ nkeynes@943: static void mmu_utlb_remap_pages( gboolean remap_priv, gboolean remap_user, int entryNo ) nkeynes@943: { nkeynes@943: int mask = mmu_utlb[entryNo].mask; nkeynes@943: uint32_t remap_addr = mmu_utlb[entryNo].vpn & mask; nkeynes@943: int i; nkeynes@943: nkeynes@943: for( i=0; i= mask ) { nkeynes@943: /* entry is no larger than the area we're replacing - map completely */ nkeynes@943: start_addr = mmu_utlb[i].vpn & mmu_utlb[i].mask; nkeynes@943: npages = get_tlb_size_pages( mmu_utlb[i].flags ); nkeynes@943: } else { nkeynes@943: /* Otherwise map subset - region covered by removed page */ nkeynes@943: start_addr = remap_addr; nkeynes@943: npages = get_tlb_size_pages( mmu_utlb[entryNo].flags ); nkeynes@943: } nkeynes@943: nkeynes@943: if( (mmu_utlb[i].flags & TLB_SHARE) || mmu_utlb[i].asid == mmu_asid ) { nkeynes@943: mmu_utlb_map_pages( priv_page, user_page, start_addr, npages ); nkeynes@943: } else if( IS_SV_ENABLED() ) { nkeynes@943: mmu_utlb_map_pages( priv_page, NULL, start_addr, npages ); nkeynes@943: } nkeynes@943: nkeynes@943: } nkeynes@943: } nkeynes@943: } nkeynes@943: nkeynes@943: /** nkeynes@939: * Remove a previous TLB mapping (replacing them with the TLB miss region). nkeynes@939: * @return FALSE if any pages were previously mapped to the TLB multihit page, nkeynes@939: * otherwise TRUE. In either case, all pages in the region are cleared to TLB miss. nkeynes@939: */ nkeynes@943: static gboolean mmu_utlb_unmap_pages( gboolean unmap_priv, gboolean unmap_user, sh4addr_t start_addr, int npages ) nkeynes@939: { nkeynes@939: mem_region_fn_t *ptr = &sh4_address_space[start_addr >> 12]; nkeynes@939: mem_region_fn_t *uptr = &sh4_user_address_space[start_addr >> 12]; nkeynes@946: struct utlb_default_regions *privdefs = &mmu_default_regions[DEFAULT_REGIONS]; nkeynes@946: struct utlb_default_regions *userdefs = privdefs; nkeynes@946: nkeynes@939: gboolean unmapping_ok = TRUE; nkeynes@939: int i; nkeynes@939: nkeynes@939: if( (start_addr & 0xFC000000) == 0xE0000000 ) { nkeynes@939: /* Storequeue mapping */ nkeynes@946: privdefs = &mmu_default_regions[DEFAULT_STOREQUEUE_REGIONS]; nkeynes@946: userdefs = mmu_user_storequeue_regions; nkeynes@939: } else if( (start_addr & 0xE0000000) == 0xC0000000 ) { nkeynes@939: unmap_user = FALSE; nkeynes@939: } else if( start_addr >= 0x80000000 ) { nkeynes@939: return TRUE; // No mapping - legal but meaningless nkeynes@939: } nkeynes@939: nkeynes@939: if( npages == 0 ) { // 1K page nkeynes@939: assert( IS_1K_PAGE_ENTRY( *ptr ) ); nkeynes@939: struct utlb_1k_entry *ent = (struct utlb_1k_entry *)*ptr; nkeynes@939: int i, idx = (start_addr >> 10) & 0x03, mergeable=1; nkeynes@946: if( ent->subpages[idx] == privdefs->tlb_multihit ) { nkeynes@939: unmapping_ok = FALSE; nkeynes@939: } nkeynes@943: if( unmap_priv ) nkeynes@946: ent->subpages[idx] = privdefs->tlb_miss; nkeynes@943: if( unmap_user ) nkeynes@946: ent->user_subpages[idx] = userdefs->tlb_miss; nkeynes@939: nkeynes@939: /* If all 4 subpages have the same content, merge them together and nkeynes@939: * release the 1K entry nkeynes@939: */ nkeynes@939: mem_region_fn_t priv_page = ent->subpages[0]; nkeynes@939: mem_region_fn_t user_page = ent->user_subpages[0]; nkeynes@939: for( i=1; i<4; i++ ) { nkeynes@939: if( priv_page != ent->subpages[i] || user_page != ent->user_subpages[i] ) { nkeynes@939: mergeable = 0; nkeynes@939: break; nkeynes@939: } nkeynes@939: } nkeynes@939: if( mergeable ) { nkeynes@939: mmu_utlb_1k_free(ent); nkeynes@939: *ptr = priv_page; nkeynes@939: *uptr = user_page; nkeynes@939: } nkeynes@939: } else { nkeynes@943: if( unmap_priv ) { nkeynes@946: /* Privileged (un)mapping */ nkeynes@939: for( i=0; itlb_multihit ) { nkeynes@939: unmapping_ok = FALSE; nkeynes@939: } nkeynes@946: *ptr++ = privdefs->tlb_miss; nkeynes@946: } nkeynes@946: } nkeynes@946: if( unmap_user ) { nkeynes@946: /* User (un)mapping */ nkeynes@946: for( i=0; itlb_multihit ) { nkeynes@946: unmapping_ok = FALSE; nkeynes@946: } nkeynes@946: *uptr++ = userdefs->tlb_miss; nkeynes@943: } nkeynes@939: } nkeynes@939: } nkeynes@943: nkeynes@939: return unmapping_ok; nkeynes@939: } nkeynes@939: nkeynes@939: static void mmu_utlb_insert_entry( int entry ) nkeynes@939: { nkeynes@939: struct utlb_entry *ent = &mmu_utlb[entry]; nkeynes@939: mem_region_fn_t page = &mmu_utlb_pages[entry].fn; nkeynes@939: mem_region_fn_t upage; nkeynes@939: sh4addr_t start_addr = ent->vpn & ent->mask; nkeynes@939: int npages = get_tlb_size_pages(ent->flags); nkeynes@939: nkeynes@946: if( (start_addr & 0xFC000000) == 0xE0000000 ) { nkeynes@946: /* Store queue mappings are a bit different - normal access is fixed to nkeynes@946: * the store queue register block, and we only map prefetches through nkeynes@946: * the TLB nkeynes@946: */ nkeynes@946: mmu_utlb_init_storequeue_vtable( ent, &mmu_utlb_pages[entry] ); nkeynes@946: nkeynes@946: if( (ent->flags & TLB_USERMODE) == 0 ) { nkeynes@946: upage = mmu_user_storequeue_regions->tlb_prot; nkeynes@946: } else if( IS_STOREQUEUE_PROTECTED() ) { nkeynes@946: upage = &p4_region_storequeue_sqmd; nkeynes@946: } else { nkeynes@946: upage = page; nkeynes@946: } nkeynes@946: nkeynes@946: } else { nkeynes@946: nkeynes@946: if( (ent->flags & TLB_USERMODE) == 0 ) { nkeynes@946: upage = &mem_region_tlb_protected; nkeynes@946: } else { nkeynes@946: upage = page; nkeynes@946: } nkeynes@946: nkeynes@946: if( (ent->flags & TLB_WRITABLE) == 0 ) { nkeynes@946: page->write_long = (mem_write_fn_t)tlb_protected_write; nkeynes@946: page->write_word = (mem_write_fn_t)tlb_protected_write; nkeynes@946: page->write_byte = (mem_write_fn_t)tlb_protected_write; nkeynes@946: page->write_burst = (mem_write_burst_fn_t)tlb_protected_write; nkeynes@975: page->read_byte_for_write = (mem_read_fn_t)tlb_protected_read_for_write; nkeynes@946: mmu_utlb_init_vtable( ent, &mmu_utlb_pages[entry], FALSE ); nkeynes@946: } else if( (ent->flags & TLB_DIRTY) == 0 ) { nkeynes@946: page->write_long = (mem_write_fn_t)tlb_initial_write; nkeynes@946: page->write_word = (mem_write_fn_t)tlb_initial_write; nkeynes@946: page->write_byte = (mem_write_fn_t)tlb_initial_write; nkeynes@946: page->write_burst = (mem_write_burst_fn_t)tlb_initial_write; nkeynes@975: page->read_byte_for_write = (mem_read_fn_t)tlb_initial_read_for_write; nkeynes@946: mmu_utlb_init_vtable( ent, &mmu_utlb_pages[entry], FALSE ); nkeynes@946: } else { nkeynes@946: mmu_utlb_init_vtable( ent, &mmu_utlb_pages[entry], TRUE ); nkeynes@946: } nkeynes@939: } nkeynes@946: nkeynes@939: mmu_utlb_pages[entry].user_fn = upage; nkeynes@939: nkeynes@939: /* Is page visible? */ nkeynes@939: if( (ent->flags & TLB_SHARE) || ent->asid == mmu_asid ) { nkeynes@939: mmu_utlb_map_pages( page, upage, start_addr, npages ); nkeynes@939: } else if( IS_SV_ENABLED() ) { nkeynes@939: mmu_utlb_map_pages( page, NULL, start_addr, npages ); nkeynes@939: } nkeynes@939: } nkeynes@939: nkeynes@939: static void mmu_utlb_remove_entry( int entry ) nkeynes@939: { nkeynes@939: int i, j; nkeynes@939: struct utlb_entry *ent = &mmu_utlb[entry]; nkeynes@939: sh4addr_t start_addr = ent->vpn&ent->mask; nkeynes@939: mem_region_fn_t *ptr = &sh4_address_space[start_addr >> 12]; nkeynes@939: mem_region_fn_t *uptr = &sh4_user_address_space[start_addr >> 12]; nkeynes@939: gboolean unmap_user; nkeynes@939: int npages = get_tlb_size_pages(ent->flags); nkeynes@939: nkeynes@939: if( (ent->flags & TLB_SHARE) || ent->asid == mmu_asid ) { nkeynes@939: unmap_user = TRUE; nkeynes@939: } else if( IS_SV_ENABLED() ) { nkeynes@939: unmap_user = FALSE; nkeynes@939: } else { nkeynes@939: return; // Not mapped nkeynes@939: } nkeynes@939: nkeynes@943: gboolean clean_unmap = mmu_utlb_unmap_pages( TRUE, unmap_user, start_addr, npages ); nkeynes@939: nkeynes@939: if( !clean_unmap ) { nkeynes@943: mmu_utlb_remap_pages( TRUE, unmap_user, entry ); nkeynes@939: } nkeynes@939: } nkeynes@939: nkeynes@939: static void mmu_utlb_register_all() nkeynes@939: { nkeynes@939: int i; nkeynes@939: for( i=0; i>12]; nkeynes@939: if( fn >= &mmu_utlb_pages[0].fn && fn < &mmu_utlb_pages[UTLB_ENTRY_COUNT].fn ) { nkeynes@939: return ((struct utlb_page_entry *)fn) - &mmu_utlb_pages[0]; nkeynes@973: } else if( fn >= &mmu_utlb_1k_pages[0].fn && fn < &mmu_utlb_1k_pages[UTLB_ENTRY_COUNT].fn ) { nkeynes@973: struct utlb_1k_entry *ent = (struct utlb_1k_entry *)fn; nkeynes@973: fn = ent->subpages[(vpn>>10)&0x03]; nkeynes@973: if( fn >= &mmu_utlb_pages[0].fn && fn < &mmu_utlb_pages[UTLB_ENTRY_COUNT].fn ) { nkeynes@973: return ((struct utlb_page_entry *)fn) - &mmu_utlb_pages[0]; nkeynes@973: } nkeynes@980: } nkeynes@980: if( fn == &mem_region_tlb_multihit ) { nkeynes@939: return -2; nkeynes@939: } else { nkeynes@939: return -1; nkeynes@939: } nkeynes@939: } nkeynes@939: nkeynes@586: nkeynes@586: /** nkeynes@586: * Perform the actual utlb lookup w/ asid matching. nkeynes@586: * Possible utcomes are: nkeynes@586: * 0..63 Single match - good, return entry found nkeynes@586: * -1 No match - raise a tlb data miss exception nkeynes@586: * -2 Multiple matches - raise a multi-hit exception (reset) nkeynes@586: * @param vpn virtual address to resolve nkeynes@586: * @return the resultant UTLB entry, or an error. nkeynes@586: */ nkeynes@586: static inline int mmu_utlb_lookup_vpn_asid( uint32_t vpn ) nkeynes@586: { nkeynes@586: int result = -1; nkeynes@586: unsigned int i; nkeynes@586: nkeynes@586: mmu_urc++; nkeynes@586: if( mmu_urc == mmu_urb || mmu_urc == 0x40 ) { nkeynes@736: mmu_urc = 0; nkeynes@586: } nkeynes@586: nkeynes@586: for( i = 0; i < UTLB_ENTRY_COUNT; i++ ) { nkeynes@736: if( (mmu_utlb[i].flags & TLB_VALID) && nkeynes@826: ((mmu_utlb[i].flags & TLB_SHARE) || mmu_asid == mmu_utlb[i].asid) && nkeynes@736: ((mmu_utlb[i].vpn ^ vpn) & mmu_utlb[i].mask) == 0 ) { nkeynes@736: if( result != -1 ) { nkeynes@736: return -2; nkeynes@736: } nkeynes@736: result = i; nkeynes@736: } nkeynes@586: } nkeynes@586: return result; nkeynes@586: } nkeynes@586: nkeynes@586: /** nkeynes@586: * Perform the actual utlb lookup matching on vpn only nkeynes@586: * Possible utcomes are: nkeynes@586: * 0..63 Single match - good, return entry found nkeynes@586: * -1 No match - raise a tlb data miss exception nkeynes@586: * -2 Multiple matches - raise a multi-hit exception (reset) nkeynes@586: * @param vpn virtual address to resolve nkeynes@586: * @return the resultant UTLB entry, or an error. nkeynes@586: */ nkeynes@586: static inline int mmu_utlb_lookup_vpn( uint32_t vpn ) nkeynes@586: { nkeynes@586: int result = -1; nkeynes@586: unsigned int i; nkeynes@586: nkeynes@586: mmu_urc++; nkeynes@586: if( mmu_urc == mmu_urb || mmu_urc == 0x40 ) { nkeynes@736: mmu_urc = 0; nkeynes@586: } nkeynes@586: nkeynes@586: for( i = 0; i < UTLB_ENTRY_COUNT; i++ ) { nkeynes@736: if( (mmu_utlb[i].flags & TLB_VALID) && nkeynes@736: ((mmu_utlb[i].vpn ^ vpn) & mmu_utlb[i].mask) == 0 ) { nkeynes@736: if( result != -1 ) { nkeynes@736: return -2; nkeynes@736: } nkeynes@736: result = i; nkeynes@736: } nkeynes@586: } nkeynes@586: nkeynes@586: return result; nkeynes@586: } nkeynes@586: nkeynes@586: /** nkeynes@586: * Update the ITLB by replacing the LRU entry with the specified UTLB entry. nkeynes@586: * @return the number (0-3) of the replaced entry. nkeynes@586: */ nkeynes@586: static int inline mmu_itlb_update_from_utlb( int entryNo ) nkeynes@586: { nkeynes@586: int replace; nkeynes@586: /* Determine entry to replace based on lrui */ nkeynes@586: if( (mmu_lrui & 0x38) == 0x38 ) { nkeynes@736: replace = 0; nkeynes@736: mmu_lrui = mmu_lrui & 0x07; nkeynes@586: } else if( (mmu_lrui & 0x26) == 0x06 ) { nkeynes@736: replace = 1; nkeynes@736: mmu_lrui = (mmu_lrui & 0x19) | 0x20; nkeynes@586: } else if( (mmu_lrui & 0x15) == 0x01 ) { nkeynes@736: replace = 2; nkeynes@736: mmu_lrui = (mmu_lrui & 0x3E) | 0x14; nkeynes@586: } else { // Note - gets invalid entries too nkeynes@736: replace = 3; nkeynes@736: mmu_lrui = (mmu_lrui | 0x0B); nkeynes@826: } nkeynes@586: nkeynes@586: mmu_itlb[replace].vpn = mmu_utlb[entryNo].vpn; nkeynes@586: mmu_itlb[replace].mask = mmu_utlb[entryNo].mask; nkeynes@586: mmu_itlb[replace].ppn = mmu_utlb[entryNo].ppn; nkeynes@586: mmu_itlb[replace].asid = mmu_utlb[entryNo].asid; nkeynes@586: mmu_itlb[replace].flags = mmu_utlb[entryNo].flags & 0x01DA; nkeynes@586: return replace; nkeynes@586: } nkeynes@586: nkeynes@586: /** nkeynes@586: * Perform the actual itlb lookup w/ asid protection nkeynes@586: * Possible utcomes are: nkeynes@586: * 0..63 Single match - good, return entry found nkeynes@586: * -1 No match - raise a tlb data miss exception nkeynes@586: * -2 Multiple matches - raise a multi-hit exception (reset) nkeynes@586: * @param vpn virtual address to resolve nkeynes@586: * @return the resultant ITLB entry, or an error. nkeynes@586: */ nkeynes@586: static inline int mmu_itlb_lookup_vpn_asid( uint32_t vpn ) nkeynes@586: { nkeynes@586: int result = -1; nkeynes@586: unsigned int i; nkeynes@586: nkeynes@586: for( i = 0; i < ITLB_ENTRY_COUNT; i++ ) { nkeynes@736: if( (mmu_itlb[i].flags & TLB_VALID) && nkeynes@826: ((mmu_itlb[i].flags & TLB_SHARE) || mmu_asid == mmu_itlb[i].asid) && nkeynes@736: ((mmu_itlb[i].vpn ^ vpn) & mmu_itlb[i].mask) == 0 ) { nkeynes@736: if( result != -1 ) { nkeynes@736: return -2; nkeynes@736: } nkeynes@736: result = i; nkeynes@736: } nkeynes@586: } nkeynes@586: nkeynes@586: if( result == -1 ) { nkeynes@939: int utlbEntry = mmu_utlb_entry_for_vpn( vpn ); nkeynes@736: if( utlbEntry < 0 ) { nkeynes@736: return utlbEntry; nkeynes@736: } else { nkeynes@736: return mmu_itlb_update_from_utlb( utlbEntry ); nkeynes@736: } nkeynes@586: } nkeynes@586: nkeynes@586: switch( result ) { nkeynes@586: case 0: mmu_lrui = (mmu_lrui & 0x07); break; nkeynes@586: case 1: mmu_lrui = (mmu_lrui & 0x19) | 0x20; break; nkeynes@586: case 2: mmu_lrui = (mmu_lrui & 0x3E) | 0x14; break; nkeynes@586: case 3: mmu_lrui = (mmu_lrui | 0x0B); break; nkeynes@586: } nkeynes@736: nkeynes@586: return result; nkeynes@586: } nkeynes@586: nkeynes@586: /** nkeynes@586: * Perform the actual itlb lookup on vpn only nkeynes@586: * Possible utcomes are: nkeynes@586: * 0..63 Single match - good, return entry found nkeynes@586: * -1 No match - raise a tlb data miss exception nkeynes@586: * -2 Multiple matches - raise a multi-hit exception (reset) nkeynes@586: * @param vpn virtual address to resolve nkeynes@586: * @return the resultant ITLB entry, or an error. nkeynes@586: */ nkeynes@586: static inline int mmu_itlb_lookup_vpn( uint32_t vpn ) nkeynes@586: { nkeynes@586: int result = -1; nkeynes@586: unsigned int i; nkeynes@586: nkeynes@586: for( i = 0; i < ITLB_ENTRY_COUNT; i++ ) { nkeynes@736: if( (mmu_itlb[i].flags & TLB_VALID) && nkeynes@736: ((mmu_itlb[i].vpn ^ vpn) & mmu_itlb[i].mask) == 0 ) { nkeynes@736: if( result != -1 ) { nkeynes@736: return -2; nkeynes@736: } nkeynes@736: result = i; nkeynes@736: } nkeynes@586: } nkeynes@586: nkeynes@586: if( result == -1 ) { nkeynes@736: int utlbEntry = mmu_utlb_lookup_vpn( vpn ); nkeynes@736: if( utlbEntry < 0 ) { nkeynes@736: return utlbEntry; nkeynes@736: } else { nkeynes@736: return mmu_itlb_update_from_utlb( utlbEntry ); nkeynes@736: } nkeynes@586: } nkeynes@586: nkeynes@586: switch( result ) { nkeynes@586: case 0: mmu_lrui = (mmu_lrui & 0x07); break; nkeynes@586: case 1: mmu_lrui = (mmu_lrui & 0x19) | 0x20; break; nkeynes@586: case 2: mmu_lrui = (mmu_lrui & 0x3E) | 0x14; break; nkeynes@586: case 3: mmu_lrui = (mmu_lrui | 0x0B); break; nkeynes@586: } nkeynes@736: nkeynes@586: return result; nkeynes@586: } nkeynes@927: nkeynes@586: /** nkeynes@586: * Update the icache for an untranslated address nkeynes@586: */ nkeynes@905: static inline void mmu_update_icache_phys( sh4addr_t addr ) nkeynes@586: { nkeynes@586: if( (addr & 0x1C000000) == 0x0C000000 ) { nkeynes@736: /* Main ram */ nkeynes@736: sh4_icache.page_vma = addr & 0xFF000000; nkeynes@736: sh4_icache.page_ppa = 0x0C000000; nkeynes@736: sh4_icache.mask = 0xFF000000; nkeynes@934: sh4_icache.page = dc_main_ram; nkeynes@586: } else if( (addr & 0x1FE00000) == 0 ) { nkeynes@736: /* BIOS ROM */ nkeynes@736: sh4_icache.page_vma = addr & 0xFFE00000; nkeynes@736: sh4_icache.page_ppa = 0; nkeynes@736: sh4_icache.mask = 0xFFE00000; nkeynes@934: sh4_icache.page = dc_boot_rom; nkeynes@586: } else { nkeynes@736: /* not supported */ nkeynes@736: sh4_icache.page_vma = -1; nkeynes@586: } nkeynes@586: } nkeynes@586: nkeynes@586: /** nkeynes@586: * Update the sh4_icache structure to describe the page(s) containing the nkeynes@586: * given vma. If the address does not reference a RAM/ROM region, the icache nkeynes@586: * will be invalidated instead. nkeynes@586: * If AT is on, this method will raise TLB exceptions normally nkeynes@586: * (hence this method should only be used immediately prior to execution of nkeynes@586: * code), and otherwise will set the icache according to the matching TLB entry. nkeynes@586: * If AT is off, this method will set the entire referenced RAM/ROM region in nkeynes@586: * the icache. nkeynes@586: * @return TRUE if the update completed (successfully or otherwise), FALSE nkeynes@586: * if an exception was raised. nkeynes@586: */ nkeynes@905: gboolean FASTCALL mmu_update_icache( sh4vma_t addr ) nkeynes@586: { nkeynes@586: int entryNo; nkeynes@586: if( IS_SH4_PRIVMODE() ) { nkeynes@736: if( addr & 0x80000000 ) { nkeynes@736: if( addr < 0xC0000000 ) { nkeynes@736: /* P1, P2 and P4 regions are pass-through (no translation) */ nkeynes@736: mmu_update_icache_phys(addr); nkeynes@736: return TRUE; nkeynes@736: } else if( addr >= 0xE0000000 && addr < 0xFFFFFF00 ) { nkeynes@939: RAISE_MEM_ERROR(EXC_DATA_ADDR_READ, addr); nkeynes@736: return FALSE; nkeynes@736: } nkeynes@736: } nkeynes@586: nkeynes@736: uint32_t mmucr = MMIO_READ(MMU,MMUCR); nkeynes@736: if( (mmucr & MMUCR_AT) == 0 ) { nkeynes@736: mmu_update_icache_phys(addr); nkeynes@736: return TRUE; nkeynes@736: } nkeynes@736: nkeynes@826: if( (mmucr & MMUCR_SV) == 0 ) nkeynes@807: entryNo = mmu_itlb_lookup_vpn_asid( addr ); nkeynes@807: else nkeynes@807: entryNo = mmu_itlb_lookup_vpn( addr ); nkeynes@586: } else { nkeynes@736: if( addr & 0x80000000 ) { nkeynes@939: RAISE_MEM_ERROR(EXC_DATA_ADDR_READ, addr); nkeynes@736: return FALSE; nkeynes@736: } nkeynes@586: nkeynes@736: uint32_t mmucr = MMIO_READ(MMU,MMUCR); nkeynes@736: if( (mmucr & MMUCR_AT) == 0 ) { nkeynes@736: mmu_update_icache_phys(addr); nkeynes@736: return TRUE; nkeynes@736: } nkeynes@736: nkeynes@807: entryNo = mmu_itlb_lookup_vpn_asid( addr ); nkeynes@807: nkeynes@736: if( entryNo != -1 && (mmu_itlb[entryNo].flags & TLB_USERMODE) == 0 ) { nkeynes@939: RAISE_MEM_ERROR(EXC_TLB_PROT_READ, addr); nkeynes@736: return FALSE; nkeynes@736: } nkeynes@586: } nkeynes@586: nkeynes@586: switch(entryNo) { nkeynes@586: case -1: nkeynes@939: RAISE_TLB_ERROR(EXC_TLB_MISS_READ, addr); nkeynes@736: return FALSE; nkeynes@586: case -2: nkeynes@939: RAISE_TLB_MULTIHIT_ERROR(addr); nkeynes@736: return FALSE; nkeynes@586: default: nkeynes@736: sh4_icache.page_ppa = mmu_itlb[entryNo].ppn & mmu_itlb[entryNo].mask; nkeynes@736: sh4_icache.page = mem_get_region( sh4_icache.page_ppa ); nkeynes@736: if( sh4_icache.page == NULL ) { nkeynes@736: sh4_icache.page_vma = -1; nkeynes@736: } else { nkeynes@736: sh4_icache.page_vma = mmu_itlb[entryNo].vpn & mmu_itlb[entryNo].mask; nkeynes@736: sh4_icache.mask = mmu_itlb[entryNo].mask; nkeynes@736: } nkeynes@736: return TRUE; nkeynes@586: } nkeynes@586: } nkeynes@586: nkeynes@597: /** nkeynes@826: * Translate address for disassembly purposes (ie performs an instruction nkeynes@597: * lookup) - does not raise exceptions or modify any state, and ignores nkeynes@597: * protection bits. Returns the translated address, or MMU_VMA_ERROR nkeynes@826: * on translation failure. nkeynes@597: */ nkeynes@905: sh4addr_t FASTCALL mmu_vma_to_phys_disasm( sh4vma_t vma ) nkeynes@597: { nkeynes@597: if( vma & 0x80000000 ) { nkeynes@736: if( vma < 0xC0000000 ) { nkeynes@736: /* P1, P2 and P4 regions are pass-through (no translation) */ nkeynes@736: return VMA_TO_EXT_ADDR(vma); nkeynes@736: } else if( vma >= 0xE0000000 && vma < 0xFFFFFF00 ) { nkeynes@736: /* Not translatable */ nkeynes@736: return MMU_VMA_ERROR; nkeynes@736: } nkeynes@597: } nkeynes@597: nkeynes@597: uint32_t mmucr = MMIO_READ(MMU,MMUCR); nkeynes@597: if( (mmucr & MMUCR_AT) == 0 ) { nkeynes@736: return VMA_TO_EXT_ADDR(vma); nkeynes@597: } nkeynes@736: nkeynes@597: int entryNo = mmu_itlb_lookup_vpn( vma ); nkeynes@597: if( entryNo == -2 ) { nkeynes@736: entryNo = mmu_itlb_lookup_vpn_asid( vma ); nkeynes@597: } nkeynes@597: if( entryNo < 0 ) { nkeynes@736: return MMU_VMA_ERROR; nkeynes@597: } else { nkeynes@826: return (mmu_itlb[entryNo].ppn & mmu_itlb[entryNo].mask) | nkeynes@826: (vma & (~mmu_itlb[entryNo].mask)); nkeynes@597: } nkeynes@597: } nkeynes@597: nkeynes@1217: /** nkeynes@1217: * Translate a virtual to physical address for reading, raising exceptions as nkeynes@1217: * observed. nkeynes@1217: * @param addr Pointer to the virtual memory address. On successful return, nkeynes@1217: * will be updated to contain the physical address. nkeynes@1217: */ nkeynes@1217: mem_region_fn_t FASTCALL mmu_get_region_for_vma_read( sh4vma_t *paddr ) nkeynes@1217: { nkeynes@1217: sh4vma_t addr = *paddr; nkeynes@1217: uint32_t mmucr = MMIO_READ(MMU,MMUCR); nkeynes@1217: if( addr & 0x80000000 ) { nkeynes@1217: if( IS_SH4_PRIVMODE() ) { nkeynes@1217: if( addr >= 0xE0000000 ) { nkeynes@1217: return sh4_address_space[((uint32_t)addr)>>12]; /* P4 - passthrough */ nkeynes@1217: } else if( addr < 0xC0000000 ) { nkeynes@1217: /* P1, P2 regions are pass-through (no translation) */ nkeynes@1217: return sh4_ext_address_space[VMA_TO_EXT_ADDR(addr)>>12]; nkeynes@1217: } nkeynes@1217: } else { nkeynes@1217: if( addr >= 0xE0000000 && addr < 0xE4000000 && nkeynes@1217: ((mmucr&MMUCR_SQMD) == 0) ) { nkeynes@1217: /* Conditional user-mode access to the store-queue (no translation) */ nkeynes@1217: return &p4_region_storequeue; nkeynes@1217: } nkeynes@1217: sh4_raise_exception(EXC_DATA_ADDR_READ); nkeynes@1217: return NULL; nkeynes@1217: } nkeynes@1217: } nkeynes@1217: nkeynes@1217: if( (mmucr & MMUCR_AT) == 0 ) { nkeynes@1295: return sh4_address_space[addr>>12]; nkeynes@1217: } nkeynes@1217: nkeynes@1217: /* If we get this far, translation is required */ nkeynes@1217: int entryNo; nkeynes@1217: if( ((mmucr & MMUCR_SV) == 0) || !IS_SH4_PRIVMODE() ) { nkeynes@1217: entryNo = mmu_utlb_lookup_vpn_asid( addr ); nkeynes@1217: } else { nkeynes@1217: entryNo = mmu_utlb_lookup_vpn( addr ); nkeynes@1217: } nkeynes@1217: nkeynes@1217: switch(entryNo) { nkeynes@1217: case -1: nkeynes@1217: RAISE_TLB_ERROR(EXC_TLB_MISS_READ,addr); nkeynes@1217: return NULL; nkeynes@1217: case -2: nkeynes@1217: RAISE_TLB_MULTIHIT_ERROR(addr); nkeynes@1217: return NULL; nkeynes@1217: default: nkeynes@1217: if( (mmu_utlb[entryNo].flags & TLB_USERMODE) == 0 && nkeynes@1217: !IS_SH4_PRIVMODE() ) { nkeynes@1217: /* protection violation */ nkeynes@1217: RAISE_MEM_ERROR(EXC_TLB_PROT_READ,addr); nkeynes@1217: return NULL; nkeynes@1217: } nkeynes@1217: nkeynes@1217: /* finally generate the target address */ nkeynes@1217: sh4addr_t pma = (mmu_utlb[entryNo].ppn & mmu_utlb[entryNo].mask) | nkeynes@1217: (addr & (~mmu_utlb[entryNo].mask)); nkeynes@1217: if( pma > 0x1C000000 ) { // Remap 1Cxx .. 1Fxx region to P4 nkeynes@1217: addr = pma | 0xE0000000; nkeynes@1217: *paddr = addr; nkeynes@1217: return sh4_address_space[addr>>12]; nkeynes@1217: } else { nkeynes@1217: *paddr = pma; nkeynes@1217: return sh4_ext_address_space[pma>>12]; nkeynes@1217: } nkeynes@1217: } nkeynes@1217: } nkeynes@1217: nkeynes@1217: /** nkeynes@1217: * Translate a virtual to physical address for prefetch, which mostly nkeynes@1217: * does not raise exceptions. nkeynes@1217: * @param addr Pointer to the virtual memory address. On successful return, nkeynes@1217: * will be updated to contain the physical address. nkeynes@1217: */ nkeynes@1217: mem_region_fn_t FASTCALL mmu_get_region_for_vma_prefetch( sh4vma_t *paddr ) nkeynes@1217: { nkeynes@1217: sh4vma_t addr = *paddr; nkeynes@1217: uint32_t mmucr = MMIO_READ(MMU,MMUCR); nkeynes@1217: if( addr & 0x80000000 ) { nkeynes@1217: if( IS_SH4_PRIVMODE() ) { nkeynes@1217: if( addr >= 0xE0000000 ) { nkeynes@1217: return sh4_address_space[((uint32_t)addr)>>12]; /* P4 - passthrough */ nkeynes@1217: } else if( addr < 0xC0000000 ) { nkeynes@1217: /* P1, P2 regions are pass-through (no translation) */ nkeynes@1217: return sh4_ext_address_space[VMA_TO_EXT_ADDR(addr)>>12]; nkeynes@1217: } nkeynes@1217: } else { nkeynes@1217: if( addr >= 0xE0000000 && addr < 0xE4000000 && nkeynes@1217: ((mmucr&MMUCR_SQMD) == 0) ) { nkeynes@1217: /* Conditional user-mode access to the store-queue (no translation) */ nkeynes@1217: return &p4_region_storequeue; nkeynes@1217: } nkeynes@1217: sh4_raise_exception(EXC_DATA_ADDR_READ); nkeynes@1217: return NULL; nkeynes@1217: } nkeynes@1217: } nkeynes@1217: nkeynes@1217: if( (mmucr & MMUCR_AT) == 0 ) { nkeynes@1295: return sh4_address_space[addr>>12]; nkeynes@1217: } nkeynes@1217: nkeynes@1217: /* If we get this far, translation is required */ nkeynes@1217: int entryNo; nkeynes@1217: if( ((mmucr & MMUCR_SV) == 0) || !IS_SH4_PRIVMODE() ) { nkeynes@1217: entryNo = mmu_utlb_lookup_vpn_asid( addr ); nkeynes@1217: } else { nkeynes@1217: entryNo = mmu_utlb_lookup_vpn( addr ); nkeynes@1217: } nkeynes@1217: nkeynes@1217: switch(entryNo) { nkeynes@1217: case -1: nkeynes@1217: return &mem_region_unmapped; nkeynes@1217: case -2: nkeynes@1217: RAISE_TLB_MULTIHIT_ERROR(addr); nkeynes@1217: return NULL; nkeynes@1217: default: nkeynes@1217: if( (mmu_utlb[entryNo].flags & TLB_USERMODE) == 0 && nkeynes@1217: !IS_SH4_PRIVMODE() ) { nkeynes@1217: /* protection violation */ nkeynes@1217: return &mem_region_unmapped; nkeynes@1217: } nkeynes@1217: nkeynes@1217: /* finally generate the target address */ nkeynes@1217: sh4addr_t pma = (mmu_utlb[entryNo].ppn & mmu_utlb[entryNo].mask) | nkeynes@1217: (addr & (~mmu_utlb[entryNo].mask)); nkeynes@1217: if( pma > 0x1C000000 ) { // Remap 1Cxx .. 1Fxx region to P4 nkeynes@1217: addr = pma | 0xE0000000; nkeynes@1217: *paddr = addr; nkeynes@1217: return sh4_address_space[addr>>12]; nkeynes@1217: } else { nkeynes@1217: *paddr = pma; nkeynes@1217: return sh4_ext_address_space[pma>>12]; nkeynes@1217: } nkeynes@1217: } nkeynes@1217: } nkeynes@1217: nkeynes@1217: /** nkeynes@1217: * Translate a virtual to physical address for writing, raising exceptions as nkeynes@1217: * observed. nkeynes@1217: */ nkeynes@1217: mem_region_fn_t FASTCALL mmu_get_region_for_vma_write( sh4vma_t *paddr ) nkeynes@1217: { nkeynes@1217: sh4vma_t addr = *paddr; nkeynes@1217: uint32_t mmucr = MMIO_READ(MMU,MMUCR); nkeynes@1217: if( addr & 0x80000000 ) { nkeynes@1217: if( IS_SH4_PRIVMODE() ) { nkeynes@1217: if( addr >= 0xE0000000 ) { nkeynes@1217: return sh4_address_space[((uint32_t)addr)>>12]; /* P4 - passthrough */ nkeynes@1217: } else if( addr < 0xC0000000 ) { nkeynes@1217: /* P1, P2 regions are pass-through (no translation) */ nkeynes@1217: return sh4_ext_address_space[VMA_TO_EXT_ADDR(addr)>>12]; nkeynes@1217: } nkeynes@1217: } else { nkeynes@1217: if( addr >= 0xE0000000 && addr < 0xE4000000 && nkeynes@1217: ((mmucr&MMUCR_SQMD) == 0) ) { nkeynes@1217: /* Conditional user-mode access to the store-queue (no translation) */ nkeynes@1217: return &p4_region_storequeue; nkeynes@1217: } nkeynes@1217: sh4_raise_exception(EXC_DATA_ADDR_WRITE); nkeynes@1217: return NULL; nkeynes@1217: } nkeynes@1217: } nkeynes@1217: nkeynes@1217: if( (mmucr & MMUCR_AT) == 0 ) { nkeynes@1295: return sh4_address_space[addr>>12]; nkeynes@1217: } nkeynes@1217: nkeynes@1217: /* If we get this far, translation is required */ nkeynes@1217: int entryNo; nkeynes@1217: if( ((mmucr & MMUCR_SV) == 0) || !IS_SH4_PRIVMODE() ) { nkeynes@1217: entryNo = mmu_utlb_lookup_vpn_asid( addr ); nkeynes@1217: } else { nkeynes@1217: entryNo = mmu_utlb_lookup_vpn( addr ); nkeynes@1217: } nkeynes@1217: nkeynes@1217: switch(entryNo) { nkeynes@1217: case -1: nkeynes@1217: RAISE_TLB_ERROR(EXC_TLB_MISS_WRITE,addr); nkeynes@1217: return NULL; nkeynes@1217: case -2: nkeynes@1217: RAISE_TLB_MULTIHIT_ERROR(addr); nkeynes@1217: return NULL; nkeynes@1217: default: nkeynes@1217: if( IS_SH4_PRIVMODE() ? ((mmu_utlb[entryNo].flags & TLB_WRITABLE) == 0) nkeynes@1217: : ((mmu_utlb[entryNo].flags & TLB_USERWRITABLE) != TLB_USERWRITABLE) ) { nkeynes@1217: /* protection violation */ nkeynes@1217: RAISE_MEM_ERROR(EXC_TLB_PROT_WRITE,addr); nkeynes@1217: return NULL; nkeynes@1217: } nkeynes@1217: nkeynes@1217: if( (mmu_utlb[entryNo].flags & TLB_DIRTY) == 0 ) { nkeynes@1217: RAISE_MEM_ERROR(EXC_INIT_PAGE_WRITE, addr); nkeynes@1217: return NULL; nkeynes@1217: } nkeynes@1217: nkeynes@1217: /* finally generate the target address */ nkeynes@1217: sh4addr_t pma = (mmu_utlb[entryNo].ppn & mmu_utlb[entryNo].mask) | nkeynes@1217: (addr & (~mmu_utlb[entryNo].mask)); nkeynes@1217: if( pma > 0x1C000000 ) { // Remap 1Cxx .. 1Fxx region to P4 nkeynes@1217: addr = pma | 0xE0000000; nkeynes@1217: *paddr = addr; nkeynes@1217: return sh4_address_space[addr>>12]; nkeynes@1217: } else { nkeynes@1217: *paddr = pma; nkeynes@1217: return sh4_ext_address_space[pma>>12]; nkeynes@1217: } nkeynes@1217: } nkeynes@1217: } nkeynes@1217: nkeynes@1217: nkeynes@1217: nkeynes@939: /********************** TLB Direct-Access Regions ***************************/ nkeynes@939: #define ITLB_ENTRY(addr) ((addr>>7)&0x03) nkeynes@939: nkeynes@939: int32_t FASTCALL mmu_itlb_addr_read( sh4addr_t addr ) nkeynes@939: { nkeynes@939: struct itlb_entry *ent = &mmu_itlb[ITLB_ENTRY(addr)]; nkeynes@939: return ent->vpn | ent->asid | (ent->flags & TLB_VALID); nkeynes@939: } nkeynes@939: nkeynes@939: void FASTCALL mmu_itlb_addr_write( sh4addr_t addr, uint32_t val ) nkeynes@939: { nkeynes@939: struct itlb_entry *ent = &mmu_itlb[ITLB_ENTRY(addr)]; nkeynes@939: ent->vpn = val & 0xFFFFFC00; nkeynes@939: ent->asid = val & 0x000000FF; nkeynes@939: ent->flags = (ent->flags & ~(TLB_VALID)) | (val&TLB_VALID); nkeynes@939: } nkeynes@939: nkeynes@939: int32_t FASTCALL mmu_itlb_data_read( sh4addr_t addr ) nkeynes@939: { nkeynes@939: struct itlb_entry *ent = &mmu_itlb[ITLB_ENTRY(addr)]; nkeynes@939: return (ent->ppn & 0x1FFFFC00) | ent->flags; nkeynes@939: } nkeynes@939: nkeynes@939: void FASTCALL mmu_itlb_data_write( sh4addr_t addr, uint32_t val ) nkeynes@939: { nkeynes@939: struct itlb_entry *ent = &mmu_itlb[ITLB_ENTRY(addr)]; nkeynes@939: ent->ppn = val & 0x1FFFFC00; nkeynes@939: ent->flags = val & 0x00001DA; nkeynes@939: ent->mask = get_tlb_size_mask(val); nkeynes@939: if( ent->ppn >= 0x1C000000 ) nkeynes@939: ent->ppn |= 0xE0000000; nkeynes@939: } nkeynes@939: nkeynes@939: #define UTLB_ENTRY(addr) ((addr>>8)&0x3F) nkeynes@939: #define UTLB_ASSOC(addr) (addr&0x80) nkeynes@939: #define UTLB_DATA2(addr) (addr&0x00800000) nkeynes@939: nkeynes@939: int32_t FASTCALL mmu_utlb_addr_read( sh4addr_t addr ) nkeynes@939: { nkeynes@939: struct utlb_entry *ent = &mmu_utlb[UTLB_ENTRY(addr)]; nkeynes@939: return ent->vpn | ent->asid | (ent->flags & TLB_VALID) | nkeynes@939: ((ent->flags & TLB_DIRTY)<<7); nkeynes@939: } nkeynes@939: int32_t FASTCALL mmu_utlb_data_read( sh4addr_t addr ) nkeynes@939: { nkeynes@939: struct utlb_entry *ent = &mmu_utlb[UTLB_ENTRY(addr)]; nkeynes@939: if( UTLB_DATA2(addr) ) { nkeynes@939: return ent->pcmcia; nkeynes@939: } else { nkeynes@939: return (ent->ppn&0x1FFFFC00) | ent->flags; nkeynes@939: } nkeynes@939: } nkeynes@939: nkeynes@939: /** nkeynes@939: * Find a UTLB entry for the associative TLB write - same as the normal nkeynes@939: * lookup but ignores the valid bit. nkeynes@939: */ nkeynes@939: static inline int mmu_utlb_lookup_assoc( uint32_t vpn, uint32_t asid ) nkeynes@939: { nkeynes@939: int result = -1; nkeynes@939: unsigned int i; nkeynes@939: for( i = 0; i < UTLB_ENTRY_COUNT; i++ ) { nkeynes@939: if( (mmu_utlb[i].flags & TLB_VALID) && nkeynes@939: ((mmu_utlb[i].flags & TLB_SHARE) || asid == mmu_utlb[i].asid) && nkeynes@939: ((mmu_utlb[i].vpn ^ vpn) & mmu_utlb[i].mask) == 0 ) { nkeynes@939: if( result != -1 ) { nkeynes@939: fprintf( stderr, "TLB Multi hit: %d %d\n", result, i ); nkeynes@939: return -2; nkeynes@939: } nkeynes@939: result = i; nkeynes@939: } nkeynes@939: } nkeynes@939: return result; nkeynes@939: } nkeynes@939: nkeynes@939: /** nkeynes@939: * Find a ITLB entry for the associative TLB write - same as the normal nkeynes@939: * lookup but ignores the valid bit. nkeynes@939: */ nkeynes@939: static inline int mmu_itlb_lookup_assoc( uint32_t vpn, uint32_t asid ) nkeynes@939: { nkeynes@939: int result = -1; nkeynes@939: unsigned int i; nkeynes@939: for( i = 0; i < ITLB_ENTRY_COUNT; i++ ) { nkeynes@939: if( (mmu_itlb[i].flags & TLB_VALID) && nkeynes@939: ((mmu_itlb[i].flags & TLB_SHARE) || asid == mmu_itlb[i].asid) && nkeynes@939: ((mmu_itlb[i].vpn ^ vpn) & mmu_itlb[i].mask) == 0 ) { nkeynes@939: if( result != -1 ) { nkeynes@939: return -2; nkeynes@939: } nkeynes@939: result = i; nkeynes@939: } nkeynes@939: } nkeynes@939: return result; nkeynes@939: } nkeynes@939: nkeynes@939: void FASTCALL mmu_utlb_addr_write( sh4addr_t addr, uint32_t val, void *exc ) nkeynes@939: { nkeynes@939: if( UTLB_ASSOC(addr) ) { nkeynes@939: int utlb = mmu_utlb_lookup_assoc( val, mmu_asid ); nkeynes@939: if( utlb >= 0 ) { nkeynes@939: struct utlb_entry *ent = &mmu_utlb[utlb]; nkeynes@939: uint32_t old_flags = ent->flags; nkeynes@939: ent->flags = ent->flags & ~(TLB_DIRTY|TLB_VALID); nkeynes@939: ent->flags |= (val & TLB_VALID); nkeynes@939: ent->flags |= ((val & 0x200)>>7); nkeynes@1090: if( IS_TLB_ENABLED() && ((old_flags^ent->flags) & (TLB_VALID|TLB_DIRTY)) != 0 ) { nkeynes@939: if( old_flags & TLB_VALID ) nkeynes@939: mmu_utlb_remove_entry( utlb ); nkeynes@939: if( ent->flags & TLB_VALID ) nkeynes@939: mmu_utlb_insert_entry( utlb ); nkeynes@939: } nkeynes@939: } nkeynes@939: nkeynes@939: int itlb = mmu_itlb_lookup_assoc( val, mmu_asid ); nkeynes@939: if( itlb >= 0 ) { nkeynes@939: struct itlb_entry *ent = &mmu_itlb[itlb]; nkeynes@939: ent->flags = (ent->flags & (~TLB_VALID)) | (val & TLB_VALID); nkeynes@939: } nkeynes@939: nkeynes@939: if( itlb == -2 || utlb == -2 ) { nkeynes@1090: RAISE_TLB_MULTIHIT_ERROR(addr); /* FIXME: should this only be raised if TLB is enabled? */ nkeynes@1202: SH4_EXCEPTION_EXIT(); nkeynes@939: return; nkeynes@939: } nkeynes@939: } else { nkeynes@939: struct utlb_entry *ent = &mmu_utlb[UTLB_ENTRY(addr)]; nkeynes@1090: if( IS_TLB_ENABLED() && ent->flags & TLB_VALID ) nkeynes@939: mmu_utlb_remove_entry( UTLB_ENTRY(addr) ); nkeynes@939: ent->vpn = (val & 0xFFFFFC00); nkeynes@939: ent->asid = (val & 0xFF); nkeynes@939: ent->flags = (ent->flags & ~(TLB_DIRTY|TLB_VALID)); nkeynes@939: ent->flags |= (val & TLB_VALID); nkeynes@939: ent->flags |= ((val & 0x200)>>7); nkeynes@1090: if( IS_TLB_ENABLED() && ent->flags & TLB_VALID ) nkeynes@939: mmu_utlb_insert_entry( UTLB_ENTRY(addr) ); nkeynes@939: } nkeynes@939: } nkeynes@939: nkeynes@939: void FASTCALL mmu_utlb_data_write( sh4addr_t addr, uint32_t val ) nkeynes@939: { nkeynes@939: struct utlb_entry *ent = &mmu_utlb[UTLB_ENTRY(addr)]; nkeynes@939: if( UTLB_DATA2(addr) ) { nkeynes@939: ent->pcmcia = val & 0x0000000F; nkeynes@939: } else { nkeynes@1090: if( IS_TLB_ENABLED() && ent->flags & TLB_VALID ) nkeynes@939: mmu_utlb_remove_entry( UTLB_ENTRY(addr) ); nkeynes@939: ent->ppn = (val & 0x1FFFFC00); nkeynes@939: ent->flags = (val & 0x000001FF); nkeynes@939: ent->mask = get_tlb_size_mask(val); nkeynes@1090: if( IS_TLB_ENABLED() && ent->flags & TLB_VALID ) nkeynes@939: mmu_utlb_insert_entry( UTLB_ENTRY(addr) ); nkeynes@939: } nkeynes@939: } nkeynes@939: nkeynes@939: struct mem_region_fn p4_region_itlb_addr = { nkeynes@939: mmu_itlb_addr_read, mmu_itlb_addr_write, nkeynes@939: mmu_itlb_addr_read, mmu_itlb_addr_write, nkeynes@939: mmu_itlb_addr_read, mmu_itlb_addr_write, nkeynes@946: unmapped_read_burst, unmapped_write_burst, nkeynes@975: unmapped_prefetch, mmu_itlb_addr_read }; nkeynes@939: struct mem_region_fn p4_region_itlb_data = { nkeynes@939: mmu_itlb_data_read, mmu_itlb_data_write, nkeynes@939: mmu_itlb_data_read, mmu_itlb_data_write, nkeynes@939: mmu_itlb_data_read, mmu_itlb_data_write, nkeynes@946: unmapped_read_burst, unmapped_write_burst, nkeynes@975: unmapped_prefetch, mmu_itlb_data_read }; nkeynes@939: struct mem_region_fn p4_region_utlb_addr = { nkeynes@939: mmu_utlb_addr_read, (mem_write_fn_t)mmu_utlb_addr_write, nkeynes@939: mmu_utlb_addr_read, (mem_write_fn_t)mmu_utlb_addr_write, nkeynes@939: mmu_utlb_addr_read, (mem_write_fn_t)mmu_utlb_addr_write, nkeynes@946: unmapped_read_burst, unmapped_write_burst, nkeynes@975: unmapped_prefetch, mmu_utlb_addr_read }; nkeynes@939: struct mem_region_fn p4_region_utlb_data = { nkeynes@939: mmu_utlb_data_read, mmu_utlb_data_write, nkeynes@939: mmu_utlb_data_read, mmu_utlb_data_write, nkeynes@939: mmu_utlb_data_read, mmu_utlb_data_write, nkeynes@946: unmapped_read_burst, unmapped_write_burst, nkeynes@975: unmapped_prefetch, mmu_utlb_data_read }; nkeynes@939: nkeynes@939: /********************** Error regions **************************/ nkeynes@939: nkeynes@939: static void FASTCALL address_error_read( sh4addr_t addr, void *exc ) nkeynes@939: { nkeynes@939: RAISE_MEM_ERROR(EXC_DATA_ADDR_READ, addr); nkeynes@1202: SH4_EXCEPTION_EXIT(); nkeynes@939: } nkeynes@939: nkeynes@975: static void FASTCALL address_error_read_for_write( sh4addr_t addr, void *exc ) nkeynes@975: { nkeynes@975: RAISE_MEM_ERROR(EXC_DATA_ADDR_WRITE, addr); nkeynes@1202: SH4_EXCEPTION_EXIT(); nkeynes@975: } nkeynes@975: nkeynes@939: static void FASTCALL address_error_read_burst( unsigned char *dest, sh4addr_t addr, void *exc ) nkeynes@939: { nkeynes@939: RAISE_MEM_ERROR(EXC_DATA_ADDR_READ, addr); nkeynes@1202: SH4_EXCEPTION_EXIT(); nkeynes@939: } nkeynes@939: nkeynes@939: static void FASTCALL address_error_write( sh4addr_t addr, uint32_t val, void *exc ) nkeynes@939: { nkeynes@939: RAISE_MEM_ERROR(EXC_DATA_ADDR_WRITE, addr); nkeynes@1202: SH4_EXCEPTION_EXIT(); nkeynes@939: } nkeynes@939: nkeynes@939: static void FASTCALL tlb_miss_read( sh4addr_t addr, void *exc ) nkeynes@939: { nkeynes@973: mmu_urc++; nkeynes@939: RAISE_TLB_ERROR(EXC_TLB_MISS_READ, addr); nkeynes@1202: SH4_EXCEPTION_EXIT(); nkeynes@939: } nkeynes@939: nkeynes@975: static void FASTCALL tlb_miss_read_for_write( sh4addr_t addr, void *exc ) nkeynes@975: { nkeynes@975: mmu_urc++; nkeynes@975: RAISE_TLB_ERROR(EXC_TLB_MISS_WRITE, addr); nkeynes@1202: SH4_EXCEPTION_EXIT(); nkeynes@975: } nkeynes@975: nkeynes@939: static void FASTCALL tlb_miss_read_burst( unsigned char *dest, sh4addr_t addr, void *exc ) nkeynes@939: { nkeynes@973: mmu_urc++; nkeynes@939: RAISE_TLB_ERROR(EXC_TLB_MISS_READ, addr); nkeynes@1202: SH4_EXCEPTION_EXIT(); nkeynes@939: } nkeynes@939: nkeynes@939: static void FASTCALL tlb_miss_write( sh4addr_t addr, uint32_t val, void *exc ) nkeynes@939: { nkeynes@973: mmu_urc++; nkeynes@939: RAISE_TLB_ERROR(EXC_TLB_MISS_WRITE, addr); nkeynes@1202: SH4_EXCEPTION_EXIT(); nkeynes@975: } nkeynes@939: nkeynes@939: static int32_t FASTCALL tlb_protected_read( sh4addr_t addr, void *exc ) nkeynes@939: { nkeynes@973: mmu_urc++; nkeynes@939: RAISE_MEM_ERROR(EXC_TLB_PROT_READ, addr); nkeynes@1202: SH4_EXCEPTION_EXIT(); nkeynes@968: return 0; nkeynes@953: } nkeynes@953: nkeynes@975: static int32_t FASTCALL tlb_protected_read_for_write( sh4addr_t addr, void *exc ) nkeynes@975: { nkeynes@975: mmu_urc++; nkeynes@975: RAISE_MEM_ERROR(EXC_TLB_PROT_WRITE, addr); nkeynes@1202: SH4_EXCEPTION_EXIT(); nkeynes@975: return 0; nkeynes@939: } nkeynes@939: nkeynes@939: static int32_t FASTCALL tlb_protected_read_burst( unsigned char *dest, sh4addr_t addr, void *exc ) nkeynes@939: { nkeynes@973: mmu_urc++; nkeynes@939: RAISE_MEM_ERROR(EXC_TLB_PROT_READ, addr); nkeynes@1202: SH4_EXCEPTION_EXIT(); nkeynes@968: return 0; nkeynes@939: } nkeynes@939: nkeynes@939: static void FASTCALL tlb_protected_write( sh4addr_t addr, uint32_t val, void *exc ) nkeynes@939: { nkeynes@973: mmu_urc++; nkeynes@939: RAISE_MEM_ERROR(EXC_TLB_PROT_WRITE, addr); nkeynes@1202: SH4_EXCEPTION_EXIT(); nkeynes@939: } nkeynes@939: nkeynes@939: static void FASTCALL tlb_initial_write( sh4addr_t addr, uint32_t val, void *exc ) nkeynes@939: { nkeynes@973: mmu_urc++; nkeynes@939: RAISE_MEM_ERROR(EXC_INIT_PAGE_WRITE, addr); nkeynes@1202: SH4_EXCEPTION_EXIT(); nkeynes@939: } nkeynes@975: nkeynes@975: static int32_t FASTCALL tlb_initial_read_for_write( sh4addr_t addr, void *exc ) nkeynes@975: { nkeynes@975: mmu_urc++; nkeynes@975: RAISE_MEM_ERROR(EXC_INIT_PAGE_WRITE, addr); nkeynes@1202: SH4_EXCEPTION_EXIT(); nkeynes@975: return 0; nkeynes@975: } nkeynes@939: nkeynes@939: static int32_t FASTCALL tlb_multi_hit_read( sh4addr_t addr, void *exc ) nkeynes@939: { nkeynes@951: sh4_raise_tlb_multihit(addr); nkeynes@1202: SH4_EXCEPTION_EXIT(); nkeynes@968: return 0; nkeynes@939: } nkeynes@939: nkeynes@939: static int32_t FASTCALL tlb_multi_hit_read_burst( unsigned char *dest, sh4addr_t addr, void *exc ) nkeynes@939: { nkeynes@951: sh4_raise_tlb_multihit(addr); nkeynes@1202: SH4_EXCEPTION_EXIT(); nkeynes@968: return 0; nkeynes@939: } nkeynes@939: static void FASTCALL tlb_multi_hit_write( sh4addr_t addr, uint32_t val, void *exc ) nkeynes@939: { nkeynes@951: sh4_raise_tlb_multihit(addr); nkeynes@1202: SH4_EXCEPTION_EXIT(); nkeynes@939: } nkeynes@939: nkeynes@939: /** nkeynes@939: * Note: Per sec 4.6.4 of the SH7750 manual, SQ nkeynes@939: */ nkeynes@939: struct mem_region_fn mem_region_address_error = { nkeynes@939: (mem_read_fn_t)address_error_read, (mem_write_fn_t)address_error_write, nkeynes@939: (mem_read_fn_t)address_error_read, (mem_write_fn_t)address_error_write, nkeynes@939: (mem_read_fn_t)address_error_read, (mem_write_fn_t)address_error_write, nkeynes@946: (mem_read_burst_fn_t)address_error_read_burst, (mem_write_burst_fn_t)address_error_write, nkeynes@975: unmapped_prefetch, (mem_read_fn_t)address_error_read_for_write }; nkeynes@939: nkeynes@939: struct mem_region_fn mem_region_tlb_miss = { nkeynes@939: (mem_read_fn_t)tlb_miss_read, (mem_write_fn_t)tlb_miss_write, nkeynes@939: (mem_read_fn_t)tlb_miss_read, (mem_write_fn_t)tlb_miss_write, nkeynes@939: (mem_read_fn_t)tlb_miss_read, (mem_write_fn_t)tlb_miss_write, nkeynes@946: (mem_read_burst_fn_t)tlb_miss_read_burst, (mem_write_burst_fn_t)tlb_miss_write, nkeynes@975: unmapped_prefetch, (mem_read_fn_t)tlb_miss_read_for_write }; nkeynes@939: nkeynes@946: struct mem_region_fn mem_region_tlb_protected = { nkeynes@939: (mem_read_fn_t)tlb_protected_read, (mem_write_fn_t)tlb_protected_write, nkeynes@939: (mem_read_fn_t)tlb_protected_read, (mem_write_fn_t)tlb_protected_write, nkeynes@939: (mem_read_fn_t)tlb_protected_read, (mem_write_fn_t)tlb_protected_write, nkeynes@946: (mem_read_burst_fn_t)tlb_protected_read_burst, (mem_write_burst_fn_t)tlb_protected_write, nkeynes@975: unmapped_prefetch, (mem_read_fn_t)tlb_protected_read_for_write }; nkeynes@939: nkeynes@939: struct mem_region_fn mem_region_tlb_multihit = { nkeynes@939: (mem_read_fn_t)tlb_multi_hit_read, (mem_write_fn_t)tlb_multi_hit_write, nkeynes@939: (mem_read_fn_t)tlb_multi_hit_read, (mem_write_fn_t)tlb_multi_hit_write, nkeynes@939: (mem_read_fn_t)tlb_multi_hit_read, (mem_write_fn_t)tlb_multi_hit_write, nkeynes@946: (mem_read_burst_fn_t)tlb_multi_hit_read_burst, (mem_write_burst_fn_t)tlb_multi_hit_write, nkeynes@975: (mem_prefetch_fn_t)tlb_multi_hit_read, (mem_read_fn_t)tlb_multi_hit_read }; nkeynes@939: nkeynes@946: nkeynes@946: /* Store-queue regions */ nkeynes@946: /* These are a bit of a pain - the first 8 fields are controlled by SQMD, while nkeynes@946: * the final (prefetch) is controlled by the actual TLB settings (plus SQMD in nkeynes@946: * some cases), in contrast to the ordinary fields above. nkeynes@946: * nkeynes@946: * There is probably a simpler way to do this. nkeynes@946: */ nkeynes@946: nkeynes@946: struct mem_region_fn p4_region_storequeue = { nkeynes@946: ccn_storequeue_read_long, ccn_storequeue_write_long, nkeynes@946: unmapped_read_long, unmapped_write_long, /* TESTME: Officially only long access is supported */ nkeynes@946: unmapped_read_long, unmapped_write_long, nkeynes@946: unmapped_read_burst, unmapped_write_burst, nkeynes@975: ccn_storequeue_prefetch, unmapped_read_long }; nkeynes@946: nkeynes@946: struct mem_region_fn p4_region_storequeue_miss = { nkeynes@946: ccn_storequeue_read_long, ccn_storequeue_write_long, nkeynes@946: unmapped_read_long, unmapped_write_long, /* TESTME: Officially only long access is supported */ nkeynes@946: unmapped_read_long, unmapped_write_long, nkeynes@946: unmapped_read_burst, unmapped_write_burst, nkeynes@975: (mem_prefetch_fn_t)tlb_miss_read, unmapped_read_long }; nkeynes@946: nkeynes@946: struct mem_region_fn p4_region_storequeue_multihit = { nkeynes@946: ccn_storequeue_read_long, ccn_storequeue_write_long, nkeynes@946: unmapped_read_long, unmapped_write_long, /* TESTME: Officially only long access is supported */ nkeynes@946: unmapped_read_long, unmapped_write_long, nkeynes@946: unmapped_read_burst, unmapped_write_burst, nkeynes@975: (mem_prefetch_fn_t)tlb_multi_hit_read, unmapped_read_long }; nkeynes@946: nkeynes@946: struct mem_region_fn p4_region_storequeue_protected = { nkeynes@946: ccn_storequeue_read_long, ccn_storequeue_write_long, nkeynes@946: unmapped_read_long, unmapped_write_long, nkeynes@946: unmapped_read_long, unmapped_write_long, nkeynes@946: unmapped_read_burst, unmapped_write_burst, nkeynes@975: (mem_prefetch_fn_t)tlb_protected_read, unmapped_read_long }; nkeynes@946: nkeynes@946: struct mem_region_fn p4_region_storequeue_sqmd = { nkeynes@946: (mem_read_fn_t)address_error_read, (mem_write_fn_t)address_error_write, nkeynes@946: (mem_read_fn_t)address_error_read, (mem_write_fn_t)address_error_write, nkeynes@946: (mem_read_fn_t)address_error_read, (mem_write_fn_t)address_error_write, nkeynes@946: (mem_read_burst_fn_t)address_error_read_burst, (mem_write_burst_fn_t)address_error_write, nkeynes@975: (mem_prefetch_fn_t)address_error_read, (mem_read_fn_t)address_error_read_for_write }; nkeynes@939: nkeynes@946: struct mem_region_fn p4_region_storequeue_sqmd_miss = { nkeynes@946: (mem_read_fn_t)address_error_read, (mem_write_fn_t)address_error_write, nkeynes@946: (mem_read_fn_t)address_error_read, (mem_write_fn_t)address_error_write, nkeynes@946: (mem_read_fn_t)address_error_read, (mem_write_fn_t)address_error_write, nkeynes@946: (mem_read_burst_fn_t)address_error_read_burst, (mem_write_burst_fn_t)address_error_write, nkeynes@975: (mem_prefetch_fn_t)tlb_miss_read, (mem_read_fn_t)address_error_read_for_write }; nkeynes@946: nkeynes@946: struct mem_region_fn p4_region_storequeue_sqmd_multihit = { nkeynes@946: (mem_read_fn_t)address_error_read, (mem_write_fn_t)address_error_write, nkeynes@946: (mem_read_fn_t)address_error_read, (mem_write_fn_t)address_error_write, nkeynes@946: (mem_read_fn_t)address_error_read, (mem_write_fn_t)address_error_write, nkeynes@946: (mem_read_burst_fn_t)address_error_read_burst, (mem_write_burst_fn_t)address_error_write, nkeynes@975: (mem_prefetch_fn_t)tlb_multi_hit_read, (mem_read_fn_t)address_error_read_for_write }; nkeynes@946: nkeynes@946: struct mem_region_fn p4_region_storequeue_sqmd_protected = { nkeynes@946: (mem_read_fn_t)address_error_read, (mem_write_fn_t)address_error_write, nkeynes@946: (mem_read_fn_t)address_error_read, (mem_write_fn_t)address_error_write, nkeynes@946: (mem_read_fn_t)address_error_read, (mem_write_fn_t)address_error_write, nkeynes@946: (mem_read_burst_fn_t)address_error_read_burst, (mem_write_burst_fn_t)address_error_write, nkeynes@975: (mem_prefetch_fn_t)tlb_protected_read, (mem_read_fn_t)address_error_read_for_write }; nkeynes@946: