source: mainline/arch/mips32/src/mm/tlb.c@ 22f7769

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 22f7769 was 22f7769, checked in by Jakub Jermar <jakub@…>, 20 years ago

Rename cpu_priority_{high|low|restore|read} functions to interrupts_{disable|enable|restore|read}.
Rename pri_t to ipl_t (Interrupt Priority Level).
Rename thread_t::pri to thread_t::priority.

  • Property mode set to 100644
File size: 8.3 KB
Line 
1/*
2 * Copyright (C) 2003-2004 Jakub Jermar
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 *
9 * - Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * - Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * - The name of the author may not be used to endorse or promote products
15 * derived from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29#include <arch/mm/tlb.h>
30#include <arch/mm/asid.h>
31#include <mm/tlb.h>
32#include <mm/page.h>
33#include <mm/vm.h>
34#include <arch/cp0.h>
35#include <panic.h>
36#include <arch.h>
37#include <symtab.h>
38#include <synch/spinlock.h>
39#include <print.h>
40#include <debug.h>
41
42static void tlb_refill_fail(struct exception_regdump *pstate);
43static void tlb_invalid_fail(struct exception_regdump *pstate);
44static void tlb_modified_fail(struct exception_regdump *pstate);
45
46static pte_t *find_mapping_and_check(__address badvaddr);
47
48static void prepare_entry_lo(entry_lo_t *lo, bool g, bool v, bool d, int c, __address pfn);
49static void prepare_entry_hi(entry_hi_t *hi, asid_t asid, __address addr);
50
51/** Initialize TLB
52 *
53 * Initialize TLB.
54 * Invalidate all entries and mark wired entries.
55 */
56void tlb_init_arch(void)
57{
58 int i;
59
60 cp0_pagemask_write(TLB_PAGE_MASK_16K);
61 cp0_entry_hi_write(0);
62 cp0_entry_lo0_write(0);
63 cp0_entry_lo1_write(0);
64
65 /*
66 * Invalidate all entries.
67 */
68 for (i = 0; i < TLB_SIZE; i++) {
69 cp0_index_write(i);
70 tlbwi();
71 }
72
73 /*
74 * The kernel is going to make use of some wired
75 * entries (e.g. mapping kernel stacks in kseg3).
76 */
77 cp0_wired_write(TLB_WIRED);
78}
79
80/** Process TLB Refill Exception
81 *
82 * Process TLB Refill Exception.
83 *
84 * @param pstate Interrupted register context.
85 */
86void tlb_refill(struct exception_regdump *pstate)
87{
88 entry_lo_t lo;
89 entry_hi_t hi;
90 __address badvaddr;
91 pte_t *pte;
92
93 badvaddr = cp0_badvaddr_read();
94
95 spinlock_lock(&VM->lock);
96
97 pte = find_mapping_and_check(badvaddr);
98 if (!pte)
99 goto fail;
100
101 /*
102 * Record access to PTE.
103 */
104 pte->a = 1;
105
106 prepare_entry_hi(&hi, VM->asid, badvaddr);
107 prepare_entry_lo(&lo, pte->lo.g, pte->lo.v, pte->lo.d, pte->lo.c, pte->lo.pfn);
108
109 /*
110 * New entry is to be inserted into TLB
111 */
112 cp0_entry_hi_write(hi.value);
113 if ((badvaddr/PAGE_SIZE) % 2 == 0) {
114 cp0_entry_lo0_write(lo.value);
115 cp0_entry_lo1_write(0);
116 }
117 else {
118 cp0_entry_lo0_write(0);
119 cp0_entry_lo1_write(lo.value);
120 }
121 tlbwr();
122
123 spinlock_unlock(&VM->lock);
124 return;
125
126fail:
127 spinlock_unlock(&VM->lock);
128 tlb_refill_fail(pstate);
129}
130
131/** Process TLB Invalid Exception
132 *
133 * Process TLB Invalid Exception.
134 *
135 * @param pstate Interrupted register context.
136 */
137void tlb_invalid(struct exception_regdump *pstate)
138{
139 tlb_index_t index;
140 __address badvaddr;
141 entry_lo_t lo;
142 entry_hi_t hi;
143 pte_t *pte;
144
145 badvaddr = cp0_badvaddr_read();
146
147 /*
148 * Locate the faulting entry in TLB.
149 */
150 hi.value = cp0_entry_hi_read();
151 prepare_entry_hi(&hi, hi.asid, badvaddr);
152 cp0_entry_hi_write(hi.value);
153 tlbp();
154 index.value = cp0_index_read();
155
156 spinlock_lock(&VM->lock);
157
158 /*
159 * Fail if the entry is not in TLB.
160 */
161 if (index.p) {
162 printf("TLB entry not found.\n");
163 goto fail;
164 }
165
166 pte = find_mapping_and_check(badvaddr);
167 if (!pte)
168 goto fail;
169
170 /*
171 * Read the faulting TLB entry.
172 */
173 tlbr();
174
175 /*
176 * Record access to PTE.
177 */
178 pte->a = 1;
179
180 prepare_entry_lo(&lo, pte->lo.g, pte->lo.v, pte->lo.d, pte->lo.c, pte->lo.pfn);
181
182 /*
183 * The entry is to be updated in TLB.
184 */
185 if ((badvaddr/PAGE_SIZE) % 2 == 0)
186 cp0_entry_lo0_write(lo.value);
187 else
188 cp0_entry_lo1_write(lo.value);
189 tlbwi();
190
191 spinlock_unlock(&VM->lock);
192 return;
193
194fail:
195 spinlock_unlock(&VM->lock);
196 tlb_invalid_fail(pstate);
197}
198
199/** Process TLB Modified Exception
200 *
201 * Process TLB Modified Exception.
202 *
203 * @param pstate Interrupted register context.
204 */
205void tlb_modified(struct exception_regdump *pstate)
206{
207 tlb_index_t index;
208 __address badvaddr;
209 entry_lo_t lo;
210 entry_hi_t hi;
211 pte_t *pte;
212
213 badvaddr = cp0_badvaddr_read();
214
215 /*
216 * Locate the faulting entry in TLB.
217 */
218 hi.value = cp0_entry_hi_read();
219 prepare_entry_hi(&hi, hi.asid, badvaddr);
220 cp0_entry_hi_write(hi.value);
221 tlbp();
222 index.value = cp0_index_read();
223
224 spinlock_lock(&VM->lock);
225
226 /*
227 * Fail if the entry is not in TLB.
228 */
229 if (index.p) {
230 printf("TLB entry not found.\n");
231 goto fail;
232 }
233
234 pte = find_mapping_and_check(badvaddr);
235 if (!pte)
236 goto fail;
237
238 /*
239 * Fail if the page is not writable.
240 */
241 if (!pte->w)
242 goto fail;
243
244 /*
245 * Read the faulting TLB entry.
246 */
247 tlbr();
248
249 /*
250 * Record access and write to PTE.
251 */
252 pte->a = 1;
253 pte->lo.d = 1;
254
255 prepare_entry_lo(&lo, pte->lo.g, pte->lo.v, pte->w, pte->lo.c, pte->lo.pfn);
256
257 /*
258 * The entry is to be updated in TLB.
259 */
260 if ((badvaddr/PAGE_SIZE) % 2 == 0)
261 cp0_entry_lo0_write(lo.value);
262 else
263 cp0_entry_lo1_write(lo.value);
264 tlbwi();
265
266 spinlock_unlock(&VM->lock);
267 return;
268
269fail:
270 spinlock_unlock(&VM->lock);
271 tlb_modified_fail(pstate);
272}
273
274void tlb_refill_fail(struct exception_regdump *pstate)
275{
276 char *symbol = "";
277 char *sym2 = "";
278
279 char *s = get_symtab_entry(pstate->epc);
280 if (s)
281 symbol = s;
282 s = get_symtab_entry(pstate->ra);
283 if (s)
284 sym2 = s;
285 panic("%X: TLB Refill Exception at %X(%s<-%s)\n", cp0_badvaddr_read(), pstate->epc, symbol, sym2);
286}
287
288
289void tlb_invalid_fail(struct exception_regdump *pstate)
290{
291 char *symbol = "";
292
293 char *s = get_symtab_entry(pstate->epc);
294 if (s)
295 symbol = s;
296 panic("%X: TLB Invalid Exception at %X(%s)\n", cp0_badvaddr_read(), pstate->epc, symbol);
297}
298
299void tlb_modified_fail(struct exception_regdump *pstate)
300{
301 char *symbol = "";
302
303 char *s = get_symtab_entry(pstate->epc);
304 if (s)
305 symbol = s;
306 panic("%X: TLB Modified Exception at %X(%s)\n", cp0_badvaddr_read(), pstate->epc, symbol);
307}
308
309/** Invalidate TLB entries with specified ASID
310 *
311 * Invalidate TLB entries with specified ASID.
312 *
313 * @param asid ASID.
314 */
315void tlb_invalidate(asid_t asid)
316{
317 entry_hi_t hi;
318 ipl_t ipl;
319 int i;
320
321 ASSERT(asid != ASID_INVALID);
322
323 ipl = interrupts_disable();
324
325 for (i = 0; i < TLB_SIZE; i++) {
326 cp0_index_write(i);
327 tlbr();
328
329 hi.value = cp0_entry_hi_read();
330 if (hi.asid == asid) {
331 cp0_pagemask_write(TLB_PAGE_MASK_16K);
332 cp0_entry_hi_write(0);
333 cp0_entry_lo0_write(0);
334 cp0_entry_lo1_write(0);
335 tlbwi();
336 }
337 }
338
339 interrupts_restore(ipl);
340}
341
342/** Try to find PTE for faulting address
343 *
344 * Try to find PTE for faulting address.
345 * The VM->lock must be held on entry to this function.
346 *
347 * @param badvaddr Faulting virtual address.
348 *
349 * @return PTE on success, NULL otherwise.
350 */
351pte_t *find_mapping_and_check(__address badvaddr)
352{
353 entry_hi_t hi;
354 pte_t *pte;
355
356 hi.value = cp0_entry_hi_read();
357
358 /*
359 * Handler cannot succeed if the ASIDs don't match.
360 */
361 if (hi.asid != VM->asid) {
362 printf("EntryHi.asid=%d, VM->asid=%d\n", hi.asid, VM->asid);
363 return NULL;
364 }
365
366 /*
367 * Handler cannot succeed if badvaddr has no mapping.
368 */
369 pte = find_mapping(badvaddr, 0);
370 if (!pte) {
371 printf("No such mapping.\n");
372 return NULL;
373 }
374
375 /*
376 * Handler cannot succeed if the mapping is marked as invalid.
377 */
378 if (!pte->lo.v) {
379 printf("Invalid mapping.\n");
380 return NULL;
381 }
382
383 return pte;
384}
385
386void prepare_entry_lo(entry_lo_t *lo, bool g, bool v, bool d, int c, __address pfn)
387{
388 lo->value = 0;
389 lo->g = g;
390 lo->v = v;
391 lo->d = d;
392 lo->c = c;
393 lo->pfn = pfn;
394}
395
396void prepare_entry_hi(entry_hi_t *hi, asid_t asid, __address addr)
397{
398 hi->value = (((addr/PAGE_SIZE)/2)*PAGE_SIZE*2);
399 hi->asid = asid;
400}
Note: See TracBrowser for help on using the repository browser.