Subversion Repositories HelenOS

Rev

Go to most recent revision | Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
1 jermar 1
/*
319 jermar 2
 * Copyright (C) 2003-2004 Jakub Jermar
1 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
#include <arch/mm/tlb.h>
130 decky 30
#include <arch/mm/asid.h>
1 jermar 31
#include <mm/tlb.h>
391 jermar 32
#include <mm/page.h>
33
#include <mm/vm.h>
1 jermar 34
#include <arch/cp0.h>
35
#include <panic.h>
36
#include <arch.h>
268 palkovsky 37
#include <symtab.h>
391 jermar 38
#include <synch/spinlock.h>
39
#include <print.h>
396 jermar 40
#include <debug.h>
268 palkovsky 41
 
391 jermar 42
static void tlb_refill_fail(struct exception_regdump *pstate);
43
static void tlb_invalid_fail(struct exception_regdump *pstate);
44
static void tlb_modified_fail(struct exception_regdump *pstate);
45
 
394 jermar 46
static pte_t *find_mapping_and_check(__address badvaddr);
399 jermar 47
 
396 jermar 48
static void prepare_entry_lo(entry_lo_t *lo, bool g, bool v, bool d, int c, __address pfn);
399 jermar 49
static void prepare_entry_hi(entry_hi_t *hi, asid_t asid, __address addr);
394 jermar 50
 
391 jermar 51
/** Initialize TLB
52
 *
53
 * Initialize TLB.
54
 * Invalidate all entries and mark wired entries.
55
 */
569 jermar 56
void tlb_arch_init(void)
389 jermar 57
{
599 jermar 58
	int i;
59
 
389 jermar 60
	cp0_pagemask_write(TLB_PAGE_MASK_16K);
599 jermar 61
	cp0_entry_hi_write(0);
62
	cp0_entry_lo0_write(0);
63
	cp0_entry_lo1_write(0);
389 jermar 64
 
599 jermar 65
	/* Clear and initialize TLB. */
66
 
67
	for (i = 0; i < TLB_ENTRY_COUNT; i++) {
68
		cp0_index_write(i);
69
		tlbwi();
70
	}
598 jermar 71
 
389 jermar 72
	/*
73
	 * The kernel is going to make use of some wired
391 jermar 74
	 * entries (e.g. mapping kernel stacks in kseg3).
389 jermar 75
	 */
76
	cp0_wired_write(TLB_WIRED);
77
}
78
 
391 jermar 79
/** Process TLB Refill Exception
80
 *
81
 * Process TLB Refill Exception.
82
 *
83
 * @param pstate Interrupted register context.
84
 */
317 palkovsky 85
void tlb_refill(struct exception_regdump *pstate)
1 jermar 86
{
396 jermar 87
	entry_lo_t lo;
399 jermar 88
	entry_hi_t hi;	
391 jermar 89
	__address badvaddr;
90
	pte_t *pte;
397 jermar 91
 
391 jermar 92
	badvaddr = cp0_badvaddr_read();
397 jermar 93
 
394 jermar 94
	spinlock_lock(&VM->lock);		
399 jermar 95
 
394 jermar 96
	pte = find_mapping_and_check(badvaddr);
97
	if (!pte)
391 jermar 98
		goto fail;
99
 
100
	/*
394 jermar 101
	 * Record access to PTE.
391 jermar 102
	 */
394 jermar 103
	pte->a = 1;
391 jermar 104
 
399 jermar 105
	prepare_entry_hi(&hi, VM->asid, badvaddr);
403 jermar 106
	prepare_entry_lo(&lo, pte->lo.g, pte->lo.v, pte->lo.d, pte->lo.c, pte->lo.pfn);
394 jermar 107
 
391 jermar 108
	/*
109
	 * New entry is to be inserted into TLB
110
	 */
399 jermar 111
	cp0_entry_hi_write(hi.value);
391 jermar 112
	if ((badvaddr/PAGE_SIZE) % 2 == 0) {
396 jermar 113
		cp0_entry_lo0_write(lo.value);
391 jermar 114
		cp0_entry_lo1_write(0);
115
	}
116
	else {
117
		cp0_entry_lo0_write(0);
396 jermar 118
		cp0_entry_lo1_write(lo.value);
391 jermar 119
	}
120
	tlbwr();
121
 
122
	spinlock_unlock(&VM->lock);
123
	return;
124
 
125
fail:
126
	spinlock_unlock(&VM->lock);
127
	tlb_refill_fail(pstate);
128
}
129
 
394 jermar 130
/** Process TLB Invalid Exception
131
 *
132
 * Process TLB Invalid Exception.
133
 *
134
 * @param pstate Interrupted register context.
135
 */
391 jermar 136
void tlb_invalid(struct exception_regdump *pstate)
137
{
396 jermar 138
	tlb_index_t index;
394 jermar 139
	__address badvaddr;
396 jermar 140
	entry_lo_t lo;
399 jermar 141
	entry_hi_t hi;
394 jermar 142
	pte_t *pte;
143
 
144
	badvaddr = cp0_badvaddr_read();
145
 
146
	/*
147
	 * Locate the faulting entry in TLB.
148
	 */
399 jermar 149
	hi.value = cp0_entry_hi_read();
150
	prepare_entry_hi(&hi, hi.asid, badvaddr);
151
	cp0_entry_hi_write(hi.value);
394 jermar 152
	tlbp();
396 jermar 153
	index.value = cp0_index_read();
394 jermar 154
 
155
	spinlock_lock(&VM->lock);	
156
 
157
	/*
158
	 * Fail if the entry is not in TLB.
159
	 */
396 jermar 160
	if (index.p) {
161
		printf("TLB entry not found.\n");
394 jermar 162
		goto fail;
396 jermar 163
	}
394 jermar 164
 
165
	pte = find_mapping_and_check(badvaddr);
166
	if (!pte)
167
		goto fail;
168
 
169
	/*
170
	 * Read the faulting TLB entry.
171
	 */
172
	tlbr();
173
 
174
	/*
175
	 * Record access to PTE.
176
	 */
177
	pte->a = 1;
178
 
403 jermar 179
	prepare_entry_lo(&lo, pte->lo.g, pte->lo.v, pte->lo.d, pte->lo.c, pte->lo.pfn);
394 jermar 180
 
181
	/*
182
	 * The entry is to be updated in TLB.
183
	 */
184
	if ((badvaddr/PAGE_SIZE) % 2 == 0)
396 jermar 185
		cp0_entry_lo0_write(lo.value);
394 jermar 186
	else
396 jermar 187
		cp0_entry_lo1_write(lo.value);
394 jermar 188
	tlbwi();
189
 
190
	spinlock_unlock(&VM->lock);	
191
	return;
192
 
193
fail:
194
	spinlock_unlock(&VM->lock);
391 jermar 195
	tlb_invalid_fail(pstate);
196
}
197
 
394 jermar 198
/** Process TLB Modified Exception
199
 *
200
 * Process TLB Modified Exception.
201
 *
202
 * @param pstate Interrupted register context.
203
 */
391 jermar 204
void tlb_modified(struct exception_regdump *pstate)
205
{
396 jermar 206
	tlb_index_t index;
394 jermar 207
	__address badvaddr;
396 jermar 208
	entry_lo_t lo;
399 jermar 209
	entry_hi_t hi;
394 jermar 210
	pte_t *pte;
211
 
212
	badvaddr = cp0_badvaddr_read();
213
 
214
	/*
215
	 * Locate the faulting entry in TLB.
216
	 */
399 jermar 217
	hi.value = cp0_entry_hi_read();
218
	prepare_entry_hi(&hi, hi.asid, badvaddr);
219
	cp0_entry_hi_write(hi.value);
394 jermar 220
	tlbp();
396 jermar 221
	index.value = cp0_index_read();
394 jermar 222
 
223
	spinlock_lock(&VM->lock);	
224
 
225
	/*
226
	 * Fail if the entry is not in TLB.
227
	 */
396 jermar 228
	if (index.p) {
229
		printf("TLB entry not found.\n");
394 jermar 230
		goto fail;
396 jermar 231
	}
394 jermar 232
 
233
	pte = find_mapping_and_check(badvaddr);
234
	if (!pte)
235
		goto fail;
236
 
237
	/*
238
	 * Fail if the page is not writable.
239
	 */
240
	if (!pte->w)
241
		goto fail;
242
 
243
	/*
244
	 * Read the faulting TLB entry.
245
	 */
246
	tlbr();
247
 
248
	/*
249
	 * Record access and write to PTE.
250
	 */
251
	pte->a = 1;
403 jermar 252
	pte->lo.d = 1;
394 jermar 253
 
403 jermar 254
	prepare_entry_lo(&lo, pte->lo.g, pte->lo.v, pte->w, pte->lo.c, pte->lo.pfn);
394 jermar 255
 
256
	/*
257
	 * The entry is to be updated in TLB.
258
	 */
259
	if ((badvaddr/PAGE_SIZE) % 2 == 0)
396 jermar 260
		cp0_entry_lo0_write(lo.value);
394 jermar 261
	else
396 jermar 262
		cp0_entry_lo1_write(lo.value);
394 jermar 263
	tlbwi();
264
 
265
	spinlock_unlock(&VM->lock);	
266
	return;
267
 
268
fail:
269
	spinlock_unlock(&VM->lock);
391 jermar 270
	tlb_modified_fail(pstate);
271
}
272
 
273
void tlb_refill_fail(struct exception_regdump *pstate)
274
{
324 palkovsky 275
	char *symbol = "";
276
	char *sym2 = "";
277
 
332 palkovsky 278
	char *s = get_symtab_entry(pstate->epc);
279
	if (s)
280
		symbol = s;
281
	s = get_symtab_entry(pstate->ra);
282
	if (s)
283
		sym2 = s;
391 jermar 284
	panic("%X: TLB Refill Exception at %X(%s<-%s)\n", cp0_badvaddr_read(), pstate->epc, symbol, sym2);
1 jermar 285
}
286
 
391 jermar 287
 
288
void tlb_invalid_fail(struct exception_regdump *pstate)
1 jermar 289
{
268 palkovsky 290
	char *symbol = "";
291
 
332 palkovsky 292
	char *s = get_symtab_entry(pstate->epc);
293
	if (s)
294
		symbol = s;
394 jermar 295
	panic("%X: TLB Invalid Exception at %X(%s)\n", cp0_badvaddr_read(), pstate->epc, symbol);
1 jermar 296
}
297
 
391 jermar 298
void tlb_modified_fail(struct exception_regdump *pstate)
389 jermar 299
{
300
	char *symbol = "";
301
 
302
	char *s = get_symtab_entry(pstate->epc);
303
	if (s)
304
		symbol = s;
394 jermar 305
	panic("%X: TLB Modified Exception at %X(%s)\n", cp0_badvaddr_read(), pstate->epc, symbol);
389 jermar 306
}
307
 
394 jermar 308
/** Try to find PTE for faulting address
309
 *
310
 * Try to find PTE for faulting address.
311
 * The VM->lock must be held on entry to this function.
312
 *
313
 * @param badvaddr Faulting virtual address.
314
 *
315
 * @return PTE on success, NULL otherwise.
316
 */
317
pte_t *find_mapping_and_check(__address badvaddr)
318
{
396 jermar 319
	entry_hi_t hi;
394 jermar 320
	pte_t *pte;
321
 
396 jermar 322
	hi.value = cp0_entry_hi_read();
394 jermar 323
 
324
	/*
325
	 * Handler cannot succeed if the ASIDs don't match.
326
	 */
396 jermar 327
	if (hi.asid != VM->asid) {
328
		printf("EntryHi.asid=%d, VM->asid=%d\n", hi.asid, VM->asid);
394 jermar 329
		return NULL;
396 jermar 330
	}
394 jermar 331
 
332
	/*
333
	 * Handler cannot succeed if badvaddr has no mapping.
334
	 */
492 jermar 335
	pte = page_mapping_find(badvaddr, 0);
396 jermar 336
	if (!pte) {
337
		printf("No such mapping.\n");
394 jermar 338
		return NULL;
396 jermar 339
	}
394 jermar 340
 
341
	/*
342
	 * Handler cannot succeed if the mapping is marked as invalid.
343
	 */
403 jermar 344
	if (!pte->lo.v) {
396 jermar 345
		printf("Invalid mapping.\n");
394 jermar 346
		return NULL;
396 jermar 347
	}
394 jermar 348
 
349
	return pte;
350
}
351
 
396 jermar 352
void prepare_entry_lo(entry_lo_t *lo, bool g, bool v, bool d, int c, __address pfn)
394 jermar 353
{
399 jermar 354
	lo->value = 0;
394 jermar 355
	lo->g = g;
356
	lo->v = v;
357
	lo->d = d;
358
	lo->c = c;
359
	lo->pfn = pfn;
360
}
399 jermar 361
 
362
void prepare_entry_hi(entry_hi_t *hi, asid_t asid, __address addr)
363
{
364
	hi->value = (((addr/PAGE_SIZE)/2)*PAGE_SIZE*2);
365
	hi->asid = asid;
366
}
569 jermar 367
 
594 jermar 368
/** Print contents of TLB. */
569 jermar 369
void tlb_print(void)
370
{
594 jermar 371
	entry_lo_t lo0, lo1;
372
	entry_hi_t hi;
373
	int i;
374
 
375
	printf("TLB:\n");
376
	for (i = 0; i < TLB_ENTRY_COUNT; i++) {
377
		cp0_index_write(i);
378
		tlbr();
379
 
380
		hi.value = cp0_entry_hi_read();
381
		lo0.value = cp0_entry_lo0_read();
382
		lo1.value = cp0_entry_lo1_read();
383
 
384
		printf("%d: asid=%d, vpn2=%d\tg[0]=%d, v[0]=%d, d[0]=%d, c[0]=%B, pfn[0]=%d\n"
385
		       "\t\t\tg[1]=%d, v[1]=%d, d[1]=%d, c[1]=%B, pfn[1]=%d\n",
386
		       i, hi.asid, hi.vpn2, lo0.g, lo0.v, lo0.d, lo0.c, lo0.pfn,
387
		       lo1.g, lo1.v, lo1.d, lo1.c, lo1.pfn);
388
	}
569 jermar 389
}
598 jermar 390
 
391
/** Invalidate all TLB entries. */
392
void tlb_invalidate_all(void)
393
{
599 jermar 394
	ipl_t ipl;
395
	entry_lo_t lo0, lo1;
598 jermar 396
	int i;
397
 
599 jermar 398
	ipl = interrupts_disable();
598 jermar 399
 
400
	for (i = 0; i < TLB_ENTRY_COUNT; i++) {
401
		cp0_index_write(i);
599 jermar 402
		tlbr();
403
 
404
		lo0.value = cp0_entry_lo0_read();
405
		lo1.value = cp0_entry_lo1_read();
406
 
407
		lo0.v = 0;
408
		lo1.v = 0;
409
 
410
		cp0_entry_lo0_write(lo0.value);
411
		cp0_entry_lo1_write(lo1.value);
412
 
598 jermar 413
		tlbwi();
414
	}
599 jermar 415
 
416
	interrupts_restore(ipl);
598 jermar 417
}
418
 
419
/** Invalidate all TLB entries belonging to specified address space.
420
 *
421
 * @param asid Address space identifier.
422
 */
423
void tlb_invalidate_asid(asid_t asid)
424
{
599 jermar 425
	ipl_t ipl;
426
	entry_lo_t lo0, lo1;
598 jermar 427
	entry_hi_t hi;
428
	int i;
429
 
599 jermar 430
	ASSERT(asid != ASID_INVALID);
431
 
432
	ipl = interrupts_disable();
433
 
598 jermar 434
	for (i = 0; i < TLB_ENTRY_COUNT; i++) {
435
		cp0_index_write(i);
436
		tlbr();
437
 
599 jermar 438
		hi.value = cp0_entry_hi_read();
439
 
598 jermar 440
		if (hi.asid == asid) {
599 jermar 441
			lo0.value = cp0_entry_lo0_read();
442
			lo1.value = cp0_entry_lo1_read();
443
 
444
			lo0.v = 0;
445
			lo1.v = 0;
446
 
447
			cp0_entry_lo0_write(lo0.value);
448
			cp0_entry_lo1_write(lo1.value);
449
 
598 jermar 450
			tlbwi();
451
		}
452
	}
599 jermar 453
 
454
	interrupts_restore(ipl);
598 jermar 455
}
456
 
457
/** Invalidate TLB entry for specified page belonging to specified address space.
458
 *
459
 * @param asid Address space identifier.
460
 * @param page Page whose TLB entry is to be invalidated.
461
 */
462
void tlb_invalidate_page(asid_t asid, __address page)
463
{
599 jermar 464
	ipl_t ipl;
465
	entry_lo_t lo0, lo1;
598 jermar 466
	entry_hi_t hi;
467
	tlb_index_t index;
468
 
599 jermar 469
	ASSERT(asid != ASID_INVALID);
470
 
471
	ipl = interrupts_disable();
472
 
598 jermar 473
	hi.value = 0;
474
	prepare_entry_hi(&hi, asid, page);
599 jermar 475
	cp0_entry_hi_write(hi.value);
476
 
598 jermar 477
	tlbp();
478
	index.value = cp0_index_read();
479
 
480
	if (!index.p) {
481
		/* Entry was found, index register contains valid index. */
599 jermar 482
		tlbr();
483
 
484
		lo0.value = cp0_entry_lo0_read();
485
		lo1.value = cp0_entry_lo1_read();
486
 
487
		lo0.v = 0;
488
		lo1.v = 0;
489
 
490
		cp0_entry_lo0_write(lo0.value);
491
		cp0_entry_lo1_write(lo1.value);
492
 
598 jermar 493
		tlbwi();
494
	}
599 jermar 495
 
496
	interrupts_restore(ipl);
598 jermar 497
}