2005-06-25 14:58:11 -07:00
/*
* arch / s390 / kernel / machine_kexec . c
*
2011-10-30 15:16:40 +01:00
* Copyright IBM Corp . 2005 , 2011
2005-06-25 14:58:11 -07:00
*
2006-12-04 15:40:33 +01:00
* Author ( s ) : Rolf Adelsberger ,
* Heiko Carstens < heiko . carstens @ de . ibm . com >
2011-10-30 15:16:40 +01:00
* Michael Holzheu < holzheu @ linux . vnet . ibm . com >
2005-06-25 14:58:11 -07:00
*/
# include <linux/device.h>
# include <linux/mm.h>
# include <linux/kexec.h>
# include <linux/delay.h>
2007-02-05 21:16:47 +01:00
# include <linux/reboot.h>
2011-03-15 17:08:33 +01:00
# include <linux/ftrace.h>
2006-02-11 17:56:01 -08:00
# include <asm/cio.h>
# include <asm/setup.h>
2005-06-25 14:58:11 -07:00
# include <asm/pgtable.h>
# include <asm/pgalloc.h>
# include <asm/system.h>
2006-02-11 17:56:01 -08:00
# include <asm/smp.h>
2006-12-04 15:40:26 +01:00
# include <asm/reset.h>
2007-03-05 23:35:45 +01:00
# include <asm/ipl.h>
2011-10-30 15:16:40 +01:00
# include <asm/diag.h>
# include <asm/asm-offsets.h>
2005-06-25 14:58:11 -07:00
2006-12-04 15:40:33 +01:00
typedef void ( * relocate_kernel_t ) ( kimage_entry_t * , unsigned long ) ;
2005-06-25 14:58:11 -07:00
2006-06-26 18:57:34 +02:00
extern const unsigned char relocate_kernel [ ] ;
extern const unsigned long long relocate_kernel_len ;
2005-06-25 14:58:11 -07:00
2011-10-30 15:16:40 +01:00
# ifdef CONFIG_CRASH_DUMP
void * fill_cpu_elf_notes ( void * ptr , struct save_area * sa ) ;
/*
* Create ELF notes for one CPU
*/
static void add_elf_notes ( int cpu )
{
struct save_area * sa = ( void * ) 4608 + store_prefix ( ) ;
void * ptr ;
memcpy ( ( void * ) ( 4608UL + sa - > pref_reg ) , sa , sizeof ( * sa ) ) ;
ptr = ( u64 * ) per_cpu_ptr ( crash_notes , cpu ) ;
ptr = fill_cpu_elf_notes ( ptr , sa ) ;
memset ( ptr , 0 , sizeof ( struct elf_note ) ) ;
}
/*
* Store status of next available physical CPU
*/
static int store_status_next ( int start_cpu , int this_cpu )
{
struct save_area * sa = ( void * ) 4608 + store_prefix ( ) ;
int cpu , rc ;
for ( cpu = start_cpu ; cpu < 65536 ; cpu + + ) {
if ( cpu = = this_cpu )
continue ;
do {
rc = raw_sigp ( cpu , sigp_stop_and_store_status ) ;
} while ( rc = = sigp_busy ) ;
if ( rc ! = sigp_order_code_accepted )
continue ;
if ( sa - > pref_reg )
return cpu ;
}
return - 1 ;
}
/*
* Initialize CPU ELF notes
*/
void setup_regs ( void )
{
unsigned long sa = S390_lowcore . prefixreg_save_area + SAVE_AREA_BASE ;
int cpu , this_cpu , phys_cpu = 0 , first = 1 ;
this_cpu = stap ( ) ;
if ( ! S390_lowcore . prefixreg_save_area )
first = 0 ;
for_each_online_cpu ( cpu ) {
if ( first ) {
add_elf_notes ( cpu ) ;
first = 0 ;
continue ;
}
phys_cpu = store_status_next ( phys_cpu , this_cpu ) ;
if ( phys_cpu = = - 1 )
break ;
add_elf_notes ( cpu ) ;
phys_cpu + + ;
}
/* Copy dump CPU store status info to absolute zero */
memcpy ( ( void * ) SAVE_AREA_BASE , ( void * ) sa , sizeof ( struct save_area ) ) ;
}
# endif
/*
* Start kdump : We expect here that a store status has been done on our CPU
*/
static void __do_machine_kdump ( void * image )
{
# ifdef CONFIG_CRASH_DUMP
int ( * start_kdump ) ( int ) = ( void * ) ( ( struct kimage * ) image ) - > start ;
2011-10-30 15:16:50 +01:00
__load_psw_mask ( PSW_MASK_BASE | PSW_DEFAULT_KEY | PSW_MASK_EA | PSW_MASK_BA ) ;
2011-10-30 15:16:40 +01:00
setup_regs ( ) ;
start_kdump ( 1 ) ;
# endif
}
/*
* Check if kdump checksums are valid : We call purgatory with parameter " 0 "
*/
static int kdump_csum_valid ( struct kimage * image )
{
# ifdef CONFIG_CRASH_DUMP
int ( * start_kdump ) ( int ) = ( void * ) image - > start ;
int rc ;
__arch_local_irq_stnsm ( 0xfb ) ; /* disable DAT */
rc = start_kdump ( 0 ) ;
__arch_local_irq_stosm ( 0x04 ) ; /* enable DAT */
return rc ? 0 : - EINVAL ;
# else
return - EINVAL ;
# endif
}
2011-10-30 15:16:44 +01:00
/*
* Map or unmap crashkernel memory
*/
static void crash_map_pages ( int enable )
{
unsigned long size = resource_size ( & crashk_res ) ;
BUG_ON ( crashk_res . start % KEXEC_CRASH_MEM_ALIGN | |
size % KEXEC_CRASH_MEM_ALIGN ) ;
if ( enable )
vmem_add_mapping ( crashk_res . start , size ) ;
else
vmem_remove_mapping ( crashk_res . start , size ) ;
}
/*
* Map crashkernel memory
*/
void crash_map_reserved_pages ( void )
{
crash_map_pages ( 1 ) ;
}
/*
* Unmap crashkernel memory
*/
void crash_unmap_reserved_pages ( void )
{
crash_map_pages ( 0 ) ;
}
2011-10-30 15:16:40 +01:00
/*
* Give back memory to hypervisor before new kdump is loaded
*/
static int machine_kexec_prepare_kdump ( void )
{
# ifdef CONFIG_CRASH_DUMP
if ( MACHINE_IS_VM )
diag10_range ( PFN_DOWN ( crashk_res . start ) ,
PFN_DOWN ( crashk_res . end - crashk_res . start + 1 ) ) ;
return 0 ;
# else
return - EINVAL ;
# endif
}
2006-12-04 15:40:33 +01:00
int machine_kexec_prepare ( struct kimage * image )
2005-06-25 14:58:11 -07:00
{
2006-12-04 15:40:33 +01:00
void * reboot_code_buffer ;
2005-06-25 14:58:11 -07:00
2007-03-05 23:35:45 +01:00
/* Can't replace kernel image since it is read-only. */
if ( ipl_flags & IPL_NSS_VALID )
return - ENOSYS ;
2011-10-30 15:16:40 +01:00
if ( image - > type = = KEXEC_TYPE_CRASH )
return machine_kexec_prepare_kdump ( ) ;
2005-06-25 14:58:11 -07:00
/* We don't support anything but the default image type for now. */
if ( image - > type ! = KEXEC_TYPE_DEFAULT )
return - EINVAL ;
/* Get the destination where the assembler code should be copied to.*/
2006-12-04 15:40:33 +01:00
reboot_code_buffer = ( void * ) page_to_phys ( image - > control_code_page ) ;
2005-06-25 14:58:11 -07:00
/* Then copy it */
2006-12-04 15:40:33 +01:00
memcpy ( reboot_code_buffer , relocate_kernel , relocate_kernel_len ) ;
2005-06-25 14:58:11 -07:00
return 0 ;
}
2006-12-04 15:40:33 +01:00
void machine_kexec_cleanup ( struct kimage * image )
2005-06-25 14:58:11 -07:00
{
}
2011-10-30 15:16:40 +01:00
void arch_crash_save_vmcoreinfo ( void )
{
VMCOREINFO_SYMBOL ( lowcore_ptr ) ;
2011-12-27 11:27:24 +01:00
VMCOREINFO_SYMBOL ( high_memory ) ;
2011-10-30 15:16:40 +01:00
VMCOREINFO_LENGTH ( lowcore_ptr , NR_CPUS ) ;
}
2006-12-04 15:40:33 +01:00
void machine_shutdown ( void )
2005-06-25 14:58:11 -07:00
{
}
2011-10-30 15:16:40 +01:00
/*
* Do normal kexec
*/
static void __do_machine_kexec ( void * data )
2005-06-25 14:58:11 -07:00
{
relocate_kernel_t data_mover ;
2010-02-26 22:37:34 +01:00
struct kimage * image = data ;
2005-06-25 14:58:11 -07:00
2006-12-04 15:40:33 +01:00
data_mover = ( relocate_kernel_t ) page_to_phys ( image - > control_code_page ) ;
2005-06-25 14:58:11 -07:00
/* Call the moving routine */
2006-12-04 15:40:33 +01:00
( * data_mover ) ( & image - > head , image - > start ) ;
2005-06-25 14:58:11 -07:00
}
2010-02-26 22:37:34 +01:00
2011-10-30 15:16:40 +01:00
/*
* Reset system and call either kdump or normal kexec
*/
static void __machine_kexec ( void * data )
{
struct kimage * image = data ;
pfault_fini ( ) ;
if ( image - > type = = KEXEC_TYPE_CRASH )
s390_reset_system ( __do_machine_kdump , data ) ;
else
s390_reset_system ( __do_machine_kexec , data ) ;
disabled_wait ( ( unsigned long ) __builtin_return_address ( 0 ) ) ;
}
/*
* Do either kdump or normal kexec . In case of kdump we first ask
* purgatory , if kdump checksums are valid .
*/
2010-02-26 22:37:34 +01:00
void machine_kexec ( struct kimage * image )
{
2011-10-30 15:16:40 +01:00
if ( image - > type = = KEXEC_TYPE_CRASH & & ! kdump_csum_valid ( image ) )
return ;
2011-03-15 17:08:33 +01:00
tracer_disable ( ) ;
2010-02-26 22:37:34 +01:00
smp_send_stop ( ) ;
smp_switch_to_ipl_cpu ( __machine_kexec , image ) ;
}