source: mainline/kernel/arch/mips32/src/mm/tlb.c@ 8fe2c9bd

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

Do not pass badvaddr but rather the page address in TLB exceptions.

The unaligned badvaddr was causing problems in connection with the
backend_phys and our mips32 PTE format. The combination resulted in
non-aligned frame addresses in the PTE. This showed, for example, when
loading some tasks from the RAM disk: tasks crashed due to wrongly
loaded sections.

  • Property mode set to 100644
File size: 9.4 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/** @addtogroup mips32mm
30 * @{
31 */
32/** @file
33 */
34
35#include <arch/mm/tlb.h>
36#include <mm/asid.h>
37#include <mm/tlb.h>
38#include <mm/page.h>
39#include <mm/as.h>
40#include <arch/cp0.h>
41#include <panic.h>
42#include <arch.h>
43#include <synch/mutex.h>
44#include <print.h>
45#include <debug.h>
46#include <align.h>
47#include <interrupt.h>
48#include <symtab.h>
49
50#define PFN_SHIFT 12
51#define VPN_SHIFT 12
52#define ADDR2VPN(a) ((a) >> VPN_SHIFT)
53#define ADDR2VPN2(a) (ADDR2VPN((a)) >> 1)
54#define VPN2ADDR(vpn) ((vpn) << VPN_SHIFT)
55#define VPN22ADDR(vpn2) (VPN2ADDR(vpn2) << 1)
56#define PFN2ADDR(pfn) ((pfn) << PFN_SHIFT)
57
58#define BANK_SELECT_BIT(a) (((a) >> PAGE_WIDTH) & 1)
59
60
61/** Initialize TLB.
62 *
63 * Invalidate all entries and mark wired entries.
64 */
65void tlb_arch_init(void)
66{
67 int i;
68
69 cp0_pagemask_write(TLB_PAGE_MASK_16K);
70 cp0_entry_hi_write(0);
71 cp0_entry_lo0_write(0);
72 cp0_entry_lo1_write(0);
73
74 /* Clear and initialize TLB. */
75
76 for (i = 0; i < TLB_ENTRY_COUNT; i++) {
77 cp0_index_write(i);
78 tlbwi();
79 }
80
81 /*
82 * The kernel is going to make use of some wired
83 * entries (e.g. mapping kernel stacks in kseg3).
84 */
85 cp0_wired_write(TLB_WIRED);
86}
87
88/** Process TLB Refill Exception.
89 *
90 * @param istate Interrupted register context.
91 */
92void tlb_refill(istate_t *istate)
93{
94 entry_lo_t lo;
95 uintptr_t badvaddr;
96 uintptr_t page;
97 pte_t *pte;
98
99 badvaddr = cp0_badvaddr_read();
100 page = ALIGN_DOWN(badvaddr, PAGE_SIZE);
101
102 pte = page_mapping_find(AS, page, true);
103 if (pte && pte->p) {
104 /*
105 * Record access to PTE.
106 */
107 pte->a = 1;
108
109 tlb_prepare_entry_lo(&lo, pte->g, pte->p, pte->d,
110 pte->cacheable, pte->pfn);
111
112 /*
113 * New entry is to be inserted into TLB
114 */
115 if (BANK_SELECT_BIT(badvaddr) == 0) {
116 cp0_entry_lo0_write(lo.value);
117 cp0_entry_lo1_write(0);
118 } else {
119 cp0_entry_lo0_write(0);
120 cp0_entry_lo1_write(lo.value);
121 }
122 cp0_pagemask_write(TLB_PAGE_MASK_16K);
123 tlbwr();
124 return;
125 }
126
127 (void) as_page_fault(page, PF_ACCESS_READ, istate);
128}
129
130/** Process TLB Invalid Exception.
131 *
132 * @param istate Interrupted register context.
133 */
134void tlb_invalid(istate_t *istate)
135{
136 entry_lo_t lo;
137 tlb_index_t index;
138 uintptr_t badvaddr;
139 uintptr_t page;
140 pte_t *pte;
141
142 /*
143 * Locate the faulting entry in TLB.
144 */
145 tlbp();
146 index.value = cp0_index_read();
147
148#if defined(PROCESSOR_4Kc)
149 /*
150 * This can happen on a 4Kc when Status.EXL is 1 and there is a TLB miss.
151 * EXL is 1 when interrupts are disabled. The combination of a TLB miss
152 * and disabled interrupts is possible in copy_to/from_uspace().
153 */
154 if (index.p) {
155 tlb_refill(istate);
156 return;
157 }
158#endif
159
160 ASSERT(!index.p);
161
162 badvaddr = cp0_badvaddr_read();
163 page = ALIGN_DOWN(badvaddr, PAGE_SIZE);
164
165 pte = page_mapping_find(AS, page, true);
166 if (pte && pte->p) {
167 /*
168 * Read the faulting TLB entry.
169 */
170 tlbr();
171
172 /*
173 * Record access to PTE.
174 */
175 pte->a = 1;
176
177 tlb_prepare_entry_lo(&lo, pte->g, pte->p, pte->d,
178 pte->cacheable, pte->pfn);
179
180 /*
181 * The entry is to be updated in TLB.
182 */
183 if (BANK_SELECT_BIT(badvaddr) == 0)
184 cp0_entry_lo0_write(lo.value);
185 else
186 cp0_entry_lo1_write(lo.value);
187 tlbwi();
188 return;
189 }
190
191 (void) as_page_fault(page, PF_ACCESS_READ, istate);
192}
193
194/** Process TLB Modified Exception.
195 *
196 * @param istate Interrupted register context.
197 */
198void tlb_modified(istate_t *istate)
199{
200 entry_lo_t lo;
201 tlb_index_t index;
202 uintptr_t badvaddr;
203 uintptr_t page;
204 pte_t *pte;
205
206 badvaddr = cp0_badvaddr_read();
207 page = ALIGN_DOWN(badvaddr, PAGE_SIZE);
208
209 /*
210 * Locate the faulting entry in TLB.
211 */
212 tlbp();
213 index.value = cp0_index_read();
214
215 /*
216 * Emit warning if the entry is not in TLB.
217 *
218 * We do not assert on this because this could be a manifestation of
219 * an emulator bug, such as QEMU Bug #1128935:
220 * https://bugs.launchpad.net/qemu/+bug/1128935
221 */
222 if (index.p) {
223 printf("%s: TLBP failed in exception handler (badvaddr=%#"
224 PRIxn ", ASID=%d).\n", __func__, badvaddr,
225 AS ? AS->asid : -1);
226 return;
227 }
228
229 pte = page_mapping_find(AS, page, true);
230 if (pte && pte->p && pte->w) {
231 /*
232 * Read the faulting TLB entry.
233 */
234 tlbr();
235
236 /*
237 * Record access and write to PTE.
238 */
239 pte->a = 1;
240 pte->d = 1;
241
242 tlb_prepare_entry_lo(&lo, pte->g, pte->p, pte->w,
243 pte->cacheable, pte->pfn);
244
245 /*
246 * The entry is to be updated in TLB.
247 */
248 if (BANK_SELECT_BIT(badvaddr) == 0)
249 cp0_entry_lo0_write(lo.value);
250 else
251 cp0_entry_lo1_write(lo.value);
252 tlbwi();
253 return;
254 }
255
256 (void) as_page_fault(page, PF_ACCESS_WRITE, istate);
257}
258
259void
260tlb_prepare_entry_lo(entry_lo_t *lo, bool g, bool v, bool d, bool cacheable,
261 uintptr_t pfn)
262{
263 lo->value = 0;
264 lo->g = g;
265 lo->v = v;
266 lo->d = d;
267 lo->c = cacheable ? PAGE_CACHEABLE_EXC_WRITE : PAGE_UNCACHED;
268 lo->pfn = pfn;
269}
270
271void tlb_prepare_entry_hi(entry_hi_t *hi, asid_t asid, uintptr_t addr)
272{
273 hi->value = 0;
274 hi->vpn2 = ADDR2VPN2(ALIGN_DOWN(addr, PAGE_SIZE));
275 hi->asid = asid;
276}
277
278/** Print contents of TLB. */
279void tlb_print(void)
280{
281 page_mask_t mask, mask_save;
282 entry_lo_t lo0, lo0_save, lo1, lo1_save;
283 entry_hi_t hi, hi_save;
284 unsigned int i;
285
286 hi_save.value = cp0_entry_hi_read();
287 lo0_save.value = cp0_entry_lo0_read();
288 lo1_save.value = cp0_entry_lo1_read();
289 mask_save.value = cp0_pagemask_read();
290
291 printf("[nr] [asid] [vpn2 ] [mask] [gvdc] [pfn ]\n");
292
293 for (i = 0; i < TLB_ENTRY_COUNT; i++) {
294 cp0_index_write(i);
295 tlbr();
296
297 mask.value = cp0_pagemask_read();
298 hi.value = cp0_entry_hi_read();
299 lo0.value = cp0_entry_lo0_read();
300 lo1.value = cp0_entry_lo1_read();
301
302 printf("%-4u %-6u %0#10x %-#6x %1u%1u%1u%1u %0#10x\n",
303 i, hi.asid, VPN22ADDR(hi.vpn2), mask.mask,
304 lo0.g, lo0.v, lo0.d, lo0.c, PFN2ADDR(lo0.pfn));
305 printf(" %1u%1u%1u%1u %0#10x\n",
306 lo1.g, lo1.v, lo1.d, lo1.c, PFN2ADDR(lo1.pfn));
307 }
308
309 cp0_entry_hi_write(hi_save.value);
310 cp0_entry_lo0_write(lo0_save.value);
311 cp0_entry_lo1_write(lo1_save.value);
312 cp0_pagemask_write(mask_save.value);
313}
314
315/** Invalidate all not wired TLB entries. */
316void tlb_invalidate_all(void)
317{
318 entry_lo_t lo0, lo1;
319 entry_hi_t hi_save;
320 int i;
321
322 ASSERT(interrupts_disabled());
323
324 hi_save.value = cp0_entry_hi_read();
325
326 for (i = TLB_WIRED; i < TLB_ENTRY_COUNT; i++) {
327 cp0_index_write(i);
328 tlbr();
329
330 lo0.value = cp0_entry_lo0_read();
331 lo1.value = cp0_entry_lo1_read();
332
333 lo0.v = 0;
334 lo1.v = 0;
335
336 cp0_entry_lo0_write(lo0.value);
337 cp0_entry_lo1_write(lo1.value);
338
339 tlbwi();
340 }
341
342 cp0_entry_hi_write(hi_save.value);
343}
344
345/** Invalidate all TLB entries belonging to specified address space.
346 *
347 * @param asid Address space identifier.
348 */
349void tlb_invalidate_asid(asid_t asid)
350{
351 entry_lo_t lo0, lo1;
352 entry_hi_t hi, hi_save;
353 int i;
354
355 ASSERT(interrupts_disabled());
356 ASSERT(asid != ASID_INVALID);
357
358 hi_save.value = cp0_entry_hi_read();
359
360 for (i = 0; i < TLB_ENTRY_COUNT; i++) {
361 cp0_index_write(i);
362 tlbr();
363
364 hi.value = cp0_entry_hi_read();
365
366 if (hi.asid == asid) {
367 lo0.value = cp0_entry_lo0_read();
368 lo1.value = cp0_entry_lo1_read();
369
370 lo0.v = 0;
371 lo1.v = 0;
372
373 cp0_entry_lo0_write(lo0.value);
374 cp0_entry_lo1_write(lo1.value);
375
376 tlbwi();
377 }
378 }
379
380 cp0_entry_hi_write(hi_save.value);
381}
382
383/** Invalidate TLB entries for specified page range belonging to specified
384 * address space.
385 *
386 * @param asid Address space identifier.
387 * @param page First page whose TLB entry is to be invalidated.
388 * @param cnt Number of entries to invalidate.
389 */
390void tlb_invalidate_pages(asid_t asid, uintptr_t page, size_t cnt)
391{
392 unsigned int i;
393 entry_lo_t lo0, lo1;
394 entry_hi_t hi, hi_save;
395 tlb_index_t index;
396
397 ASSERT(interrupts_disabled());
398
399 if (asid == ASID_INVALID)
400 return;
401
402 hi_save.value = cp0_entry_hi_read();
403
404 for (i = 0; i < cnt + 1; i += 2) {
405 tlb_prepare_entry_hi(&hi, asid, page + i * PAGE_SIZE);
406 cp0_entry_hi_write(hi.value);
407
408 tlbp();
409 index.value = cp0_index_read();
410
411 if (!index.p) {
412 /*
413 * Entry was found, index register contains valid
414 * index.
415 */
416 tlbr();
417
418 lo0.value = cp0_entry_lo0_read();
419 lo1.value = cp0_entry_lo1_read();
420
421 lo0.v = 0;
422 lo1.v = 0;
423
424 cp0_entry_lo0_write(lo0.value);
425 cp0_entry_lo1_write(lo1.value);
426
427 tlbwi();
428 }
429 }
430
431 cp0_entry_hi_write(hi_save.value);
432}
433
434/** @}
435 */
Note: See TracBrowser for help on using the repository browser.