aboutsummaryrefslogtreecommitdiffstats
path: root/arch/s390/mm/fault.c
diff options
context:
space:
mode:
authorTrond Myklebust <Trond.Myklebust@netapp.com>2007-02-13 01:43:25 -0500
committerTrond Myklebust <Trond.Myklebust@netapp.com>2007-02-13 01:43:25 -0500
commitd9bc125caf592b7d081021f32ce5b717efdf70c8 (patch)
tree263b7066ba22ddce21db610c0300f6eaac6f2064 /arch/s390/mm/fault.c
parent43d78ef2ba5bec26d0315859e8324bfc0be23766 (diff)
parentec2f9d1331f658433411c58077871e1eef4ee1b4 (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.c97
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;
52extern void die(const char *,struct pt_regs *,long); 52extern void die(const char *,struct pt_regs *,long);
53 53
54#ifdef CONFIG_KPROBES 54#ifdef CONFIG_KPROBES
55ATOMIC_NOTIFIER_HEAD(notify_page_fault_chain); 55static ATOMIC_NOTIFIER_HEAD(notify_page_fault_chain);
56int register_page_fault_notifier(struct notifier_block *nb) 56int register_page_fault_notifier(struct notifier_block *nb)
57{ 57{
58 return atomic_notifier_chain_register(&notify_page_fault_chain, nb); 58 return atomic_notifier_chain_register(&notify_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
86extern 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 */
93void bust_spinlocks(int yes) 91void 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 */
142static inline int check_user_space(struct pt_regs *regs, int error_code) 142static 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
187extern long sys_sigreturn(struct pt_regs *regs);
188extern long sys_rt_sigreturn(struct pt_regs *regs);
189extern long sys32_sigreturn(struct pt_regs *regs);
190extern long sys32_rt_sigreturn(struct pt_regs *regs);
191
192static 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
213static 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;
251out_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
455asmlinkage void 537static void pfault_interrupt(__u16 error_code)
456pfault_interrupt(__u16 error_code)
457{ 538{
458 struct task_struct *tsk; 539 struct task_struct *tsk;
459 __u16 subcode; 540 __u16 subcode;