source: mainline/arch/mips32/src/mm/tlb.c@ 4b2c872d

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 4b2c872d 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
RevLine 
[f761f1eb]1/*
[178ec7b]2 * Copyright (C) 2003-2004 Jakub Jermar
[f761f1eb]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>
[0970f43]30#include <arch/mm/asid.h>
[f761f1eb]31#include <mm/tlb.h>
[1084a784]32#include <mm/page.h>
33#include <mm/vm.h>
[f761f1eb]34#include <arch/cp0.h>
35#include <panic.h>
36#include <arch.h>
[ab08b42]37#include <symtab.h>
[1084a784]38#include <synch/spinlock.h>
39#include <print.h>
[cc205f1]40#include <debug.h>
[9c0a9b3]41
[1084a784]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
[38a1a84]46static pte_t *find_mapping_and_check(__address badvaddr);
[8c5e6c7]47
[cc205f1]48static void prepare_entry_lo(entry_lo_t *lo, bool g, bool v, bool d, int c, __address pfn);
[8c5e6c7]49static void prepare_entry_hi(entry_hi_t *hi, asid_t asid, __address addr);
[38a1a84]50
[1084a784]51/** Initialize TLB
52 *
53 * Initialize TLB.
54 * Invalidate all entries and mark wired entries.
55 */
[ce031f0]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++) {
[1084a784]69 cp0_index_write(i);
[ce031f0]70 tlbwi();
71 }
72
73 /*
74 * The kernel is going to make use of some wired
[1084a784]75 * entries (e.g. mapping kernel stacks in kseg3).
[ce031f0]76 */
77 cp0_wired_write(TLB_WIRED);
78}
79
[1084a784]80/** Process TLB Refill Exception
81 *
82 * Process TLB Refill Exception.
83 *
84 * @param pstate Interrupted register context.
85 */
[909c6e3]86void tlb_refill(struct exception_regdump *pstate)
[1084a784]87{
[cc205f1]88 entry_lo_t lo;
[8c5e6c7]89 entry_hi_t hi;
[1084a784]90 __address badvaddr;
91 pte_t *pte;
[fd3c9e5]92
[1084a784]93 badvaddr = cp0_badvaddr_read();
[fd3c9e5]94
[38a1a84]95 spinlock_lock(&VM->lock);
[8c5e6c7]96
[38a1a84]97 pte = find_mapping_and_check(badvaddr);
[1084a784]98 if (!pte)
99 goto fail;
[38a1a84]100
[1084a784]101 /*
[38a1a84]102 * Record access to PTE.
[1084a784]103 */
[38a1a84]104 pte->a = 1;
105
[8c5e6c7]106 prepare_entry_hi(&hi, VM->asid, badvaddr);
[a016b63]107 prepare_entry_lo(&lo, pte->lo.g, pte->lo.v, pte->lo.d, pte->lo.c, pte->lo.pfn);
[1084a784]108
109 /*
110 * New entry is to be inserted into TLB
111 */
[8c5e6c7]112 cp0_entry_hi_write(hi.value);
[1084a784]113 if ((badvaddr/PAGE_SIZE) % 2 == 0) {
[cc205f1]114 cp0_entry_lo0_write(lo.value);
[1084a784]115 cp0_entry_lo1_write(0);
116 }
117 else {
118 cp0_entry_lo0_write(0);
[cc205f1]119 cp0_entry_lo1_write(lo.value);
[1084a784]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
[38a1a84]131/** Process TLB Invalid Exception
132 *
133 * Process TLB Invalid Exception.
134 *
135 * @param pstate Interrupted register context.
136 */
[1084a784]137void tlb_invalid(struct exception_regdump *pstate)
138{
[cc205f1]139 tlb_index_t index;
[38a1a84]140 __address badvaddr;
[cc205f1]141 entry_lo_t lo;
[8c5e6c7]142 entry_hi_t hi;
[38a1a84]143 pte_t *pte;
144
145 badvaddr = cp0_badvaddr_read();
146
147 /*
148 * Locate the faulting entry in TLB.
149 */
[8c5e6c7]150 hi.value = cp0_entry_hi_read();
151 prepare_entry_hi(&hi, hi.asid, badvaddr);
152 cp0_entry_hi_write(hi.value);
[38a1a84]153 tlbp();
[cc205f1]154 index.value = cp0_index_read();
[38a1a84]155
156 spinlock_lock(&VM->lock);
157
158 /*
159 * Fail if the entry is not in TLB.
160 */
[cc205f1]161 if (index.p) {
162 printf("TLB entry not found.\n");
[38a1a84]163 goto fail;
[cc205f1]164 }
[38a1a84]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
[a016b63]180 prepare_entry_lo(&lo, pte->lo.g, pte->lo.v, pte->lo.d, pte->lo.c, pte->lo.pfn);
[38a1a84]181
182 /*
183 * The entry is to be updated in TLB.
184 */
185 if ((badvaddr/PAGE_SIZE) % 2 == 0)
[cc205f1]186 cp0_entry_lo0_write(lo.value);
[38a1a84]187 else
[cc205f1]188 cp0_entry_lo1_write(lo.value);
[38a1a84]189 tlbwi();
190
191 spinlock_unlock(&VM->lock);
192 return;
193
194fail:
195 spinlock_unlock(&VM->lock);
[1084a784]196 tlb_invalid_fail(pstate);
197}
198
[38a1a84]199/** Process TLB Modified Exception
200 *
201 * Process TLB Modified Exception.
202 *
203 * @param pstate Interrupted register context.
204 */
[1084a784]205void tlb_modified(struct exception_regdump *pstate)
206{
[cc205f1]207 tlb_index_t index;
[38a1a84]208 __address badvaddr;
[cc205f1]209 entry_lo_t lo;
[8c5e6c7]210 entry_hi_t hi;
[38a1a84]211 pte_t *pte;
212
213 badvaddr = cp0_badvaddr_read();
214
215 /*
216 * Locate the faulting entry in TLB.
217 */
[8c5e6c7]218 hi.value = cp0_entry_hi_read();
219 prepare_entry_hi(&hi, hi.asid, badvaddr);
220 cp0_entry_hi_write(hi.value);
[38a1a84]221 tlbp();
[cc205f1]222 index.value = cp0_index_read();
[38a1a84]223
224 spinlock_lock(&VM->lock);
225
226 /*
227 * Fail if the entry is not in TLB.
228 */
[cc205f1]229 if (index.p) {
230 printf("TLB entry not found.\n");
[38a1a84]231 goto fail;
[cc205f1]232 }
[38a1a84]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;
[a016b63]253 pte->lo.d = 1;
[38a1a84]254
[a016b63]255 prepare_entry_lo(&lo, pte->lo.g, pte->lo.v, pte->w, pte->lo.c, pte->lo.pfn);
[38a1a84]256
257 /*
258 * The entry is to be updated in TLB.
259 */
260 if ((badvaddr/PAGE_SIZE) % 2 == 0)
[cc205f1]261 cp0_entry_lo0_write(lo.value);
[38a1a84]262 else
[cc205f1]263 cp0_entry_lo1_write(lo.value);
[38a1a84]264 tlbwi();
265
266 spinlock_unlock(&VM->lock);
267 return;
268
269fail:
270 spinlock_unlock(&VM->lock);
[1084a784]271 tlb_modified_fail(pstate);
272}
273
274void tlb_refill_fail(struct exception_regdump *pstate)
[f761f1eb]275{
[38de8a5]276 char *symbol = "";
277 char *sym2 = "";
278
[3156582]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;
[1084a784]285 panic("%X: TLB Refill Exception at %X(%s<-%s)\n", cp0_badvaddr_read(), pstate->epc, symbol, sym2);
[f761f1eb]286}
287
[1084a784]288
289void tlb_invalid_fail(struct exception_regdump *pstate)
[f761f1eb]290{
[ab08b42]291 char *symbol = "";
292
[3156582]293 char *s = get_symtab_entry(pstate->epc);
294 if (s)
295 symbol = s;
[38a1a84]296 panic("%X: TLB Invalid Exception at %X(%s)\n", cp0_badvaddr_read(), pstate->epc, symbol);
[f761f1eb]297}
298
[1084a784]299void tlb_modified_fail(struct exception_regdump *pstate)
[ce031f0]300{
301 char *symbol = "";
302
303 char *s = get_symtab_entry(pstate->epc);
304 if (s)
305 symbol = s;
[38a1a84]306 panic("%X: TLB Modified Exception at %X(%s)\n", cp0_badvaddr_read(), pstate->epc, symbol);
[ce031f0]307}
308
[cc205f1]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)
[f761f1eb]316{
[cc205f1]317 entry_hi_t hi;
[22f7769]318 ipl_t ipl;
[cc205f1]319 int i;
[0970f43]320
[cc205f1]321 ASSERT(asid != ASID_INVALID);
322
[22f7769]323 ipl = interrupts_disable();
[0970f43]324
[cc205f1]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 }
[0970f43]338
[22f7769]339 interrupts_restore(ipl);
[f761f1eb]340}
[38a1a84]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{
[cc205f1]353 entry_hi_t hi;
[38a1a84]354 pte_t *pte;
355
[cc205f1]356 hi.value = cp0_entry_hi_read();
[38a1a84]357
358 /*
359 * Handler cannot succeed if the ASIDs don't match.
360 */
[cc205f1]361 if (hi.asid != VM->asid) {
362 printf("EntryHi.asid=%d, VM->asid=%d\n", hi.asid, VM->asid);
[38a1a84]363 return NULL;
[cc205f1]364 }
[38a1a84]365
366 /*
367 * Handler cannot succeed if badvaddr has no mapping.
368 */
369 pte = find_mapping(badvaddr, 0);
[cc205f1]370 if (!pte) {
371 printf("No such mapping.\n");
[38a1a84]372 return NULL;
[cc205f1]373 }
[38a1a84]374
375 /*
376 * Handler cannot succeed if the mapping is marked as invalid.
377 */
[a016b63]378 if (!pte->lo.v) {
[cc205f1]379 printf("Invalid mapping.\n");
[38a1a84]380 return NULL;
[cc205f1]381 }
[38a1a84]382
383 return pte;
384}
385
[cc205f1]386void prepare_entry_lo(entry_lo_t *lo, bool g, bool v, bool d, int c, __address pfn)
[38a1a84]387{
[8c5e6c7]388 lo->value = 0;
[38a1a84]389 lo->g = g;
390 lo->v = v;
391 lo->d = d;
392 lo->c = c;
393 lo->pfn = pfn;
[8c5e6c7]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;
[38a1a84]400}
Note: See TracBrowser for help on using the repository browser.