diff options
Diffstat (limited to 'arch/x86/kernel/machine_kexec_32.c')
-rw-r--r-- | arch/x86/kernel/machine_kexec_32.c | 63 |
1 files changed, 54 insertions, 9 deletions
diff --git a/arch/x86/kernel/machine_kexec_32.c b/arch/x86/kernel/machine_kexec_32.c index d0b234c9fc31..0732adba05ca 100644 --- a/arch/x86/kernel/machine_kexec_32.c +++ b/arch/x86/kernel/machine_kexec_32.c | |||
@@ -11,6 +11,9 @@ | |||
11 | #include <linux/delay.h> | 11 | #include <linux/delay.h> |
12 | #include <linux/init.h> | 12 | #include <linux/init.h> |
13 | #include <linux/numa.h> | 13 | #include <linux/numa.h> |
14 | #include <linux/ftrace.h> | ||
15 | #include <linux/suspend.h> | ||
16 | |||
14 | #include <asm/pgtable.h> | 17 | #include <asm/pgtable.h> |
15 | #include <asm/pgalloc.h> | 18 | #include <asm/pgalloc.h> |
16 | #include <asm/tlbflush.h> | 19 | #include <asm/tlbflush.h> |
@@ -20,6 +23,7 @@ | |||
20 | #include <asm/cpufeature.h> | 23 | #include <asm/cpufeature.h> |
21 | #include <asm/desc.h> | 24 | #include <asm/desc.h> |
22 | #include <asm/system.h> | 25 | #include <asm/system.h> |
26 | #include <asm/cacheflush.h> | ||
23 | 27 | ||
24 | #define PAGE_ALIGNED __attribute__ ((__aligned__(PAGE_SIZE))) | 28 | #define PAGE_ALIGNED __attribute__ ((__aligned__(PAGE_SIZE))) |
25 | static u32 kexec_pgd[1024] PAGE_ALIGNED; | 29 | static u32 kexec_pgd[1024] PAGE_ALIGNED; |
@@ -39,7 +43,7 @@ static void set_idt(void *newidt, __u16 limit) | |||
39 | curidt.address = (unsigned long)newidt; | 43 | curidt.address = (unsigned long)newidt; |
40 | 44 | ||
41 | load_idt(&curidt); | 45 | load_idt(&curidt); |
42 | }; | 46 | } |
43 | 47 | ||
44 | 48 | ||
45 | static void set_gdt(void *newgdt, __u16 limit) | 49 | static void set_gdt(void *newgdt, __u16 limit) |
@@ -51,7 +55,7 @@ static void set_gdt(void *newgdt, __u16 limit) | |||
51 | curgdt.address = (unsigned long)newgdt; | 55 | curgdt.address = (unsigned long)newgdt; |
52 | 56 | ||
53 | load_gdt(&curgdt); | 57 | load_gdt(&curgdt); |
54 | }; | 58 | } |
55 | 59 | ||
56 | static void load_segments(void) | 60 | static void load_segments(void) |
57 | { | 61 | { |
@@ -75,7 +79,7 @@ static void load_segments(void) | |||
75 | /* | 79 | /* |
76 | * A architecture hook called to validate the | 80 | * A architecture hook called to validate the |
77 | * proposed image and prepare the control pages | 81 | * proposed image and prepare the control pages |
78 | * as needed. The pages for KEXEC_CONTROL_CODE_SIZE | 82 | * as needed. The pages for KEXEC_CONTROL_PAGE_SIZE |
79 | * have been allocated, but the segments have yet | 83 | * have been allocated, but the segments have yet |
80 | * been copied into the kernel. | 84 | * been copied into the kernel. |
81 | * | 85 | * |
@@ -83,10 +87,12 @@ static void load_segments(void) | |||
83 | * reboot code buffer to allow us to avoid allocations | 87 | * reboot code buffer to allow us to avoid allocations |
84 | * later. | 88 | * later. |
85 | * | 89 | * |
86 | * Currently nothing. | 90 | * Make control page executable. |
87 | */ | 91 | */ |
88 | int machine_kexec_prepare(struct kimage *image) | 92 | int machine_kexec_prepare(struct kimage *image) |
89 | { | 93 | { |
94 | if (nx_enabled) | ||
95 | set_pages_x(image->control_code_page, 1); | ||
90 | return 0; | 96 | return 0; |
91 | } | 97 | } |
92 | 98 | ||
@@ -96,25 +102,54 @@ int machine_kexec_prepare(struct kimage *image) | |||
96 | */ | 102 | */ |
97 | void machine_kexec_cleanup(struct kimage *image) | 103 | void machine_kexec_cleanup(struct kimage *image) |
98 | { | 104 | { |
105 | if (nx_enabled) | ||
106 | set_pages_nx(image->control_code_page, 1); | ||
99 | } | 107 | } |
100 | 108 | ||
101 | /* | 109 | /* |
102 | * Do not allocate memory (or fail in any way) in machine_kexec(). | 110 | * Do not allocate memory (or fail in any way) in machine_kexec(). |
103 | * We are past the point of no return, committed to rebooting now. | 111 | * We are past the point of no return, committed to rebooting now. |
104 | */ | 112 | */ |
105 | NORET_TYPE void machine_kexec(struct kimage *image) | 113 | void machine_kexec(struct kimage *image) |
106 | { | 114 | { |
107 | unsigned long page_list[PAGES_NR]; | 115 | unsigned long page_list[PAGES_NR]; |
108 | void *control_page; | 116 | void *control_page; |
117 | int save_ftrace_enabled; | ||
118 | asmlinkage unsigned long | ||
119 | (*relocate_kernel_ptr)(unsigned long indirection_page, | ||
120 | unsigned long control_page, | ||
121 | unsigned long start_address, | ||
122 | unsigned int has_pae, | ||
123 | unsigned int preserve_context); | ||
124 | |||
125 | #ifdef CONFIG_KEXEC_JUMP | ||
126 | if (kexec_image->preserve_context) | ||
127 | save_processor_state(); | ||
128 | #endif | ||
129 | |||
130 | save_ftrace_enabled = __ftrace_enabled_save(); | ||
109 | 131 | ||
110 | /* Interrupts aren't acceptable while we reboot */ | 132 | /* Interrupts aren't acceptable while we reboot */ |
111 | local_irq_disable(); | 133 | local_irq_disable(); |
112 | 134 | ||
135 | if (image->preserve_context) { | ||
136 | #ifdef CONFIG_X86_IO_APIC | ||
137 | /* We need to put APICs in legacy mode so that we can | ||
138 | * get timer interrupts in second kernel. kexec/kdump | ||
139 | * paths already have calls to disable_IO_APIC() in | ||
140 | * one form or other. kexec jump path also need | ||
141 | * one. | ||
142 | */ | ||
143 | disable_IO_APIC(); | ||
144 | #endif | ||
145 | } | ||
146 | |||
113 | control_page = page_address(image->control_code_page); | 147 | control_page = page_address(image->control_code_page); |
114 | memcpy(control_page, relocate_kernel, PAGE_SIZE); | 148 | memcpy(control_page, relocate_kernel, KEXEC_CONTROL_CODE_MAX_SIZE); |
115 | 149 | ||
150 | relocate_kernel_ptr = control_page; | ||
116 | page_list[PA_CONTROL_PAGE] = __pa(control_page); | 151 | page_list[PA_CONTROL_PAGE] = __pa(control_page); |
117 | page_list[VA_CONTROL_PAGE] = (unsigned long)relocate_kernel; | 152 | page_list[VA_CONTROL_PAGE] = (unsigned long)control_page; |
118 | page_list[PA_PGD] = __pa(kexec_pgd); | 153 | page_list[PA_PGD] = __pa(kexec_pgd); |
119 | page_list[VA_PGD] = (unsigned long)kexec_pgd; | 154 | page_list[VA_PGD] = (unsigned long)kexec_pgd; |
120 | #ifdef CONFIG_X86_PAE | 155 | #ifdef CONFIG_X86_PAE |
@@ -127,6 +162,7 @@ NORET_TYPE void machine_kexec(struct kimage *image) | |||
127 | page_list[VA_PTE_0] = (unsigned long)kexec_pte0; | 162 | page_list[VA_PTE_0] = (unsigned long)kexec_pte0; |
128 | page_list[PA_PTE_1] = __pa(kexec_pte1); | 163 | page_list[PA_PTE_1] = __pa(kexec_pte1); |
129 | page_list[VA_PTE_1] = (unsigned long)kexec_pte1; | 164 | page_list[VA_PTE_1] = (unsigned long)kexec_pte1; |
165 | page_list[PA_SWAP_PAGE] = (page_to_pfn(image->swap_page) << PAGE_SHIFT); | ||
130 | 166 | ||
131 | /* The segment registers are funny things, they have both a | 167 | /* The segment registers are funny things, they have both a |
132 | * visible and an invisible part. Whenever the visible part is | 168 | * visible and an invisible part. Whenever the visible part is |
@@ -145,8 +181,17 @@ NORET_TYPE void machine_kexec(struct kimage *image) | |||
145 | set_idt(phys_to_virt(0),0); | 181 | set_idt(phys_to_virt(0),0); |
146 | 182 | ||
147 | /* now call it */ | 183 | /* now call it */ |
148 | relocate_kernel((unsigned long)image->head, (unsigned long)page_list, | 184 | image->start = relocate_kernel_ptr((unsigned long)image->head, |
149 | image->start, cpu_has_pae); | 185 | (unsigned long)page_list, |
186 | image->start, cpu_has_pae, | ||
187 | image->preserve_context); | ||
188 | |||
189 | #ifdef CONFIG_KEXEC_JUMP | ||
190 | if (kexec_image->preserve_context) | ||
191 | restore_processor_state(); | ||
192 | #endif | ||
193 | |||
194 | __ftrace_enabled_restore(save_ftrace_enabled); | ||
150 | } | 195 | } |
151 | 196 | ||
152 | void arch_crash_save_vmcoreinfo(void) | 197 | void arch_crash_save_vmcoreinfo(void) |