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


Ignore:
Timestamp:
2013-03-24T15:34:29Z (11 years ago)
Author:
Vojtech Horky <vojtechhorky@…>
Branches:
lfn, master, serial, ticket/834-toolchain-update, topic/msim-upgrade, topic/simplify-dev-export
Children:
8a26f82
Parents:
b92a0ee (diff), 119b46e (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

    rb92a0ee rb183ce0a  
    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;
     96        uintptr_t page;
    8997        pte_t *pte;
    9098       
    9199        badvaddr = cp0_badvaddr_read();
    92         asid = AS->asid;
    93        
    94         pte = find_mapping_and_check(badvaddr, PF_ACCESS_READ, istate);
    95         if (pte) {
     100        page = ALIGN_DOWN(badvaddr, PAGE_SIZE);
     101
     102        pte = page_mapping_find(AS, page, true);
     103        if (pte && pte->p) {
    96104                /*
    97105                 * Record access to PTE.
     
    99107                pte->a = 1;
    100108
    101                 tlb_prepare_entry_hi(&hi, asid, badvaddr);
    102109                tlb_prepare_entry_lo(&lo, pte->g, pte->p, pte->d,
    103110                    pte->cacheable, pte->pfn);
     
    106113                 * New entry is to be inserted into TLB
    107114                 */
    108                 cp0_entry_hi_write(hi.value);
    109                 if ((badvaddr / PAGE_SIZE) % 2 == 0) {
     115                if (BANK_SELECT_BIT(badvaddr) == 0) {
    110116                        cp0_entry_lo0_write(lo.value);
    111117                        cp0_entry_lo1_write(0);
     
    116122                cp0_pagemask_write(TLB_PAGE_MASK_16K);
    117123                tlbwr();
    118         }
     124                return;
     125        }
     126
     127        (void) as_page_fault(page, PF_ACCESS_READ, istate);
    119128}
    120129
     
    125134void tlb_invalid(istate_t *istate)
    126135{
     136        entry_lo_t lo;
    127137        tlb_index_t index;
    128138        uintptr_t badvaddr;
    129         entry_lo_t lo;
    130         entry_hi_t hi;
     139        uintptr_t page;
    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        page = ALIGN_DOWN(badvaddr, PAGE_SIZE);
     164
     165        pte = page_mapping_find(AS, page, true);
     166        if (pte && pte->p) {
    148167                /*
    149168                 * Read the faulting TLB entry.
     
    162181                 * The entry is to be updated in TLB.
    163182                 */
    164                 if ((badvaddr / PAGE_SIZE) % 2 == 0)
     183                if (BANK_SELECT_BIT(badvaddr) == 0)
    165184                        cp0_entry_lo0_write(lo.value);
    166185                else
    167186                        cp0_entry_lo1_write(lo.value);
    168                 cp0_pagemask_write(TLB_PAGE_MASK_16K);
    169187                tlbwi();
    170         }
     188                return;
     189        }
     190
     191        (void) as_page_fault(page, PF_ACCESS_READ, istate);
    171192}
    172193
     
    177198void tlb_modified(istate_t *istate)
    178199{
     200        entry_lo_t lo;
    179201        tlb_index_t index;
    180202        uintptr_t badvaddr;
    181         entry_lo_t lo;
    182         entry_hi_t hi;
     203        uintptr_t page;
    183204        pte_t *pte;
    184205
    185206        badvaddr = cp0_badvaddr_read();
     207        page = ALIGN_DOWN(badvaddr, PAGE_SIZE);
    186208
    187209        /*
    188210         * Locate the faulting entry in TLB.
    189211         */
    190         hi.value = cp0_entry_hi_read();
    191         tlb_prepare_entry_hi(&hi, hi.asid, badvaddr);
    192         cp0_entry_hi_write(hi.value);
    193212        tlbp();
    194213        index.value = cp0_index_read();
    195214
    196215        /*
    197          * Fail if the entry is not in TLB.
     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 
    198221         */
    199         ASSERT(!index.p);
    200 
    201         pte = find_mapping_and_check(badvaddr, PF_ACCESS_WRITE, istate);
    202         if (pte) {
     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) {
    203231                /*
    204232                 * Read the faulting TLB entry.
     
    218246                 * The entry is to be updated in TLB.
    219247                 */
    220                 if ((badvaddr / PAGE_SIZE) % 2 == 0)
     248                if (BANK_SELECT_BIT(badvaddr) == 0)
    221249                        cp0_entry_lo0_write(lo.value);
    222250                else
    223251                        cp0_entry_lo1_write(lo.value);
    224                 cp0_pagemask_write(TLB_PAGE_MASK_16K);
    225252                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;
     253                return;
     254        }
     255
     256        (void) as_page_fault(page, PF_ACCESS_WRITE, istate);
    270257}
    271258
     
    284271void tlb_prepare_entry_hi(entry_hi_t *hi, asid_t asid, uintptr_t addr)
    285272{
    286         hi->value = ALIGN_DOWN(addr, PAGE_SIZE * 2);
     273        hi->value = 0;
     274        hi->vpn2 = ADDR2VPN2(ALIGN_DOWN(addr, PAGE_SIZE));
    287275        hi->asid = asid;
    288276}
     
    291279void tlb_print(void)
    292280{
    293         page_mask_t mask;
    294         entry_lo_t lo0, lo1;
     281        page_mask_t mask, mask_save;
     282        entry_lo_t lo0, lo0_save, lo1, lo1_save;
    295283        entry_hi_t hi, hi_save;
    296284        unsigned int i;
    297285
    298286        hi_save.value = cp0_entry_hi_read();
    299        
    300         printf("[nr] [asid] [vpn2] [mask] [gvdc] [pfn ]\n");
     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");
    301292       
    302293        for (i = 0; i < TLB_ENTRY_COUNT; i++) {
     
    309300                lo1.value = cp0_entry_lo1_read();
    310301               
    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);
     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));
    316307        }
    317308       
    318309        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);
    319313}
    320314
     
    322316void tlb_invalidate_all(void)
    323317{
    324         ipl_t ipl;
    325318        entry_lo_t lo0, lo1;
    326319        entry_hi_t hi_save;
    327320        int i;
    328321
     322        ASSERT(interrupts_disabled());
     323
    329324        hi_save.value = cp0_entry_hi_read();
    330         ipl = interrupts_disable();
    331325
    332326        for (i = TLB_WIRED; i < TLB_ENTRY_COUNT; i++) {
     
    346340        }
    347341       
    348         interrupts_restore(ipl);
    349342        cp0_entry_hi_write(hi_save.value);
    350343}
     
    356349void tlb_invalidate_asid(asid_t asid)
    357350{
    358         ipl_t ipl;
    359351        entry_lo_t lo0, lo1;
    360352        entry_hi_t hi, hi_save;
    361353        int i;
    362354
     355        ASSERT(interrupts_disabled());
    363356        ASSERT(asid != ASID_INVALID);
    364357
    365358        hi_save.value = cp0_entry_hi_read();
    366         ipl = interrupts_disable();
    367359       
    368360        for (i = 0; i < TLB_ENTRY_COUNT; i++) {
     
    386378        }
    387379       
    388         interrupts_restore(ipl);
    389380        cp0_entry_hi_write(hi_save.value);
    390381}
     
    400391{
    401392        unsigned int i;
    402         ipl_t ipl;
    403393        entry_lo_t lo0, lo1;
    404394        entry_hi_t hi, hi_save;
    405395        tlb_index_t index;
     396
     397        ASSERT(interrupts_disabled());
    406398       
    407399        if (asid == ASID_INVALID)
     
    409401
    410402        hi_save.value = cp0_entry_hi_read();
    411         ipl = interrupts_disable();
    412403
    413404        for (i = 0; i < cnt + 1; i += 2) {
    414                 hi.value = 0;
    415405                tlb_prepare_entry_hi(&hi, asid, page + i * PAGE_SIZE);
    416406                cp0_entry_hi_write(hi.value);
     
    439429        }
    440430       
    441         interrupts_restore(ipl);
    442431        cp0_entry_hi_write(hi_save.value);
    443432}
Note: See TracChangeset for help on using the changeset viewer.