2019-05-29 17:17:59 +03:00
// SPDX-License-Identifier: GPL-2.0-only
2016-11-29 15:45:51 +03:00
/*
* Load ELF vmlinux file for the kexec_file_load syscall .
*
* Copyright ( C ) 2004 Adam Litke ( agl @ us . ibm . com )
* Copyright ( C ) 2004 IBM Corp .
* Copyright ( C ) 2005 R Sharada ( sharada @ in . ibm . com )
* Copyright ( C ) 2006 Mohan Kumar M ( mohan @ in . ibm . com )
* Copyright ( C ) 2016 IBM Corporation
*
* Based on kexec - tools ' kexec - elf - exec . c and kexec - elf - ppc64 . c .
* Heavily modified for the kernel by
* Thiago Jung Bauermann < bauerman @ linux . vnet . ibm . com > .
*/
# define pr_fmt(fmt) "kexec_elf: " fmt
# include <linux/elf.h>
# include <linux/kexec.h>
# include <linux/libfdt.h>
# include <linux/module.h>
2021-02-21 20:49:24 +03:00
# include <linux/of.h>
2016-11-29 15:45:51 +03:00
# include <linux/of_fdt.h>
# include <linux/slab.h>
# include <linux/types.h>
static void * elf64_load ( struct kimage * image , char * kernel_buf ,
unsigned long kernel_len , char * initrd ,
unsigned long initrd_len , char * cmdline ,
unsigned long cmdline_len )
{
int ret ;
2018-04-14 01:36:43 +03:00
unsigned long kernel_load_addr ;
2016-11-29 15:45:51 +03:00
unsigned long initrd_load_addr = 0 , fdt_load_addr ;
void * fdt ;
const void * slave_code ;
struct elfhdr ehdr ;
2020-07-29 14:43:14 +03:00
char * modified_cmdline = NULL ;
2019-08-23 22:49:13 +03:00
struct kexec_elf_info elf_info ;
2016-11-29 15:45:51 +03:00
struct kexec_buf kbuf = { . image = image , . buf_min = 0 ,
. buf_max = ppc64_rma_size } ;
2018-04-14 01:36:43 +03:00
struct kexec_buf pbuf = { . image = image , . buf_min = 0 ,
2019-05-23 01:01:58 +03:00
. buf_max = ppc64_rma_size , . top_down = true ,
. mem = KEXEC_BUF_MEM_UNKNOWN } ;
2016-11-29 15:45:51 +03:00
2019-08-23 22:49:13 +03:00
ret = kexec_build_elf_info ( kernel_buf , kernel_len , & ehdr , & elf_info ) ;
2016-11-29 15:45:51 +03:00
if ( ret )
2021-04-21 19:36:10 +03:00
return ERR_PTR ( ret ) ;
2016-11-29 15:45:51 +03:00
2020-07-29 14:40:16 +03:00
if ( image - > type = = KEXEC_TYPE_CRASH ) {
/* min & max buffer values for kdump case */
kbuf . buf_min = pbuf . buf_min = crashk_res . start ;
kbuf . buf_max = pbuf . buf_max =
( ( crashk_res . end < ppc64_rma_size ) ?
crashk_res . end : ( ppc64_rma_size - 1 ) ) ;
}
2019-08-23 22:49:13 +03:00
ret = kexec_elf_load ( image , & ehdr , & elf_info , & kbuf , & kernel_load_addr ) ;
2016-11-29 15:45:51 +03:00
if ( ret )
goto out ;
pr_debug ( " Loaded the kernel at 0x%lx \n " , kernel_load_addr ) ;
2018-04-14 01:36:43 +03:00
ret = kexec_load_purgatory ( image , & pbuf ) ;
2016-11-29 15:45:51 +03:00
if ( ret ) {
pr_err ( " Loading purgatory failed. \n " ) ;
goto out ;
}
2018-04-14 01:36:43 +03:00
pr_debug ( " Loaded purgatory at 0x%lx \n " , pbuf . mem ) ;
2016-11-29 15:45:51 +03:00
2020-07-29 14:42:58 +03:00
/* Load additional segments needed for panic kernel */
if ( image - > type = = KEXEC_TYPE_CRASH ) {
ret = load_crashdump_segments_ppc64 ( image , & kbuf ) ;
if ( ret ) {
pr_err ( " Failed to load kdump kernel segments \n " ) ;
goto out ;
}
2020-07-29 14:43:14 +03:00
/* Setup cmdline for kdump kernel case */
modified_cmdline = setup_kdump_cmdline ( image , cmdline ,
cmdline_len ) ;
if ( ! modified_cmdline ) {
pr_err ( " Setting up cmdline for kdump kernel failed \n " ) ;
ret = - EINVAL ;
goto out ;
}
cmdline = modified_cmdline ;
2020-07-29 14:42:58 +03:00
}
2016-11-29 15:45:51 +03:00
if ( initrd ! = NULL ) {
kbuf . buffer = initrd ;
kbuf . bufsz = kbuf . memsz = initrd_len ;
kbuf . buf_align = PAGE_SIZE ;
kbuf . top_down = false ;
2019-05-23 01:01:58 +03:00
kbuf . mem = KEXEC_BUF_MEM_UNKNOWN ;
2016-11-29 15:45:51 +03:00
ret = kexec_add_buffer ( & kbuf ) ;
if ( ret )
goto out ;
initrd_load_addr = kbuf . mem ;
pr_debug ( " Loaded initrd at 0x%lx \n " , initrd_load_addr ) ;
}
2021-02-21 20:49:24 +03:00
fdt = of_kexec_alloc_and_setup_fdt ( image , initrd_load_addr ,
initrd_len , cmdline ,
2021-02-20 03:52:04 +03:00
kexec_extra_fdt_size_ppc64 ( image ) ) ;
2016-11-29 15:45:51 +03:00
if ( ! fdt ) {
pr_err ( " Error setting up the new device tree. \n " ) ;
ret = - EINVAL ;
goto out ;
}
2020-07-29 14:39:41 +03:00
ret = setup_new_fdt_ppc64 ( image , fdt , initrd_load_addr ,
initrd_len , cmdline ) ;
2016-11-29 15:45:51 +03:00
if ( ret )
2021-04-21 19:36:09 +03:00
goto out_free_fdt ;
2016-11-29 15:45:51 +03:00
fdt_pack ( fdt ) ;
kbuf . buffer = fdt ;
2021-02-21 20:49:24 +03:00
kbuf . bufsz = kbuf . memsz = fdt_totalsize ( fdt ) ;
2016-11-29 15:45:51 +03:00
kbuf . buf_align = PAGE_SIZE ;
kbuf . top_down = true ;
2019-05-23 01:01:58 +03:00
kbuf . mem = KEXEC_BUF_MEM_UNKNOWN ;
2016-11-29 15:45:51 +03:00
ret = kexec_add_buffer ( & kbuf ) ;
if ( ret )
2021-04-21 19:36:09 +03:00
goto out_free_fdt ;
2021-02-21 20:49:24 +03:00
/* FDT will be freed in arch_kimage_file_post_load_cleanup */
image - > arch . fdt = fdt ;
2016-11-29 15:45:51 +03:00
fdt_load_addr = kbuf . mem ;
pr_debug ( " Loaded device tree at 0x%lx \n " , fdt_load_addr ) ;
slave_code = elf_info . buffer + elf_info . proghdrs [ 0 ] . p_offset ;
2020-07-29 14:39:41 +03:00
ret = setup_purgatory_ppc64 ( image , slave_code , fdt , kernel_load_addr ,
fdt_load_addr ) ;
2016-11-29 15:45:51 +03:00
if ( ret )
pr_err ( " Error setting up the purgatory. \n " ) ;
2021-04-21 19:36:09 +03:00
goto out ;
out_free_fdt :
kvfree ( fdt ) ;
2016-11-29 15:45:51 +03:00
out :
2020-07-29 14:43:14 +03:00
kfree ( modified_cmdline ) ;
2019-08-23 22:49:13 +03:00
kexec_free_elf_info ( & elf_info ) ;
2016-11-29 15:45:51 +03:00
2021-02-21 20:49:24 +03:00
return ret ? ERR_PTR ( ret ) : NULL ;
2016-11-29 15:45:51 +03:00
}
2018-04-14 01:35:49 +03:00
const struct kexec_file_ops kexec_elf64_ops = {
2019-08-23 22:49:13 +03:00
. probe = kexec_elf_probe ,
2016-11-29 15:45:51 +03:00
. load = elf64_load ,
} ;