Subversion Repositories HelenOS-historic

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
}