source: mainline/kernel/arch/mips32/src/mm/tlb.c@ 3e200736

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 3e200736 was b2fa1204, checked in by Martin Sucha <sucha14@…>, 12 years ago

Cherrypick usage of kernel logger

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