Subversion Repositories HelenOS-historic

Rev

Rev 594 | Rev 599 | 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.     cp0_pagemask_write(TLB_PAGE_MASK_16K);
  59.  
  60.     tlb_invalidate_all();
  61.        
  62.     /*
  63.      * The kernel is going to make use of some wired
  64.      * entries (e.g. mapping kernel stacks in kseg3).
  65.      */
  66.     cp0_wired_write(TLB_WIRED);
  67. }
  68.  
  69. /** Process TLB Refill Exception
  70.  *
  71.  * Process TLB Refill Exception.
  72.  *
  73.  * @param pstate Interrupted register context.
  74.  */
  75. void tlb_refill(struct exception_regdump *pstate)
  76. {
  77.     entry_lo_t lo;
  78.     entry_hi_t hi; 
  79.     __address badvaddr;
  80.     pte_t *pte;
  81.  
  82.     badvaddr = cp0_badvaddr_read();
  83.  
  84.     spinlock_lock(&VM->lock);      
  85.  
  86.     pte = find_mapping_and_check(badvaddr);
  87.     if (!pte)
  88.         goto fail;
  89.  
  90.     /*
  91.      * Record access to PTE.
  92.      */
  93.     pte->a = 1;
  94.  
  95.     prepare_entry_hi(&hi, VM->asid, badvaddr);
  96.     prepare_entry_lo(&lo, pte->lo.g, pte->lo.v, pte->lo.d, pte->lo.c, pte->lo.pfn);
  97.  
  98.     /*
  99.      * New entry is to be inserted into TLB
  100.      */
  101.     cp0_entry_hi_write(hi.value);
  102.     if ((badvaddr/PAGE_SIZE) % 2 == 0) {
  103.         cp0_entry_lo0_write(lo.value);
  104.         cp0_entry_lo1_write(0);
  105.     }
  106.     else {
  107.         cp0_entry_lo0_write(0);
  108.         cp0_entry_lo1_write(lo.value);
  109.     }
  110.     tlbwr();
  111.  
  112.     spinlock_unlock(&VM->lock);
  113.     return;
  114.    
  115. fail:
  116.     spinlock_unlock(&VM->lock);
  117.     tlb_refill_fail(pstate);
  118. }
  119.  
  120. /** Process TLB Invalid Exception
  121.  *
  122.  * Process TLB Invalid Exception.
  123.  *
  124.  * @param pstate Interrupted register context.
  125.  */
  126. void tlb_invalid(struct exception_regdump *pstate)
  127. {
  128.     tlb_index_t index;
  129.     __address badvaddr;
  130.     entry_lo_t lo;
  131.     entry_hi_t hi;
  132.     pte_t *pte;
  133.  
  134.     badvaddr = cp0_badvaddr_read();
  135.  
  136.     /*
  137.      * Locate the faulting entry in TLB.
  138.      */
  139.     hi.value = cp0_entry_hi_read();
  140.     prepare_entry_hi(&hi, hi.asid, badvaddr);
  141.     cp0_entry_hi_write(hi.value);
  142.     tlbp();
  143.     index.value = cp0_index_read();
  144.    
  145.     spinlock_lock(&VM->lock);  
  146.    
  147.     /*
  148.      * Fail if the entry is not in TLB.
  149.      */
  150.     if (index.p) {
  151.         printf("TLB entry not found.\n");
  152.         goto fail;
  153.     }
  154.  
  155.     pte = find_mapping_and_check(badvaddr);
  156.     if (!pte)
  157.         goto fail;
  158.  
  159.     /*
  160.      * Read the faulting TLB entry.
  161.      */
  162.     tlbr();
  163.  
  164.     /*
  165.      * Record access to PTE.
  166.      */
  167.     pte->a = 1;
  168.  
  169.     prepare_entry_lo(&lo, pte->lo.g, pte->lo.v, pte->lo.d, pte->lo.c, pte->lo.pfn);
  170.  
  171.     /*
  172.      * The entry is to be updated in TLB.
  173.      */
  174.     if ((badvaddr/PAGE_SIZE) % 2 == 0)
  175.         cp0_entry_lo0_write(lo.value);
  176.     else
  177.         cp0_entry_lo1_write(lo.value);
  178.     tlbwi();
  179.  
  180.     spinlock_unlock(&VM->lock);
  181.     return;
  182.    
  183. fail:
  184.     spinlock_unlock(&VM->lock);
  185.     tlb_invalid_fail(pstate);
  186. }
  187.  
  188. /** Process TLB Modified Exception
  189.  *
  190.  * Process TLB Modified Exception.
  191.  *
  192.  * @param pstate Interrupted register context.
  193.  */
  194. void tlb_modified(struct exception_regdump *pstate)
  195. {
  196.     tlb_index_t index;
  197.     __address badvaddr;
  198.     entry_lo_t lo;
  199.     entry_hi_t hi;
  200.     pte_t *pte;
  201.  
  202.     badvaddr = cp0_badvaddr_read();
  203.  
  204.     /*
  205.      * Locate the faulting entry in TLB.
  206.      */
  207.     hi.value = cp0_entry_hi_read();
  208.     prepare_entry_hi(&hi, hi.asid, badvaddr);
  209.     cp0_entry_hi_write(hi.value);
  210.     tlbp();
  211.     index.value = cp0_index_read();
  212.    
  213.     spinlock_lock(&VM->lock);  
  214.    
  215.     /*
  216.      * Fail if the entry is not in TLB.
  217.      */
  218.     if (index.p) {
  219.         printf("TLB entry not found.\n");
  220.         goto fail;
  221.     }
  222.  
  223.     pte = find_mapping_and_check(badvaddr);
  224.     if (!pte)
  225.         goto fail;
  226.  
  227.     /*
  228.      * Fail if the page is not writable.
  229.      */
  230.     if (!pte->w)
  231.         goto fail;
  232.  
  233.     /*
  234.      * Read the faulting TLB entry.
  235.      */
  236.     tlbr();
  237.  
  238.     /*
  239.      * Record access and write to PTE.
  240.      */
  241.     pte->a = 1;
  242.     pte->lo.d = 1;
  243.  
  244.     prepare_entry_lo(&lo, pte->lo.g, pte->lo.v, pte->w, pte->lo.c, pte->lo.pfn);
  245.  
  246.     /*
  247.      * The entry is to be updated in TLB.
  248.      */
  249.     if ((badvaddr/PAGE_SIZE) % 2 == 0)
  250.         cp0_entry_lo0_write(lo.value);
  251.     else
  252.         cp0_entry_lo1_write(lo.value);
  253.     tlbwi();
  254.  
  255.     spinlock_unlock(&VM->lock);
  256.     return;
  257.    
  258. fail:
  259.     spinlock_unlock(&VM->lock);
  260.     tlb_modified_fail(pstate);
  261. }
  262.  
  263. void tlb_refill_fail(struct exception_regdump *pstate)
  264. {
  265.     char *symbol = "";
  266.     char *sym2 = "";
  267.  
  268.     char *s = get_symtab_entry(pstate->epc);
  269.     if (s)
  270.         symbol = s;
  271.     s = get_symtab_entry(pstate->ra);
  272.     if (s)
  273.         sym2 = s;
  274.     panic("%X: TLB Refill Exception at %X(%s<-%s)\n", cp0_badvaddr_read(), pstate->epc, symbol, sym2);
  275. }
  276.  
  277.  
  278. void tlb_invalid_fail(struct exception_regdump *pstate)
  279. {
  280.     char *symbol = "";
  281.  
  282.     char *s = get_symtab_entry(pstate->epc);
  283.     if (s)
  284.         symbol = s;
  285.     panic("%X: TLB Invalid Exception at %X(%s)\n", cp0_badvaddr_read(), pstate->epc, symbol);
  286. }
  287.  
  288. void tlb_modified_fail(struct exception_regdump *pstate)
  289. {
  290.     char *symbol = "";
  291.  
  292.     char *s = get_symtab_entry(pstate->epc);
  293.     if (s)
  294.         symbol = s;
  295.     panic("%X: TLB Modified Exception at %X(%s)\n", cp0_badvaddr_read(), pstate->epc, symbol);
  296. }
  297.  
  298. /** Invalidate TLB entries with specified ASID
  299.  *
  300.  * Invalidate TLB entries with specified ASID.
  301.  *
  302.  * @param asid ASID.
  303.  */
  304. void tlb_invalidate(asid_t asid)
  305. {
  306.     entry_hi_t hi;
  307.     ipl_t ipl;
  308.     int i; 
  309.    
  310.     ASSERT(asid != ASID_INVALID);
  311.  
  312.     ipl = interrupts_disable();
  313.    
  314.     for (i = 0; i < TLB_ENTRY_COUNT; i++) {
  315.         cp0_index_write(i);
  316.         tlbr();
  317.        
  318.         hi.value = cp0_entry_hi_read();
  319.         if (hi.asid == asid) {
  320.             cp0_pagemask_write(TLB_PAGE_MASK_16K);
  321.             cp0_entry_hi_write(0);
  322.             cp0_entry_lo0_write(0);
  323.             cp0_entry_lo1_write(0);
  324.             tlbwi();
  325.         }
  326.     }
  327.    
  328.     interrupts_restore(ipl);
  329. }
  330.  
  331. /** Try to find PTE for faulting address
  332.  *
  333.  * Try to find PTE for faulting address.
  334.  * The VM->lock must be held on entry to this function.
  335.  *
  336.  * @param badvaddr Faulting virtual address.
  337.  *
  338.  * @return PTE on success, NULL otherwise.
  339.  */
  340. pte_t *find_mapping_and_check(__address badvaddr)
  341. {
  342.     entry_hi_t hi;
  343.     pte_t *pte;
  344.  
  345.     hi.value = cp0_entry_hi_read();
  346.  
  347.     /*
  348.      * Handler cannot succeed if the ASIDs don't match.
  349.      */
  350.     if (hi.asid != VM->asid) {
  351.         printf("EntryHi.asid=%d, VM->asid=%d\n", hi.asid, VM->asid);
  352.         return NULL;
  353.     }
  354.    
  355.     /*
  356.      * Handler cannot succeed if badvaddr has no mapping.
  357.      */
  358.     pte = page_mapping_find(badvaddr, 0);
  359.     if (!pte) {
  360.         printf("No such mapping.\n");
  361.         return NULL;
  362.     }
  363.  
  364.     /*
  365.      * Handler cannot succeed if the mapping is marked as invalid.
  366.      */
  367.     if (!pte->lo.v) {
  368.         printf("Invalid mapping.\n");
  369.         return NULL;
  370.     }
  371.  
  372.     return pte;
  373. }
  374.  
  375. void prepare_entry_lo(entry_lo_t *lo, bool g, bool v, bool d, int c, __address pfn)
  376. {
  377.     lo->value = 0;
  378.     lo->g = g;
  379.     lo->v = v;
  380.     lo->d = d;
  381.     lo->c = c;
  382.     lo->pfn = pfn;
  383. }
  384.  
  385. void prepare_entry_hi(entry_hi_t *hi, asid_t asid, __address addr)
  386. {
  387.     hi->value = (((addr/PAGE_SIZE)/2)*PAGE_SIZE*2);
  388.     hi->asid = asid;
  389. }
  390.  
  391. /** Print contents of TLB. */
  392. void tlb_print(void)
  393. {
  394.     entry_lo_t lo0, lo1;
  395.     entry_hi_t hi;
  396.     int i;
  397.  
  398.     printf("TLB:\n");
  399.     for (i = 0; i < TLB_ENTRY_COUNT; i++) {
  400.         cp0_index_write(i);
  401.         tlbr();
  402.        
  403.         hi.value = cp0_entry_hi_read();
  404.         lo0.value = cp0_entry_lo0_read();
  405.         lo1.value = cp0_entry_lo1_read();
  406.        
  407.         printf("%d: asid=%d, vpn2=%d\tg[0]=%d, v[0]=%d, d[0]=%d, c[0]=%B, pfn[0]=%d\n"
  408.                "\t\t\tg[1]=%d, v[1]=%d, d[1]=%d, c[1]=%B, pfn[1]=%d\n",
  409.                i, hi.asid, hi.vpn2, lo0.g, lo0.v, lo0.d, lo0.c, lo0.pfn,
  410.                lo1.g, lo1.v, lo1.d, lo1.c, lo1.pfn);
  411.     }
  412. }
  413.  
  414. /** Invalidate all TLB entries. */
  415. void tlb_invalidate_all(void)
  416. {
  417.     int i;
  418.  
  419.     cp0_entry_hi_write(0);
  420.     cp0_entry_lo0_write(0);
  421.     cp0_entry_lo1_write(0);
  422.  
  423.     for (i = 0; i < TLB_ENTRY_COUNT; i++) {
  424.         cp0_index_write(i);
  425.         tlbwi();
  426.     }
  427. }
  428.  
  429. /** Invalidate all TLB entries belonging to specified address space.
  430.  *
  431.  * @param asid Address space identifier.
  432.  */
  433. void tlb_invalidate_asid(asid_t asid)
  434. {
  435.     entry_hi_t hi;
  436.     int i;
  437.  
  438.     for (i = 0; i < TLB_ENTRY_COUNT; i++) {
  439.         cp0_index_write(i);
  440.         tlbr();
  441.        
  442.         if (hi.asid == asid) {
  443.             cp0_entry_lo0_write(0);
  444.             cp0_entry_lo1_write(0);
  445.             tlbwi();
  446.         }
  447.     }
  448.  
  449. }
  450.  
  451. /** Invalidate TLB entry for specified page belonging to specified address space.
  452.  *
  453.  * @param asid Address space identifier.
  454.  * @param page Page whose TLB entry is to be invalidated.
  455.  */
  456. void tlb_invalidate_page(asid_t asid, __address page)
  457. {
  458.     entry_hi_t hi;
  459.     tlb_index_t index;
  460.     int i;
  461.  
  462.     hi.value = 0;
  463.     prepare_entry_hi(&hi, asid, page);
  464.    
  465.     tlbp();
  466.     index.value = cp0_index_read();
  467.  
  468.     if (!index.p) {
  469.         /* Entry was found, index register contains valid index. */
  470.         cp0_entry_lo0_write(0);
  471.         cp0_entry_lo1_write(0);
  472.         tlbwi();
  473.     }
  474. }
  475.