Subversion Repositories HelenOS

Rev

Rev 396 | Rev 399 | 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. static void prepare_entry_lo(entry_lo_t *lo, bool g, bool v, bool d, int c, __address pfn);
  48.  
  49. /** Initialize TLB
  50.  *
  51.  * Initialize TLB.
  52.  * Invalidate all entries and mark wired entries.
  53.  */
  54. void tlb_init_arch(void)
  55. {
  56.     int i;
  57.  
  58.     cp0_pagemask_write(TLB_PAGE_MASK_16K);
  59.     cp0_entry_hi_write(0);
  60.     cp0_entry_lo0_write(0);
  61.     cp0_entry_lo1_write(0);
  62.  
  63.     /*
  64.      * Invalidate all entries.
  65.      */
  66.     for (i = 0; i < TLB_SIZE; i++) {
  67.         cp0_index_write(i);
  68.         tlbwi();
  69.     }
  70.    
  71.     /*
  72.      * The kernel is going to make use of some wired
  73.      * entries (e.g. mapping kernel stacks in kseg3).
  74.      */
  75.     cp0_wired_write(TLB_WIRED);
  76. }
  77.  
  78. /** Process TLB Refill Exception
  79.  *
  80.  * Process TLB Refill Exception.
  81.  *
  82.  * @param pstate Interrupted register context.
  83.  */
  84. void tlb_refill(struct exception_regdump *pstate)
  85. {
  86.     entry_lo_t lo;
  87.     __address badvaddr;
  88.     pte_t *pte;
  89.  
  90. // debug   
  91.     entry_hi_t hi;
  92.  
  93.     badvaddr = cp0_badvaddr_read();
  94.  
  95. // debug
  96.     hi.value = cp0_entry_hi_read();
  97.     printf("TLB Refill: hi.vnp2=%X\n", hi.vpn2);
  98.    
  99.     spinlock_lock(&VM->lock);      
  100.     pte = find_mapping_and_check(badvaddr);
  101.     if (!pte)
  102.         goto fail;
  103.  
  104.     /*
  105.      * Record access to PTE.
  106.      */
  107.     pte->a = 1;
  108.  
  109.     prepare_entry_lo(&lo, pte->g, pte->v, pte->d, pte->c, pte->pfn);
  110.  
  111.     /*
  112.      * New entry is to be inserted into TLB
  113.      */
  114.     if ((badvaddr/PAGE_SIZE) % 2 == 0) {
  115.         cp0_entry_lo0_write(lo.value);
  116.         cp0_entry_lo1_write(0);
  117.     }
  118.     else {
  119.         cp0_entry_lo0_write(0);
  120.         cp0_entry_lo1_write(lo.value);
  121.     }
  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.     pte_t *pte;
  144.  
  145.     badvaddr = cp0_badvaddr_read();
  146.  
  147.     /*
  148.      * Locate the faulting entry in TLB.
  149.      */
  150.     tlbp();
  151.     index.value = cp0_index_read();
  152.    
  153.     spinlock_lock(&VM->lock);  
  154.    
  155.     /*
  156.      * Fail if the entry is not in TLB.
  157.      */
  158.     if (index.p) {
  159.         printf("TLB entry not found.\n");
  160.         goto fail;
  161.     }
  162.  
  163.     pte = find_mapping_and_check(badvaddr);
  164.     if (!pte)
  165.         goto fail;
  166.  
  167.     /*
  168.      * Read the faulting TLB entry.
  169.      */
  170.     tlbr();
  171.  
  172.     /*
  173.      * Record access to PTE.
  174.      */
  175.     pte->a = 1;
  176.  
  177.     prepare_entry_lo(&lo, pte->g, pte->v, pte->d, pte->c, pte->pfn);
  178.  
  179.     /*
  180.      * The entry is to be updated in TLB.
  181.      */
  182.     if ((badvaddr/PAGE_SIZE) % 2 == 0)
  183.         cp0_entry_lo0_write(lo.value);
  184.     else
  185.         cp0_entry_lo1_write(lo.value);
  186.     tlbwi();
  187.  
  188.     spinlock_unlock(&VM->lock);
  189.     return;
  190.    
  191. fail:
  192.     spinlock_unlock(&VM->lock);
  193.     tlb_invalid_fail(pstate);
  194. }
  195.  
  196. /** Process TLB Modified Exception
  197.  *
  198.  * Process TLB Modified Exception.
  199.  *
  200.  * @param pstate Interrupted register context.
  201.  */
  202. void tlb_modified(struct exception_regdump *pstate)
  203. {
  204.     tlb_index_t index;
  205.     __address badvaddr;
  206.     entry_lo_t lo;
  207.     pte_t *pte;
  208.  
  209.     badvaddr = cp0_badvaddr_read();
  210.  
  211.     /*
  212.      * Locate the faulting entry in TLB.
  213.      */
  214.     tlbp();
  215.     index.value = cp0_index_read();
  216.    
  217.     spinlock_lock(&VM->lock);  
  218.    
  219.     /*
  220.      * Fail if the entry is not in TLB.
  221.      */
  222.     if (index.p) {
  223.         printf("TLB entry not found.\n");
  224.         goto fail;
  225.     }
  226.  
  227.     pte = find_mapping_and_check(badvaddr);
  228.     if (!pte)
  229.         goto fail;
  230.  
  231.     /*
  232.      * Fail if the page is not writable.
  233.      */
  234.     if (!pte->w)
  235.         goto fail;
  236.  
  237.     /*
  238.      * Read the faulting TLB entry.
  239.      */
  240.     tlbr();
  241.  
  242.     /*
  243.      * Record access and write to PTE.
  244.      */
  245.     pte->a = 1;
  246.     pte->d = 1;
  247.  
  248.     prepare_entry_lo(&lo, pte->g, pte->v, pte->w, pte->c, pte->pfn);
  249.  
  250.     /*
  251.      * The entry is to be updated in TLB.
  252.      */
  253.     if ((badvaddr/PAGE_SIZE) % 2 == 0)
  254.         cp0_entry_lo0_write(lo.value);
  255.     else
  256.         cp0_entry_lo1_write(lo.value);
  257.     tlbwi();
  258.  
  259.     spinlock_unlock(&VM->lock);
  260.     return;
  261.    
  262. fail:
  263.     spinlock_unlock(&VM->lock);
  264.     tlb_modified_fail(pstate);
  265. }
  266.  
  267. void tlb_refill_fail(struct exception_regdump *pstate)
  268. {
  269.     char *symbol = "";
  270.     char *sym2 = "";
  271.  
  272.     char *s = get_symtab_entry(pstate->epc);
  273.     if (s)
  274.         symbol = s;
  275.     s = get_symtab_entry(pstate->ra);
  276.     if (s)
  277.         sym2 = s;
  278.     panic("%X: TLB Refill Exception at %X(%s<-%s)\n", cp0_badvaddr_read(), pstate->epc, symbol, sym2);
  279. }
  280.  
  281.  
  282. void tlb_invalid_fail(struct exception_regdump *pstate)
  283. {
  284.     char *symbol = "";
  285.  
  286.     char *s = get_symtab_entry(pstate->epc);
  287.     if (s)
  288.         symbol = s;
  289.     panic("%X: TLB Invalid Exception at %X(%s)\n", cp0_badvaddr_read(), pstate->epc, symbol);
  290. }
  291.  
  292. void tlb_modified_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 Modified Exception at %X(%s)\n", cp0_badvaddr_read(), pstate->epc, symbol);
  300. }
  301.  
  302. /** Invalidate TLB entries with specified ASID
  303.  *
  304.  * Invalidate TLB entries with specified ASID.
  305.  *
  306.  * @param asid ASID.
  307.  */
  308. void tlb_invalidate(asid_t asid)
  309. {
  310.     entry_hi_t hi;
  311.     pri_t pri;
  312.     int i; 
  313.    
  314.     ASSERT(asid != ASID_INVALID);
  315.  
  316.     pri = cpu_priority_high();
  317.    
  318.     for (i = 0; i < TLB_SIZE; i++) {
  319.         cp0_index_write(i);
  320.         tlbr();
  321.        
  322.         hi.value = cp0_entry_hi_read();
  323.         if (hi.asid == asid) {
  324.             cp0_pagemask_write(TLB_PAGE_MASK_16K);
  325.             cp0_entry_hi_write(0);
  326.             cp0_entry_lo0_write(0);
  327.             cp0_entry_lo1_write(0);
  328.             tlbwi();
  329.         }
  330.     }
  331.    
  332.     cpu_priority_restore(pri);
  333. }
  334.  
  335. /** Try to find PTE for faulting address
  336.  *
  337.  * Try to find PTE for faulting address.
  338.  * The VM->lock must be held on entry to this function.
  339.  *
  340.  * @param badvaddr Faulting virtual address.
  341.  *
  342.  * @return PTE on success, NULL otherwise.
  343.  */
  344. pte_t *find_mapping_and_check(__address badvaddr)
  345. {
  346.     entry_hi_t hi;
  347.     pte_t *pte;
  348.  
  349.     hi.value = cp0_entry_hi_read();
  350.  
  351.     /*
  352.      * Handler cannot succeed if the ASIDs don't match.
  353.      */
  354.     if (hi.asid != VM->asid) {
  355.         printf("EntryHi.asid=%d, VM->asid=%d\n", hi.asid, VM->asid);
  356.         return NULL;
  357.     }
  358.    
  359.     /*
  360.      * Handler cannot succeed if badvaddr has no mapping.
  361.      */
  362.     pte = find_mapping(badvaddr, 0);
  363.     if (!pte) {
  364.         printf("No such mapping.\n");
  365.         return NULL;
  366.     }
  367.  
  368.     /*
  369.      * Handler cannot succeed if the mapping is marked as invalid.
  370.      */
  371.     if (!pte->v) {
  372.         printf("Invalid mapping.\n");
  373.         return NULL;
  374.     }
  375.  
  376.     return pte;
  377. }
  378.  
  379. void prepare_entry_lo(entry_lo_t *lo, bool g, bool v, bool d, int c, __address pfn)
  380. {
  381.     lo->g = g;
  382.     lo->v = v;
  383.     lo->d = d;
  384.     lo->c = c;
  385.     lo->pfn = pfn;
  386.     lo->zero = 0;
  387. }
  388.