diff options
author | Trond Myklebust <Trond.Myklebust@netapp.com> | 2007-02-13 01:43:25 -0500 |
---|---|---|
committer | Trond Myklebust <Trond.Myklebust@netapp.com> | 2007-02-13 01:43:25 -0500 |
commit | d9bc125caf592b7d081021f32ce5b717efdf70c8 (patch) | |
tree | 263b7066ba22ddce21db610c0300f6eaac6f2064 /arch/s390/mm/fault.c | |
parent | 43d78ef2ba5bec26d0315859e8324bfc0be23766 (diff) | |
parent | ec2f9d1331f658433411c58077871e1eef4ee1b4 (diff) |
Merge branch 'master' of /home/trondmy/kernel/linux-2.6/
Conflicts:
net/sunrpc/auth_gss/gss_krb5_crypto.c
net/sunrpc/auth_gss/gss_spkm3_token.c
net/sunrpc/clnt.c
Merge with mainline and fix conflicts.
Diffstat (limited to 'arch/s390/mm/fault.c')
-rw-r--r-- | arch/s390/mm/fault.c | 97 |
1 files changed, 89 insertions, 8 deletions
diff --git a/arch/s390/mm/fault.c b/arch/s390/mm/fault.c index cd85e34d8703..641aef36ccc4 100644 --- a/arch/s390/mm/fault.c +++ b/arch/s390/mm/fault.c | |||
@@ -52,7 +52,7 @@ extern int sysctl_userprocess_debug; | |||
52 | extern void die(const char *,struct pt_regs *,long); | 52 | extern void die(const char *,struct pt_regs *,long); |
53 | 53 | ||
54 | #ifdef CONFIG_KPROBES | 54 | #ifdef CONFIG_KPROBES |
55 | ATOMIC_NOTIFIER_HEAD(notify_page_fault_chain); | 55 | static ATOMIC_NOTIFIER_HEAD(notify_page_fault_chain); |
56 | int register_page_fault_notifier(struct notifier_block *nb) | 56 | int register_page_fault_notifier(struct notifier_block *nb) |
57 | { | 57 | { |
58 | return atomic_notifier_chain_register(¬ify_page_fault_chain, nb); | 58 | return atomic_notifier_chain_register(¬ify_page_fault_chain, nb); |
@@ -83,12 +83,10 @@ static inline int notify_page_fault(enum die_val val, const char *str, | |||
83 | } | 83 | } |
84 | #endif | 84 | #endif |
85 | 85 | ||
86 | extern spinlock_t timerlist_lock; | ||
87 | 86 | ||
88 | /* | 87 | /* |
89 | * Unlock any spinlocks which will prevent us from getting the | 88 | * Unlock any spinlocks which will prevent us from getting the |
90 | * message out (timerlist_lock is acquired through the | 89 | * message out. |
91 | * console unblank code) | ||
92 | */ | 90 | */ |
93 | void bust_spinlocks(int yes) | 91 | void bust_spinlocks(int yes) |
94 | { | 92 | { |
@@ -137,7 +135,9 @@ static int __check_access_register(struct pt_regs *regs, int error_code) | |||
137 | 135 | ||
138 | /* | 136 | /* |
139 | * Check which address space the address belongs to. | 137 | * Check which address space the address belongs to. |
140 | * Returns 1 for user space and 0 for kernel space. | 138 | * May return 1 or 2 for user space and 0 for kernel space. |
139 | * Returns 2 for user space in primary addressing mode with | ||
140 | * CONFIG_S390_EXEC_PROTECT on and kernel parameter noexec=on. | ||
141 | */ | 141 | */ |
142 | static inline int check_user_space(struct pt_regs *regs, int error_code) | 142 | static inline int check_user_space(struct pt_regs *regs, int error_code) |
143 | { | 143 | { |
@@ -154,7 +154,7 @@ static inline int check_user_space(struct pt_regs *regs, int error_code) | |||
154 | return __check_access_register(regs, error_code); | 154 | return __check_access_register(regs, error_code); |
155 | if (descriptor == 2) | 155 | if (descriptor == 2) |
156 | return current->thread.mm_segment.ar4; | 156 | return current->thread.mm_segment.ar4; |
157 | return descriptor != 0; | 157 | return ((descriptor != 0) ^ (switch_amode)) << s390_noexec; |
158 | } | 158 | } |
159 | 159 | ||
160 | /* | 160 | /* |
@@ -183,6 +183,77 @@ static void do_sigsegv(struct pt_regs *regs, unsigned long error_code, | |||
183 | force_sig_info(SIGSEGV, &si, current); | 183 | force_sig_info(SIGSEGV, &si, current); |
184 | } | 184 | } |
185 | 185 | ||
186 | #ifdef CONFIG_S390_EXEC_PROTECT | ||
187 | extern long sys_sigreturn(struct pt_regs *regs); | ||
188 | extern long sys_rt_sigreturn(struct pt_regs *regs); | ||
189 | extern long sys32_sigreturn(struct pt_regs *regs); | ||
190 | extern long sys32_rt_sigreturn(struct pt_regs *regs); | ||
191 | |||
192 | static inline void do_sigreturn(struct mm_struct *mm, struct pt_regs *regs, | ||
193 | int rt) | ||
194 | { | ||
195 | up_read(&mm->mmap_sem); | ||
196 | clear_tsk_thread_flag(current, TIF_SINGLE_STEP); | ||
197 | #ifdef CONFIG_COMPAT | ||
198 | if (test_tsk_thread_flag(current, TIF_31BIT)) { | ||
199 | if (rt) | ||
200 | sys32_rt_sigreturn(regs); | ||
201 | else | ||
202 | sys32_sigreturn(regs); | ||
203 | return; | ||
204 | } | ||
205 | #endif /* CONFIG_COMPAT */ | ||
206 | if (rt) | ||
207 | sys_rt_sigreturn(regs); | ||
208 | else | ||
209 | sys_sigreturn(regs); | ||
210 | return; | ||
211 | } | ||
212 | |||
213 | static int signal_return(struct mm_struct *mm, struct pt_regs *regs, | ||
214 | unsigned long address, unsigned long error_code) | ||
215 | { | ||
216 | pgd_t *pgd; | ||
217 | pmd_t *pmd; | ||
218 | pte_t *pte; | ||
219 | u16 *instruction; | ||
220 | unsigned long pfn, uaddr = regs->psw.addr; | ||
221 | |||
222 | spin_lock(&mm->page_table_lock); | ||
223 | pgd = pgd_offset(mm, uaddr); | ||
224 | if (pgd_none(*pgd) || unlikely(pgd_bad(*pgd))) | ||
225 | goto out_fault; | ||
226 | pmd = pmd_offset(pgd, uaddr); | ||
227 | if (pmd_none(*pmd) || unlikely(pmd_bad(*pmd))) | ||
228 | goto out_fault; | ||
229 | pte = pte_offset_map(pmd_offset(pgd_offset(mm, uaddr), uaddr), uaddr); | ||
230 | if (!pte || !pte_present(*pte)) | ||
231 | goto out_fault; | ||
232 | pfn = pte_pfn(*pte); | ||
233 | if (!pfn_valid(pfn)) | ||
234 | goto out_fault; | ||
235 | spin_unlock(&mm->page_table_lock); | ||
236 | |||
237 | instruction = (u16 *) ((pfn << PAGE_SHIFT) + (uaddr & (PAGE_SIZE-1))); | ||
238 | if (*instruction == 0x0a77) | ||
239 | do_sigreturn(mm, regs, 0); | ||
240 | else if (*instruction == 0x0aad) | ||
241 | do_sigreturn(mm, regs, 1); | ||
242 | else { | ||
243 | printk("- XXX - do_exception: task = %s, primary, NO EXEC " | ||
244 | "-> SIGSEGV\n", current->comm); | ||
245 | up_read(&mm->mmap_sem); | ||
246 | current->thread.prot_addr = address; | ||
247 | current->thread.trap_no = error_code; | ||
248 | do_sigsegv(regs, error_code, SEGV_MAPERR, address); | ||
249 | } | ||
250 | return 0; | ||
251 | out_fault: | ||
252 | spin_unlock(&mm->page_table_lock); | ||
253 | return -EFAULT; | ||
254 | } | ||
255 | #endif /* CONFIG_S390_EXEC_PROTECT */ | ||
256 | |||
186 | /* | 257 | /* |
187 | * This routine handles page faults. It determines the address, | 258 | * This routine handles page faults. It determines the address, |
188 | * and the problem, and then passes it off to one of the appropriate | 259 | * and the problem, and then passes it off to one of the appropriate |
@@ -260,6 +331,17 @@ do_exception(struct pt_regs *regs, unsigned long error_code, int is_protection) | |||
260 | vma = find_vma(mm, address); | 331 | vma = find_vma(mm, address); |
261 | if (!vma) | 332 | if (!vma) |
262 | goto bad_area; | 333 | goto bad_area; |
334 | |||
335 | #ifdef CONFIG_S390_EXEC_PROTECT | ||
336 | if (unlikely((user_address == 2) && !(vma->vm_flags & VM_EXEC))) | ||
337 | if (!signal_return(mm, regs, address, error_code)) | ||
338 | /* | ||
339 | * signal_return() has done an up_read(&mm->mmap_sem) | ||
340 | * if it returns 0. | ||
341 | */ | ||
342 | return; | ||
343 | #endif | ||
344 | |||
263 | if (vma->vm_start <= address) | 345 | if (vma->vm_start <= address) |
264 | goto good_area; | 346 | goto good_area; |
265 | if (!(vma->vm_flags & VM_GROWSDOWN)) | 347 | if (!(vma->vm_flags & VM_GROWSDOWN)) |
@@ -452,8 +534,7 @@ void pfault_fini(void) | |||
452 | : : "a" (&refbk), "m" (refbk) : "cc"); | 534 | : : "a" (&refbk), "m" (refbk) : "cc"); |
453 | } | 535 | } |
454 | 536 | ||
455 | asmlinkage void | 537 | static void pfault_interrupt(__u16 error_code) |
456 | pfault_interrupt(__u16 error_code) | ||
457 | { | 538 | { |
458 | struct task_struct *tsk; | 539 | struct task_struct *tsk; |
459 | __u16 subcode; | 540 | __u16 subcode; |