Ignore:
File:
1 edited

Legend:

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

    r59fb782 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;
    9689        pte_t *pte;
    9790       
    9891        badvaddr = cp0_badvaddr_read();
    99 
    100         pte = page_mapping_find(AS, badvaddr, true);
    101         if (pte && pte->p) {
     92        asid = AS->asid;
     93       
     94        pte = find_mapping_and_check(badvaddr, PF_ACCESS_READ, istate);
     95        if (pte) {
    10296                /*
    10397                 * Record access to PTE.
     
    10599                pte->a = 1;
    106100
     101                tlb_prepare_entry_hi(&hi, asid, badvaddr);
    107102                tlb_prepare_entry_lo(&lo, pte->g, pte->p, pte->d,
    108103                    pte->cacheable, pte->pfn);
     
    111106                 * New entry is to be inserted into TLB
    112107                 */
    113                 if (BANK_SELECT_BIT(badvaddr) == 0) {
     108                cp0_entry_hi_write(hi.value);
     109                if ((badvaddr / PAGE_SIZE) % 2 == 0) {
    114110                        cp0_entry_lo0_write(lo.value);
    115111                        cp0_entry_lo1_write(0);
     
    120116                cp0_pagemask_write(TLB_PAGE_MASK_16K);
    121117                tlbwr();
    122                 return;
    123         }
    124 
    125         (void) as_page_fault(badvaddr, PF_ACCESS_READ, istate);
     118        }
    126119}
    127120
     
    132125void tlb_invalid(istate_t *istate)
    133126{
    134         entry_lo_t lo;
    135127        tlb_index_t index;
    136128        uintptr_t badvaddr;
     129        entry_lo_t lo;
     130        entry_hi_t hi;
    137131        pte_t *pte;
     132
     133        badvaddr = cp0_badvaddr_read();
    138134
    139135        /*
    140136         * Locate the faulting entry in TLB.
    141137         */
     138        hi.value = cp0_entry_hi_read();
     139        tlb_prepare_entry_hi(&hi, hi.asid, badvaddr);
     140        cp0_entry_hi_write(hi.value);
    142141        tlbp();
    143142        index.value = cp0_index_read();
    144143
    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().
    150          */
    151         if (index.p) {
    152                 tlb_refill(istate);
    153                 return;
    154         }
    155 #endif
    156 
    157144        ASSERT(!index.p);
    158145
    159         badvaddr = cp0_badvaddr_read();
    160 
    161         pte = page_mapping_find(AS, badvaddr, true);
    162         if (pte && pte->p) {
     146        pte = find_mapping_and_check(badvaddr, PF_ACCESS_READ, istate);
     147        if (pte) {
    163148                /*
    164149                 * Read the faulting TLB entry.
     
    177162                 * The entry is to be updated in TLB.
    178163                 */
    179                 if (BANK_SELECT_BIT(badvaddr) == 0)
     164                if ((badvaddr / PAGE_SIZE) % 2 == 0)
    180165                        cp0_entry_lo0_write(lo.value);
    181166                else
    182167                        cp0_entry_lo1_write(lo.value);
     168                cp0_pagemask_write(TLB_PAGE_MASK_16K);
    183169                tlbwi();
    184                 return;
    185         }
    186 
    187         (void) as_page_fault(badvaddr, PF_ACCESS_READ, istate);
     170        }
    188171}
    189172
     
    194177void tlb_modified(istate_t *istate)
    195178{
    196         entry_lo_t lo;
    197179        tlb_index_t index;
    198180        uintptr_t badvaddr;
     181        entry_lo_t lo;
     182        entry_hi_t hi;
    199183        pte_t *pte;
    200184
     
    204188         * Locate the faulting entry in TLB.
    205189         */
     190        hi.value = cp0_entry_hi_read();
     191        tlb_prepare_entry_hi(&hi, hi.asid, badvaddr);
     192        cp0_entry_hi_write(hi.value);
    206193        tlbp();
    207194        index.value = cp0_index_read();
    208195
    209196        /*
    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 
     197         * Fail if the entry is not in TLB.
    215198         */
    216         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 
    223         pte = page_mapping_find(AS, badvaddr, true);
    224         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) {
    225203                /*
    226204                 * Read the faulting TLB entry.
     
    240218                 * The entry is to be updated in TLB.
    241219                 */
    242                 if (BANK_SELECT_BIT(badvaddr) == 0)
     220                if ((badvaddr / PAGE_SIZE) % 2 == 0)
    243221                        cp0_entry_lo0_write(lo.value);
    244222                else
    245223                        cp0_entry_lo1_write(lo.value);
     224                cp0_pagemask_write(TLB_PAGE_MASK_16K);
    246225                tlbwi();
    247                 return;
    248         }
    249 
    250         (void) as_page_fault(badvaddr, 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;
    251270}
    252271
     
    265284void tlb_prepare_entry_hi(entry_hi_t *hi, asid_t asid, uintptr_t addr)
    266285{
    267         hi->value = 0;
    268         hi->vpn2 = ADDR2VPN2(ALIGN_DOWN(addr, PAGE_SIZE));
     286        hi->value = ALIGN_DOWN(addr, PAGE_SIZE * 2);
    269287        hi->asid = asid;
    270288}
     
    273291void tlb_print(void)
    274292{
    275         page_mask_t mask, mask_save;
    276         entry_lo_t lo0, lo0_save, lo1, lo1_save;
     293        page_mask_t mask;
     294        entry_lo_t lo0, lo1;
    277295        entry_hi_t hi, hi_save;
    278296        unsigned int i;
    279297
    280298        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");
     299       
     300        printf("[nr] [asid] [vpn2] [mask] [gvdc] [pfn ]\n");
    286301       
    287302        for (i = 0; i < TLB_ENTRY_COUNT; i++) {
     
    294309                lo1.value = cp0_entry_lo1_read();
    295310               
    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));
     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);
    301316        }
    302317       
    303318        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);
    307319}
    308320
     
    310322void tlb_invalidate_all(void)
    311323{
     324        ipl_t ipl;
    312325        entry_lo_t lo0, lo1;
    313326        entry_hi_t hi_save;
    314327        int i;
    315328
    316         ASSERT(interrupts_disabled());
    317 
    318329        hi_save.value = cp0_entry_hi_read();
     330        ipl = interrupts_disable();
    319331
    320332        for (i = TLB_WIRED; i < TLB_ENTRY_COUNT; i++) {
     
    334346        }
    335347       
     348        interrupts_restore(ipl);
    336349        cp0_entry_hi_write(hi_save.value);
    337350}
     
    343356void tlb_invalidate_asid(asid_t asid)
    344357{
     358        ipl_t ipl;
    345359        entry_lo_t lo0, lo1;
    346360        entry_hi_t hi, hi_save;
    347361        int i;
    348362
    349         ASSERT(interrupts_disabled());
    350363        ASSERT(asid != ASID_INVALID);
    351364
    352365        hi_save.value = cp0_entry_hi_read();
     366        ipl = interrupts_disable();
    353367       
    354368        for (i = 0; i < TLB_ENTRY_COUNT; i++) {
     
    372386        }
    373387       
     388        interrupts_restore(ipl);
    374389        cp0_entry_hi_write(hi_save.value);
    375390}
     
    385400{
    386401        unsigned int i;
     402        ipl_t ipl;
    387403        entry_lo_t lo0, lo1;
    388404        entry_hi_t hi, hi_save;
    389405        tlb_index_t index;
    390 
    391         ASSERT(interrupts_disabled());
    392406       
    393407        if (asid == ASID_INVALID)
     
    395409
    396410        hi_save.value = cp0_entry_hi_read();
     411        ipl = interrupts_disable();
    397412
    398413        for (i = 0; i < cnt + 1; i += 2) {
     414                hi.value = 0;
    399415                tlb_prepare_entry_hi(&hi, asid, page + i * PAGE_SIZE);
    400416                cp0_entry_hi_write(hi.value);
     
    423439        }
    424440       
     441        interrupts_restore(ipl);
    425442        cp0_entry_hi_write(hi_save.value);
    426443}
Note: See TracChangeset for help on using the changeset viewer.