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