2005-04-16 15:20:36 -07:00
/*
* This file is subject to the terms and conditions of the GNU General Public
* License . See the file " COPYING " in the main directory of this archive
* for more details .
*
* Copyright ( C ) 1995 - 2000 by Ralf Baechle
*/
# include <linux/signal.h>
# include <linux/sched.h>
# include <linux/interrupt.h>
# include <linux/kernel.h>
# include <linux/errno.h>
# include <linux/string.h>
# include <linux/types.h>
# include <linux/ptrace.h>
# include <linux/mman.h>
# include <linux/mm.h>
# include <linux/smp.h>
# include <linux/module.h>
2010-08-03 11:22:20 -07:00
# include <linux/kprobes.h>
2010-10-12 19:37:21 +08:00
# include <linux/perf_event.h>
2005-04-16 15:20:36 -07:00
# include <asm/branch.h>
# include <asm/mmu_context.h>
# include <asm/system.h>
# include <asm/uaccess.h>
# include <asm/ptrace.h>
2005-02-19 13:56:04 +00:00
# include <asm/highmem.h> /* For VMALLOC_END */
2010-08-03 11:22:20 -07:00
# include <linux/kdebug.h>
2005-04-16 15:20:36 -07:00
/*
* This routine handles page faults . It determines the address ,
* and the problem , and then passes it off to one of the appropriate
* routines .
*/
2010-08-03 11:22:20 -07:00
asmlinkage void __kprobes do_page_fault ( struct pt_regs * regs , unsigned long write ,
2005-04-16 15:20:36 -07:00
unsigned long address )
{
struct vm_area_struct * vma = NULL ;
struct task_struct * tsk = current ;
struct mm_struct * mm = tsk - > mm ;
const int field = sizeof ( unsigned long ) * 2 ;
siginfo_t info ;
2007-07-19 01:47:05 -07:00
int fault ;
2005-04-16 15:20:36 -07:00
#if 0
2007-03-29 22:30:01 +01:00
printk ( " Cpu%d[%s:%d:%0*lx:%ld:%0*lx] \n " , raw_smp_processor_id ( ) ,
2005-04-16 15:20:36 -07:00
current - > comm , current - > pid , field , address , write ,
field , regs - > cp0_epc ) ;
# endif
2010-08-03 11:22:20 -07:00
# ifdef CONFIG_KPROBES
/*
* This is to notify the fault handler of the kprobes . The
* exception code is redundant as it is also carried in REGS ,
* but we pass it anyhow .
*/
if ( notify_die ( DIE_PAGE_FAULT , " page fault " , regs , - 1 ,
( regs - > cp0_cause > > 2 ) & 0x1f , SIGSEGV ) = = NOTIFY_STOP )
return ;
# endif
2005-04-16 15:20:36 -07:00
info . si_code = SEGV_MAPERR ;
/*
* We fault - in kernel - space virtual memory on - demand . The
* ' reference ' page table is init_mm . pgd .
*
* NOTE ! We MUST NOT take any locks for this case . We may
* be in an interrupt or a critical region , and should
* only copy the information from the master page table ,
* nothing more .
*/
2009-09-02 15:47:34 -07:00
# ifdef CONFIG_64BIT
# define VMALLOC_FAULT_TARGET no_context
# else
# define VMALLOC_FAULT_TARGET vmalloc_fault
# endif
2005-02-19 13:56:04 +00:00
if ( unlikely ( address > = VMALLOC_START & & address < = VMALLOC_END ) )
2009-09-02 15:47:34 -07:00
goto VMALLOC_FAULT_TARGET ;
[MIPS] Load modules to CKSEG0 if CONFIG_BUILD_ELF64=n
This is a patch to load 64-bit modules to CKSEG0 so that can be
compiled with -msym32 option. This makes each module ~10% smaller.
* introduce MODULE_START and MODULE_END
* custom module_alloc()
* PGD for modules
* change XTLB refill handler synthesizer
* enable -msym32 for modules again
(revert ca78b1a5c6a6e70e052d3ea253828e49b5d07c8a)
New XTLB refill handler looks like this:
80000080 dmfc0 k0,C0_BADVADDR
80000084 bltz k0,800000e4 # goto l_module_alloc
80000088 lui k1,0x8046 # %high(pgd_current)
8000008c ld k1,24600(k1) # %low(pgd_current)
80000090 dsrl k0,k0,0x1b # l_vmalloc_done:
80000094 andi k0,k0,0x1ff8
80000098 daddu k1,k1,k0
8000009c dmfc0 k0,C0_BADVADDR
800000a0 ld k1,0(k1)
800000a4 dsrl k0,k0,0x12
800000a8 andi k0,k0,0xff8
800000ac daddu k1,k1,k0
800000b0 dmfc0 k0,C0_XCONTEXT
800000b4 ld k1,0(k1)
800000b8 andi k0,k0,0xff0
800000bc daddu k1,k1,k0
800000c0 ld k0,0(k1)
800000c4 ld k1,8(k1)
800000c8 dsrl k0,k0,0x6
800000cc mtc0 k0,C0_ENTRYLO0
800000d0 dsrl k1,k1,0x6
800000d4 mtc0 k1,C0_ENTRYL01
800000d8 nop
800000dc tlbwr
800000e0 eret
800000e4 dsll k1,k0,0x2 # l_module_alloc:
800000e8 bgez k1,80000008 # goto l_vmalloc
800000ec lui k1,0xc000
800000f0 dsubu k0,k0,k1
800000f4 lui k1,0x8046 # %high(module_pg_dir)
800000f8 beq zero,zero,80000000
800000fc nop
80000000 beq zero,zero,80000090 # goto l_vmalloc_done
80000004 daddiu k1,k1,0x4000
80000008 dsll32 k1,k1,0x0 # l_vmalloc:
8000000c dsubu k0,k0,k1
80000010 beq zero,zero,80000090 # goto l_vmalloc_done
80000014 lui k1,0x8046 # %high(swapper_pg_dir)
Signed-off-by: Atsushi Nemoto <anemo@mba.ocn.ne.jp>
Signed-off-by: Ralf Baechle <ralf@linux-mips.org>
2006-10-26 00:08:31 +09:00
# ifdef MODULE_START
if ( unlikely ( address > = MODULE_START & & address < MODULE_END ) )
2009-09-02 15:47:34 -07:00
goto VMALLOC_FAULT_TARGET ;
[MIPS] Load modules to CKSEG0 if CONFIG_BUILD_ELF64=n
This is a patch to load 64-bit modules to CKSEG0 so that can be
compiled with -msym32 option. This makes each module ~10% smaller.
* introduce MODULE_START and MODULE_END
* custom module_alloc()
* PGD for modules
* change XTLB refill handler synthesizer
* enable -msym32 for modules again
(revert ca78b1a5c6a6e70e052d3ea253828e49b5d07c8a)
New XTLB refill handler looks like this:
80000080 dmfc0 k0,C0_BADVADDR
80000084 bltz k0,800000e4 # goto l_module_alloc
80000088 lui k1,0x8046 # %high(pgd_current)
8000008c ld k1,24600(k1) # %low(pgd_current)
80000090 dsrl k0,k0,0x1b # l_vmalloc_done:
80000094 andi k0,k0,0x1ff8
80000098 daddu k1,k1,k0
8000009c dmfc0 k0,C0_BADVADDR
800000a0 ld k1,0(k1)
800000a4 dsrl k0,k0,0x12
800000a8 andi k0,k0,0xff8
800000ac daddu k1,k1,k0
800000b0 dmfc0 k0,C0_XCONTEXT
800000b4 ld k1,0(k1)
800000b8 andi k0,k0,0xff0
800000bc daddu k1,k1,k0
800000c0 ld k0,0(k1)
800000c4 ld k1,8(k1)
800000c8 dsrl k0,k0,0x6
800000cc mtc0 k0,C0_ENTRYLO0
800000d0 dsrl k1,k1,0x6
800000d4 mtc0 k1,C0_ENTRYL01
800000d8 nop
800000dc tlbwr
800000e0 eret
800000e4 dsll k1,k0,0x2 # l_module_alloc:
800000e8 bgez k1,80000008 # goto l_vmalloc
800000ec lui k1,0xc000
800000f0 dsubu k0,k0,k1
800000f4 lui k1,0x8046 # %high(module_pg_dir)
800000f8 beq zero,zero,80000000
800000fc nop
80000000 beq zero,zero,80000090 # goto l_vmalloc_done
80000004 daddiu k1,k1,0x4000
80000008 dsll32 k1,k1,0x0 # l_vmalloc:
8000000c dsubu k0,k0,k1
80000010 beq zero,zero,80000090 # goto l_vmalloc_done
80000014 lui k1,0x8046 # %high(swapper_pg_dir)
Signed-off-by: Atsushi Nemoto <anemo@mba.ocn.ne.jp>
Signed-off-by: Ralf Baechle <ralf@linux-mips.org>
2006-10-26 00:08:31 +09:00
# endif
2005-04-16 15:20:36 -07:00
/*
* If we ' re in an interrupt or have no user
* context , we must not take the fault . .
*/
if ( in_atomic ( ) | | ! mm )
goto bad_area_nosemaphore ;
down_read ( & mm - > mmap_sem ) ;
vma = find_vma ( mm , address ) ;
if ( ! vma )
goto bad_area ;
if ( vma - > vm_start < = address )
goto good_area ;
if ( ! ( vma - > vm_flags & VM_GROWSDOWN ) )
goto bad_area ;
if ( expand_stack ( vma , address ) )
goto bad_area ;
/*
* Ok , we have a good vm_area for this memory access , so
* we can handle it . .
*/
good_area :
info . si_code = SEGV_ACCERR ;
if ( write ) {
if ( ! ( vma - > vm_flags & VM_WRITE ) )
goto bad_area ;
} else {
2010-02-10 15:12:47 -08:00
if ( kernel_uses_smartmips_rixi ) {
if ( address = = regs - > cp0_epc & & ! ( vma - > vm_flags & VM_EXEC ) ) {
#if 0
pr_notice ( " Cpu%d[%s:%d:%0*lx:%ld:%0*lx] XI violation \n " ,
raw_smp_processor_id ( ) ,
current - > comm , current - > pid ,
field , address , write ,
field , regs - > cp0_epc ) ;
# endif
goto bad_area ;
}
if ( ! ( vma - > vm_flags & VM_READ ) ) {
#if 0
pr_notice ( " Cpu%d[%s:%d:%0*lx:%ld:%0*lx] RI violation \n " ,
raw_smp_processor_id ( ) ,
current - > comm , current - > pid ,
field , address , write ,
field , regs - > cp0_epc ) ;
# endif
goto bad_area ;
}
} else {
if ( ! ( vma - > vm_flags & ( VM_READ | VM_WRITE | VM_EXEC ) ) )
goto bad_area ;
}
2005-04-16 15:20:36 -07:00
}
/*
* If for any reason at all we couldn ' t handle the fault ,
* make sure we exit gracefully rather than endlessly redo
* the fault .
*/
2009-04-10 09:01:23 -07:00
fault = handle_mm_fault ( mm , vma , address , write ? FAULT_FLAG_WRITE : 0 ) ;
2011-06-27 14:41:57 +02:00
perf_sw_event ( PERF_COUNT_SW_PAGE_FAULTS , 1 , regs , address ) ;
2007-07-19 01:47:05 -07:00
if ( unlikely ( fault & VM_FAULT_ERROR ) ) {
if ( fault & VM_FAULT_OOM )
goto out_of_memory ;
else if ( fault & VM_FAULT_SIGBUS )
goto do_sigbus ;
2005-04-16 15:20:36 -07:00
BUG ( ) ;
}
2010-10-12 19:37:21 +08:00
if ( fault & VM_FAULT_MAJOR ) {
2011-06-27 14:41:57 +02:00
perf_sw_event ( PERF_COUNT_SW_PAGE_FAULTS_MAJ , 1 , regs , address ) ;
2007-07-19 01:47:05 -07:00
tsk - > maj_flt + + ;
2010-10-12 19:37:21 +08:00
} else {
2011-06-27 14:41:57 +02:00
perf_sw_event ( PERF_COUNT_SW_PAGE_FAULTS_MIN , 1 , regs , address ) ;
2007-07-19 01:47:05 -07:00
tsk - > min_flt + + ;
2010-10-12 19:37:21 +08:00
}
2005-04-16 15:20:36 -07:00
up_read ( & mm - > mmap_sem ) ;
return ;
/*
* Something tried to access memory that isn ' t in our memory map . .
* Fix it , but check if it ' s kernel or user first . .
*/
bad_area :
up_read ( & mm - > mmap_sem ) ;
bad_area_nosemaphore :
/* User mode accesses just cause a SIGSEGV */
if ( user_mode ( regs ) ) {
tsk - > thread . cp0_badvaddr = address ;
tsk - > thread . error_code = write ;
#if 0
printk ( " do_page_fault() #2: sending SIGSEGV to %s for "
" invalid %s \n %0*lx (epc == %0*lx, ra == %0*lx) \n " ,
tsk - > comm ,
write ? " write access to " : " read access from " ,
field , address ,
field , ( unsigned long ) regs - > cp0_epc ,
field , ( unsigned long ) regs - > regs [ 31 ] ) ;
# endif
info . si_signo = SIGSEGV ;
info . si_errno = 0 ;
/* info.si_code has been set above */
2005-03-01 19:22:29 +00:00
info . si_addr = ( void __user * ) address ;
2005-04-16 15:20:36 -07:00
force_sig_info ( SIGSEGV , & info , tsk ) ;
return ;
}
no_context :
/* Are we prepared to handle this kernel fault? */
if ( fixup_exception ( regs ) ) {
current - > thread . cp0_baduaddr = address ;
return ;
}
/*
* Oops . The kernel tried to access some bad page . We ' ll have to
* terminate things with extreme prejudice .
*/
bust_spinlocks ( 1 ) ;
printk ( KERN_ALERT " CPU %d Unable to handle kernel paging request at "
" virtual address %0*lx, epc == %0*lx, ra == %0*lx \n " ,
2007-03-29 22:30:01 +01:00
raw_smp_processor_id ( ) , field , address , field , regs - > cp0_epc ,
2005-04-16 15:20:36 -07:00
field , regs - > regs [ 31 ] ) ;
die ( " Oops " , regs ) ;
out_of_memory :
2009-01-12 00:09:13 +00:00
/*
* We ran out of memory , call the OOM killer , and return the userspace
* ( which will retry the fault , or kill us if we got oom - killed ) .
*/
2009-07-04 01:33:09 +09:00
up_read ( & mm - > mmap_sem ) ;
2009-01-12 00:09:13 +00:00
pagefault_out_of_memory ( ) ;
return ;
2005-04-16 15:20:36 -07:00
do_sigbus :
up_read ( & mm - > mmap_sem ) ;
/* Kernel mode? Handle exceptions or die */
if ( ! user_mode ( regs ) )
goto no_context ;
2006-04-05 09:45:45 +01:00
else
2005-04-16 15:20:36 -07:00
/*
* Send a sigbus , regardless of whether we were in kernel
* or user mode .
*/
2006-04-05 09:45:45 +01:00
#if 0
printk ( " do_page_fault() #3: sending SIGBUS to %s for "
" invalid %s \n %0*lx (epc == %0*lx, ra == %0*lx) \n " ,
tsk - > comm ,
write ? " write access to " : " read access from " ,
field , address ,
field , ( unsigned long ) regs - > cp0_epc ,
field , ( unsigned long ) regs - > regs [ 31 ] ) ;
# endif
2005-04-16 15:20:36 -07:00
tsk - > thread . cp0_badvaddr = address ;
info . si_signo = SIGBUS ;
info . si_errno = 0 ;
info . si_code = BUS_ADRERR ;
2005-03-01 19:22:29 +00:00
info . si_addr = ( void __user * ) address ;
2005-04-16 15:20:36 -07:00
force_sig_info ( SIGBUS , & info , tsk ) ;
return ;
2009-09-02 15:47:34 -07:00
# ifndef CONFIG_64BIT
2005-04-16 15:20:36 -07:00
vmalloc_fault :
{
/*
* Synchronize this task ' s top level page - table
* with the ' reference ' page table .
*
* Do _not_ use " tsk " here . We might be inside
* an interrupt in the middle of a task switch . .
*/
int offset = __pgd_offset ( address ) ;
pgd_t * pgd , * pgd_k ;
2005-02-10 12:19:59 +00:00
pud_t * pud , * pud_k ;
2005-04-16 15:20:36 -07:00
pmd_t * pmd , * pmd_k ;
pte_t * pte_k ;
2007-03-29 22:30:01 +01:00
pgd = ( pgd_t * ) pgd_current [ raw_smp_processor_id ( ) ] + offset ;
2005-04-16 15:20:36 -07:00
pgd_k = init_mm . pgd + offset ;
if ( ! pgd_present ( * pgd_k ) )
goto no_context ;
set_pgd ( pgd , * pgd_k ) ;
2005-02-10 12:19:59 +00:00
pud = pud_offset ( pgd , address ) ;
pud_k = pud_offset ( pgd_k , address ) ;
if ( ! pud_present ( * pud_k ) )
goto no_context ;
pmd = pmd_offset ( pud , address ) ;
pmd_k = pmd_offset ( pud_k , address ) ;
2005-04-16 15:20:36 -07:00
if ( ! pmd_present ( * pmd_k ) )
goto no_context ;
set_pmd ( pmd , * pmd_k ) ;
pte_k = pte_offset_kernel ( pmd_k , address ) ;
if ( ! pte_present ( * pte_k ) )
goto no_context ;
return ;
}
2009-09-02 15:47:34 -07:00
# endif
2005-04-16 15:20:36 -07:00
}