# BRCM_VERSION=3
[bcm963xx.git] / kernel / linux / arch / cris / arch-v10 / mm / fault.c
1 /*
2  *  linux/arch/cris/mm/fault.c
3  *
4  *  Low level bus fault handler
5  *
6  *
7  *  Copyright (C) 2000, 2001  Axis Communications AB
8  *
9  *  Authors:  Bjorn Wesen 
10  * 
11  */
12
13 #include <linux/mm.h>
14 #include <asm/uaccess.h>
15 #include <asm/pgtable.h>
16 #include <asm/arch/svinto.h>
17
18 /* debug of low-level TLB reload */
19 #undef DEBUG
20
21 #ifdef DEBUG
22 #define D(x) x
23 #else
24 #define D(x)
25 #endif
26
27 extern volatile pgd_t *current_pgd;
28
29 extern const struct exception_table_entry
30         *search_exception_tables(unsigned long addr);
31
32 asmlinkage void do_page_fault(unsigned long address, struct pt_regs *regs,
33                               int protection, int writeaccess);
34
35 /* fast TLB-fill fault handler
36  * this is called from entry.S with interrupts disabled
37  */
38
39 void
40 handle_mmu_bus_fault(struct pt_regs *regs)
41 {
42         int cause;
43 #ifdef DEBUG
44         int select;
45         int index;
46         int page_id;
47         int acc, inv;
48 #endif
49         int miss, we, writeac;
50         pmd_t *pmd;
51         pte_t pte;
52         unsigned long address;
53
54         cause = *R_MMU_CAUSE;
55
56         address = cause & PAGE_MASK; /* get faulting address */
57
58 #ifdef DEBUG
59         select = *R_TLB_SELECT;
60         page_id = IO_EXTRACT(R_MMU_CAUSE,  page_id,   cause);
61         acc     = IO_EXTRACT(R_MMU_CAUSE,  acc_excp,  cause);
62         inv     = IO_EXTRACT(R_MMU_CAUSE,  inv_excp,  cause);  
63         index   = IO_EXTRACT(R_TLB_SELECT, index,     select);
64 #endif
65         miss    = IO_EXTRACT(R_MMU_CAUSE,  miss_excp, cause);
66         we      = IO_EXTRACT(R_MMU_CAUSE,  we_excp,   cause);
67         writeac = IO_EXTRACT(R_MMU_CAUSE,  wr_rd,     cause);
68
69         /* ETRAX 100LX TR89 bugfix: if the second half of an unaligned
70          * write causes a MMU-fault, it will not be restarted correctly.
71          * This could happen if a write crosses a page-boundary and the
72          * second page is not yet COW'ed or even loaded. The workaround
73          * is to clear the unaligned bit in the CPU status record, so 
74          * that the CPU will rerun both the first and second halves of
75          * the instruction. This will not have any sideeffects unless
76          * the first half goes to any device or memory that can't be
77          * written twice, and which is mapped through the MMU.
78          *
79          * We only need to do this for writes.
80          */
81
82         if(writeac)
83                 regs->csrinstr &= ~(1 << 5);
84         
85         D(printk("bus_fault from IRP 0x%lx: addr 0x%lx, miss %d, inv %d, we %d, acc %d, dx %d pid %d\n",
86                  regs->irp, address, miss, inv, we, acc, index, page_id));
87
88         /* for a miss, we need to reload the TLB entry */
89
90         if (miss) {
91                 /* see if the pte exists at all
92                  * refer through current_pgd, dont use mm->pgd
93                  */
94
95                 pmd = (pmd_t *)(current_pgd + pgd_index(address));
96                 if (pmd_none(*pmd)) {
97                         do_page_fault(address, regs, 0, writeac);
98                         return;
99                 }
100                 if (pmd_bad(*pmd)) {
101                         printk("bad pgdir entry 0x%lx at 0x%p\n", *(unsigned long*)pmd, pmd);
102                         pmd_clear(pmd);
103                         return;
104                 }
105                 pte = *pte_offset_kernel(pmd, address);
106                 if (!pte_present(pte)) {
107                         do_page_fault(address, regs, 0, writeac);
108                         return;
109                 }
110
111 #ifdef DEBUG
112                 printk(" found pte %lx pg %p ", pte_val(pte), pte_page(pte));
113                 if (pte_val(pte) & _PAGE_SILENT_WRITE)
114                         printk("Silent-W ");
115                 if (pte_val(pte) & _PAGE_KERNEL)
116                         printk("Kernel ");
117                 if (pte_val(pte) & _PAGE_SILENT_READ)
118                         printk("Silent-R ");
119                 if (pte_val(pte) & _PAGE_GLOBAL)
120                         printk("Global ");
121                 if (pte_val(pte) & _PAGE_PRESENT)
122                         printk("Present ");
123                 if (pte_val(pte) & _PAGE_ACCESSED)
124                         printk("Accessed ");
125                 if (pte_val(pte) & _PAGE_MODIFIED)
126                         printk("Modified ");
127                 if (pte_val(pte) & _PAGE_READ)
128                         printk("Readable ");
129                 if (pte_val(pte) & _PAGE_WRITE)
130                         printk("Writeable ");
131                 printk("\n");
132 #endif
133
134                 /* load up the chosen TLB entry
135                  * this assumes the pte format is the same as the TLB_LO layout.
136                  *
137                  * the write to R_TLB_LO also writes the vpn and page_id fields from
138                  * R_MMU_CAUSE, which we in this case obviously want to keep
139                  */
140
141                 *R_TLB_LO = pte_val(pte);
142
143                 return;
144         }
145
146         /* leave it to the MM system fault handler */
147         do_page_fault(address, regs, 1, we);
148 }
149
150 /* Called from arch/cris/mm/fault.c to find fixup code. */
151 int
152 find_fixup_code(struct pt_regs *regs)
153 {
154         const struct exception_table_entry *fixup;
155
156         if ((fixup = search_exception_tables(regs->irp)) != 0) {
157                 /* Adjust the instruction pointer in the stackframe. */
158                 regs->irp = fixup->fixup;
159                 
160                 /* 
161                  * Don't return by restoring the CPU state, so switch
162                  * frame-type. 
163                  */
164                 regs->frametype = CRIS_FRAME_NORMAL;
165                 return 1;
166         }
167
168         return 0;
169 }