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 | }
|