2005-04-17 02:20:36 +04:00
/*
* linux / arch / arm / kernel / process . c
*
* Copyright ( C ) 1996 - 2000 Russell King - Converted to ARM .
* Original Copyright ( C ) 1995 Linus Torvalds
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation .
*/
# include <stdarg.h>
# include <linux/module.h>
# include <linux/sched.h>
# include <linux/kernel.h>
# include <linux/mm.h>
# include <linux/stddef.h>
# include <linux/unistd.h>
# include <linux/user.h>
# include <linux/delay.h>
# include <linux/reboot.h>
# include <linux/interrupt.h>
# include <linux/kallsyms.h>
# include <linux/init.h>
2005-11-03 01:24:33 +03:00
# include <linux/cpu.h>
2006-03-16 02:17:23 +03:00
# include <linux/elfcore.h>
2006-06-19 22:57:12 +04:00
# include <linux/pm.h>
2007-03-14 19:33:24 +03:00
# include <linux/tick.h>
2007-06-18 17:59:45 +04:00
# include <linux/utsname.h>
2008-09-06 14:35:55 +04:00
# include <linux/uaccess.h>
2010-06-15 00:27:19 +04:00
# include <linux/random.h>
2005-04-17 02:20:36 +04:00
2010-07-26 15:22:12 +04:00
# include <asm/cacheflush.h>
2005-04-17 02:20:36 +04:00
# include <asm/leds.h>
# include <asm/processor.h>
2006-01-03 20:39:34 +03:00
# include <asm/system.h>
2006-06-21 16:31:52 +04:00
# include <asm/thread_notify.h>
2009-02-11 15:07:53 +03:00
# include <asm/stacktrace.h>
2005-06-27 17:04:05 +04:00
# include <asm/mach/time.h>
2005-04-17 02:20:36 +04:00
2010-05-25 07:55:42 +04:00
# ifdef CONFIG_CC_STACKPROTECTOR
# include <linux/stackprotector.h>
unsigned long __stack_chk_guard __read_mostly ;
EXPORT_SYMBOL ( __stack_chk_guard ) ;
# endif
2007-01-09 15:57:37 +03:00
static const char * processor_modes [ ] = {
" USER_26 " , " FIQ_26 " , " IRQ_26 " , " SVC_26 " , " UK4_26 " , " UK5_26 " , " UK6_26 " , " UK7_26 " ,
" UK8_26 " , " UK9_26 " , " UK10_26 " , " UK11_26 " , " UK12_26 " , " UK13_26 " , " UK14_26 " , " UK15_26 " ,
" USER_32 " , " FIQ_32 " , " IRQ_32 " , " SVC_32 " , " UK4_32 " , " UK5_32 " , " UK6_32 " , " ABT_32 " ,
" UK8_32 " , " UK9_32 " , " UK10_32 " , " UND_32 " , " UK12_32 " , " UK13_32 " , " UK14_32 " , " SYS_32 "
} ;
2007-06-26 04:38:27 +04:00
static const char * isa_modes [ ] = {
" ARM " , " Thumb " , " Jazelle " , " ThumbEE "
} ;
2005-04-17 02:20:36 +04:00
extern void setup_mm_for_reboot ( char mode ) ;
static volatile int hlt_counter ;
2008-08-05 19:14:15 +04:00
# include <mach/system.h>
2005-04-17 02:20:36 +04:00
void disable_hlt ( void )
{
hlt_counter + + ;
}
EXPORT_SYMBOL ( disable_hlt ) ;
void enable_hlt ( void )
{
hlt_counter - - ;
}
EXPORT_SYMBOL ( enable_hlt ) ;
static int __init nohlt_setup ( char * __unused )
{
hlt_counter = 1 ;
return 1 ;
}
static int __init hlt_setup ( char * __unused )
{
hlt_counter = 0 ;
return 1 ;
}
__setup ( " nohlt " , nohlt_setup ) ;
__setup ( " hlt " , hlt_setup ) ;
2009-03-19 19:20:24 +03:00
void arm_machine_restart ( char mode , const char * cmd )
2006-06-19 22:57:12 +04:00
{
2010-07-26 15:22:12 +04:00
/* Disable interrupts first */
local_irq_disable ( ) ;
local_fiq_disable ( ) ;
2006-06-19 22:57:12 +04:00
/*
* Tell the mm system that we are going to reboot -
* we may need it to insert some 1 : 1 mappings so that
* soft boot works .
*/
setup_mm_for_reboot ( mode ) ;
2010-07-26 15:22:12 +04:00
/* Clean and invalidate caches */
flush_cache_all ( ) ;
/* Turn off caching */
cpu_proc_fin ( ) ;
/* Push out any further dirty data, and ensure cache is empty */
flush_cache_all ( ) ;
2006-06-19 22:57:12 +04:00
/*
* Now call the architecture specific reboot code .
*/
2009-03-19 19:20:24 +03:00
arch_reset ( mode , cmd ) ;
2006-06-19 22:57:12 +04:00
/*
* Whoops - the architecture was unable to reboot .
* Tell the user !
*/
mdelay ( 1000 ) ;
printk ( " Reboot failed -- System halted \n " ) ;
while ( 1 ) ;
}
2005-04-17 02:20:36 +04:00
/*
2006-06-19 22:57:12 +04:00
* Function pointers to optional machine specific functions
2005-04-17 02:20:36 +04:00
*/
void ( * pm_power_off ) ( void ) ;
EXPORT_SYMBOL ( pm_power_off ) ;
2009-03-19 19:20:24 +03:00
void ( * arm_pm_restart ) ( char str , const char * cmd ) = arm_machine_restart ;
2006-06-19 22:57:12 +04:00
EXPORT_SYMBOL_GPL ( arm_pm_restart ) ;
2005-04-17 02:20:36 +04:00
/*
* This is our default idle handler . We need to disable
* interrupts here to ensure we don ' t miss a wakeup call .
*/
2006-03-16 02:17:23 +03:00
static void default_idle ( void )
2005-04-17 02:20:36 +04:00
{
2009-06-23 01:34:55 +04:00
if ( ! need_resched ( ) )
arch_idle ( ) ;
local_irq_enable ( ) ;
2005-04-17 02:20:36 +04:00
}
2009-06-23 01:34:55 +04:00
void ( * pm_idle ) ( void ) = default_idle ;
EXPORT_SYMBOL ( pm_idle ) ;
2005-04-17 02:20:36 +04:00
/*
2009-06-23 01:34:55 +04:00
* The idle thread , has rather strange semantics for calling pm_idle ,
* but this is what x86 does and we need to do the same , so that
* things like cpuidle get called in the same way . The only difference
* is that we always respect ' hlt_counter ' to prevent low power idle .
2005-04-17 02:20:36 +04:00
*/
void cpu_idle ( void )
{
local_fiq_enable ( ) ;
/* endless idle loop with no priority at all */
while ( 1 ) {
2009-06-23 01:34:55 +04:00
tick_nohz_stop_sched_tick ( 1 ) ;
leds_event ( led_idle_start ) ;
while ( ! need_resched ( ) ) {
2005-11-03 01:24:33 +03:00
# ifdef CONFIG_HOTPLUG_CPU
2009-06-23 01:34:55 +04:00
if ( cpu_is_offline ( smp_processor_id ( ) ) )
cpu_die ( ) ;
2005-11-03 01:24:33 +03:00
# endif
2009-06-23 01:34:55 +04:00
local_irq_disable ( ) ;
if ( hlt_counter ) {
local_irq_enable ( ) ;
cpu_relax ( ) ;
} else {
stop_critical_timings ( ) ;
pm_idle ( ) ;
start_critical_timings ( ) ;
/*
* This will eventually be removed - pm_idle
* functions should always return with IRQs
* enabled .
*/
WARN_ON ( irqs_disabled ( ) ) ;
local_irq_enable ( ) ;
}
}
2005-04-17 02:20:36 +04:00
leds_event ( led_idle_end ) ;
2007-03-14 19:33:24 +03:00
tick_nohz_restart_sched_tick ( ) ;
2005-11-09 08:39:01 +03:00
preempt_enable_no_resched ( ) ;
2005-04-17 02:20:36 +04:00
schedule ( ) ;
2005-11-09 08:39:01 +03:00
preempt_disable ( ) ;
2005-04-17 02:20:36 +04:00
}
}
static char reboot_mode = ' h ' ;
int __init reboot_setup ( char * str )
{
reboot_mode = str [ 0 ] ;
return 1 ;
}
__setup ( " reboot= " , reboot_setup ) ;
2010-07-26 16:31:27 +04:00
void machine_shutdown ( void )
2005-04-17 02:20:36 +04:00
{
2010-07-26 16:31:27 +04:00
# ifdef CONFIG_SMP
smp_send_stop ( ) ;
# endif
2005-04-17 02:20:36 +04:00
}
2010-07-26 16:31:27 +04:00
void machine_halt ( void )
{
machine_shutdown ( ) ;
while ( 1 ) ;
}
2005-04-17 02:20:36 +04:00
void machine_power_off ( void )
{
2010-07-26 16:31:27 +04:00
machine_shutdown ( ) ;
2005-04-17 02:20:36 +04:00
if ( pm_power_off )
pm_power_off ( ) ;
}
2009-03-19 19:20:24 +03:00
void machine_restart ( char * cmd )
2005-04-17 02:20:36 +04:00
{
2010-07-26 16:31:27 +04:00
machine_shutdown ( ) ;
2009-03-19 19:20:24 +03:00
arm_pm_restart ( reboot_mode , cmd ) ;
2005-04-17 02:20:36 +04:00
}
2005-04-17 18:50:36 +04:00
void __show_regs ( struct pt_regs * regs )
2005-04-17 02:20:36 +04:00
{
2007-06-18 17:59:45 +04:00
unsigned long flags ;
char buf [ 64 ] ;
2005-04-17 02:20:36 +04:00
2007-06-18 17:59:45 +04:00
printk ( " CPU: %d %s (%s %.*s) \n " ,
2010-01-08 18:59:34 +03:00
raw_smp_processor_id ( ) , print_tainted ( ) ,
init_utsname ( ) - > release ,
2007-06-18 17:59:45 +04:00
( int ) strcspn ( init_utsname ( ) - > version , " " ) ,
init_utsname ( ) - > version ) ;
2005-04-17 02:20:36 +04:00
print_symbol ( " PC is at %s \n " , instruction_pointer ( regs ) ) ;
print_symbol ( " LR is at %s \n " , regs - > ARM_lr ) ;
2007-06-18 17:59:45 +04:00
printk ( " pc : [<%08lx>] lr : [<%08lx>] psr: %08lx \n "
2005-04-17 02:20:36 +04:00
" sp : %08lx ip : %08lx fp : %08lx \n " ,
2007-06-18 17:59:45 +04:00
regs - > ARM_pc , regs - > ARM_lr , regs - > ARM_cpsr ,
regs - > ARM_sp , regs - > ARM_ip , regs - > ARM_fp ) ;
2005-04-17 02:20:36 +04:00
printk ( " r10: %08lx r9 : %08lx r8 : %08lx \n " ,
regs - > ARM_r10 , regs - > ARM_r9 ,
regs - > ARM_r8 ) ;
printk ( " r7 : %08lx r6 : %08lx r5 : %08lx r4 : %08lx \n " ,
regs - > ARM_r7 , regs - > ARM_r6 ,
regs - > ARM_r5 , regs - > ARM_r4 ) ;
printk ( " r3 : %08lx r2 : %08lx r1 : %08lx r0 : %08lx \n " ,
regs - > ARM_r3 , regs - > ARM_r2 ,
regs - > ARM_r1 , regs - > ARM_r0 ) ;
2007-06-18 17:59:45 +04:00
flags = regs - > ARM_cpsr ;
buf [ 0 ] = flags & PSR_N_BIT ? ' N ' : ' n ' ;
buf [ 1 ] = flags & PSR_Z_BIT ? ' Z ' : ' z ' ;
buf [ 2 ] = flags & PSR_C_BIT ? ' C ' : ' c ' ;
buf [ 3 ] = flags & PSR_V_BIT ? ' V ' : ' v ' ;
buf [ 4 ] = ' \0 ' ;
2007-06-26 04:38:27 +04:00
printk ( " Flags: %s IRQs o%s FIQs o%s Mode %s ISA %s Segment %s \n " ,
2007-06-18 17:59:45 +04:00
buf , interrupts_enabled ( regs ) ? " n " : " ff " ,
2005-04-17 02:20:36 +04:00
fast_interrupts_enabled ( regs ) ? " n " : " ff " ,
processor_modes [ processor_mode ( regs ) ] ,
2007-06-26 04:38:27 +04:00
isa_modes [ isa_mode ( regs ) ] ,
2005-04-17 02:20:36 +04:00
get_fs ( ) = = get_ds ( ) ? " kernel " : " user " ) ;
2007-06-18 17:59:45 +04:00
# ifdef CONFIG_CPU_CP15
2005-04-17 02:20:36 +04:00
{
2006-09-26 12:36:37 +04:00
unsigned int ctrl ;
2007-06-18 17:59:45 +04:00
buf [ 0 ] = ' \0 ' ;
2006-09-26 12:36:37 +04:00
# ifdef CONFIG_CPU_CP15_MMU
2007-06-18 17:59:45 +04:00
{
unsigned int transbase , dac ;
asm ( " mrc p15, 0, %0, c2, c0 \n \t "
" mrc p15, 0, %1, c3, c0 \n "
: " =r " ( transbase ) , " =r " ( dac ) ) ;
snprintf ( buf , sizeof ( buf ) , " Table: %08x DAC: %08x " ,
transbase , dac ) ;
}
2006-09-26 12:36:37 +04:00
# endif
2007-06-18 17:59:45 +04:00
asm ( " mrc p15, 0, %0, c1, c0 \n " : " =r " ( ctrl ) ) ;
printk ( " Control: %08x%s \n " , ctrl , buf ) ;
}
2006-09-26 12:36:37 +04:00
# endif
2005-04-17 02:20:36 +04:00
}
2005-04-17 18:50:36 +04:00
void show_regs ( struct pt_regs * regs )
{
printk ( " \n " ) ;
2007-10-19 10:40:41 +04:00
printk ( " Pid: %d, comm: %20s \n " , task_pid_nr ( current ) , current - > comm ) ;
2005-04-17 18:50:36 +04:00
__show_regs ( regs ) ;
__backtrace ( ) ;
}
2009-12-18 17:34:43 +03:00
ATOMIC_NOTIFIER_HEAD ( thread_notify_head ) ;
EXPORT_SYMBOL_GPL ( thread_notify_head ) ;
2005-04-17 02:20:36 +04:00
/*
* Free current thread data structures etc . .
*/
void exit_thread ( void )
{
2009-12-18 17:34:43 +03:00
thread_notify ( THREAD_NOTIFY_EXIT , current_thread_info ( ) ) ;
2005-04-17 02:20:36 +04:00
}
void flush_thread ( void )
{
struct thread_info * thread = current_thread_info ( ) ;
struct task_struct * tsk = current ;
memset ( thread - > used_cp , 0 , sizeof ( thread - > used_cp ) ) ;
memset ( & tsk - > thread . debug , 0 , sizeof ( struct debug_info ) ) ;
2006-06-21 16:31:52 +04:00
memset ( & thread - > fpstate , 0 , sizeof ( union fp_state ) ) ;
thread_notify ( THREAD_NOTIFY_FLUSH , thread ) ;
2005-04-17 02:20:36 +04:00
}
void release_thread ( struct task_struct * dead_task )
{
}
asmlinkage void ret_from_fork ( void ) __asm__ ( " ret_from_fork " ) ;
int
2009-04-03 03:56:59 +04:00
copy_thread ( unsigned long clone_flags , unsigned long stack_start ,
2005-04-17 02:20:36 +04:00
unsigned long stk_sz , struct task_struct * p , struct pt_regs * regs )
{
2006-01-12 12:05:57 +03:00
struct thread_info * thread = task_thread_info ( p ) ;
struct pt_regs * childregs = task_pt_regs ( p ) ;
2005-04-17 02:20:36 +04:00
* childregs = * regs ;
childregs - > ARM_r0 = 0 ;
childregs - > ARM_sp = stack_start ;
memset ( & thread - > cpu_context , 0 , sizeof ( struct cpu_context_save ) ) ;
thread - > cpu_context . sp = ( unsigned long ) childregs ;
thread - > cpu_context . pc = ( unsigned long ) ret_from_fork ;
if ( clone_flags & CLONE_SETTLS )
thread - > tp_value = regs - > ARM_r3 ;
return 0 ;
}
2009-10-13 11:54:30 +04:00
/*
* Fill in the task ' s elfregs structure for a core dump .
*/
int dump_task_regs ( struct task_struct * t , elf_gregset_t * elfregs )
{
elf_core_copy_regs ( elfregs , task_pt_regs ( t ) ) ;
return 1 ;
}
2005-04-17 02:20:36 +04:00
/*
* fill in the fpe structure for a core dump . . .
*/
int dump_fpu ( struct pt_regs * regs , struct user_fp * fp )
{
struct thread_info * thread = current_thread_info ( ) ;
int used_math = thread - > used_cp [ 1 ] | thread - > used_cp [ 2 ] ;
if ( used_math )
memcpy ( fp , & thread - > fpstate . soft , sizeof ( * fp ) ) ;
return used_math ! = 0 ;
}
EXPORT_SYMBOL ( dump_fpu ) ;
/*
* Shuffle the argument into the correct register before calling the
2010-07-10 13:10:18 +04:00
* thread function . r4 is the thread argument , r5 is the pointer to
* the thread function , and r6 points to the exit function .
2005-04-17 02:20:36 +04:00
*/
extern void kernel_thread_helper ( void ) ;
2010-04-19 13:15:03 +04:00
asm ( " .pushsection .text \n "
2005-04-17 02:20:36 +04:00
" .align \n "
" .type kernel_thread_helper, #function \n "
" kernel_thread_helper: \n "
2010-07-10 13:10:18 +04:00
# ifdef CONFIG_TRACE_IRQFLAGS
" bl trace_hardirqs_on \n "
# endif
" msr cpsr_c, r7 \n "
" mov r0, r4 \n "
" mov lr, r6 \n "
" mov pc, r5 \n "
2005-04-17 02:20:36 +04:00
" .size kernel_thread_helper, . - kernel_thread_helper \n "
2010-04-19 13:15:03 +04:00
" .popsection " ) ;
2005-04-17 02:20:36 +04:00
2009-06-19 19:43:08 +04:00
# ifdef CONFIG_ARM_UNWIND
extern void kernel_thread_exit ( long code ) ;
2010-04-19 13:15:03 +04:00
asm ( " .pushsection .text \n "
2009-06-19 19:43:08 +04:00
" .align \n "
" .type kernel_thread_exit, #function \n "
" kernel_thread_exit: \n "
" .fnstart \n "
" .cantunwind \n "
" bl do_exit \n "
" nop \n "
" .fnend \n "
" .size kernel_thread_exit, . - kernel_thread_exit \n "
2010-04-19 13:15:03 +04:00
" .popsection " ) ;
2009-06-19 19:43:08 +04:00
# else
# define kernel_thread_exit do_exit
# endif
2005-04-17 02:20:36 +04:00
/*
* Create a kernel thread .
*/
pid_t kernel_thread ( int ( * fn ) ( void * ) , void * arg , unsigned long flags )
{
struct pt_regs regs ;
memset ( & regs , 0 , sizeof ( regs ) ) ;
2010-07-10 13:10:18 +04:00
regs . ARM_r4 = ( unsigned long ) arg ;
regs . ARM_r5 = ( unsigned long ) fn ;
regs . ARM_r6 = ( unsigned long ) kernel_thread_exit ;
regs . ARM_r7 = SVC_MODE | PSR_ENDSTATE | PSR_ISETSTATE ;
2005-04-17 02:20:36 +04:00
regs . ARM_pc = ( unsigned long ) kernel_thread_helper ;
2010-07-10 13:10:18 +04:00
regs . ARM_cpsr = regs . ARM_r7 | PSR_I_BIT ;
2005-04-17 02:20:36 +04:00
return do_fork ( flags | CLONE_VM | CLONE_UNTRACED , 0 , & regs , 0 , NULL , NULL ) ;
}
EXPORT_SYMBOL ( kernel_thread ) ;
unsigned long get_wchan ( struct task_struct * p )
{
2009-02-11 15:07:53 +03:00
struct stackframe frame ;
2005-04-17 02:20:36 +04:00
int count = 0 ;
if ( ! p | | p = = current | | p - > state = = TASK_RUNNING )
return 0 ;
2009-02-11 15:07:53 +03:00
frame . fp = thread_saved_fp ( p ) ;
frame . sp = thread_saved_sp ( p ) ;
frame . lr = 0 ; /* recovered from the stack */
frame . pc = thread_saved_pc ( p ) ;
2005-04-17 02:20:36 +04:00
do {
2009-02-11 15:07:53 +03:00
int ret = unwind_frame ( & frame ) ;
if ( ret < 0 )
2005-04-17 02:20:36 +04:00
return 0 ;
2009-02-11 15:07:53 +03:00
if ( ! in_sched_functions ( frame . pc ) )
return frame . pc ;
2005-04-17 02:20:36 +04:00
} while ( count + + < 16 ) ;
return 0 ;
}
2010-06-15 00:27:19 +04:00
unsigned long arch_randomize_brk ( struct mm_struct * mm )
{
unsigned long range_end = mm - > brk + 0x02000000 ;
return randomize_range ( mm - > brk , range_end , 0 ) ? : mm - > brk ;
}