source: mainline/kernel/arch/mips32/src/mm/tlb.c@ 89f61be

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

Fix conversion from pfn to address in tlb_print().

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