xref: /openbmc/linux/arch/powerpc/kexec/file_load.c (revision 8be98d2f2a0a262f8bf8a0bc1fdf522b3c7aab17)
1793b08e2SChristophe Leroy // SPDX-License-Identifier: GPL-2.0-only
2793b08e2SChristophe Leroy /*
319031275SHari Bathini  * powerpc code to implement the kexec_file_load syscall
4793b08e2SChristophe Leroy  *
5793b08e2SChristophe Leroy  * Copyright (C) 2004  Adam Litke (agl@us.ibm.com)
6793b08e2SChristophe Leroy  * Copyright (C) 2004  IBM Corp.
7793b08e2SChristophe Leroy  * Copyright (C) 2004,2005  Milton D Miller II, IBM Corporation
8793b08e2SChristophe Leroy  * Copyright (C) 2005  R Sharada (sharada@in.ibm.com)
9793b08e2SChristophe Leroy  * Copyright (C) 2006  Mohan Kumar M (mohan@in.ibm.com)
10793b08e2SChristophe Leroy  * Copyright (C) 2016  IBM Corporation
11793b08e2SChristophe Leroy  *
12793b08e2SChristophe Leroy  * Based on kexec-tools' kexec-elf-ppc64.c, fs2dt.c.
13793b08e2SChristophe Leroy  * Heavily modified for the kernel by
14793b08e2SChristophe Leroy  * Thiago Jung Bauermann <bauerman@linux.vnet.ibm.com>.
15793b08e2SChristophe Leroy  */
16793b08e2SChristophe Leroy 
17793b08e2SChristophe Leroy #include <linux/slab.h>
18793b08e2SChristophe Leroy #include <linux/kexec.h>
19793b08e2SChristophe Leroy #include <linux/of_fdt.h>
20793b08e2SChristophe Leroy #include <linux/libfdt.h>
21cb350c1fSHari Bathini #include <asm/setup.h>
22793b08e2SChristophe Leroy 
2319031275SHari Bathini #define SLAVE_CODE_SIZE		256	/* First 0x100 bytes */
24793b08e2SChristophe Leroy 
25793b08e2SChristophe Leroy /**
26cb350c1fSHari Bathini  * setup_kdump_cmdline - Prepend "elfcorehdr=<addr> " to command line
27cb350c1fSHari Bathini  *                       of kdump kernel for exporting the core.
28cb350c1fSHari Bathini  * @image:               Kexec image
29cb350c1fSHari Bathini  * @cmdline:             Command line parameters to update.
30cb350c1fSHari Bathini  * @cmdline_len:         Length of the cmdline parameters.
31cb350c1fSHari Bathini  *
32cb350c1fSHari Bathini  * kdump segment must be setup before calling this function.
33cb350c1fSHari Bathini  *
34cb350c1fSHari Bathini  * Returns new cmdline buffer for kdump kernel on success, NULL otherwise.
35cb350c1fSHari Bathini  */
setup_kdump_cmdline(struct kimage * image,char * cmdline,unsigned long cmdline_len)36cb350c1fSHari Bathini char *setup_kdump_cmdline(struct kimage *image, char *cmdline,
37cb350c1fSHari Bathini 			  unsigned long cmdline_len)
38cb350c1fSHari Bathini {
39cb350c1fSHari Bathini 	int elfcorehdr_strlen;
40cb350c1fSHari Bathini 	char *cmdline_ptr;
41cb350c1fSHari Bathini 
42cb350c1fSHari Bathini 	cmdline_ptr = kzalloc(COMMAND_LINE_SIZE, GFP_KERNEL);
43cb350c1fSHari Bathini 	if (!cmdline_ptr)
44cb350c1fSHari Bathini 		return NULL;
45cb350c1fSHari Bathini 
46cb350c1fSHari Bathini 	elfcorehdr_strlen = sprintf(cmdline_ptr, "elfcorehdr=0x%lx ",
47*e6635babSLakshmi Ramasubramanian 				    image->elf_load_addr);
48cb350c1fSHari Bathini 
49cb350c1fSHari Bathini 	if (elfcorehdr_strlen + cmdline_len > COMMAND_LINE_SIZE) {
50cb350c1fSHari Bathini 		pr_err("Appending elfcorehdr=<addr> exceeds cmdline size\n");
51cb350c1fSHari Bathini 		kfree(cmdline_ptr);
52cb350c1fSHari Bathini 		return NULL;
53cb350c1fSHari Bathini 	}
54cb350c1fSHari Bathini 
55cb350c1fSHari Bathini 	memcpy(cmdline_ptr + elfcorehdr_strlen, cmdline, cmdline_len);
56cb350c1fSHari Bathini 	// Ensure it's nul terminated
57cb350c1fSHari Bathini 	cmdline_ptr[COMMAND_LINE_SIZE - 1] = '\0';
58cb350c1fSHari Bathini 	return cmdline_ptr;
59cb350c1fSHari Bathini }
60cb350c1fSHari Bathini 
61cb350c1fSHari Bathini /**
62793b08e2SChristophe Leroy  * setup_purgatory - initialize the purgatory's global variables
63793b08e2SChristophe Leroy  * @image:		kexec image.
64793b08e2SChristophe Leroy  * @slave_code:		Slave code for the purgatory.
65793b08e2SChristophe Leroy  * @fdt:		Flattened device tree for the next kernel.
66793b08e2SChristophe Leroy  * @kernel_load_addr:	Address where the kernel is loaded.
67793b08e2SChristophe Leroy  * @fdt_load_addr:	Address where the flattened device tree is loaded.
68793b08e2SChristophe Leroy  *
69793b08e2SChristophe Leroy  * Return: 0 on success, or negative errno on error.
70793b08e2SChristophe Leroy  */
setup_purgatory(struct kimage * image,const void * slave_code,const void * fdt,unsigned long kernel_load_addr,unsigned long fdt_load_addr)71793b08e2SChristophe Leroy int setup_purgatory(struct kimage *image, const void *slave_code,
72793b08e2SChristophe Leroy 		    const void *fdt, unsigned long kernel_load_addr,
73793b08e2SChristophe Leroy 		    unsigned long fdt_load_addr)
74793b08e2SChristophe Leroy {
75793b08e2SChristophe Leroy 	unsigned int *slave_code_buf, master_entry;
76793b08e2SChristophe Leroy 	int ret;
77793b08e2SChristophe Leroy 
78793b08e2SChristophe Leroy 	slave_code_buf = kmalloc(SLAVE_CODE_SIZE, GFP_KERNEL);
79793b08e2SChristophe Leroy 	if (!slave_code_buf)
80793b08e2SChristophe Leroy 		return -ENOMEM;
81793b08e2SChristophe Leroy 
82793b08e2SChristophe Leroy 	/* Get the slave code from the new kernel and put it in purgatory. */
83793b08e2SChristophe Leroy 	ret = kexec_purgatory_get_set_symbol(image, "purgatory_start",
84793b08e2SChristophe Leroy 					     slave_code_buf, SLAVE_CODE_SIZE,
85793b08e2SChristophe Leroy 					     true);
86793b08e2SChristophe Leroy 	if (ret) {
87793b08e2SChristophe Leroy 		kfree(slave_code_buf);
88793b08e2SChristophe Leroy 		return ret;
89793b08e2SChristophe Leroy 	}
90793b08e2SChristophe Leroy 
91793b08e2SChristophe Leroy 	master_entry = slave_code_buf[0];
92793b08e2SChristophe Leroy 	memcpy(slave_code_buf, slave_code, SLAVE_CODE_SIZE);
93793b08e2SChristophe Leroy 	slave_code_buf[0] = master_entry;
94793b08e2SChristophe Leroy 	ret = kexec_purgatory_get_set_symbol(image, "purgatory_start",
95793b08e2SChristophe Leroy 					     slave_code_buf, SLAVE_CODE_SIZE,
96793b08e2SChristophe Leroy 					     false);
97793b08e2SChristophe Leroy 	kfree(slave_code_buf);
98793b08e2SChristophe Leroy 
99793b08e2SChristophe Leroy 	ret = kexec_purgatory_get_set_symbol(image, "kernel", &kernel_load_addr,
100793b08e2SChristophe Leroy 					     sizeof(kernel_load_addr), false);
101793b08e2SChristophe Leroy 	if (ret)
102793b08e2SChristophe Leroy 		return ret;
103793b08e2SChristophe Leroy 	ret = kexec_purgatory_get_set_symbol(image, "dt_offset", &fdt_load_addr,
104793b08e2SChristophe Leroy 					     sizeof(fdt_load_addr), false);
105793b08e2SChristophe Leroy 	if (ret)
106793b08e2SChristophe Leroy 		return ret;
107793b08e2SChristophe Leroy 
108793b08e2SChristophe Leroy 	return 0;
109793b08e2SChristophe Leroy }
110