Changeset 06b0211b in mainline for kernel/arch/mips32/src/mm/tlb.c


Ignore:
Timestamp:
2013-04-29T11:29:45Z (12 years ago)
Author:
Vojtech Horky <vojtechhorky@…>
Branches:
lfn, master, serial, ticket/834-toolchain-update, topic/msim-upgrade, topic/simplify-dev-export
Children:
aa2b32c
Parents:
ba4799a (diff), df956b9b (diff)
Note: this is a merge changeset, the changes displayed below correspond to the merge itself.
Use the (diff) links above to see all the changes relative to each parent.
Message:

Merge mainline changes

File:
1 edited

Legend:

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

    rba4799a r06b0211b  
    4848#include <symtab.h>
    4949
    50 static pte_t *find_mapping_and_check(uintptr_t, int, istate_t *);
     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       
    5160
    5261/** Initialize TLB.
     
    8493{
    8594        entry_lo_t lo;
    86         entry_hi_t hi;
    87         asid_t asid;
    8895        uintptr_t badvaddr;
    8996        pte_t *pte;
    9097       
    9198        badvaddr = cp0_badvaddr_read();
    92         asid = AS->asid;
    93        
    94         pte = find_mapping_and_check(badvaddr, PF_ACCESS_READ, istate);
    95         if (pte) {
     99
     100        pte = page_mapping_find(AS, badvaddr, true);
     101        if (pte && pte->p) {
    96102                /*
    97103                 * Record access to PTE.
     
    99105                pte->a = 1;
    100106
    101                 tlb_prepare_entry_hi(&hi, asid, badvaddr);
    102107                tlb_prepare_entry_lo(&lo, pte->g, pte->p, pte->d,
    103108                    pte->cacheable, pte->pfn);
     
    106111                 * New entry is to be inserted into TLB
    107112                 */
    108                 cp0_entry_hi_write(hi.value);
    109                 if ((badvaddr / PAGE_SIZE) % 2 == 0) {
     113                if (BANK_SELECT_BIT(badvaddr) == 0) {
    110114                        cp0_entry_lo0_write(lo.value);
    111115                        cp0_entry_lo1_write(0);
     
    116120                cp0_pagemask_write(TLB_PAGE_MASK_16K);
    117121                tlbwr();
    118         }
     122                return;
     123        }
     124
     125        (void) as_page_fault(badvaddr, PF_ACCESS_READ, istate);
    119126}
    120127
     
    125132void tlb_invalid(istate_t *istate)
    126133{
     134        entry_lo_t lo;
    127135        tlb_index_t index;
    128136        uintptr_t badvaddr;
    129         entry_lo_t lo;
    130         entry_hi_t hi;
    131137        pte_t *pte;
    132 
    133         badvaddr = cp0_badvaddr_read();
    134138
    135139        /*
    136140         * Locate the faulting entry in TLB.
    137141         */
    138         hi.value = cp0_entry_hi_read();
    139         tlb_prepare_entry_hi(&hi, hi.asid, badvaddr);
    140         cp0_entry_hi_write(hi.value);
    141142        tlbp();
    142143        index.value = cp0_index_read();
    143144
     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
    144157        ASSERT(!index.p);
    145158
    146         pte = find_mapping_and_check(badvaddr, PF_ACCESS_READ, istate);
    147         if (pte) {
     159        badvaddr = cp0_badvaddr_read();
     160
     161        pte = page_mapping_find(AS, badvaddr, true);
     162        if (pte && pte->p) {
    148163                /*
    149164                 * Read the faulting TLB entry.
     
    162177                 * The entry is to be updated in TLB.
    163178                 */
    164                 if ((badvaddr / PAGE_SIZE) % 2 == 0)
     179                if (BANK_SELECT_BIT(badvaddr) == 0)
    165180                        cp0_entry_lo0_write(lo.value);
    166181                else
    167182                        cp0_entry_lo1_write(lo.value);
    168                 cp0_pagemask_write(TLB_PAGE_MASK_16K);
    169183                tlbwi();
    170         }
     184                return;
     185        }
     186
     187        (void) as_page_fault(badvaddr, PF_ACCESS_READ, istate);
    171188}
    172189
     
    177194void tlb_modified(istate_t *istate)
    178195{
     196        entry_lo_t lo;
    179197        tlb_index_t index;
    180198        uintptr_t badvaddr;
    181         entry_lo_t lo;
    182         entry_hi_t hi;
    183199        pte_t *pte;
    184200
     
    188204         * Locate the faulting entry in TLB.
    189205         */
    190         hi.value = cp0_entry_hi_read();
    191         tlb_prepare_entry_hi(&hi, hi.asid, badvaddr);
    192         cp0_entry_hi_write(hi.value);
    193206        tlbp();
    194207        index.value = cp0_index_read();
    195208
    196209        /*
    197          * 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 
    198215         */
    199         ASSERT(!index.p);
    200 
    201         pte = find_mapping_and_check(badvaddr, PF_ACCESS_WRITE, istate);
    202         if (pte) {
     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) {
    203225                /*
    204226                 * Read the faulting TLB entry.
     
    218240                 * The entry is to be updated in TLB.
    219241                 */
    220                 if ((badvaddr / PAGE_SIZE) % 2 == 0)
     242                if (BANK_SELECT_BIT(badvaddr) == 0)
    221243                        cp0_entry_lo0_write(lo.value);
    222244                else
    223245                        cp0_entry_lo1_write(lo.value);
    224                 cp0_pagemask_write(TLB_PAGE_MASK_16K);
    225246                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;
     247                return;
     248        }
     249
     250        (void) as_page_fault(badvaddr, PF_ACCESS_WRITE, istate);
    270251}
    271252
     
    284265void tlb_prepare_entry_hi(entry_hi_t *hi, asid_t asid, uintptr_t addr)
    285266{
    286         hi->value = ALIGN_DOWN(addr, PAGE_SIZE * 2);
     267        hi->value = 0;
     268        hi->vpn2 = ADDR2VPN2(ALIGN_DOWN(addr, PAGE_SIZE));
    287269        hi->asid = asid;
    288270}
     
    291273void tlb_print(void)
    292274{
    293         page_mask_t mask;
    294         entry_lo_t lo0, lo1;
     275        page_mask_t mask, mask_save;
     276        entry_lo_t lo0, lo0_save, lo1, lo1_save;
    295277        entry_hi_t hi, hi_save;
    296278        unsigned int i;
    297279
    298280        hi_save.value = cp0_entry_hi_read();
    299        
    300         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");
    301286       
    302287        for (i = 0; i < TLB_ENTRY_COUNT; i++) {
     
    309294                lo1.value = cp0_entry_lo1_read();
    310295               
    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);
     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));
    316301        }
    317302       
    318303        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);
    319307}
    320308
     
    322310void tlb_invalidate_all(void)
    323311{
    324         ipl_t ipl;
    325312        entry_lo_t lo0, lo1;
    326313        entry_hi_t hi_save;
    327314        int i;
    328315
     316        ASSERT(interrupts_disabled());
     317
    329318        hi_save.value = cp0_entry_hi_read();
    330         ipl = interrupts_disable();
    331319
    332320        for (i = TLB_WIRED; i < TLB_ENTRY_COUNT; i++) {
     
    346334        }
    347335       
    348         interrupts_restore(ipl);
    349336        cp0_entry_hi_write(hi_save.value);
    350337}
     
    356343void tlb_invalidate_asid(asid_t asid)
    357344{
    358         ipl_t ipl;
    359345        entry_lo_t lo0, lo1;
    360346        entry_hi_t hi, hi_save;
    361347        int i;
    362348
     349        ASSERT(interrupts_disabled());
    363350        ASSERT(asid != ASID_INVALID);
    364351
    365352        hi_save.value = cp0_entry_hi_read();
    366         ipl = interrupts_disable();
    367353       
    368354        for (i = 0; i < TLB_ENTRY_COUNT; i++) {
     
    386372        }
    387373       
    388         interrupts_restore(ipl);
    389374        cp0_entry_hi_write(hi_save.value);
    390375}
     
    400385{
    401386        unsigned int i;
    402         ipl_t ipl;
    403387        entry_lo_t lo0, lo1;
    404388        entry_hi_t hi, hi_save;
    405389        tlb_index_t index;
     390
     391        ASSERT(interrupts_disabled());
    406392       
    407393        if (asid == ASID_INVALID)
     
    409395
    410396        hi_save.value = cp0_entry_hi_read();
    411         ipl = interrupts_disable();
    412397
    413398        for (i = 0; i < cnt + 1; i += 2) {
    414                 hi.value = 0;
    415399                tlb_prepare_entry_hi(&hi, asid, page + i * PAGE_SIZE);
    416400                cp0_entry_hi_write(hi.value);
     
    439423        }
    440424       
    441         interrupts_restore(ipl);
    442425        cp0_entry_hi_write(hi_save.value);
    443426}
Note: See TracChangeset for help on using the changeset viewer.