Subversion Repositories HelenOS-historic

Rev

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