Search
lxdream.org :: lxdream/src/sh4/mmu.c
lxdream 0.9.1
released Jun 29
Download Now
filename src/sh4/mmu.c
changeset 931:430048ea8b71
prev929:fd8cb0c82f5f
next933:880c37bb1909
author nkeynes
date Tue Dec 23 05:48:05 2008 +0000 (13 years ago)
branchlxdream-mem
permissions -rw-r--r--
last change More refactoring and general cleanup. Most things should be working again now.
Split off cache and start real implementation, breaking save states in the process
file annotate diff log raw
nkeynes@550
     1
/**
nkeynes@586
     2
 * $Id$
nkeynes@826
     3
 *
nkeynes@550
     4
 * MMU implementation
nkeynes@550
     5
 *
nkeynes@550
     6
 * Copyright (c) 2005 Nathan Keynes.
nkeynes@550
     7
 *
nkeynes@550
     8
 * This program is free software; you can redistribute it and/or modify
nkeynes@550
     9
 * it under the terms of the GNU General Public License as published by
nkeynes@550
    10
 * the Free Software Foundation; either version 2 of the License, or
nkeynes@550
    11
 * (at your option) any later version.
nkeynes@550
    12
 *
nkeynes@550
    13
 * This program is distributed in the hope that it will be useful,
nkeynes@550
    14
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
nkeynes@550
    15
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
nkeynes@550
    16
 * GNU General Public License for more details.
nkeynes@550
    17
 */
nkeynes@550
    18
#define MODULE sh4_module
nkeynes@550
    19
nkeynes@550
    20
#include <stdio.h>
nkeynes@915
    21
#include <assert.h>
nkeynes@550
    22
#include "sh4/sh4mmio.h"
nkeynes@550
    23
#include "sh4/sh4core.h"
nkeynes@669
    24
#include "sh4/sh4trans.h"
nkeynes@550
    25
#include "mem.h"
nkeynes@931
    26
#include "mmu.h"
nkeynes@550
    27
nkeynes@927
    28
#ifdef HAVE_FRAME_ADDRESS
nkeynes@927
    29
#define RETURN_VIA(exc) do{ *(((void **)__builtin_frame_address(0))+1) = exc; return; } while(0)
nkeynes@927
    30
#else
nkeynes@927
    31
#define RETURN_VIA(exc) return MMU_VMA_ERROR
nkeynes@927
    32
#endif
nkeynes@927
    33
nkeynes@586
    34
/* The MMU (practically unique in the system) is allowed to raise exceptions
nkeynes@586
    35
 * directly, with a return code indicating that one was raised and the caller
nkeynes@586
    36
 * had better behave appropriately.
nkeynes@586
    37
 */
nkeynes@586
    38
#define RAISE_TLB_ERROR(code, vpn) \
nkeynes@586
    39
    MMIO_WRITE(MMU, TEA, vpn); \
nkeynes@586
    40
    MMIO_WRITE(MMU, PTEH, ((MMIO_READ(MMU, PTEH) & 0x000003FF) | (vpn&0xFFFFFC00))); \
nkeynes@586
    41
    sh4_raise_tlb_exception(code);
nkeynes@586
    42
nkeynes@586
    43
#define RAISE_MEM_ERROR(code, vpn) \
nkeynes@586
    44
    MMIO_WRITE(MMU, TEA, vpn); \
nkeynes@586
    45
    MMIO_WRITE(MMU, PTEH, ((MMIO_READ(MMU, PTEH) & 0x000003FF) | (vpn&0xFFFFFC00))); \
nkeynes@586
    46
    sh4_raise_exception(code);
nkeynes@586
    47
nkeynes@586
    48
#define RAISE_OTHER_ERROR(code) \
nkeynes@586
    49
    sh4_raise_exception(code);
nkeynes@586
    50
/**
nkeynes@586
    51
 * Abort with a non-MMU address error. Caused by user-mode code attempting
nkeynes@586
    52
 * to access privileged regions, or alignment faults.
nkeynes@586
    53
 */
nkeynes@586
    54
#define MMU_READ_ADDR_ERROR() RAISE_OTHER_ERROR(EXC_DATA_ADDR_READ)
nkeynes@586
    55
#define MMU_WRITE_ADDR_ERROR() RAISE_OTHER_ERROR(EXC_DATA_ADDR_WRITE)
nkeynes@586
    56
nkeynes@586
    57
#define MMU_TLB_READ_MISS_ERROR(vpn) RAISE_TLB_ERROR(EXC_TLB_MISS_READ, vpn)
nkeynes@586
    58
#define MMU_TLB_WRITE_MISS_ERROR(vpn) RAISE_TLB_ERROR(EXC_TLB_MISS_WRITE, vpn)
nkeynes@586
    59
#define MMU_TLB_INITIAL_WRITE_ERROR(vpn) RAISE_MEM_ERROR(EXC_INIT_PAGE_WRITE, vpn)
nkeynes@586
    60
#define MMU_TLB_READ_PROT_ERROR(vpn) RAISE_MEM_ERROR(EXC_TLB_PROT_READ, vpn)
nkeynes@586
    61
#define MMU_TLB_WRITE_PROT_ERROR(vpn) RAISE_MEM_ERROR(EXC_TLB_PROT_WRITE, vpn)
nkeynes@586
    62
#define MMU_TLB_MULTI_HIT_ERROR(vpn) sh4_raise_reset(EXC_TLB_MULTI_HIT); \
nkeynes@586
    63
    MMIO_WRITE(MMU, TEA, vpn); \
nkeynes@586
    64
    MMIO_WRITE(MMU, PTEH, ((MMIO_READ(MMU, PTEH) & 0x000003FF) | (vpn&0xFFFFFC00)));
nkeynes@586
    65
nkeynes@586
    66
nkeynes@796
    67
#define OCRAM_START (0x1C000000>>LXDREAM_PAGE_BITS)
nkeynes@796
    68
#define OCRAM_END   (0x20000000>>LXDREAM_PAGE_BITS)
nkeynes@550
    69
nkeynes@915
    70
nkeynes@550
    71
static struct itlb_entry mmu_itlb[ITLB_ENTRY_COUNT];
nkeynes@550
    72
static struct utlb_entry mmu_utlb[UTLB_ENTRY_COUNT];
nkeynes@550
    73
static uint32_t mmu_urc;
nkeynes@550
    74
static uint32_t mmu_urb;
nkeynes@550
    75
static uint32_t mmu_lrui;
nkeynes@586
    76
static uint32_t mmu_asid; // current asid
nkeynes@550
    77
nkeynes@915
    78
static struct utlb_sort_entry mmu_utlb_sorted[UTLB_ENTRY_COUNT];
nkeynes@915
    79
static uint32_t mmu_utlb_entries; // Number of entries in mmu_utlb_sorted. 
nkeynes@915
    80
nkeynes@550
    81
static sh4ptr_t cache = NULL;
nkeynes@550
    82
nkeynes@550
    83
static void mmu_invalidate_tlb();
nkeynes@915
    84
static void mmu_utlb_sorted_reset();
nkeynes@931
    85
static void mmu_utlb_sorted_reload();
nkeynes@550
    86
nkeynes@586
    87
static uint32_t get_mask_for_flags( uint32_t flags )
nkeynes@586
    88
{
nkeynes@586
    89
    switch( flags & TLB_SIZE_MASK ) {
nkeynes@586
    90
    case TLB_SIZE_1K: return MASK_1K;
nkeynes@586
    91
    case TLB_SIZE_4K: return MASK_4K;
nkeynes@586
    92
    case TLB_SIZE_64K: return MASK_64K;
nkeynes@586
    93
    case TLB_SIZE_1M: return MASK_1M;
nkeynes@669
    94
    default: return 0; /* Unreachable */
nkeynes@586
    95
    }
nkeynes@586
    96
}
nkeynes@586
    97
nkeynes@929
    98
MMIO_REGION_READ_FN( MMU, reg )
nkeynes@550
    99
{
nkeynes@929
   100
    reg &= 0xFFF;
nkeynes@550
   101
    switch( reg ) {
nkeynes@550
   102
    case MMUCR:
nkeynes@736
   103
        return MMIO_READ( MMU, MMUCR) | (mmu_urc<<10) | (mmu_urb<<18) | (mmu_lrui<<26);
nkeynes@550
   104
    default:
nkeynes@736
   105
        return MMIO_READ( MMU, reg );
nkeynes@550
   106
    }
nkeynes@550
   107
}
nkeynes@550
   108
nkeynes@929
   109
MMIO_REGION_WRITE_FN( MMU, reg, val )
nkeynes@550
   110
{
nkeynes@586
   111
    uint32_t tmp;
nkeynes@929
   112
    reg &= 0xFFF;
nkeynes@550
   113
    switch(reg) {
nkeynes@818
   114
    case SH4VER:
nkeynes@818
   115
        return;
nkeynes@550
   116
    case PTEH:
nkeynes@736
   117
        val &= 0xFFFFFCFF;
nkeynes@736
   118
        if( (val & 0xFF) != mmu_asid ) {
nkeynes@736
   119
            mmu_asid = val&0xFF;
nkeynes@736
   120
            sh4_icache.page_vma = -1; // invalidate icache as asid has changed
nkeynes@736
   121
        }
nkeynes@736
   122
        break;
nkeynes@550
   123
    case PTEL:
nkeynes@736
   124
        val &= 0x1FFFFDFF;
nkeynes@736
   125
        break;
nkeynes@550
   126
    case PTEA:
nkeynes@736
   127
        val &= 0x0000000F;
nkeynes@736
   128
        break;
nkeynes@826
   129
    case TRA:
nkeynes@826
   130
    	val &= 0x000003FC;
nkeynes@826
   131
    	break;
nkeynes@826
   132
    case EXPEVT:
nkeynes@826
   133
    case INTEVT:
nkeynes@826
   134
    	val &= 0x00000FFF;
nkeynes@826
   135
    	break;
nkeynes@550
   136
    case MMUCR:
nkeynes@736
   137
        if( val & MMUCR_TI ) {
nkeynes@736
   138
            mmu_invalidate_tlb();
nkeynes@736
   139
        }
nkeynes@736
   140
        mmu_urc = (val >> 10) & 0x3F;
nkeynes@736
   141
        mmu_urb = (val >> 18) & 0x3F;
nkeynes@736
   142
        mmu_lrui = (val >> 26) & 0x3F;
nkeynes@736
   143
        val &= 0x00000301;
nkeynes@736
   144
        tmp = MMIO_READ( MMU, MMUCR );
nkeynes@915
   145
        if( (val ^ tmp) & (MMUCR_AT|MMUCR_SV) ) {
nkeynes@736
   146
            // AT flag has changed state - flush the xlt cache as all bets
nkeynes@736
   147
            // are off now. We also need to force an immediate exit from the
nkeynes@736
   148
            // current block
nkeynes@736
   149
            MMIO_WRITE( MMU, MMUCR, val );
nkeynes@740
   150
            sh4_flush_icache();
nkeynes@736
   151
        }
nkeynes@736
   152
        break;
nkeynes@550
   153
    case CCR:
nkeynes@931
   154
        CCN_set_cache_control( val );
nkeynes@817
   155
        val &= 0x81A7;
nkeynes@736
   156
        break;
nkeynes@826
   157
    case MMUUNK1:
nkeynes@826
   158
    	/* Note that if the high bit is set, this appears to reset the machine.
nkeynes@826
   159
    	 * Not emulating this behaviour yet until we know why...
nkeynes@826
   160
    	 */
nkeynes@826
   161
    	val &= 0x00010007;
nkeynes@826
   162
    	break;
nkeynes@826
   163
    case QACR0:
nkeynes@826
   164
    case QACR1:
nkeynes@826
   165
    	val &= 0x0000001C;
nkeynes@826
   166
    	break;
nkeynes@819
   167
    case PMCR1:
nkeynes@841
   168
        PMM_write_control(0, val);
nkeynes@841
   169
        val &= 0x0000C13F;
nkeynes@841
   170
        break;
nkeynes@819
   171
    case PMCR2:
nkeynes@841
   172
        PMM_write_control(1, val);
nkeynes@841
   173
        val &= 0x0000C13F;
nkeynes@819
   174
        break;
nkeynes@550
   175
    default:
nkeynes@736
   176
        break;
nkeynes@550
   177
    }
nkeynes@550
   178
    MMIO_WRITE( MMU, reg, val );
nkeynes@550
   179
}
nkeynes@550
   180
nkeynes@550
   181
nkeynes@826
   182
void MMU_init()
nkeynes@550
   183
{
nkeynes@550
   184
}
nkeynes@550
   185
nkeynes@550
   186
void MMU_reset()
nkeynes@550
   187
{
nkeynes@550
   188
    mmio_region_MMU_write( CCR, 0 );
nkeynes@586
   189
    mmio_region_MMU_write( MMUCR, 0 );
nkeynes@915
   190
    mmu_utlb_sorted_reload();
nkeynes@550
   191
}
nkeynes@550
   192
nkeynes@550
   193
void MMU_save_state( FILE *f )
nkeynes@550
   194
{
nkeynes@550
   195
    fwrite( &mmu_itlb, sizeof(mmu_itlb), 1, f );
nkeynes@550
   196
    fwrite( &mmu_utlb, sizeof(mmu_utlb), 1, f );
nkeynes@586
   197
    fwrite( &mmu_urc, sizeof(mmu_urc), 1, f );
nkeynes@586
   198
    fwrite( &mmu_urb, sizeof(mmu_urb), 1, f );
nkeynes@586
   199
    fwrite( &mmu_lrui, sizeof(mmu_lrui), 1, f );
nkeynes@586
   200
    fwrite( &mmu_asid, sizeof(mmu_asid), 1, f );
nkeynes@550
   201
}
nkeynes@550
   202
nkeynes@550
   203
int MMU_load_state( FILE *f )
nkeynes@550
   204
{
nkeynes@550
   205
    if( fread( &mmu_itlb, sizeof(mmu_itlb), 1, f ) != 1 ) {
nkeynes@736
   206
        return 1;
nkeynes@550
   207
    }
nkeynes@550
   208
    if( fread( &mmu_utlb, sizeof(mmu_utlb), 1, f ) != 1 ) {
nkeynes@736
   209
        return 1;
nkeynes@550
   210
    }
nkeynes@586
   211
    if( fread( &mmu_urc, sizeof(mmu_urc), 1, f ) != 1 ) {
nkeynes@736
   212
        return 1;
nkeynes@586
   213
    }
nkeynes@586
   214
    if( fread( &mmu_urc, sizeof(mmu_urb), 1, f ) != 1 ) {
nkeynes@736
   215
        return 1;
nkeynes@586
   216
    }
nkeynes@586
   217
    if( fread( &mmu_lrui, sizeof(mmu_lrui), 1, f ) != 1 ) {
nkeynes@736
   218
        return 1;
nkeynes@586
   219
    }
nkeynes@586
   220
    if( fread( &mmu_asid, sizeof(mmu_asid), 1, f ) != 1 ) {
nkeynes@736
   221
        return 1;
nkeynes@586
   222
    }
nkeynes@915
   223
    mmu_utlb_sorted_reload();
nkeynes@550
   224
    return 0;
nkeynes@550
   225
}
nkeynes@550
   226
nkeynes@550
   227
nkeynes@915
   228
/******************* Sorted TLB data structure ****************/
nkeynes@915
   229
/*
nkeynes@915
   230
 * mmu_utlb_sorted maintains a list of all active (valid) entries,
nkeynes@915
   231
 * sorted by masked VPN and then ASID. Multi-hit entries are resolved 
nkeynes@915
   232
 * ahead of time, and have -1 recorded as the corresponding PPN.
nkeynes@915
   233
 * 
nkeynes@915
   234
 * FIXME: Multi-hit detection doesn't pick up cases where two pages 
nkeynes@915
   235
 * overlap due to different sizes (and don't share the same base
nkeynes@915
   236
 * address). 
nkeynes@915
   237
 */ 
nkeynes@915
   238
static void mmu_utlb_sorted_reset() 
nkeynes@915
   239
{
nkeynes@915
   240
    mmu_utlb_entries = 0;
nkeynes@915
   241
}
nkeynes@915
   242
nkeynes@915
   243
/**
nkeynes@915
   244
 * Find an entry in the sorted table (VPN+ASID check). 
nkeynes@915
   245
 */
nkeynes@915
   246
static inline int mmu_utlb_sorted_find( sh4addr_t vma )
nkeynes@915
   247
{
nkeynes@915
   248
    int low = 0;
nkeynes@915
   249
    int high = mmu_utlb_entries;
nkeynes@915
   250
    uint32_t lookup = (vma & 0xFFFFFC00) + mmu_asid;
nkeynes@915
   251
nkeynes@915
   252
    mmu_urc++;
nkeynes@915
   253
    if( mmu_urc == mmu_urb || mmu_urc == 0x40 ) {
nkeynes@915
   254
        mmu_urc = 0;
nkeynes@915
   255
    }
nkeynes@915
   256
nkeynes@915
   257
    while( low != high ) {
nkeynes@915
   258
        int posn = (high+low)>>1;
nkeynes@915
   259
        int masked = lookup & mmu_utlb_sorted[posn].mask;
nkeynes@915
   260
        if( mmu_utlb_sorted[posn].key < masked ) {
nkeynes@915
   261
            low = posn+1;
nkeynes@915
   262
        } else if( mmu_utlb_sorted[posn].key > masked ) {
nkeynes@915
   263
            high = posn;
nkeynes@915
   264
        } else {
nkeynes@915
   265
            return mmu_utlb_sorted[posn].entryNo;
nkeynes@915
   266
        }
nkeynes@915
   267
    }
nkeynes@915
   268
    return -1;
nkeynes@915
   269
nkeynes@915
   270
}
nkeynes@915
   271
nkeynes@915
   272
static void mmu_utlb_insert_entry( int entry )
nkeynes@915
   273
{
nkeynes@915
   274
    int low = 0;
nkeynes@915
   275
    int high = mmu_utlb_entries;
nkeynes@915
   276
    uint32_t key = (mmu_utlb[entry].vpn & mmu_utlb[entry].mask) + mmu_utlb[entry].asid;
nkeynes@915
   277
nkeynes@915
   278
    assert( mmu_utlb_entries < UTLB_ENTRY_COUNT );
nkeynes@915
   279
    /* Find the insertion point */
nkeynes@915
   280
    while( low != high ) {
nkeynes@915
   281
        int posn = (high+low)>>1;
nkeynes@915
   282
        if( mmu_utlb_sorted[posn].key < key ) {
nkeynes@915
   283
            low = posn+1;
nkeynes@915
   284
        } else if( mmu_utlb_sorted[posn].key > key ) {
nkeynes@915
   285
            high = posn;
nkeynes@915
   286
        } else {
nkeynes@915
   287
            /* Exact match - multi-hit */
nkeynes@915
   288
            mmu_utlb_sorted[posn].entryNo = -2;
nkeynes@915
   289
            return;
nkeynes@915
   290
        }
nkeynes@915
   291
    } /* 0 2 4 6 */
nkeynes@915
   292
    memmove( &mmu_utlb_sorted[low+1], &mmu_utlb_sorted[low], 
nkeynes@915
   293
             (mmu_utlb_entries - low) * sizeof(struct utlb_sort_entry) );
nkeynes@915
   294
    mmu_utlb_sorted[low].key = key;
nkeynes@915
   295
    mmu_utlb_sorted[low].mask = mmu_utlb[entry].mask | 0x000000FF;
nkeynes@915
   296
    mmu_utlb_sorted[low].entryNo = entry;
nkeynes@915
   297
    mmu_utlb_entries++;
nkeynes@915
   298
}
nkeynes@915
   299
nkeynes@915
   300
static void mmu_utlb_remove_entry( int entry )
nkeynes@915
   301
{
nkeynes@915
   302
    int low = 0;
nkeynes@915
   303
    int high = mmu_utlb_entries;
nkeynes@915
   304
    uint32_t key = (mmu_utlb[entry].vpn & mmu_utlb[entry].mask) + mmu_utlb[entry].asid;
nkeynes@915
   305
    while( low != high ) {
nkeynes@915
   306
        int posn = (high+low)>>1;
nkeynes@915
   307
        if( mmu_utlb_sorted[posn].key < key ) {
nkeynes@915
   308
            low = posn+1;
nkeynes@915
   309
        } else if( mmu_utlb_sorted[posn].key > key ) {
nkeynes@915
   310
            high = posn;
nkeynes@915
   311
        } else {
nkeynes@915
   312
            if( mmu_utlb_sorted[posn].entryNo == -2 ) {
nkeynes@915
   313
                /* Multiple-entry recorded - rebuild the whole table minus entry */
nkeynes@915
   314
                int i;
nkeynes@915
   315
                mmu_utlb_entries = 0;
nkeynes@915
   316
                for( i=0; i< UTLB_ENTRY_COUNT; i++ ) {
nkeynes@915
   317
                    if( i != entry && (mmu_utlb[i].flags & TLB_VALID)  ) {
nkeynes@915
   318
                        mmu_utlb_insert_entry(i);
nkeynes@915
   319
                    }
nkeynes@915
   320
                }
nkeynes@915
   321
            } else {
nkeynes@915
   322
                mmu_utlb_entries--;
nkeynes@915
   323
                memmove( &mmu_utlb_sorted[posn], &mmu_utlb_sorted[posn+1],
nkeynes@915
   324
                         (mmu_utlb_entries - posn)*sizeof(struct utlb_sort_entry) );
nkeynes@915
   325
            }
nkeynes@915
   326
            return;
nkeynes@915
   327
        }
nkeynes@915
   328
    }
nkeynes@915
   329
    assert( 0 && "UTLB key not found!" );
nkeynes@915
   330
}
nkeynes@915
   331
nkeynes@915
   332
static void mmu_utlb_sorted_reload()
nkeynes@915
   333
{
nkeynes@915
   334
    int i;
nkeynes@915
   335
    mmu_utlb_entries = 0;
nkeynes@915
   336
    for( i=0; i<UTLB_ENTRY_COUNT; i++ ) {
nkeynes@915
   337
        if( mmu_utlb[i].flags & TLB_VALID ) 
nkeynes@915
   338
            mmu_utlb_insert_entry( i );
nkeynes@915
   339
    }
nkeynes@915
   340
}
nkeynes@915
   341
nkeynes@550
   342
/* TLB maintanence */
nkeynes@550
   343
nkeynes@550
   344
/**
nkeynes@550
   345
 * LDTLB instruction implementation. Copies PTEH, PTEL and PTEA into the UTLB
nkeynes@550
   346
 * entry identified by MMUCR.URC. Does not modify MMUCR or the ITLB.
nkeynes@550
   347
 */
nkeynes@550
   348
void MMU_ldtlb()
nkeynes@550
   349
{
nkeynes@915
   350
    if( mmu_utlb[mmu_urc].flags & TLB_VALID )
nkeynes@915
   351
        mmu_utlb_remove_entry( mmu_urc );
nkeynes@550
   352
    mmu_utlb[mmu_urc].vpn = MMIO_READ(MMU, PTEH) & 0xFFFFFC00;
nkeynes@550
   353
    mmu_utlb[mmu_urc].asid = MMIO_READ(MMU, PTEH) & 0x000000FF;
nkeynes@550
   354
    mmu_utlb[mmu_urc].ppn = MMIO_READ(MMU, PTEL) & 0x1FFFFC00;
nkeynes@550
   355
    mmu_utlb[mmu_urc].flags = MMIO_READ(MMU, PTEL) & 0x00001FF;
nkeynes@550
   356
    mmu_utlb[mmu_urc].pcmcia = MMIO_READ(MMU, PTEA);
nkeynes@586
   357
    mmu_utlb[mmu_urc].mask = get_mask_for_flags(mmu_utlb[mmu_urc].flags);
nkeynes@915
   358
    if( mmu_utlb[mmu_urc].ppn >= 0x1C000000 )
nkeynes@915
   359
        mmu_utlb[mmu_urc].ppn |= 0xE0000000;
nkeynes@915
   360
    if( mmu_utlb[mmu_urc].flags & TLB_VALID )
nkeynes@915
   361
        mmu_utlb_insert_entry( mmu_urc );
nkeynes@550
   362
}
nkeynes@550
   363
nkeynes@550
   364
static void mmu_invalidate_tlb()
nkeynes@550
   365
{
nkeynes@550
   366
    int i;
nkeynes@550
   367
    for( i=0; i<ITLB_ENTRY_COUNT; i++ ) {
nkeynes@736
   368
        mmu_itlb[i].flags &= (~TLB_VALID);
nkeynes@550
   369
    }
nkeynes@550
   370
    for( i=0; i<UTLB_ENTRY_COUNT; i++ ) {
nkeynes@736
   371
        mmu_utlb[i].flags &= (~TLB_VALID);
nkeynes@550
   372
    }
nkeynes@915
   373
    mmu_utlb_entries = 0;
nkeynes@550
   374
}
nkeynes@550
   375
nkeynes@550
   376
#define ITLB_ENTRY(addr) ((addr>>7)&0x03)
nkeynes@550
   377
nkeynes@929
   378
int32_t FASTCALL mmu_itlb_addr_read( sh4addr_t addr )
nkeynes@550
   379
{
nkeynes@550
   380
    struct itlb_entry *ent = &mmu_itlb[ITLB_ENTRY(addr)];
nkeynes@550
   381
    return ent->vpn | ent->asid | (ent->flags & TLB_VALID);
nkeynes@550
   382
}
nkeynes@929
   383
int32_t FASTCALL mmu_itlb_data_read( sh4addr_t addr )
nkeynes@550
   384
{
nkeynes@550
   385
    struct itlb_entry *ent = &mmu_itlb[ITLB_ENTRY(addr)];
nkeynes@915
   386
    return (ent->ppn & 0x1FFFFC00) | ent->flags;
nkeynes@550
   387
}
nkeynes@550
   388
nkeynes@929
   389
void FASTCALL mmu_itlb_addr_write( sh4addr_t addr, uint32_t val )
nkeynes@550
   390
{
nkeynes@550
   391
    struct itlb_entry *ent = &mmu_itlb[ITLB_ENTRY(addr)];
nkeynes@550
   392
    ent->vpn = val & 0xFFFFFC00;
nkeynes@550
   393
    ent->asid = val & 0x000000FF;
nkeynes@550
   394
    ent->flags = (ent->flags & ~(TLB_VALID)) | (val&TLB_VALID);
nkeynes@550
   395
}
nkeynes@550
   396
nkeynes@929
   397
void FASTCALL mmu_itlb_data_write( sh4addr_t addr, uint32_t val )
nkeynes@550
   398
{
nkeynes@550
   399
    struct itlb_entry *ent = &mmu_itlb[ITLB_ENTRY(addr)];
nkeynes@550
   400
    ent->ppn = val & 0x1FFFFC00;
nkeynes@550
   401
    ent->flags = val & 0x00001DA;
nkeynes@586
   402
    ent->mask = get_mask_for_flags(val);
nkeynes@915
   403
    if( ent->ppn >= 0x1C000000 )
nkeynes@915
   404
        ent->ppn |= 0xE0000000;
nkeynes@550
   405
}
nkeynes@550
   406
nkeynes@550
   407
#define UTLB_ENTRY(addr) ((addr>>8)&0x3F)
nkeynes@550
   408
#define UTLB_ASSOC(addr) (addr&0x80)
nkeynes@550
   409
#define UTLB_DATA2(addr) (addr&0x00800000)
nkeynes@550
   410
nkeynes@929
   411
int32_t FASTCALL mmu_utlb_addr_read( sh4addr_t addr )
nkeynes@550
   412
{
nkeynes@550
   413
    struct utlb_entry *ent = &mmu_utlb[UTLB_ENTRY(addr)];
nkeynes@550
   414
    return ent->vpn | ent->asid | (ent->flags & TLB_VALID) |
nkeynes@736
   415
    ((ent->flags & TLB_DIRTY)<<7);
nkeynes@550
   416
}
nkeynes@929
   417
int32_t FASTCALL mmu_utlb_data_read( sh4addr_t addr )
nkeynes@550
   418
{
nkeynes@550
   419
    struct utlb_entry *ent = &mmu_utlb[UTLB_ENTRY(addr)];
nkeynes@550
   420
    if( UTLB_DATA2(addr) ) {
nkeynes@736
   421
        return ent->pcmcia;
nkeynes@550
   422
    } else {
nkeynes@915
   423
        return (ent->ppn&0x1FFFFC00) | ent->flags;
nkeynes@550
   424
    }
nkeynes@550
   425
}
nkeynes@550
   426
nkeynes@586
   427
/**
nkeynes@586
   428
 * Find a UTLB entry for the associative TLB write - same as the normal
nkeynes@586
   429
 * lookup but ignores the valid bit.
nkeynes@586
   430
 */
nkeynes@669
   431
static inline int mmu_utlb_lookup_assoc( uint32_t vpn, uint32_t asid )
nkeynes@586
   432
{
nkeynes@586
   433
    int result = -1;
nkeynes@586
   434
    unsigned int i;
nkeynes@586
   435
    for( i = 0; i < UTLB_ENTRY_COUNT; i++ ) {
nkeynes@736
   436
        if( (mmu_utlb[i].flags & TLB_VALID) &&
nkeynes@826
   437
                ((mmu_utlb[i].flags & TLB_SHARE) || asid == mmu_utlb[i].asid) &&
nkeynes@736
   438
                ((mmu_utlb[i].vpn ^ vpn) & mmu_utlb[i].mask) == 0 ) {
nkeynes@736
   439
            if( result != -1 ) {
nkeynes@736
   440
                fprintf( stderr, "TLB Multi hit: %d %d\n", result, i );
nkeynes@736
   441
                return -2;
nkeynes@736
   442
            }
nkeynes@736
   443
            result = i;
nkeynes@736
   444
        }
nkeynes@586
   445
    }
nkeynes@586
   446
    return result;
nkeynes@586
   447
}
nkeynes@586
   448
nkeynes@586
   449
/**
nkeynes@586
   450
 * Find a ITLB entry for the associative TLB write - same as the normal
nkeynes@586
   451
 * lookup but ignores the valid bit.
nkeynes@586
   452
 */
nkeynes@669
   453
static inline int mmu_itlb_lookup_assoc( uint32_t vpn, uint32_t asid )
nkeynes@586
   454
{
nkeynes@586
   455
    int result = -1;
nkeynes@586
   456
    unsigned int i;
nkeynes@586
   457
    for( i = 0; i < ITLB_ENTRY_COUNT; i++ ) {
nkeynes@736
   458
        if( (mmu_itlb[i].flags & TLB_VALID) &&
nkeynes@826
   459
                ((mmu_itlb[i].flags & TLB_SHARE) || asid == mmu_itlb[i].asid) &&
nkeynes@736
   460
                ((mmu_itlb[i].vpn ^ vpn) & mmu_itlb[i].mask) == 0 ) {
nkeynes@736
   461
            if( result != -1 ) {
nkeynes@736
   462
                return -2;
nkeynes@736
   463
            }
nkeynes@736
   464
            result = i;
nkeynes@736
   465
        }
nkeynes@586
   466
    }
nkeynes@586
   467
    return result;
nkeynes@586
   468
}
nkeynes@586
   469
nkeynes@929
   470
void FASTCALL mmu_utlb_addr_write( sh4addr_t addr, uint32_t val )
nkeynes@550
   471
{
nkeynes@550
   472
    if( UTLB_ASSOC(addr) ) {
nkeynes@736
   473
        int utlb = mmu_utlb_lookup_assoc( val, mmu_asid );
nkeynes@736
   474
        if( utlb >= 0 ) {
nkeynes@736
   475
            struct utlb_entry *ent = &mmu_utlb[utlb];
nkeynes@915
   476
            uint32_t old_flags = ent->flags;
nkeynes@736
   477
            ent->flags = ent->flags & ~(TLB_DIRTY|TLB_VALID);
nkeynes@736
   478
            ent->flags |= (val & TLB_VALID);
nkeynes@736
   479
            ent->flags |= ((val & 0x200)>>7);
nkeynes@915
   480
            if( (old_flags & TLB_VALID) && !(ent->flags&TLB_VALID) ) {
nkeynes@915
   481
                mmu_utlb_remove_entry( utlb );
nkeynes@915
   482
            } else if( !(old_flags & TLB_VALID) && (ent->flags&TLB_VALID) ) {
nkeynes@915
   483
                mmu_utlb_insert_entry( utlb );
nkeynes@915
   484
            }
nkeynes@736
   485
        }
nkeynes@586
   486
nkeynes@736
   487
        int itlb = mmu_itlb_lookup_assoc( val, mmu_asid );
nkeynes@736
   488
        if( itlb >= 0 ) {
nkeynes@736
   489
            struct itlb_entry *ent = &mmu_itlb[itlb];
nkeynes@736
   490
            ent->flags = (ent->flags & (~TLB_VALID)) | (val & TLB_VALID);
nkeynes@736
   491
        }
nkeynes@586
   492
nkeynes@736
   493
        if( itlb == -2 || utlb == -2 ) {
nkeynes@736
   494
            MMU_TLB_MULTI_HIT_ERROR(addr);
nkeynes@736
   495
            return;
nkeynes@736
   496
        }
nkeynes@550
   497
    } else {
nkeynes@736
   498
        struct utlb_entry *ent = &mmu_utlb[UTLB_ENTRY(addr)];
nkeynes@915
   499
        if( ent->flags & TLB_VALID ) 
nkeynes@915
   500
            mmu_utlb_remove_entry( UTLB_ENTRY(addr) );
nkeynes@736
   501
        ent->vpn = (val & 0xFFFFFC00);
nkeynes@736
   502
        ent->asid = (val & 0xFF);
nkeynes@736
   503
        ent->flags = (ent->flags & ~(TLB_DIRTY|TLB_VALID));
nkeynes@736
   504
        ent->flags |= (val & TLB_VALID);
nkeynes@736
   505
        ent->flags |= ((val & 0x200)>>7);
nkeynes@915
   506
        if( ent->flags & TLB_VALID ) 
nkeynes@915
   507
            mmu_utlb_insert_entry( UTLB_ENTRY(addr) );
nkeynes@550
   508
    }
nkeynes@550
   509
}
nkeynes@550
   510
nkeynes@929
   511
void FASTCALL mmu_utlb_data_write( sh4addr_t addr, uint32_t val )
nkeynes@550
   512
{
nkeynes@550
   513
    struct utlb_entry *ent = &mmu_utlb[UTLB_ENTRY(addr)];
nkeynes@550
   514
    if( UTLB_DATA2(addr) ) {
nkeynes@736
   515
        ent->pcmcia = val & 0x0000000F;
nkeynes@550
   516
    } else {
nkeynes@915
   517
        if( ent->flags & TLB_VALID ) 
nkeynes@915
   518
            mmu_utlb_remove_entry( UTLB_ENTRY(addr) );
nkeynes@736
   519
        ent->ppn = (val & 0x1FFFFC00);
nkeynes@736
   520
        ent->flags = (val & 0x000001FF);
nkeynes@736
   521
        ent->mask = get_mask_for_flags(val);
nkeynes@915
   522
        if( mmu_utlb[mmu_urc].ppn >= 0x1C000000 )
nkeynes@915
   523
            mmu_utlb[mmu_urc].ppn |= 0xE0000000;
nkeynes@915
   524
        if( ent->flags & TLB_VALID ) 
nkeynes@915
   525
            mmu_utlb_insert_entry( UTLB_ENTRY(addr) );
nkeynes@550
   526
    }
nkeynes@550
   527
}
nkeynes@550
   528
nkeynes@550
   529
/* Cache access - not implemented */
nkeynes@550
   530
nkeynes@929
   531
int32_t FASTCALL mmu_icache_addr_read( sh4addr_t addr )
nkeynes@550
   532
{
nkeynes@550
   533
    return 0; // not implemented
nkeynes@550
   534
}
nkeynes@929
   535
int32_t FASTCALL mmu_icache_data_read( sh4addr_t addr )
nkeynes@550
   536
{
nkeynes@550
   537
    return 0; // not implemented
nkeynes@550
   538
}
nkeynes@929
   539
int32_t FASTCALL mmu_ocache_addr_read( sh4addr_t addr )
nkeynes@550
   540
{
nkeynes@550
   541
    return 0; // not implemented
nkeynes@550
   542
}
nkeynes@929
   543
int32_t FASTCALL mmu_ocache_data_read( sh4addr_t addr )
nkeynes@550
   544
{
nkeynes@550
   545
    return 0; // not implemented
nkeynes@550
   546
}
nkeynes@550
   547
nkeynes@929
   548
void FASTCALL mmu_icache_addr_write( sh4addr_t addr, uint32_t val )
nkeynes@550
   549
{
nkeynes@550
   550
}
nkeynes@550
   551
nkeynes@929
   552
void FASTCALL mmu_icache_data_write( sh4addr_t addr, uint32_t val )
nkeynes@550
   553
{
nkeynes@550
   554
}
nkeynes@550
   555
nkeynes@929
   556
void FASTCALL mmu_ocache_addr_write( sh4addr_t addr, uint32_t val )
nkeynes@550
   557
{
nkeynes@550
   558
}
nkeynes@550
   559
nkeynes@929
   560
void FASTCALL mmu_ocache_data_write( sh4addr_t addr, uint32_t val )
nkeynes@550
   561
{
nkeynes@550
   562
}
nkeynes@586
   563
nkeynes@586
   564
/******************************************************************************/
nkeynes@586
   565
/*                        MMU TLB address translation                         */
nkeynes@586
   566
/******************************************************************************/
nkeynes@586
   567
nkeynes@586
   568
/**
nkeynes@826
   569
 * The translations are excessively complicated, but unfortunately it's a
nkeynes@586
   570
 * complicated system. TODO: make this not be painfully slow.
nkeynes@586
   571
 */
nkeynes@586
   572
nkeynes@586
   573
/**
nkeynes@586
   574
 * Perform the actual utlb lookup w/ asid matching.
nkeynes@586
   575
 * Possible utcomes are:
nkeynes@586
   576
 *   0..63 Single match - good, return entry found
nkeynes@586
   577
 *   -1 No match - raise a tlb data miss exception
nkeynes@586
   578
 *   -2 Multiple matches - raise a multi-hit exception (reset)
nkeynes@586
   579
 * @param vpn virtual address to resolve
nkeynes@586
   580
 * @return the resultant UTLB entry, or an error.
nkeynes@586
   581
 */
nkeynes@586
   582
static inline int mmu_utlb_lookup_vpn_asid( uint32_t vpn )
nkeynes@586
   583
{
nkeynes@586
   584
    int result = -1;
nkeynes@586
   585
    unsigned int i;
nkeynes@586
   586
nkeynes@586
   587
    mmu_urc++;
nkeynes@586
   588
    if( mmu_urc == mmu_urb || mmu_urc == 0x40 ) {
nkeynes@736
   589
        mmu_urc = 0;
nkeynes@586
   590
    }
nkeynes@586
   591
nkeynes@586
   592
    for( i = 0; i < UTLB_ENTRY_COUNT; i++ ) {
nkeynes@736
   593
        if( (mmu_utlb[i].flags & TLB_VALID) &&
nkeynes@826
   594
                ((mmu_utlb[i].flags & TLB_SHARE) || mmu_asid == mmu_utlb[i].asid) &&
nkeynes@736
   595
                ((mmu_utlb[i].vpn ^ vpn) & mmu_utlb[i].mask) == 0 ) {
nkeynes@736
   596
            if( result != -1 ) {
nkeynes@736
   597
                return -2;
nkeynes@736
   598
            }
nkeynes@736
   599
            result = i;
nkeynes@736
   600
        }
nkeynes@586
   601
    }
nkeynes@586
   602
    return result;
nkeynes@586
   603
}
nkeynes@586
   604
nkeynes@586
   605
/**
nkeynes@586
   606
 * Perform the actual utlb lookup matching on vpn only
nkeynes@586
   607
 * Possible utcomes are:
nkeynes@586
   608
 *   0..63 Single match - good, return entry found
nkeynes@586
   609
 *   -1 No match - raise a tlb data miss exception
nkeynes@586
   610
 *   -2 Multiple matches - raise a multi-hit exception (reset)
nkeynes@586
   611
 * @param vpn virtual address to resolve
nkeynes@586
   612
 * @return the resultant UTLB entry, or an error.
nkeynes@586
   613
 */
nkeynes@586
   614
static inline int mmu_utlb_lookup_vpn( uint32_t vpn )
nkeynes@586
   615
{
nkeynes@586
   616
    int result = -1;
nkeynes@586
   617
    unsigned int i;
nkeynes@586
   618
nkeynes@586
   619
    mmu_urc++;
nkeynes@586
   620
    if( mmu_urc == mmu_urb || mmu_urc == 0x40 ) {
nkeynes@736
   621
        mmu_urc = 0;
nkeynes@586
   622
    }
nkeynes@586
   623
nkeynes@586
   624
    for( i = 0; i < UTLB_ENTRY_COUNT; i++ ) {
nkeynes@736
   625
        if( (mmu_utlb[i].flags & TLB_VALID) &&
nkeynes@736
   626
                ((mmu_utlb[i].vpn ^ vpn) & mmu_utlb[i].mask) == 0 ) {
nkeynes@736
   627
            if( result != -1 ) {
nkeynes@736
   628
                return -2;
nkeynes@736
   629
            }
nkeynes@736
   630
            result = i;
nkeynes@736
   631
        }
nkeynes@586
   632
    }
nkeynes@586
   633
nkeynes@586
   634
    return result;
nkeynes@586
   635
}
nkeynes@586
   636
nkeynes@586
   637
/**
nkeynes@586
   638
 * Update the ITLB by replacing the LRU entry with the specified UTLB entry.
nkeynes@586
   639
 * @return the number (0-3) of the replaced entry.
nkeynes@586
   640
 */
nkeynes@586
   641
static int inline mmu_itlb_update_from_utlb( int entryNo )
nkeynes@586
   642
{
nkeynes@586
   643
    int replace;
nkeynes@586
   644
    /* Determine entry to replace based on lrui */
nkeynes@586
   645
    if( (mmu_lrui & 0x38) == 0x38 ) {
nkeynes@736
   646
        replace = 0;
nkeynes@736
   647
        mmu_lrui = mmu_lrui & 0x07;
nkeynes@586
   648
    } else if( (mmu_lrui & 0x26) == 0x06 ) {
nkeynes@736
   649
        replace = 1;
nkeynes@736
   650
        mmu_lrui = (mmu_lrui & 0x19) | 0x20;
nkeynes@586
   651
    } else if( (mmu_lrui & 0x15) == 0x01 ) {
nkeynes@736
   652
        replace = 2;
nkeynes@736
   653
        mmu_lrui = (mmu_lrui & 0x3E) | 0x14;
nkeynes@586
   654
    } else { // Note - gets invalid entries too
nkeynes@736
   655
        replace = 3;
nkeynes@736
   656
        mmu_lrui = (mmu_lrui | 0x0B);
nkeynes@826
   657
    }
nkeynes@586
   658
nkeynes@586
   659
    mmu_itlb[replace].vpn = mmu_utlb[entryNo].vpn;
nkeynes@586
   660
    mmu_itlb[replace].mask = mmu_utlb[entryNo].mask;
nkeynes@586
   661
    mmu_itlb[replace].ppn = mmu_utlb[entryNo].ppn;
nkeynes@586
   662
    mmu_itlb[replace].asid = mmu_utlb[entryNo].asid;
nkeynes@586
   663
    mmu_itlb[replace].flags = mmu_utlb[entryNo].flags & 0x01DA;
nkeynes@586
   664
    return replace;
nkeynes@586
   665
}
nkeynes@586
   666
nkeynes@586
   667
/**
nkeynes@586
   668
 * Perform the actual itlb lookup w/ asid protection
nkeynes@586
   669
 * Possible utcomes are:
nkeynes@586
   670
 *   0..63 Single match - good, return entry found
nkeynes@586
   671
 *   -1 No match - raise a tlb data miss exception
nkeynes@586
   672
 *   -2 Multiple matches - raise a multi-hit exception (reset)
nkeynes@586
   673
 * @param vpn virtual address to resolve
nkeynes@586
   674
 * @return the resultant ITLB entry, or an error.
nkeynes@586
   675
 */
nkeynes@586
   676
static inline int mmu_itlb_lookup_vpn_asid( uint32_t vpn )
nkeynes@586
   677
{
nkeynes@586
   678
    int result = -1;
nkeynes@586
   679
    unsigned int i;
nkeynes@586
   680
nkeynes@586
   681
    for( i = 0; i < ITLB_ENTRY_COUNT; i++ ) {
nkeynes@736
   682
        if( (mmu_itlb[i].flags & TLB_VALID) &&
nkeynes@826
   683
                ((mmu_itlb[i].flags & TLB_SHARE) || mmu_asid == mmu_itlb[i].asid) &&
nkeynes@736
   684
                ((mmu_itlb[i].vpn ^ vpn) & mmu_itlb[i].mask) == 0 ) {
nkeynes@736
   685
            if( result != -1 ) {
nkeynes@736
   686
                return -2;
nkeynes@736
   687
            }
nkeynes@736
   688
            result = i;
nkeynes@736
   689
        }
nkeynes@586
   690
    }
nkeynes@586
   691
nkeynes@586
   692
    if( result == -1 ) {
nkeynes@915
   693
        int utlbEntry = mmu_utlb_sorted_find( vpn );
nkeynes@736
   694
        if( utlbEntry < 0 ) {
nkeynes@736
   695
            return utlbEntry;
nkeynes@736
   696
        } else {
nkeynes@736
   697
            return mmu_itlb_update_from_utlb( utlbEntry );
nkeynes@736
   698
        }
nkeynes@586
   699
    }
nkeynes@586
   700
nkeynes@586
   701
    switch( result ) {
nkeynes@586
   702
    case 0: mmu_lrui = (mmu_lrui & 0x07); break;
nkeynes@586
   703
    case 1: mmu_lrui = (mmu_lrui & 0x19) | 0x20; break;
nkeynes@586
   704
    case 2: mmu_lrui = (mmu_lrui & 0x3E) | 0x14; break;
nkeynes@586
   705
    case 3: mmu_lrui = (mmu_lrui | 0x0B); break;
nkeynes@586
   706
    }
nkeynes@736
   707
nkeynes@586
   708
    return result;
nkeynes@586
   709
}
nkeynes@586
   710
nkeynes@586
   711
/**
nkeynes@586
   712
 * Perform the actual itlb lookup on vpn only
nkeynes@586
   713
 * Possible utcomes are:
nkeynes@586
   714
 *   0..63 Single match - good, return entry found
nkeynes@586
   715
 *   -1 No match - raise a tlb data miss exception
nkeynes@586
   716
 *   -2 Multiple matches - raise a multi-hit exception (reset)
nkeynes@586
   717
 * @param vpn virtual address to resolve
nkeynes@586
   718
 * @return the resultant ITLB entry, or an error.
nkeynes@586
   719
 */
nkeynes@586
   720
static inline int mmu_itlb_lookup_vpn( uint32_t vpn )
nkeynes@586
   721
{
nkeynes@586
   722
    int result = -1;
nkeynes@586
   723
    unsigned int i;
nkeynes@586
   724
nkeynes@586
   725
    for( i = 0; i < ITLB_ENTRY_COUNT; i++ ) {
nkeynes@736
   726
        if( (mmu_itlb[i].flags & TLB_VALID) &&
nkeynes@736
   727
                ((mmu_itlb[i].vpn ^ vpn) & mmu_itlb[i].mask) == 0 ) {
nkeynes@736
   728
            if( result != -1 ) {
nkeynes@736
   729
                return -2;
nkeynes@736
   730
            }
nkeynes@736
   731
            result = i;
nkeynes@736
   732
        }
nkeynes@586
   733
    }
nkeynes@586
   734
nkeynes@586
   735
    if( result == -1 ) {
nkeynes@736
   736
        int utlbEntry = mmu_utlb_lookup_vpn( vpn );
nkeynes@736
   737
        if( utlbEntry < 0 ) {
nkeynes@736
   738
            return utlbEntry;
nkeynes@736
   739
        } else {
nkeynes@736
   740
            return mmu_itlb_update_from_utlb( utlbEntry );
nkeynes@736
   741
        }
nkeynes@586
   742
    }
nkeynes@586
   743
nkeynes@586
   744
    switch( result ) {
nkeynes@586
   745
    case 0: mmu_lrui = (mmu_lrui & 0x07); break;
nkeynes@586
   746
    case 1: mmu_lrui = (mmu_lrui & 0x19) | 0x20; break;
nkeynes@586
   747
    case 2: mmu_lrui = (mmu_lrui & 0x3E) | 0x14; break;
nkeynes@586
   748
    case 3: mmu_lrui = (mmu_lrui | 0x0B); break;
nkeynes@586
   749
    }
nkeynes@736
   750
nkeynes@586
   751
    return result;
nkeynes@586
   752
}
nkeynes@927
   753
nkeynes@927
   754
#ifdef HAVE_FRAME_ADDRESS
nkeynes@927
   755
sh4addr_t FASTCALL mmu_vma_to_phys_read( sh4vma_t addr, void *exc )
nkeynes@927
   756
#else
nkeynes@905
   757
sh4addr_t FASTCALL mmu_vma_to_phys_read( sh4vma_t addr )
nkeynes@927
   758
#endif
nkeynes@586
   759
{
nkeynes@586
   760
    uint32_t mmucr = MMIO_READ(MMU,MMUCR);
nkeynes@586
   761
    if( addr & 0x80000000 ) {
nkeynes@736
   762
        if( IS_SH4_PRIVMODE() ) {
nkeynes@736
   763
            if( addr >= 0xE0000000 ) {
nkeynes@736
   764
                return addr; /* P4 - passthrough */
nkeynes@736
   765
            } else if( addr < 0xC0000000 ) {
nkeynes@736
   766
                /* P1, P2 regions are pass-through (no translation) */
nkeynes@736
   767
                return VMA_TO_EXT_ADDR(addr);
nkeynes@736
   768
            }
nkeynes@736
   769
        } else {
nkeynes@736
   770
            if( addr >= 0xE0000000 && addr < 0xE4000000 &&
nkeynes@736
   771
                    ((mmucr&MMUCR_SQMD) == 0) ) {
nkeynes@736
   772
                /* Conditional user-mode access to the store-queue (no translation) */
nkeynes@736
   773
                return addr;
nkeynes@736
   774
            }
nkeynes@736
   775
            MMU_READ_ADDR_ERROR();
nkeynes@927
   776
            RETURN_VIA(exc);
nkeynes@736
   777
        }
nkeynes@586
   778
    }
nkeynes@736
   779
nkeynes@586
   780
    if( (mmucr & MMUCR_AT) == 0 ) {
nkeynes@736
   781
        return VMA_TO_EXT_ADDR(addr);
nkeynes@586
   782
    }
nkeynes@586
   783
nkeynes@586
   784
    /* If we get this far, translation is required */
nkeynes@586
   785
    int entryNo;
nkeynes@586
   786
    if( ((mmucr & MMUCR_SV) == 0) || !IS_SH4_PRIVMODE() ) {
nkeynes@915
   787
        entryNo = mmu_utlb_sorted_find( addr );
nkeynes@586
   788
    } else {
nkeynes@736
   789
        entryNo = mmu_utlb_lookup_vpn( addr );
nkeynes@586
   790
    }
nkeynes@586
   791
nkeynes@586
   792
    switch(entryNo) {
nkeynes@586
   793
    case -1:
nkeynes@736
   794
    MMU_TLB_READ_MISS_ERROR(addr);
nkeynes@927
   795
    RETURN_VIA(exc);
nkeynes@586
   796
    case -2:
nkeynes@736
   797
    MMU_TLB_MULTI_HIT_ERROR(addr);
nkeynes@927
   798
    RETURN_VIA(exc);
nkeynes@586
   799
    default:
nkeynes@736
   800
        if( (mmu_utlb[entryNo].flags & TLB_USERMODE) == 0 &&
nkeynes@736
   801
                !IS_SH4_PRIVMODE() ) {
nkeynes@736
   802
            /* protection violation */
nkeynes@736
   803
            MMU_TLB_READ_PROT_ERROR(addr);
nkeynes@927
   804
            RETURN_VIA(exc);
nkeynes@736
   805
        }
nkeynes@586
   806
nkeynes@736
   807
        /* finally generate the target address */
nkeynes@915
   808
        return (mmu_utlb[entryNo].ppn & mmu_utlb[entryNo].mask) |
nkeynes@810
   809
        	(addr & (~mmu_utlb[entryNo].mask));
nkeynes@586
   810
    }
nkeynes@586
   811
}
nkeynes@586
   812
nkeynes@927
   813
#ifdef HAVE_FRAME_ADDRESS
nkeynes@927
   814
sh4addr_t FASTCALL mmu_vma_to_phys_write( sh4vma_t addr, void *exc )
nkeynes@927
   815
#else
nkeynes@905
   816
sh4addr_t FASTCALL mmu_vma_to_phys_write( sh4vma_t addr )
nkeynes@927
   817
#endif
nkeynes@586
   818
{
nkeynes@586
   819
    uint32_t mmucr = MMIO_READ(MMU,MMUCR);
nkeynes@586
   820
    if( addr & 0x80000000 ) {
nkeynes@736
   821
        if( IS_SH4_PRIVMODE() ) {
nkeynes@736
   822
            if( addr >= 0xE0000000 ) {
nkeynes@736
   823
                return addr; /* P4 - passthrough */
nkeynes@736
   824
            } else if( addr < 0xC0000000 ) {
nkeynes@736
   825
                /* P1, P2 regions are pass-through (no translation) */
nkeynes@736
   826
                return VMA_TO_EXT_ADDR(addr);
nkeynes@736
   827
            }
nkeynes@736
   828
        } else {
nkeynes@736
   829
            if( addr >= 0xE0000000 && addr < 0xE4000000 &&
nkeynes@736
   830
                    ((mmucr&MMUCR_SQMD) == 0) ) {
nkeynes@736
   831
                /* Conditional user-mode access to the store-queue (no translation) */
nkeynes@736
   832
                return addr;
nkeynes@736
   833
            }
nkeynes@736
   834
            MMU_WRITE_ADDR_ERROR();
nkeynes@927
   835
            RETURN_VIA(exc);
nkeynes@736
   836
        }
nkeynes@586
   837
    }
nkeynes@736
   838
nkeynes@586
   839
    if( (mmucr & MMUCR_AT) == 0 ) {
nkeynes@736
   840
        return VMA_TO_EXT_ADDR(addr);
nkeynes@586
   841
    }
nkeynes@586
   842
nkeynes@586
   843
    /* If we get this far, translation is required */
nkeynes@586
   844
    int entryNo;
nkeynes@586
   845
    if( ((mmucr & MMUCR_SV) == 0) || !IS_SH4_PRIVMODE() ) {
nkeynes@915
   846
        entryNo = mmu_utlb_sorted_find( addr );
nkeynes@586
   847
    } else {
nkeynes@736
   848
        entryNo = mmu_utlb_lookup_vpn( addr );
nkeynes@586
   849
    }
nkeynes@586
   850
nkeynes@586
   851
    switch(entryNo) {
nkeynes@586
   852
    case -1:
nkeynes@736
   853
    MMU_TLB_WRITE_MISS_ERROR(addr);
nkeynes@927
   854
    RETURN_VIA(exc);
nkeynes@586
   855
    case -2:
nkeynes@736
   856
    MMU_TLB_MULTI_HIT_ERROR(addr);
nkeynes@927
   857
    RETURN_VIA(exc);
nkeynes@586
   858
    default:
nkeynes@736
   859
        if( IS_SH4_PRIVMODE() ? ((mmu_utlb[entryNo].flags & TLB_WRITABLE) == 0)
nkeynes@736
   860
                : ((mmu_utlb[entryNo].flags & TLB_USERWRITABLE) != TLB_USERWRITABLE) ) {
nkeynes@736
   861
            /* protection violation */
nkeynes@736
   862
            MMU_TLB_WRITE_PROT_ERROR(addr);
nkeynes@927
   863
            RETURN_VIA(exc);
nkeynes@736
   864
        }
nkeynes@586
   865
nkeynes@736
   866
        if( (mmu_utlb[entryNo].flags & TLB_DIRTY) == 0 ) {
nkeynes@736
   867
            MMU_TLB_INITIAL_WRITE_ERROR(addr);
nkeynes@927
   868
            RETURN_VIA(exc);
nkeynes@736
   869
        }
nkeynes@586
   870
nkeynes@736
   871
        /* finally generate the target address */
nkeynes@826
   872
        sh4addr_t pma = (mmu_utlb[entryNo].ppn & mmu_utlb[entryNo].mask) |
nkeynes@810
   873
        	(addr & (~mmu_utlb[entryNo].mask));
nkeynes@810
   874
        return pma;
nkeynes@586
   875
    }
nkeynes@586
   876
}
nkeynes@586
   877
nkeynes@586
   878
/**
nkeynes@586
   879
 * Update the icache for an untranslated address
nkeynes@586
   880
 */
nkeynes@905
   881
static inline void mmu_update_icache_phys( sh4addr_t addr )
nkeynes@586
   882
{
nkeynes@586
   883
    if( (addr & 0x1C000000) == 0x0C000000 ) {
nkeynes@736
   884
        /* Main ram */
nkeynes@736
   885
        sh4_icache.page_vma = addr & 0xFF000000;
nkeynes@736
   886
        sh4_icache.page_ppa = 0x0C000000;
nkeynes@736
   887
        sh4_icache.mask = 0xFF000000;
nkeynes@736
   888
        sh4_icache.page = sh4_main_ram;
nkeynes@586
   889
    } else if( (addr & 0x1FE00000) == 0 ) {
nkeynes@736
   890
        /* BIOS ROM */
nkeynes@736
   891
        sh4_icache.page_vma = addr & 0xFFE00000;
nkeynes@736
   892
        sh4_icache.page_ppa = 0;
nkeynes@736
   893
        sh4_icache.mask = 0xFFE00000;
nkeynes@736
   894
        sh4_icache.page = mem_get_region(0);
nkeynes@586
   895
    } else {
nkeynes@736
   896
        /* not supported */
nkeynes@736
   897
        sh4_icache.page_vma = -1;
nkeynes@586
   898
    }
nkeynes@586
   899
}
nkeynes@586
   900
nkeynes@586
   901
/**
nkeynes@586
   902
 * Update the sh4_icache structure to describe the page(s) containing the
nkeynes@586
   903
 * given vma. If the address does not reference a RAM/ROM region, the icache
nkeynes@586
   904
 * will be invalidated instead.
nkeynes@586
   905
 * If AT is on, this method will raise TLB exceptions normally
nkeynes@586
   906
 * (hence this method should only be used immediately prior to execution of
nkeynes@586
   907
 * code), and otherwise will set the icache according to the matching TLB entry.
nkeynes@586
   908
 * If AT is off, this method will set the entire referenced RAM/ROM region in
nkeynes@586
   909
 * the icache.
nkeynes@586
   910
 * @return TRUE if the update completed (successfully or otherwise), FALSE
nkeynes@586
   911
 * if an exception was raised.
nkeynes@586
   912
 */
nkeynes@905
   913
gboolean FASTCALL mmu_update_icache( sh4vma_t addr )
nkeynes@586
   914
{
nkeynes@586
   915
    int entryNo;
nkeynes@586
   916
    if( IS_SH4_PRIVMODE()  ) {
nkeynes@736
   917
        if( addr & 0x80000000 ) {
nkeynes@736
   918
            if( addr < 0xC0000000 ) {
nkeynes@736
   919
                /* P1, P2 and P4 regions are pass-through (no translation) */
nkeynes@736
   920
                mmu_update_icache_phys(addr);
nkeynes@736
   921
                return TRUE;
nkeynes@736
   922
            } else if( addr >= 0xE0000000 && addr < 0xFFFFFF00 ) {
nkeynes@736
   923
                MMU_READ_ADDR_ERROR();
nkeynes@736
   924
                return FALSE;
nkeynes@736
   925
            }
nkeynes@736
   926
        }
nkeynes@586
   927
nkeynes@736
   928
        uint32_t mmucr = MMIO_READ(MMU,MMUCR);
nkeynes@736
   929
        if( (mmucr & MMUCR_AT) == 0 ) {
nkeynes@736
   930
            mmu_update_icache_phys(addr);
nkeynes@736
   931
            return TRUE;
nkeynes@736
   932
        }
nkeynes@736
   933
nkeynes@826
   934
        if( (mmucr & MMUCR_SV) == 0 )
nkeynes@807
   935
        	entryNo = mmu_itlb_lookup_vpn_asid( addr );
nkeynes@807
   936
        else
nkeynes@807
   937
        	entryNo = mmu_itlb_lookup_vpn( addr );
nkeynes@586
   938
    } else {
nkeynes@736
   939
        if( addr & 0x80000000 ) {
nkeynes@736
   940
            MMU_READ_ADDR_ERROR();
nkeynes@736
   941
            return FALSE;
nkeynes@736
   942
        }
nkeynes@586
   943
nkeynes@736
   944
        uint32_t mmucr = MMIO_READ(MMU,MMUCR);
nkeynes@736
   945
        if( (mmucr & MMUCR_AT) == 0 ) {
nkeynes@736
   946
            mmu_update_icache_phys(addr);
nkeynes@736
   947
            return TRUE;
nkeynes@736
   948
        }
nkeynes@736
   949
nkeynes@807
   950
        entryNo = mmu_itlb_lookup_vpn_asid( addr );
nkeynes@807
   951
nkeynes@736
   952
        if( entryNo != -1 && (mmu_itlb[entryNo].flags & TLB_USERMODE) == 0 ) {
nkeynes@736
   953
            MMU_TLB_READ_PROT_ERROR(addr);
nkeynes@736
   954
            return FALSE;
nkeynes@736
   955
        }
nkeynes@586
   956
    }
nkeynes@586
   957
nkeynes@586
   958
    switch(entryNo) {
nkeynes@586
   959
    case -1:
nkeynes@736
   960
    MMU_TLB_READ_MISS_ERROR(addr);
nkeynes@736
   961
    return FALSE;
nkeynes@586
   962
    case -2:
nkeynes@736
   963
    MMU_TLB_MULTI_HIT_ERROR(addr);
nkeynes@736
   964
    return FALSE;
nkeynes@586
   965
    default:
nkeynes@736
   966
        sh4_icache.page_ppa = mmu_itlb[entryNo].ppn & mmu_itlb[entryNo].mask;
nkeynes@736
   967
        sh4_icache.page = mem_get_region( sh4_icache.page_ppa );
nkeynes@736
   968
        if( sh4_icache.page == NULL ) {
nkeynes@736
   969
            sh4_icache.page_vma = -1;
nkeynes@736
   970
        } else {
nkeynes@736
   971
            sh4_icache.page_vma = mmu_itlb[entryNo].vpn & mmu_itlb[entryNo].mask;
nkeynes@736
   972
            sh4_icache.mask = mmu_itlb[entryNo].mask;
nkeynes@736
   973
        }
nkeynes@736
   974
        return TRUE;
nkeynes@586
   975
    }
nkeynes@586
   976
}
nkeynes@586
   977
nkeynes@597
   978
/**
nkeynes@826
   979
 * Translate address for disassembly purposes (ie performs an instruction
nkeynes@597
   980
 * lookup) - does not raise exceptions or modify any state, and ignores
nkeynes@597
   981
 * protection bits. Returns the translated address, or MMU_VMA_ERROR
nkeynes@826
   982
 * on translation failure.
nkeynes@597
   983
 */
nkeynes@905
   984
sh4addr_t FASTCALL mmu_vma_to_phys_disasm( sh4vma_t vma )
nkeynes@597
   985
{
nkeynes@597
   986
    if( vma & 0x80000000 ) {
nkeynes@736
   987
        if( vma < 0xC0000000 ) {
nkeynes@736
   988
            /* P1, P2 and P4 regions are pass-through (no translation) */
nkeynes@736
   989
            return VMA_TO_EXT_ADDR(vma);
nkeynes@736
   990
        } else if( vma >= 0xE0000000 && vma < 0xFFFFFF00 ) {
nkeynes@736
   991
            /* Not translatable */
nkeynes@736
   992
            return MMU_VMA_ERROR;
nkeynes@736
   993
        }
nkeynes@597
   994
    }
nkeynes@597
   995
nkeynes@597
   996
    uint32_t mmucr = MMIO_READ(MMU,MMUCR);
nkeynes@597
   997
    if( (mmucr & MMUCR_AT) == 0 ) {
nkeynes@736
   998
        return VMA_TO_EXT_ADDR(vma);
nkeynes@597
   999
    }
nkeynes@736
  1000
nkeynes@597
  1001
    int entryNo = mmu_itlb_lookup_vpn( vma );
nkeynes@597
  1002
    if( entryNo == -2 ) {
nkeynes@736
  1003
        entryNo = mmu_itlb_lookup_vpn_asid( vma );
nkeynes@597
  1004
    }
nkeynes@597
  1005
    if( entryNo < 0 ) {
nkeynes@736
  1006
        return MMU_VMA_ERROR;
nkeynes@597
  1007
    } else {
nkeynes@826
  1008
        return (mmu_itlb[entryNo].ppn & mmu_itlb[entryNo].mask) |
nkeynes@826
  1009
        (vma & (~mmu_itlb[entryNo].mask));
nkeynes@597
  1010
    }
nkeynes@597
  1011
}
nkeynes@597
  1012
nkeynes@911
  1013
void FASTCALL sh4_flush_store_queue( sh4addr_t addr )
nkeynes@911
  1014
{
nkeynes@911
  1015
    int queue = (addr&0x20)>>2;
nkeynes@911
  1016
    uint32_t hi = MMIO_READ( MMU, QACR0 + (queue>>1)) << 24;
nkeynes@911
  1017
    sh4ptr_t src = (sh4ptr_t)&sh4r.store_queue[queue];
nkeynes@911
  1018
    sh4addr_t target = (addr&0x03FFFFE0) | hi;
nkeynes@931
  1019
    ext_address_space[target>>12]->write_burst( target, src );
nkeynes@931
  1020
//    mem_copy_to_sh4( target, src, 32 );
nkeynes@911
  1021
} 
nkeynes@911
  1022
nkeynes@911
  1023
gboolean FASTCALL sh4_flush_store_queue_mmu( sh4addr_t addr )
nkeynes@586
  1024
{
nkeynes@586
  1025
    uint32_t mmucr = MMIO_READ(MMU,MMUCR);
nkeynes@586
  1026
    int queue = (addr&0x20)>>2;
nkeynes@586
  1027
    sh4ptr_t src = (sh4ptr_t)&sh4r.store_queue[queue];
nkeynes@586
  1028
    sh4addr_t target;
nkeynes@586
  1029
    /* Store queue operation */
nkeynes@736
  1030
nkeynes@911
  1031
    int entryNo;
nkeynes@911
  1032
    if( ((mmucr & MMUCR_SV) == 0) || !IS_SH4_PRIVMODE() ) {
nkeynes@911
  1033
    	entryNo = mmu_utlb_lookup_vpn_asid( addr );
nkeynes@911
  1034
    } else {
nkeynes@911
  1035
    	entryNo = mmu_utlb_lookup_vpn( addr );
nkeynes@911
  1036
    }
nkeynes@911
  1037
    switch(entryNo) {
nkeynes@911
  1038
    case -1:
nkeynes@911
  1039
    MMU_TLB_WRITE_MISS_ERROR(addr);
nkeynes@911
  1040
    return FALSE;
nkeynes@911
  1041
    case -2:
nkeynes@911
  1042
    MMU_TLB_MULTI_HIT_ERROR(addr);
nkeynes@911
  1043
    return FALSE;
nkeynes@911
  1044
    default:
nkeynes@911
  1045
    	if( IS_SH4_PRIVMODE() ? ((mmu_utlb[entryNo].flags & TLB_WRITABLE) == 0)
nkeynes@911
  1046
    			: ((mmu_utlb[entryNo].flags & TLB_USERWRITABLE) != TLB_USERWRITABLE) ) {
nkeynes@911
  1047
    		/* protection violation */
nkeynes@911
  1048
    		MMU_TLB_WRITE_PROT_ERROR(addr);
nkeynes@911
  1049
    		return FALSE;
nkeynes@911
  1050
    	}
nkeynes@736
  1051
nkeynes@911
  1052
    	if( (mmu_utlb[entryNo].flags & TLB_DIRTY) == 0 ) {
nkeynes@911
  1053
    		MMU_TLB_INITIAL_WRITE_ERROR(addr);
nkeynes@911
  1054
    		return FALSE;
nkeynes@911
  1055
    	}
nkeynes@911
  1056
nkeynes@911
  1057
    	/* finally generate the target address */
nkeynes@911
  1058
    	target = ((mmu_utlb[entryNo].ppn & mmu_utlb[entryNo].mask) |
nkeynes@911
  1059
    			(addr & (~mmu_utlb[entryNo].mask))) & 0xFFFFFFE0;
nkeynes@586
  1060
    }
nkeynes@911
  1061
nkeynes@931
  1062
    ext_address_space[target>>12]->write_burst( target, src );
nkeynes@931
  1063
    // mem_copy_to_sh4( target, src, 32 );
nkeynes@586
  1064
    return TRUE;
nkeynes@586
  1065
}
nkeynes@586
  1066
.