Subversion Repositories HelenOS

Rev

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