source: mainline/kernel/arch/mips32/src/mm/tlb.c@ 2b95d13

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

Streamline TLB miss handling.

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