diff options
Diffstat (limited to 'arch/arm')
-rw-r--r-- | arch/arm/Kconfig | 14 | ||||
-rw-r--r-- | arch/arm/kernel/Makefile | 1 | ||||
-rw-r--r-- | arch/arm/kernel/calls.S | 1 | ||||
-rw-r--r-- | arch/arm/kernel/machine_kexec.c | 78 | ||||
-rw-r--r-- | arch/arm/kernel/relocate_kernel.S | 74 |
5 files changed, 168 insertions, 0 deletions
diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig index 6783c2e5512d..a8298949d166 100644 --- a/arch/arm/Kconfig +++ b/arch/arm/Kconfig | |||
@@ -738,6 +738,20 @@ config XIP_PHYS_ADDR | |||
738 | be linked for and stored to. This address is dependent on your | 738 | be linked for and stored to. This address is dependent on your |
739 | own flash usage. | 739 | own flash usage. |
740 | 740 | ||
741 | config KEXEC | ||
742 | bool "Kexec system call (EXPERIMENTAL)" | ||
743 | depends on EXPERIMENTAL | ||
744 | help | ||
745 | kexec is a system call that implements the ability to shutdown your | ||
746 | current kernel, and to start another kernel. It is like a reboot | ||
747 | but it is indepedent of the system firmware. And like a reboot | ||
748 | you can start any kernel with it, not just Linux. | ||
749 | |||
750 | It is an ongoing process to be certain the hardware in a machine | ||
751 | is properly shutdown, so do not be surprised if this code does not | ||
752 | initially work for you. It may help to enable device hotplugging | ||
753 | support. | ||
754 | |||
741 | endmenu | 755 | endmenu |
742 | 756 | ||
743 | if (ARCH_SA1100 || ARCH_INTEGRATOR || ARCH_OMAP || ARCH_IMX ) | 757 | if (ARCH_SA1100 || ARCH_INTEGRATOR || ARCH_OMAP || ARCH_IMX ) |
diff --git a/arch/arm/kernel/Makefile b/arch/arm/kernel/Makefile index ab06a86e85d5..d5002889773e 100644 --- a/arch/arm/kernel/Makefile +++ b/arch/arm/kernel/Makefile | |||
@@ -19,6 +19,7 @@ obj-$(CONFIG_ARTHUR) += arthur.o | |||
19 | obj-$(CONFIG_ISA_DMA) += dma-isa.o | 19 | obj-$(CONFIG_ISA_DMA) += dma-isa.o |
20 | obj-$(CONFIG_PCI) += bios32.o isa.o | 20 | obj-$(CONFIG_PCI) += bios32.o isa.o |
21 | obj-$(CONFIG_SMP) += smp.o | 21 | obj-$(CONFIG_SMP) += smp.o |
22 | obj-$(CONFIG_KEXEC) += machine_kexec.o relocate_kernel.o | ||
22 | obj-$(CONFIG_OABI_COMPAT) += sys_oabi-compat.o | 23 | obj-$(CONFIG_OABI_COMPAT) += sys_oabi-compat.o |
23 | 24 | ||
24 | obj-$(CONFIG_CRUNCH) += crunch.o crunch-bits.o | 25 | obj-$(CONFIG_CRUNCH) += crunch.o crunch-bits.o |
diff --git a/arch/arm/kernel/calls.S b/arch/arm/kernel/calls.S index f7598cbc7ec5..ae89cdd82b16 100644 --- a/arch/arm/kernel/calls.S +++ b/arch/arm/kernel/calls.S | |||
@@ -356,6 +356,7 @@ | |||
356 | CALL(sys_move_pages) | 356 | CALL(sys_move_pages) |
357 | /* 345 */ CALL(sys_getcpu) | 357 | /* 345 */ CALL(sys_getcpu) |
358 | CALL(sys_ni_syscall) /* eventually epoll_pwait */ | 358 | CALL(sys_ni_syscall) /* eventually epoll_pwait */ |
359 | CALL(sys_kexec_load) | ||
359 | #ifndef syscalls_counted | 360 | #ifndef syscalls_counted |
360 | .equ syscalls_padding, ((NR_syscalls + 3) & ~3) - NR_syscalls | 361 | .equ syscalls_padding, ((NR_syscalls + 3) & ~3) - NR_syscalls |
361 | #define syscalls_counted | 362 | #define syscalls_counted |
diff --git a/arch/arm/kernel/machine_kexec.c b/arch/arm/kernel/machine_kexec.c new file mode 100644 index 000000000000..863c66454f2b --- /dev/null +++ b/arch/arm/kernel/machine_kexec.c | |||
@@ -0,0 +1,78 @@ | |||
1 | /* | ||
2 | * machine_kexec.c - handle transition of Linux booting another kernel | ||
3 | */ | ||
4 | |||
5 | #include <linux/mm.h> | ||
6 | #include <linux/kexec.h> | ||
7 | #include <linux/delay.h> | ||
8 | #include <linux/reboot.h> | ||
9 | #include <asm/pgtable.h> | ||
10 | #include <asm/pgalloc.h> | ||
11 | #include <asm/mmu_context.h> | ||
12 | #include <asm/io.h> | ||
13 | #include <asm/cacheflush.h> | ||
14 | #include <asm/mach-types.h> | ||
15 | |||
16 | const extern unsigned char relocate_new_kernel[]; | ||
17 | const extern unsigned int relocate_new_kernel_size; | ||
18 | |||
19 | extern void setup_mm_for_reboot(char mode); | ||
20 | |||
21 | extern unsigned long kexec_start_address; | ||
22 | extern unsigned long kexec_indirection_page; | ||
23 | extern unsigned long kexec_mach_type; | ||
24 | |||
25 | /* | ||
26 | * Provide a dummy crash_notes definition while crash dump arrives to arm. | ||
27 | * This prevents breakage of crash_notes attribute in kernel/ksysfs.c. | ||
28 | */ | ||
29 | |||
30 | int machine_kexec_prepare(struct kimage *image) | ||
31 | { | ||
32 | return 0; | ||
33 | } | ||
34 | |||
35 | void machine_kexec_cleanup(struct kimage *image) | ||
36 | { | ||
37 | } | ||
38 | |||
39 | void machine_shutdown(void) | ||
40 | { | ||
41 | } | ||
42 | |||
43 | void machine_crash_shutdown(struct pt_regs *regs) | ||
44 | { | ||
45 | } | ||
46 | |||
47 | void machine_kexec(struct kimage *image) | ||
48 | { | ||
49 | unsigned long page_list; | ||
50 | unsigned long reboot_code_buffer_phys; | ||
51 | void *reboot_code_buffer; | ||
52 | |||
53 | |||
54 | page_list = image->head & PAGE_MASK; | ||
55 | |||
56 | /* we need both effective and real address here */ | ||
57 | reboot_code_buffer_phys = | ||
58 | page_to_pfn(image->control_code_page) << PAGE_SHIFT; | ||
59 | reboot_code_buffer = page_address(image->control_code_page); | ||
60 | |||
61 | /* Prepare parameters for reboot_code_buffer*/ | ||
62 | kexec_start_address = image->start; | ||
63 | kexec_indirection_page = page_list; | ||
64 | kexec_mach_type = machine_arch_type; | ||
65 | |||
66 | /* copy our kernel relocation code to the control code page */ | ||
67 | memcpy(reboot_code_buffer, | ||
68 | relocate_new_kernel, relocate_new_kernel_size); | ||
69 | |||
70 | |||
71 | flush_icache_range((unsigned long) reboot_code_buffer, | ||
72 | (unsigned long) reboot_code_buffer + KEXEC_CONTROL_CODE_SIZE); | ||
73 | printk(KERN_INFO "Bye!\n"); | ||
74 | |||
75 | cpu_proc_fin(); | ||
76 | setup_mm_for_reboot(0); /* mode is not used, so just pass 0*/ | ||
77 | cpu_reset(reboot_code_buffer_phys); | ||
78 | } | ||
diff --git a/arch/arm/kernel/relocate_kernel.S b/arch/arm/kernel/relocate_kernel.S new file mode 100644 index 000000000000..7baadae7cb27 --- /dev/null +++ b/arch/arm/kernel/relocate_kernel.S | |||
@@ -0,0 +1,74 @@ | |||
1 | /* | ||
2 | * relocate_kernel.S - put the kernel image in place to boot | ||
3 | */ | ||
4 | |||
5 | #include <asm/kexec.h> | ||
6 | |||
7 | .globl relocate_new_kernel | ||
8 | relocate_new_kernel: | ||
9 | |||
10 | ldr r0,kexec_indirection_page | ||
11 | ldr r1,kexec_start_address | ||
12 | |||
13 | |||
14 | 0: /* top, read another word for the indirection page */ | ||
15 | ldr r3, [r0],#4 | ||
16 | |||
17 | /* Is it a destination page. Put destination address to r4 */ | ||
18 | tst r3,#1,0 | ||
19 | beq 1f | ||
20 | bic r4,r3,#1 | ||
21 | b 0b | ||
22 | 1: | ||
23 | /* Is it an indirection page */ | ||
24 | tst r3,#2,0 | ||
25 | beq 1f | ||
26 | bic r0,r3,#2 | ||
27 | b 0b | ||
28 | 1: | ||
29 | |||
30 | /* are we done ? */ | ||
31 | tst r3,#4,0 | ||
32 | beq 1f | ||
33 | b 2f | ||
34 | |||
35 | 1: | ||
36 | /* is it source ? */ | ||
37 | tst r3,#8,0 | ||
38 | beq 0b | ||
39 | bic r3,r3,#8 | ||
40 | mov r6,#1024 | ||
41 | 9: | ||
42 | ldr r5,[r3],#4 | ||
43 | str r5,[r4],#4 | ||
44 | subs r6,r6,#1 | ||
45 | bne 9b | ||
46 | b 0b | ||
47 | |||
48 | 2: | ||
49 | /* Jump to relocated kernel */ | ||
50 | mov lr,r1 | ||
51 | mov r0,#0 | ||
52 | ldr r1,kexec_mach_type | ||
53 | mov r2,#0 | ||
54 | mov pc,lr | ||
55 | |||
56 | .globl kexec_start_address | ||
57 | kexec_start_address: | ||
58 | .long 0x0 | ||
59 | |||
60 | .globl kexec_indirection_page | ||
61 | kexec_indirection_page: | ||
62 | .long 0x0 | ||
63 | |||
64 | .globl kexec_mach_type | ||
65 | kexec_mach_type: | ||
66 | .long 0x0 | ||
67 | |||
68 | relocate_new_kernel_end: | ||
69 | |||
70 | .globl relocate_new_kernel_size | ||
71 | relocate_new_kernel_size: | ||
72 | .long relocate_new_kernel_end - relocate_new_kernel | ||
73 | |||
74 | |||