2011-10-30 15:16:40 +01:00
/*
* S390 kdump implementation
*
* Copyright IBM Corp . 2011
* Author ( s ) : Michael Holzheu < holzheu @ linux . vnet . ibm . com >
*/
# include <linux/crash_dump.h>
# include <asm/lowcore.h>
# include <linux/kernel.h>
# include <linux/module.h>
# include <linux/gfp.h>
# include <linux/slab.h>
# include <linux/bootmem.h>
# include <linux/elf.h>
2015-10-29 10:59:15 +01:00
# include <asm/asm-offsets.h>
2014-01-29 18:16:01 +01:00
# include <linux/memblock.h>
2012-03-11 11:59:34 -04:00
# include <asm/os_info.h>
2012-09-14 14:11:32 +02:00
# include <asm/elf.h>
# include <asm/ipl.h>
2013-09-11 14:24:54 -07:00
# include <asm/sclp.h>
2011-10-30 15:16:40 +01:00
# define PTR_ADD(x, y) (((char *) (x)) + ((unsigned long) (y)))
# define PTR_SUB(x, y) (((char *) (x)) - ((unsigned long) (y)))
# define PTR_DIFF(x, y) ((unsigned long)(((char *) (x)) - ((unsigned long) (y))))
2014-01-29 18:16:01 +01:00
static struct memblock_region oldmem_region ;
static struct memblock_type oldmem_type = {
. cnt = 1 ,
. max = 1 ,
. total_size = 0 ,
. regions = & oldmem_region ,
} ;
2015-10-29 10:59:15 +01:00
struct save_area {
struct list_head list ;
u64 psw [ 2 ] ;
u64 ctrs [ 16 ] ;
u64 gprs [ 16 ] ;
u32 acrs [ 16 ] ;
u64 fprs [ 16 ] ;
u32 fpc ;
u32 prefix ;
u64 todpreg ;
u64 timer ;
u64 todcmp ;
u64 vxrs_low [ 16 ] ;
__vector128 vxrs_high [ 16 ] ;
} ;
static LIST_HEAD ( dump_save_areas ) ;
/*
* Allocate a save area
*/
struct save_area * __init save_area_alloc ( bool is_boot_cpu )
{
struct save_area * sa ;
sa = ( void * ) memblock_alloc ( sizeof ( * sa ) , 8 ) ;
if ( is_boot_cpu )
list_add ( & sa - > list , & dump_save_areas ) ;
else
list_add_tail ( & sa - > list , & dump_save_areas ) ;
return sa ;
}
/*
* Return the address of the save area for the boot CPU
*/
struct save_area * __init save_area_boot_cpu ( void )
{
if ( list_empty ( & dump_save_areas ) )
return NULL ;
return list_first_entry ( & dump_save_areas , struct save_area , list ) ;
}
/*
* Copy CPU registers into the save area
*/
void __init save_area_add_regs ( struct save_area * sa , void * regs )
{
2015-12-31 10:29:00 +01:00
struct lowcore * lc ;
2015-10-29 10:59:15 +01:00
2015-12-31 10:29:00 +01:00
lc = ( struct lowcore * ) ( regs - __LC_FPREGS_SAVE_AREA ) ;
2015-10-29 10:59:15 +01:00
memcpy ( & sa - > psw , & lc - > psw_save_area , sizeof ( sa - > psw ) ) ;
memcpy ( & sa - > ctrs , & lc - > cregs_save_area , sizeof ( sa - > ctrs ) ) ;
memcpy ( & sa - > gprs , & lc - > gpregs_save_area , sizeof ( sa - > gprs ) ) ;
memcpy ( & sa - > acrs , & lc - > access_regs_save_area , sizeof ( sa - > acrs ) ) ;
memcpy ( & sa - > fprs , & lc - > floating_pt_save_area , sizeof ( sa - > fprs ) ) ;
memcpy ( & sa - > fpc , & lc - > fpt_creg_save_area , sizeof ( sa - > fpc ) ) ;
memcpy ( & sa - > prefix , & lc - > prefixreg_save_area , sizeof ( sa - > prefix ) ) ;
memcpy ( & sa - > todpreg , & lc - > tod_progreg_save_area , sizeof ( sa - > todpreg ) ) ;
memcpy ( & sa - > timer , & lc - > cpu_timer_save_area , sizeof ( sa - > timer ) ) ;
memcpy ( & sa - > todcmp , & lc - > clock_comp_save_area , sizeof ( sa - > todcmp ) ) ;
}
/*
* Copy vector registers into the save area
*/
void __init save_area_add_vxrs ( struct save_area * sa , __vector128 * vxrs )
{
int i ;
/* Copy lower halves of vector registers 0-15 */
for ( i = 0 ; i < 16 ; i + + )
memcpy ( & sa - > vxrs_low [ i ] , & vxrs [ i ] . u [ 2 ] , 8 ) ;
/* Copy vector registers 16-31 */
memcpy ( sa - > vxrs_high , vxrs + 16 , 16 * sizeof ( __vector128 ) ) ;
}
2013-10-11 10:29:23 +02:00
2013-07-18 12:18:27 +02:00
/*
* Return physical address for virtual address
*/
static inline void * load_real_addr ( void * addr )
{
unsigned long real_addr ;
asm volatile (
" lra %0,0(%1) \n "
" jz 0f \n "
" la %0,0 \n "
" 0: "
: " =a " ( real_addr ) : " a " ( addr ) : " cc " ) ;
return ( void * ) real_addr ;
}
/*
2015-10-12 10:43:37 +02:00
* Copy memory of the old , dumped system to a kernel space virtual address
2013-07-18 12:18:27 +02:00
*/
2015-10-12 10:43:37 +02:00
int copy_oldmem_kernel ( void * dst , void * src , size_t count )
2013-09-11 14:24:54 -07:00
{
2015-10-12 10:43:37 +02:00
unsigned long from , len ;
void * ra ;
2013-09-11 14:24:54 -07:00
int rc ;
2015-10-12 10:43:37 +02:00
while ( count ) {
from = __pa ( src ) ;
if ( ! OLDMEM_BASE & & from < sclp . hsa_size ) {
/* Copy from zfcpdump HSA area */
len = min ( count , sclp . hsa_size - from ) ;
rc = memcpy_hsa_kernel ( dst , from , len ) ;
if ( rc )
return rc ;
} else {
/* Check for swapped kdump oldmem areas */
if ( OLDMEM_BASE & & from - OLDMEM_BASE < OLDMEM_SIZE ) {
from - = OLDMEM_BASE ;
len = min ( count , OLDMEM_SIZE - from ) ;
} else if ( OLDMEM_BASE & & from < OLDMEM_SIZE ) {
len = min ( count , OLDMEM_SIZE - from ) ;
from + = OLDMEM_BASE ;
} else {
len = count ;
}
if ( is_vmalloc_or_module_addr ( dst ) ) {
ra = load_real_addr ( dst ) ;
len = min ( PAGE_SIZE - offset_in_page ( ra ) , len ) ;
} else {
ra = dst ;
}
if ( memcpy_real ( ra , ( void * ) from , len ) )
return - EFAULT ;
}
dst + = len ;
src + = len ;
count - = len ;
2013-09-11 14:24:54 -07:00
}
2015-10-12 10:43:37 +02:00
return 0 ;
2013-09-11 14:24:54 -07:00
}
/*
2015-10-12 10:43:37 +02:00
* Copy memory of the old , dumped system to a user space virtual address
2011-10-30 15:16:40 +01:00
*/
2015-10-12 10:43:37 +02:00
int copy_oldmem_user ( void __user * dst , void * src , size_t count )
2011-10-30 15:16:40 +01:00
{
2015-10-12 10:43:37 +02:00
unsigned long from , len ;
2013-07-18 12:18:27 +02:00
int rc ;
2011-10-30 15:16:40 +01:00
2015-10-12 10:43:37 +02:00
while ( count ) {
from = __pa ( src ) ;
if ( ! OLDMEM_BASE & & from < sclp . hsa_size ) {
/* Copy from zfcpdump HSA area */
len = min ( count , sclp . hsa_size - from ) ;
rc = memcpy_hsa_user ( dst , from , len ) ;
if ( rc )
return rc ;
} else {
/* Check for swapped kdump oldmem areas */
if ( OLDMEM_BASE & & from - OLDMEM_BASE < OLDMEM_SIZE ) {
from - = OLDMEM_BASE ;
len = min ( count , OLDMEM_SIZE - from ) ;
} else if ( OLDMEM_BASE & & from < OLDMEM_SIZE ) {
len = min ( count , OLDMEM_SIZE - from ) ;
from + = OLDMEM_BASE ;
} else {
len = count ;
}
rc = copy_to_user_real ( dst , ( void * ) from , count ) ;
if ( rc )
return rc ;
}
dst + = len ;
src + = len ;
count - = len ;
}
return 0 ;
2013-09-11 14:24:54 -07:00
}
/*
* Copy one page from " oldmem "
*/
ssize_t copy_oldmem_page ( unsigned long pfn , char * buf , size_t csize ,
unsigned long offset , int userbuf )
{
2015-10-12 10:43:37 +02:00
void * src ;
int rc ;
2013-09-11 14:24:54 -07:00
if ( ! csize )
return 0 ;
2015-10-12 10:43:37 +02:00
src = ( void * ) ( pfn < < PAGE_SHIFT ) + offset ;
if ( userbuf )
rc = copy_oldmem_user ( ( void __force __user * ) buf , src , csize ) ;
2013-09-11 14:24:54 -07:00
else
2015-10-12 10:43:37 +02:00
rc = copy_oldmem_kernel ( ( void * ) buf , src , csize ) ;
return rc ;
2011-10-30 15:16:40 +01:00
}
2013-09-11 14:24:52 -07:00
/*
2013-09-11 14:24:54 -07:00
* Remap " oldmem " for kdump
2013-09-11 14:24:52 -07:00
*
* For the kdump reserved memory this functions performs a swap operation :
* [ 0 - OLDMEM_SIZE ] is mapped to [ OLDMEM_BASE - OLDMEM_BASE + OLDMEM_SIZE ]
*/
2013-09-11 14:24:54 -07:00
static int remap_oldmem_pfn_range_kdump ( struct vm_area_struct * vma ,
unsigned long from , unsigned long pfn ,
unsigned long size , pgprot_t prot )
2013-09-11 14:24:52 -07:00
{
unsigned long size_old ;
int rc ;
if ( pfn < OLDMEM_SIZE > > PAGE_SHIFT ) {
size_old = min ( size , OLDMEM_SIZE - ( pfn < < PAGE_SHIFT ) ) ;
rc = remap_pfn_range ( vma , from ,
pfn + ( OLDMEM_BASE > > PAGE_SHIFT ) ,
size_old , prot ) ;
if ( rc | | size = = size_old )
return rc ;
size - = size_old ;
from + = size_old ;
pfn + = size_old > > PAGE_SHIFT ;
}
return remap_pfn_range ( vma , from , pfn , size , prot ) ;
}
2013-09-11 14:24:54 -07:00
/*
* Remap " oldmem " for zfcpdump
*
2013-11-13 10:38:27 +01:00
* We only map available memory above HSA size . Memory below HSA size
* is read on demand using the copy_oldmem_page ( ) function .
2013-09-11 14:24:54 -07:00
*/
static int remap_oldmem_pfn_range_zfcpdump ( struct vm_area_struct * vma ,
unsigned long from ,
unsigned long pfn ,
unsigned long size , pgprot_t prot )
{
2015-05-06 13:18:59 +02:00
unsigned long hsa_end = sclp . hsa_size ;
2013-09-11 14:24:54 -07:00
unsigned long size_hsa ;
2013-11-13 10:38:27 +01:00
if ( pfn < hsa_end > > PAGE_SHIFT ) {
size_hsa = min ( size , hsa_end - ( pfn < < PAGE_SHIFT ) ) ;
2013-09-11 14:24:54 -07:00
if ( size = = size_hsa )
return 0 ;
size - = size_hsa ;
from + = size_hsa ;
pfn + = size_hsa > > PAGE_SHIFT ;
}
return remap_pfn_range ( vma , from , pfn , size , prot ) ;
}
/*
* Remap " oldmem " for kdump or zfcpdump
*/
int remap_oldmem_pfn_range ( struct vm_area_struct * vma , unsigned long from ,
unsigned long pfn , unsigned long size , pgprot_t prot )
{
if ( OLDMEM_BASE )
return remap_oldmem_pfn_range_kdump ( vma , from , pfn , size , prot ) ;
else
return remap_oldmem_pfn_range_zfcpdump ( vma , from , pfn , size ,
prot ) ;
}
2011-10-30 15:16:40 +01:00
/*
* Alloc memory and panic in case of ENOMEM
*/
static void * kzalloc_panic ( int len )
{
void * rc ;
rc = kzalloc ( len , GFP_KERNEL ) ;
if ( ! rc )
panic ( " s390 kdump kzalloc (%d) failed " , len ) ;
return rc ;
}
/*
* Initialize ELF note
*/
2015-10-29 10:59:15 +01:00
static void * nt_init_name ( void * buf , Elf64_Word type , void * desc , int d_len ,
const char * name )
2011-10-30 15:16:40 +01:00
{
Elf64_Nhdr * note ;
u64 len ;
note = ( Elf64_Nhdr * ) buf ;
note - > n_namesz = strlen ( name ) + 1 ;
note - > n_descsz = d_len ;
note - > n_type = type ;
len = sizeof ( Elf64_Nhdr ) ;
memcpy ( buf + len , name , note - > n_namesz ) ;
len = roundup ( len + note - > n_namesz , 4 ) ;
memcpy ( buf + len , desc , note - > n_descsz ) ;
len = roundup ( len + note - > n_descsz , 4 ) ;
return PTR_ADD ( buf , len ) ;
}
2015-10-29 10:59:15 +01:00
static inline void * nt_init ( void * buf , Elf64_Word type , void * desc , int d_len )
2011-10-30 15:16:40 +01:00
{
2015-10-29 10:59:15 +01:00
return nt_init_name ( buf , type , desc , d_len , KEXEC_CORE_NOTE_NAME ) ;
2011-10-30 15:16:40 +01:00
}
/*
2015-10-29 10:59:15 +01:00
* Fill ELF notes for one CPU with save area registers
2011-10-30 15:16:40 +01:00
*/
2015-10-29 10:59:15 +01:00
static void * fill_cpu_elf_notes ( void * ptr , int cpu , struct save_area * sa )
2011-10-30 15:16:40 +01:00
{
2015-10-29 10:59:15 +01:00
struct elf_prstatus nt_prstatus ;
2011-10-30 15:16:40 +01:00
elf_fpregset_t nt_fpregset ;
2015-10-29 10:59:15 +01:00
/* Prepare prstatus note */
memset ( & nt_prstatus , 0 , sizeof ( nt_prstatus ) ) ;
memcpy ( & nt_prstatus . pr_reg . gprs , sa - > gprs , sizeof ( sa - > gprs ) ) ;
memcpy ( & nt_prstatus . pr_reg . psw , sa - > psw , sizeof ( sa - > psw ) ) ;
memcpy ( & nt_prstatus . pr_reg . acrs , sa - > acrs , sizeof ( sa - > acrs ) ) ;
nt_prstatus . pr_pid = cpu ;
/* Prepare fpregset (floating point) note */
2011-10-30 15:16:40 +01:00
memset ( & nt_fpregset , 0 , sizeof ( nt_fpregset ) ) ;
2015-10-29 10:59:15 +01:00
memcpy ( & nt_fpregset . fpc , & sa - > fpc , sizeof ( sa - > fpc ) ) ;
memcpy ( & nt_fpregset . fprs , & sa - > fprs , sizeof ( sa - > fprs ) ) ;
/* Create ELF notes for the CPU */
ptr = nt_init ( ptr , NT_PRSTATUS , & nt_prstatus , sizeof ( nt_prstatus ) ) ;
ptr = nt_init ( ptr , NT_PRFPREG , & nt_fpregset , sizeof ( nt_fpregset ) ) ;
ptr = nt_init ( ptr , NT_S390_TIMER , & sa - > timer , sizeof ( sa - > timer ) ) ;
ptr = nt_init ( ptr , NT_S390_TODCMP , & sa - > todcmp , sizeof ( sa - > todcmp ) ) ;
ptr = nt_init ( ptr , NT_S390_TODPREG , & sa - > todpreg , sizeof ( sa - > todpreg ) ) ;
ptr = nt_init ( ptr , NT_S390_CTRS , & sa - > ctrs , sizeof ( sa - > ctrs ) ) ;
ptr = nt_init ( ptr , NT_S390_PREFIX , & sa - > prefix , sizeof ( sa - > prefix ) ) ;
if ( MACHINE_HAS_VX ) {
ptr = nt_init ( ptr , NT_S390_VXRS_HIGH ,
& sa - > vxrs_high , sizeof ( sa - > vxrs_high ) ) ;
ptr = nt_init ( ptr , NT_S390_VXRS_LOW ,
& sa - > vxrs_low , sizeof ( sa - > vxrs_low ) ) ;
2014-10-06 17:57:43 +02:00
}
2011-10-30 15:16:40 +01:00
return ptr ;
}
/*
* Initialize prpsinfo note ( new kernel )
*/
static void * nt_prpsinfo ( void * ptr )
{
struct elf_prpsinfo prpsinfo ;
memset ( & prpsinfo , 0 , sizeof ( prpsinfo ) ) ;
prpsinfo . pr_sname = ' R ' ;
strcpy ( prpsinfo . pr_fname , " vmlinux " ) ;
2015-10-29 10:59:15 +01:00
return nt_init ( ptr , NT_PRPSINFO , & prpsinfo , sizeof ( prpsinfo ) ) ;
2011-10-30 15:16:40 +01:00
}
/*
2012-03-11 11:59:34 -04:00
* Get vmcoreinfo using lowcore - > vmcore_info ( new kernel )
2011-10-30 15:16:40 +01:00
*/
2012-03-11 11:59:34 -04:00
static void * get_vmcoreinfo_old ( unsigned long * size )
2011-10-30 15:16:40 +01:00
{
char nt_name [ 11 ] , * vmcoreinfo ;
Elf64_Nhdr note ;
void * addr ;
2015-10-12 10:43:37 +02:00
if ( copy_oldmem_kernel ( & addr , & S390_lowcore . vmcore_info , sizeof ( addr ) ) )
2012-03-11 11:59:34 -04:00
return NULL ;
2011-10-30 15:16:40 +01:00
memset ( nt_name , 0 , sizeof ( nt_name ) ) ;
2015-10-12 10:43:37 +02:00
if ( copy_oldmem_kernel ( & note , addr , sizeof ( note ) ) )
2012-03-11 11:59:34 -04:00
return NULL ;
2015-10-12 10:43:37 +02:00
if ( copy_oldmem_kernel ( nt_name , addr + sizeof ( note ) ,
sizeof ( nt_name ) - 1 ) )
2012-03-11 11:59:34 -04:00
return NULL ;
2011-10-30 15:16:40 +01:00
if ( strcmp ( nt_name , " VMCOREINFO " ) ! = 0 )
2012-03-11 11:59:34 -04:00
return NULL ;
vmcoreinfo = kzalloc_panic ( note . n_descsz ) ;
2015-10-12 10:43:37 +02:00
if ( copy_oldmem_kernel ( vmcoreinfo , addr + 24 , note . n_descsz ) )
2012-03-11 11:59:34 -04:00
return NULL ;
* size = note . n_descsz ;
return vmcoreinfo ;
}
/*
* Initialize vmcoreinfo note ( new kernel )
*/
static void * nt_vmcoreinfo ( void * ptr )
{
unsigned long size ;
void * vmcoreinfo ;
vmcoreinfo = os_info_old_entry ( OS_INFO_VMCOREINFO , & size ) ;
if ( ! vmcoreinfo )
vmcoreinfo = get_vmcoreinfo_old ( & size ) ;
if ( ! vmcoreinfo )
2011-10-30 15:16:40 +01:00
return ptr ;
2015-10-29 10:59:15 +01:00
return nt_init_name ( ptr , 0 , vmcoreinfo , size , " VMCOREINFO " ) ;
2011-10-30 15:16:40 +01:00
}
/*
* Initialize ELF header ( new kernel )
*/
static void * ehdr_init ( Elf64_Ehdr * ehdr , int mem_chunk_cnt )
{
memset ( ehdr , 0 , sizeof ( * ehdr ) ) ;
memcpy ( ehdr - > e_ident , ELFMAG , SELFMAG ) ;
ehdr - > e_ident [ EI_CLASS ] = ELFCLASS64 ;
ehdr - > e_ident [ EI_DATA ] = ELFDATA2MSB ;
ehdr - > e_ident [ EI_VERSION ] = EV_CURRENT ;
memset ( ehdr - > e_ident + EI_PAD , 0 , EI_NIDENT - EI_PAD ) ;
ehdr - > e_type = ET_CORE ;
ehdr - > e_machine = EM_S390 ;
ehdr - > e_version = EV_CURRENT ;
ehdr - > e_phoff = sizeof ( Elf64_Ehdr ) ;
ehdr - > e_ehsize = sizeof ( Elf64_Ehdr ) ;
ehdr - > e_phentsize = sizeof ( Elf64_Phdr ) ;
ehdr - > e_phnum = mem_chunk_cnt + 1 ;
return ehdr + 1 ;
}
/*
* Return CPU count for ELF header ( new kernel )
*/
static int get_cpu_cnt ( void )
{
2015-10-29 10:59:15 +01:00
struct save_area * sa ;
int cpus = 0 ;
2011-10-30 15:16:40 +01:00
2015-10-29 10:59:15 +01:00
list_for_each_entry ( sa , & dump_save_areas , list )
if ( sa - > prefix ! = 0 )
cpus + + ;
2011-10-30 15:16:40 +01:00
return cpus ;
}
/*
* Return memory chunk count for ELF header ( new kernel )
*/
static int get_mem_chunk_cnt ( void )
{
2014-01-29 18:16:01 +01:00
int cnt = 0 ;
u64 idx ;
2015-09-16 00:54:24 +06:00
for_each_mem_range ( idx , & memblock . physmem , & oldmem_type , NUMA_NO_NODE ,
MEMBLOCK_NONE , NULL , NULL , NULL )
2011-10-30 15:16:40 +01:00
cnt + + ;
return cnt ;
}
/*
* Initialize ELF loads ( new kernel )
*/
2014-01-29 18:16:01 +01:00
static void loads_init ( Elf64_Phdr * phdr , u64 loads_offset )
2011-10-30 15:16:40 +01:00
{
2014-01-29 18:16:01 +01:00
phys_addr_t start , end ;
u64 idx ;
2011-10-30 15:16:40 +01:00
2015-09-16 00:54:24 +06:00
for_each_mem_range ( idx , & memblock . physmem , & oldmem_type , NUMA_NO_NODE ,
MEMBLOCK_NONE , & start , & end , NULL ) {
2014-01-29 18:16:01 +01:00
phdr - > p_filesz = end - start ;
2011-10-30 15:16:40 +01:00
phdr - > p_type = PT_LOAD ;
2014-01-29 18:16:01 +01:00
phdr - > p_offset = start ;
phdr - > p_vaddr = start ;
phdr - > p_paddr = start ;
phdr - > p_memsz = end - start ;
2011-10-30 15:16:40 +01:00
phdr - > p_flags = PF_R | PF_W | PF_X ;
phdr - > p_align = PAGE_SIZE ;
phdr + + ;
}
}
/*
* Initialize notes ( new kernel )
*/
static void * notes_init ( Elf64_Phdr * phdr , void * ptr , u64 notes_offset )
{
2015-10-29 10:59:15 +01:00
struct save_area * sa ;
2011-10-30 15:16:40 +01:00
void * ptr_start = ptr ;
2015-10-29 10:59:15 +01:00
int cpu ;
2011-10-30 15:16:40 +01:00
ptr = nt_prpsinfo ( ptr ) ;
2015-10-29 10:59:15 +01:00
cpu = 1 ;
list_for_each_entry ( sa , & dump_save_areas , list )
if ( sa - > prefix ! = 0 )
ptr = fill_cpu_elf_notes ( ptr , cpu + + , sa ) ;
2011-10-30 15:16:40 +01:00
ptr = nt_vmcoreinfo ( ptr ) ;
memset ( phdr , 0 , sizeof ( * phdr ) ) ;
phdr - > p_type = PT_NOTE ;
2013-09-11 14:24:50 -07:00
phdr - > p_offset = notes_offset ;
2011-10-30 15:16:40 +01:00
phdr - > p_filesz = ( unsigned long ) PTR_SUB ( ptr , ptr_start ) ;
phdr - > p_memsz = phdr - > p_filesz ;
return ptr ;
}
/*
* Create ELF core header ( new kernel )
*/
2013-09-11 14:24:50 -07:00
int elfcorehdr_alloc ( unsigned long long * addr , unsigned long long * size )
2011-10-30 15:16:40 +01:00
{
Elf64_Phdr * phdr_notes , * phdr_loads ;
int mem_chunk_cnt ;
void * ptr , * hdr ;
u32 alloc_size ;
u64 hdr_off ;
2013-09-11 14:24:54 -07:00
/* If we are not in kdump or zfcpdump mode return */
if ( ! OLDMEM_BASE & & ipl_info . type ! = IPL_TYPE_FCP_DUMP )
2013-09-11 14:24:50 -07:00
return 0 ;
2013-11-13 10:38:27 +01:00
/* If we cannot get HSA size for zfcpdump return error */
2015-05-06 13:18:59 +02:00
if ( ipl_info . type = = IPL_TYPE_FCP_DUMP & & ! sclp . hsa_size )
2013-11-13 10:38:27 +01:00
return - ENODEV ;
2014-01-29 18:16:01 +01:00
/* For kdump, exclude previous crashkernel memory */
if ( OLDMEM_BASE ) {
oldmem_region . base = OLDMEM_BASE ;
oldmem_region . size = OLDMEM_SIZE ;
oldmem_type . total_size = OLDMEM_SIZE ;
}
2011-10-30 15:16:40 +01:00
mem_chunk_cnt = get_mem_chunk_cnt ( ) ;
2014-10-06 17:57:43 +02:00
alloc_size = 0x1000 + get_cpu_cnt ( ) * 0x4a0 +
2011-10-30 15:16:40 +01:00
mem_chunk_cnt * sizeof ( Elf64_Phdr ) ;
hdr = kzalloc_panic ( alloc_size ) ;
/* Init elf header */
ptr = ehdr_init ( hdr , mem_chunk_cnt ) ;
/* Init program headers */
phdr_notes = ptr ;
ptr = PTR_ADD ( ptr , sizeof ( Elf64_Phdr ) ) ;
phdr_loads = ptr ;
ptr = PTR_ADD ( ptr , sizeof ( Elf64_Phdr ) * mem_chunk_cnt ) ;
/* Init notes */
hdr_off = PTR_DIFF ( ptr , hdr ) ;
ptr = notes_init ( phdr_notes , ptr , ( ( unsigned long ) hdr ) + hdr_off ) ;
/* Init loads */
hdr_off = PTR_DIFF ( ptr , hdr ) ;
2013-09-11 14:24:50 -07:00
loads_init ( phdr_loads , hdr_off ) ;
* addr = ( unsigned long long ) hdr ;
* size = ( unsigned long long ) hdr_off ;
BUG_ON ( elfcorehdr_size > alloc_size ) ;
return 0 ;
2011-10-30 15:16:40 +01:00
}
/*
2013-09-11 14:24:50 -07:00
* Free ELF core header ( new kernel )
2011-10-30 15:16:40 +01:00
*/
2013-09-11 14:24:50 -07:00
void elfcorehdr_free ( unsigned long long addr )
2011-10-30 15:16:40 +01:00
{
2013-09-11 14:24:50 -07:00
kfree ( ( void * ) ( unsigned long ) addr ) ;
}
/*
* Read from ELF header
*/
ssize_t elfcorehdr_read ( char * buf , size_t count , u64 * ppos )
{
void * src = ( void * ) ( unsigned long ) * ppos ;
memcpy ( buf , src , count ) ;
* ppos + = count ;
return count ;
2011-10-30 15:16:40 +01:00
}
2013-09-11 14:24:50 -07:00
/*
* Read from ELF notes data
*/
ssize_t elfcorehdr_read_notes ( char * buf , size_t count , u64 * ppos )
{
void * src = ( void * ) ( unsigned long ) * ppos ;
2015-10-14 15:53:06 +02:00
memcpy ( buf , src , count ) ;
2013-09-11 14:24:50 -07:00
* ppos + = count ;
return count ;
}