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