[502609c8] | 1 | /* |
---|
| 2 | * Simple interface to the PowerPC 405 MMU |
---|
| 3 | * |
---|
| 4 | * Michael Hamel ADInstruments 2008 |
---|
| 5 | * |
---|
| 6 | */ |
---|
| 7 | |
---|
| 8 | |
---|
| 9 | #include <bsp.h> |
---|
| 10 | #include <libcpu/powerpc-utility.h> |
---|
| 11 | #include "mmu_405.h" |
---|
[28593c6e] | 12 | #include <inttypes.h> |
---|
[502609c8] | 13 | |
---|
| 14 | /* #define qLogTLB */ |
---|
| 15 | /* #define qLogTLBDetails */ |
---|
| 16 | |
---|
| 17 | |
---|
[ff1c613] | 18 | /*----------------------------- TLB handling -------------------------------- */ |
---|
[502609c8] | 19 | /* The following are in assembler in mmu_405asm.S */ |
---|
| 20 | extern void MMU_GetTLBEntry(uint8_t index, uint32_t* tagword, uint32_t* dataword, uint8_t* pid); |
---|
| 21 | extern void MMU_SetTLBEntry(uint8_t index, uint32_t hiword, uint32_t loword, uint8_t pid); |
---|
[ff1c613] | 22 | extern void MMU_ClearTLBs(void); |
---|
[502609c8] | 23 | extern int16_t MMU_FindTLBEntry(uint32_t address); |
---|
| 24 | |
---|
| 25 | |
---|
[ff1c613] | 26 | enum { kNTLBs = 64 }; /* for 403GCX and 405 */ |
---|
[502609c8] | 27 | |
---|
[ff1c613] | 28 | static bool sFreeTLBs[kNTLBs]; |
---|
| 29 | static uint8_t sLastIndex = 0; |
---|
| 30 | static int sNInUse = 0; |
---|
[502609c8] | 31 | |
---|
| 32 | static void MMUFault(const char* what) |
---|
| 33 | /* Used for all setup faults; these can't really be ignored */ |
---|
| 34 | { |
---|
[ff1c613] | 35 | printk("\n>>>MMU fatal error %s\n",what); |
---|
| 36 | rtems_fatal_error_occurred(RTEMS_INTERNAL_ERROR); |
---|
[502609c8] | 37 | } |
---|
| 38 | |
---|
[ff1c613] | 39 | static uint8_t AllocTLB(void) |
---|
[502609c8] | 40 | { |
---|
[ff1c613] | 41 | uint8_t index; |
---|
| 42 | |
---|
| 43 | index = sLastIndex; |
---|
| 44 | do { |
---|
| 45 | index++; |
---|
| 46 | if (index == kNTLBs) |
---|
| 47 | index = 0; |
---|
| 48 | if (index == sLastIndex) |
---|
| 49 | MMUFault("TLB table full"); |
---|
| 50 | } while (! sFreeTLBs[index]); |
---|
| 51 | sFreeTLBs[index] = false; |
---|
| 52 | sLastIndex = index; |
---|
| 53 | sNInUse++; |
---|
| 54 | return index; |
---|
[502609c8] | 55 | } |
---|
| 56 | |
---|
| 57 | static void FreeTLB(uint8_t index) |
---|
| 58 | { |
---|
[ff1c613] | 59 | MMU_SetTLBEntry(index,0,0,0); |
---|
| 60 | sFreeTLBs[index] = true; |
---|
| 61 | sLastIndex = index-1; |
---|
| 62 | sNInUse--; |
---|
[502609c8] | 63 | } |
---|
| 64 | |
---|
| 65 | |
---|
| 66 | /*---------------------------- MMU operations ---------------------------------- */ |
---|
| 67 | |
---|
| 68 | int DataMissException(BSP_Exception_frame *f, unsigned int vector); |
---|
| 69 | int InstructionMissException(BSP_Exception_frame *f, unsigned int vector); |
---|
| 70 | int InstructionFetchException(BSP_Exception_frame *f, unsigned int vector); |
---|
[ff1c613] | 71 | void mmu_initialise(void); |
---|
| 72 | int mmu_get_tlb_count(void); |
---|
| 73 | uint8_t mmu_new_processID(void); |
---|
| 74 | uint8_t mmu_current_processID(void); |
---|
[502609c8] | 75 | |
---|
| 76 | void |
---|
[ff1c613] | 77 | mmu_initialise(void) |
---|
[502609c8] | 78 | /* Clear the TLBs and set up exception handlers for the MMU miss handlers */ |
---|
| 79 | { |
---|
[ff1c613] | 80 | int i; |
---|
| 81 | |
---|
| 82 | MMU_ClearTLBs(); |
---|
| 83 | for (i=0; i<kNTLBs; i++) { |
---|
| 84 | sFreeTLBs[i] = true; |
---|
| 85 | MMU_SetTLBEntry(i,0,0,0xFF); |
---|
| 86 | } |
---|
| 87 | ppc_exc_set_handler(ASM_ISI_VECTOR ,InstructionFetchException); |
---|
| 88 | ppc_exc_set_handler(ASM_BOOKE_ITLBMISS_VECTOR ,DataMissException); |
---|
| 89 | ppc_exc_set_handler(ASM_BOOKE_DTLBMISS_VECTOR ,InstructionMissException); |
---|
[502609c8] | 90 | } |
---|
| 91 | |
---|
| 92 | static void |
---|
| 93 | MakeTLBEntries(uint32_t startAt, uint32_t nBytes, bool EX, bool WR, bool I, uint8_t PID) |
---|
| 94 | { |
---|
[ff1c613] | 95 | uint32_t mask, options, tagWord, dataWord; |
---|
| 96 | uint8_t index, sizeCode, pid; |
---|
| 97 | |
---|
| 98 | if ((startAt & 0x3FF) != 0) |
---|
| 99 | MMUFault("TLB entry not on 1K boundary"); |
---|
| 100 | if ((nBytes & 0x3FF) != 0) |
---|
| 101 | MMUFault("TLB size not on 1K boundary"); |
---|
| 102 | |
---|
| 103 | options = 0; |
---|
| 104 | if (EX) options += 0x200; |
---|
| 105 | if (WR) options += 0x100; |
---|
| 106 | if (I) options += 5; |
---|
| 107 | |
---|
| 108 | #ifdef qLogTLB |
---|
| 109 | printk("TLB: make entries for $%X bytes from $%X..$%X PID %d",nBytes, startAt, startAt+nBytes-1, PID); |
---|
| 110 | if (EX) printk(" EX"); |
---|
| 111 | if (WR) printk(" WR"); |
---|
| 112 | if (I) printk(" I"); |
---|
| 113 | printk("\n"); |
---|
| 114 | #endif |
---|
| 115 | |
---|
| 116 | while (nBytes > 0) { |
---|
| 117 | /* Find the largest block we can base on this address */ |
---|
| 118 | mask = 0x3FF; |
---|
| 119 | sizeCode = 0; |
---|
| 120 | while (mask < nBytes && ((startAt & mask)==0) && sizeCode < 8) { |
---|
| 121 | mask = (mask<<2) + 3; |
---|
| 122 | sizeCode++; |
---|
| 123 | } |
---|
| 124 | mask >>= 2; |
---|
| 125 | sizeCode--; |
---|
| 126 | |
---|
| 127 | /* Make a TLB entry describing this, ZSEL=0 */ |
---|
| 128 | tagWord = startAt | (sizeCode<<7) | 0x40; |
---|
| 129 | dataWord = startAt | options; |
---|
| 130 | index = AllocTLB(); |
---|
| 131 | MMU_SetTLBEntry( index , tagWord, dataWord, PID); |
---|
| 132 | |
---|
| 133 | { |
---|
| 134 | /* Paranoia: check that we can read that back... */ |
---|
| 135 | uint8_t tdex, oldpid; |
---|
| 136 | |
---|
| 137 | oldpid = mmu_current_processID(); |
---|
| 138 | mmu_set_processID(PID); |
---|
| 139 | tdex = MMU_FindTLBEntry(startAt); |
---|
| 140 | mmu_set_processID(oldpid); |
---|
| 141 | |
---|
| 142 | if (tdex != index) { |
---|
[28593c6e] | 143 | printk(" Add TLB %d: At %" PRIx32 " for $%" PRIx32 |
---|
| 144 | " sizecode %d tagWord $%" PRIx32 " ", |
---|
| 145 | index, startAt, mask+1,sizeCode,tagWord); |
---|
[ff1c613] | 146 | printk(" -- find failed, %d/%d!\n",tdex,index); |
---|
| 147 | MMU_GetTLBEntry(index, &tagWord, &dataWord, &pid); |
---|
[28593c6e] | 148 | printk(" -- reads back $%" PRIx32 " : $%" PRIx32 |
---|
| 149 | ", PID %d\n",tagWord,dataWord,pid); |
---|
[ff1c613] | 150 | } else { |
---|
| 151 | #ifdef qLogTLBDetails |
---|
| 152 | printk(" Add TLB %d: At %X for $%X sizecode %d tagWord $%X\n",index, startAt, mask+1,sizeCode,tagWord); |
---|
| 153 | #endif |
---|
| 154 | } |
---|
| 155 | } |
---|
| 156 | |
---|
| 157 | /* Subtract block from startAddr and nBytes */ |
---|
| 158 | mask++; /* Convert to a byte count */ |
---|
| 159 | startAt += mask; |
---|
| 160 | nBytes -= mask; |
---|
| 161 | } |
---|
| 162 | #ifdef qLogTLB |
---|
| 163 | printk(" %d in use\n",sNInUse); |
---|
| 164 | #endif |
---|
[502609c8] | 165 | } |
---|
| 166 | |
---|
| 167 | void |
---|
| 168 | mmu_remove_space(uint32_t startAt, uint32_t endAt) |
---|
| 169 | { |
---|
[ff1c613] | 170 | int16_t index; |
---|
| 171 | int32_t size; |
---|
| 172 | uint32_t tagword, dataword, nBytes; |
---|
| 173 | uint8_t pid, sCode; |
---|
| 174 | |
---|
| 175 | nBytes = endAt - startAt; |
---|
| 176 | |
---|
| 177 | #ifdef qLogTLB |
---|
| 178 | printk("TLB: delete entries for $%X bytes from $%X\n",nBytes,startAt); |
---|
| 179 | #endif |
---|
| 180 | |
---|
| 181 | while (nBytes > 0) { |
---|
| 182 | index = MMU_FindTLBEntry( (uint32_t)startAt ); |
---|
| 183 | size = 1024; |
---|
| 184 | if (index >= 0) { |
---|
| 185 | MMU_GetTLBEntry(index, &tagword, &dataword, &pid); |
---|
| 186 | if ((tagword & 0x40) == 0) |
---|
| 187 | MMUFault("Undefine failed: redundant entries?"); |
---|
| 188 | if ((tagword & 0xFFFFFC00) != (uint32_t)startAt) |
---|
| 189 | MMUFault("Undefine not on TLB boundary"); |
---|
| 190 | FreeTLB(index); |
---|
| 191 | sCode = (tagword >> 7) & 7; |
---|
| 192 | while (sCode > 0) { |
---|
| 193 | size <<= 2; |
---|
| 194 | sCode--; |
---|
| 195 | } |
---|
| 196 | #ifdef qLogTLBDetails |
---|
| 197 | printk(" Free TLB %d: At %X for $%X\n",index, startAt, size); |
---|
| 198 | #endif |
---|
| 199 | } |
---|
| 200 | startAt += size; |
---|
| 201 | nBytes -= size; |
---|
| 202 | } |
---|
[502609c8] | 203 | } |
---|
| 204 | |
---|
| 205 | void |
---|
| 206 | mmu_add_space(uint32_t startAddr, uint32_t endAddr, MMUAccessType permissions, uint8_t processID) |
---|
| 207 | /* Convert accesstype to write-enable, executable, and cache-inhibit bits */ |
---|
| 208 | { |
---|
[ff1c613] | 209 | bool EX, WR, I; |
---|
| 210 | |
---|
| 211 | EX = false; |
---|
| 212 | WR = false; |
---|
| 213 | I = false; |
---|
| 214 | switch (permissions) { |
---|
| 215 | case executable : EX = true; break; |
---|
| 216 | case readOnlyData : break; |
---|
| 217 | case readOnlyNoCache : I = true; break; |
---|
| 218 | case readWriteData : WR = true; break; |
---|
| 219 | case readWriteNoCache : WR = true; I= true; break; |
---|
| 220 | case readWriteExecutable: WR = true; EX = true; break; |
---|
| 221 | } |
---|
| 222 | MakeTLBEntries( (uint32_t)startAddr, (uint32_t)(endAddr-startAddr+1), EX, WR, I, processID); |
---|
[502609c8] | 223 | } |
---|
| 224 | |
---|
| 225 | int |
---|
[ff1c613] | 226 | mmu_get_tlb_count(void) |
---|
[502609c8] | 227 | { |
---|
[ff1c613] | 228 | return sNInUse; |
---|
[502609c8] | 229 | } |
---|
| 230 | |
---|
| 231 | /*---------------------------- CPU process ID handling ---------------------------------- |
---|
| 232 | * Really dumb system where we just hand out sequential numbers and eventually fail |
---|
| 233 | * As long as we only use 8-9 processes this isn't a problem */ |
---|
| 234 | |
---|
| 235 | static uint8_t sNextPID = 1; |
---|
| 236 | |
---|
[ff1c613] | 237 | #define SPR_PID 0x3B1 |
---|
[502609c8] | 238 | |
---|
[ff1c613] | 239 | uint8_t mmu_new_processID(void) |
---|
[502609c8] | 240 | { |
---|
[ff1c613] | 241 | return sNextPID++; |
---|
[502609c8] | 242 | } |
---|
| 243 | |
---|
| 244 | void mmu_free_processID(uint8_t freeThis) |
---|
| 245 | { |
---|
| 246 | } |
---|
| 247 | |
---|
[ff1c613] | 248 | uint8_t mmu_current_processID(void) |
---|
[502609c8] | 249 | { |
---|
[ff1c613] | 250 | return PPC_SPECIAL_PURPOSE_REGISTER(SPR_PID); |
---|
[502609c8] | 251 | } |
---|
| 252 | |
---|
| 253 | uint8_t mmu_set_processID(uint8_t newID) |
---|
| 254 | { |
---|
[ff1c613] | 255 | uint8_t prev = mmu_current_processID(); |
---|
| 256 | PPC_SET_SPECIAL_PURPOSE_REGISTER(SPR_PID,newID); |
---|
| 257 | return prev; |
---|
[502609c8] | 258 | } |
---|
| 259 | |
---|
| 260 | |
---|
| 261 | /* ------------------ Fault handlers ------------------ */ |
---|
| 262 | |
---|
[ff1c613] | 263 | #define SPR_ESR 0x3D4 |
---|
| 264 | #define SPR_DEAR 0x3D5 |
---|
[502609c8] | 265 | |
---|
| 266 | enum { kESR_DST = 0x00800000 }; |
---|
| 267 | |
---|
| 268 | int DataMissException(BSP_Exception_frame *f, unsigned int vector) |
---|
| 269 | { |
---|
[ff1c613] | 270 | uint32_t addr, excSyn; |
---|
| 271 | |
---|
| 272 | addr = PPC_SPECIAL_PURPOSE_REGISTER(SPR_DEAR); |
---|
| 273 | excSyn = PPC_SPECIAL_PURPOSE_REGISTER(SPR_ESR); |
---|
[28593c6e] | 274 | if (excSyn & kESR_DST) printk("\n---Data write to $%" PRIx32 |
---|
[f2b5d672] | 275 | " attempted at $%" PRIxPTR "\n",addr,f->EXC_SRR0); |
---|
[28593c6e] | 276 | else printk("\n---Data read from $%" PRIx32 " attempted at $%" |
---|
[f2b5d672] | 277 | PRIxPTR "\n",addr,f->EXC_SRR0); |
---|
[ff1c613] | 278 | return -1; |
---|
[502609c8] | 279 | } |
---|
| 280 | |
---|
| 281 | int InstructionMissException(BSP_Exception_frame *f, unsigned int vector) |
---|
| 282 | { |
---|
[f2b5d672] | 283 | printk("\n---Instruction fetch attempted from $%" PRIxPTR ", no TLB exists\n", |
---|
[28593c6e] | 284 | f->EXC_SRR0); |
---|
[ff1c613] | 285 | return -1; |
---|
[502609c8] | 286 | } |
---|
| 287 | |
---|
| 288 | int InstructionFetchException(BSP_Exception_frame *f, unsigned int vector) |
---|
| 289 | { |
---|
[f2b5d672] | 290 | printk("\n---Instruction fetch attempted from $%" PRIxPTR |
---|
[28593c6e] | 291 | ", TLB is no-execute\n",f->EXC_SRR0); |
---|
[ff1c613] | 292 | return -1; |
---|
[502609c8] | 293 | } |
---|