diff options
Diffstat (limited to 'arch/avr32/kernel/entry-avr32b.S')
-rw-r--r-- | arch/avr32/kernel/entry-avr32b.S | 88 |
1 files changed, 53 insertions, 35 deletions
diff --git a/arch/avr32/kernel/entry-avr32b.S b/arch/avr32/kernel/entry-avr32b.S index 5f31702d6b1c..2b398cae110c 100644 --- a/arch/avr32/kernel/entry-avr32b.S +++ b/arch/avr32/kernel/entry-avr32b.S | |||
@@ -74,50 +74,41 @@ exception_vectors: | |||
74 | .align 2 | 74 | .align 2 |
75 | bral do_dtlb_modified | 75 | bral do_dtlb_modified |
76 | 76 | ||
77 | /* | ||
78 | * r0 : PGD/PT/PTE | ||
79 | * r1 : Offending address | ||
80 | * r2 : Scratch register | ||
81 | * r3 : Cause (5, 12 or 13) | ||
82 | */ | ||
83 | #define tlbmiss_save pushm r0-r3 | 77 | #define tlbmiss_save pushm r0-r3 |
84 | #define tlbmiss_restore popm r0-r3 | 78 | #define tlbmiss_restore popm r0-r3 |
85 | 79 | ||
86 | .section .tlbx.ex.text,"ax",@progbits | 80 | .org 0x50 |
87 | .global itlb_miss | 81 | .global itlb_miss |
88 | itlb_miss: | 82 | itlb_miss: |
89 | tlbmiss_save | 83 | tlbmiss_save |
90 | rjmp tlb_miss_common | 84 | rjmp tlb_miss_common |
91 | 85 | ||
92 | .section .tlbr.ex.text,"ax",@progbits | 86 | .org 0x60 |
93 | dtlb_miss_read: | 87 | dtlb_miss_read: |
94 | tlbmiss_save | 88 | tlbmiss_save |
95 | rjmp tlb_miss_common | 89 | rjmp tlb_miss_common |
96 | 90 | ||
97 | .section .tlbw.ex.text,"ax",@progbits | 91 | .org 0x70 |
98 | dtlb_miss_write: | 92 | dtlb_miss_write: |
99 | tlbmiss_save | 93 | tlbmiss_save |
100 | 94 | ||
101 | .global tlb_miss_common | 95 | .global tlb_miss_common |
96 | .align 2 | ||
102 | tlb_miss_common: | 97 | tlb_miss_common: |
103 | mfsr r0, SYSREG_TLBEAR | 98 | mfsr r0, SYSREG_TLBEAR |
104 | mfsr r1, SYSREG_PTBR | 99 | mfsr r1, SYSREG_PTBR |
105 | 100 | ||
106 | /* Is it the vmalloc space? */ | 101 | /* |
107 | bld r0, 31 | 102 | * First level lookup: The PGD contains virtual pointers to |
108 | brcs handle_vmalloc_miss | 103 | * the second-level page tables, but they may be NULL if not |
109 | 104 | * present. | |
110 | /* First level lookup */ | 105 | */ |
111 | pgtbl_lookup: | 106 | pgtbl_lookup: |
112 | lsr r2, r0, PGDIR_SHIFT | 107 | lsr r2, r0, PGDIR_SHIFT |
113 | ld.w r3, r1[r2 << 2] | 108 | ld.w r3, r1[r2 << 2] |
114 | bfextu r1, r0, PAGE_SHIFT, PGDIR_SHIFT - PAGE_SHIFT | 109 | bfextu r1, r0, PAGE_SHIFT, PGDIR_SHIFT - PAGE_SHIFT |
115 | bld r3, _PAGE_BIT_PRESENT | 110 | cp.w r3, 0 |
116 | brcc page_table_not_present | 111 | breq page_table_not_present |
117 | |||
118 | /* Translate to virtual address in P1. */ | ||
119 | andl r3, 0xf000 | ||
120 | sbr r3, 31 | ||
121 | 112 | ||
122 | /* Second level lookup */ | 113 | /* Second level lookup */ |
123 | ld.w r2, r3[r1 << 2] | 114 | ld.w r2, r3[r1 << 2] |
@@ -148,16 +139,55 @@ pgtbl_lookup: | |||
148 | tlbmiss_restore | 139 | tlbmiss_restore |
149 | rete | 140 | rete |
150 | 141 | ||
151 | handle_vmalloc_miss: | 142 | /* The slow path of the TLB miss handler */ |
152 | /* Simply do the lookup in init's page table */ | 143 | .align 2 |
144 | page_table_not_present: | ||
145 | /* Do we need to synchronize with swapper_pg_dir? */ | ||
146 | bld r0, 31 | ||
147 | brcs sync_with_swapper_pg_dir | ||
148 | |||
149 | page_not_present: | ||
150 | tlbmiss_restore | ||
151 | sub sp, 4 | ||
152 | stmts --sp, r0-lr | ||
153 | rcall save_full_context_ex | ||
154 | mfsr r12, SYSREG_ECR | ||
155 | mov r11, sp | ||
156 | rcall do_page_fault | ||
157 | rjmp ret_from_exception | ||
158 | |||
159 | .align 2 | ||
160 | sync_with_swapper_pg_dir: | ||
161 | /* | ||
162 | * If swapper_pg_dir contains a non-NULL second-level page | ||
163 | * table pointer, copy it into the current PGD. If not, we | ||
164 | * must handle it as a full-blown page fault. | ||
165 | * | ||
166 | * Jumping back to pgtbl_lookup causes an unnecessary lookup, | ||
167 | * but it is guaranteed to be a cache hit, it won't happen | ||
168 | * very often, and we absolutely do not want to sacrifice any | ||
169 | * performance in the fast path in order to improve this. | ||
170 | */ | ||
153 | mov r1, lo(swapper_pg_dir) | 171 | mov r1, lo(swapper_pg_dir) |
154 | orh r1, hi(swapper_pg_dir) | 172 | orh r1, hi(swapper_pg_dir) |
173 | ld.w r3, r1[r2 << 2] | ||
174 | cp.w r3, 0 | ||
175 | breq page_not_present | ||
176 | mfsr r1, SYSREG_PTBR | ||
177 | st.w r1[r2 << 2], r3 | ||
155 | rjmp pgtbl_lookup | 178 | rjmp pgtbl_lookup |
156 | 179 | ||
180 | /* | ||
181 | * We currently have two bytes left at this point until we | ||
182 | * crash into the system call handler... | ||
183 | * | ||
184 | * Don't worry, the assembler will let us know. | ||
185 | */ | ||
186 | |||
157 | 187 | ||
158 | /* --- System Call --- */ | 188 | /* --- System Call --- */ |
159 | 189 | ||
160 | .section .scall.text,"ax",@progbits | 190 | .org 0x100 |
161 | system_call: | 191 | system_call: |
162 | #ifdef CONFIG_PREEMPT | 192 | #ifdef CONFIG_PREEMPT |
163 | mask_interrupts | 193 | mask_interrupts |
@@ -266,18 +296,6 @@ syscall_exit_work: | |||
266 | brcc syscall_exit_cont | 296 | brcc syscall_exit_cont |
267 | rjmp enter_monitor_mode | 297 | rjmp enter_monitor_mode |
268 | 298 | ||
269 | /* The slow path of the TLB miss handler */ | ||
270 | page_table_not_present: | ||
271 | page_not_present: | ||
272 | tlbmiss_restore | ||
273 | sub sp, 4 | ||
274 | stmts --sp, r0-lr | ||
275 | rcall save_full_context_ex | ||
276 | mfsr r12, SYSREG_ECR | ||
277 | mov r11, sp | ||
278 | rcall do_page_fault | ||
279 | rjmp ret_from_exception | ||
280 | |||
281 | /* This function expects to find offending PC in SYSREG_RAR_EX */ | 299 | /* This function expects to find offending PC in SYSREG_RAR_EX */ |
282 | .type save_full_context_ex, @function | 300 | .type save_full_context_ex, @function |
283 | .align 2 | 301 | .align 2 |