xref: /linux/arch/powerpc/kexec/file_load.c (revision 3a2c4d55e32ad65efebdb6de44eef3bfa08bb49d)
1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3  * powerpc code to implement the kexec_file_load syscall
4  *
5  * Copyright (C) 2004  Adam Litke (agl@us.ibm.com)
6  * Copyright (C) 2004  IBM Corp.
7  * Copyright (C) 2004,2005  Milton D Miller II, IBM Corporation
8  * Copyright (C) 2005  R Sharada (sharada@in.ibm.com)
9  * Copyright (C) 2006  Mohan Kumar M (mohan@in.ibm.com)
10  * Copyright (C) 2016  IBM Corporation
11  *
12  * Based on kexec-tools' kexec-elf-ppc64.c, fs2dt.c.
13  * Heavily modified for the kernel by
14  * Thiago Jung Bauermann <bauerman@linux.vnet.ibm.com>.
15  */
16 
17 #include <linux/slab.h>
18 #include <linux/kexec.h>
19 #include <linux/of_fdt.h>
20 #include <linux/libfdt.h>
21 #include <asm/setup.h>
22 
23 #define SLAVE_CODE_SIZE		256	/* First 0x100 bytes */
24 
25 /**
26  * setup_kdump_cmdline - Prepend "elfcorehdr=<addr> " to command line
27  *                       of kdump kernel for exporting the core.
28  * @image:               Kexec image
29  * @cmdline:             Command line parameters to update.
30  * @cmdline_len:         Length of the cmdline parameters.
31  *
32  * kdump segment must be setup before calling this function.
33  *
34  * Returns new cmdline buffer for kdump kernel on success, NULL otherwise.
35  */
36 char *setup_kdump_cmdline(struct kimage *image, char *cmdline,
37 			  unsigned long cmdline_len)
38 {
39 	char *cmdline_ptr;
40 
41 	cmdline_ptr = kmalloc(COMMAND_LINE_SIZE, GFP_KERNEL);
42 	if (!cmdline_ptr)
43 		return NULL;
44 
45 	if (snprintf(cmdline_ptr, COMMAND_LINE_SIZE, "elfcorehdr=0x%lx %s",
46 		     image->elf_load_addr, cmdline_len ? cmdline : "") >= COMMAND_LINE_SIZE) {
47 		pr_err("Prepending elfcorehdr=<addr> exceeds cmdline size\n");
48 		kfree(cmdline_ptr);
49 		return NULL;
50 	}
51 
52 	return cmdline_ptr;
53 }
54 
55 /**
56  * setup_purgatory - initialize the purgatory's global variables
57  * @image:		kexec image.
58  * @slave_code:		Slave code for the purgatory.
59  * @fdt:		Flattened device tree for the next kernel.
60  * @kernel_load_addr:	Address where the kernel is loaded.
61  * @fdt_load_addr:	Address where the flattened device tree is loaded.
62  *
63  * Return: 0 on success, or negative errno on error.
64  */
65 int setup_purgatory(struct kimage *image, const void *slave_code,
66 		    const void *fdt, unsigned long kernel_load_addr,
67 		    unsigned long fdt_load_addr)
68 {
69 	unsigned int *slave_code_buf, master_entry;
70 	int ret;
71 
72 	slave_code_buf = kmalloc(SLAVE_CODE_SIZE, GFP_KERNEL);
73 	if (!slave_code_buf)
74 		return -ENOMEM;
75 
76 	/* Get the slave code from the new kernel and put it in purgatory. */
77 	ret = kexec_purgatory_get_set_symbol(image, "purgatory_start",
78 					     slave_code_buf, SLAVE_CODE_SIZE,
79 					     true);
80 	if (ret) {
81 		kfree(slave_code_buf);
82 		return ret;
83 	}
84 
85 	master_entry = slave_code_buf[0];
86 	memcpy(slave_code_buf, slave_code, SLAVE_CODE_SIZE);
87 	slave_code_buf[0] = master_entry;
88 	ret = kexec_purgatory_get_set_symbol(image, "purgatory_start",
89 					     slave_code_buf, SLAVE_CODE_SIZE,
90 					     false);
91 	kfree(slave_code_buf);
92 
93 	ret = kexec_purgatory_get_set_symbol(image, "kernel", &kernel_load_addr,
94 					     sizeof(kernel_load_addr), false);
95 	if (ret)
96 		return ret;
97 	ret = kexec_purgatory_get_set_symbol(image, "dt_offset", &fdt_load_addr,
98 					     sizeof(fdt_load_addr), false);
99 	if (ret)
100 		return ret;
101 
102 	return 0;
103 }
104