2007-02-06 21:29:00 +01:00
/*
* machine_kexec . c - handle transition of Linux booting another kernel
*/
# include <linux/mm.h>
# include <linux/kexec.h>
# include <linux/delay.h>
# include <linux/reboot.h>
2008-09-06 12:10:45 +01:00
# include <linux/io.h>
2012-02-03 14:48:01 +01:00
# include <linux/irq.h>
2012-09-21 18:56:11 +01:00
# include <linux/memblock.h>
2007-02-06 21:29:00 +01:00
# include <asm/pgtable.h>
2012-09-21 18:56:02 +01:00
# include <linux/of_fdt.h>
2007-02-06 21:29:00 +01:00
# include <asm/pgalloc.h>
# include <asm/mmu_context.h>
# include <asm/cacheflush.h>
# include <asm/mach-types.h>
2012-03-28 18:30:01 +01:00
# include <asm/system_misc.h>
2007-02-06 21:29:00 +01:00
2009-02-09 21:35:39 +01:00
extern const unsigned char relocate_new_kernel [ ] ;
extern const unsigned int relocate_new_kernel_size ;
2007-02-06 21:29:00 +01:00
extern unsigned long kexec_start_address ;
extern unsigned long kexec_indirection_page ;
extern unsigned long kexec_mach_type ;
2008-01-02 00:56:46 +01:00
extern unsigned long kexec_boot_atags ;
2007-02-06 21:29:00 +01:00
2010-12-03 10:53:38 +01:00
static atomic_t waiting_for_crash_ipi ;
2007-02-06 21:29:00 +01:00
/*
* Provide a dummy crash_notes definition while crash dump arrives to arm .
* This prevents breakage of crash_notes attribute in kernel / ksysfs . c .
*/
int machine_kexec_prepare ( struct kimage * image )
{
2012-09-21 18:56:02 +01:00
struct kexec_segment * current_segment ;
__be32 header ;
int i , err ;
/*
* No segment at default ATAGs address . try to locate
* a dtb using magic .
*/
for ( i = 0 ; i < image - > nr_segments ; i + + ) {
current_segment = & image - > segment [ i ] ;
2012-10-16 19:35:14 +01:00
if ( ! memblock_is_region_memory ( current_segment - > mem ,
current_segment - > memsz ) )
return - EINVAL ;
2012-09-21 18:56:11 +01:00
2012-09-21 18:56:02 +01:00
err = get_user ( header , ( __be32 * ) current_segment - > buf ) ;
if ( err )
return err ;
if ( be32_to_cpu ( header ) = = OF_DT_HEADER )
kexec_boot_atags = current_segment - > mem ;
}
2007-02-06 21:29:00 +01:00
return 0 ;
}
void machine_kexec_cleanup ( struct kimage * image )
{
}
2010-12-03 10:53:38 +01:00
void machine_crash_nonpanic_core ( void * unused )
{
struct pt_regs regs ;
crash_setup_regs ( & regs , NULL ) ;
printk ( KERN_DEBUG " CPU %u will stop doing anything useful since another CPU has crashed \n " ,
smp_processor_id ( ) ) ;
crash_save_cpu ( & regs , smp_processor_id ( ) ) ;
flush_cache_all ( ) ;
atomic_dec ( & waiting_for_crash_ipi ) ;
while ( 1 )
cpu_relax ( ) ;
}
2012-02-03 14:48:01 +01:00
static void machine_kexec_mask_interrupts ( void )
{
unsigned int i ;
struct irq_desc * desc ;
for_each_irq_desc ( i , desc ) {
struct irq_chip * chip ;
chip = irq_desc_get_chip ( desc ) ;
if ( ! chip )
continue ;
if ( chip - > irq_eoi & & irqd_irq_inprogress ( & desc - > irq_data ) )
chip - > irq_eoi ( & desc - > irq_data ) ;
if ( chip - > irq_mask )
chip - > irq_mask ( & desc - > irq_data ) ;
if ( chip - > irq_disable & & ! irqd_irq_disabled ( & desc - > irq_data ) )
chip - > irq_disable ( & desc - > irq_data ) ;
}
}
2007-02-06 21:29:00 +01:00
void machine_crash_shutdown ( struct pt_regs * regs )
{
2010-12-03 10:53:38 +01:00
unsigned long msecs ;
2010-05-10 09:23:37 +01:00
local_irq_disable ( ) ;
2010-12-03 10:53:38 +01:00
atomic_set ( & waiting_for_crash_ipi , num_online_cpus ( ) - 1 ) ;
smp_call_function ( machine_crash_nonpanic_core , NULL , false ) ;
msecs = 1000 ; /* Wait at most a second for the other cpus to stop */
while ( ( atomic_read ( & waiting_for_crash_ipi ) > 0 ) & & msecs ) {
mdelay ( 1 ) ;
msecs - - ;
}
if ( atomic_read ( & waiting_for_crash_ipi ) > 0 )
printk ( KERN_WARNING " Non-crashing CPUs did not react to IPI \n " ) ;
2010-05-10 09:23:37 +01:00
crash_save_cpu ( regs , smp_processor_id ( ) ) ;
2012-02-03 14:48:01 +01:00
machine_kexec_mask_interrupts ( ) ;
2010-05-10 09:23:37 +01:00
printk ( KERN_INFO " Loading crashdump kernel... \n " ) ;
2007-02-06 21:29:00 +01:00
}
2011-02-02 17:16:09 -05:00
/*
* Function pointer to optional machine - specific reinitialization
*/
void ( * kexec_reinit ) ( void ) ;
2007-02-06 21:29:00 +01:00
void machine_kexec ( struct kimage * image )
{
2011-11-07 19:04:36 +00:00
unsigned long page_list ;
2007-02-06 21:29:00 +01:00
unsigned long reboot_code_buffer_phys ;
void * reboot_code_buffer ;
ARM: 7759/1: decouple CPU offlining from reboot/shutdown
Add comments to machine_shutdown()/halt()/power_off()/restart() that
describe their purpose and/or requirements re: CPUs being active/not.
In machine_shutdown(), replace the call to smp_send_stop() with a call to
disable_nonboot_cpus(). This completely disables all but one CPU, thus
satisfying the requirement that only a single CPU be active for kexec.
Adjust Kconfig dependencies for this change.
In machine_halt()/power_off()/restart(), call smp_send_stop() directly,
rather than via machine_shutdown(); these functions don't need to
completely de-activate all CPUs using hotplug, but rather just quiesce
them.
Remove smp_kill_cpus(), and its call from smp_send_stop().
smp_kill_cpus() was indirectly calling smp_ops.cpu_kill() without calling
smp_ops.cpu_die() on the target CPUs first. At least some implementations
of smp_ops had issues with this; it caused cpu_kill() to hang on Tegra,
for example. Since smp_send_stop() is only used for shutdown, halt, and
power-off, there is no need to attempt any kind of CPU hotplug here.
Adjust Kconfig to reflect that machine_shutdown() (and hence kexec)
relies upon disable_nonboot_cpus(). However, this alone doesn't guarantee
that hotplug will work, or even that hotplug is implemented for a
particular piece of HW that a multi-platform zImage runs on. Hence, add
error-checking to machine_kexec() to determine whether it did work.
Suggested-by: Russell King <rmk+kernel@arm.linux.org.uk>
Signed-off-by: Stephen Warren <swarren@nvidia.com>
Acked-by: Will Deacon <will.deacon@arm.com>
Tested-by: Zhangfei Gao <zhangfei.gao@gmail.com>
Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
2013-06-14 16:14:14 +01:00
if ( num_online_cpus ( ) > 1 ) {
pr_err ( " kexec: error: multiple CPUs still online \n " ) ;
return ;
}
2011-11-07 19:04:36 +00:00
page_list = image - > head & PAGE_MASK ;
2007-02-06 21:29:00 +01:00
/* we need both effective and real address here */
reboot_code_buffer_phys =
page_to_pfn ( image - > control_code_page ) < < PAGE_SHIFT ;
reboot_code_buffer = page_address ( image - > control_code_page ) ;
2011-11-07 19:04:36 +00:00
/* Prepare parameters for reboot_code_buffer*/
kexec_start_address = image - > start ;
kexec_indirection_page = page_list ;
kexec_mach_type = machine_arch_type ;
2012-09-21 18:56:02 +01:00
if ( ! kexec_boot_atags )
kexec_boot_atags = image - > start - KEXEC_ARM_ZIMAGE_OFFSET + KEXEC_ARM_ATAGS_OFFSET ;
2011-11-07 19:04:36 +00:00
/* copy our kernel relocation code to the control code page */
memcpy ( reboot_code_buffer ,
relocate_new_kernel , relocate_new_kernel_size ) ;
flush_icache_range ( ( unsigned long ) reboot_code_buffer ,
( unsigned long ) reboot_code_buffer + KEXEC_CONTROL_PAGE_SIZE ) ;
2007-02-06 21:29:00 +01:00
printk ( KERN_INFO " Bye! \n " ) ;
2011-02-02 17:16:09 -05:00
if ( kexec_reinit )
kexec_reinit ( ) ;
2011-06-06 12:35:46 +01:00
soft_restart ( reboot_code_buffer_phys ) ;
2007-02-06 21:29:00 +01:00
}