2019-06-03 08:44:46 +03:00
// SPDX-License-Identifier: GPL-2.0-only
2006-10-18 17:14:55 +04:00
/*
* machine_kexec . c for kexec
* Created by < nschichan @ corp . free . fr > on Thu Oct 12 15 : 15 : 06 2006
*/
2012-10-11 20:14:58 +04:00
# include <linux/compiler.h>
2006-10-18 17:14:55 +04:00
# include <linux/kexec.h>
# include <linux/mm.h>
# include <linux/delay.h>
2018-09-12 00:49:24 +03:00
# include <linux/libfdt.h>
2006-10-18 17:14:55 +04:00
# include <asm/cacheflush.h>
# include <asm/page.h>
2007-02-17 22:11:19 +03:00
extern const unsigned char relocate_new_kernel [ ] ;
2007-07-31 18:16:32 +04:00
extern const size_t relocate_new_kernel_size ;
2006-10-18 17:14:55 +04:00
extern unsigned long kexec_start_address ;
extern unsigned long kexec_indirection_page ;
2018-09-12 00:49:21 +03:00
static unsigned long reboot_code_buffer ;
2012-10-11 20:14:58 +04:00
# ifdef CONFIG_SMP
2018-09-12 00:49:21 +03:00
static void ( * relocated_kexec_smp_wait ) ( void * ) ;
2012-10-11 20:14:58 +04:00
atomic_t kexec_ready_to_reboot = ATOMIC_INIT ( 0 ) ;
2016-10-11 23:54:26 +03:00
void ( * _crash_smp_send_stop ) ( void ) = NULL ;
2012-10-11 20:14:58 +04:00
# endif
2018-09-12 00:49:21 +03:00
void ( * _machine_kexec_shutdown ) ( void ) = NULL ;
void ( * _machine_crash_shutdown ) ( struct pt_regs * regs ) = NULL ;
2016-11-23 16:43:51 +03:00
static void kexec_image_info ( const struct kimage * kimage )
{
unsigned long i ;
pr_debug ( " kexec kimage info: \n " ) ;
pr_debug ( " type: %d \n " , kimage - > type ) ;
pr_debug ( " start: %lx \n " , kimage - > start ) ;
pr_debug ( " head: %lx \n " , kimage - > head ) ;
pr_debug ( " nr_segments: %lu \n " , kimage - > nr_segments ) ;
for ( i = 0 ; i < kimage - > nr_segments ; i + + ) {
pr_debug ( " segment[%lu]: %016lx - %016lx, 0x%lx bytes, %lu pages \n " ,
i ,
kimage - > segment [ i ] . mem ,
kimage - > segment [ i ] . mem + kimage - > segment [ i ] . memsz ,
( unsigned long ) kimage - > segment [ i ] . memsz ,
( unsigned long ) kimage - > segment [ i ] . memsz / PAGE_SIZE ) ;
}
}
2018-09-12 00:49:24 +03:00
# ifdef CONFIG_UHI_BOOT
static int uhi_machine_kexec_prepare ( struct kimage * kimage )
{
int i ;
/*
* In case DTB file is not passed to the new kernel , a flat device
* tree will be created by kexec tool . It holds modified command
* line for the new kernel .
*/
for ( i = 0 ; i < kimage - > nr_segments ; i + + ) {
struct fdt_header fdt ;
if ( kimage - > segment [ i ] . memsz < = sizeof ( fdt ) )
continue ;
if ( copy_from_user ( & fdt , kimage - > segment [ i ] . buf , sizeof ( fdt ) ) )
continue ;
if ( fdt_check_header ( & fdt ) )
continue ;
kexec_args [ 0 ] = - 2 ;
kexec_args [ 1 ] = ( unsigned long )
phys_to_virt ( ( unsigned long ) kimage - > segment [ i ] . mem ) ;
break ;
}
return 0 ;
}
int ( * _machine_kexec_prepare ) ( struct kimage * ) = uhi_machine_kexec_prepare ;
# else
int ( * _machine_kexec_prepare ) ( struct kimage * ) = NULL ;
# endif /* CONFIG_UHI_BOOT */
2006-10-18 17:14:55 +04:00
int
machine_kexec_prepare ( struct kimage * kimage )
{
2018-09-12 00:49:21 +03:00
# ifdef CONFIG_SMP
if ( ! kexec_nonboot_cpu_func ( ) )
return - EINVAL ;
# endif
2016-11-23 16:43:51 +03:00
kexec_image_info ( kimage ) ;
2012-10-11 20:14:58 +04:00
if ( _machine_kexec_prepare )
return _machine_kexec_prepare ( kimage ) ;
2018-09-12 00:49:21 +03:00
2006-10-18 17:14:55 +04:00
return 0 ;
}
void
machine_kexec_cleanup ( struct kimage * kimage )
{
}
2018-09-12 00:49:21 +03:00
# ifdef CONFIG_SMP
static void kexec_shutdown_secondary ( void * param )
{
int cpu = smp_processor_id ( ) ;
if ( ! cpu_online ( cpu ) )
return ;
/* We won't be sent IPIs any more. */
set_cpu_online ( cpu , false ) ;
local_irq_disable ( ) ;
while ( ! atomic_read ( & kexec_ready_to_reboot ) )
cpu_relax ( ) ;
kexec_reboot ( ) ;
/* NOTREACHED */
}
# endif
2006-10-18 17:14:55 +04:00
void
machine_shutdown ( void )
{
2012-10-11 20:14:58 +04:00
if ( _machine_kexec_shutdown )
_machine_kexec_shutdown ( ) ;
2018-09-12 00:49:21 +03:00
# ifdef CONFIG_SMP
smp_call_function ( kexec_shutdown_secondary , NULL , 0 ) ;
while ( num_online_cpus ( ) > 1 ) {
cpu_relax ( ) ;
mdelay ( 1 ) ;
}
# endif
2006-10-18 17:14:55 +04:00
}
void
machine_crash_shutdown ( struct pt_regs * regs )
{
2012-10-11 20:14:58 +04:00
if ( _machine_crash_shutdown )
_machine_crash_shutdown ( regs ) ;
else
default_machine_crash_shutdown ( regs ) ;
2006-10-18 17:14:55 +04:00
}
2018-09-12 00:49:21 +03:00
# ifdef CONFIG_SMP
void kexec_nonboot_cpu_jump ( void )
{
local_flush_icache_range ( ( unsigned long ) relocated_kexec_smp_wait ,
reboot_code_buffer + relocate_new_kernel_size ) ;
relocated_kexec_smp_wait ( NULL ) ;
}
# endif
void kexec_reboot ( void )
{
void ( * do_kexec ) ( void ) __noreturn ;
2018-09-26 22:49:10 +03:00
/*
* We know we were online , and there will be no incoming IPIs at
* this point . Mark online again before rebooting so that the crash
* analysis tool will see us correctly .
*/
set_cpu_online ( smp_processor_id ( ) , true ) ;
/* Ensure remote CPUs observe that we're online before rebooting. */
smp_mb__after_atomic ( ) ;
2018-09-12 00:49:21 +03:00
# ifdef CONFIG_SMP
if ( smp_processor_id ( ) > 0 ) {
/*
* Instead of cpu_relax ( ) or wait , this is needed for kexec
* smp reboot . Kdump usually doesn ' t require an smp new
* kernel , but kexec may do .
*/
kexec_nonboot_cpu ( ) ;
/* NOTREACHED */
}
# endif
/*
* Make sure we get correct instructions written by the
* machine_kexec ( ) CPU .
*/
local_flush_icache_range ( reboot_code_buffer ,
reboot_code_buffer + relocate_new_kernel_size ) ;
do_kexec = ( void * ) reboot_code_buffer ;
do_kexec ( ) ;
}
2007-07-31 18:16:32 +04:00
2006-10-18 17:14:55 +04:00
void
machine_kexec ( struct kimage * image )
{
unsigned long entry ;
unsigned long * ptr ;
reboot_code_buffer =
( unsigned long ) page_address ( image - > control_code_page ) ;
2012-10-11 20:14:58 +04:00
kexec_start_address =
( unsigned long ) phys_to_virt ( image - > start ) ;
2014-07-31 15:42:29 +04:00
if ( image - > type = = KEXEC_TYPE_DEFAULT ) {
kexec_indirection_page =
( unsigned long ) phys_to_virt ( image - > head & PAGE_MASK ) ;
} else {
kexec_indirection_page = ( unsigned long ) & image - > head ;
}
2006-10-18 17:14:55 +04:00
memcpy ( ( void * ) reboot_code_buffer , relocate_new_kernel ,
relocate_new_kernel_size ) ;
/*
* The generic kexec code builds a page list with physical
* addresses . they are directly accessible through KSEG0 ( or
* CKSEG0 or XPHYS if on 64 bit system ) , hence the
2012-10-11 20:14:58 +04:00
* phys_to_virt ( ) call .
2006-10-18 17:14:55 +04:00
*/
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 )
2007-07-31 18:16:32 +04:00
* ptr = ( unsigned long ) phys_to_virt ( * ptr ) ;
2006-10-18 17:14:55 +04:00
}
2018-09-12 00:49:20 +03:00
/* Mark offline BEFORE disabling local irq. */
set_cpu_online ( smp_processor_id ( ) , false ) ;
2006-10-18 17:14:55 +04:00
/*
* we do not want to be bothered .
*/
local_irq_disable ( ) ;
2007-07-31 18:16:32 +04:00
printk ( " Will call new kernel at %08lx \n " , image - > start ) ;
2006-10-18 17:14:55 +04:00
printk ( " Bye ... \n " ) ;
2018-09-12 00:49:21 +03:00
/* Make reboot code buffer available to the boot CPU. */
2007-08-20 17:57:38 +04:00
__flush_cache_all ( ) ;
2012-10-11 20:14:58 +04:00
# ifdef CONFIG_SMP
/* All secondary cpus now may jump to kexec_wait cycle */
relocated_kexec_smp_wait = reboot_code_buffer +
( void * ) ( kexec_smp_wait - relocate_new_kernel ) ;
smp_wmb ( ) ;
atomic_set ( & kexec_ready_to_reboot , 1 ) ;
# endif
2018-09-12 00:49:21 +03:00
kexec_reboot ( ) ;
2006-10-18 17:14:55 +04:00
}