Commit 3ab83521378268044a448113c6aa9a9e245f4d2f

Authored by Huang Ying
Committed by Linus Torvalds
1 parent 7fccf03265

kexec jump

This patch provides an enhancement to kexec/kdump.  It implements the
following features:

- Backup/restore memory used by the original kernel before/after
  kexec.

- Save/restore CPU state before/after kexec.

The features of this patch can be used as a general method to call program in
physical mode (paging turning off).  This can be used to call BIOS code under
Linux.

kexec-tools needs to be patched to support kexec jump. The patches and
the precompiled kexec can be download from the following URL:

       source: http://khibernation.sourceforge.net/download/release_v10/kexec-tools/kexec-tools-src_git_kh10.tar.bz2
       patches: http://khibernation.sourceforge.net/download/release_v10/kexec-tools/kexec-tools-patches_git_kh10.tar.bz2
       binary: http://khibernation.sourceforge.net/download/release_v10/kexec-tools/kexec_git_kh10

Usage example of calling some physical mode code and return:

1. Compile and install patched kernel with following options selected:

CONFIG_X86_32=y
CONFIG_KEXEC=y
CONFIG_PM=y
CONFIG_KEXEC_JUMP=y

2. Build patched kexec-tool or download the pre-built one.

3. Build some physical mode executable named such as "phy_mode"

4. Boot kernel compiled in step 1.

5. Load physical mode executable with /sbin/kexec. The shell command
   line can be as follow:

   /sbin/kexec --load-preserve-context --args-none phy_mode

6. Call physical mode executable with following shell command line:

   /sbin/kexec -e

Implementation point:

To support jumping without reserving memory.  One shadow backup page (source
page) is allocated for each page used by kexeced code image (destination
page).  When do kexec_load, the image of kexeced code is loaded into source
pages, and before executing, the destination pages and the source pages are
swapped, so the contents of destination pages are backupped.  Before jumping
to the kexeced code image and after jumping back to the original kernel, the
destination pages and the source pages are swapped too.

C ABI (calling convention) is used as communication protocol between
kernel and called code.

A flag named KEXEC_PRESERVE_CONTEXT for sys_kexec_load is added to
indicate that the loaded kernel image is used for jumping back.

Now, only the i386 architecture is supported.

Signed-off-by: Huang Ying <ying.huang@intel.com>
Acked-by: Vivek Goyal <vgoyal@redhat.com>
Cc: "Eric W. Biederman" <ebiederm@xmission.com>
Cc: Pavel Machek <pavel@ucw.cz>
Cc: Nigel Cunningham <nigel@nigel.suspend2.net>
Cc: "Rafael J. Wysocki" <rjw@sisk.pl>
Cc: Ingo Molnar <mingo@elte.hu>
Cc: Thomas Gleixner <tglx@linutronix.de>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>

Showing 10 changed files with 269 additions and 68 deletions Side-by-side Diff

arch/powerpc/kernel/machine_kexec.c
... ... @@ -48,7 +48,7 @@
48 48 * Do not allocate memory (or fail in any way) in machine_kexec().
49 49 * We are past the point of no return, committed to rebooting now.
50 50 */
51   -NORET_TYPE void machine_kexec(struct kimage *image)
  51 +void machine_kexec(struct kimage *image)
52 52 {
53 53 if (ppc_md.machine_kexec)
54 54 ppc_md.machine_kexec(image);
arch/sh/kernel/machine_kexec.c
... ... @@ -70,7 +70,7 @@
70 70 * Do not allocate memory (or fail in any way) in machine_kexec().
71 71 * We are past the point of no return, committed to rebooting now.
72 72 */
73   -NORET_TYPE void machine_kexec(struct kimage *image)
  73 +void machine_kexec(struct kimage *image)
74 74 {
75 75  
76 76 unsigned long page_list;
... ... @@ -1279,6 +1279,13 @@
1279 1279 (CONFIG_RELOCATABLE=y).
1280 1280 For more details see Documentation/kdump/kdump.txt
1281 1281  
  1282 +config KEXEC_JUMP
  1283 + bool "kexec jump (EXPERIMENTAL)"
  1284 + depends on EXPERIMENTAL
  1285 + depends on KEXEC && PM_SLEEP && X86_32
  1286 + help
  1287 + Invoke code in physical address mode via KEXEC
  1288 +
1282 1289 config PHYSICAL_START
1283 1290 hex "Physical address where the kernel is loaded" if (EMBEDDED || CRASH_DUMP)
1284 1291 default "0x1000000" if X86_NUMAQ
arch/x86/kernel/machine_kexec_32.c
... ... @@ -22,6 +22,7 @@
22 22 #include <asm/cpufeature.h>
23 23 #include <asm/desc.h>
24 24 #include <asm/system.h>
  25 +#include <asm/cacheflush.h>
25 26  
26 27 #define PAGE_ALIGNED __attribute__ ((__aligned__(PAGE_SIZE)))
27 28 static u32 kexec_pgd[1024] PAGE_ALIGNED;
28 29  
... ... @@ -85,10 +86,12 @@
85 86 * reboot code buffer to allow us to avoid allocations
86 87 * later.
87 88 *
88   - * Currently nothing.
  89 + * Make control page executable.
89 90 */
90 91 int machine_kexec_prepare(struct kimage *image)
91 92 {
  93 + if (nx_enabled)
  94 + set_pages_x(image->control_code_page, 1);
92 95 return 0;
93 96 }
94 97  
95 98  
96 99  
... ... @@ -98,16 +101,24 @@
98 101 */
99 102 void machine_kexec_cleanup(struct kimage *image)
100 103 {
  104 + if (nx_enabled)
  105 + set_pages_nx(image->control_code_page, 1);
101 106 }
102 107  
103 108 /*
104 109 * Do not allocate memory (or fail in any way) in machine_kexec().
105 110 * We are past the point of no return, committed to rebooting now.
106 111 */
107   -NORET_TYPE void machine_kexec(struct kimage *image)
  112 +void machine_kexec(struct kimage *image)
108 113 {
109 114 unsigned long page_list[PAGES_NR];
110 115 void *control_page;
  116 + asmlinkage unsigned long
  117 + (*relocate_kernel_ptr)(unsigned long indirection_page,
  118 + unsigned long control_page,
  119 + unsigned long start_address,
  120 + unsigned int has_pae,
  121 + unsigned int preserve_context);
111 122  
112 123 tracer_disable();
113 124  
114 125  
115 126  
... ... @@ -115,10 +126,11 @@
115 126 local_irq_disable();
116 127  
117 128 control_page = page_address(image->control_code_page);
118   - memcpy(control_page, relocate_kernel, PAGE_SIZE);
  129 + memcpy(control_page, relocate_kernel, PAGE_SIZE/2);
119 130  
  131 + relocate_kernel_ptr = control_page;
120 132 page_list[PA_CONTROL_PAGE] = __pa(control_page);
121   - page_list[VA_CONTROL_PAGE] = (unsigned long)relocate_kernel;
  133 + page_list[VA_CONTROL_PAGE] = (unsigned long)control_page;
122 134 page_list[PA_PGD] = __pa(kexec_pgd);
123 135 page_list[VA_PGD] = (unsigned long)kexec_pgd;
124 136 #ifdef CONFIG_X86_PAE
... ... @@ -131,6 +143,7 @@
131 143 page_list[VA_PTE_0] = (unsigned long)kexec_pte0;
132 144 page_list[PA_PTE_1] = __pa(kexec_pte1);
133 145 page_list[VA_PTE_1] = (unsigned long)kexec_pte1;
  146 + page_list[PA_SWAP_PAGE] = (page_to_pfn(image->swap_page) << PAGE_SHIFT);
134 147  
135 148 /* The segment registers are funny things, they have both a
136 149 * visible and an invisible part. Whenever the visible part is
... ... @@ -149,8 +162,10 @@
149 162 set_idt(phys_to_virt(0),0);
150 163  
151 164 /* now call it */
152   - relocate_kernel((unsigned long)image->head, (unsigned long)page_list,
153   - image->start, cpu_has_pae);
  165 + image->start = relocate_kernel_ptr((unsigned long)image->head,
  166 + (unsigned long)page_list,
  167 + image->start, cpu_has_pae,
  168 + image->preserve_context);
154 169 }
155 170  
156 171 void arch_crash_save_vmcoreinfo(void)
arch/x86/kernel/machine_kexec_64.c
... ... @@ -181,7 +181,7 @@
181 181 * Do not allocate memory (or fail in any way) in machine_kexec().
182 182 * We are past the point of no return, committed to rebooting now.
183 183 */
184   -NORET_TYPE void machine_kexec(struct kimage *image)
  184 +void machine_kexec(struct kimage *image)
185 185 {
186 186 unsigned long page_list[PAGES_NR];
187 187 void *control_page;
arch/x86/kernel/relocate_kernel_32.S
... ... @@ -20,12 +20,45 @@
20 20 #define PAGE_ATTR (_PAGE_PRESENT | _PAGE_RW | _PAGE_ACCESSED | _PAGE_DIRTY)
21 21 #define PAE_PGD_ATTR (_PAGE_PRESENT)
22 22  
  23 +/* control_page + PAGE_SIZE/2 ~ control_page + PAGE_SIZE * 3/4 are
  24 + * used to save some data for jumping back
  25 + */
  26 +#define DATA(offset) (PAGE_SIZE/2+(offset))
  27 +
  28 +/* Minimal CPU state */
  29 +#define ESP DATA(0x0)
  30 +#define CR0 DATA(0x4)
  31 +#define CR3 DATA(0x8)
  32 +#define CR4 DATA(0xc)
  33 +
  34 +/* other data */
  35 +#define CP_VA_CONTROL_PAGE DATA(0x10)
  36 +#define CP_PA_PGD DATA(0x14)
  37 +#define CP_PA_SWAP_PAGE DATA(0x18)
  38 +#define CP_PA_BACKUP_PAGES_MAP DATA(0x1c)
  39 +
23 40 .text
24 41 .align PAGE_SIZE
25 42 .globl relocate_kernel
26 43 relocate_kernel:
27   - movl 8(%esp), %ebp /* list of pages */
  44 + /* Save the CPU context, used for jumping back */
28 45  
  46 + pushl %ebx
  47 + pushl %esi
  48 + pushl %edi
  49 + pushl %ebp
  50 + pushf
  51 +
  52 + movl 20+8(%esp), %ebp /* list of pages */
  53 + movl PTR(VA_CONTROL_PAGE)(%ebp), %edi
  54 + movl %esp, ESP(%edi)
  55 + movl %cr0, %eax
  56 + movl %eax, CR0(%edi)
  57 + movl %cr3, %eax
  58 + movl %eax, CR3(%edi)
  59 + movl %cr4, %eax
  60 + movl %eax, CR4(%edi)
  61 +
29 62 #ifdef CONFIG_X86_PAE
30 63 /* map the control page at its virtual address */
31 64  
32 65  
... ... @@ -138,15 +171,25 @@
138 171  
139 172 relocate_new_kernel:
140 173 /* read the arguments and say goodbye to the stack */
141   - movl 4(%esp), %ebx /* page_list */
142   - movl 8(%esp), %ebp /* list of pages */
143   - movl 12(%esp), %edx /* start address */
144   - movl 16(%esp), %ecx /* cpu_has_pae */
  174 + movl 20+4(%esp), %ebx /* page_list */
  175 + movl 20+8(%esp), %ebp /* list of pages */
  176 + movl 20+12(%esp), %edx /* start address */
  177 + movl 20+16(%esp), %ecx /* cpu_has_pae */
  178 + movl 20+20(%esp), %esi /* preserve_context */
145 179  
146 180 /* zero out flags, and disable interrupts */
147 181 pushl $0
148 182 popfl
149 183  
  184 + /* save some information for jumping back */
  185 + movl PTR(VA_CONTROL_PAGE)(%ebp), %edi
  186 + movl %edi, CP_VA_CONTROL_PAGE(%edi)
  187 + movl PTR(PA_PGD)(%ebp), %eax
  188 + movl %eax, CP_PA_PGD(%edi)
  189 + movl PTR(PA_SWAP_PAGE)(%ebp), %eax
  190 + movl %eax, CP_PA_SWAP_PAGE(%edi)
  191 + movl %ebx, CP_PA_BACKUP_PAGES_MAP(%edi)
  192 +
150 193 /* get physical address of control page now */
151 194 /* this is impossible after page table switch */
152 195 movl PTR(PA_CONTROL_PAGE)(%ebp), %edi
153 196  
... ... @@ -197,8 +240,90 @@
197 240 xorl %eax, %eax
198 241 movl %eax, %cr3
199 242  
  243 + movl CP_PA_SWAP_PAGE(%edi), %eax
  244 + pushl %eax
  245 + pushl %ebx
  246 + call swap_pages
  247 + addl $8, %esp
  248 +
  249 + /* To be certain of avoiding problems with self-modifying code
  250 + * I need to execute a serializing instruction here.
  251 + * So I flush the TLB, it's handy, and not processor dependent.
  252 + */
  253 + xorl %eax, %eax
  254 + movl %eax, %cr3
  255 +
  256 + /* set all of the registers to known values */
  257 + /* leave %esp alone */
  258 +
  259 + testl %esi, %esi
  260 + jnz 1f
  261 + xorl %edi, %edi
  262 + xorl %eax, %eax
  263 + xorl %ebx, %ebx
  264 + xorl %ecx, %ecx
  265 + xorl %edx, %edx
  266 + xorl %esi, %esi
  267 + xorl %ebp, %ebp
  268 + ret
  269 +1:
  270 + popl %edx
  271 + movl CP_PA_SWAP_PAGE(%edi), %esp
  272 + addl $PAGE_SIZE, %esp
  273 +2:
  274 + call *%edx
  275 +
  276 + /* get the re-entry point of the peer system */
  277 + movl 0(%esp), %ebp
  278 + call 1f
  279 +1:
  280 + popl %ebx
  281 + subl $(1b - relocate_kernel), %ebx
  282 + movl CP_VA_CONTROL_PAGE(%ebx), %edi
  283 + lea PAGE_SIZE(%ebx), %esp
  284 + movl CP_PA_SWAP_PAGE(%ebx), %eax
  285 + movl CP_PA_BACKUP_PAGES_MAP(%ebx), %edx
  286 + pushl %eax
  287 + pushl %edx
  288 + call swap_pages
  289 + addl $8, %esp
  290 + movl CP_PA_PGD(%ebx), %eax
  291 + movl %eax, %cr3
  292 + movl %cr0, %eax
  293 + orl $(1<<31), %eax
  294 + movl %eax, %cr0
  295 + lea PAGE_SIZE(%edi), %esp
  296 + movl %edi, %eax
  297 + addl $(virtual_mapped - relocate_kernel), %eax
  298 + pushl %eax
  299 + ret
  300 +
  301 +virtual_mapped:
  302 + movl CR4(%edi), %eax
  303 + movl %eax, %cr4
  304 + movl CR3(%edi), %eax
  305 + movl %eax, %cr3
  306 + movl CR0(%edi), %eax
  307 + movl %eax, %cr0
  308 + movl ESP(%edi), %esp
  309 + movl %ebp, %eax
  310 +
  311 + popf
  312 + popl %ebp
  313 + popl %edi
  314 + popl %esi
  315 + popl %ebx
  316 + ret
  317 +
200 318 /* Do the copies */
201   - movl %ebx, %ecx
  319 +swap_pages:
  320 + movl 8(%esp), %edx
  321 + movl 4(%esp), %ecx
  322 + pushl %ebp
  323 + pushl %ebx
  324 + pushl %edi
  325 + pushl %esi
  326 + movl %ecx, %ebx
202 327 jmp 1f
203 328  
204 329 0: /* top, read another word from the indirection page */
205 330  
206 331  
207 332  
208 333  
... ... @@ -226,28 +351,29 @@
226 351 movl %ecx, %esi /* For every source page do a copy */
227 352 andl $0xfffff000, %esi
228 353  
  354 + movl %edi, %eax
  355 + movl %esi, %ebp
  356 +
  357 + movl %edx, %edi
229 358 movl $1024, %ecx
230 359 rep ; movsl
231   - jmp 0b
232 360  
233   -3:
  361 + movl %ebp, %edi
  362 + movl %eax, %esi
  363 + movl $1024, %ecx
  364 + rep ; movsl
234 365  
235   - /* To be certain of avoiding problems with self-modifying code
236   - * I need to execute a serializing instruction here.
237   - * So I flush the TLB, it's handy, and not processor dependent.
238   - */
239   - xorl %eax, %eax
240   - movl %eax, %cr3
  366 + movl %eax, %edi
  367 + movl %edx, %esi
  368 + movl $1024, %ecx
  369 + rep ; movsl
241 370  
242   - /* set all of the registers to known values */
243   - /* leave %esp alone */
244   -
245   - xorl %eax, %eax
246   - xorl %ebx, %ebx
247   - xorl %ecx, %ecx
248   - xorl %edx, %edx
249   - xorl %esi, %esi
250   - xorl %edi, %edi
251   - xorl %ebp, %ebp
  371 + lea PAGE_SIZE(%ebp), %esi
  372 + jmp 0b
  373 +3:
  374 + popl %esi
  375 + popl %edi
  376 + popl %ebx
  377 + popl %ebp
252 378 ret
include/asm-x86/kexec.h
... ... @@ -10,14 +10,15 @@
10 10 # define VA_PTE_0 5
11 11 # define PA_PTE_1 6
12 12 # define VA_PTE_1 7
  13 +# define PA_SWAP_PAGE 8
13 14 # ifdef CONFIG_X86_PAE
14   -# define PA_PMD_0 8
15   -# define VA_PMD_0 9
16   -# define PA_PMD_1 10
17   -# define VA_PMD_1 11
18   -# define PAGES_NR 12
  15 +# define PA_PMD_0 9
  16 +# define VA_PMD_0 10
  17 +# define PA_PMD_1 11
  18 +# define VA_PMD_1 12
  19 +# define PAGES_NR 13
19 20 # else
20   -# define PAGES_NR 8
  21 +# define PAGES_NR 9
21 22 # endif
22 23 #else
23 24 # define PA_CONTROL_PAGE 0
24 25  
... ... @@ -152,11 +153,12 @@
152 153 }
153 154  
154 155 #ifdef CONFIG_X86_32
155   -asmlinkage NORET_TYPE void
  156 +asmlinkage unsigned long
156 157 relocate_kernel(unsigned long indirection_page,
157 158 unsigned long control_page,
158 159 unsigned long start_address,
159   - unsigned int has_pae) ATTRIB_NORET;
  160 + unsigned int has_pae,
  161 + unsigned int preserve_context);
160 162 #else
161 163 NORET_TYPE void
162 164 relocate_kernel(unsigned long indirection_page,
include/linux/kexec.h
... ... @@ -83,6 +83,7 @@
83 83  
84 84 unsigned long start;
85 85 struct page *control_code_page;
  86 + struct page *swap_page;
86 87  
87 88 unsigned long nr_segments;
88 89 struct kexec_segment segment[KEXEC_SEGMENT_MAX];
89 90  
90 91  
... ... @@ -98,18 +99,20 @@
98 99 unsigned int type : 1;
99 100 #define KEXEC_TYPE_DEFAULT 0
100 101 #define KEXEC_TYPE_CRASH 1
  102 + unsigned int preserve_context : 1;
101 103 };
102 104  
103 105  
104 106  
105 107 /* kexec interface functions */
106   -extern NORET_TYPE void machine_kexec(struct kimage *image) ATTRIB_NORET;
  108 +extern void machine_kexec(struct kimage *image);
107 109 extern int machine_kexec_prepare(struct kimage *image);
108 110 extern void machine_kexec_cleanup(struct kimage *image);
109 111 extern asmlinkage long sys_kexec_load(unsigned long entry,
110 112 unsigned long nr_segments,
111 113 struct kexec_segment __user *segments,
112 114 unsigned long flags);
  115 +extern int kernel_kexec(void);
113 116 #ifdef CONFIG_COMPAT
114 117 extern asmlinkage long compat_sys_kexec_load(unsigned long entry,
115 118 unsigned long nr_segments,
... ... @@ -156,8 +159,9 @@
156 159 #define kexec_flush_icache_page(page)
157 160 #endif
158 161  
159   -#define KEXEC_ON_CRASH 0x00000001
160   -#define KEXEC_ARCH_MASK 0xffff0000
  162 +#define KEXEC_ON_CRASH 0x00000001
  163 +#define KEXEC_PRESERVE_CONTEXT 0x00000002
  164 +#define KEXEC_ARCH_MASK 0xffff0000
161 165  
162 166 /* These values match the ELF architecture values.
163 167 * Unless there is a good reason that should continue to be the case.
... ... @@ -174,7 +178,12 @@
174 178 #define KEXEC_ARCH_MIPS_LE (10 << 16)
175 179 #define KEXEC_ARCH_MIPS ( 8 << 16)
176 180  
177   -#define KEXEC_FLAGS (KEXEC_ON_CRASH) /* List of defined/legal kexec flags */
  181 +/* List of defined/legal kexec flags */
  182 +#ifndef CONFIG_KEXEC_JUMP
  183 +#define KEXEC_FLAGS KEXEC_ON_CRASH
  184 +#else
  185 +#define KEXEC_FLAGS (KEXEC_ON_CRASH | KEXEC_PRESERVE_CONTEXT)
  186 +#endif
178 187  
179 188 #define VMCOREINFO_BYTES (4096)
180 189 #define VMCOREINFO_NOTE_NAME "VMCOREINFO"
... ... @@ -24,6 +24,8 @@
24 24 #include <linux/utsrelease.h>
25 25 #include <linux/utsname.h>
26 26 #include <linux/numa.h>
  27 +#include <linux/suspend.h>
  28 +#include <linux/device.h>
27 29  
28 30 #include <asm/page.h>
29 31 #include <asm/uaccess.h>
... ... @@ -242,6 +244,12 @@
242 244 goto out;
243 245 }
244 246  
  247 + image->swap_page = kimage_alloc_control_pages(image, 0);
  248 + if (!image->swap_page) {
  249 + printk(KERN_ERR "Could not allocate swap buffer\n");
  250 + goto out;
  251 + }
  252 +
245 253 result = 0;
246 254 out:
247 255 if (result == 0)
... ... @@ -986,6 +994,8 @@
986 994 if (result)
987 995 goto out;
988 996  
  997 + if (flags & KEXEC_PRESERVE_CONTEXT)
  998 + image->preserve_context = 1;
989 999 result = machine_kexec_prepare(image);
990 1000 if (result)
991 1001 goto out;
... ... @@ -1411,4 +1421,51 @@
1411 1421 }
1412 1422  
1413 1423 module_init(crash_save_vmcoreinfo_init)
  1424 +
  1425 +/**
  1426 + * kernel_kexec - reboot the system
  1427 + *
  1428 + * Move into place and start executing a preloaded standalone
  1429 + * executable. If nothing was preloaded return an error.
  1430 + */
  1431 +int kernel_kexec(void)
  1432 +{
  1433 + int error = 0;
  1434 +
  1435 + if (xchg(&kexec_lock, 1))
  1436 + return -EBUSY;
  1437 + if (!kexec_image) {
  1438 + error = -EINVAL;
  1439 + goto Unlock;
  1440 + }
  1441 +
  1442 + if (kexec_image->preserve_context) {
  1443 +#ifdef CONFIG_KEXEC_JUMP
  1444 + local_irq_disable();
  1445 + save_processor_state();
  1446 +#endif
  1447 + } else {
  1448 + blocking_notifier_call_chain(&reboot_notifier_list,
  1449 + SYS_RESTART, NULL);
  1450 + system_state = SYSTEM_RESTART;
  1451 + device_shutdown();
  1452 + sysdev_shutdown();
  1453 + printk(KERN_EMERG "Starting new kernel\n");
  1454 + machine_shutdown();
  1455 + }
  1456 +
  1457 + machine_kexec(kexec_image);
  1458 +
  1459 + if (kexec_image->preserve_context) {
  1460 +#ifdef CONFIG_KEXEC_JUMP
  1461 + restore_processor_state();
  1462 + local_irq_enable();
  1463 +#endif
  1464 + }
  1465 +
  1466 + Unlock:
  1467 + xchg(&kexec_lock, 0);
  1468 +
  1469 + return error;
  1470 +}
... ... @@ -301,26 +301,6 @@
301 301 }
302 302 EXPORT_SYMBOL_GPL(kernel_restart);
303 303  
304   -/**
305   - * kernel_kexec - reboot the system
306   - *
307   - * Move into place and start executing a preloaded standalone
308   - * executable. If nothing was preloaded return an error.
309   - */
310   -static void kernel_kexec(void)
311   -{
312   -#ifdef CONFIG_KEXEC
313   - struct kimage *image;
314   - image = xchg(&kexec_image, NULL);
315   - if (!image)
316   - return;
317   - kernel_restart_prepare(NULL);
318   - printk(KERN_EMERG "Starting new kernel\n");
319   - machine_shutdown();
320   - machine_kexec(image);
321   -#endif
322   -}
323   -
324 304 static void kernel_shutdown_prepare(enum system_states state)
325 305 {
326 306 blocking_notifier_call_chain(&reboot_notifier_list,
327 307  
... ... @@ -425,10 +405,15 @@
425 405 kernel_restart(buffer);
426 406 break;
427 407  
  408 +#ifdef CONFIG_KEXEC
428 409 case LINUX_REBOOT_CMD_KEXEC:
429   - kernel_kexec();
430   - unlock_kernel();
431   - return -EINVAL;
  410 + {
  411 + int ret;
  412 + ret = kernel_kexec();
  413 + unlock_kernel();
  414 + return ret;
  415 + }
  416 +#endif
432 417  
433 418 #ifdef CONFIG_HIBERNATION
434 419 case LINUX_REBOOT_CMD_SW_SUSPEND: