Subversion Repositories HelenOS-historic

Rev

Rev 612 | Rev 687 | Go to most recent revision | Blame | Compare with Previous | Last modification | View Log | Download | RSS feed

  1. /*
  2.  * Copyright (C) 2003-2004 Jakub Jermar
  3.  * All rights reserved.
  4.  *
  5.  * Redistribution and use in source and binary forms, with or without
  6.  * modification, are permitted provided that the following conditions
  7.  * are met:
  8.  *
  9.  * - Redistributions of source code must retain the above copyright
  10.  *   notice, this list of conditions and the following disclaimer.
  11.  * - Redistributions in binary form must reproduce the above copyright
  12.  *   notice, this list of conditions and the following disclaimer in the
  13.  *   documentation and/or other materials provided with the distribution.
  14.  * - The name of the author may not be used to endorse or promote products
  15.  *   derived from this software without specific prior written permission.
  16.  *
  17.  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
  18.  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
  19.  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
  20.  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
  21.  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
  22.  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
  23.  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
  24.  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  25.  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
  26.  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  27.  */
  28.  
  29. #include <arch/mm/tlb.h>
  30. #include <arch/mm/asid.h>
  31. #include <mm/tlb.h>
  32. #include <mm/page.h>
  33. #include <mm/vm.h>
  34. #include <arch/cp0.h>
  35. #include <panic.h>
  36. #include <arch.h>
  37. #include <symtab.h>
  38. #include <synch/spinlock.h>
  39. #include <print.h>
  40. #include <debug.h>
  41.  
  42. static void tlb_refill_fail(struct exception_regdump *pstate);
  43. static void tlb_invalid_fail(struct exception_regdump *pstate);
  44. static void tlb_modified_fail(struct exception_regdump *pstate);
  45.  
  46. static pte_t *find_mapping_and_check(__address badvaddr);
  47.  
  48. static void prepare_entry_lo(entry_lo_t *lo, bool g, bool v, bool d, int c, __address pfn);
  49. static void prepare_entry_hi(entry_hi_t *hi, asid_t asid, __address addr);
  50.  
  51. /** Initialize TLB
  52.  *
  53.  * Initialize TLB.
  54.  * Invalidate all entries and mark wired entries.
  55.  */
  56. void tlb_arch_init(void)
  57. {
  58.     int i;
  59.  
  60.     cp0_pagemask_write(TLB_PAGE_MASK_16K);
  61.     cp0_entry_hi_write(0);
  62.     cp0_entry_lo0_write(0);
  63.     cp0_entry_lo1_write(0);
  64.  
  65.     /* Clear and initialize TLB. */
  66.    
  67.     for (i = 0; i < TLB_ENTRY_COUNT; i++) {
  68.         cp0_index_write(i);
  69.         tlbwi();
  70.     }
  71.  
  72.        
  73.     /*
  74.      * The kernel is going to make use of some wired
  75.      * entries (e.g. mapping kernel stacks in kseg3).
  76.      */
  77.     cp0_wired_write(TLB_WIRED);
  78. }
  79.  
  80. /** Process TLB Refill Exception
  81.  *
  82.  * Process TLB Refill Exception.
  83.  *
  84.  * @param pstate Interrupted register context.
  85.  */
  86. void tlb_refill(struct exception_regdump *pstate)
  87. {
  88.     entry_lo_t lo;
  89.     entry_hi_t hi; 
  90.     __address badvaddr;
  91.     pte_t *pte;
  92.  
  93.     badvaddr = cp0_badvaddr_read();
  94.  
  95.     spinlock_lock(&VM->lock);      
  96.  
  97.     pte = find_mapping_and_check(badvaddr);
  98.     if (!pte)
  99.         goto fail;
  100.  
  101.     /*
  102.      * Record access to PTE.
  103.      */
  104.     pte->a = 1;
  105.  
  106.     prepare_entry_hi(&hi, VM->asid, badvaddr);
  107.     prepare_entry_lo(&lo, pte->lo.g, pte->lo.v, pte->lo.d, pte->lo.c, pte->lo.pfn);
  108.  
  109.     /*
  110.      * New entry is to be inserted into TLB
  111.      */
  112.     cp0_entry_hi_write(hi.value);
  113.     if ((badvaddr/PAGE_SIZE) % 2 == 0) {
  114.         cp0_entry_lo0_write(lo.value);
  115.         cp0_entry_lo1_write(0);
  116.     }
  117.     else {
  118.         cp0_entry_lo0_write(0);
  119.         cp0_entry_lo1_write(lo.value);
  120.     }
  121.     cp0_pagemask_write(TLB_PAGE_MASK_16K);
  122.     tlbwr();
  123.  
  124.     spinlock_unlock(&VM->lock);
  125.     return;
  126.    
  127. fail:
  128.     spinlock_unlock(&VM->lock);
  129.     tlb_refill_fail(pstate);
  130. }
  131.  
  132. /** Process TLB Invalid Exception
  133.  *
  134.  * Process TLB Invalid Exception.
  135.  *
  136.  * @param pstate Interrupted register context.
  137.  */
  138. void tlb_invalid(struct exception_regdump *pstate)
  139. {
  140.     tlb_index_t index;
  141.     __address badvaddr;
  142.     entry_lo_t lo;
  143.     entry_hi_t hi;
  144.     pte_t *pte;
  145.  
  146.     badvaddr = cp0_badvaddr_read();
  147.  
  148.     /*
  149.      * Locate the faulting entry in TLB.
  150.      */
  151.     hi.value = cp0_entry_hi_read();
  152.     prepare_entry_hi(&hi, hi.asid, badvaddr);
  153.     cp0_entry_hi_write(hi.value);
  154.     tlbp();
  155.     index.value = cp0_index_read();
  156.    
  157.     spinlock_lock(&VM->lock);  
  158.    
  159.     /*
  160.      * Fail if the entry is not in TLB.
  161.      */
  162.     if (index.p) {
  163.         printf("TLB entry not found.\n");
  164.         goto fail;
  165.     }
  166.  
  167.     pte = find_mapping_and_check(badvaddr);
  168.     if (!pte)
  169.         goto fail;
  170.  
  171.     /*
  172.      * Read the faulting TLB entry.
  173.      */
  174.     tlbr();
  175.  
  176.     /*
  177.      * Record access to PTE.
  178.      */
  179.     pte->a = 1;
  180.  
  181.     prepare_entry_lo(&lo, pte->lo.g, pte->lo.v, pte->lo.d, pte->lo.c, pte->lo.pfn);
  182.  
  183.     /*
  184.      * The entry is to be updated in TLB.
  185.      */
  186.     if ((badvaddr/PAGE_SIZE) % 2 == 0)
  187.         cp0_entry_lo0_write(lo.value);
  188.     else
  189.         cp0_entry_lo1_write(lo.value);
  190.     cp0_pagemask_write(TLB_PAGE_MASK_16K);
  191.     tlbwi();
  192.  
  193.     spinlock_unlock(&VM->lock);
  194.     return;
  195.    
  196. fail:
  197.     spinlock_unlock(&VM->lock);
  198.     tlb_invalid_fail(pstate);
  199. }
  200.  
  201. /** Process TLB Modified Exception
  202.  *
  203.  * Process TLB Modified Exception.
  204.  *
  205.  * @param pstate Interrupted register context.
  206.  */
  207. void tlb_modified(struct exception_regdump *pstate)
  208. {
  209.     tlb_index_t index;
  210.     __address badvaddr;
  211.     entry_lo_t lo;
  212.     entry_hi_t hi;
  213.     pte_t *pte;
  214.  
  215.     badvaddr = cp0_badvaddr_read();
  216.  
  217.     /*
  218.      * Locate the faulting entry in TLB.
  219.      */
  220.     hi.value = cp0_entry_hi_read();
  221.     prepare_entry_hi(&hi, hi.asid, badvaddr);
  222.     cp0_entry_hi_write(hi.value);
  223.     tlbp();
  224.     index.value = cp0_index_read();
  225.    
  226.     spinlock_lock(&VM->lock);  
  227.    
  228.     /*
  229.      * Fail if the entry is not in TLB.
  230.      */
  231.     if (index.p) {
  232.         printf("TLB entry not found.\n");
  233.         goto fail;
  234.     }
  235.  
  236.     pte = find_mapping_and_check(badvaddr);
  237.     if (!pte)
  238.         goto fail;
  239.  
  240.     /*
  241.      * Fail if the page is not writable.
  242.      */
  243.     if (!pte->w)
  244.         goto fail;
  245.  
  246.     /*
  247.      * Read the faulting TLB entry.
  248.      */
  249.     tlbr();
  250.  
  251.     /*
  252.      * Record access and write to PTE.
  253.      */
  254.     pte->a = 1;
  255.     pte->lo.d = 1;
  256.  
  257.     prepare_entry_lo(&lo, pte->lo.g, pte->lo.v, pte->w, pte->lo.c, pte->lo.pfn);
  258.  
  259.     /*
  260.      * The entry is to be updated in TLB.
  261.      */
  262.     if ((badvaddr/PAGE_SIZE) % 2 == 0)
  263.         cp0_entry_lo0_write(lo.value);
  264.     else
  265.         cp0_entry_lo1_write(lo.value);
  266.     cp0_pagemask_write(TLB_PAGE_MASK_16K);
  267.     tlbwi();
  268.  
  269.     spinlock_unlock(&VM->lock);
  270.     return;
  271.    
  272. fail:
  273.     spinlock_unlock(&VM->lock);
  274.     tlb_modified_fail(pstate);
  275. }
  276.  
  277. void tlb_refill_fail(struct exception_regdump *pstate)
  278. {
  279.     char *symbol = "";
  280.     char *sym2 = "";
  281.  
  282.     char *s = get_symtab_entry(pstate->epc);
  283.     if (s)
  284.         symbol = s;
  285.     s = get_symtab_entry(pstate->ra);
  286.     if (s)
  287.         sym2 = s;
  288.     panic("%X: TLB Refill Exception at %X(%s<-%s)\n", cp0_badvaddr_read(), pstate->epc, symbol, sym2);
  289. }
  290.  
  291.  
  292. void tlb_invalid_fail(struct exception_regdump *pstate)
  293. {
  294.     char *symbol = "";
  295.  
  296.     char *s = get_symtab_entry(pstate->epc);
  297.     if (s)
  298.         symbol = s;
  299.     panic("%X: TLB Invalid Exception at %X(%s)\n", cp0_badvaddr_read(), pstate->epc, symbol);
  300. }
  301.  
  302. void tlb_modified_fail(struct exception_regdump *pstate)
  303. {
  304.     char *symbol = "";
  305.  
  306.     char *s = get_symtab_entry(pstate->epc);
  307.     if (s)
  308.         symbol = s;
  309.     panic("%X: TLB Modified Exception at %X(%s)\n", cp0_badvaddr_read(), pstate->epc, symbol);
  310. }
  311.  
  312. /** Try to find PTE for faulting address
  313.  *
  314.  * Try to find PTE for faulting address.
  315.  * The VM->lock must be held on entry to this function.
  316.  *
  317.  * @param badvaddr Faulting virtual address.
  318.  *
  319.  * @return PTE on success, NULL otherwise.
  320.  */
  321. pte_t *find_mapping_and_check(__address badvaddr)
  322. {
  323.     entry_hi_t hi;
  324.     pte_t *pte;
  325.  
  326.     hi.value = cp0_entry_hi_read();
  327.  
  328.     /*
  329.      * Handler cannot succeed if the ASIDs don't match.
  330.      */
  331.     if (hi.asid != VM->asid) {
  332.         printf("EntryHi.asid=%d, VM->asid=%d\n", hi.asid, VM->asid);
  333.         return NULL;
  334.     }
  335.    
  336.     /*
  337.      * Handler cannot succeed if badvaddr has no mapping.
  338.      */
  339.     pte = page_mapping_find(badvaddr, 0);
  340.     if (!pte) {
  341.         printf("No such mapping.\n");
  342.         return NULL;
  343.     }
  344.  
  345.     /*
  346.      * Handler cannot succeed if the mapping is marked as invalid.
  347.      */
  348.     if (!pte->lo.v) {
  349.         printf("Invalid mapping.\n");
  350.         return NULL;
  351.     }
  352.  
  353.     return pte;
  354. }
  355.  
  356. void prepare_entry_lo(entry_lo_t *lo, bool g, bool v, bool d, int c, __address pfn)
  357. {
  358.     lo->value = 0;
  359.     lo->g = g;
  360.     lo->v = v;
  361.     lo->d = d;
  362.     lo->c = c;
  363.     lo->pfn = pfn;
  364. }
  365.  
  366. void prepare_entry_hi(entry_hi_t *hi, asid_t asid, __address addr)
  367. {
  368.     hi->value = (((addr/PAGE_SIZE)/2)*PAGE_SIZE*2);
  369.     hi->asid = asid;
  370. }
  371.  
  372. /** Print contents of TLB. */
  373. void tlb_print(void)
  374. {
  375.     page_mask_t mask;
  376.     entry_lo_t lo0, lo1;
  377.     entry_hi_t hi;
  378.     int i;
  379.  
  380.     printf("TLB:\n");
  381.     for (i = 0; i < TLB_ENTRY_COUNT; i++) {
  382.         cp0_index_write(i);
  383.         tlbr();
  384.        
  385.         mask.value = cp0_pagemask_read();
  386.         hi.value = cp0_entry_hi_read();
  387.         lo0.value = cp0_entry_lo0_read();
  388.         lo1.value = cp0_entry_lo1_read();
  389.        
  390.         printf("%d: asid=%d, vpn2=%d, mask=%d\tg[0]=%d, v[0]=%d, d[0]=%d, c[0]=%B, pfn[0]=%d\n"
  391.                "\t\t\t\tg[1]=%d, v[1]=%d, d[1]=%d, c[1]=%B, pfn[1]=%d\n",
  392.                i, hi.asid, hi.vpn2, mask.mask, lo0.g, lo0.v, lo0.d, lo0.c, lo0.pfn,
  393.                lo1.g, lo1.v, lo1.d, lo1.c, lo1.pfn);
  394.     }
  395. }
  396.  
  397. /** Invalidate all not wired TLB entries. */
  398. void tlb_invalidate_all(void)
  399. {
  400.     ipl_t ipl;
  401.     entry_lo_t lo0, lo1;
  402.     int i;
  403.  
  404.     ipl = interrupts_disable();
  405.  
  406.     for (i = TLB_WIRED; i < TLB_ENTRY_COUNT; i++) {
  407.         cp0_index_write(i);
  408.         tlbr();
  409.  
  410.         lo0.value = cp0_entry_lo0_read();
  411.         lo1.value = cp0_entry_lo1_read();
  412.  
  413.         lo0.v = 0;
  414.         lo1.v = 0;
  415.  
  416.         cp0_entry_lo0_write(lo0.value);
  417.         cp0_entry_lo1_write(lo1.value);
  418.                
  419.         tlbwi();
  420.     }
  421.    
  422.     interrupts_restore(ipl);
  423. }
  424.  
  425. /** Invalidate all TLB entries belonging to specified address space.
  426.  *
  427.  * @param asid Address space identifier.
  428.  */
  429. void tlb_invalidate_asid(asid_t asid)
  430. {
  431.     ipl_t ipl;
  432.     entry_lo_t lo0, lo1;
  433.     entry_hi_t hi;
  434.     int i;
  435.  
  436.     ASSERT(asid != ASID_INVALID);
  437.  
  438.     ipl = interrupts_disable();
  439.    
  440.     for (i = 0; i < TLB_ENTRY_COUNT; i++) {
  441.         cp0_index_write(i);
  442.         tlbr();
  443.        
  444.         hi.value = cp0_entry_hi_read();
  445.        
  446.         if (hi.asid == asid) {
  447.             lo0.value = cp0_entry_lo0_read();
  448.             lo1.value = cp0_entry_lo1_read();
  449.  
  450.             lo0.v = 0;
  451.             lo1.v = 0;
  452.  
  453.             cp0_entry_lo0_write(lo0.value);
  454.             cp0_entry_lo1_write(lo1.value);
  455.  
  456.             tlbwi();
  457.         }
  458.     }
  459.    
  460.     interrupts_restore(ipl);
  461. }
  462.  
  463. /** Invalidate TLB entry for specified page belonging to specified address space.
  464.  *
  465.  * @param asid Address space identifier.
  466.  * @param page Page whose TLB entry is to be invalidated.
  467.  */
  468. void tlb_invalidate_page(asid_t asid, __address page)
  469. {
  470.     ipl_t ipl;
  471.     entry_lo_t lo0, lo1;
  472.     entry_hi_t hi;
  473.     tlb_index_t index;
  474.  
  475.     ASSERT(asid != ASID_INVALID);
  476.  
  477.     ipl = interrupts_disable();
  478.  
  479.     hi.value = 0;
  480.     prepare_entry_hi(&hi, asid, page);
  481.     cp0_entry_hi_write(hi.value);
  482.  
  483.     tlbp();
  484.     index.value = cp0_index_read();
  485.  
  486.     if (!index.p) {
  487.         /* Entry was found, index register contains valid index. */
  488.         tlbr();
  489.  
  490.         lo0.value = cp0_entry_lo0_read();
  491.         lo1.value = cp0_entry_lo1_read();
  492.  
  493.         lo0.v = 0;
  494.         lo1.v = 0;
  495.  
  496.         cp0_entry_lo0_write(lo0.value);
  497.         cp0_entry_lo1_write(lo1.value);
  498.  
  499.         tlbwi();
  500.     }
  501.    
  502.     interrupts_restore(ipl);
  503. }
  504.