Ignore:
File:
1 edited

Legend:

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

    r8fe2c9bd r1dbc43f  
    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 pte_t *find_mapping_and_check(uintptr_t, int, istate_t *);
    6051
    6152/** Initialize TLB.
     
    9384{
    9485        entry_lo_t lo;
     86        entry_hi_t hi;
     87        asid_t asid;
    9588        uintptr_t badvaddr;
    96         uintptr_t page;
    9789        pte_t *pte;
    9890       
    9991        badvaddr = cp0_badvaddr_read();
    100         page = ALIGN_DOWN(badvaddr, PAGE_SIZE);
    101 
    102         pte = page_mapping_find(AS, page, true);
    103         if (pte && pte->p) {
     92        asid = AS->asid;
     93       
     94        pte = find_mapping_and_check(badvaddr, PF_ACCESS_READ, istate);
     95        if (pte) {
    10496                /*
    10597                 * Record access to PTE.
     
    10799                pte->a = 1;
    108100
     101                tlb_prepare_entry_hi(&hi, asid, badvaddr);
    109102                tlb_prepare_entry_lo(&lo, pte->g, pte->p, pte->d,
    110103                    pte->cacheable, pte->pfn);
     
    113106                 * New entry is to be inserted into TLB
    114107                 */
    115                 if (BANK_SELECT_BIT(badvaddr) == 0) {
     108                cp0_entry_hi_write(hi.value);
     109                if ((badvaddr / PAGE_SIZE) % 2 == 0) {
    116110                        cp0_entry_lo0_write(lo.value);
    117111                        cp0_entry_lo1_write(0);
     
    122116                cp0_pagemask_write(TLB_PAGE_MASK_16K);
    123117                tlbwr();
    124                 return;
    125         }
    126 
    127         (void) as_page_fault(page, PF_ACCESS_READ, istate);
     118        }
    128119}
    129120
     
    134125void tlb_invalid(istate_t *istate)
    135126{
    136         entry_lo_t lo;
    137127        tlb_index_t index;
    138128        uintptr_t badvaddr;
    139         uintptr_t page;
     129        entry_lo_t lo;
     130        entry_hi_t hi;
    140131        pte_t *pte;
     132
     133        badvaddr = cp0_badvaddr_read();
    141134
    142135        /*
    143136         * Locate the faulting entry in TLB.
    144137         */
     138        hi.value = cp0_entry_hi_read();
     139        tlb_prepare_entry_hi(&hi, hi.asid, badvaddr);
     140        cp0_entry_hi_write(hi.value);
    145141        tlbp();
    146142        index.value = cp0_index_read();
    147143
    148 #if defined(PROCESSOR_4Kc)
    149         /*
    150          * This can happen on a 4Kc when Status.EXL is 1 and there is a TLB miss.
    151          * EXL is 1 when interrupts are disabled. The combination of a TLB miss
    152          * and disabled interrupts is possible in copy_to/from_uspace().
    153          */
    154         if (index.p) {
    155                 tlb_refill(istate);
    156                 return;
    157         }
    158 #endif
    159 
    160144        ASSERT(!index.p);
    161145
    162         badvaddr = cp0_badvaddr_read();
    163         page = ALIGN_DOWN(badvaddr, PAGE_SIZE);
    164 
    165         pte = page_mapping_find(AS, page, true);
    166         if (pte && pte->p) {
     146        pte = find_mapping_and_check(badvaddr, PF_ACCESS_READ, istate);
     147        if (pte) {
    167148                /*
    168149                 * Read the faulting TLB entry.
     
    181162                 * The entry is to be updated in TLB.
    182163                 */
    183                 if (BANK_SELECT_BIT(badvaddr) == 0)
     164                if ((badvaddr / PAGE_SIZE) % 2 == 0)
    184165                        cp0_entry_lo0_write(lo.value);
    185166                else
    186167                        cp0_entry_lo1_write(lo.value);
     168                cp0_pagemask_write(TLB_PAGE_MASK_16K);
    187169                tlbwi();
    188                 return;
    189         }
    190 
    191         (void) as_page_fault(page, PF_ACCESS_READ, istate);
     170        }
    192171}
    193172
     
    198177void tlb_modified(istate_t *istate)
    199178{
    200         entry_lo_t lo;
    201179        tlb_index_t index;
    202180        uintptr_t badvaddr;
    203         uintptr_t page;
     181        entry_lo_t lo;
     182        entry_hi_t hi;
    204183        pte_t *pte;
    205184
    206185        badvaddr = cp0_badvaddr_read();
    207         page = ALIGN_DOWN(badvaddr, PAGE_SIZE);
    208186
    209187        /*
    210188         * Locate the faulting entry in TLB.
    211189         */
     190        hi.value = cp0_entry_hi_read();
     191        tlb_prepare_entry_hi(&hi, hi.asid, badvaddr);
     192        cp0_entry_hi_write(hi.value);
    212193        tlbp();
    213194        index.value = cp0_index_read();
    214195
    215196        /*
    216          * Emit warning if the entry is not in TLB.
    217          *
    218          * We do not assert on this because this could be a manifestation of
    219          * an emulator bug, such as QEMU Bug #1128935:
    220          * https://bugs.launchpad.net/qemu/+bug/1128935 
     197         * Fail if the entry is not in TLB.
    221198         */
    222         if (index.p) {
    223                 printf("%s: TLBP failed in exception handler (badvaddr=%#"
    224                     PRIxn ", ASID=%d).\n", __func__, badvaddr,
    225                     AS ? AS->asid : -1);
    226                 return;
    227         }
    228 
    229         pte = page_mapping_find(AS, page, true);
    230         if (pte && pte->p && pte->w) {
     199        ASSERT(!index.p);
     200
     201        pte = find_mapping_and_check(badvaddr, PF_ACCESS_WRITE, istate);
     202        if (pte) {
    231203                /*
    232204                 * Read the faulting TLB entry.
     
    246218                 * The entry is to be updated in TLB.
    247219                 */
    248                 if (BANK_SELECT_BIT(badvaddr) == 0)
     220                if ((badvaddr / PAGE_SIZE) % 2 == 0)
    249221                        cp0_entry_lo0_write(lo.value);
    250222                else
    251223                        cp0_entry_lo1_write(lo.value);
     224                cp0_pagemask_write(TLB_PAGE_MASK_16K);
    252225                tlbwi();
    253                 return;
    254         }
    255 
    256         (void) as_page_fault(page, PF_ACCESS_WRITE, istate);
     226        }
     227}
     228
     229/** Try to find PTE for faulting address.
     230 *
     231 * @param badvaddr      Faulting virtual address.
     232 * @param access        Access mode that caused the fault.
     233 * @param istate        Pointer to interrupted state.
     234 *
     235 * @return              PTE on success, NULL otherwise.
     236 */
     237pte_t *find_mapping_and_check(uintptr_t badvaddr, int access, istate_t *istate)
     238{
     239        entry_hi_t hi;
     240        pte_t *pte;
     241
     242        hi.value = cp0_entry_hi_read();
     243
     244        ASSERT(hi.asid == AS->asid);
     245
     246        /*
     247         * Check if the mapping exists in page tables.
     248         */     
     249        pte = page_mapping_find(AS, badvaddr, true);
     250        if (pte && pte->p && (pte->w || access != PF_ACCESS_WRITE)) {
     251                /*
     252                 * Mapping found in page tables.
     253                 * Immediately succeed.
     254                 */
     255                return pte;
     256        }
     257
     258        /*
     259         * Mapping not found in page tables.
     260         * Resort to higher-level page fault handler.
     261         */
     262        if (as_page_fault(badvaddr, access, istate) == AS_PF_OK) {
     263                pte = page_mapping_find(AS, badvaddr, true);
     264                ASSERT(pte && pte->p);
     265                ASSERT(pte->w || access != PF_ACCESS_WRITE);
     266                return pte;
     267        }
     268
     269        return NULL;
    257270}
    258271
     
    271284void tlb_prepare_entry_hi(entry_hi_t *hi, asid_t asid, uintptr_t addr)
    272285{
    273         hi->value = 0;
    274         hi->vpn2 = ADDR2VPN2(ALIGN_DOWN(addr, PAGE_SIZE));
     286        hi->value = ALIGN_DOWN(addr, PAGE_SIZE * 2);
    275287        hi->asid = asid;
    276288}
     
    279291void tlb_print(void)
    280292{
    281         page_mask_t mask, mask_save;
    282         entry_lo_t lo0, lo0_save, lo1, lo1_save;
     293        page_mask_t mask;
     294        entry_lo_t lo0, lo1;
    283295        entry_hi_t hi, hi_save;
    284296        unsigned int i;
    285297
    286298        hi_save.value = cp0_entry_hi_read();
    287         lo0_save.value = cp0_entry_lo0_read();
    288         lo1_save.value = cp0_entry_lo1_read();
    289         mask_save.value = cp0_pagemask_read();
    290        
    291         printf("[nr] [asid] [vpn2    ] [mask] [gvdc] [pfn     ]\n");
     299       
     300        printf("[nr] [asid] [vpn2] [mask] [gvdc] [pfn ]\n");
    292301       
    293302        for (i = 0; i < TLB_ENTRY_COUNT; i++) {
     
    300309                lo1.value = cp0_entry_lo1_read();
    301310               
    302                 printf("%-4u %-6u %0#10x %-#6x  %1u%1u%1u%1u  %0#10x\n",
    303                     i, hi.asid, VPN22ADDR(hi.vpn2), mask.mask,
    304                     lo0.g, lo0.v, lo0.d, lo0.c, PFN2ADDR(lo0.pfn));
    305                 printf("                               %1u%1u%1u%1u  %0#10x\n",
    306                     lo1.g, lo1.v, lo1.d, lo1.c, PFN2ADDR(lo1.pfn));
     311                printf("%-4u %-6u %#6x %#6x  %1u%1u%1u%1u  %#6x\n",
     312                    i, hi.asid, hi.vpn2, mask.mask,
     313                    lo0.g, lo0.v, lo0.d, lo0.c, lo0.pfn);
     314                printf("                           %1u%1u%1u%1u  %#6x\n",
     315                    lo1.g, lo1.v, lo1.d, lo1.c, lo1.pfn);
    307316        }
    308317       
    309318        cp0_entry_hi_write(hi_save.value);
    310         cp0_entry_lo0_write(lo0_save.value);
    311         cp0_entry_lo1_write(lo1_save.value);
    312         cp0_pagemask_write(mask_save.value);
    313319}
    314320
     
    316322void tlb_invalidate_all(void)
    317323{
     324        ipl_t ipl;
    318325        entry_lo_t lo0, lo1;
    319326        entry_hi_t hi_save;
    320327        int i;
    321328
    322         ASSERT(interrupts_disabled());
    323 
    324329        hi_save.value = cp0_entry_hi_read();
     330        ipl = interrupts_disable();
    325331
    326332        for (i = TLB_WIRED; i < TLB_ENTRY_COUNT; i++) {
     
    340346        }
    341347       
     348        interrupts_restore(ipl);
    342349        cp0_entry_hi_write(hi_save.value);
    343350}
     
    349356void tlb_invalidate_asid(asid_t asid)
    350357{
     358        ipl_t ipl;
    351359        entry_lo_t lo0, lo1;
    352360        entry_hi_t hi, hi_save;
    353361        int i;
    354362
    355         ASSERT(interrupts_disabled());
    356363        ASSERT(asid != ASID_INVALID);
    357364
    358365        hi_save.value = cp0_entry_hi_read();
     366        ipl = interrupts_disable();
    359367       
    360368        for (i = 0; i < TLB_ENTRY_COUNT; i++) {
     
    378386        }
    379387       
     388        interrupts_restore(ipl);
    380389        cp0_entry_hi_write(hi_save.value);
    381390}
     
    391400{
    392401        unsigned int i;
     402        ipl_t ipl;
    393403        entry_lo_t lo0, lo1;
    394404        entry_hi_t hi, hi_save;
    395405        tlb_index_t index;
    396 
    397         ASSERT(interrupts_disabled());
    398406       
    399407        if (asid == ASID_INVALID)
     
    401409
    402410        hi_save.value = cp0_entry_hi_read();
     411        ipl = interrupts_disable();
    403412
    404413        for (i = 0; i < cnt + 1; i += 2) {
     414                hi.value = 0;
    405415                tlb_prepare_entry_hi(&hi, asid, page + i * PAGE_SIZE);
    406416                cp0_entry_hi_write(hi.value);
     
    429439        }
    430440       
     441        interrupts_restore(ipl);
    431442        cp0_entry_hi_write(hi_save.value);
    432443}
Note: See TracChangeset for help on using the changeset viewer.