Subversion Repositories HelenOS-historic

Rev

Rev 598 | Rev 612 | 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>
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
}