Search
lxdream.org :: lxdream/src/sh4/mmu.c
lxdream 0.9.1
released Jun 29
Download Now
filename src/sh4/mmu.c
changeset 971:886e1ec8447d
prev968:6fb1481859a4
next973:7434ac745eff
author nkeynes
date Thu Jan 22 02:58:13 2009 +0000 (13 years ago)
permissions -rw-r--r--
last change Fix 1k-entry allocation
Break asid remap into two passes for simplicity
file annotate diff log raw
nkeynes@550
     1
/**
nkeynes@586
     2
 * $Id$
nkeynes@826
     3
 *
nkeynes@953
     4
 * SH4 MMU implementation based on address space page maps. This module
nkeynes@953
     5
 * is responsible for all address decoding functions. 
nkeynes@550
     6
 *
nkeynes@550
     7
 * Copyright (c) 2005 Nathan Keynes.
nkeynes@550
     8
 *
nkeynes@550
     9
 * This program is free software; you can redistribute it and/or modify
nkeynes@550
    10
 * it under the terms of the GNU General Public License as published by
nkeynes@550
    11
 * the Free Software Foundation; either version 2 of the License, or
nkeynes@550
    12
 * (at your option) any later version.
nkeynes@550
    13
 *
nkeynes@550
    14
 * This program is distributed in the hope that it will be useful,
nkeynes@550
    15
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
nkeynes@550
    16
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
nkeynes@550
    17
 * GNU General Public License for more details.
nkeynes@550
    18
 */
nkeynes@550
    19
#define MODULE sh4_module
nkeynes@550
    20
nkeynes@550
    21
#include <stdio.h>
nkeynes@915
    22
#include <assert.h>
nkeynes@550
    23
#include "sh4/sh4mmio.h"
nkeynes@550
    24
#include "sh4/sh4core.h"
nkeynes@669
    25
#include "sh4/sh4trans.h"
nkeynes@953
    26
#include "dreamcast.h"
nkeynes@550
    27
#include "mem.h"
nkeynes@953
    28
#include "mmu.h"
nkeynes@550
    29
nkeynes@953
    30
#define RAISE_TLB_ERROR(code, vpn) sh4_raise_tlb_exception(code, vpn)
nkeynes@586
    31
#define RAISE_MEM_ERROR(code, vpn) \
nkeynes@586
    32
    MMIO_WRITE(MMU, TEA, vpn); \
nkeynes@586
    33
    MMIO_WRITE(MMU, PTEH, ((MMIO_READ(MMU, PTEH) & 0x000003FF) | (vpn&0xFFFFFC00))); \
nkeynes@586
    34
    sh4_raise_exception(code);
nkeynes@953
    35
#define RAISE_TLB_MULTIHIT_ERROR(vpn) sh4_raise_tlb_multihit(vpn)
nkeynes@586
    36
nkeynes@953
    37
/* An entry is a 1K entry if it's one of the mmu_utlb_1k_pages entries */
nkeynes@953
    38
#define IS_1K_PAGE_ENTRY(ent)  ( ((uintptr_t)(((struct utlb_1k_entry *)ent) - &mmu_utlb_1k_pages[0])) < UTLB_ENTRY_COUNT )
nkeynes@586
    39
nkeynes@953
    40
/* Primary address space (used directly by SH4 cores) */
nkeynes@953
    41
mem_region_fn_t *sh4_address_space;
nkeynes@953
    42
mem_region_fn_t *sh4_user_address_space;
nkeynes@586
    43
nkeynes@953
    44
/* Accessed from the UTLB accessor methods */
nkeynes@953
    45
uint32_t mmu_urc;
nkeynes@953
    46
uint32_t mmu_urb;
nkeynes@953
    47
static gboolean mmu_urc_overflow; /* If true, urc was set >= urb */  
nkeynes@586
    48
nkeynes@953
    49
/* Module globals */
nkeynes@550
    50
static struct itlb_entry mmu_itlb[ITLB_ENTRY_COUNT];
nkeynes@550
    51
static struct utlb_entry mmu_utlb[UTLB_ENTRY_COUNT];
nkeynes@953
    52
static struct utlb_page_entry mmu_utlb_pages[UTLB_ENTRY_COUNT];
nkeynes@550
    53
static uint32_t mmu_lrui;
nkeynes@586
    54
static uint32_t mmu_asid; // current asid
nkeynes@953
    55
static struct utlb_default_regions *mmu_user_storequeue_regions;
nkeynes@550
    56
nkeynes@953
    57
/* Structures for 1K page handling */
nkeynes@953
    58
static struct utlb_1k_entry mmu_utlb_1k_pages[UTLB_ENTRY_COUNT];
nkeynes@953
    59
static int mmu_utlb_1k_free_list[UTLB_ENTRY_COUNT];
nkeynes@953
    60
static int mmu_utlb_1k_free_index;
nkeynes@915
    61
nkeynes@550
    62
nkeynes@953
    63
/* Function prototypes */
nkeynes@550
    64
static void mmu_invalidate_tlb();
nkeynes@953
    65
static void mmu_utlb_register_all();
nkeynes@953
    66
static void mmu_utlb_remove_entry(int);
nkeynes@953
    67
static void mmu_utlb_insert_entry(int);
nkeynes@953
    68
static void mmu_register_mem_region( uint32_t start, uint32_t end, mem_region_fn_t fn );
nkeynes@953
    69
static void mmu_register_user_mem_region( uint32_t start, uint32_t end, mem_region_fn_t fn );
nkeynes@953
    70
static void mmu_set_tlb_enabled( int tlb_on );
nkeynes@953
    71
static void mmu_set_tlb_asid( uint32_t asid );
nkeynes@953
    72
static void mmu_set_storequeue_protected( int protected, int tlb_on );
nkeynes@953
    73
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@953
    74
static void mmu_utlb_remap_pages( gboolean remap_priv, gboolean remap_user, int entryNo );
nkeynes@953
    75
static gboolean mmu_utlb_unmap_pages( gboolean unmap_priv, gboolean unmap_user, sh4addr_t start_addr, int npages );
nkeynes@953
    76
static gboolean mmu_ext_page_remapped( sh4addr_t page, mem_region_fn_t fn, void *user_data );
nkeynes@953
    77
static void mmu_utlb_1k_init();
nkeynes@953
    78
static struct utlb_1k_entry *mmu_utlb_1k_alloc();
nkeynes@953
    79
static void mmu_utlb_1k_free( struct utlb_1k_entry *entry );
nkeynes@955
    80
static int mmu_read_urc();
nkeynes@550
    81
nkeynes@953
    82
static void FASTCALL tlb_miss_read( sh4addr_t addr, void *exc );
nkeynes@953
    83
static int32_t FASTCALL tlb_protected_read( sh4addr_t addr, void *exc );
nkeynes@953
    84
static void FASTCALL tlb_protected_write( sh4addr_t addr, uint32_t val, void *exc );
nkeynes@953
    85
static void FASTCALL tlb_initial_write( sh4addr_t addr, uint32_t val, void *exc );
nkeynes@953
    86
static uint32_t get_tlb_size_mask( uint32_t flags );
nkeynes@953
    87
static uint32_t get_tlb_size_pages( uint32_t flags );
nkeynes@550
    88
nkeynes@953
    89
#define DEFAULT_REGIONS 0
nkeynes@953
    90
#define DEFAULT_STOREQUEUE_REGIONS 1
nkeynes@953
    91
#define DEFAULT_STOREQUEUE_SQMD_REGIONS 2
nkeynes@586
    92
nkeynes@953
    93
static struct utlb_default_regions mmu_default_regions[3] = {
nkeynes@953
    94
        { &mem_region_tlb_miss, &mem_region_tlb_protected, &mem_region_tlb_multihit },
nkeynes@953
    95
        { &p4_region_storequeue_miss, &p4_region_storequeue_protected, &p4_region_storequeue_multihit },
nkeynes@953
    96
        { &p4_region_storequeue_sqmd_miss, &p4_region_storequeue_sqmd_protected, &p4_region_storequeue_sqmd_multihit } };
nkeynes@550
    97
nkeynes@953
    98
#define IS_STOREQUEUE_PROTECTED() (mmu_user_storequeue_regions == &mmu_default_regions[DEFAULT_STOREQUEUE_SQMD_REGIONS])
nkeynes@550
    99
nkeynes@953
   100
/*********************** Module public functions ****************************/
nkeynes@550
   101
nkeynes@953
   102
/**
nkeynes@953
   103
 * Allocate memory for the address space maps, and initialize them according
nkeynes@953
   104
 * to the default (reset) values. (TLB is disabled by default)
nkeynes@953
   105
 */
nkeynes@953
   106
                           
nkeynes@826
   107
void MMU_init()
nkeynes@550
   108
{
nkeynes@953
   109
    sh4_address_space = mem_alloc_pages( sizeof(mem_region_fn_t) * 256 );
nkeynes@953
   110
    sh4_user_address_space = mem_alloc_pages( sizeof(mem_region_fn_t) * 256 );
nkeynes@953
   111
    mmu_user_storequeue_regions = &mmu_default_regions[DEFAULT_STOREQUEUE_REGIONS];
nkeynes@953
   112
    
nkeynes@953
   113
    mmu_set_tlb_enabled(0);
nkeynes@953
   114
    mmu_register_user_mem_region( 0x80000000, 0x00000000, &mem_region_address_error );
nkeynes@953
   115
    mmu_register_user_mem_region( 0xE0000000, 0xE4000000, &p4_region_storequeue );                                
nkeynes@953
   116
    
nkeynes@953
   117
    /* Setup P4 tlb/cache access regions */
nkeynes@953
   118
    mmu_register_mem_region( 0xE0000000, 0xE4000000, &p4_region_storequeue );
nkeynes@953
   119
    mmu_register_mem_region( 0xE4000000, 0xF0000000, &mem_region_unmapped );
nkeynes@953
   120
    mmu_register_mem_region( 0xF0000000, 0xF1000000, &p4_region_icache_addr );
nkeynes@953
   121
    mmu_register_mem_region( 0xF1000000, 0xF2000000, &p4_region_icache_data );
nkeynes@953
   122
    mmu_register_mem_region( 0xF2000000, 0xF3000000, &p4_region_itlb_addr );
nkeynes@953
   123
    mmu_register_mem_region( 0xF3000000, 0xF4000000, &p4_region_itlb_data );
nkeynes@953
   124
    mmu_register_mem_region( 0xF4000000, 0xF5000000, &p4_region_ocache_addr );
nkeynes@953
   125
    mmu_register_mem_region( 0xF5000000, 0xF6000000, &p4_region_ocache_data );
nkeynes@953
   126
    mmu_register_mem_region( 0xF6000000, 0xF7000000, &p4_region_utlb_addr );
nkeynes@953
   127
    mmu_register_mem_region( 0xF7000000, 0xF8000000, &p4_region_utlb_data );
nkeynes@953
   128
    mmu_register_mem_region( 0xF8000000, 0x00000000, &mem_region_unmapped );
nkeynes@953
   129
    
nkeynes@953
   130
    /* Setup P4 control region */
nkeynes@953
   131
    mmu_register_mem_region( 0xFF000000, 0xFF001000, &mmio_region_MMU.fn );
nkeynes@953
   132
    mmu_register_mem_region( 0xFF100000, 0xFF101000, &mmio_region_PMM.fn );
nkeynes@953
   133
    mmu_register_mem_region( 0xFF200000, 0xFF201000, &mmio_region_UBC.fn );
nkeynes@953
   134
    mmu_register_mem_region( 0xFF800000, 0xFF801000, &mmio_region_BSC.fn );
nkeynes@953
   135
    mmu_register_mem_region( 0xFF900000, 0xFFA00000, &mem_region_unmapped ); // SDMR2 + SDMR3
nkeynes@953
   136
    mmu_register_mem_region( 0xFFA00000, 0xFFA01000, &mmio_region_DMAC.fn );
nkeynes@953
   137
    mmu_register_mem_region( 0xFFC00000, 0xFFC01000, &mmio_region_CPG.fn );
nkeynes@953
   138
    mmu_register_mem_region( 0xFFC80000, 0xFFC81000, &mmio_region_RTC.fn );
nkeynes@953
   139
    mmu_register_mem_region( 0xFFD00000, 0xFFD01000, &mmio_region_INTC.fn );
nkeynes@953
   140
    mmu_register_mem_region( 0xFFD80000, 0xFFD81000, &mmio_region_TMU.fn );
nkeynes@953
   141
    mmu_register_mem_region( 0xFFE00000, 0xFFE01000, &mmio_region_SCI.fn );
nkeynes@953
   142
    mmu_register_mem_region( 0xFFE80000, 0xFFE81000, &mmio_region_SCIF.fn );
nkeynes@953
   143
    mmu_register_mem_region( 0xFFF00000, 0xFFF01000, &mem_region_unmapped ); // H-UDI
nkeynes@953
   144
    
nkeynes@953
   145
    register_mem_page_remapped_hook( mmu_ext_page_remapped, NULL );
nkeynes@953
   146
    mmu_utlb_1k_init();
nkeynes@953
   147
    
nkeynes@960
   148
    /* Ensure the code regions are executable (64-bit only). Although it might
nkeynes@960
   149
     * be more portable to mmap these at runtime rather than using static decls
nkeynes@960
   150
     */
nkeynes@960
   151
#if SIZEOF_VOID_P == 8
nkeynes@953
   152
    mem_unprotect( mmu_utlb_pages, sizeof(mmu_utlb_pages) );
nkeynes@953
   153
    mem_unprotect( mmu_utlb_1k_pages, sizeof(mmu_utlb_1k_pages) );
nkeynes@960
   154
#endif
nkeynes@550
   155
}
nkeynes@550
   156
nkeynes@550
   157
void MMU_reset()
nkeynes@550
   158
{
nkeynes@550
   159
    mmio_region_MMU_write( CCR, 0 );
nkeynes@586
   160
    mmio_region_MMU_write( MMUCR, 0 );
nkeynes@550
   161
}
nkeynes@550
   162
nkeynes@550
   163
void MMU_save_state( FILE *f )
nkeynes@550
   164
{
nkeynes@955
   165
    mmu_read_urc();   
nkeynes@550
   166
    fwrite( &mmu_itlb, sizeof(mmu_itlb), 1, f );
nkeynes@550
   167
    fwrite( &mmu_utlb, sizeof(mmu_utlb), 1, f );
nkeynes@586
   168
    fwrite( &mmu_urc, sizeof(mmu_urc), 1, f );
nkeynes@586
   169
    fwrite( &mmu_urb, sizeof(mmu_urb), 1, f );
nkeynes@586
   170
    fwrite( &mmu_lrui, sizeof(mmu_lrui), 1, f );
nkeynes@586
   171
    fwrite( &mmu_asid, sizeof(mmu_asid), 1, f );
nkeynes@550
   172
}
nkeynes@550
   173
nkeynes@550
   174
int MMU_load_state( FILE *f )
nkeynes@550
   175
{
nkeynes@550
   176
    if( fread( &mmu_itlb, sizeof(mmu_itlb), 1, f ) != 1 ) {
nkeynes@736
   177
        return 1;
nkeynes@550
   178
    }
nkeynes@550
   179
    if( fread( &mmu_utlb, sizeof(mmu_utlb), 1, f ) != 1 ) {
nkeynes@736
   180
        return 1;
nkeynes@550
   181
    }
nkeynes@586
   182
    if( fread( &mmu_urc, sizeof(mmu_urc), 1, f ) != 1 ) {
nkeynes@736
   183
        return 1;
nkeynes@586
   184
    }
nkeynes@586
   185
    if( fread( &mmu_urc, sizeof(mmu_urb), 1, f ) != 1 ) {
nkeynes@736
   186
        return 1;
nkeynes@586
   187
    }
nkeynes@586
   188
    if( fread( &mmu_lrui, sizeof(mmu_lrui), 1, f ) != 1 ) {
nkeynes@736
   189
        return 1;
nkeynes@586
   190
    }
nkeynes@586
   191
    if( fread( &mmu_asid, sizeof(mmu_asid), 1, f ) != 1 ) {
nkeynes@736
   192
        return 1;
nkeynes@586
   193
    }
nkeynes@953
   194
nkeynes@953
   195
    uint32_t mmucr = MMIO_READ(MMU,MMUCR);
nkeynes@953
   196
    mmu_urc_overflow = mmu_urc >= mmu_urb;
nkeynes@953
   197
    mmu_set_tlb_enabled(mmucr&MMUCR_AT);
nkeynes@953
   198
    mmu_set_storequeue_protected(mmucr&MMUCR_SQMD, mmucr&MMUCR_AT);
nkeynes@550
   199
    return 0;
nkeynes@550
   200
}
nkeynes@550
   201
nkeynes@550
   202
/**
nkeynes@550
   203
 * LDTLB instruction implementation. Copies PTEH, PTEL and PTEA into the UTLB
nkeynes@550
   204
 * entry identified by MMUCR.URC. Does not modify MMUCR or the ITLB.
nkeynes@550
   205
 */
nkeynes@550
   206
void MMU_ldtlb()
nkeynes@550
   207
{
nkeynes@955
   208
    int urc = mmu_read_urc();
nkeynes@955
   209
    if( mmu_utlb[urc].flags & TLB_VALID )
nkeynes@955
   210
        mmu_utlb_remove_entry( urc );
nkeynes@955
   211
    mmu_utlb[urc].vpn = MMIO_READ(MMU, PTEH) & 0xFFFFFC00;
nkeynes@955
   212
    mmu_utlb[urc].asid = MMIO_READ(MMU, PTEH) & 0x000000FF;
nkeynes@955
   213
    mmu_utlb[urc].ppn = MMIO_READ(MMU, PTEL) & 0x1FFFFC00;
nkeynes@955
   214
    mmu_utlb[urc].flags = MMIO_READ(MMU, PTEL) & 0x00001FF;
nkeynes@955
   215
    mmu_utlb[urc].pcmcia = MMIO_READ(MMU, PTEA);
nkeynes@955
   216
    mmu_utlb[urc].mask = get_tlb_size_mask(mmu_utlb[urc].flags);
nkeynes@955
   217
    if( mmu_utlb[urc].flags & TLB_VALID )
nkeynes@955
   218
        mmu_utlb_insert_entry( urc );
nkeynes@550
   219
}
nkeynes@550
   220
nkeynes@953
   221
nkeynes@953
   222
MMIO_REGION_READ_FN( MMU, reg )
nkeynes@953
   223
{
nkeynes@953
   224
    reg &= 0xFFF;
nkeynes@953
   225
    switch( reg ) {
nkeynes@953
   226
    case MMUCR:
nkeynes@955
   227
        return MMIO_READ( MMU, MMUCR) | (mmu_read_urc()<<10) | ((mmu_urb&0x3F)<<18) | (mmu_lrui<<26);
nkeynes@953
   228
    default:
nkeynes@953
   229
        return MMIO_READ( MMU, reg );
nkeynes@953
   230
    }
nkeynes@953
   231
}
nkeynes@953
   232
nkeynes@953
   233
MMIO_REGION_WRITE_FN( MMU, reg, val )
nkeynes@953
   234
{
nkeynes@953
   235
    uint32_t tmp;
nkeynes@953
   236
    reg &= 0xFFF;
nkeynes@953
   237
    switch(reg) {
nkeynes@953
   238
    case SH4VER:
nkeynes@953
   239
        return;
nkeynes@953
   240
    case PTEH:
nkeynes@953
   241
        val &= 0xFFFFFCFF;
nkeynes@953
   242
        if( (val & 0xFF) != mmu_asid ) {
nkeynes@953
   243
            mmu_set_tlb_asid( val&0xFF );
nkeynes@953
   244
            sh4_icache.page_vma = -1; // invalidate icache as asid has changed
nkeynes@953
   245
        }
nkeynes@953
   246
        break;
nkeynes@953
   247
    case PTEL:
nkeynes@953
   248
        val &= 0x1FFFFDFF;
nkeynes@953
   249
        break;
nkeynes@953
   250
    case PTEA:
nkeynes@953
   251
        val &= 0x0000000F;
nkeynes@953
   252
        break;
nkeynes@953
   253
    case TRA:
nkeynes@953
   254
        val &= 0x000003FC;
nkeynes@953
   255
        break;
nkeynes@953
   256
    case EXPEVT:
nkeynes@953
   257
    case INTEVT:
nkeynes@953
   258
        val &= 0x00000FFF;
nkeynes@953
   259
        break;
nkeynes@953
   260
    case MMUCR:
nkeynes@953
   261
        if( val & MMUCR_TI ) {
nkeynes@953
   262
            mmu_invalidate_tlb();
nkeynes@953
   263
        }
nkeynes@953
   264
        mmu_urc = (val >> 10) & 0x3F;
nkeynes@953
   265
        mmu_urb = (val >> 18) & 0x3F;
nkeynes@953
   266
        if( mmu_urb == 0 ) {
nkeynes@953
   267
            mmu_urb = 0x40;
nkeynes@953
   268
        } else if( mmu_urc >= mmu_urb ) {
nkeynes@953
   269
            mmu_urc_overflow = TRUE;
nkeynes@953
   270
        }
nkeynes@953
   271
        mmu_lrui = (val >> 26) & 0x3F;
nkeynes@953
   272
        val &= 0x00000301;
nkeynes@953
   273
        tmp = MMIO_READ( MMU, MMUCR );
nkeynes@953
   274
        if( (val ^ tmp) & (MMUCR_SQMD) ) {
nkeynes@953
   275
            mmu_set_storequeue_protected( val & MMUCR_SQMD, val&MMUCR_AT );
nkeynes@953
   276
        }
nkeynes@953
   277
        if( (val ^ tmp) & (MMUCR_AT) ) {
nkeynes@953
   278
            // AT flag has changed state - flush the xlt cache as all bets
nkeynes@953
   279
            // are off now. We also need to force an immediate exit from the
nkeynes@953
   280
            // current block
nkeynes@953
   281
            mmu_set_tlb_enabled( val & MMUCR_AT );
nkeynes@953
   282
            MMIO_WRITE( MMU, MMUCR, val );
nkeynes@953
   283
            sh4_core_exit( CORE_EXIT_FLUSH_ICACHE );
nkeynes@953
   284
            xlat_flush_cache(); // If we're not running, flush the cache anyway
nkeynes@953
   285
        }
nkeynes@953
   286
        break;
nkeynes@953
   287
    case CCR:
nkeynes@953
   288
        CCN_set_cache_control( val );
nkeynes@953
   289
        val &= 0x81A7;
nkeynes@953
   290
        break;
nkeynes@953
   291
    case MMUUNK1:
nkeynes@953
   292
        /* Note that if the high bit is set, this appears to reset the machine.
nkeynes@953
   293
         * Not emulating this behaviour yet until we know why...
nkeynes@953
   294
         */
nkeynes@953
   295
        val &= 0x00010007;
nkeynes@953
   296
        break;
nkeynes@953
   297
    case QACR0:
nkeynes@953
   298
    case QACR1:
nkeynes@953
   299
        val &= 0x0000001C;
nkeynes@953
   300
        break;
nkeynes@953
   301
    case PMCR1:
nkeynes@953
   302
        PMM_write_control(0, val);
nkeynes@953
   303
        val &= 0x0000C13F;
nkeynes@953
   304
        break;
nkeynes@953
   305
    case PMCR2:
nkeynes@953
   306
        PMM_write_control(1, val);
nkeynes@953
   307
        val &= 0x0000C13F;
nkeynes@953
   308
        break;
nkeynes@953
   309
    default:
nkeynes@953
   310
        break;
nkeynes@953
   311
    }
nkeynes@953
   312
    MMIO_WRITE( MMU, reg, val );
nkeynes@953
   313
}
nkeynes@953
   314
nkeynes@953
   315
/********************** 1K Page handling ***********************/
nkeynes@953
   316
/* Since we use 4K pages as our native page size, 1K pages need a bit of extra
nkeynes@953
   317
 * effort to manage - we justify this on the basis that most programs won't
nkeynes@953
   318
 * actually use 1K pages, so we may as well optimize for the common case.
nkeynes@953
   319
 * 
nkeynes@953
   320
 * Implementation uses an intermediate page entry (the utlb_1k_entry) that
nkeynes@953
   321
 * redirects requests to the 'real' page entry. These are allocated on an
nkeynes@953
   322
 * as-needed basis, and returned to the pool when all subpages are empty.
nkeynes@953
   323
 */ 
nkeynes@953
   324
static void mmu_utlb_1k_init()
nkeynes@953
   325
{
nkeynes@953
   326
    int i;
nkeynes@953
   327
    for( i=0; i<UTLB_ENTRY_COUNT; i++ ) {
nkeynes@953
   328
        mmu_utlb_1k_free_list[i] = i;
nkeynes@953
   329
        mmu_utlb_1k_init_vtable( &mmu_utlb_1k_pages[i] );
nkeynes@953
   330
    }
nkeynes@953
   331
    mmu_utlb_1k_free_index = 0;
nkeynes@953
   332
}
nkeynes@953
   333
nkeynes@953
   334
static struct utlb_1k_entry *mmu_utlb_1k_alloc()
nkeynes@953
   335
{
nkeynes@953
   336
    assert( mmu_utlb_1k_free_index < UTLB_ENTRY_COUNT );
nkeynes@971
   337
    struct utlb_1k_entry *entry = &mmu_utlb_1k_pages[mmu_utlb_1k_free_list[mmu_utlb_1k_free_index++]];
nkeynes@953
   338
    return entry;
nkeynes@953
   339
}    
nkeynes@953
   340
nkeynes@953
   341
static void mmu_utlb_1k_free( struct utlb_1k_entry *ent )
nkeynes@953
   342
{
nkeynes@953
   343
    unsigned int entryNo = ent - &mmu_utlb_1k_pages[0];
nkeynes@953
   344
    assert( entryNo < UTLB_ENTRY_COUNT );
nkeynes@953
   345
    assert( mmu_utlb_1k_free_index > 0 );
nkeynes@953
   346
    mmu_utlb_1k_free_list[--mmu_utlb_1k_free_index] = entryNo;
nkeynes@953
   347
}
nkeynes@953
   348
nkeynes@953
   349
nkeynes@953
   350
/********************** Address space maintenance *************************/
nkeynes@953
   351
nkeynes@953
   352
/**
nkeynes@953
   353
 * MMU accessor functions just increment URC - fixup here if necessary
nkeynes@953
   354
 */
nkeynes@955
   355
static int mmu_read_urc()
nkeynes@953
   356
{
nkeynes@953
   357
    if( mmu_urc_overflow ) {
nkeynes@953
   358
        if( mmu_urc >= 0x40 ) {
nkeynes@953
   359
            mmu_urc_overflow = FALSE;
nkeynes@953
   360
            mmu_urc -= 0x40;
nkeynes@953
   361
            mmu_urc %= mmu_urb;
nkeynes@953
   362
        }
nkeynes@953
   363
    } else {
nkeynes@953
   364
        mmu_urc %= mmu_urb;
nkeynes@953
   365
    }
nkeynes@955
   366
    return mmu_urc;
nkeynes@953
   367
}
nkeynes@953
   368
nkeynes@953
   369
static void mmu_register_mem_region( uint32_t start, uint32_t end, mem_region_fn_t fn )
nkeynes@953
   370
{
nkeynes@953
   371
    int count = (end - start) >> 12;
nkeynes@953
   372
    mem_region_fn_t *ptr = &sh4_address_space[start>>12];
nkeynes@953
   373
    while( count-- > 0 ) {
nkeynes@953
   374
        *ptr++ = fn;
nkeynes@953
   375
    }
nkeynes@953
   376
}
nkeynes@953
   377
static void mmu_register_user_mem_region( uint32_t start, uint32_t end, mem_region_fn_t fn )
nkeynes@953
   378
{
nkeynes@953
   379
    int count = (end - start) >> 12;
nkeynes@953
   380
    mem_region_fn_t *ptr = &sh4_user_address_space[start>>12];
nkeynes@953
   381
    while( count-- > 0 ) {
nkeynes@953
   382
        *ptr++ = fn;
nkeynes@953
   383
    }
nkeynes@953
   384
}
nkeynes@953
   385
nkeynes@953
   386
static gboolean mmu_ext_page_remapped( sh4addr_t page, mem_region_fn_t fn, void *user_data )
nkeynes@953
   387
{
nkeynes@953
   388
    int i;
nkeynes@953
   389
    if( (MMIO_READ(MMU,MMUCR)) & MMUCR_AT ) {
nkeynes@953
   390
        /* TLB on */
nkeynes@953
   391
        sh4_address_space[(page|0x80000000)>>12] = fn; /* Direct map to P1 and P2 */
nkeynes@953
   392
        sh4_address_space[(page|0xA0000000)>>12] = fn;
nkeynes@953
   393
        /* Scan UTLB and update any direct-referencing entries */
nkeynes@953
   394
    } else {
nkeynes@953
   395
        /* Direct map to U0, P0, P1, P2, P3 */
nkeynes@953
   396
        for( i=0; i<= 0xC0000000; i+= 0x20000000 ) {
nkeynes@953
   397
            sh4_address_space[(page|i)>>12] = fn;
nkeynes@953
   398
        }
nkeynes@953
   399
        for( i=0; i < 0x80000000; i+= 0x20000000 ) {
nkeynes@953
   400
            sh4_user_address_space[(page|i)>>12] = fn;
nkeynes@953
   401
        }
nkeynes@953
   402
    }
nkeynes@963
   403
    return TRUE;
nkeynes@953
   404
}
nkeynes@953
   405
nkeynes@953
   406
static void mmu_set_tlb_enabled( int tlb_on )
nkeynes@953
   407
{
nkeynes@953
   408
    mem_region_fn_t *ptr, *uptr;
nkeynes@953
   409
    int i;
nkeynes@953
   410
    
nkeynes@953
   411
    /* Reset the storequeue area */
nkeynes@953
   412
nkeynes@953
   413
    if( tlb_on ) {
nkeynes@953
   414
        mmu_register_mem_region(0x00000000, 0x80000000, &mem_region_tlb_miss );
nkeynes@953
   415
        mmu_register_mem_region(0xC0000000, 0xE0000000, &mem_region_tlb_miss );
nkeynes@953
   416
        mmu_register_user_mem_region(0x00000000, 0x80000000, &mem_region_tlb_miss );
nkeynes@953
   417
        
nkeynes@953
   418
        /* Default SQ prefetch goes to TLB miss (?) */
nkeynes@953
   419
        mmu_register_mem_region( 0xE0000000, 0xE4000000, &p4_region_storequeue_miss );
nkeynes@953
   420
        mmu_register_user_mem_region( 0xE0000000, 0xE4000000, mmu_user_storequeue_regions->tlb_miss );
nkeynes@953
   421
        mmu_utlb_register_all();
nkeynes@953
   422
    } else {
nkeynes@953
   423
        for( i=0, ptr = sh4_address_space; i<7; i++, ptr += LXDREAM_PAGE_TABLE_ENTRIES ) {
nkeynes@953
   424
            memcpy( ptr, ext_address_space, sizeof(mem_region_fn_t) * LXDREAM_PAGE_TABLE_ENTRIES );
nkeynes@953
   425
        }
nkeynes@953
   426
        for( i=0, ptr = sh4_user_address_space; i<4; i++, ptr += LXDREAM_PAGE_TABLE_ENTRIES ) {
nkeynes@953
   427
            memcpy( ptr, ext_address_space, sizeof(mem_region_fn_t) * LXDREAM_PAGE_TABLE_ENTRIES );
nkeynes@953
   428
        }
nkeynes@953
   429
nkeynes@953
   430
        mmu_register_mem_region( 0xE0000000, 0xE4000000, &p4_region_storequeue );
nkeynes@953
   431
        if( IS_STOREQUEUE_PROTECTED() ) {
nkeynes@953
   432
            mmu_register_user_mem_region( 0xE0000000, 0xE4000000, &p4_region_storequeue_sqmd );
nkeynes@953
   433
        } else {
nkeynes@953
   434
            mmu_register_user_mem_region( 0xE0000000, 0xE4000000, &p4_region_storequeue );
nkeynes@953
   435
        }
nkeynes@953
   436
    }
nkeynes@953
   437
    
nkeynes@953
   438
}
nkeynes@953
   439
nkeynes@953
   440
/**
nkeynes@953
   441
 * Flip the SQMD switch - this is rather expensive, so will need to be changed if
nkeynes@953
   442
 * anything expects to do this frequently.
nkeynes@953
   443
 */
nkeynes@953
   444
static void mmu_set_storequeue_protected( int protected, int tlb_on ) 
nkeynes@953
   445
{
nkeynes@953
   446
    mem_region_fn_t nontlb_region;
nkeynes@953
   447
    int i;
nkeynes@953
   448
nkeynes@953
   449
    if( protected ) {
nkeynes@953
   450
        mmu_user_storequeue_regions = &mmu_default_regions[DEFAULT_STOREQUEUE_SQMD_REGIONS];
nkeynes@953
   451
        nontlb_region = &p4_region_storequeue_sqmd;
nkeynes@953
   452
    } else {
nkeynes@953
   453
        mmu_user_storequeue_regions = &mmu_default_regions[DEFAULT_STOREQUEUE_REGIONS];
nkeynes@953
   454
        nontlb_region = &p4_region_storequeue; 
nkeynes@953
   455
    }
nkeynes@953
   456
nkeynes@953
   457
    if( tlb_on ) {
nkeynes@953
   458
        mmu_register_user_mem_region( 0xE0000000, 0xE4000000, mmu_user_storequeue_regions->tlb_miss );
nkeynes@953
   459
        for( i=0; i<UTLB_ENTRY_COUNT; i++ ) {
nkeynes@953
   460
            if( (mmu_utlb[i].vpn & 0xFC000000) == 0xE0000000 ) {
nkeynes@953
   461
                mmu_utlb_insert_entry(i);
nkeynes@953
   462
            }
nkeynes@953
   463
        }
nkeynes@953
   464
    } else {
nkeynes@953
   465
        mmu_register_user_mem_region( 0xE0000000, 0xE4000000, nontlb_region ); 
nkeynes@953
   466
    }
nkeynes@953
   467
    
nkeynes@953
   468
}
nkeynes@953
   469
nkeynes@953
   470
static void mmu_set_tlb_asid( uint32_t asid )
nkeynes@953
   471
{
nkeynes@953
   472
    /* Scan for pages that need to be remapped */
nkeynes@953
   473
    int i;
nkeynes@953
   474
    if( IS_SV_ENABLED() ) {
nkeynes@953
   475
        for( i=0; i<UTLB_ENTRY_COUNT; i++ ) {
nkeynes@971
   476
            if( mmu_utlb[i].asid == mmu_asid && 
nkeynes@971
   477
                (mmu_utlb[i].flags & (TLB_VALID|TLB_SHARE)) == (TLB_VALID) ) {
nkeynes@971
   478
                // Matches old ASID - unmap out
nkeynes@971
   479
                if( !mmu_utlb_unmap_pages( FALSE, TRUE, mmu_utlb[i].vpn&mmu_utlb[i].mask,
nkeynes@971
   480
                        get_tlb_size_pages(mmu_utlb[i].flags) ) )
nkeynes@971
   481
                    mmu_utlb_remap_pages( FALSE, TRUE, i );
nkeynes@971
   482
            }
nkeynes@971
   483
        }
nkeynes@971
   484
        for( i=0; i<UTLB_ENTRY_COUNT; i++ ) {
nkeynes@971
   485
            if( mmu_utlb[i].asid == asid && 
nkeynes@971
   486
                (mmu_utlb[i].flags & (TLB_VALID|TLB_SHARE)) == (TLB_VALID) ) {
nkeynes@971
   487
                // Matches new ASID - map in
nkeynes@971
   488
                mmu_utlb_map_pages( NULL, mmu_utlb_pages[i].user_fn, 
nkeynes@971
   489
                        mmu_utlb[i].vpn&mmu_utlb[i].mask, 
nkeynes@971
   490
                        get_tlb_size_pages(mmu_utlb[i].flags) );
nkeynes@953
   491
            }
nkeynes@953
   492
        }
nkeynes@953
   493
    } else {
nkeynes@953
   494
        // Remap both Priv+user pages
nkeynes@953
   495
        for( i=0; i<UTLB_ENTRY_COUNT; i++ ) {
nkeynes@971
   496
            if( mmu_utlb[i].asid == mmu_asid &&
nkeynes@971
   497
                (mmu_utlb[i].flags & (TLB_VALID|TLB_SHARE)) == (TLB_VALID) ) {
nkeynes@971
   498
                if( !mmu_utlb_unmap_pages( TRUE, TRUE, mmu_utlb[i].vpn&mmu_utlb[i].mask,
nkeynes@971
   499
                        get_tlb_size_pages(mmu_utlb[i].flags) ) )
nkeynes@971
   500
                    mmu_utlb_remap_pages( TRUE, TRUE, i );
nkeynes@971
   501
            }
nkeynes@971
   502
        }
nkeynes@971
   503
        for( i=0; i<UTLB_ENTRY_COUNT; i++ ) {
nkeynes@971
   504
            if( mmu_utlb[i].asid == asid &&
nkeynes@971
   505
                (mmu_utlb[i].flags & (TLB_VALID|TLB_SHARE)) == (TLB_VALID) ) {
nkeynes@971
   506
                mmu_utlb_map_pages( &mmu_utlb_pages[i].fn, mmu_utlb_pages[i].user_fn, 
nkeynes@971
   507
                        mmu_utlb[i].vpn&mmu_utlb[i].mask, 
nkeynes@971
   508
                        get_tlb_size_pages(mmu_utlb[i].flags) );  
nkeynes@953
   509
            }
nkeynes@953
   510
        }
nkeynes@953
   511
    }
nkeynes@953
   512
    
nkeynes@953
   513
    mmu_asid = asid;
nkeynes@953
   514
}
nkeynes@953
   515
nkeynes@953
   516
static uint32_t get_tlb_size_mask( uint32_t flags )
nkeynes@953
   517
{
nkeynes@953
   518
    switch( flags & TLB_SIZE_MASK ) {
nkeynes@953
   519
    case TLB_SIZE_1K: return MASK_1K;
nkeynes@953
   520
    case TLB_SIZE_4K: return MASK_4K;
nkeynes@953
   521
    case TLB_SIZE_64K: return MASK_64K;
nkeynes@953
   522
    case TLB_SIZE_1M: return MASK_1M;
nkeynes@953
   523
    default: return 0; /* Unreachable */
nkeynes@953
   524
    }
nkeynes@953
   525
}
nkeynes@953
   526
static uint32_t get_tlb_size_pages( uint32_t flags )
nkeynes@953
   527
{
nkeynes@953
   528
    switch( flags & TLB_SIZE_MASK ) {
nkeynes@953
   529
    case TLB_SIZE_1K: return 0;
nkeynes@953
   530
    case TLB_SIZE_4K: return 1;
nkeynes@953
   531
    case TLB_SIZE_64K: return 16;
nkeynes@953
   532
    case TLB_SIZE_1M: return 256;
nkeynes@953
   533
    default: return 0; /* Unreachable */
nkeynes@953
   534
    }
nkeynes@953
   535
}
nkeynes@953
   536
nkeynes@953
   537
/**
nkeynes@953
   538
 * Add a new TLB entry mapping to the address space table. If any of the pages
nkeynes@953
   539
 * are already mapped, they are mapped to the TLB multi-hit page instead.
nkeynes@953
   540
 * @return FALSE if a TLB multihit situation was detected, otherwise TRUE.
nkeynes@953
   541
 */ 
nkeynes@953
   542
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@953
   543
{
nkeynes@953
   544
    mem_region_fn_t *ptr = &sh4_address_space[start_addr >> 12];
nkeynes@953
   545
    mem_region_fn_t *uptr = &sh4_user_address_space[start_addr >> 12];
nkeynes@953
   546
    struct utlb_default_regions *privdefs = &mmu_default_regions[DEFAULT_REGIONS];
nkeynes@953
   547
    struct utlb_default_regions *userdefs = privdefs;    
nkeynes@953
   548
    
nkeynes@953
   549
    gboolean mapping_ok = TRUE;
nkeynes@953
   550
    int i;
nkeynes@953
   551
    
nkeynes@953
   552
    if( (start_addr & 0xFC000000) == 0xE0000000 ) {
nkeynes@953
   553
        /* Storequeue mapping */
nkeynes@953
   554
        privdefs = &mmu_default_regions[DEFAULT_STOREQUEUE_REGIONS];
nkeynes@953
   555
        userdefs = mmu_user_storequeue_regions;
nkeynes@953
   556
    } else if( (start_addr & 0xE0000000) == 0xC0000000 ) {
nkeynes@953
   557
        user_page = NULL; /* No user access to P3 region */
nkeynes@953
   558
    } else if( start_addr >= 0x80000000 ) {
nkeynes@953
   559
        return TRUE; // No mapping - legal but meaningless
nkeynes@953
   560
    }
nkeynes@953
   561
nkeynes@953
   562
    if( npages == 0 ) {
nkeynes@953
   563
        struct utlb_1k_entry *ent;
nkeynes@953
   564
        int i, idx = (start_addr >> 10) & 0x03;
nkeynes@953
   565
        if( IS_1K_PAGE_ENTRY(*ptr) ) {
nkeynes@953
   566
            ent = (struct utlb_1k_entry *)*ptr;
nkeynes@953
   567
        } else {
nkeynes@953
   568
            ent = mmu_utlb_1k_alloc();
nkeynes@953
   569
            /* New 1K struct - init to previous contents of region */
nkeynes@953
   570
            for( i=0; i<4; i++ ) {
nkeynes@953
   571
                ent->subpages[i] = *ptr;
nkeynes@953
   572
                ent->user_subpages[i] = *uptr;
nkeynes@953
   573
            }
nkeynes@953
   574
            *ptr = &ent->fn;
nkeynes@953
   575
            *uptr = &ent->user_fn;
nkeynes@953
   576
        }
nkeynes@953
   577
        
nkeynes@953
   578
        if( priv_page != NULL ) {
nkeynes@953
   579
            if( ent->subpages[idx] == privdefs->tlb_miss ) {
nkeynes@953
   580
                ent->subpages[idx] = priv_page;
nkeynes@953
   581
            } else {
nkeynes@953
   582
                mapping_ok = FALSE;
nkeynes@953
   583
                ent->subpages[idx] = privdefs->tlb_multihit;
nkeynes@953
   584
            }
nkeynes@953
   585
        }
nkeynes@953
   586
        if( user_page != NULL ) {
nkeynes@953
   587
            if( ent->user_subpages[idx] == userdefs->tlb_miss ) {
nkeynes@953
   588
                ent->user_subpages[idx] = user_page;
nkeynes@953
   589
            } else {
nkeynes@953
   590
                mapping_ok = FALSE;
nkeynes@953
   591
                ent->user_subpages[idx] = userdefs->tlb_multihit;
nkeynes@953
   592
            }
nkeynes@953
   593
        }
nkeynes@953
   594
        
nkeynes@953
   595
    } else {
nkeynes@953
   596
        if( priv_page != NULL ) {
nkeynes@953
   597
            /* Privileged mapping only */
nkeynes@953
   598
            for( i=0; i<npages; i++ ) {
nkeynes@953
   599
                if( *ptr == privdefs->tlb_miss ) {
nkeynes@953
   600
                    *ptr++ = priv_page;
nkeynes@953
   601
                } else {
nkeynes@953
   602
                    mapping_ok = FALSE;
nkeynes@953
   603
                    *ptr++ = privdefs->tlb_multihit;
nkeynes@953
   604
                }
nkeynes@953
   605
            }
nkeynes@953
   606
        }
nkeynes@953
   607
        if( user_page != NULL ) {
nkeynes@953
   608
            /* User mapping only (eg ASID change remap w/ SV=1) */
nkeynes@953
   609
            for( i=0; i<npages; i++ ) {
nkeynes@953
   610
                if( *uptr == userdefs->tlb_miss ) {
nkeynes@953
   611
                    *uptr++ = user_page;
nkeynes@953
   612
                } else {
nkeynes@953
   613
                    mapping_ok = FALSE;
nkeynes@953
   614
                    *uptr++ = userdefs->tlb_multihit;
nkeynes@953
   615
                }
nkeynes@953
   616
            }        
nkeynes@953
   617
        }
nkeynes@953
   618
    }
nkeynes@953
   619
nkeynes@953
   620
    return mapping_ok;
nkeynes@953
   621
}
nkeynes@953
   622
nkeynes@953
   623
/**
nkeynes@953
   624
 * Remap any pages within the region covered by entryNo, but not including 
nkeynes@953
   625
 * entryNo itself. This is used to reestablish pages that were previously
nkeynes@953
   626
 * covered by a multi-hit exception region when one of the pages is removed.
nkeynes@953
   627
 */
nkeynes@953
   628
static void mmu_utlb_remap_pages( gboolean remap_priv, gboolean remap_user, int entryNo )
nkeynes@953
   629
{
nkeynes@953
   630
    int mask = mmu_utlb[entryNo].mask;
nkeynes@953
   631
    uint32_t remap_addr = mmu_utlb[entryNo].vpn & mask;
nkeynes@953
   632
    int i;
nkeynes@953
   633
    
nkeynes@953
   634
    for( i=0; i<UTLB_ENTRY_COUNT; i++ ) {
nkeynes@953
   635
        if( i != entryNo && (mmu_utlb[i].vpn & mask) == remap_addr && (mmu_utlb[i].flags & TLB_VALID) ) {
nkeynes@953
   636
            /* Overlapping region */
nkeynes@953
   637
            mem_region_fn_t priv_page = (remap_priv ? &mmu_utlb_pages[i].fn : NULL);
nkeynes@953
   638
            mem_region_fn_t user_page = (remap_priv ? mmu_utlb_pages[i].user_fn : NULL);
nkeynes@953
   639
            uint32_t start_addr;
nkeynes@953
   640
            int npages;
nkeynes@953
   641
nkeynes@953
   642
            if( mmu_utlb[i].mask >= mask ) {
nkeynes@953
   643
                /* entry is no larger than the area we're replacing - map completely */
nkeynes@953
   644
                start_addr = mmu_utlb[i].vpn & mmu_utlb[i].mask;
nkeynes@953
   645
                npages = get_tlb_size_pages( mmu_utlb[i].flags );
nkeynes@953
   646
            } else {
nkeynes@953
   647
                /* Otherwise map subset - region covered by removed page */
nkeynes@953
   648
                start_addr = remap_addr;
nkeynes@953
   649
                npages = get_tlb_size_pages( mmu_utlb[entryNo].flags );
nkeynes@953
   650
            }
nkeynes@953
   651
nkeynes@953
   652
            if( (mmu_utlb[i].flags & TLB_SHARE) || mmu_utlb[i].asid == mmu_asid ) { 
nkeynes@953
   653
                mmu_utlb_map_pages( priv_page, user_page, start_addr, npages );
nkeynes@953
   654
            } else if( IS_SV_ENABLED() ) {
nkeynes@953
   655
                mmu_utlb_map_pages( priv_page, NULL, start_addr, npages );
nkeynes@953
   656
            }
nkeynes@953
   657
nkeynes@953
   658
        }
nkeynes@953
   659
    }
nkeynes@953
   660
}
nkeynes@953
   661
nkeynes@953
   662
/**
nkeynes@953
   663
 * Remove a previous TLB mapping (replacing them with the TLB miss region).
nkeynes@953
   664
 * @return FALSE if any pages were previously mapped to the TLB multihit page, 
nkeynes@953
   665
 * otherwise TRUE. In either case, all pages in the region are cleared to TLB miss.
nkeynes@953
   666
 */
nkeynes@953
   667
static gboolean mmu_utlb_unmap_pages( gboolean unmap_priv, gboolean unmap_user, sh4addr_t start_addr, int npages )
nkeynes@953
   668
{
nkeynes@953
   669
    mem_region_fn_t *ptr = &sh4_address_space[start_addr >> 12];
nkeynes@953
   670
    mem_region_fn_t *uptr = &sh4_user_address_space[start_addr >> 12];
nkeynes@953
   671
    struct utlb_default_regions *privdefs = &mmu_default_regions[DEFAULT_REGIONS];
nkeynes@953
   672
    struct utlb_default_regions *userdefs = privdefs;
nkeynes@953
   673
nkeynes@953
   674
    gboolean unmapping_ok = TRUE;
nkeynes@953
   675
    int i;
nkeynes@953
   676
    
nkeynes@953
   677
    if( (start_addr & 0xFC000000) == 0xE0000000 ) {
nkeynes@953
   678
        /* Storequeue mapping */
nkeynes@953
   679
        privdefs = &mmu_default_regions[DEFAULT_STOREQUEUE_REGIONS];
nkeynes@953
   680
        userdefs = mmu_user_storequeue_regions;
nkeynes@953
   681
    } else if( (start_addr & 0xE0000000) == 0xC0000000 ) {
nkeynes@953
   682
        unmap_user = FALSE;
nkeynes@953
   683
    } else if( start_addr >= 0x80000000 ) {
nkeynes@953
   684
        return TRUE; // No mapping - legal but meaningless
nkeynes@953
   685
    }
nkeynes@953
   686
nkeynes@953
   687
    if( npages == 0 ) { // 1K page
nkeynes@953
   688
        assert( IS_1K_PAGE_ENTRY( *ptr ) );
nkeynes@953
   689
        struct utlb_1k_entry *ent = (struct utlb_1k_entry *)*ptr;
nkeynes@953
   690
        int i, idx = (start_addr >> 10) & 0x03, mergeable=1;
nkeynes@953
   691
        if( ent->subpages[idx] == privdefs->tlb_multihit ) {
nkeynes@953
   692
            unmapping_ok = FALSE;
nkeynes@953
   693
        }
nkeynes@953
   694
        if( unmap_priv )
nkeynes@953
   695
            ent->subpages[idx] = privdefs->tlb_miss;
nkeynes@953
   696
        if( unmap_user )
nkeynes@953
   697
            ent->user_subpages[idx] = userdefs->tlb_miss;
nkeynes@953
   698
nkeynes@953
   699
        /* If all 4 subpages have the same content, merge them together and
nkeynes@953
   700
         * release the 1K entry
nkeynes@953
   701
         */
nkeynes@953
   702
        mem_region_fn_t priv_page = ent->subpages[0];
nkeynes@953
   703
        mem_region_fn_t user_page = ent->user_subpages[0];
nkeynes@953
   704
        for( i=1; i<4; i++ ) {
nkeynes@953
   705
            if( priv_page != ent->subpages[i] || user_page != ent->user_subpages[i] ) {
nkeynes@953
   706
                mergeable = 0;
nkeynes@953
   707
                break;
nkeynes@953
   708
            }
nkeynes@953
   709
        }
nkeynes@953
   710
        if( mergeable ) {
nkeynes@953
   711
            mmu_utlb_1k_free(ent);
nkeynes@953
   712
            *ptr = priv_page;
nkeynes@953
   713
            *uptr = user_page;
nkeynes@953
   714
        }
nkeynes@953
   715
    } else {
nkeynes@953
   716
        if( unmap_priv ) {
nkeynes@953
   717
            /* Privileged (un)mapping */
nkeynes@953
   718
            for( i=0; i<npages; i++ ) {
nkeynes@953
   719
                if( *ptr == privdefs->tlb_multihit ) {
nkeynes@953
   720
                    unmapping_ok = FALSE;
nkeynes@953
   721
                }
nkeynes@953
   722
                *ptr++ = privdefs->tlb_miss;
nkeynes@953
   723
            }
nkeynes@953
   724
        }
nkeynes@953
   725
        if( unmap_user ) {
nkeynes@953
   726
            /* User (un)mapping */
nkeynes@953
   727
            for( i=0; i<npages; i++ ) {
nkeynes@953
   728
                if( *uptr == userdefs->tlb_multihit ) {
nkeynes@953
   729
                    unmapping_ok = FALSE;
nkeynes@953
   730
                }
nkeynes@953
   731
                *uptr++ = userdefs->tlb_miss;
nkeynes@953
   732
            }            
nkeynes@953
   733
        }
nkeynes@953
   734
    }
nkeynes@953
   735
    
nkeynes@953
   736
    return unmapping_ok;
nkeynes@953
   737
}
nkeynes@953
   738
nkeynes@953
   739
static void mmu_utlb_insert_entry( int entry )
nkeynes@953
   740
{
nkeynes@953
   741
    struct utlb_entry *ent = &mmu_utlb[entry];
nkeynes@953
   742
    mem_region_fn_t page = &mmu_utlb_pages[entry].fn;
nkeynes@953
   743
    mem_region_fn_t upage;
nkeynes@953
   744
    sh4addr_t start_addr = ent->vpn & ent->mask;
nkeynes@953
   745
    int npages = get_tlb_size_pages(ent->flags);
nkeynes@953
   746
nkeynes@953
   747
    if( (start_addr & 0xFC000000) == 0xE0000000 ) {
nkeynes@953
   748
        /* Store queue mappings are a bit different - normal access is fixed to
nkeynes@953
   749
         * the store queue register block, and we only map prefetches through
nkeynes@953
   750
         * the TLB 
nkeynes@953
   751
         */
nkeynes@953
   752
        mmu_utlb_init_storequeue_vtable( ent, &mmu_utlb_pages[entry] );
nkeynes@953
   753
nkeynes@953
   754
        if( (ent->flags & TLB_USERMODE) == 0 ) {
nkeynes@953
   755
            upage = mmu_user_storequeue_regions->tlb_prot;
nkeynes@953
   756
        } else if( IS_STOREQUEUE_PROTECTED() ) {
nkeynes@953
   757
            upage = &p4_region_storequeue_sqmd;
nkeynes@953
   758
        } else {
nkeynes@953
   759
            upage = page;
nkeynes@953
   760
        }
nkeynes@953
   761
nkeynes@953
   762
    }  else {
nkeynes@953
   763
nkeynes@953
   764
        if( (ent->flags & TLB_USERMODE) == 0 ) {
nkeynes@953
   765
            upage = &mem_region_tlb_protected;
nkeynes@953
   766
        } else {        
nkeynes@953
   767
            upage = page;
nkeynes@953
   768
        }
nkeynes@953
   769
nkeynes@953
   770
        if( (ent->flags & TLB_WRITABLE) == 0 ) {
nkeynes@953
   771
            page->write_long = (mem_write_fn_t)tlb_protected_write;
nkeynes@953
   772
            page->write_word = (mem_write_fn_t)tlb_protected_write;
nkeynes@953
   773
            page->write_byte = (mem_write_fn_t)tlb_protected_write;
nkeynes@953
   774
            page->write_burst = (mem_write_burst_fn_t)tlb_protected_write;
nkeynes@953
   775
            mmu_utlb_init_vtable( ent, &mmu_utlb_pages[entry], FALSE );
nkeynes@953
   776
        } else if( (ent->flags & TLB_DIRTY) == 0 ) {
nkeynes@953
   777
            page->write_long = (mem_write_fn_t)tlb_initial_write;
nkeynes@953
   778
            page->write_word = (mem_write_fn_t)tlb_initial_write;
nkeynes@953
   779
            page->write_byte = (mem_write_fn_t)tlb_initial_write;
nkeynes@953
   780
            page->write_burst = (mem_write_burst_fn_t)tlb_initial_write;
nkeynes@953
   781
            mmu_utlb_init_vtable( ent, &mmu_utlb_pages[entry], FALSE );
nkeynes@953
   782
        } else {
nkeynes@953
   783
            mmu_utlb_init_vtable( ent, &mmu_utlb_pages[entry], TRUE );
nkeynes@953
   784
        }
nkeynes@953
   785
    }
nkeynes@953
   786
    
nkeynes@953
   787
    mmu_utlb_pages[entry].user_fn = upage;
nkeynes@953
   788
nkeynes@953
   789
    /* Is page visible? */
nkeynes@953
   790
    if( (ent->flags & TLB_SHARE) || ent->asid == mmu_asid ) { 
nkeynes@953
   791
        mmu_utlb_map_pages( page, upage, start_addr, npages );
nkeynes@953
   792
    } else if( IS_SV_ENABLED() ) {
nkeynes@953
   793
        mmu_utlb_map_pages( page, NULL, start_addr, npages );
nkeynes@953
   794
    }
nkeynes@953
   795
}
nkeynes@953
   796
nkeynes@953
   797
static void mmu_utlb_remove_entry( int entry )
nkeynes@953
   798
{
nkeynes@953
   799
    int i, j;
nkeynes@953
   800
    struct utlb_entry *ent = &mmu_utlb[entry];
nkeynes@953
   801
    sh4addr_t start_addr = ent->vpn&ent->mask;
nkeynes@953
   802
    mem_region_fn_t *ptr = &sh4_address_space[start_addr >> 12];
nkeynes@953
   803
    mem_region_fn_t *uptr = &sh4_user_address_space[start_addr >> 12];
nkeynes@953
   804
    gboolean unmap_user;
nkeynes@953
   805
    int npages = get_tlb_size_pages(ent->flags);
nkeynes@953
   806
    
nkeynes@953
   807
    if( (ent->flags & TLB_SHARE) || ent->asid == mmu_asid ) {
nkeynes@953
   808
        unmap_user = TRUE;
nkeynes@953
   809
    } else if( IS_SV_ENABLED() ) {
nkeynes@953
   810
        unmap_user = FALSE;
nkeynes@953
   811
    } else {
nkeynes@953
   812
        return; // Not mapped
nkeynes@953
   813
    }
nkeynes@953
   814
    
nkeynes@953
   815
    gboolean clean_unmap = mmu_utlb_unmap_pages( TRUE, unmap_user, start_addr, npages );
nkeynes@953
   816
    
nkeynes@953
   817
    if( !clean_unmap ) {
nkeynes@953
   818
        mmu_utlb_remap_pages( TRUE, unmap_user, entry );
nkeynes@953
   819
    }
nkeynes@953
   820
}
nkeynes@953
   821
nkeynes@953
   822
static void mmu_utlb_register_all()
nkeynes@953
   823
{
nkeynes@953
   824
    int i;
nkeynes@953
   825
    for( i=0; i<UTLB_ENTRY_COUNT; i++ ) {
nkeynes@953
   826
        if( mmu_utlb[i].flags & TLB_VALID ) 
nkeynes@953
   827
            mmu_utlb_insert_entry( i );
nkeynes@953
   828
    }
nkeynes@953
   829
}
nkeynes@953
   830
nkeynes@550
   831
static void mmu_invalidate_tlb()
nkeynes@550
   832
{
nkeynes@550
   833
    int i;
nkeynes@550
   834
    for( i=0; i<ITLB_ENTRY_COUNT; i++ ) {
nkeynes@736
   835
        mmu_itlb[i].flags &= (~TLB_VALID);
nkeynes@550
   836
    }
nkeynes@953
   837
    if( IS_TLB_ENABLED() ) {
nkeynes@953
   838
        for( i=0; i<UTLB_ENTRY_COUNT; i++ ) {
nkeynes@953
   839
            if( mmu_utlb[i].flags & TLB_VALID ) {
nkeynes@953
   840
                mmu_utlb_remove_entry( i );
nkeynes@953
   841
            }
nkeynes@953
   842
        }
nkeynes@953
   843
    }
nkeynes@550
   844
    for( i=0; i<UTLB_ENTRY_COUNT; i++ ) {
nkeynes@736
   845
        mmu_utlb[i].flags &= (~TLB_VALID);
nkeynes@550
   846
    }
nkeynes@550
   847
}
nkeynes@586
   848
nkeynes@586
   849
/******************************************************************************/
nkeynes@586
   850
/*                        MMU TLB address translation                         */
nkeynes@586
   851
/******************************************************************************/
nkeynes@586
   852
nkeynes@586
   853
/**
nkeynes@953
   854
 * Translate a 32-bit address into a UTLB entry number. Does not check for
nkeynes@953
   855
 * page protection etc.
nkeynes@953
   856
 * @return the entryNo if found, -1 if not found, and -2 for a multi-hit.
nkeynes@586
   857
 */
nkeynes@953
   858
int mmu_utlb_entry_for_vpn( uint32_t vpn )
nkeynes@953
   859
{
nkeynes@953
   860
    mem_region_fn_t fn = sh4_address_space[vpn>>12];
nkeynes@953
   861
    if( fn >= &mmu_utlb_pages[0].fn && fn < &mmu_utlb_pages[UTLB_ENTRY_COUNT].fn ) {
nkeynes@953
   862
        return ((struct utlb_page_entry *)fn) - &mmu_utlb_pages[0];
nkeynes@953
   863
    } else if( fn == &mem_region_tlb_multihit ) {
nkeynes@953
   864
        return -2;
nkeynes@953
   865
    } else {
nkeynes@953
   866
        return -1;
nkeynes@953
   867
    }
nkeynes@953
   868
}
nkeynes@953
   869
nkeynes@586
   870
nkeynes@586
   871
/**
nkeynes@586
   872
 * Perform the actual utlb lookup w/ asid matching.
nkeynes@586
   873
 * Possible utcomes are:
nkeynes@586
   874
 *   0..63 Single match - good, return entry found
nkeynes@586
   875
 *   -1 No match - raise a tlb data miss exception
nkeynes@586
   876
 *   -2 Multiple matches - raise a multi-hit exception (reset)
nkeynes@586
   877
 * @param vpn virtual address to resolve
nkeynes@586
   878
 * @return the resultant UTLB entry, or an error.
nkeynes@586
   879
 */
nkeynes@586
   880
static inline int mmu_utlb_lookup_vpn_asid( uint32_t vpn )
nkeynes@586
   881
{
nkeynes@586
   882
    int result = -1;
nkeynes@586
   883
    unsigned int i;
nkeynes@586
   884
nkeynes@586
   885
    mmu_urc++;
nkeynes@586
   886
    if( mmu_urc == mmu_urb || mmu_urc == 0x40 ) {
nkeynes@736
   887
        mmu_urc = 0;
nkeynes@586
   888
    }
nkeynes@586
   889
nkeynes@586
   890
    for( i = 0; i < UTLB_ENTRY_COUNT; i++ ) {
nkeynes@736
   891
        if( (mmu_utlb[i].flags & TLB_VALID) &&
nkeynes@826
   892
                ((mmu_utlb[i].flags & TLB_SHARE) || mmu_asid == mmu_utlb[i].asid) &&
nkeynes@736
   893
                ((mmu_utlb[i].vpn ^ vpn) & mmu_utlb[i].mask) == 0 ) {
nkeynes@736
   894
            if( result != -1 ) {
nkeynes@736
   895
                return -2;
nkeynes@736
   896
            }
nkeynes@736
   897
            result = i;
nkeynes@736
   898
        }
nkeynes@586
   899
    }
nkeynes@586
   900
    return result;
nkeynes@586
   901
}
nkeynes@586
   902
nkeynes@586
   903
/**
nkeynes@586
   904
 * Perform the actual utlb lookup matching on vpn only
nkeynes@586
   905
 * Possible utcomes are:
nkeynes@586
   906
 *   0..63 Single match - good, return entry found
nkeynes@586
   907
 *   -1 No match - raise a tlb data miss exception
nkeynes@586
   908
 *   -2 Multiple matches - raise a multi-hit exception (reset)
nkeynes@586
   909
 * @param vpn virtual address to resolve
nkeynes@586
   910
 * @return the resultant UTLB entry, or an error.
nkeynes@586
   911
 */
nkeynes@586
   912
static inline int mmu_utlb_lookup_vpn( uint32_t vpn )
nkeynes@586
   913
{
nkeynes@586
   914
    int result = -1;
nkeynes@586
   915
    unsigned int i;
nkeynes@586
   916
nkeynes@586
   917
    mmu_urc++;
nkeynes@586
   918
    if( mmu_urc == mmu_urb || mmu_urc == 0x40 ) {
nkeynes@736
   919
        mmu_urc = 0;
nkeynes@586
   920
    }
nkeynes@586
   921
nkeynes@586
   922
    for( i = 0; i < UTLB_ENTRY_COUNT; i++ ) {
nkeynes@736
   923
        if( (mmu_utlb[i].flags & TLB_VALID) &&
nkeynes@736
   924
                ((mmu_utlb[i].vpn ^ vpn) & mmu_utlb[i].mask) == 0 ) {
nkeynes@736
   925
            if( result != -1 ) {
nkeynes@736
   926
                return -2;
nkeynes@736
   927
            }
nkeynes@736
   928
            result = i;
nkeynes@736
   929
        }
nkeynes@586
   930
    }
nkeynes@586
   931
nkeynes@586
   932
    return result;
nkeynes@586
   933
}
nkeynes@586
   934
nkeynes@586
   935
/**
nkeynes@586
   936
 * Update the ITLB by replacing the LRU entry with the specified UTLB entry.
nkeynes@586
   937
 * @return the number (0-3) of the replaced entry.
nkeynes@586
   938
 */
nkeynes@586
   939
static int inline mmu_itlb_update_from_utlb( int entryNo )
nkeynes@586
   940
{
nkeynes@586
   941
    int replace;
nkeynes@586
   942
    /* Determine entry to replace based on lrui */
nkeynes@586
   943
    if( (mmu_lrui & 0x38) == 0x38 ) {
nkeynes@736
   944
        replace = 0;
nkeynes@736
   945
        mmu_lrui = mmu_lrui & 0x07;
nkeynes@586
   946
    } else if( (mmu_lrui & 0x26) == 0x06 ) {
nkeynes@736
   947
        replace = 1;
nkeynes@736
   948
        mmu_lrui = (mmu_lrui & 0x19) | 0x20;
nkeynes@586
   949
    } else if( (mmu_lrui & 0x15) == 0x01 ) {
nkeynes@736
   950
        replace = 2;
nkeynes@736
   951
        mmu_lrui = (mmu_lrui & 0x3E) | 0x14;
nkeynes@586
   952
    } else { // Note - gets invalid entries too
nkeynes@736
   953
        replace = 3;
nkeynes@736
   954
        mmu_lrui = (mmu_lrui | 0x0B);
nkeynes@826
   955
    }
nkeynes@586
   956
nkeynes@586
   957
    mmu_itlb[replace].vpn = mmu_utlb[entryNo].vpn;
nkeynes@586
   958
    mmu_itlb[replace].mask = mmu_utlb[entryNo].mask;
nkeynes@586
   959
    mmu_itlb[replace].ppn = mmu_utlb[entryNo].ppn;
nkeynes@586
   960
    mmu_itlb[replace].asid = mmu_utlb[entryNo].asid;
nkeynes@586
   961
    mmu_itlb[replace].flags = mmu_utlb[entryNo].flags & 0x01DA;
nkeynes@586
   962
    return replace;
nkeynes@586
   963
}
nkeynes@586
   964
nkeynes@586
   965
/**
nkeynes@586
   966
 * Perform the actual itlb lookup w/ asid protection
nkeynes@586
   967
 * Possible utcomes are:
nkeynes@586
   968
 *   0..63 Single match - good, return entry found
nkeynes@586
   969
 *   -1 No match - raise a tlb data miss exception
nkeynes@586
   970
 *   -2 Multiple matches - raise a multi-hit exception (reset)
nkeynes@586
   971
 * @param vpn virtual address to resolve
nkeynes@586
   972
 * @return the resultant ITLB entry, or an error.
nkeynes@586
   973
 */
nkeynes@586
   974
static inline int mmu_itlb_lookup_vpn_asid( uint32_t vpn )
nkeynes@586
   975
{
nkeynes@586
   976
    int result = -1;
nkeynes@586
   977
    unsigned int i;
nkeynes@586
   978
nkeynes@586
   979
    for( i = 0; i < ITLB_ENTRY_COUNT; i++ ) {
nkeynes@736
   980
        if( (mmu_itlb[i].flags & TLB_VALID) &&
nkeynes@826
   981
                ((mmu_itlb[i].flags & TLB_SHARE) || mmu_asid == mmu_itlb[i].asid) &&
nkeynes@736
   982
                ((mmu_itlb[i].vpn ^ vpn) & mmu_itlb[i].mask) == 0 ) {
nkeynes@736
   983
            if( result != -1 ) {
nkeynes@736
   984
                return -2;
nkeynes@736
   985
            }
nkeynes@736
   986
            result = i;
nkeynes@736
   987
        }
nkeynes@586
   988
    }
nkeynes@586
   989
nkeynes@586
   990
    if( result == -1 ) {
nkeynes@953
   991
        int utlbEntry = mmu_utlb_entry_for_vpn( vpn );
nkeynes@736
   992
        if( utlbEntry < 0 ) {
nkeynes@736
   993
            return utlbEntry;
nkeynes@736
   994
        } else {
nkeynes@736
   995
            return mmu_itlb_update_from_utlb( utlbEntry );
nkeynes@736
   996
        }
nkeynes@586
   997
    }
nkeynes@586
   998
nkeynes@586
   999
    switch( result ) {
nkeynes@586
  1000
    case 0: mmu_lrui = (mmu_lrui & 0x07); break;
nkeynes@586
  1001
    case 1: mmu_lrui = (mmu_lrui & 0x19) | 0x20; break;
nkeynes@586
  1002
    case 2: mmu_lrui = (mmu_lrui & 0x3E) | 0x14; break;
nkeynes@586
  1003
    case 3: mmu_lrui = (mmu_lrui | 0x0B); break;
nkeynes@586
  1004
    }
nkeynes@736
  1005
nkeynes@586
  1006
    return result;
nkeynes@586
  1007
}
nkeynes@586
  1008
nkeynes@586
  1009
/**
nkeynes@586
  1010
 * Perform the actual itlb lookup on vpn only
nkeynes@586
  1011
 * Possible utcomes are:
nkeynes@586
  1012
 *   0..63 Single match - good, return entry found
nkeynes@586
  1013
 *   -1 No match - raise a tlb data miss exception
nkeynes@586
  1014
 *   -2 Multiple matches - raise a multi-hit exception (reset)
nkeynes@586
  1015
 * @param vpn virtual address to resolve
nkeynes@586
  1016
 * @return the resultant ITLB entry, or an error.
nkeynes@586
  1017
 */
nkeynes@586
  1018
static inline int mmu_itlb_lookup_vpn( uint32_t vpn )
nkeynes@586
  1019
{
nkeynes@586
  1020
    int result = -1;
nkeynes@586
  1021
    unsigned int i;
nkeynes@586
  1022
nkeynes@586
  1023
    for( i = 0; i < ITLB_ENTRY_COUNT; i++ ) {
nkeynes@736
  1024
        if( (mmu_itlb[i].flags & TLB_VALID) &&
nkeynes@736
  1025
                ((mmu_itlb[i].vpn ^ vpn) & mmu_itlb[i].mask) == 0 ) {
nkeynes@736
  1026
            if( result != -1 ) {
nkeynes@736
  1027
                return -2;
nkeynes@736
  1028
            }
nkeynes@736
  1029
            result = i;
nkeynes@736
  1030
        }
nkeynes@586
  1031
    }
nkeynes@586
  1032
nkeynes@586
  1033
    if( result == -1 ) {
nkeynes@736
  1034
        int utlbEntry = mmu_utlb_lookup_vpn( vpn );
nkeynes@736
  1035
        if( utlbEntry < 0 ) {
nkeynes@736
  1036
            return utlbEntry;
nkeynes@736
  1037
        } else {
nkeynes@736
  1038
            return mmu_itlb_update_from_utlb( utlbEntry );
nkeynes@736
  1039
        }
nkeynes@586
  1040
    }
nkeynes@586
  1041
nkeynes@586
  1042
    switch( result ) {
nkeynes@586
  1043
    case 0: mmu_lrui = (mmu_lrui & 0x07); break;
nkeynes@586
  1044
    case 1: mmu_lrui = (mmu_lrui & 0x19) | 0x20; break;
nkeynes@586
  1045
    case 2: mmu_lrui = (mmu_lrui & 0x3E) | 0x14; break;
nkeynes@586
  1046
    case 3: mmu_lrui = (mmu_lrui | 0x0B); break;
nkeynes@586
  1047
    }
nkeynes@736
  1048
nkeynes@586
  1049
    return result;
nkeynes@586
  1050
}
nkeynes@927
  1051
nkeynes@586
  1052
/**
nkeynes@586
  1053
 * Update the icache for an untranslated address
nkeynes@586
  1054
 */
nkeynes@905
  1055
static inline void mmu_update_icache_phys( sh4addr_t addr )
nkeynes@586
  1056
{
nkeynes@586
  1057
    if( (addr & 0x1C000000) == 0x0C000000 ) {
nkeynes@736
  1058
        /* Main ram */
nkeynes@736
  1059
        sh4_icache.page_vma = addr & 0xFF000000;
nkeynes@736
  1060
        sh4_icache.page_ppa = 0x0C000000;
nkeynes@736
  1061
        sh4_icache.mask = 0xFF000000;
nkeynes@953
  1062
        sh4_icache.page = dc_main_ram;
nkeynes@586
  1063
    } else if( (addr & 0x1FE00000) == 0 ) {
nkeynes@736
  1064
        /* BIOS ROM */
nkeynes@736
  1065
        sh4_icache.page_vma = addr & 0xFFE00000;
nkeynes@736
  1066
        sh4_icache.page_ppa = 0;
nkeynes@736
  1067
        sh4_icache.mask = 0xFFE00000;
nkeynes@953
  1068
        sh4_icache.page = dc_boot_rom;
nkeynes@586
  1069
    } else {
nkeynes@736
  1070
        /* not supported */
nkeynes@736
  1071
        sh4_icache.page_vma = -1;
nkeynes@586
  1072
    }
nkeynes@586
  1073
}
nkeynes@586
  1074
nkeynes@586
  1075
/**
nkeynes@586
  1076
 * Update the sh4_icache structure to describe the page(s) containing the
nkeynes@586
  1077
 * given vma. If the address does not reference a RAM/ROM region, the icache
nkeynes@586
  1078
 * will be invalidated instead.
nkeynes@586
  1079
 * If AT is on, this method will raise TLB exceptions normally
nkeynes@586
  1080
 * (hence this method should only be used immediately prior to execution of
nkeynes@586
  1081
 * code), and otherwise will set the icache according to the matching TLB entry.
nkeynes@586
  1082
 * If AT is off, this method will set the entire referenced RAM/ROM region in
nkeynes@586
  1083
 * the icache.
nkeynes@586
  1084
 * @return TRUE if the update completed (successfully or otherwise), FALSE
nkeynes@586
  1085
 * if an exception was raised.
nkeynes@586
  1086
 */
nkeynes@905
  1087
gboolean FASTCALL mmu_update_icache( sh4vma_t addr )
nkeynes@586
  1088
{
nkeynes@586
  1089
    int entryNo;
nkeynes@586
  1090
    if( IS_SH4_PRIVMODE()  ) {
nkeynes@736
  1091
        if( addr & 0x80000000 ) {
nkeynes@736
  1092
            if( addr < 0xC0000000 ) {
nkeynes@736
  1093
                /* P1, P2 and P4 regions are pass-through (no translation) */
nkeynes@736
  1094
                mmu_update_icache_phys(addr);
nkeynes@736
  1095
                return TRUE;
nkeynes@736
  1096
            } else if( addr >= 0xE0000000 && addr < 0xFFFFFF00 ) {
nkeynes@953
  1097
                RAISE_MEM_ERROR(EXC_DATA_ADDR_READ, addr);
nkeynes@736
  1098
                return FALSE;
nkeynes@736
  1099
            }
nkeynes@736
  1100
        }
nkeynes@586
  1101
nkeynes@736
  1102
        uint32_t mmucr = MMIO_READ(MMU,MMUCR);
nkeynes@736
  1103
        if( (mmucr & MMUCR_AT) == 0 ) {
nkeynes@736
  1104
            mmu_update_icache_phys(addr);
nkeynes@736
  1105
            return TRUE;
nkeynes@736
  1106
        }
nkeynes@736
  1107
nkeynes@826
  1108
        if( (mmucr & MMUCR_SV) == 0 )
nkeynes@807
  1109
        	entryNo = mmu_itlb_lookup_vpn_asid( addr );
nkeynes@807
  1110
        else
nkeynes@807
  1111
        	entryNo = mmu_itlb_lookup_vpn( addr );
nkeynes@586
  1112
    } else {
nkeynes@736
  1113
        if( addr & 0x80000000 ) {
nkeynes@953
  1114
            RAISE_MEM_ERROR(EXC_DATA_ADDR_READ, addr);
nkeynes@736
  1115
            return FALSE;
nkeynes@736
  1116
        }
nkeynes@586
  1117
nkeynes@736
  1118
        uint32_t mmucr = MMIO_READ(MMU,MMUCR);
nkeynes@736
  1119
        if( (mmucr & MMUCR_AT) == 0 ) {
nkeynes@736
  1120
            mmu_update_icache_phys(addr);
nkeynes@736
  1121
            return TRUE;
nkeynes@736
  1122
        }
nkeynes@736
  1123
nkeynes@807
  1124
        entryNo = mmu_itlb_lookup_vpn_asid( addr );
nkeynes@807
  1125
nkeynes@736
  1126
        if( entryNo != -1 && (mmu_itlb[entryNo].flags & TLB_USERMODE) == 0 ) {
nkeynes@953
  1127
            RAISE_MEM_ERROR(EXC_TLB_PROT_READ, addr);
nkeynes@736
  1128
            return FALSE;
nkeynes@736
  1129
        }
nkeynes@586
  1130
    }
nkeynes@586
  1131
nkeynes@586
  1132
    switch(entryNo) {
nkeynes@586
  1133
    case -1:
nkeynes@953
  1134
    RAISE_TLB_ERROR(EXC_TLB_MISS_READ, addr);
nkeynes@736
  1135
    return FALSE;
nkeynes@586
  1136
    case -2:
nkeynes@953
  1137
    RAISE_TLB_MULTIHIT_ERROR(addr);
nkeynes@736
  1138
    return FALSE;
nkeynes@586
  1139
    default:
nkeynes@736
  1140
        sh4_icache.page_ppa = mmu_itlb[entryNo].ppn & mmu_itlb[entryNo].mask;
nkeynes@736
  1141
        sh4_icache.page = mem_get_region( sh4_icache.page_ppa );
nkeynes@736
  1142
        if( sh4_icache.page == NULL ) {
nkeynes@736
  1143
            sh4_icache.page_vma = -1;
nkeynes@736
  1144
        } else {
nkeynes@736
  1145
            sh4_icache.page_vma = mmu_itlb[entryNo].vpn & mmu_itlb[entryNo].mask;
nkeynes@736
  1146
            sh4_icache.mask = mmu_itlb[entryNo].mask;
nkeynes@736
  1147
        }
nkeynes@736
  1148
        return TRUE;
nkeynes@586
  1149
    }
nkeynes@586
  1150
}
nkeynes@586
  1151
nkeynes@597
  1152
/**
nkeynes@826
  1153
 * Translate address for disassembly purposes (ie performs an instruction
nkeynes@597
  1154
 * lookup) - does not raise exceptions or modify any state, and ignores
nkeynes@597
  1155
 * protection bits. Returns the translated address, or MMU_VMA_ERROR
nkeynes@826
  1156
 * on translation failure.
nkeynes@597
  1157
 */
nkeynes@905
  1158
sh4addr_t FASTCALL mmu_vma_to_phys_disasm( sh4vma_t vma )
nkeynes@597
  1159
{
nkeynes@597
  1160
    if( vma & 0x80000000 ) {
nkeynes@736
  1161
        if( vma < 0xC0000000 ) {
nkeynes@736
  1162
            /* P1, P2 and P4 regions are pass-through (no translation) */
nkeynes@736
  1163
            return VMA_TO_EXT_ADDR(vma);
nkeynes@736
  1164
        } else if( vma >= 0xE0000000 && vma < 0xFFFFFF00 ) {
nkeynes@736
  1165
            /* Not translatable */
nkeynes@736
  1166
            return MMU_VMA_ERROR;
nkeynes@736
  1167
        }
nkeynes@597
  1168
    }
nkeynes@597
  1169
nkeynes@597
  1170
    uint32_t mmucr = MMIO_READ(MMU,MMUCR);
nkeynes@597
  1171
    if( (mmucr & MMUCR_AT) == 0 ) {
nkeynes@736
  1172
        return VMA_TO_EXT_ADDR(vma);
nkeynes@597
  1173
    }
nkeynes@736
  1174
nkeynes@597
  1175
    int entryNo = mmu_itlb_lookup_vpn( vma );
nkeynes@597
  1176
    if( entryNo == -2 ) {
nkeynes@736
  1177
        entryNo = mmu_itlb_lookup_vpn_asid( vma );
nkeynes@597
  1178
    }
nkeynes@597
  1179
    if( entryNo < 0 ) {
nkeynes@736
  1180
        return MMU_VMA_ERROR;
nkeynes@597
  1181
    } else {
nkeynes@826
  1182
        return (mmu_itlb[entryNo].ppn & mmu_itlb[entryNo].mask) |
nkeynes@826
  1183
        (vma & (~mmu_itlb[entryNo].mask));
nkeynes@597
  1184
    }
nkeynes@597
  1185
}
nkeynes@597
  1186
nkeynes@953
  1187
/********************** TLB Direct-Access Regions ***************************/
nkeynes@953
  1188
#ifdef HAVE_FRAME_ADDRESS
nkeynes@968
  1189
#define EXCEPTION_EXIT() do{ *(((void **)__builtin_frame_address(0))+1) = exc; } while(0)
nkeynes@953
  1190
#else
nkeynes@953
  1191
#define EXCEPTION_EXIT() sh4_core_exit(CORE_EXIT_EXCEPTION)
nkeynes@953
  1192
#endif
nkeynes@953
  1193
nkeynes@953
  1194
nkeynes@953
  1195
#define ITLB_ENTRY(addr) ((addr>>7)&0x03)
nkeynes@953
  1196
nkeynes@953
  1197
int32_t FASTCALL mmu_itlb_addr_read( sh4addr_t addr )
nkeynes@911
  1198
{
nkeynes@953
  1199
    struct itlb_entry *ent = &mmu_itlb[ITLB_ENTRY(addr)];
nkeynes@953
  1200
    return ent->vpn | ent->asid | (ent->flags & TLB_VALID);
nkeynes@586
  1201
}
nkeynes@586
  1202
nkeynes@953
  1203
void FASTCALL mmu_itlb_addr_write( sh4addr_t addr, uint32_t val )
nkeynes@953
  1204
{
nkeynes@953
  1205
    struct itlb_entry *ent = &mmu_itlb[ITLB_ENTRY(addr)];
nkeynes@953
  1206
    ent->vpn = val & 0xFFFFFC00;
nkeynes@953
  1207
    ent->asid = val & 0x000000FF;
nkeynes@953
  1208
    ent->flags = (ent->flags & ~(TLB_VALID)) | (val&TLB_VALID);
nkeynes@953
  1209
}
nkeynes@953
  1210
nkeynes@953
  1211
int32_t FASTCALL mmu_itlb_data_read( sh4addr_t addr )
nkeynes@953
  1212
{
nkeynes@953
  1213
    struct itlb_entry *ent = &mmu_itlb[ITLB_ENTRY(addr)];
nkeynes@953
  1214
    return (ent->ppn & 0x1FFFFC00) | ent->flags;
nkeynes@953
  1215
}
nkeynes@953
  1216
nkeynes@953
  1217
void FASTCALL mmu_itlb_data_write( sh4addr_t addr, uint32_t val )
nkeynes@953
  1218
{
nkeynes@953
  1219
    struct itlb_entry *ent = &mmu_itlb[ITLB_ENTRY(addr)];
nkeynes@953
  1220
    ent->ppn = val & 0x1FFFFC00;
nkeynes@953
  1221
    ent->flags = val & 0x00001DA;
nkeynes@953
  1222
    ent->mask = get_tlb_size_mask(val);
nkeynes@953
  1223
    if( ent->ppn >= 0x1C000000 )
nkeynes@953
  1224
        ent->ppn |= 0xE0000000;
nkeynes@953
  1225
}
nkeynes@953
  1226
nkeynes@953
  1227
#define UTLB_ENTRY(addr) ((addr>>8)&0x3F)
nkeynes@953
  1228
#define UTLB_ASSOC(addr) (addr&0x80)
nkeynes@953
  1229
#define UTLB_DATA2(addr) (addr&0x00800000)
nkeynes@953
  1230
nkeynes@953
  1231
int32_t FASTCALL mmu_utlb_addr_read( sh4addr_t addr )
nkeynes@953
  1232
{
nkeynes@953
  1233
    struct utlb_entry *ent = &mmu_utlb[UTLB_ENTRY(addr)];
nkeynes@953
  1234
    return ent->vpn | ent->asid | (ent->flags & TLB_VALID) |
nkeynes@953
  1235
    ((ent->flags & TLB_DIRTY)<<7);
nkeynes@953
  1236
}
nkeynes@953
  1237
int32_t FASTCALL mmu_utlb_data_read( sh4addr_t addr )
nkeynes@953
  1238
{
nkeynes@953
  1239
    struct utlb_entry *ent = &mmu_utlb[UTLB_ENTRY(addr)];
nkeynes@953
  1240
    if( UTLB_DATA2(addr) ) {
nkeynes@953
  1241
        return ent->pcmcia;
nkeynes@953
  1242
    } else {
nkeynes@953
  1243
        return (ent->ppn&0x1FFFFC00) | ent->flags;
nkeynes@953
  1244
    }
nkeynes@953
  1245
}
nkeynes@953
  1246
nkeynes@953
  1247
/**
nkeynes@953
  1248
 * Find a UTLB entry for the associative TLB write - same as the normal
nkeynes@953
  1249
 * lookup but ignores the valid bit.
nkeynes@953
  1250
 */
nkeynes@953
  1251
static inline int mmu_utlb_lookup_assoc( uint32_t vpn, uint32_t asid )
nkeynes@953
  1252
{
nkeynes@953
  1253
    int result = -1;
nkeynes@953
  1254
    unsigned int i;
nkeynes@953
  1255
    for( i = 0; i < UTLB_ENTRY_COUNT; i++ ) {
nkeynes@953
  1256
        if( (mmu_utlb[i].flags & TLB_VALID) &&
nkeynes@953
  1257
                ((mmu_utlb[i].flags & TLB_SHARE) || asid == mmu_utlb[i].asid) &&
nkeynes@953
  1258
                ((mmu_utlb[i].vpn ^ vpn) & mmu_utlb[i].mask) == 0 ) {
nkeynes@953
  1259
            if( result != -1 ) {
nkeynes@953
  1260
                fprintf( stderr, "TLB Multi hit: %d %d\n", result, i );
nkeynes@953
  1261
                return -2;
nkeynes@953
  1262
            }
nkeynes@953
  1263
            result = i;
nkeynes@953
  1264
        }
nkeynes@953
  1265
    }
nkeynes@953
  1266
    return result;
nkeynes@953
  1267
}
nkeynes@953
  1268
nkeynes@953
  1269
/**
nkeynes@953
  1270
 * Find a ITLB entry for the associative TLB write - same as the normal
nkeynes@953
  1271
 * lookup but ignores the valid bit.
nkeynes@953
  1272
 */
nkeynes@953
  1273
static inline int mmu_itlb_lookup_assoc( uint32_t vpn, uint32_t asid )
nkeynes@953
  1274
{
nkeynes@953
  1275
    int result = -1;
nkeynes@953
  1276
    unsigned int i;
nkeynes@953
  1277
    for( i = 0; i < ITLB_ENTRY_COUNT; i++ ) {
nkeynes@953
  1278
        if( (mmu_itlb[i].flags & TLB_VALID) &&
nkeynes@953
  1279
                ((mmu_itlb[i].flags & TLB_SHARE) || asid == mmu_itlb[i].asid) &&
nkeynes@953
  1280
                ((mmu_itlb[i].vpn ^ vpn) & mmu_itlb[i].mask) == 0 ) {
nkeynes@953
  1281
            if( result != -1 ) {
nkeynes@953
  1282
                return -2;
nkeynes@953
  1283
            }
nkeynes@953
  1284
            result = i;
nkeynes@953
  1285
        }
nkeynes@953
  1286
    }
nkeynes@953
  1287
    return result;
nkeynes@953
  1288
}
nkeynes@953
  1289
nkeynes@953
  1290
void FASTCALL mmu_utlb_addr_write( sh4addr_t addr, uint32_t val, void *exc )
nkeynes@953
  1291
{
nkeynes@953
  1292
    if( UTLB_ASSOC(addr) ) {
nkeynes@953
  1293
        int utlb = mmu_utlb_lookup_assoc( val, mmu_asid );
nkeynes@953
  1294
        if( utlb >= 0 ) {
nkeynes@953
  1295
            struct utlb_entry *ent = &mmu_utlb[utlb];
nkeynes@953
  1296
            uint32_t old_flags = ent->flags;
nkeynes@953
  1297
            ent->flags = ent->flags & ~(TLB_DIRTY|TLB_VALID);
nkeynes@953
  1298
            ent->flags |= (val & TLB_VALID);
nkeynes@953
  1299
            ent->flags |= ((val & 0x200)>>7);
nkeynes@953
  1300
            if( ((old_flags^ent->flags) & (TLB_VALID|TLB_DIRTY)) != 0 ) {
nkeynes@953
  1301
                if( old_flags & TLB_VALID )
nkeynes@953
  1302
                    mmu_utlb_remove_entry( utlb );
nkeynes@953
  1303
                if( ent->flags & TLB_VALID )
nkeynes@953
  1304
                    mmu_utlb_insert_entry( utlb );
nkeynes@953
  1305
            }
nkeynes@953
  1306
        }
nkeynes@953
  1307
nkeynes@953
  1308
        int itlb = mmu_itlb_lookup_assoc( val, mmu_asid );
nkeynes@953
  1309
        if( itlb >= 0 ) {
nkeynes@953
  1310
            struct itlb_entry *ent = &mmu_itlb[itlb];
nkeynes@953
  1311
            ent->flags = (ent->flags & (~TLB_VALID)) | (val & TLB_VALID);
nkeynes@953
  1312
        }
nkeynes@953
  1313
nkeynes@953
  1314
        if( itlb == -2 || utlb == -2 ) {
nkeynes@953
  1315
            RAISE_TLB_MULTIHIT_ERROR(addr);
nkeynes@953
  1316
            EXCEPTION_EXIT();
nkeynes@953
  1317
            return;
nkeynes@953
  1318
        }
nkeynes@953
  1319
    } else {
nkeynes@953
  1320
        struct utlb_entry *ent = &mmu_utlb[UTLB_ENTRY(addr)];
nkeynes@953
  1321
        if( ent->flags & TLB_VALID ) 
nkeynes@953
  1322
            mmu_utlb_remove_entry( UTLB_ENTRY(addr) );
nkeynes@953
  1323
        ent->vpn = (val & 0xFFFFFC00);
nkeynes@953
  1324
        ent->asid = (val & 0xFF);
nkeynes@953
  1325
        ent->flags = (ent->flags & ~(TLB_DIRTY|TLB_VALID));
nkeynes@953
  1326
        ent->flags |= (val & TLB_VALID);
nkeynes@953
  1327
        ent->flags |= ((val & 0x200)>>7);
nkeynes@953
  1328
        if( ent->flags & TLB_VALID ) 
nkeynes@953
  1329
            mmu_utlb_insert_entry( UTLB_ENTRY(addr) );
nkeynes@953
  1330
    }
nkeynes@953
  1331
}
nkeynes@953
  1332
nkeynes@953
  1333
void FASTCALL mmu_utlb_data_write( sh4addr_t addr, uint32_t val )
nkeynes@953
  1334
{
nkeynes@953
  1335
    struct utlb_entry *ent = &mmu_utlb[UTLB_ENTRY(addr)];
nkeynes@953
  1336
    if( UTLB_DATA2(addr) ) {
nkeynes@953
  1337
        ent->pcmcia = val & 0x0000000F;
nkeynes@953
  1338
    } else {
nkeynes@953
  1339
        if( ent->flags & TLB_VALID ) 
nkeynes@953
  1340
            mmu_utlb_remove_entry( UTLB_ENTRY(addr) );
nkeynes@953
  1341
        ent->ppn = (val & 0x1FFFFC00);
nkeynes@953
  1342
        ent->flags = (val & 0x000001FF);
nkeynes@953
  1343
        ent->mask = get_tlb_size_mask(val);
nkeynes@953
  1344
        if( ent->flags & TLB_VALID ) 
nkeynes@953
  1345
            mmu_utlb_insert_entry( UTLB_ENTRY(addr) );
nkeynes@953
  1346
    }
nkeynes@953
  1347
}
nkeynes@953
  1348
nkeynes@953
  1349
struct mem_region_fn p4_region_itlb_addr = {
nkeynes@953
  1350
        mmu_itlb_addr_read, mmu_itlb_addr_write,
nkeynes@953
  1351
        mmu_itlb_addr_read, mmu_itlb_addr_write,
nkeynes@953
  1352
        mmu_itlb_addr_read, mmu_itlb_addr_write,
nkeynes@953
  1353
        unmapped_read_burst, unmapped_write_burst,
nkeynes@953
  1354
        unmapped_prefetch };
nkeynes@953
  1355
struct mem_region_fn p4_region_itlb_data = {
nkeynes@953
  1356
        mmu_itlb_data_read, mmu_itlb_data_write,
nkeynes@953
  1357
        mmu_itlb_data_read, mmu_itlb_data_write,
nkeynes@953
  1358
        mmu_itlb_data_read, mmu_itlb_data_write,
nkeynes@953
  1359
        unmapped_read_burst, unmapped_write_burst,
nkeynes@953
  1360
        unmapped_prefetch };
nkeynes@953
  1361
struct mem_region_fn p4_region_utlb_addr = {
nkeynes@953
  1362
        mmu_utlb_addr_read, (mem_write_fn_t)mmu_utlb_addr_write,
nkeynes@953
  1363
        mmu_utlb_addr_read, (mem_write_fn_t)mmu_utlb_addr_write,
nkeynes@953
  1364
        mmu_utlb_addr_read, (mem_write_fn_t)mmu_utlb_addr_write,
nkeynes@953
  1365
        unmapped_read_burst, unmapped_write_burst,
nkeynes@953
  1366
        unmapped_prefetch };
nkeynes@953
  1367
struct mem_region_fn p4_region_utlb_data = {
nkeynes@953
  1368
        mmu_utlb_data_read, mmu_utlb_data_write,
nkeynes@953
  1369
        mmu_utlb_data_read, mmu_utlb_data_write,
nkeynes@953
  1370
        mmu_utlb_data_read, mmu_utlb_data_write,
nkeynes@953
  1371
        unmapped_read_burst, unmapped_write_burst,
nkeynes@953
  1372
        unmapped_prefetch };
nkeynes@953
  1373
nkeynes@953
  1374
/********************** Error regions **************************/
nkeynes@953
  1375
nkeynes@953
  1376
static void FASTCALL address_error_read( sh4addr_t addr, void *exc ) 
nkeynes@953
  1377
{
nkeynes@953
  1378
    RAISE_MEM_ERROR(EXC_DATA_ADDR_READ, addr);
nkeynes@953
  1379
    EXCEPTION_EXIT();
nkeynes@953
  1380
}
nkeynes@953
  1381
nkeynes@953
  1382
static void FASTCALL address_error_read_burst( unsigned char *dest, sh4addr_t addr, void *exc ) 
nkeynes@953
  1383
{
nkeynes@953
  1384
    RAISE_MEM_ERROR(EXC_DATA_ADDR_READ, addr);
nkeynes@953
  1385
    EXCEPTION_EXIT();
nkeynes@953
  1386
}
nkeynes@953
  1387
nkeynes@953
  1388
static void FASTCALL address_error_write( sh4addr_t addr, uint32_t val, void *exc )
nkeynes@953
  1389
{
nkeynes@953
  1390
    RAISE_MEM_ERROR(EXC_DATA_ADDR_WRITE, addr);
nkeynes@953
  1391
    EXCEPTION_EXIT();
nkeynes@953
  1392
}
nkeynes@953
  1393
nkeynes@953
  1394
static void FASTCALL tlb_miss_read( sh4addr_t addr, void *exc )
nkeynes@953
  1395
{
nkeynes@953
  1396
    RAISE_TLB_ERROR(EXC_TLB_MISS_READ, addr);
nkeynes@953
  1397
    EXCEPTION_EXIT();
nkeynes@953
  1398
}
nkeynes@953
  1399
nkeynes@953
  1400
static void FASTCALL tlb_miss_read_burst( unsigned char *dest, sh4addr_t addr, void *exc )
nkeynes@953
  1401
{
nkeynes@953
  1402
    RAISE_TLB_ERROR(EXC_TLB_MISS_READ, addr);
nkeynes@953
  1403
    EXCEPTION_EXIT();
nkeynes@953
  1404
}
nkeynes@953
  1405
nkeynes@953
  1406
static void FASTCALL tlb_miss_write( sh4addr_t addr, uint32_t val, void *exc )
nkeynes@953
  1407
{
nkeynes@953
  1408
    RAISE_TLB_ERROR(EXC_TLB_MISS_WRITE, addr);
nkeynes@953
  1409
    EXCEPTION_EXIT();
nkeynes@953
  1410
}    
nkeynes@953
  1411
nkeynes@953
  1412
static int32_t FASTCALL tlb_protected_read( sh4addr_t addr, void *exc )
nkeynes@953
  1413
{
nkeynes@953
  1414
    RAISE_MEM_ERROR(EXC_TLB_PROT_READ, addr);
nkeynes@953
  1415
    EXCEPTION_EXIT();
nkeynes@968
  1416
    return 0; 
nkeynes@953
  1417
}
nkeynes@953
  1418
nkeynes@953
  1419
static int32_t FASTCALL tlb_protected_read_burst( unsigned char *dest, sh4addr_t addr, void *exc )
nkeynes@953
  1420
{
nkeynes@953
  1421
    RAISE_MEM_ERROR(EXC_TLB_PROT_READ, addr);
nkeynes@953
  1422
    EXCEPTION_EXIT();
nkeynes@968
  1423
    return 0;
nkeynes@953
  1424
}
nkeynes@953
  1425
nkeynes@953
  1426
static void FASTCALL tlb_protected_write( sh4addr_t addr, uint32_t val, void *exc )
nkeynes@953
  1427
{
nkeynes@953
  1428
    RAISE_MEM_ERROR(EXC_TLB_PROT_WRITE, addr);
nkeynes@953
  1429
    EXCEPTION_EXIT();
nkeynes@953
  1430
}
nkeynes@953
  1431
nkeynes@953
  1432
static void FASTCALL tlb_initial_write( sh4addr_t addr, uint32_t val, void *exc )
nkeynes@953
  1433
{
nkeynes@953
  1434
    RAISE_MEM_ERROR(EXC_INIT_PAGE_WRITE, addr);
nkeynes@953
  1435
    EXCEPTION_EXIT();
nkeynes@953
  1436
}
nkeynes@953
  1437
    
nkeynes@953
  1438
static int32_t FASTCALL tlb_multi_hit_read( sh4addr_t addr, void *exc )
nkeynes@953
  1439
{
nkeynes@953
  1440
    sh4_raise_tlb_multihit(addr);
nkeynes@953
  1441
    EXCEPTION_EXIT();
nkeynes@968
  1442
    return 0; 
nkeynes@953
  1443
}
nkeynes@953
  1444
nkeynes@953
  1445
static int32_t FASTCALL tlb_multi_hit_read_burst( unsigned char *dest, sh4addr_t addr, void *exc )
nkeynes@953
  1446
{
nkeynes@953
  1447
    sh4_raise_tlb_multihit(addr);
nkeynes@953
  1448
    EXCEPTION_EXIT();
nkeynes@968
  1449
    return 0; 
nkeynes@953
  1450
}
nkeynes@953
  1451
static void FASTCALL tlb_multi_hit_write( sh4addr_t addr, uint32_t val, void *exc )
nkeynes@953
  1452
{
nkeynes@953
  1453
    sh4_raise_tlb_multihit(addr);
nkeynes@953
  1454
    EXCEPTION_EXIT();
nkeynes@953
  1455
}
nkeynes@953
  1456
nkeynes@953
  1457
/**
nkeynes@953
  1458
 * Note: Per sec 4.6.4 of the SH7750 manual, SQ 
nkeynes@953
  1459
 */
nkeynes@953
  1460
struct mem_region_fn mem_region_address_error = {
nkeynes@953
  1461
        (mem_read_fn_t)address_error_read, (mem_write_fn_t)address_error_write,
nkeynes@953
  1462
        (mem_read_fn_t)address_error_read, (mem_write_fn_t)address_error_write,
nkeynes@953
  1463
        (mem_read_fn_t)address_error_read, (mem_write_fn_t)address_error_write,
nkeynes@953
  1464
        (mem_read_burst_fn_t)address_error_read_burst, (mem_write_burst_fn_t)address_error_write,
nkeynes@953
  1465
        unmapped_prefetch };
nkeynes@953
  1466
nkeynes@953
  1467
struct mem_region_fn mem_region_tlb_miss = {
nkeynes@953
  1468
        (mem_read_fn_t)tlb_miss_read, (mem_write_fn_t)tlb_miss_write,
nkeynes@953
  1469
        (mem_read_fn_t)tlb_miss_read, (mem_write_fn_t)tlb_miss_write,
nkeynes@953
  1470
        (mem_read_fn_t)tlb_miss_read, (mem_write_fn_t)tlb_miss_write,
nkeynes@953
  1471
        (mem_read_burst_fn_t)tlb_miss_read_burst, (mem_write_burst_fn_t)tlb_miss_write,
nkeynes@953
  1472
        unmapped_prefetch };
nkeynes@953
  1473
nkeynes@953
  1474
struct mem_region_fn mem_region_tlb_protected = {
nkeynes@953
  1475
        (mem_read_fn_t)tlb_protected_read, (mem_write_fn_t)tlb_protected_write,
nkeynes@953
  1476
        (mem_read_fn_t)tlb_protected_read, (mem_write_fn_t)tlb_protected_write,
nkeynes@953
  1477
        (mem_read_fn_t)tlb_protected_read, (mem_write_fn_t)tlb_protected_write,
nkeynes@953
  1478
        (mem_read_burst_fn_t)tlb_protected_read_burst, (mem_write_burst_fn_t)tlb_protected_write,
nkeynes@953
  1479
        unmapped_prefetch };
nkeynes@953
  1480
nkeynes@953
  1481
struct mem_region_fn mem_region_tlb_multihit = {
nkeynes@953
  1482
        (mem_read_fn_t)tlb_multi_hit_read, (mem_write_fn_t)tlb_multi_hit_write,
nkeynes@953
  1483
        (mem_read_fn_t)tlb_multi_hit_read, (mem_write_fn_t)tlb_multi_hit_write,
nkeynes@953
  1484
        (mem_read_fn_t)tlb_multi_hit_read, (mem_write_fn_t)tlb_multi_hit_write,
nkeynes@953
  1485
        (mem_read_burst_fn_t)tlb_multi_hit_read_burst, (mem_write_burst_fn_t)tlb_multi_hit_write,
nkeynes@953
  1486
        (mem_prefetch_fn_t)tlb_multi_hit_read };
nkeynes@953
  1487
        
nkeynes@953
  1488
nkeynes@953
  1489
/* Store-queue regions */
nkeynes@953
  1490
/* These are a bit of a pain - the first 8 fields are controlled by SQMD, while 
nkeynes@953
  1491
 * the final (prefetch) is controlled by the actual TLB settings (plus SQMD in
nkeynes@953
  1492
 * some cases), in contrast to the ordinary fields above.
nkeynes@953
  1493
 * 
nkeynes@953
  1494
 * There is probably a simpler way to do this.
nkeynes@953
  1495
 */
nkeynes@953
  1496
nkeynes@953
  1497
struct mem_region_fn p4_region_storequeue = { 
nkeynes@953
  1498
        ccn_storequeue_read_long, ccn_storequeue_write_long,
nkeynes@953
  1499
        unmapped_read_long, unmapped_write_long, /* TESTME: Officially only long access is supported */
nkeynes@953
  1500
        unmapped_read_long, unmapped_write_long,
nkeynes@953
  1501
        unmapped_read_burst, unmapped_write_burst,
nkeynes@953
  1502
        ccn_storequeue_prefetch }; 
nkeynes@953
  1503
nkeynes@953
  1504
struct mem_region_fn p4_region_storequeue_miss = { 
nkeynes@953
  1505
        ccn_storequeue_read_long, ccn_storequeue_write_long,
nkeynes@953
  1506
        unmapped_read_long, unmapped_write_long, /* TESTME: Officially only long access is supported */
nkeynes@953
  1507
        unmapped_read_long, unmapped_write_long,
nkeynes@953
  1508
        unmapped_read_burst, unmapped_write_burst,
nkeynes@953
  1509
        (mem_prefetch_fn_t)tlb_miss_read }; 
nkeynes@953
  1510
nkeynes@953
  1511
struct mem_region_fn p4_region_storequeue_multihit = { 
nkeynes@953
  1512
        ccn_storequeue_read_long, ccn_storequeue_write_long,
nkeynes@953
  1513
        unmapped_read_long, unmapped_write_long, /* TESTME: Officially only long access is supported */
nkeynes@953
  1514
        unmapped_read_long, unmapped_write_long,
nkeynes@953
  1515
        unmapped_read_burst, unmapped_write_burst,
nkeynes@953
  1516
        (mem_prefetch_fn_t)tlb_multi_hit_read }; 
nkeynes@953
  1517
nkeynes@953
  1518
struct mem_region_fn p4_region_storequeue_protected = {
nkeynes@953
  1519
        ccn_storequeue_read_long, ccn_storequeue_write_long,
nkeynes@953
  1520
        unmapped_read_long, unmapped_write_long,
nkeynes@953
  1521
        unmapped_read_long, unmapped_write_long,
nkeynes@953
  1522
        unmapped_read_burst, unmapped_write_burst,
nkeynes@953
  1523
        (mem_prefetch_fn_t)tlb_protected_read };
nkeynes@953
  1524
nkeynes@953
  1525
struct mem_region_fn p4_region_storequeue_sqmd = {
nkeynes@953
  1526
        (mem_read_fn_t)address_error_read, (mem_write_fn_t)address_error_write,
nkeynes@953
  1527
        (mem_read_fn_t)address_error_read, (mem_write_fn_t)address_error_write,
nkeynes@953
  1528
        (mem_read_fn_t)address_error_read, (mem_write_fn_t)address_error_write,
nkeynes@953
  1529
        (mem_read_burst_fn_t)address_error_read_burst, (mem_write_burst_fn_t)address_error_write,
nkeynes@953
  1530
        (mem_prefetch_fn_t)address_error_read };        
nkeynes@953
  1531
        
nkeynes@953
  1532
struct mem_region_fn p4_region_storequeue_sqmd_miss = { 
nkeynes@953
  1533
        (mem_read_fn_t)address_error_read, (mem_write_fn_t)address_error_write,
nkeynes@953
  1534
        (mem_read_fn_t)address_error_read, (mem_write_fn_t)address_error_write,
nkeynes@953
  1535
        (mem_read_fn_t)address_error_read, (mem_write_fn_t)address_error_write,
nkeynes@953
  1536
        (mem_read_burst_fn_t)address_error_read_burst, (mem_write_burst_fn_t)address_error_write,
nkeynes@953
  1537
        (mem_prefetch_fn_t)tlb_miss_read }; 
nkeynes@953
  1538
nkeynes@953
  1539
struct mem_region_fn p4_region_storequeue_sqmd_multihit = {
nkeynes@953
  1540
        (mem_read_fn_t)address_error_read, (mem_write_fn_t)address_error_write,
nkeynes@953
  1541
        (mem_read_fn_t)address_error_read, (mem_write_fn_t)address_error_write,
nkeynes@953
  1542
        (mem_read_fn_t)address_error_read, (mem_write_fn_t)address_error_write,
nkeynes@953
  1543
        (mem_read_burst_fn_t)address_error_read_burst, (mem_write_burst_fn_t)address_error_write,
nkeynes@953
  1544
        (mem_prefetch_fn_t)tlb_multi_hit_read };        
nkeynes@953
  1545
        
nkeynes@953
  1546
struct mem_region_fn p4_region_storequeue_sqmd_protected = {
nkeynes@953
  1547
        (mem_read_fn_t)address_error_read, (mem_write_fn_t)address_error_write,
nkeynes@953
  1548
        (mem_read_fn_t)address_error_read, (mem_write_fn_t)address_error_write,
nkeynes@953
  1549
        (mem_read_fn_t)address_error_read, (mem_write_fn_t)address_error_write,
nkeynes@953
  1550
        (mem_read_burst_fn_t)address_error_read_burst, (mem_write_burst_fn_t)address_error_write,
nkeynes@953
  1551
        (mem_prefetch_fn_t)tlb_protected_read };
nkeynes@953
  1552
.