Ignore:
File:
1 edited

Legend:

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

    r1dbc43f rb2fa1204  
    4343#include <synch/mutex.h>
    4444#include <print.h>
     45#include <log.h>
    4546#include <debug.h>
    4647#include <align.h>
     
    4849#include <symtab.h>
    4950
    50 static pte_t *find_mapping_and_check(uintptr_t, int, istate_t *);
     51#define PFN_SHIFT  12
     52#define VPN_SHIFT  12
     53
     54#define ADDR2HI_VPN(a)   ((a) >> VPN_SHIFT)
     55#define ADDR2HI_VPN2(a)  (ADDR2HI_VPN((a)) >> 1)
     56
     57#define HI_VPN2ADDR(vpn)    ((vpn) << VPN_SHIFT)
     58#define HI_VPN22ADDR(vpn2)  (HI_VPN2ADDR(vpn2) << 1)
     59
     60#define LO_PFN2ADDR(pfn)  ((pfn) << PFN_SHIFT)
     61
     62#define BANK_SELECT_BIT(a)  (((a) >> PAGE_WIDTH) & 1)
    5163
    5264/** Initialize TLB.
     
    8496{
    8597        entry_lo_t lo;
    86         entry_hi_t hi;
    87         asid_t asid;
    8898        uintptr_t badvaddr;
    8999        pte_t *pte;
    90100       
    91101        badvaddr = cp0_badvaddr_read();
    92         asid = AS->asid;
    93        
    94         pte = find_mapping_and_check(badvaddr, PF_ACCESS_READ, istate);
    95         if (pte) {
     102
     103        pte = page_mapping_find(AS, badvaddr, true);
     104        if (pte && pte->p) {
    96105                /*
    97106                 * Record access to PTE.
     
    99108                pte->a = 1;
    100109
    101                 tlb_prepare_entry_hi(&hi, asid, badvaddr);
    102110                tlb_prepare_entry_lo(&lo, pte->g, pte->p, pte->d,
    103111                    pte->cacheable, pte->pfn);
     
    106114                 * New entry is to be inserted into TLB
    107115                 */
    108                 cp0_entry_hi_write(hi.value);
    109                 if ((badvaddr / PAGE_SIZE) % 2 == 0) {
     116                if (BANK_SELECT_BIT(badvaddr) == 0) {
    110117                        cp0_entry_lo0_write(lo.value);
    111118                        cp0_entry_lo1_write(0);
     
    116123                cp0_pagemask_write(TLB_PAGE_MASK_16K);
    117124                tlbwr();
    118         }
     125                return;
     126        }
     127
     128        (void) as_page_fault(badvaddr, PF_ACCESS_READ, istate);
    119129}
    120130
     
    125135void tlb_invalid(istate_t *istate)
    126136{
     137        entry_lo_t lo;
    127138        tlb_index_t index;
    128139        uintptr_t badvaddr;
    129         entry_lo_t lo;
    130         entry_hi_t hi;
    131140        pte_t *pte;
    132 
    133         badvaddr = cp0_badvaddr_read();
    134141
    135142        /*
    136143         * Locate the faulting entry in TLB.
    137144         */
    138         hi.value = cp0_entry_hi_read();
    139         tlb_prepare_entry_hi(&hi, hi.asid, badvaddr);
    140         cp0_entry_hi_write(hi.value);
    141145        tlbp();
    142146        index.value = cp0_index_read();
    143147
     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
    144160        ASSERT(!index.p);
    145161
    146         pte = find_mapping_and_check(badvaddr, PF_ACCESS_READ, istate);
    147         if (pte) {
     162        badvaddr = cp0_badvaddr_read();
     163
     164        pte = page_mapping_find(AS, badvaddr, true);
     165        if (pte && pte->p) {
    148166                /*
    149167                 * Read the faulting TLB entry.
     
    162180                 * The entry is to be updated in TLB.
    163181                 */
    164                 if ((badvaddr / PAGE_SIZE) % 2 == 0)
     182                if (BANK_SELECT_BIT(badvaddr) == 0)
    165183                        cp0_entry_lo0_write(lo.value);
    166184                else
    167185                        cp0_entry_lo1_write(lo.value);
    168                 cp0_pagemask_write(TLB_PAGE_MASK_16K);
    169186                tlbwi();
    170         }
     187                return;
     188        }
     189
     190        (void) as_page_fault(badvaddr, PF_ACCESS_READ, istate);
    171191}
    172192
     
    177197void tlb_modified(istate_t *istate)
    178198{
     199        entry_lo_t lo;
    179200        tlb_index_t index;
    180201        uintptr_t badvaddr;
    181         entry_lo_t lo;
    182         entry_hi_t hi;
    183202        pte_t *pte;
    184203
     
    188207         * Locate the faulting entry in TLB.
    189208         */
    190         hi.value = cp0_entry_hi_read();
    191         tlb_prepare_entry_hi(&hi, hi.asid, badvaddr);
    192         cp0_entry_hi_write(hi.value);
    193209        tlbp();
    194210        index.value = cp0_index_read();
    195211
    196212        /*
    197          * Fail if the entry is not in TLB.
     213         * Emit warning if the entry is not in TLB.
     214         *
     215         * We do not assert on this because this could be a manifestation of
     216         * an emulator bug, such as QEMU Bug #1128935:
     217         * https://bugs.launchpad.net/qemu/+bug/1128935 
    198218         */
    199         ASSERT(!index.p);
    200 
    201         pte = find_mapping_and_check(badvaddr, PF_ACCESS_WRITE, istate);
    202         if (pte) {
     219        if (index.p) {
     220                log(LF_ARCH, LVL_WARN, "%s: TLBP failed in exception handler (badvaddr=%#"
     221                    PRIxn ", ASID=%d).\n", __func__, badvaddr,
     222                    AS ? AS->asid : -1);
     223                return;
     224        }
     225
     226        pte = page_mapping_find(AS, badvaddr, true);
     227        if (pte && pte->p && pte->w) {
    203228                /*
    204229                 * Read the faulting TLB entry.
     
    218243                 * The entry is to be updated in TLB.
    219244                 */
    220                 if ((badvaddr / PAGE_SIZE) % 2 == 0)
     245                if (BANK_SELECT_BIT(badvaddr) == 0)
    221246                        cp0_entry_lo0_write(lo.value);
    222247                else
    223248                        cp0_entry_lo1_write(lo.value);
    224                 cp0_pagemask_write(TLB_PAGE_MASK_16K);
    225249                tlbwi();
    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  */
    237 pte_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;
     250                return;
     251        }
     252
     253        (void) as_page_fault(badvaddr, PF_ACCESS_WRITE, istate);
    270254}
    271255
     
    284268void tlb_prepare_entry_hi(entry_hi_t *hi, asid_t asid, uintptr_t addr)
    285269{
    286         hi->value = ALIGN_DOWN(addr, PAGE_SIZE * 2);
     270        hi->value = 0;
     271        hi->vpn2 = ADDR2HI_VPN2(ALIGN_DOWN(addr, PAGE_SIZE));
    287272        hi->asid = asid;
    288273}
     
    291276void tlb_print(void)
    292277{
    293         page_mask_t mask;
    294         entry_lo_t lo0, lo1;
     278        page_mask_t mask, mask_save;
     279        entry_lo_t lo0, lo0_save, lo1, lo1_save;
    295280        entry_hi_t hi, hi_save;
    296281        unsigned int i;
    297282
    298283        hi_save.value = cp0_entry_hi_read();
    299        
    300         printf("[nr] [asid] [vpn2] [mask] [gvdc] [pfn ]\n");
     284        lo0_save.value = cp0_entry_lo0_read();
     285        lo1_save.value = cp0_entry_lo1_read();
     286        mask_save.value = cp0_pagemask_read();
     287       
     288        printf("[nr] [asid] [vpn2    ] [mask] [gvdc] [pfn     ]\n");
    301289       
    302290        for (i = 0; i < TLB_ENTRY_COUNT; i++) {
     
    309297                lo1.value = cp0_entry_lo1_read();
    310298               
    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);
     299                printf("%-4u %-6u %0#10x %-#6x  %1u%1u%1u%1u  %0#10x\n",
     300                    i, hi.asid, HI_VPN22ADDR(hi.vpn2), mask.mask,
     301                    lo0.g, lo0.v, lo0.d, lo0.c, LO_PFN2ADDR(lo0.pfn));
     302                printf("                               %1u%1u%1u%1u  %0#10x\n",
     303                    lo1.g, lo1.v, lo1.d, lo1.c, LO_PFN2ADDR(lo1.pfn));
    316304        }
    317305       
    318306        cp0_entry_hi_write(hi_save.value);
     307        cp0_entry_lo0_write(lo0_save.value);
     308        cp0_entry_lo1_write(lo1_save.value);
     309        cp0_pagemask_write(mask_save.value);
    319310}
    320311
     
    322313void tlb_invalidate_all(void)
    323314{
    324         ipl_t ipl;
    325315        entry_lo_t lo0, lo1;
    326316        entry_hi_t hi_save;
    327317        int i;
    328318
     319        ASSERT(interrupts_disabled());
     320
    329321        hi_save.value = cp0_entry_hi_read();
    330         ipl = interrupts_disable();
    331322
    332323        for (i = TLB_WIRED; i < TLB_ENTRY_COUNT; i++) {
     
    346337        }
    347338       
    348         interrupts_restore(ipl);
    349339        cp0_entry_hi_write(hi_save.value);
    350340}
     
    356346void tlb_invalidate_asid(asid_t asid)
    357347{
    358         ipl_t ipl;
    359348        entry_lo_t lo0, lo1;
    360349        entry_hi_t hi, hi_save;
    361350        int i;
    362351
     352        ASSERT(interrupts_disabled());
    363353        ASSERT(asid != ASID_INVALID);
    364354
    365355        hi_save.value = cp0_entry_hi_read();
    366         ipl = interrupts_disable();
    367356       
    368357        for (i = 0; i < TLB_ENTRY_COUNT; i++) {
     
    386375        }
    387376       
    388         interrupts_restore(ipl);
    389377        cp0_entry_hi_write(hi_save.value);
    390378}
     
    400388{
    401389        unsigned int i;
    402         ipl_t ipl;
    403390        entry_lo_t lo0, lo1;
    404391        entry_hi_t hi, hi_save;
    405392        tlb_index_t index;
     393
     394        ASSERT(interrupts_disabled());
    406395       
    407396        if (asid == ASID_INVALID)
     
    409398
    410399        hi_save.value = cp0_entry_hi_read();
    411         ipl = interrupts_disable();
    412400
    413401        for (i = 0; i < cnt + 1; i += 2) {
    414                 hi.value = 0;
    415402                tlb_prepare_entry_hi(&hi, asid, page + i * PAGE_SIZE);
    416403                cp0_entry_hi_write(hi.value);
     
    439426        }
    440427       
    441         interrupts_restore(ipl);
    442428        cp0_entry_hi_write(hi_save.value);
    443429}
Note: See TracChangeset for help on using the changeset viewer.