diff options
Diffstat (limited to 'arch/cris/arch-v10/mm/fault.c')
-rw-r--r-- | arch/cris/arch-v10/mm/fault.c | 117 |
1 files changed, 117 insertions, 0 deletions
diff --git a/arch/cris/arch-v10/mm/fault.c b/arch/cris/arch-v10/mm/fault.c new file mode 100644 index 000000000000..6805cdb25a53 --- /dev/null +++ b/arch/cris/arch-v10/mm/fault.c | |||
@@ -0,0 +1,117 @@ | |||
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 | int select; | ||
44 | #ifdef DEBUG | ||
45 | int index; | ||
46 | int page_id; | ||
47 | int acc, inv; | ||
48 | #endif | ||
49 | pgd_t* pgd = (pgd_t*)current_pgd; | ||
50 | pmd_t *pmd; | ||
51 | pte_t pte; | ||
52 | int miss, we, writeac; | ||
53 | unsigned long address; | ||
54 | unsigned long flags; | ||
55 | |||
56 | cause = *R_MMU_CAUSE; | ||
57 | |||
58 | address = cause & PAGE_MASK; /* get faulting address */ | ||
59 | select = *R_TLB_SELECT; | ||
60 | |||
61 | #ifdef DEBUG | ||
62 | page_id = IO_EXTRACT(R_MMU_CAUSE, page_id, cause); | ||
63 | acc = IO_EXTRACT(R_MMU_CAUSE, acc_excp, cause); | ||
64 | inv = IO_EXTRACT(R_MMU_CAUSE, inv_excp, cause); | ||
65 | index = IO_EXTRACT(R_TLB_SELECT, index, select); | ||
66 | #endif | ||
67 | miss = IO_EXTRACT(R_MMU_CAUSE, miss_excp, cause); | ||
68 | we = IO_EXTRACT(R_MMU_CAUSE, we_excp, cause); | ||
69 | writeac = IO_EXTRACT(R_MMU_CAUSE, wr_rd, cause); | ||
70 | |||
71 | D(printk("bus_fault from IRP 0x%lx: addr 0x%lx, miss %d, inv %d, we %d, acc %d, dx %d pid %d\n", | ||
72 | regs->irp, address, miss, inv, we, acc, index, page_id)); | ||
73 | |||
74 | /* leave it to the MM system fault handler */ | ||
75 | if (miss) | ||
76 | do_page_fault(address, regs, 0, writeac); | ||
77 | else | ||
78 | do_page_fault(address, regs, 1, we); | ||
79 | |||
80 | /* Reload TLB with new entry to avoid an extra miss exception. | ||
81 | * do_page_fault may have flushed the TLB so we have to restore | ||
82 | * the MMU registers. | ||
83 | */ | ||
84 | local_save_flags(flags); | ||
85 | local_irq_disable(); | ||
86 | pmd = (pmd_t *)(pgd + pgd_index(address)); | ||
87 | if (pmd_none(*pmd)) | ||
88 | return; | ||
89 | pte = *pte_offset_kernel(pmd, address); | ||
90 | if (!pte_present(pte)) | ||
91 | return; | ||
92 | *R_TLB_SELECT = select; | ||
93 | *R_TLB_HI = cause; | ||
94 | *R_TLB_LO = pte_val(pte); | ||
95 | local_irq_restore(flags); | ||
96 | } | ||
97 | |||
98 | /* Called from arch/cris/mm/fault.c to find fixup code. */ | ||
99 | int | ||
100 | find_fixup_code(struct pt_regs *regs) | ||
101 | { | ||
102 | const struct exception_table_entry *fixup; | ||
103 | |||
104 | if ((fixup = search_exception_tables(regs->irp)) != 0) { | ||
105 | /* Adjust the instruction pointer in the stackframe. */ | ||
106 | regs->irp = fixup->fixup; | ||
107 | |||
108 | /* | ||
109 | * Don't return by restoring the CPU state, so switch | ||
110 | * frame-type. | ||
111 | */ | ||
112 | regs->frametype = CRIS_FRAME_NORMAL; | ||
113 | return 1; | ||
114 | } | ||
115 | |||
116 | return 0; | ||
117 | } | ||