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

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

new physical memory allocator supporting physical address constrains
the buddy allocator framework is retired and replaced by a two-level bitmap
the allocator can allocate an arbitrary number of frames, not only a power-of-two count

Caution: Change of semantics
The physical memory allocator no longer allocates naturally aligned blocks. If you require an aligned block, specify it as the constraint.

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