aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorVivek Goyal <vgoyal@redhat.com>2014-08-08 17:26:06 -0400
committerLinus Torvalds <torvalds@linux-foundation.org>2014-08-08 18:57:33 -0400
commit27f48d3e633be23656a097baa3be336e04a82d84 (patch)
tree8933767497b417ee532f63b9582734743eed8e41
parent12db5562e0352986a265841638482b84f3a6899b (diff)
kexec-bzImage64: support for loading bzImage using 64bit entry
This is loader specific code which can load bzImage and set it up for 64bit entry. This does not take care of 32bit entry or real mode entry. 32bit mode entry can be implemented if somebody needs it. Signed-off-by: Vivek Goyal <vgoyal@redhat.com> Cc: Borislav Petkov <bp@suse.de> Cc: Michael Kerrisk <mtk.manpages@gmail.com> Cc: Yinghai Lu <yinghai@kernel.org> Cc: Eric Biederman <ebiederm@xmission.com> Cc: H. Peter Anvin <hpa@zytor.com> Cc: Matthew Garrett <mjg59@srcf.ucam.org> Cc: Greg Kroah-Hartman <greg@kroah.com> Cc: Dave Young <dyoung@redhat.com> Cc: WANG Chao <chaowang@redhat.com> Cc: Baoquan He <bhe@redhat.com> Cc: Andy Lutomirski <luto@amacapital.net> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
-rw-r--r--arch/x86/include/asm/kexec-bzimage64.h6
-rw-r--r--arch/x86/include/asm/kexec.h21
-rw-r--r--arch/x86/kernel/Makefile1
-rw-r--r--arch/x86/kernel/kexec-bzimage64.c375
-rw-r--r--arch/x86/kernel/machine_kexec_64.c5
-rw-r--r--include/linux/kexec.h2
-rw-r--r--kernel/kexec.c11
7 files changed, 415 insertions, 6 deletions
diff --git a/arch/x86/include/asm/kexec-bzimage64.h b/arch/x86/include/asm/kexec-bzimage64.h
new file mode 100644
index 000000000000..d1b5d194e31d
--- /dev/null
+++ b/arch/x86/include/asm/kexec-bzimage64.h
@@ -0,0 +1,6 @@
1#ifndef _ASM_KEXEC_BZIMAGE64_H
2#define _ASM_KEXEC_BZIMAGE64_H
3
4extern struct kexec_file_ops kexec_bzImage64_ops;
5
6#endif /* _ASM_KEXE_BZIMAGE64_H */
diff --git a/arch/x86/include/asm/kexec.h b/arch/x86/include/asm/kexec.h
index 17483a492f18..0dfccced4edf 100644
--- a/arch/x86/include/asm/kexec.h
+++ b/arch/x86/include/asm/kexec.h
@@ -23,6 +23,7 @@
23 23
24#include <asm/page.h> 24#include <asm/page.h>
25#include <asm/ptrace.h> 25#include <asm/ptrace.h>
26#include <asm/bootparam.h>
26 27
27/* 28/*
28 * KEXEC_SOURCE_MEMORY_LIMIT maximum page get_free_page can return. 29 * KEXEC_SOURCE_MEMORY_LIMIT maximum page get_free_page can return.
@@ -161,6 +162,26 @@ struct kimage_arch {
161 pmd_t *pmd; 162 pmd_t *pmd;
162 pte_t *pte; 163 pte_t *pte;
163}; 164};
165
166struct kexec_entry64_regs {
167 uint64_t rax;
168 uint64_t rbx;
169 uint64_t rcx;
170 uint64_t rdx;
171 uint64_t rsi;
172 uint64_t rdi;
173 uint64_t rsp;
174 uint64_t rbp;
175 uint64_t r8;
176 uint64_t r9;
177 uint64_t r10;
178 uint64_t r11;
179 uint64_t r12;
180 uint64_t r13;
181 uint64_t r14;
182 uint64_t r15;
183 uint64_t rip;
184};
164#endif 185#endif
165 186
166typedef void crash_vmclear_fn(void); 187typedef void crash_vmclear_fn(void);
diff --git a/arch/x86/kernel/Makefile b/arch/x86/kernel/Makefile
index bde3993624f1..b5ea75c4a4b4 100644
--- a/arch/x86/kernel/Makefile
+++ b/arch/x86/kernel/Makefile
@@ -118,4 +118,5 @@ ifeq ($(CONFIG_X86_64),y)
118 118
119 obj-$(CONFIG_PCI_MMCONFIG) += mmconf-fam10h_64.o 119 obj-$(CONFIG_PCI_MMCONFIG) += mmconf-fam10h_64.o
120 obj-y += vsmp_64.o 120 obj-y += vsmp_64.o
121 obj-$(CONFIG_KEXEC) += kexec-bzimage64.o
121endif 122endif
diff --git a/arch/x86/kernel/kexec-bzimage64.c b/arch/x86/kernel/kexec-bzimage64.c
new file mode 100644
index 000000000000..bcedd100192f
--- /dev/null
+++ b/arch/x86/kernel/kexec-bzimage64.c
@@ -0,0 +1,375 @@
1/*
2 * Kexec bzImage loader
3 *
4 * Copyright (C) 2014 Red Hat Inc.
5 * Authors:
6 * Vivek Goyal <vgoyal@redhat.com>
7 *
8 * This source code is licensed under the GNU General Public License,
9 * Version 2. See the file COPYING for more details.
10 */
11
12#define pr_fmt(fmt) "kexec-bzImage64: " fmt
13
14#include <linux/string.h>
15#include <linux/printk.h>
16#include <linux/errno.h>
17#include <linux/slab.h>
18#include <linux/kexec.h>
19#include <linux/kernel.h>
20#include <linux/mm.h>
21
22#include <asm/bootparam.h>
23#include <asm/setup.h>
24
25/*
26 * Defines lowest physical address for various segments. Not sure where
27 * exactly these limits came from. Current bzimage64 loader in kexec-tools
28 * uses these so I am retaining it. It can be changed over time as we gain
29 * more insight.
30 */
31#define MIN_PURGATORY_ADDR 0x3000
32#define MIN_BOOTPARAM_ADDR 0x3000
33#define MIN_KERNEL_LOAD_ADDR 0x100000
34#define MIN_INITRD_LOAD_ADDR 0x1000000
35
36/*
37 * This is a place holder for all boot loader specific data structure which
38 * gets allocated in one call but gets freed much later during cleanup
39 * time. Right now there is only one field but it can grow as need be.
40 */
41struct bzimage64_data {
42 /*
43 * Temporary buffer to hold bootparams buffer. This should be
44 * freed once the bootparam segment has been loaded.
45 */
46 void *bootparams_buf;
47};
48
49static int setup_initrd(struct boot_params *params,
50 unsigned long initrd_load_addr, unsigned long initrd_len)
51{
52 params->hdr.ramdisk_image = initrd_load_addr & 0xffffffffUL;
53 params->hdr.ramdisk_size = initrd_len & 0xffffffffUL;
54
55 params->ext_ramdisk_image = initrd_load_addr >> 32;
56 params->ext_ramdisk_size = initrd_len >> 32;
57
58 return 0;
59}
60
61static int setup_cmdline(struct boot_params *params,
62 unsigned long bootparams_load_addr,
63 unsigned long cmdline_offset, char *cmdline,
64 unsigned long cmdline_len)
65{
66 char *cmdline_ptr = ((char *)params) + cmdline_offset;
67 unsigned long cmdline_ptr_phys;
68 uint32_t cmdline_low_32, cmdline_ext_32;
69
70 memcpy(cmdline_ptr, cmdline, cmdline_len);
71 cmdline_ptr[cmdline_len - 1] = '\0';
72
73 cmdline_ptr_phys = bootparams_load_addr + cmdline_offset;
74 cmdline_low_32 = cmdline_ptr_phys & 0xffffffffUL;
75 cmdline_ext_32 = cmdline_ptr_phys >> 32;
76
77 params->hdr.cmd_line_ptr = cmdline_low_32;
78 if (cmdline_ext_32)
79 params->ext_cmd_line_ptr = cmdline_ext_32;
80
81 return 0;
82}
83
84static int setup_memory_map_entries(struct boot_params *params)
85{
86 unsigned int nr_e820_entries;
87
88 nr_e820_entries = e820_saved.nr_map;
89
90 /* TODO: Pass entries more than E820MAX in bootparams setup data */
91 if (nr_e820_entries > E820MAX)
92 nr_e820_entries = E820MAX;
93
94 params->e820_entries = nr_e820_entries;
95 memcpy(&params->e820_map, &e820_saved.map,
96 nr_e820_entries * sizeof(struct e820entry));
97
98 return 0;
99}
100
101static int setup_boot_parameters(struct boot_params *params)
102{
103 unsigned int nr_e820_entries;
104 unsigned long long mem_k, start, end;
105 int i;
106
107 /* Get subarch from existing bootparams */
108 params->hdr.hardware_subarch = boot_params.hdr.hardware_subarch;
109
110 /* Copying screen_info will do? */
111 memcpy(&params->screen_info, &boot_params.screen_info,
112 sizeof(struct screen_info));
113
114 /* Fill in memsize later */
115 params->screen_info.ext_mem_k = 0;
116 params->alt_mem_k = 0;
117
118 /* Default APM info */
119 memset(&params->apm_bios_info, 0, sizeof(params->apm_bios_info));
120
121 /* Default drive info */
122 memset(&params->hd0_info, 0, sizeof(params->hd0_info));
123 memset(&params->hd1_info, 0, sizeof(params->hd1_info));
124
125 /* Default sysdesc table */
126 params->sys_desc_table.length = 0;
127
128 setup_memory_map_entries(params);
129 nr_e820_entries = params->e820_entries;
130
131 for (i = 0; i < nr_e820_entries; i++) {
132 if (params->e820_map[i].type != E820_RAM)
133 continue;
134 start = params->e820_map[i].addr;
135 end = params->e820_map[i].addr + params->e820_map[i].size - 1;
136
137 if ((start <= 0x100000) && end > 0x100000) {
138 mem_k = (end >> 10) - (0x100000 >> 10);
139 params->screen_info.ext_mem_k = mem_k;
140 params->alt_mem_k = mem_k;
141 if (mem_k > 0xfc00)
142 params->screen_info.ext_mem_k = 0xfc00; /* 64M*/
143 if (mem_k > 0xffffffff)
144 params->alt_mem_k = 0xffffffff;
145 }
146 }
147
148 /* Setup EDD info */
149 memcpy(params->eddbuf, boot_params.eddbuf,
150 EDDMAXNR * sizeof(struct edd_info));
151 params->eddbuf_entries = boot_params.eddbuf_entries;
152
153 memcpy(params->edd_mbr_sig_buffer, boot_params.edd_mbr_sig_buffer,
154 EDD_MBR_SIG_MAX * sizeof(unsigned int));
155
156 return 0;
157}
158
159int bzImage64_probe(const char *buf, unsigned long len)
160{
161 int ret = -ENOEXEC;
162 struct setup_header *header;
163
164 /* kernel should be atleast two sectors long */
165 if (len < 2 * 512) {
166 pr_err("File is too short to be a bzImage\n");
167 return ret;
168 }
169
170 header = (struct setup_header *)(buf + offsetof(struct boot_params, hdr));
171 if (memcmp((char *)&header->header, "HdrS", 4) != 0) {
172 pr_err("Not a bzImage\n");
173 return ret;
174 }
175
176 if (header->boot_flag != 0xAA55) {
177 pr_err("No x86 boot sector present\n");
178 return ret;
179 }
180
181 if (header->version < 0x020C) {
182 pr_err("Must be at least protocol version 2.12\n");
183 return ret;
184 }
185
186 if (!(header->loadflags & LOADED_HIGH)) {
187 pr_err("zImage not a bzImage\n");
188 return ret;
189 }
190
191 if (!(header->xloadflags & XLF_KERNEL_64)) {
192 pr_err("Not a bzImage64. XLF_KERNEL_64 is not set.\n");
193 return ret;
194 }
195
196 if (!(header->xloadflags & XLF_CAN_BE_LOADED_ABOVE_4G)) {
197 pr_err("XLF_CAN_BE_LOADED_ABOVE_4G is not set.\n");
198 return ret;
199 }
200
201 /* I've got a bzImage */
202 pr_debug("It's a relocatable bzImage64\n");
203 ret = 0;
204
205 return ret;
206}
207
208void *bzImage64_load(struct kimage *image, char *kernel,
209 unsigned long kernel_len, char *initrd,
210 unsigned long initrd_len, char *cmdline,
211 unsigned long cmdline_len)
212{
213
214 struct setup_header *header;
215 int setup_sects, kern16_size, ret = 0;
216 unsigned long setup_header_size, params_cmdline_sz;
217 struct boot_params *params;
218 unsigned long bootparam_load_addr, kernel_load_addr, initrd_load_addr;
219 unsigned long purgatory_load_addr;
220 unsigned long kernel_bufsz, kernel_memsz, kernel_align;
221 char *kernel_buf;
222 struct bzimage64_data *ldata;
223 struct kexec_entry64_regs regs64;
224 void *stack;
225 unsigned int setup_hdr_offset = offsetof(struct boot_params, hdr);
226
227 header = (struct setup_header *)(kernel + setup_hdr_offset);
228 setup_sects = header->setup_sects;
229 if (setup_sects == 0)
230 setup_sects = 4;
231
232 kern16_size = (setup_sects + 1) * 512;
233 if (kernel_len < kern16_size) {
234 pr_err("bzImage truncated\n");
235 return ERR_PTR(-ENOEXEC);
236 }
237
238 if (cmdline_len > header->cmdline_size) {
239 pr_err("Kernel command line too long\n");
240 return ERR_PTR(-EINVAL);
241 }
242
243 /*
244 * Load purgatory. For 64bit entry point, purgatory code can be
245 * anywhere.
246 */
247 ret = kexec_load_purgatory(image, MIN_PURGATORY_ADDR, ULONG_MAX, 1,
248 &purgatory_load_addr);
249 if (ret) {
250 pr_err("Loading purgatory failed\n");
251 return ERR_PTR(ret);
252 }
253
254 pr_debug("Loaded purgatory at 0x%lx\n", purgatory_load_addr);
255
256 /* Load Bootparams and cmdline */
257 params_cmdline_sz = sizeof(struct boot_params) + cmdline_len;
258 params = kzalloc(params_cmdline_sz, GFP_KERNEL);
259 if (!params)
260 return ERR_PTR(-ENOMEM);
261
262 /* Copy setup header onto bootparams. Documentation/x86/boot.txt */
263 setup_header_size = 0x0202 + kernel[0x0201] - setup_hdr_offset;
264
265 /* Is there a limit on setup header size? */
266 memcpy(&params->hdr, (kernel + setup_hdr_offset), setup_header_size);
267
268 ret = kexec_add_buffer(image, (char *)params, params_cmdline_sz,
269 params_cmdline_sz, 16, MIN_BOOTPARAM_ADDR,
270 ULONG_MAX, 1, &bootparam_load_addr);
271 if (ret)
272 goto out_free_params;
273 pr_debug("Loaded boot_param and command line at 0x%lx bufsz=0x%lx memsz=0x%lx\n",
274 bootparam_load_addr, params_cmdline_sz, params_cmdline_sz);
275
276 /* Load kernel */
277 kernel_buf = kernel + kern16_size;
278 kernel_bufsz = kernel_len - kern16_size;
279 kernel_memsz = PAGE_ALIGN(header->init_size);
280 kernel_align = header->kernel_alignment;
281
282 ret = kexec_add_buffer(image, kernel_buf,
283 kernel_bufsz, kernel_memsz, kernel_align,
284 MIN_KERNEL_LOAD_ADDR, ULONG_MAX, 1,
285 &kernel_load_addr);
286 if (ret)
287 goto out_free_params;
288
289 pr_debug("Loaded 64bit kernel at 0x%lx bufsz=0x%lx memsz=0x%lx\n",
290 kernel_load_addr, kernel_memsz, kernel_memsz);
291
292 /* Load initrd high */
293 if (initrd) {
294 ret = kexec_add_buffer(image, initrd, initrd_len, initrd_len,
295 PAGE_SIZE, MIN_INITRD_LOAD_ADDR,
296 ULONG_MAX, 1, &initrd_load_addr);
297 if (ret)
298 goto out_free_params;
299
300 pr_debug("Loaded initrd at 0x%lx bufsz=0x%lx memsz=0x%lx\n",
301 initrd_load_addr, initrd_len, initrd_len);
302
303 setup_initrd(params, initrd_load_addr, initrd_len);
304 }
305
306 setup_cmdline(params, bootparam_load_addr, sizeof(struct boot_params),
307 cmdline, cmdline_len);
308
309 /* bootloader info. Do we need a separate ID for kexec kernel loader? */
310 params->hdr.type_of_loader = 0x0D << 4;
311 params->hdr.loadflags = 0;
312
313 /* Setup purgatory regs for entry */
314 ret = kexec_purgatory_get_set_symbol(image, "entry64_regs", &regs64,
315 sizeof(regs64), 1);
316 if (ret)
317 goto out_free_params;
318
319 regs64.rbx = 0; /* Bootstrap Processor */
320 regs64.rsi = bootparam_load_addr;
321 regs64.rip = kernel_load_addr + 0x200;
322 stack = kexec_purgatory_get_symbol_addr(image, "stack_end");
323 if (IS_ERR(stack)) {
324 pr_err("Could not find address of symbol stack_end\n");
325 ret = -EINVAL;
326 goto out_free_params;
327 }
328
329 regs64.rsp = (unsigned long)stack;
330 ret = kexec_purgatory_get_set_symbol(image, "entry64_regs", &regs64,
331 sizeof(regs64), 0);
332 if (ret)
333 goto out_free_params;
334
335 setup_boot_parameters(params);
336
337 /* Allocate loader specific data */
338 ldata = kzalloc(sizeof(struct bzimage64_data), GFP_KERNEL);
339 if (!ldata) {
340 ret = -ENOMEM;
341 goto out_free_params;
342 }
343
344 /*
345 * Store pointer to params so that it could be freed after loading
346 * params segment has been loaded and contents have been copied
347 * somewhere else.
348 */
349 ldata->bootparams_buf = params;
350 return ldata;
351
352out_free_params:
353 kfree(params);
354 return ERR_PTR(ret);
355}
356
357/* This cleanup function is called after various segments have been loaded */
358int bzImage64_cleanup(void *loader_data)
359{
360 struct bzimage64_data *ldata = loader_data;
361
362 if (!ldata)
363 return 0;
364
365 kfree(ldata->bootparams_buf);
366 ldata->bootparams_buf = NULL;
367
368 return 0;
369}
370
371struct kexec_file_ops kexec_bzImage64_ops = {
372 .probe = bzImage64_probe,
373 .load = bzImage64_load,
374 .cleanup = bzImage64_cleanup,
375};
diff --git a/arch/x86/kernel/machine_kexec_64.c b/arch/x86/kernel/machine_kexec_64.c
index 88404c440727..18d0f9e0b6da 100644
--- a/arch/x86/kernel/machine_kexec_64.c
+++ b/arch/x86/kernel/machine_kexec_64.c
@@ -23,9 +23,10 @@
23#include <asm/tlbflush.h> 23#include <asm/tlbflush.h>
24#include <asm/mmu_context.h> 24#include <asm/mmu_context.h>
25#include <asm/debugreg.h> 25#include <asm/debugreg.h>
26#include <asm/kexec-bzimage64.h>
26 27
27static struct kexec_file_ops *kexec_file_loaders[] = { 28static struct kexec_file_ops *kexec_file_loaders[] = {
28 NULL, 29 &kexec_bzImage64_ops,
29}; 30};
30 31
31static void free_transition_pgtable(struct kimage *image) 32static void free_transition_pgtable(struct kimage *image)
@@ -328,7 +329,7 @@ int arch_kimage_file_post_load_cleanup(struct kimage *image)
328 if (!image->fops || !image->fops->cleanup) 329 if (!image->fops || !image->fops->cleanup)
329 return 0; 330 return 0;
330 331
331 return image->fops->cleanup(image); 332 return image->fops->cleanup(image->image_loader_data);
332} 333}
333 334
334/* 335/*
diff --git a/include/linux/kexec.h b/include/linux/kexec.h
index 84f09e9eca26..9481703b0e7a 100644
--- a/include/linux/kexec.h
+++ b/include/linux/kexec.h
@@ -190,7 +190,7 @@ typedef void *(kexec_load_t)(struct kimage *image, char *kernel_buf,
190 unsigned long kernel_len, char *initrd, 190 unsigned long kernel_len, char *initrd,
191 unsigned long initrd_len, char *cmdline, 191 unsigned long initrd_len, char *cmdline,
192 unsigned long cmdline_len); 192 unsigned long cmdline_len);
193typedef int (kexec_cleanup_t)(struct kimage *image); 193typedef int (kexec_cleanup_t)(void *loader_data);
194 194
195struct kexec_file_ops { 195struct kexec_file_ops {
196 kexec_probe_t *probe; 196 kexec_probe_t *probe;
diff --git a/kernel/kexec.c b/kernel/kexec.c
index 669e331aa9ec..0926f2a3ed03 100644
--- a/kernel/kexec.c
+++ b/kernel/kexec.c
@@ -460,6 +460,14 @@ static void kimage_file_post_load_cleanup(struct kimage *image)
460 460
461 /* See if architecture has anything to cleanup post load */ 461 /* See if architecture has anything to cleanup post load */
462 arch_kimage_file_post_load_cleanup(image); 462 arch_kimage_file_post_load_cleanup(image);
463
464 /*
465 * Above call should have called into bootloader to free up
466 * any data stored in kimage->image_loader_data. It should
467 * be ok now to free it up.
468 */
469 kfree(image->image_loader_data);
470 image->image_loader_data = NULL;
463} 471}
464 472
465/* 473/*
@@ -576,7 +584,6 @@ out_free_control_pages:
576 kimage_free_page_list(&image->control_pages); 584 kimage_free_page_list(&image->control_pages);
577out_free_post_load_bufs: 585out_free_post_load_bufs:
578 kimage_file_post_load_cleanup(image); 586 kimage_file_post_load_cleanup(image);
579 kfree(image->image_loader_data);
580out_free_image: 587out_free_image:
581 kfree(image); 588 kfree(image);
582 return ret; 589 return ret;
@@ -900,8 +907,6 @@ static void kimage_free(struct kimage *image)
900 /* Free the kexec control pages... */ 907 /* Free the kexec control pages... */
901 kimage_free_page_list(&image->control_pages); 908 kimage_free_page_list(&image->control_pages);
902 909
903 kfree(image->image_loader_data);
904
905 /* 910 /*
906 * Free up any temporary buffers allocated. This might hit if 911 * Free up any temporary buffers allocated. This might hit if
907 * error occurred much later after buffer allocation. 912 * error occurred much later after buffer allocation.