Ignore:
File:
1 edited

Legend:

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

    raaa6af2 r59fb782  
    4848#include <symtab.h>
    4949
    50 static void tlb_refill_fail(istate_t *);
    51 static void tlb_invalid_fail(istate_t *);
    52 static void tlb_modified_fail(istate_t *);
    53 
    54 static pte_t *find_mapping_and_check(uintptr_t, int, istate_t *, int *);
     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       
    5560
    5661/** Initialize TLB.
     
    8893{
    8994        entry_lo_t lo;
    90         entry_hi_t hi;
    91         asid_t asid;
    9295        uintptr_t badvaddr;
    9396        pte_t *pte;
    94         int pfrc;
    9597       
    9698        badvaddr = cp0_badvaddr_read();
    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);
     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);
    113119                }
    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        
    142 fail:
    143         tlb_refill_fail(istate);
     120                cp0_pagemask_write(TLB_PAGE_MASK_16K);
     121                tlbwr();
     122                return;
     123        }
     124
     125        (void) as_page_fault(badvaddr, PF_ACCESS_READ, istate);
    144126}
    145127
     
    150132void tlb_invalid(istate_t *istate)
    151133{
     134        entry_lo_t lo;
    152135        tlb_index_t index;
    153136        uintptr_t badvaddr;
    154         entry_lo_t lo;
    155         entry_hi_t hi;
    156137        pte_t *pte;
    157         int pfrc;
    158 
    159         badvaddr = cp0_badvaddr_read();
    160138
    161139        /*
    162140         * Locate the faulting entry in TLB.
    163141         */
    164         hi.value = cp0_entry_hi_read();
    165         tlb_prepare_entry_hi(&hi, hi.asid, badvaddr);
    166         cp0_entry_hi_write(hi.value);
    167142        tlbp();
    168143        index.value = cp0_index_read();
    169144
     145#if defined(PROCESSOR_4Kc)
    170146        /*
    171          * Fail if the entry is not in TLB.
     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().
    172150         */
    173151        if (index.p) {
    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        
    220 fail:
    221         tlb_invalid_fail(istate);
     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);
    222188}
    223189
     
    228194void tlb_modified(istate_t *istate)
    229195{
     196        entry_lo_t lo;
    230197        tlb_index_t index;
    231198        uintptr_t badvaddr;
    232         entry_lo_t lo;
    233         entry_hi_t hi;
    234199        pte_t *pte;
    235         int pfrc;
    236200
    237201        badvaddr = cp0_badvaddr_read();
     
    240204         * Locate the faulting entry in TLB.
    241205         */
    242         hi.value = cp0_entry_hi_read();
    243         tlb_prepare_entry_hi(&hi, hi.asid, badvaddr);
    244         cp0_entry_hi_write(hi.value);
    245206        tlbp();
    246207        index.value = cp0_index_read();
    247208
    248209        /*
    249          * Fail if the entry is not in TLB.
     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 
    250215         */
    251216        if (index.p) {
    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        
    299 fail:
    300         tlb_modified_fail(istate);
    301 }
    302 
    303 void 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 
    313 void 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 
    322 void 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  */
    341 pte_t *
    342 find_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          */     
     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
    361223        pte = page_mapping_find(AS, badvaddr, true);
    362         if (pte && pte->p && (pte->w || access != PF_ACCESS_WRITE)) {
    363                 /*
    364                  * Mapping found in page tables.
    365                  * Immediately succeed.
    366                  */
    367                 return pte;
    368         } else {
    369                 int rc;
    370                
    371                 /*
    372                  * Mapping not found in page tables.
    373                  * Resort to higher-level page fault handler.
    374                  */
    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         }
     224        if (pte && pte->p && pte->w) {
     225                /*
     226                 * Read the faulting TLB entry.
     227                 */
     228                tlbr();
     229
     230                /*
     231                 * Record access and write to PTE.
     232                 */
     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);
    396251}
    397252
     
    410265void tlb_prepare_entry_hi(entry_hi_t *hi, asid_t asid, uintptr_t addr)
    411266{
    412         hi->value = ALIGN_DOWN(addr, PAGE_SIZE * 2);
     267        hi->value = 0;
     268        hi->vpn2 = ADDR2VPN2(ALIGN_DOWN(addr, PAGE_SIZE));
    413269        hi->asid = asid;
    414270}
     
    417273void tlb_print(void)
    418274{
    419         page_mask_t mask;
    420         entry_lo_t lo0, lo1;
     275        page_mask_t mask, mask_save;
     276        entry_lo_t lo0, lo0_save, lo1, lo1_save;
    421277        entry_hi_t hi, hi_save;
    422278        unsigned int i;
    423279
    424280        hi_save.value = cp0_entry_hi_read();
    425        
    426         printf("[nr] [asid] [vpn2] [mask] [gvdc] [pfn ]\n");
     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");
    427286       
    428287        for (i = 0; i < TLB_ENTRY_COUNT; i++) {
     
    435294                lo1.value = cp0_entry_lo1_read();
    436295               
    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);
     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));
    442301        }
    443302       
    444303        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);
    445307}
    446308
     
    448310void tlb_invalidate_all(void)
    449311{
    450         ipl_t ipl;
    451312        entry_lo_t lo0, lo1;
    452313        entry_hi_t hi_save;
    453314        int i;
    454315
     316        ASSERT(interrupts_disabled());
     317
    455318        hi_save.value = cp0_entry_hi_read();
    456         ipl = interrupts_disable();
    457319
    458320        for (i = TLB_WIRED; i < TLB_ENTRY_COUNT; i++) {
     
    472334        }
    473335       
    474         interrupts_restore(ipl);
    475336        cp0_entry_hi_write(hi_save.value);
    476337}
     
    482343void tlb_invalidate_asid(asid_t asid)
    483344{
    484         ipl_t ipl;
    485345        entry_lo_t lo0, lo1;
    486346        entry_hi_t hi, hi_save;
    487347        int i;
    488348
     349        ASSERT(interrupts_disabled());
    489350        ASSERT(asid != ASID_INVALID);
    490351
    491352        hi_save.value = cp0_entry_hi_read();
    492         ipl = interrupts_disable();
    493353       
    494354        for (i = 0; i < TLB_ENTRY_COUNT; i++) {
     
    512372        }
    513373       
    514         interrupts_restore(ipl);
    515374        cp0_entry_hi_write(hi_save.value);
    516375}
     
    526385{
    527386        unsigned int i;
    528         ipl_t ipl;
    529387        entry_lo_t lo0, lo1;
    530388        entry_hi_t hi, hi_save;
    531389        tlb_index_t index;
     390
     391        ASSERT(interrupts_disabled());
    532392       
    533393        if (asid == ASID_INVALID)
     
    535395
    536396        hi_save.value = cp0_entry_hi_read();
    537         ipl = interrupts_disable();
    538397
    539398        for (i = 0; i < cnt + 1; i += 2) {
    540                 hi.value = 0;
    541399                tlb_prepare_entry_hi(&hi, asid, page + i * PAGE_SIZE);
    542400                cp0_entry_hi_write(hi.value);
     
    565423        }
    566424       
    567         interrupts_restore(ipl);
    568425        cp0_entry_hi_write(hi_save.value);
    569426}
Note: See TracChangeset for help on using the changeset viewer.