Ignore:
File:
1 edited

Legend:

Unmodified
Added
Removed
  • kernel/arch/mips32/src/mm/tlb.c

    r59fb782 raaa6af2  
    4848#include <symtab.h>
    4949
    50 #define PFN_SHIFT       12
    51 #define VPN_SHIFT       12
    52 #define ADDR2VPN(a)     ((a) >> VPN_SHIFT)
    53 #define ADDR2VPN2(a)    (ADDR2VPN((a)) >> 1)
    54 #define VPN2ADDR(vpn)   ((vpn) << VPN_SHIFT)
    55 #define VPN22ADDR(vpn2) (VPN2ADDR(vpn2) << 1)
    56 #define PFN2ADDR(pfn)   ((pfn) << PFN_SHIFT)
    57 
    58 #define BANK_SELECT_BIT(a)      (((a) >> PAGE_WIDTH) & 1)
    59        
     50static void tlb_refill_fail(istate_t *);
     51static void tlb_invalid_fail(istate_t *);
     52static void tlb_modified_fail(istate_t *);
     53
     54static pte_t *find_mapping_and_check(uintptr_t, int, istate_t *, int *);
    6055
    6156/** Initialize TLB.
     
    9388{
    9489        entry_lo_t lo;
     90        entry_hi_t hi;
     91        asid_t asid;
    9592        uintptr_t badvaddr;
    9693        pte_t *pte;
     94        int pfrc;
    9795       
    9896        badvaddr = cp0_badvaddr_read();
    99 
    100         pte = page_mapping_find(AS, badvaddr, true);
    101         if (pte && pte->p) {
    102                 /*
    103                  * Record access to PTE.
    104                  */
    105                 pte->a = 1;
    106 
    107                 tlb_prepare_entry_lo(&lo, pte->g, pte->p, pte->d,
    108                     pte->cacheable, pte->pfn);
    109 
    110                 /*
    111                  * New entry is to be inserted into TLB
    112                  */
    113                 if (BANK_SELECT_BIT(badvaddr) == 0) {
    114                         cp0_entry_lo0_write(lo.value);
    115                         cp0_entry_lo1_write(0);
    116                 } else {
    117                         cp0_entry_lo0_write(0);
    118                         cp0_entry_lo1_write(lo.value);
     97        asid = AS->asid;
     98       
     99        pte = find_mapping_and_check(badvaddr, PF_ACCESS_READ, istate, &pfrc);
     100        if (!pte) {
     101                switch (pfrc) {
     102                case AS_PF_FAULT:
     103                        goto fail;
     104                        break;
     105                case AS_PF_DEFER:
     106                        /*
     107                         * The page fault came during copy_from_uspace()
     108                         * or copy_to_uspace().
     109                         */
     110                        return;
     111                default:
     112                        panic("Unexpected pfrc (%d).", pfrc);
    119113                }
    120                 cp0_pagemask_write(TLB_PAGE_MASK_16K);
    121                 tlbwr();
    122                 return;
    123         }
    124 
    125         (void) as_page_fault(badvaddr, PF_ACCESS_READ, istate);
     114        }
     115
     116        /*
     117         * Record access to PTE.
     118         */
     119        pte->a = 1;
     120
     121        tlb_prepare_entry_hi(&hi, asid, badvaddr);
     122        tlb_prepare_entry_lo(&lo, pte->g, pte->p, pte->d, pte->cacheable,
     123            pte->pfn);
     124
     125        /*
     126         * New entry is to be inserted into TLB
     127         */
     128        cp0_entry_hi_write(hi.value);
     129        if ((badvaddr / PAGE_SIZE) % 2 == 0) {
     130                cp0_entry_lo0_write(lo.value);
     131                cp0_entry_lo1_write(0);
     132        }
     133        else {
     134                cp0_entry_lo0_write(0);
     135                cp0_entry_lo1_write(lo.value);
     136        }
     137        cp0_pagemask_write(TLB_PAGE_MASK_16K);
     138        tlbwr();
     139
     140        return;
     141       
     142fail:
     143        tlb_refill_fail(istate);
    126144}
    127145
     
    132150void tlb_invalid(istate_t *istate)
    133151{
    134         entry_lo_t lo;
    135152        tlb_index_t index;
    136153        uintptr_t badvaddr;
     154        entry_lo_t lo;
     155        entry_hi_t hi;
    137156        pte_t *pte;
     157        int pfrc;
     158
     159        badvaddr = cp0_badvaddr_read();
    138160
    139161        /*
    140162         * Locate the faulting entry in TLB.
    141163         */
     164        hi.value = cp0_entry_hi_read();
     165        tlb_prepare_entry_hi(&hi, hi.asid, badvaddr);
     166        cp0_entry_hi_write(hi.value);
    142167        tlbp();
    143168        index.value = cp0_index_read();
    144169
    145 #if defined(PROCESSOR_4Kc)
    146         /*
    147          * This can happen on a 4Kc when Status.EXL is 1 and there is a TLB miss.
    148          * EXL is 1 when interrupts are disabled. The combination of a TLB miss
    149          * and disabled interrupts is possible in copy_to/from_uspace().
     170        /*
     171         * Fail if the entry is not in TLB.
    150172         */
    151173        if (index.p) {
    152                 tlb_refill(istate);
    153                 return;
    154         }
    155 #endif
    156 
    157         ASSERT(!index.p);
    158 
    159         badvaddr = cp0_badvaddr_read();
    160 
    161         pte = page_mapping_find(AS, badvaddr, true);
    162         if (pte && pte->p) {
    163                 /*
    164                  * Read the faulting TLB entry.
    165                  */
    166                 tlbr();
    167 
    168                 /*
    169                  * Record access to PTE.
    170                  */
    171                 pte->a = 1;
    172 
    173                 tlb_prepare_entry_lo(&lo, pte->g, pte->p, pte->d,
    174                     pte->cacheable, pte->pfn);
    175 
    176                 /*
    177                  * The entry is to be updated in TLB.
    178                  */
    179                 if (BANK_SELECT_BIT(badvaddr) == 0)
    180                         cp0_entry_lo0_write(lo.value);
    181                 else
    182                         cp0_entry_lo1_write(lo.value);
    183                 tlbwi();
    184                 return;
    185         }
    186 
    187         (void) as_page_fault(badvaddr, PF_ACCESS_READ, istate);
     174                printf("TLB entry not found.\n");
     175                goto fail;
     176        }
     177
     178        pte = find_mapping_and_check(badvaddr, PF_ACCESS_READ, istate, &pfrc);
     179        if (!pte) {
     180                switch (pfrc) {
     181                case AS_PF_FAULT:
     182                        goto fail;
     183                        break;
     184                case AS_PF_DEFER:
     185                        /*
     186                         * The page fault came during copy_from_uspace()
     187                         * or copy_to_uspace().
     188                         */
     189                        return;
     190                default:
     191                        panic("Unexpected pfrc (%d).", pfrc);
     192                }
     193        }
     194
     195        /*
     196         * Read the faulting TLB entry.
     197         */
     198        tlbr();
     199
     200        /*
     201         * Record access to PTE.
     202         */
     203        pte->a = 1;
     204
     205        tlb_prepare_entry_lo(&lo, pte->g, pte->p, pte->d, pte->cacheable,
     206            pte->pfn);
     207
     208        /*
     209         * The entry is to be updated in TLB.
     210         */
     211        if ((badvaddr / PAGE_SIZE) % 2 == 0)
     212                cp0_entry_lo0_write(lo.value);
     213        else
     214                cp0_entry_lo1_write(lo.value);
     215        cp0_pagemask_write(TLB_PAGE_MASK_16K);
     216        tlbwi();
     217
     218        return;
     219       
     220fail:
     221        tlb_invalid_fail(istate);
    188222}
    189223
     
    194228void tlb_modified(istate_t *istate)
    195229{
    196         entry_lo_t lo;
    197230        tlb_index_t index;
    198231        uintptr_t badvaddr;
     232        entry_lo_t lo;
     233        entry_hi_t hi;
    199234        pte_t *pte;
     235        int pfrc;
    200236
    201237        badvaddr = cp0_badvaddr_read();
     
    204240         * Locate the faulting entry in TLB.
    205241         */
     242        hi.value = cp0_entry_hi_read();
     243        tlb_prepare_entry_hi(&hi, hi.asid, badvaddr);
     244        cp0_entry_hi_write(hi.value);
    206245        tlbp();
    207246        index.value = cp0_index_read();
    208247
    209248        /*
    210          * Emit warning if the entry is not in TLB.
    211          *
    212          * We do not assert on this because this could be a manifestation of
    213          * an emulator bug, such as QEMU Bug #1128935:
    214          * https://bugs.launchpad.net/qemu/+bug/1128935 
     249         * Fail if the entry is not in TLB.
    215250         */
    216251        if (index.p) {
    217                 printf("%s: TLBP failed in exception handler (badvaddr=%#"
    218                     PRIxn ", ASID=%d).\n", __func__, badvaddr,
    219                     AS ? AS->asid : -1);
    220                 return;
    221         }
    222 
     252                printf("TLB entry not found.\n");
     253                goto fail;
     254        }
     255
     256        pte = find_mapping_and_check(badvaddr, PF_ACCESS_WRITE, istate, &pfrc);
     257        if (!pte) {
     258                switch (pfrc) {
     259                case AS_PF_FAULT:
     260                        goto fail;
     261                        break;
     262                case AS_PF_DEFER:
     263                        /*
     264                         * The page fault came during copy_from_uspace()
     265                         * or copy_to_uspace().
     266                         */
     267                        return;
     268                default:
     269                        panic("Unexpected pfrc (%d).", pfrc);
     270                }
     271        }
     272
     273        /*
     274         * Read the faulting TLB entry.
     275         */
     276        tlbr();
     277
     278        /*
     279         * Record access and write to PTE.
     280         */
     281        pte->a = 1;
     282        pte->d = 1;
     283
     284        tlb_prepare_entry_lo(&lo, pte->g, pte->p, pte->w, pte->cacheable,
     285            pte->pfn);
     286
     287        /*
     288         * The entry is to be updated in TLB.
     289         */
     290        if ((badvaddr / PAGE_SIZE) % 2 == 0)
     291                cp0_entry_lo0_write(lo.value);
     292        else
     293                cp0_entry_lo1_write(lo.value);
     294        cp0_pagemask_write(TLB_PAGE_MASK_16K);
     295        tlbwi();
     296
     297        return;
     298       
     299fail:
     300        tlb_modified_fail(istate);
     301}
     302
     303void tlb_refill_fail(istate_t *istate)
     304{
     305        uintptr_t va = cp0_badvaddr_read();
     306       
     307        fault_if_from_uspace(istate, "TLB Refill Exception on %p.",
     308            (void *) va);
     309        panic_memtrap(istate, PF_ACCESS_UNKNOWN, va, "TLB Refill Exception.");
     310}
     311
     312
     313void tlb_invalid_fail(istate_t *istate)
     314{
     315        uintptr_t va = cp0_badvaddr_read();
     316       
     317        fault_if_from_uspace(istate, "TLB Invalid Exception on %p.",
     318            (void *) va);
     319        panic_memtrap(istate, PF_ACCESS_UNKNOWN, va, "TLB Invalid Exception.");
     320}
     321
     322void tlb_modified_fail(istate_t *istate)
     323{
     324        uintptr_t va = cp0_badvaddr_read();
     325       
     326        fault_if_from_uspace(istate, "TLB Modified Exception on %p.",
     327            (void *) va);
     328        panic_memtrap(istate, PF_ACCESS_WRITE, va, "TLB Modified Exception.");
     329}
     330
     331/** Try to find PTE for faulting address.
     332 *
     333 * @param badvaddr      Faulting virtual address.
     334 * @param access        Access mode that caused the fault.
     335 * @param istate        Pointer to interrupted state.
     336 * @param pfrc          Pointer to variable where as_page_fault() return code
     337 *                      will be stored.
     338 *
     339 * @return              PTE on success, NULL otherwise.
     340 */
     341pte_t *
     342find_mapping_and_check(uintptr_t badvaddr, int access, istate_t *istate,
     343    int *pfrc)
     344{
     345        entry_hi_t hi;
     346        pte_t *pte;
     347
     348        hi.value = cp0_entry_hi_read();
     349
     350        /*
     351         * Handler cannot succeed if the ASIDs don't match.
     352         */
     353        if (hi.asid != AS->asid) {
     354                printf("EntryHi.asid=%d, AS->asid=%d\n", hi.asid, AS->asid);
     355                return NULL;
     356        }
     357
     358        /*
     359         * Check if the mapping exists in page tables.
     360         */     
    223361        pte = page_mapping_find(AS, badvaddr, true);
    224         if (pte && pte->p && pte->w) {
     362        if (pte && pte->p && (pte->w || access != PF_ACCESS_WRITE)) {
    225363                /*
    226                  * Read the faulting TLB entry.
     364                 * Mapping found in page tables.
     365                 * Immediately succeed.
    227366                 */
    228                 tlbr();
    229 
     367                return pte;
     368        } else {
     369                int rc;
     370               
    230371                /*
    231                  * Record access and write to PTE.
     372                 * Mapping not found in page tables.
     373                 * Resort to higher-level page fault handler.
    232374                 */
    233                 pte->a = 1;
    234                 pte->d = 1;
    235 
    236                 tlb_prepare_entry_lo(&lo, pte->g, pte->p, pte->w,
    237                     pte->cacheable, pte->pfn);
    238 
    239                 /*
    240                  * The entry is to be updated in TLB.
    241                  */
    242                 if (BANK_SELECT_BIT(badvaddr) == 0)
    243                         cp0_entry_lo0_write(lo.value);
    244                 else
    245                         cp0_entry_lo1_write(lo.value);
    246                 tlbwi();
    247                 return;
    248         }
    249 
    250         (void) as_page_fault(badvaddr, PF_ACCESS_WRITE, istate);
     375                switch (rc = as_page_fault(badvaddr, access, istate)) {
     376                case AS_PF_OK:
     377                        /*
     378                         * The higher-level page fault handler succeeded,
     379                         * The mapping ought to be in place.
     380                         */
     381                        pte = page_mapping_find(AS, badvaddr, true);
     382                        ASSERT(pte && pte->p);
     383                        ASSERT(pte->w || access != PF_ACCESS_WRITE);
     384                        return pte;
     385                case AS_PF_DEFER:
     386                        *pfrc = AS_PF_DEFER;
     387                        return NULL;
     388                case AS_PF_FAULT:
     389                        *pfrc = AS_PF_FAULT;
     390                        return NULL;
     391                default:
     392                        panic("Unexpected rc (%d).", rc);
     393                }
     394               
     395        }
    251396}
    252397
     
    265410void tlb_prepare_entry_hi(entry_hi_t *hi, asid_t asid, uintptr_t addr)
    266411{
    267         hi->value = 0;
    268         hi->vpn2 = ADDR2VPN2(ALIGN_DOWN(addr, PAGE_SIZE));
     412        hi->value = ALIGN_DOWN(addr, PAGE_SIZE * 2);
    269413        hi->asid = asid;
    270414}
     
    273417void tlb_print(void)
    274418{
    275         page_mask_t mask, mask_save;
    276         entry_lo_t lo0, lo0_save, lo1, lo1_save;
     419        page_mask_t mask;
     420        entry_lo_t lo0, lo1;
    277421        entry_hi_t hi, hi_save;
    278422        unsigned int i;
    279423
    280424        hi_save.value = cp0_entry_hi_read();
    281         lo0_save.value = cp0_entry_lo0_read();
    282         lo1_save.value = cp0_entry_lo1_read();
    283         mask_save.value = cp0_pagemask_read();
    284        
    285         printf("[nr] [asid] [vpn2    ] [mask] [gvdc] [pfn     ]\n");
     425       
     426        printf("[nr] [asid] [vpn2] [mask] [gvdc] [pfn ]\n");
    286427       
    287428        for (i = 0; i < TLB_ENTRY_COUNT; i++) {
     
    294435                lo1.value = cp0_entry_lo1_read();
    295436               
    296                 printf("%-4u %-6u %0#10x %-#6x  %1u%1u%1u%1u  %0#10x\n",
    297                     i, hi.asid, VPN22ADDR(hi.vpn2), mask.mask,
    298                     lo0.g, lo0.v, lo0.d, lo0.c, PFN2ADDR(lo0.pfn));
    299                 printf("                               %1u%1u%1u%1u  %0#10x\n",
    300                     lo1.g, lo1.v, lo1.d, lo1.c, PFN2ADDR(lo1.pfn));
     437                printf("%-4u %-6u %#6x %#6x  %1u%1u%1u%1u  %#6x\n",
     438                    i, hi.asid, hi.vpn2, mask.mask,
     439                    lo0.g, lo0.v, lo0.d, lo0.c, lo0.pfn);
     440                printf("                           %1u%1u%1u%1u  %#6x\n",
     441                    lo1.g, lo1.v, lo1.d, lo1.c, lo1.pfn);
    301442        }
    302443       
    303444        cp0_entry_hi_write(hi_save.value);
    304         cp0_entry_lo0_write(lo0_save.value);
    305         cp0_entry_lo1_write(lo1_save.value);
    306         cp0_pagemask_write(mask_save.value);
    307445}
    308446
     
    310448void tlb_invalidate_all(void)
    311449{
     450        ipl_t ipl;
    312451        entry_lo_t lo0, lo1;
    313452        entry_hi_t hi_save;
    314453        int i;
    315454
    316         ASSERT(interrupts_disabled());
    317 
    318455        hi_save.value = cp0_entry_hi_read();
     456        ipl = interrupts_disable();
    319457
    320458        for (i = TLB_WIRED; i < TLB_ENTRY_COUNT; i++) {
     
    334472        }
    335473       
     474        interrupts_restore(ipl);
    336475        cp0_entry_hi_write(hi_save.value);
    337476}
     
    343482void tlb_invalidate_asid(asid_t asid)
    344483{
     484        ipl_t ipl;
    345485        entry_lo_t lo0, lo1;
    346486        entry_hi_t hi, hi_save;
    347487        int i;
    348488
    349         ASSERT(interrupts_disabled());
    350489        ASSERT(asid != ASID_INVALID);
    351490
    352491        hi_save.value = cp0_entry_hi_read();
     492        ipl = interrupts_disable();
    353493       
    354494        for (i = 0; i < TLB_ENTRY_COUNT; i++) {
     
    372512        }
    373513       
     514        interrupts_restore(ipl);
    374515        cp0_entry_hi_write(hi_save.value);
    375516}
     
    385526{
    386527        unsigned int i;
     528        ipl_t ipl;
    387529        entry_lo_t lo0, lo1;
    388530        entry_hi_t hi, hi_save;
    389531        tlb_index_t index;
    390 
    391         ASSERT(interrupts_disabled());
    392532       
    393533        if (asid == ASID_INVALID)
     
    395535
    396536        hi_save.value = cp0_entry_hi_read();
     537        ipl = interrupts_disable();
    397538
    398539        for (i = 0; i < cnt + 1; i += 2) {
     540                hi.value = 0;
    399541                tlb_prepare_entry_hi(&hi, asid, page + i * PAGE_SIZE);
    400542                cp0_entry_hi_write(hi.value);
     
    423565        }
    424566       
     567        interrupts_restore(ipl);
    425568        cp0_entry_hi_write(hi_save.value);
    426569}
Note: See TracChangeset for help on using the changeset viewer.