Search
lxdream.org :: lxdream/src/sh4/mmu.c
lxdream 0.9.1
released Jun 29
Download Now
filename src/sh4/mmu.c
changeset 550:a27e31340147
next559:06714bc64271
next586:2a3ba82cf243
author nkeynes
date Thu Dec 06 10:43:30 2007 +0000 (12 years ago)
permissions -rw-r--r--
last change Add support for the MMIO side of the TLB (and LDTLB)
file annotate diff log raw
nkeynes@550
     1
/**
nkeynes@550
     2
 * $Id: mmu.c,v 1.15 2007-11-08 11:54:16 nkeynes Exp $
nkeynes@550
     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@550
    21
#include "sh4/sh4mmio.h"
nkeynes@550
    22
#include "sh4/sh4core.h"
nkeynes@550
    23
#include "mem.h"
nkeynes@550
    24
nkeynes@550
    25
#define OCRAM_START (0x1C000000>>PAGE_BITS)
nkeynes@550
    26
#define OCRAM_END   (0x20000000>>PAGE_BITS)
nkeynes@550
    27
nkeynes@550
    28
#define ITLB_ENTRY_COUNT 4
nkeynes@550
    29
#define UTLB_ENTRY_COUNT 64
nkeynes@550
    30
nkeynes@550
    31
/* Entry address */
nkeynes@550
    32
#define TLB_VALID     0x00000100
nkeynes@550
    33
#define TLB_USERMODE  0x00000040
nkeynes@550
    34
#define TLB_WRITABLE  0x00000020
nkeynes@550
    35
#define TLB_SIZE_MASK 0x00000090
nkeynes@550
    36
#define TLB_SIZE_1K   0x00000000
nkeynes@550
    37
#define TLB_SIZE_4K   0x00000010
nkeynes@550
    38
#define TLB_SIZE_64K  0x00000080
nkeynes@550
    39
#define TLB_SIZE_1M   0x00000090
nkeynes@550
    40
#define TLB_CACHEABLE 0x00000008
nkeynes@550
    41
#define TLB_DIRTY     0x00000004
nkeynes@550
    42
#define TLB_SHARE     0x00000002
nkeynes@550
    43
#define TLB_WRITETHRU 0x00000001
nkeynes@550
    44
nkeynes@550
    45
nkeynes@550
    46
struct itlb_entry {
nkeynes@550
    47
    sh4addr_t vpn; // Virtual Page Number
nkeynes@550
    48
    uint32_t asid; // Process ID
nkeynes@550
    49
    sh4addr_t ppn; // Physical Page Number
nkeynes@550
    50
    uint32_t flags;
nkeynes@550
    51
};
nkeynes@550
    52
nkeynes@550
    53
struct utlb_entry {
nkeynes@550
    54
    sh4addr_t vpn; // Virtual Page Number
nkeynes@550
    55
    uint32_t asid; // Process ID
nkeynes@550
    56
    sh4addr_t ppn; // Physical Page Number
nkeynes@550
    57
    uint32_t flags;
nkeynes@550
    58
    uint32_t pcmcia; // extra pcmcia data - not used
nkeynes@550
    59
};
nkeynes@550
    60
nkeynes@550
    61
static struct itlb_entry mmu_itlb[ITLB_ENTRY_COUNT];
nkeynes@550
    62
static struct utlb_entry mmu_utlb[UTLB_ENTRY_COUNT];
nkeynes@550
    63
static uint32_t mmu_urc;
nkeynes@550
    64
static uint32_t mmu_urb;
nkeynes@550
    65
static uint32_t mmu_lrui;
nkeynes@550
    66
nkeynes@550
    67
static sh4ptr_t cache = NULL;
nkeynes@550
    68
nkeynes@550
    69
static void mmu_invalidate_tlb();
nkeynes@550
    70
nkeynes@550
    71
nkeynes@550
    72
int32_t mmio_region_MMU_read( uint32_t reg )
nkeynes@550
    73
{
nkeynes@550
    74
    switch( reg ) {
nkeynes@550
    75
    case MMUCR:
nkeynes@550
    76
	return MMIO_READ( MMU, MMUCR) | (mmu_urc<<10) | (mmu_urb<<18) | (mmu_lrui<<26);
nkeynes@550
    77
    default:
nkeynes@550
    78
	return MMIO_READ( MMU, reg );
nkeynes@550
    79
    }
nkeynes@550
    80
}
nkeynes@550
    81
nkeynes@550
    82
void mmio_region_MMU_write( uint32_t reg, uint32_t val )
nkeynes@550
    83
{
nkeynes@550
    84
    switch(reg) {
nkeynes@550
    85
    case PTEH:
nkeynes@550
    86
	val &= 0xFFFFFCFF;
nkeynes@550
    87
	break;
nkeynes@550
    88
    case PTEL:
nkeynes@550
    89
	val &= 0x1FFFFDFF;
nkeynes@550
    90
	break;
nkeynes@550
    91
    case PTEA:
nkeynes@550
    92
	val &= 0x0000000F;
nkeynes@550
    93
	break;
nkeynes@550
    94
    case MMUCR:
nkeynes@550
    95
	if( val & MMUCR_TI ) {
nkeynes@550
    96
	    mmu_invalidate_tlb();
nkeynes@550
    97
	}
nkeynes@550
    98
	mmu_urc = (val >> 10) & 0x3F;
nkeynes@550
    99
	mmu_urb = (val >> 18) & 0x3F;
nkeynes@550
   100
	mmu_lrui = (val >> 26) & 0x3F;
nkeynes@550
   101
	val &= 0x00000301;
nkeynes@550
   102
	break;
nkeynes@550
   103
    case CCR:
nkeynes@550
   104
	mmu_set_cache_mode( val & (CCR_OIX|CCR_ORA) );
nkeynes@550
   105
	break;
nkeynes@550
   106
    default:
nkeynes@550
   107
	break;
nkeynes@550
   108
    }
nkeynes@550
   109
    MMIO_WRITE( MMU, reg, val );
nkeynes@550
   110
}
nkeynes@550
   111
nkeynes@550
   112
nkeynes@550
   113
void MMU_init() 
nkeynes@550
   114
{
nkeynes@550
   115
    cache = mem_alloc_pages(2);
nkeynes@550
   116
}
nkeynes@550
   117
nkeynes@550
   118
void MMU_reset()
nkeynes@550
   119
{
nkeynes@550
   120
    mmio_region_MMU_write( CCR, 0 );
nkeynes@550
   121
}
nkeynes@550
   122
nkeynes@550
   123
void MMU_save_state( FILE *f )
nkeynes@550
   124
{
nkeynes@550
   125
    fwrite( cache, 4096, 2, f );
nkeynes@550
   126
    fwrite( &mmu_itlb, sizeof(mmu_itlb), 1, f );
nkeynes@550
   127
    fwrite( &mmu_utlb, sizeof(mmu_utlb), 1, f );
nkeynes@550
   128
}
nkeynes@550
   129
nkeynes@550
   130
int MMU_load_state( FILE *f )
nkeynes@550
   131
{
nkeynes@550
   132
    /* Setup the cache mode according to the saved register value
nkeynes@550
   133
     * (mem_load runs before this point to load all MMIO data)
nkeynes@550
   134
     */
nkeynes@550
   135
    mmio_region_MMU_write( CCR, MMIO_READ(MMU, CCR) );
nkeynes@550
   136
    if( fread( cache, 4096, 2, f ) != 2 ) {
nkeynes@550
   137
	return 1;
nkeynes@550
   138
    }
nkeynes@550
   139
    if( fread( &mmu_itlb, sizeof(mmu_itlb), 1, f ) != 1 ) {
nkeynes@550
   140
	return 1;
nkeynes@550
   141
    }
nkeynes@550
   142
    if( fread( &mmu_utlb, sizeof(mmu_utlb), 1, f ) != 1 ) {
nkeynes@550
   143
	return 1;
nkeynes@550
   144
    }
nkeynes@550
   145
    return 0;
nkeynes@550
   146
}
nkeynes@550
   147
nkeynes@550
   148
void mmu_set_cache_mode( int mode )
nkeynes@550
   149
{
nkeynes@550
   150
    uint32_t i;
nkeynes@550
   151
    switch( mode ) {
nkeynes@550
   152
        case MEM_OC_INDEX0: /* OIX=0 */
nkeynes@550
   153
            for( i=OCRAM_START; i<OCRAM_END; i++ )
nkeynes@550
   154
                page_map[i] = cache + ((i&0x02)<<(PAGE_BITS-1));
nkeynes@550
   155
            break;
nkeynes@550
   156
        case MEM_OC_INDEX1: /* OIX=1 */
nkeynes@550
   157
            for( i=OCRAM_START; i<OCRAM_END; i++ )
nkeynes@550
   158
                page_map[i] = cache + ((i&0x02000000)>>(25-PAGE_BITS));
nkeynes@550
   159
            break;
nkeynes@550
   160
        default: /* disabled */
nkeynes@550
   161
            for( i=OCRAM_START; i<OCRAM_END; i++ )
nkeynes@550
   162
                page_map[i] = NULL;
nkeynes@550
   163
            break;
nkeynes@550
   164
    }
nkeynes@550
   165
}
nkeynes@550
   166
nkeynes@550
   167
/* TLB maintanence */
nkeynes@550
   168
nkeynes@550
   169
/**
nkeynes@550
   170
 * LDTLB instruction implementation. Copies PTEH, PTEL and PTEA into the UTLB
nkeynes@550
   171
 * entry identified by MMUCR.URC. Does not modify MMUCR or the ITLB.
nkeynes@550
   172
 */
nkeynes@550
   173
void MMU_ldtlb()
nkeynes@550
   174
{
nkeynes@550
   175
    mmu_utlb[mmu_urc].vpn = MMIO_READ(MMU, PTEH) & 0xFFFFFC00;
nkeynes@550
   176
    mmu_utlb[mmu_urc].asid = MMIO_READ(MMU, PTEH) & 0x000000FF;
nkeynes@550
   177
    mmu_utlb[mmu_urc].ppn = MMIO_READ(MMU, PTEL) & 0x1FFFFC00;
nkeynes@550
   178
    mmu_utlb[mmu_urc].flags = MMIO_READ(MMU, PTEL) & 0x00001FF;
nkeynes@550
   179
    mmu_utlb[mmu_urc].pcmcia = MMIO_READ(MMU, PTEA);
nkeynes@550
   180
}
nkeynes@550
   181
nkeynes@550
   182
uint64_t mmu_translate_read( sh4addr_t addr )
nkeynes@550
   183
{
nkeynes@550
   184
    uint32_t mmucr = MMIO_READ(MMU,MMUCR);
nkeynes@550
   185
    if( IS_SH4_PRIVMODE() ) {
nkeynes@550
   186
	switch( addr & 0xE0000000 ) {
nkeynes@550
   187
	case 0x80000000: case 0xA0000000:
nkeynes@550
   188
	    /* Non-translated read P1,P2 */
nkeynes@550
   189
	    break;
nkeynes@550
   190
	case 0xE0000000:
nkeynes@550
   191
	    /* Non-translated read P4 */
nkeynes@550
   192
	    break;
nkeynes@550
   193
	default:
nkeynes@550
   194
	    if( mmucr&MMUCR_AT ) {
nkeynes@550
   195
	    } else {
nkeynes@550
   196
		// direct read
nkeynes@550
   197
	    }
nkeynes@550
   198
	}
nkeynes@550
   199
    } else {
nkeynes@550
   200
	if( addr & 0x80000000 ) {
nkeynes@550
   201
	    if( ((addr&0xFC000000) == 0xE0000000 ) &&
nkeynes@550
   202
		((mmucr&MMUCR_SQMD) == 0) ) { 
nkeynes@550
   203
		// Store queue
nkeynes@550
   204
		return 0;
nkeynes@550
   205
	    }
nkeynes@550
   206
//	    MMU_READ_ADDR_ERROR();
nkeynes@550
   207
	}
nkeynes@550
   208
	if( mmucr&MMUCR_AT ) {
nkeynes@550
   209
	    uint32_t vpn = addr & 0xFFFFFC00;
nkeynes@550
   210
	    uint32_t asid = MMIO_READ(MMU,PTEH)&0xFF;
nkeynes@550
   211
	} else {
nkeynes@550
   212
	    // direct read
nkeynes@550
   213
	}
nkeynes@550
   214
    }
nkeynes@550
   215
}
nkeynes@550
   216
nkeynes@550
   217
static void mmu_invalidate_tlb()
nkeynes@550
   218
{
nkeynes@550
   219
    int i;
nkeynes@550
   220
    for( i=0; i<ITLB_ENTRY_COUNT; i++ ) {
nkeynes@550
   221
	mmu_itlb[i].flags &= (~TLB_VALID);
nkeynes@550
   222
    }
nkeynes@550
   223
    for( i=0; i<UTLB_ENTRY_COUNT; i++ ) {
nkeynes@550
   224
	mmu_utlb[i].flags &= (~TLB_VALID);
nkeynes@550
   225
    }
nkeynes@550
   226
}
nkeynes@550
   227
nkeynes@550
   228
#define ITLB_ENTRY(addr) ((addr>>7)&0x03)
nkeynes@550
   229
nkeynes@550
   230
int32_t mmu_itlb_addr_read( sh4addr_t addr )
nkeynes@550
   231
{
nkeynes@550
   232
    struct itlb_entry *ent = &mmu_itlb[ITLB_ENTRY(addr)];
nkeynes@550
   233
    return ent->vpn | ent->asid | (ent->flags & TLB_VALID);
nkeynes@550
   234
}
nkeynes@550
   235
int32_t mmu_itlb_data_read( sh4addr_t addr )
nkeynes@550
   236
{
nkeynes@550
   237
    struct itlb_entry *ent = &mmu_itlb[ITLB_ENTRY(addr)];
nkeynes@550
   238
    return ent->ppn | ent->flags;
nkeynes@550
   239
}
nkeynes@550
   240
nkeynes@550
   241
void mmu_itlb_addr_write( sh4addr_t addr, uint32_t val )
nkeynes@550
   242
{
nkeynes@550
   243
    struct itlb_entry *ent = &mmu_itlb[ITLB_ENTRY(addr)];
nkeynes@550
   244
    ent->vpn = val & 0xFFFFFC00;
nkeynes@550
   245
    ent->asid = val & 0x000000FF;
nkeynes@550
   246
    ent->flags = (ent->flags & ~(TLB_VALID)) | (val&TLB_VALID);
nkeynes@550
   247
}
nkeynes@550
   248
nkeynes@550
   249
void mmu_itlb_data_write( sh4addr_t addr, uint32_t val )
nkeynes@550
   250
{
nkeynes@550
   251
    struct itlb_entry *ent = &mmu_itlb[ITLB_ENTRY(addr)];
nkeynes@550
   252
    ent->ppn = val & 0x1FFFFC00;
nkeynes@550
   253
    ent->flags = val & 0x00001DA;
nkeynes@550
   254
}
nkeynes@550
   255
nkeynes@550
   256
#define UTLB_ENTRY(addr) ((addr>>8)&0x3F)
nkeynes@550
   257
#define UTLB_ASSOC(addr) (addr&0x80)
nkeynes@550
   258
#define UTLB_DATA2(addr) (addr&0x00800000)
nkeynes@550
   259
nkeynes@550
   260
int32_t mmu_utlb_addr_read( sh4addr_t addr )
nkeynes@550
   261
{
nkeynes@550
   262
    struct utlb_entry *ent = &mmu_utlb[UTLB_ENTRY(addr)];
nkeynes@550
   263
    return ent->vpn | ent->asid | (ent->flags & TLB_VALID) |
nkeynes@550
   264
	((ent->flags & TLB_DIRTY)<<7);
nkeynes@550
   265
}
nkeynes@550
   266
int32_t mmu_utlb_data_read( sh4addr_t addr )
nkeynes@550
   267
{
nkeynes@550
   268
    struct utlb_entry *ent = &mmu_utlb[UTLB_ENTRY(addr)];
nkeynes@550
   269
    if( UTLB_DATA2(addr) ) {
nkeynes@550
   270
	return ent->pcmcia;
nkeynes@550
   271
    } else {
nkeynes@550
   272
	return ent->ppn | ent->flags;
nkeynes@550
   273
    }
nkeynes@550
   274
}
nkeynes@550
   275
nkeynes@550
   276
void mmu_utlb_addr_write( sh4addr_t addr, uint32_t val )
nkeynes@550
   277
{
nkeynes@550
   278
    if( UTLB_ASSOC(addr) ) {
nkeynes@550
   279
    } else {
nkeynes@550
   280
	struct utlb_entry *ent = &mmu_utlb[UTLB_ENTRY(addr)];
nkeynes@550
   281
	ent->vpn = (val & 0xFFFFFC00);
nkeynes@550
   282
	ent->asid = (val & 0xFF);
nkeynes@550
   283
	ent->flags = (ent->flags & ~(TLB_DIRTY|TLB_VALID));
nkeynes@550
   284
	ent->flags |= (val & TLB_VALID);
nkeynes@550
   285
	ent->flags |= ((val & 0x200)>>7);
nkeynes@550
   286
    }
nkeynes@550
   287
}
nkeynes@550
   288
nkeynes@550
   289
void mmu_utlb_data_write( sh4addr_t addr, uint32_t val )
nkeynes@550
   290
{
nkeynes@550
   291
    struct utlb_entry *ent = &mmu_utlb[UTLB_ENTRY(addr)];
nkeynes@550
   292
    if( UTLB_DATA2(addr) ) {
nkeynes@550
   293
	ent->pcmcia = val & 0x0000000F;
nkeynes@550
   294
    } else {
nkeynes@550
   295
	ent->ppn = (val & 0x1FFFFC00);
nkeynes@550
   296
	ent->flags = (val & 0x000001FF);
nkeynes@550
   297
    }
nkeynes@550
   298
}
nkeynes@550
   299
nkeynes@550
   300
/* Cache access - not implemented */
nkeynes@550
   301
nkeynes@550
   302
int32_t mmu_icache_addr_read( sh4addr_t addr )
nkeynes@550
   303
{
nkeynes@550
   304
    return 0; // not implemented
nkeynes@550
   305
}
nkeynes@550
   306
int32_t mmu_icache_data_read( sh4addr_t addr )
nkeynes@550
   307
{
nkeynes@550
   308
    return 0; // not implemented
nkeynes@550
   309
}
nkeynes@550
   310
int32_t mmu_ocache_addr_read( sh4addr_t addr )
nkeynes@550
   311
{
nkeynes@550
   312
    return 0; // not implemented
nkeynes@550
   313
}
nkeynes@550
   314
int32_t mmu_ocache_data_read( sh4addr_t addr )
nkeynes@550
   315
{
nkeynes@550
   316
    return 0; // not implemented
nkeynes@550
   317
}
nkeynes@550
   318
nkeynes@550
   319
void mmu_icache_addr_write( sh4addr_t addr, uint32_t val )
nkeynes@550
   320
{
nkeynes@550
   321
}
nkeynes@550
   322
nkeynes@550
   323
void mmu_icache_data_write( sh4addr_t addr, uint32_t val )
nkeynes@550
   324
{
nkeynes@550
   325
}
nkeynes@550
   326
nkeynes@550
   327
void mmu_ocache_addr_write( sh4addr_t addr, uint32_t val )
nkeynes@550
   328
{
nkeynes@550
   329
}
nkeynes@550
   330
nkeynes@550
   331
void mmu_ocache_data_write( sh4addr_t addr, uint32_t val )
nkeynes@550
   332
{
nkeynes@550
   333
}
.