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" |
---|
12 | #include <inttypes.h> |
---|
13 | |
---|
14 | /* #define qLogTLB */ |
---|
15 | /* #define qLogTLBDetails */ |
---|
16 | |
---|
17 | |
---|
18 | /*----------------------------- TLB handling -------------------------------- */ |
---|
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); |
---|
22 | extern void MMU_ClearTLBs(void); |
---|
23 | extern int16_t MMU_FindTLBEntry(uint32_t address); |
---|
24 | |
---|
25 | |
---|
26 | enum { kNTLBs = 64 }; /* for 403GCX and 405 */ |
---|
27 | |
---|
28 | static bool sFreeTLBs[kNTLBs]; |
---|
29 | static uint8_t sLastIndex = 0; |
---|
30 | static int sNInUse = 0; |
---|
31 | |
---|
32 | static void MMUFault(const char* what) |
---|
33 | /* Used for all setup faults; these can't really be ignored */ |
---|
34 | { |
---|
35 | printk("\n>>>MMU fatal error %s\n",what); |
---|
36 | rtems_fatal_error_occurred(RTEMS_INTERNAL_ERROR); |
---|
37 | } |
---|
38 | |
---|
39 | static uint8_t AllocTLB(void) |
---|
40 | { |
---|
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; |
---|
55 | } |
---|
56 | |
---|
57 | static void FreeTLB(uint8_t index) |
---|
58 | { |
---|
59 | MMU_SetTLBEntry(index,0,0,0); |
---|
60 | sFreeTLBs[index] = true; |
---|
61 | sLastIndex = index-1; |
---|
62 | sNInUse--; |
---|
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); |
---|
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); |
---|
75 | |
---|
76 | void |
---|
77 | mmu_initialise(void) |
---|
78 | /* Clear the TLBs and set up exception handlers for the MMU miss handlers */ |
---|
79 | { |
---|
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); |
---|
90 | } |
---|
91 | |
---|
92 | static void |
---|
93 | MakeTLBEntries(uint32_t startAt, uint32_t nBytes, bool EX, bool WR, bool I, uint8_t PID) |
---|
94 | { |
---|
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) { |
---|
143 | printk(" Add TLB %d: At %" PRIx32 " for $%" PRIx32 |
---|
144 | " sizecode %d tagWord $%" PRIx32 " ", |
---|
145 | index, startAt, mask+1,sizeCode,tagWord); |
---|
146 | printk(" -- find failed, %d/%d!\n",tdex,index); |
---|
147 | MMU_GetTLBEntry(index, &tagWord, &dataWord, &pid); |
---|
148 | printk(" -- reads back $%" PRIx32 " : $%" PRIx32 |
---|
149 | ", PID %d\n",tagWord,dataWord,pid); |
---|
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 |
---|
165 | } |
---|
166 | |
---|
167 | void |
---|
168 | mmu_remove_space(uint32_t startAt, uint32_t endAt) |
---|
169 | { |
---|
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 | } |
---|
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 | { |
---|
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); |
---|
223 | } |
---|
224 | |
---|
225 | int |
---|
226 | mmu_get_tlb_count(void) |
---|
227 | { |
---|
228 | return sNInUse; |
---|
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 | |
---|
237 | #define SPR_PID 0x3B1 |
---|
238 | |
---|
239 | uint8_t mmu_new_processID(void) |
---|
240 | { |
---|
241 | return sNextPID++; |
---|
242 | } |
---|
243 | |
---|
244 | void mmu_free_processID(uint8_t freeThis) |
---|
245 | { |
---|
246 | } |
---|
247 | |
---|
248 | uint8_t mmu_current_processID(void) |
---|
249 | { |
---|
250 | return PPC_SPECIAL_PURPOSE_REGISTER(SPR_PID); |
---|
251 | } |
---|
252 | |
---|
253 | uint8_t mmu_set_processID(uint8_t newID) |
---|
254 | { |
---|
255 | uint8_t prev = mmu_current_processID(); |
---|
256 | PPC_SET_SPECIAL_PURPOSE_REGISTER(SPR_PID,newID); |
---|
257 | return prev; |
---|
258 | } |
---|
259 | |
---|
260 | |
---|
261 | /* ------------------ Fault handlers ------------------ */ |
---|
262 | |
---|
263 | #define SPR_ESR 0x3D4 |
---|
264 | #define SPR_DEAR 0x3D5 |
---|
265 | |
---|
266 | enum { kESR_DST = 0x00800000 }; |
---|
267 | |
---|
268 | int DataMissException(BSP_Exception_frame *f, unsigned int vector) |
---|
269 | { |
---|
270 | uint32_t addr, excSyn; |
---|
271 | |
---|
272 | addr = PPC_SPECIAL_PURPOSE_REGISTER(SPR_DEAR); |
---|
273 | excSyn = PPC_SPECIAL_PURPOSE_REGISTER(SPR_ESR); |
---|
274 | if (excSyn & kESR_DST) printk("\n---Data write to $%" PRIx32 |
---|
275 | " attempted at $%" PRIxPTR "\n",addr,f->EXC_SRR0); |
---|
276 | else printk("\n---Data read from $%" PRIx32 " attempted at $%" |
---|
277 | PRIxPTR "\n",addr,f->EXC_SRR0); |
---|
278 | return -1; |
---|
279 | } |
---|
280 | |
---|
281 | int InstructionMissException(BSP_Exception_frame *f, unsigned int vector) |
---|
282 | { |
---|
283 | printk("\n---Instruction fetch attempted from $%" PRIxPTR ", no TLB exists\n", |
---|
284 | f->EXC_SRR0); |
---|
285 | return -1; |
---|
286 | } |
---|
287 | |
---|
288 | int InstructionFetchException(BSP_Exception_frame *f, unsigned int vector) |
---|
289 | { |
---|
290 | printk("\n---Instruction fetch attempted from $%" PRIxPTR |
---|
291 | ", TLB is no-execute\n",f->EXC_SRR0); |
---|
292 | return -1; |
---|
293 | } |
---|