Subversion Repositories HelenOS

Rev

Rev 413 | Rev 501 | 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_init_arch(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.     /*
  66.      * Invalidate all entries.
  67.      */
  68.     for (i = 0; i < TLB_SIZE; i++) {
  69.         cp0_index_write(i);
  70.         tlbwi();
  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.     tlbwr();
  122.  
  123.     spinlock_unlock(&VM->lock);
  124.     return;
  125.    
  126. fail:
  127.     spinlock_unlock(&VM->lock);
  128.     tlb_refill_fail(pstate);
  129. }
  130.  
  131. /** Process TLB Invalid Exception
  132.  *
  133.  * Process TLB Invalid Exception.
  134.  *
  135.  * @param pstate Interrupted register context.
  136.  */
  137. void tlb_invalid(struct exception_regdump *pstate)
  138. {
  139.     tlb_index_t index;
  140.     __address badvaddr;
  141.     entry_lo_t lo;
  142.     entry_hi_t hi;
  143.     pte_t *pte;
  144.  
  145.     badvaddr = cp0_badvaddr_read();
  146.  
  147.     /*
  148.      * Locate the faulting entry in TLB.
  149.      */
  150.     hi.value = cp0_entry_hi_read();
  151.     prepare_entry_hi(&hi, hi.asid, badvaddr);
  152.     cp0_entry_hi_write(hi.value);
  153.     tlbp();
  154.     index.value = cp0_index_read();
  155.    
  156.     spinlock_lock(&VM->lock);  
  157.    
  158.     /*
  159.      * Fail if the entry is not in TLB.
  160.      */
  161.     if (index.p) {
  162.         printf("TLB entry not found.\n");
  163.         goto fail;
  164.     }
  165.  
  166.     pte = find_mapping_and_check(badvaddr);
  167.     if (!pte)
  168.         goto fail;
  169.  
  170.     /*
  171.      * Read the faulting TLB entry.
  172.      */
  173.     tlbr();
  174.  
  175.     /*
  176.      * Record access to PTE.
  177.      */
  178.     pte->a = 1;
  179.  
  180.     prepare_entry_lo(&lo, pte->lo.g, pte->lo.v, pte->lo.d, pte->lo.c, pte->lo.pfn);
  181.  
  182.     /*
  183.      * The entry is to be updated in TLB.
  184.      */
  185.     if ((badvaddr/PAGE_SIZE) % 2 == 0)
  186.         cp0_entry_lo0_write(lo.value);
  187.     else
  188.         cp0_entry_lo1_write(lo.value);
  189.     tlbwi();
  190.  
  191.     spinlock_unlock(&VM->lock);
  192.     return;
  193.    
  194. fail:
  195.     spinlock_unlock(&VM->lock);
  196.     tlb_invalid_fail(pstate);
  197. }
  198.  
  199. /** Process TLB Modified Exception
  200.  *
  201.  * Process TLB Modified Exception.
  202.  *
  203.  * @param pstate Interrupted register context.
  204.  */
  205. void tlb_modified(struct exception_regdump *pstate)
  206. {
  207.     tlb_index_t index;
  208.     __address badvaddr;
  209.     entry_lo_t lo;
  210.     entry_hi_t hi;
  211.     pte_t *pte;
  212.  
  213.     badvaddr = cp0_badvaddr_read();
  214.  
  215.     /*
  216.      * Locate the faulting entry in TLB.
  217.      */
  218.     hi.value = cp0_entry_hi_read();
  219.     prepare_entry_hi(&hi, hi.asid, badvaddr);
  220.     cp0_entry_hi_write(hi.value);
  221.     tlbp();
  222.     index.value = cp0_index_read();
  223.    
  224.     spinlock_lock(&VM->lock);  
  225.    
  226.     /*
  227.      * Fail if the entry is not in TLB.
  228.      */
  229.     if (index.p) {
  230.         printf("TLB entry not found.\n");
  231.         goto fail;
  232.     }
  233.  
  234.     pte = find_mapping_and_check(badvaddr);
  235.     if (!pte)
  236.         goto fail;
  237.  
  238.     /*
  239.      * Fail if the page is not writable.
  240.      */
  241.     if (!pte->w)
  242.         goto fail;
  243.  
  244.     /*
  245.      * Read the faulting TLB entry.
  246.      */
  247.     tlbr();
  248.  
  249.     /*
  250.      * Record access and write to PTE.
  251.      */
  252.     pte->a = 1;
  253.     pte->lo.d = 1;
  254.  
  255.     prepare_entry_lo(&lo, pte->lo.g, pte->lo.v, pte->w, pte->lo.c, pte->lo.pfn);
  256.  
  257.     /*
  258.      * The entry is to be updated in TLB.
  259.      */
  260.     if ((badvaddr/PAGE_SIZE) % 2 == 0)
  261.         cp0_entry_lo0_write(lo.value);
  262.     else
  263.         cp0_entry_lo1_write(lo.value);
  264.     tlbwi();
  265.  
  266.     spinlock_unlock(&VM->lock);
  267.     return;
  268.    
  269. fail:
  270.     spinlock_unlock(&VM->lock);
  271.     tlb_modified_fail(pstate);
  272. }
  273.  
  274. void tlb_refill_fail(struct exception_regdump *pstate)
  275. {
  276.     char *symbol = "";
  277.     char *sym2 = "";
  278.  
  279.     char *s = get_symtab_entry(pstate->epc);
  280.     if (s)
  281.         symbol = s;
  282.     s = get_symtab_entry(pstate->ra);
  283.     if (s)
  284.         sym2 = s;
  285.     panic("%X: TLB Refill Exception at %X(%s<-%s)\n", cp0_badvaddr_read(), pstate->epc, symbol, sym2);
  286. }
  287.  
  288.  
  289. void tlb_invalid_fail(struct exception_regdump *pstate)
  290. {
  291.     char *symbol = "";
  292.  
  293.     char *s = get_symtab_entry(pstate->epc);
  294.     if (s)
  295.         symbol = s;
  296.     panic("%X: TLB Invalid Exception at %X(%s)\n", cp0_badvaddr_read(), pstate->epc, symbol);
  297. }
  298.  
  299. void tlb_modified_fail(struct exception_regdump *pstate)
  300. {
  301.     char *symbol = "";
  302.  
  303.     char *s = get_symtab_entry(pstate->epc);
  304.     if (s)
  305.         symbol = s;
  306.     panic("%X: TLB Modified Exception at %X(%s)\n", cp0_badvaddr_read(), pstate->epc, symbol);
  307. }
  308.  
  309. /** Invalidate TLB entries with specified ASID
  310.  *
  311.  * Invalidate TLB entries with specified ASID.
  312.  *
  313.  * @param asid ASID.
  314.  */
  315. void tlb_invalidate(asid_t asid)
  316. {
  317.     entry_hi_t hi;
  318.     ipl_t ipl;
  319.     int i; 
  320.    
  321.     ASSERT(asid != ASID_INVALID);
  322.  
  323.     ipl = interrupts_disable();
  324.    
  325.     for (i = 0; i < TLB_SIZE; i++) {
  326.         cp0_index_write(i);
  327.         tlbr();
  328.        
  329.         hi.value = cp0_entry_hi_read();
  330.         if (hi.asid == asid) {
  331.             cp0_pagemask_write(TLB_PAGE_MASK_16K);
  332.             cp0_entry_hi_write(0);
  333.             cp0_entry_lo0_write(0);
  334.             cp0_entry_lo1_write(0);
  335.             tlbwi();
  336.         }
  337.     }
  338.    
  339.     interrupts_restore(ipl);
  340. }
  341.  
  342. /** Try to find PTE for faulting address
  343.  *
  344.  * Try to find PTE for faulting address.
  345.  * The VM->lock must be held on entry to this function.
  346.  *
  347.  * @param badvaddr Faulting virtual address.
  348.  *
  349.  * @return PTE on success, NULL otherwise.
  350.  */
  351. pte_t *find_mapping_and_check(__address badvaddr)
  352. {
  353.     entry_hi_t hi;
  354.     pte_t *pte;
  355.  
  356.     hi.value = cp0_entry_hi_read();
  357.  
  358.     /*
  359.      * Handler cannot succeed if the ASIDs don't match.
  360.      */
  361.     if (hi.asid != VM->asid) {
  362.         printf("EntryHi.asid=%d, VM->asid=%d\n", hi.asid, VM->asid);
  363.         return NULL;
  364.     }
  365.    
  366.     /*
  367.      * Handler cannot succeed if badvaddr has no mapping.
  368.      */
  369.     pte = page_mapping_find(badvaddr, 0);
  370.     if (!pte) {
  371.         printf("No such mapping.\n");
  372.         return NULL;
  373.     }
  374.  
  375.     /*
  376.      * Handler cannot succeed if the mapping is marked as invalid.
  377.      */
  378.     if (!pte->lo.v) {
  379.         printf("Invalid mapping.\n");
  380.         return NULL;
  381.     }
  382.  
  383.     return pte;
  384. }
  385.  
  386. void prepare_entry_lo(entry_lo_t *lo, bool g, bool v, bool d, int c, __address pfn)
  387. {
  388.     lo->value = 0;
  389.     lo->g = g;
  390.     lo->v = v;
  391.     lo->d = d;
  392.     lo->c = c;
  393.     lo->pfn = pfn;
  394. }
  395.  
  396. void prepare_entry_hi(entry_hi_t *hi, asid_t asid, __address addr)
  397. {
  398.     hi->value = (((addr/PAGE_SIZE)/2)*PAGE_SIZE*2);
  399.     hi->asid = asid;
  400. }
  401.