2018-12-28 11:32:24 +03:00
// SPDX-License-Identifier: GPL-2.0
2006-01-17 09:14:10 +03:00
/*
* machine_kexec . c - handle transition of Linux booting another kernel
* Copyright ( C ) 2002 - 2003 Eric Biederman < ebiederm @ xmission . com >
*
* GameCube / ppc32 port Copyright ( C ) 2004 Albert Herranz
* LANDISK / sh4 supported by kogiidena
*/
# include <linux/mm.h>
# include <linux/kexec.h>
# include <linux/delay.h>
# include <linux/reboot.h>
2008-08-04 08:42:49 +04:00
# include <linux/numa.h>
2009-03-18 13:07:16 +03:00
# include <linux/ftrace.h>
2009-03-18 11:51:29 +03:00
# include <linux/suspend.h>
2010-07-12 08:36:09 +04:00
# include <linux/memblock.h>
2006-01-17 09:14:10 +03:00
# include <asm/mmu_context.h>
# include <asm/io.h>
# include <asm/cacheflush.h>
2010-01-12 08:50:43 +03:00
# include <asm/sh_bios.h>
2010-01-20 10:42:52 +03:00
# include <asm/reboot.h>
2006-01-17 09:14:10 +03:00
2009-03-18 11:51:29 +03:00
typedef void ( * relocate_new_kernel_t ) ( unsigned long indirection_page ,
unsigned long reboot_code_buffer ,
unsigned long start_address ) ;
2006-01-17 09:14:10 +03:00
2006-06-26 20:57:34 +04:00
extern const unsigned char relocate_new_kernel [ ] ;
extern const unsigned int relocate_new_kernel_size ;
2009-03-18 11:51:29 +03:00
extern void * vbr_base ;
2006-01-17 09:14:10 +03:00
2010-01-20 10:42:52 +03:00
void native_machine_crash_shutdown ( struct pt_regs * regs )
2006-01-17 09:14:10 +03:00
{
2010-01-20 10:42:52 +03:00
/* Nothing to do for UP, but definitely broken for SMP.. */
2006-01-17 09:14:10 +03:00
}
/*
* Do what every setup is needed on image and the
* reboot code buffer to allow us to avoid allocations
* later .
*/
int machine_kexec_prepare ( struct kimage * image )
{
return 0 ;
}
void machine_kexec_cleanup ( struct kimage * image )
{
}
static void kexec_info ( struct kimage * image )
{
int i ;
printk ( " kexec information \n " ) ;
for ( i = 0 ; i < image - > nr_segments ; i + + ) {
printk ( " segment[%d]: 0x%08x - 0x%08x (0x%08x) \n " ,
i ,
( unsigned int ) image - > segment [ i ] . mem ,
2007-04-27 06:25:57 +04:00
( unsigned int ) image - > segment [ i ] . mem +
image - > segment [ i ] . memsz ,
2006-01-17 09:14:10 +03:00
( unsigned int ) image - > segment [ i ] . memsz ) ;
2007-04-27 06:25:57 +04:00
}
2006-01-17 09:14:10 +03:00
printk ( " start : 0x%08x \n \n " , ( unsigned int ) image - > start ) ;
}
/*
* Do not allocate memory ( or fail in any way ) in machine_kexec ( ) .
* We are past the point of no return , committed to rebooting now .
*/
2008-07-26 06:45:07 +04:00
void machine_kexec ( struct kimage * image )
2006-01-17 09:14:10 +03:00
{
unsigned long page_list ;
unsigned long reboot_code_buffer ;
relocate_new_kernel_t rnk ;
2009-03-18 11:49:45 +03:00
unsigned long entry ;
unsigned long * ptr ;
2009-03-18 13:07:16 +03:00
int save_ftrace_enabled ;
2009-03-18 11:49:45 +03:00
/*
* Nicked from the mips version of machine_kexec ( ) :
* The generic kexec code builds a page list with physical
* addresses . Use phys_to_virt ( ) to convert them to virtual .
*/
for ( ptr = & image - > head ; ( entry = * ptr ) & & ! ( entry & IND_DONE ) ;
ptr = ( entry & IND_INDIRECTION ) ?
phys_to_virt ( entry & PAGE_MASK ) : ptr + 1 ) {
if ( * ptr & IND_SOURCE | | * ptr & IND_INDIRECTION | |
* ptr & IND_DESTINATION )
* ptr = ( unsigned long ) phys_to_virt ( * ptr ) ;
}
2006-01-17 09:14:10 +03:00
2009-03-18 11:51:29 +03:00
# ifdef CONFIG_KEXEC_JUMP
if ( image - > preserve_context )
save_processor_state ( ) ;
# endif
2009-03-18 13:07:16 +03:00
save_ftrace_enabled = __ftrace_enabled_save ( ) ;
2006-01-17 09:14:10 +03:00
/* Interrupts aren't acceptable while we reboot */
local_irq_disable ( ) ;
page_list = image - > head ;
/* we need both effective and real address here */
reboot_code_buffer =
( unsigned long ) page_address ( image - > control_code_page ) ;
/* copy our kernel relocation code to the control code page */
memcpy ( ( void * ) reboot_code_buffer , relocate_new_kernel ,
relocate_new_kernel_size ) ;
2009-03-18 13:06:15 +03:00
kexec_info ( image ) ;
2006-01-17 09:14:10 +03:00
flush_cache_all ( ) ;
2010-01-12 08:50:43 +03:00
sh_bios_vbr_reload ( ) ;
2009-03-18 13:06:15 +03:00
2006-01-17 09:14:10 +03:00
/* now call it */
rnk = ( relocate_new_kernel_t ) reboot_code_buffer ;
2009-03-19 13:04:29 +03:00
( * rnk ) ( page_list , reboot_code_buffer ,
( unsigned long ) phys_to_virt ( image - > start ) ) ;
2009-03-18 11:51:29 +03:00
# ifdef CONFIG_KEXEC_JUMP
asm volatile ( " ldc %0, vbr " : : " r " ( & vbr_base ) : " memory " ) ;
2009-03-18 13:06:15 +03:00
2009-03-18 11:51:29 +03:00
if ( image - > preserve_context )
restore_processor_state ( ) ;
/* Convert page list back to physical addresses, what a mess. */
for ( ptr = & image - > head ; ( entry = * ptr ) & & ! ( entry & IND_DONE ) ;
ptr = ( * ptr & IND_INDIRECTION ) ?
phys_to_virt ( * ptr & PAGE_MASK ) : ptr + 1 ) {
if ( * ptr & IND_SOURCE | | * ptr & IND_INDIRECTION | |
* ptr & IND_DESTINATION )
* ptr = virt_to_phys ( * ptr ) ;
}
# endif
2009-03-18 13:07:16 +03:00
__ftrace_enabled_restore ( save_ftrace_enabled ) ;
2006-01-17 09:14:10 +03:00
}
2008-08-04 08:42:49 +04:00
void arch_crash_save_vmcoreinfo ( void )
{
# ifdef CONFIG_NUMA
VMCOREINFO_SYMBOL ( node_data ) ;
VMCOREINFO_LENGTH ( node_data , MAX_NUMNODES ) ;
# endif
2010-05-07 12:14:00 +04:00
# ifdef CONFIG_X2TLB
VMCOREINFO_CONFIG ( X2TLB ) ;
# endif
2008-08-04 08:42:49 +04:00
}
2010-05-07 09:54:55 +04:00
void __init reserve_crashkernel ( void )
{
unsigned long long crash_size , crash_base ;
int ret ;
2010-07-12 08:36:09 +04:00
ret = parse_crashkernel ( boot_command_line , memblock_phys_mem_size ( ) ,
2010-05-07 09:54:55 +04:00
& crash_size , & crash_base ) ;
if ( ret = = 0 & & crash_size > 0 ) {
crashk_res . start = crash_base ;
crashk_res . end = crash_base + crash_size - 1 ;
}
if ( crashk_res . end = = crashk_res . start )
goto disable ;
2011-06-09 20:13:32 +04:00
crash_size = PAGE_ALIGN ( resource_size ( & crashk_res ) ) ;
2010-05-07 09:54:55 +04:00
if ( ! crashk_res . start ) {
2010-07-12 08:36:09 +04:00
unsigned long max = memblock_end_of_DRAM ( ) - memory_limit ;
2019-03-12 09:29:31 +03:00
crashk_res . start = memblock_phys_alloc_range ( crash_size ,
PAGE_SIZE , 0 , max ) ;
2010-05-07 09:54:55 +04:00
if ( ! crashk_res . start ) {
pr_err ( " crashkernel allocation failed \n " ) ;
goto disable ;
}
} else {
2010-07-12 08:36:09 +04:00
ret = memblock_reserve ( crashk_res . start , crash_size ) ;
2010-05-07 09:54:55 +04:00
if ( unlikely ( ret < 0 ) ) {
pr_err ( " crashkernel reservation failed - "
" memory is in use \n " ) ;
goto disable ;
}
}
2010-05-10 15:17:25 +04:00
crashk_res . end = crashk_res . start + crash_size - 1 ;
/*
* Crash kernel trumps memory limit
*/
2010-07-12 08:36:09 +04:00
if ( ( memblock_end_of_DRAM ( ) - memory_limit ) < = crashk_res . end ) {
2010-05-10 15:17:25 +04:00
memory_limit = 0 ;
pr_info ( " Disabled memory limit for crashkernel \n " ) ;
}
pr_info ( " Reserving %ldMB of memory at 0x%08lx "
2010-05-07 09:54:55 +04:00
" for crashkernel (System RAM: %ldMB) \n " ,
( unsigned long ) ( crash_size > > 20 ) ,
2010-05-10 15:17:25 +04:00
( unsigned long ) ( crashk_res . start ) ,
2010-07-12 08:36:09 +04:00
( unsigned long ) ( memblock_phys_mem_size ( ) > > 20 ) ) ;
2010-05-07 09:54:55 +04:00
return ;
disable :
crashk_res . start = crashk_res . end = 0 ;
}