From: James S. <jsi...@us...> - 2002-06-15 19:17:12
|
Update of /cvsroot/linuxconsole/ruby/linux/arch/s390x/mm In directory usw-pr-cvs1:/tmp/cvs-serv22898/linux/arch/s390x/mm Modified Files: fault.c Log Message: Synced to 2.5.21 Index: fault.c =================================================================== RCS file: /cvsroot/linuxconsole/ruby/linux/arch/s390x/mm/fault.c,v retrieving revision 1.3 retrieving revision 1.4 diff -u -d -r1.3 -r1.4 --- fault.c 26 Dec 2001 17:28:11 -0000 1.3 +++ fault.c 15 Jun 2002 19:17:09 -0000 1.4 @@ -34,7 +34,77 @@ #endif extern void die(const char *,struct pt_regs *,long); -static void force_sigsegv(struct task_struct *tsk, int code, void *address); + +/* + * Check which address space is addressed by the access + * register in S390_lowcore.exc_access_id. + * Returns 1 for user space and 0 for kernel space. + */ +static int __check_access_register(struct pt_regs *regs, int error_code) +{ + int areg = S390_lowcore.exc_access_id; + + if (areg == 0) + /* Access via access register 0 -> kernel address */ + return 0; + if (regs && areg < NUM_ACRS && regs->acrs[areg] <= 1) + /* + * access register contains 0 -> kernel address, + * access register contains 1 -> user space address + */ + return regs->acrs[areg]; + + /* Something unhealthy was done with the access registers... */ + die("page fault via unknown access register", regs, error_code); + do_exit(SIGKILL); + return 0; +} + +/* + * Check which address space the address belongs to. + * Returns 1 for user space and 0 for kernel space. + */ +static inline int check_user_space(struct pt_regs *regs, int error_code) +{ + /* + * The lowest two bits of S390_lowcore.trans_exc_code indicate + * which paging table was used: + * 0: Primary Segment Table Descriptor + * 1: STD determined via access register + * 2: Secondary Segment Table Descriptor + * 3: Home Segment Table Descriptor + */ + int descriptor = S390_lowcore.trans_exc_code & 3; + if (descriptor == 1) + return __check_access_register(regs, error_code); + return descriptor >> 1; +} + +/* + * Send SIGSEGV to task. This is an external routine + * to keep the stack usage of do_page_fault small. + */ +static void force_sigsegv(struct pt_regs *regs, unsigned long error_code, + int si_code, unsigned long address) +{ + struct siginfo si; + +#if defined(CONFIG_SYSCTL) || defined(CONFIG_PROCESS_DEBUG) +#if defined(CONFIG_SYSCTL) + if (sysctl_userprocess_debug) +#endif + { + printk("User process fault: interruption code 0x%lX\n", + error_code); + printk("failing address: %lX\n", address); + show_regs(regs); + } +#endif + si.si_signo = SIGSEGV; + si.si_code = si_code; + si.si_addr = (void *) address; + force_sig_info(SIGSEGV, &si, current); +} /* * This routine handles page faults. It determines the address, @@ -42,21 +112,20 @@ * routines. * * error_code: - * ****0004 Protection -> Write-Protection (suprression) - * ****0010 Segment translation -> Not present (nullification) - * ****0011 Page translation -> Not present (nullification) - * ****003B Region third exception -> Not present (nullification) + * 04 Protection -> Write-Protection (suprression) + * 10 Segment translation -> Not present (nullification) + * 11 Page translation -> Not present (nullification) + * 3b Region third trans. -> Not present (nullification) */ -asmlinkage void do_page_fault(struct pt_regs *regs, unsigned long error_code) +extern inline void do_exception(struct pt_regs *regs, unsigned long error_code) { struct task_struct *tsk; struct mm_struct *mm; struct vm_area_struct * vma; unsigned long address; + int user_address; unsigned long fixup; - int write; int si_code = SEGV_MAPERR; - int kernel_address = 0; tsk = current; mm = tsk->mm; @@ -66,13 +135,13 @@ * as a special case because the translation exception code * field is not guaranteed to contain valid data in this case. */ - if ((error_code & 0xff) == 4 && !(S390_lowcore.trans_exc_code & 4)) { + if (error_code == 4 && !(S390_lowcore.trans_exc_code & 4)) { /* Low-address protection hit in kernel mode means NULL pointer write access in kernel mode. */ if (!(regs->psw.mask & PSW_PROBLEM_STATE)) { address = 0; - kernel_address = 1; + user_address = 0; goto no_context; } @@ -86,52 +155,15 @@ * more specific the segment and page table portion of * the address */ - - address = S390_lowcore.trans_exc_code&-4096L; - - - /* - * Check which address space the address belongs to - */ - switch (S390_lowcore.trans_exc_code & 3) - { - case 0: /* Primary Segment Table Descriptor */ - kernel_address = 1; - goto no_context; - - case 1: /* STD determined via access register */ - if (S390_lowcore.exc_access_id == 0) - { - kernel_address = 1; - goto no_context; - } - if (regs && S390_lowcore.exc_access_id < NUM_ACRS) - { - if (regs->acrs[S390_lowcore.exc_access_id] == 0) - { - kernel_address = 1; - goto no_context; - } - if (regs->acrs[S390_lowcore.exc_access_id] == 1) - { - /* user space address */ - break; - } - } - die("page fault via unknown access register", regs, error_code); - do_exit(SIGKILL); - break; - - case 2: /* Secondary Segment Table Descriptor */ - case 3: /* Home Segment Table Descriptor */ - /* user space address */ - break; - } + address = S390_lowcore.trans_exc_code & -4096L; + user_address = check_user_space(regs, error_code); /* - * Check whether we have a user MM in the first place. + * Verify that the fault happened in user space, that + * we are not in an interrupt and that there is a + * user context. */ - if (in_interrupt() || !mm || !(regs->psw.mask & _PSW_IO_MASK_BIT)) + if (user_address == 0 || in_interrupt() || !mm) goto no_context; /* @@ -139,7 +171,6 @@ * task's user address space, so we can switch on the * interrupts again and then search the VMAs */ - __sti(); down_read(&mm->mmap_sem); @@ -158,31 +189,23 @@ * we can handle it.. */ good_area: - write = 0; si_code = SEGV_ACCERR; + if (error_code != 4) { + /* page not present, check vm flags */ + if (!(vma->vm_flags & (VM_READ | VM_EXEC | VM_WRITE))) + goto bad_area; + } else { + if (!(vma->vm_flags & VM_WRITE)) + goto bad_area; + } - switch (error_code & 0xFF) { - case 0x04: /* write, present*/ - write = 1; - break; - case 0x10: /* not present*/ - case 0x11: /* not present*/ - case 0x3B: /* not present*/ - if (!(vma->vm_flags & (VM_READ | VM_EXEC | VM_WRITE))) - goto bad_area; - break; - default: - printk("code should be 4, 10 or 11 (%lX) \n",error_code&0xFF); - goto bad_area; - } - - survive: +survive: /* * If for any reason at all we couldn't handle the fault, * make sure we exit gracefully rather than endlessly redo * the fault. */ - switch (handle_mm_fault(mm, vma, address, write)) { + switch (handle_mm_fault(mm, vma, address, error_code == 4)) { case 1: tsk->min_flt++; break; @@ -209,22 +232,7 @@ if (regs->psw.mask & PSW_PROBLEM_STATE) { tsk->thread.prot_addr = address; tsk->thread.trap_no = error_code; -#ifndef CONFIG_SYSCTL -#ifdef CONFIG_PROCESS_DEBUG - printk("User process fault: interruption code 0x%lX\n",error_code); - printk("failing address: %lX\n",address); - show_regs(regs); -#endif -#else - if (sysctl_userprocess_debug) { - printk("User process fault: interruption code 0x%lX\n", - error_code); - printk("failing address: %lX\n", address); - show_regs(regs); - } -#endif - - force_sigsegv(tsk, si_code, (void *)address); + force_sigsegv(regs, error_code, si_code, address); return; } @@ -239,8 +247,7 @@ * Oops. The kernel tried to access some bad page. We'll have to * terminate things with extreme prejudice. */ - - if (kernel_address) + if (user_address == 0) printk(KERN_ALERT "Unable to handle kernel pointer dereference" " at virtual kernel address %016lx\n", address); else @@ -258,9 +265,7 @@ out_of_memory: up_read(&mm->mmap_sem); if (tsk->pid == 1) { - tsk->policy |= SCHED_YIELD; - schedule(); - down_read(&mm->mmap_sem); + yield(); goto survive; } printk("VM: killing process %s\n", tsk->comm); @@ -284,19 +289,26 @@ goto no_context; } -/* - * Send SIGSEGV to task. This is an external routine - * to keep the stack usage of do_page_fault small. - */ -static void force_sigsegv(struct task_struct *tsk, int code, void *address) +void do_protection_exception(struct pt_regs *regs, unsigned long error_code) { - struct siginfo si; - si.si_signo = SIGSEGV; - si.si_code = code; - si.si_addr = address; - force_sig_info(SIGSEGV, &si, tsk); + regs->psw.addr -= (error_code >> 16); + do_exception(regs, 4); } +void do_segment_exception(struct pt_regs *regs, unsigned long error_code) +{ + do_exception(regs, 0x10); +} + +void do_page_exception(struct pt_regs *regs, unsigned long error_code) +{ + do_exception(regs, 0x11); +} + +void do_region_exception(struct pt_regs *regs, unsigned long error_code) +{ + do_exception(regs, 0x3b); +} #ifdef CONFIG_PFAULT /* |