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