diff options
Diffstat (limited to 'arch/parisc/mm/fault.c')
-rw-r--r-- | arch/parisc/mm/fault.c | 58 |
1 files changed, 31 insertions, 27 deletions
diff --git a/arch/parisc/mm/fault.c b/arch/parisc/mm/fault.c index b2e3e9a8cece..92c7fa4ecc3f 100644 --- a/arch/parisc/mm/fault.c +++ b/arch/parisc/mm/fault.c | |||
@@ -139,13 +139,41 @@ parisc_acctyp(unsigned long code, unsigned int inst) | |||
139 | } | 139 | } |
140 | #endif | 140 | #endif |
141 | 141 | ||
142 | int fixup_exception(struct pt_regs *regs) | ||
143 | { | ||
144 | const struct exception_table_entry *fix; | ||
145 | |||
146 | fix = search_exception_tables(regs->iaoq[0]); | ||
147 | if (fix) { | ||
148 | struct exception_data *d; | ||
149 | d = &__get_cpu_var(exception_data); | ||
150 | d->fault_ip = regs->iaoq[0]; | ||
151 | d->fault_space = regs->isr; | ||
152 | d->fault_addr = regs->ior; | ||
153 | |||
154 | regs->iaoq[0] = ((fix->fixup) & ~3); | ||
155 | /* | ||
156 | * NOTE: In some cases the faulting instruction | ||
157 | * may be in the delay slot of a branch. We | ||
158 | * don't want to take the branch, so we don't | ||
159 | * increment iaoq[1], instead we set it to be | ||
160 | * iaoq[0]+4, and clear the B bit in the PSW | ||
161 | */ | ||
162 | regs->iaoq[1] = regs->iaoq[0] + 4; | ||
163 | regs->gr[0] &= ~PSW_B; /* IPSW in gr[0] */ | ||
164 | |||
165 | return 1; | ||
166 | } | ||
167 | |||
168 | return 0; | ||
169 | } | ||
170 | |||
142 | void do_page_fault(struct pt_regs *regs, unsigned long code, | 171 | void do_page_fault(struct pt_regs *regs, unsigned long code, |
143 | unsigned long address) | 172 | unsigned long address) |
144 | { | 173 | { |
145 | struct vm_area_struct *vma, *prev_vma; | 174 | struct vm_area_struct *vma, *prev_vma; |
146 | struct task_struct *tsk = current; | 175 | struct task_struct *tsk = current; |
147 | struct mm_struct *mm = tsk->mm; | 176 | struct mm_struct *mm = tsk->mm; |
148 | const struct exception_table_entry *fix; | ||
149 | unsigned long acc_type; | 177 | unsigned long acc_type; |
150 | int fault; | 178 | int fault; |
151 | 179 | ||
@@ -229,32 +257,8 @@ bad_area: | |||
229 | 257 | ||
230 | no_context: | 258 | no_context: |
231 | 259 | ||
232 | if (!user_mode(regs)) { | 260 | if (!user_mode(regs) && fixup_exception(regs)) { |
233 | fix = search_exception_tables(regs->iaoq[0]); | 261 | return; |
234 | |||
235 | if (fix) { | ||
236 | struct exception_data *d; | ||
237 | |||
238 | d = &__get_cpu_var(exception_data); | ||
239 | d->fault_ip = regs->iaoq[0]; | ||
240 | d->fault_space = regs->isr; | ||
241 | d->fault_addr = regs->ior; | ||
242 | |||
243 | regs->iaoq[0] = ((fix->fixup) & ~3); | ||
244 | |||
245 | /* | ||
246 | * NOTE: In some cases the faulting instruction | ||
247 | * may be in the delay slot of a branch. We | ||
248 | * don't want to take the branch, so we don't | ||
249 | * increment iaoq[1], instead we set it to be | ||
250 | * iaoq[0]+4, and clear the B bit in the PSW | ||
251 | */ | ||
252 | |||
253 | regs->iaoq[1] = regs->iaoq[0] + 4; | ||
254 | regs->gr[0] &= ~PSW_B; /* IPSW in gr[0] */ | ||
255 | |||
256 | return; | ||
257 | } | ||
258 | } | 262 | } |
259 | 263 | ||
260 | parisc_terminate("Bad Address (null pointer deref?)", regs, code, address); | 264 | parisc_terminate("Bad Address (null pointer deref?)", regs, code, address); |