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
RevLine 
[f761f1eb]1/*
[df4ed85]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
[7f341820]29/** @addtogroup mips32mm
[b45c443]30 * @{
31 */
32/** @file
33 */
34
[f761f1eb]35#include <arch/mm/tlb.h>
[4512d7e]36#include <mm/asid.h>
[f761f1eb]37#include <mm/tlb.h>
[1084a784]38#include <mm/page.h>
[20d50a1]39#include <mm/as.h>
[f761f1eb]40#include <arch/cp0.h>
41#include <panic.h>
42#include <arch.h>
[7f341820]43#include <synch/mutex.h>
[1084a784]44#include <print.h>
[cc205f1]45#include <debug.h>
[2d01bbd]46#include <align.h>
[874621f]47#include <interrupt.h>
[e2b762ec]48#include <symtab.h>
49
[89f61be]50#define PFN_SHIFT 12
51#define VPN_SHIFT 12
[e05b956]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)
[89f61be]56#define PFN2ADDR(pfn) ((pfn) << PFN_SHIFT)
[e05b956]57
58#define BANK_SELECT_BIT(a) (((a) >> PAGE_WIDTH) & 1)
59
60
[91befde0]61/** Initialize TLB.
[1084a784]62 *
63 * Invalidate all entries and mark wired entries.
64 */
[b00fdde]65void tlb_arch_init(void)
[ce031f0]66{
[dd14cced]67 int i;
68
[ce031f0]69 cp0_pagemask_write(TLB_PAGE_MASK_16K);
[dd14cced]70 cp0_entry_hi_write(0);
71 cp0_entry_lo0_write(0);
72 cp0_entry_lo1_write(0);
[ce031f0]73
[dd14cced]74 /* Clear and initialize TLB. */
75
76 for (i = 0; i < TLB_ENTRY_COUNT; i++) {
77 cp0_index_write(i);
78 tlbwi();
79 }
[54a7a20]80
[ce031f0]81 /*
82 * The kernel is going to make use of some wired
[1084a784]83 * entries (e.g. mapping kernel stacks in kseg3).
[ce031f0]84 */
85 cp0_wired_write(TLB_WIRED);
86}
87
[91befde0]88/** Process TLB Refill Exception.
[1084a784]89 *
[91befde0]90 * @param istate Interrupted register context.
[1084a784]91 */
[25d7709]92void tlb_refill(istate_t *istate)
[1084a784]93{
[e05b956]94 entry_lo_t lo;
[7f1c620]95 uintptr_t badvaddr;
[1084a784]96 pte_t *pte;
[7f341820]97
[1084a784]98 badvaddr = cp0_badvaddr_read();
[e05b956]99
[976c434]100 pte = page_mapping_find(AS, badvaddr, true);
101 if (pte && pte->p) {
[1dbc43f]102 /*
103 * Record access to PTE.
104 */
105 pte->a = 1;
[38a1a84]106
[1dbc43f]107 tlb_prepare_entry_lo(&lo, pte->g, pte->p, pte->d,
108 pte->cacheable, pte->pfn);
[1084a784]109
[1dbc43f]110 /*
111 * New entry is to be inserted into TLB
112 */
[e05b956]113 if (BANK_SELECT_BIT(badvaddr) == 0) {
[1dbc43f]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();
[976c434]122 return;
[1084a784]123 }
[976c434]124
125 (void) as_page_fault(badvaddr, PF_ACCESS_READ, istate);
[1084a784]126}
127
[91befde0]128/** Process TLB Invalid Exception.
[38a1a84]129 *
[91befde0]130 * @param istate Interrupted register context.
[38a1a84]131 */
[25d7709]132void tlb_invalid(istate_t *istate)
[1084a784]133{
[e05b956]134 entry_lo_t lo;
[cc205f1]135 tlb_index_t index;
[7f1c620]136 uintptr_t badvaddr;
[38a1a84]137 pte_t *pte;
138
139 /*
140 * Locate the faulting entry in TLB.
141 */
142 tlbp();
[cc205f1]143 index.value = cp0_index_read();
[2299914]144
[cf538e7]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
[1dbc43f]157 ASSERT(!index.p);
[38a1a84]158
[e05b956]159 badvaddr = cp0_badvaddr_read();
160
[976c434]161 pte = page_mapping_find(AS, badvaddr, true);
162 if (pte && pte->p) {
[1dbc43f]163 /*
164 * Read the faulting TLB entry.
165 */
166 tlbr();
[38a1a84]167
[1dbc43f]168 /*
169 * Record access to PTE.
170 */
171 pte->a = 1;
[38a1a84]172
[1dbc43f]173 tlb_prepare_entry_lo(&lo, pte->g, pte->p, pte->d,
174 pte->cacheable, pte->pfn);
[38a1a84]175
[1dbc43f]176 /*
177 * The entry is to be updated in TLB.
178 */
[e05b956]179 if (BANK_SELECT_BIT(badvaddr) == 0)
[1dbc43f]180 cp0_entry_lo0_write(lo.value);
181 else
182 cp0_entry_lo1_write(lo.value);
183 tlbwi();
[976c434]184 return;
[1dbc43f]185 }
[976c434]186
187 (void) as_page_fault(badvaddr, PF_ACCESS_READ, istate);
[1084a784]188}
189
[91befde0]190/** Process TLB Modified Exception.
[38a1a84]191 *
[91befde0]192 * @param istate Interrupted register context.
[38a1a84]193 */
[25d7709]194void tlb_modified(istate_t *istate)
[1084a784]195{
[e05b956]196 entry_lo_t lo;
[cc205f1]197 tlb_index_t index;
[7f1c620]198 uintptr_t badvaddr;
[38a1a84]199 pte_t *pte;
200
[ddfd158]201 badvaddr = cp0_badvaddr_read();
202
[38a1a84]203 /*
204 * Locate the faulting entry in TLB.
205 */
206 tlbp();
[cc205f1]207 index.value = cp0_index_read();
[2299914]208
[38a1a84]209 /*
[ddfd158]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
[38a1a84]215 */
[ddfd158]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 }
[e05b956]222
[976c434]223 pte = page_mapping_find(AS, badvaddr, true);
224 if (pte && pte->p && pte->w) {
[1dbc43f]225 /*
226 * Read the faulting TLB entry.
227 */
228 tlbr();
[f761f1eb]229
[1dbc43f]230 /*
231 * Record access and write to PTE.
232 */
233 pte->a = 1;
234 pte->d = 1;
[1084a784]235
[1dbc43f]236 tlb_prepare_entry_lo(&lo, pte->g, pte->p, pte->w,
237 pte->cacheable, pte->pfn);
[f761f1eb]238
[1dbc43f]239 /*
240 * The entry is to be updated in TLB.
241 */
[e05b956]242 if (BANK_SELECT_BIT(badvaddr) == 0)
[1dbc43f]243 cp0_entry_lo0_write(lo.value);
244 else
245 cp0_entry_lo1_write(lo.value);
246 tlbwi();
[976c434]247 return;
[1dbc43f]248 }
249
[976c434]250 (void) as_page_fault(badvaddr, PF_ACCESS_WRITE, istate);
[38a1a84]251}
252
[91befde0]253void
254tlb_prepare_entry_lo(entry_lo_t *lo, bool g, bool v, bool d, bool cacheable,
255 uintptr_t pfn)
[38a1a84]256{
[8c5e6c7]257 lo->value = 0;
[38a1a84]258 lo->g = g;
259 lo->v = v;
260 lo->d = d;
[0882a9a]261 lo->c = cacheable ? PAGE_CACHEABLE_EXC_WRITE : PAGE_UNCACHED;
[38a1a84]262 lo->pfn = pfn;
[8c5e6c7]263}
264
[edebc15c]265void tlb_prepare_entry_hi(entry_hi_t *hi, asid_t asid, uintptr_t addr)
[8c5e6c7]266{
[e05b956]267 hi->value = 0;
268 hi->vpn2 = ADDR2VPN2(ALIGN_DOWN(addr, PAGE_SIZE));
[8c5e6c7]269 hi->asid = asid;
[38a1a84]270}
[b00fdde]271
[02055415]272/** Print contents of TLB. */
[b00fdde]273void tlb_print(void)
274{
[e05b956]275 page_mask_t mask, mask_save;
276 entry_lo_t lo0, lo0_save, lo1, lo1_save;
[f9425006]277 entry_hi_t hi, hi_save;
[a0f6a61]278 unsigned int i;
[02055415]279
[f9425006]280 hi_save.value = cp0_entry_hi_read();
[e05b956]281 lo0_save.value = cp0_entry_lo0_read();
282 lo1_save.value = cp0_entry_lo1_read();
283 mask_save.value = cp0_pagemask_read();
[a0f6a61]284
[e05b956]285 printf("[nr] [asid] [vpn2 ] [mask] [gvdc] [pfn ]\n");
[a0f6a61]286
[02055415]287 for (i = 0; i < TLB_ENTRY_COUNT; i++) {
288 cp0_index_write(i);
289 tlbr();
290
[0bd4f56d]291 mask.value = cp0_pagemask_read();
[02055415]292 hi.value = cp0_entry_hi_read();
293 lo0.value = cp0_entry_lo0_read();
294 lo1.value = cp0_entry_lo1_read();
295
[e05b956]296 printf("%-4u %-6u %0#10x %-#6x %1u%1u%1u%1u %0#10x\n",
297 i, hi.asid, VPN22ADDR(hi.vpn2), mask.mask,
[89f61be]298 lo0.g, lo0.v, lo0.d, lo0.c, PFN2ADDR(lo0.pfn));
[e05b956]299 printf(" %1u%1u%1u%1u %0#10x\n",
[89f61be]300 lo1.g, lo1.v, lo1.d, lo1.c, PFN2ADDR(lo1.pfn));
[02055415]301 }
[f9425006]302
303 cp0_entry_hi_write(hi_save.value);
[e05b956]304 cp0_entry_lo0_write(lo0_save.value);
305 cp0_entry_lo1_write(lo1_save.value);
306 cp0_pagemask_write(mask_save.value);
[b00fdde]307}
[a98d2ec]308
[8ad925c]309/** Invalidate all not wired TLB entries. */
[a98d2ec]310void tlb_invalidate_all(void)
311{
[dd14cced]312 entry_lo_t lo0, lo1;
[f9425006]313 entry_hi_t hi_save;
[a98d2ec]314 int i;
315
[e05b956]316 ASSERT(interrupts_disabled());
317
[f9425006]318 hi_save.value = cp0_entry_hi_read();
[a98d2ec]319
[8ad925c]320 for (i = TLB_WIRED; i < TLB_ENTRY_COUNT; i++) {
[a98d2ec]321 cp0_index_write(i);
[dd14cced]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
[a98d2ec]333 tlbwi();
334 }
[dd14cced]335
[f9425006]336 cp0_entry_hi_write(hi_save.value);
[a98d2ec]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{
[dd14cced]345 entry_lo_t lo0, lo1;
[f9425006]346 entry_hi_t hi, hi_save;
[a98d2ec]347 int i;
348
[e05b956]349 ASSERT(interrupts_disabled());
[dd14cced]350 ASSERT(asid != ASID_INVALID);
351
[f9425006]352 hi_save.value = cp0_entry_hi_read();
[dd14cced]353
[a98d2ec]354 for (i = 0; i < TLB_ENTRY_COUNT; i++) {
355 cp0_index_write(i);
356 tlbr();
357
[dd14cced]358 hi.value = cp0_entry_hi_read();
359
[a98d2ec]360 if (hi.asid == asid) {
[dd14cced]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
[a98d2ec]370 tlbwi();
371 }
372 }
[dd14cced]373
[f9425006]374 cp0_entry_hi_write(hi_save.value);
[a98d2ec]375}
376
[91befde0]377/** Invalidate TLB entries for specified page range belonging to specified
378 * address space.
[a98d2ec]379 *
[91befde0]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.
[a98d2ec]383 */
[98000fb]384void tlb_invalidate_pages(asid_t asid, uintptr_t page, size_t cnt)
[a98d2ec]385{
[6c441cf8]386 unsigned int i;
[dd14cced]387 entry_lo_t lo0, lo1;
[f9425006]388 entry_hi_t hi, hi_save;
[a98d2ec]389 tlb_index_t index;
[e05b956]390
391 ASSERT(interrupts_disabled());
[bd81386]392
393 if (asid == ASID_INVALID)
394 return;
[dd14cced]395
[f9425006]396 hi_save.value = cp0_entry_hi_read();
[a98d2ec]397
[6c441cf8]398 for (i = 0; i < cnt + 1; i += 2) {
[edebc15c]399 tlb_prepare_entry_hi(&hi, asid, page + i * PAGE_SIZE);
[4512d7e]400 cp0_entry_hi_write(hi.value);
[dd14cced]401
[4512d7e]402 tlbp();
403 index.value = cp0_index_read();
[a98d2ec]404
[4512d7e]405 if (!index.p) {
[91befde0]406 /*
407 * Entry was found, index register contains valid
408 * index.
409 */
[4512d7e]410 tlbr();
[dd14cced]411
[4512d7e]412 lo0.value = cp0_entry_lo0_read();
413 lo1.value = cp0_entry_lo1_read();
[dd14cced]414
[4512d7e]415 lo0.v = 0;
416 lo1.v = 0;
[dd14cced]417
[4512d7e]418 cp0_entry_lo0_write(lo0.value);
419 cp0_entry_lo1_write(lo1.value);
[dd14cced]420
[4512d7e]421 tlbwi();
422 }
[a98d2ec]423 }
[dd14cced]424
[f9425006]425 cp0_entry_hi_write(hi_save.value);
[a98d2ec]426}
[b45c443]427
[a6dd361]428/** @}
[b45c443]429 */
Note: See TracBrowser for help on using the repository browser.