Subversion Repositories HelenOS-historic

Rev

Rev 594 | Rev 599 | 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
{
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
}