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
*
* This source code is licensed under the GNU General Public License ,
* Version 2. See the file COPYING for more details .
*/
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>
# 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 ;
2012-10-11 20:14:58 +04:00
int ( * _machine_kexec_prepare ) ( struct kimage * ) = NULL ;
void ( * _machine_kexec_shutdown ) ( void ) = NULL ;
void ( * _machine_crash_shutdown ) ( struct pt_regs * regs ) = NULL ;
# ifdef CONFIG_SMP
void ( * relocated_kexec_smp_wait ) ( void * ) ;
atomic_t kexec_ready_to_reboot = ATOMIC_INIT ( 0 ) ;
# endif
2006-10-18 17:14:55 +04:00
int
machine_kexec_prepare ( struct kimage * kimage )
{
2012-10-11 20:14:58 +04:00
if ( _machine_kexec_prepare )
return _machine_kexec_prepare ( kimage ) ;
2006-10-18 17:14:55 +04:00
return 0 ;
}
void
machine_kexec_cleanup ( struct kimage * kimage )
{
}
void
machine_shutdown ( void )
{
2012-10-11 20:14:58 +04:00
if ( _machine_kexec_shutdown )
_machine_kexec_shutdown ( ) ;
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
}
2012-10-11 20:14:58 +04:00
typedef void ( * noretfun_t ) ( void ) __noreturn ;
2007-07-31 18:16:32 +04:00
2006-10-18 17:14:55 +04:00
void
machine_kexec ( struct kimage * image )
{
unsigned long reboot_code_buffer ;
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
}
/*
* 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 " ) ;
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
2007-07-31 18:16:32 +04:00
( ( noretfun_t ) reboot_code_buffer ) ( ) ;
2006-10-18 17:14:55 +04:00
}