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>
# 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 ;
unsigned int fdt_size ;
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 ;
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 )
goto out ;
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
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 ) ;
}
fdt_size = fdt_totalsize ( initial_boot_params ) * 2 ;
fdt = kmalloc ( fdt_size , GFP_KERNEL ) ;
if ( ! fdt ) {
pr_err ( " Not enough memory for the device tree. \n " ) ;
ret = - ENOMEM ;
goto out ;
}
ret = fdt_open_into ( initial_boot_params , fdt , fdt_size ) ;
if ( ret < 0 ) {
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 )
goto out ;
fdt_pack ( fdt ) ;
kbuf . buffer = fdt ;
kbuf . bufsz = kbuf . memsz = fdt_size ;
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 )
goto out ;
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 " ) ;
out :
2019-08-23 22:49:13 +03:00
kexec_free_elf_info ( & elf_info ) ;
2016-11-29 15:45:51 +03:00
/* Make kimage_file_post_load_cleanup free the fdt buffer for us. */
return ret ? ERR_PTR ( ret ) : fdt ;
}
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 ,
} ;