Commit | Line | Data |
---|---|---|
5033cba0 EB |
1 | /* |
2 | * relocate_kernel.S - put the kernel image in place to boot | |
3 | * Copyright (C) 2002-2004 Eric Biederman <ebiederm@xmission.com> | |
4 | * | |
5 | * This source code is licensed under the GNU General Public License, | |
6 | * Version 2. See the file COPYING for more details. | |
7 | */ | |
8 | ||
9 | #include <linux/linkage.h> | |
3566561b MD |
10 | #include <asm/page.h> |
11 | #include <asm/kexec.h> | |
fd3af531 | 12 | #include <asm/processor-flags.h> |
366932de | 13 | #include <asm/pgtable.h> |
3566561b MD |
14 | |
15 | /* | |
16 | * Must be relocatable PIC code callable as a C function | |
17 | */ | |
18 | ||
19 | #define PTR(x) (x << 2) | |
366932de | 20 | #define PAGE_ATTR (_PAGE_PRESENT | _PAGE_RW | _PAGE_ACCESSED | _PAGE_DIRTY) |
21 | #define PAE_PGD_ATTR (_PAGE_PRESENT) | |
3566561b | 22 | |
fb45daa6 HY |
23 | /* control_page + KEXEC_CONTROL_CODE_MAX_SIZE |
24 | * ~ control_page + PAGE_SIZE are used as data storage and stack for | |
25 | * jumping back | |
3ab83521 | 26 | */ |
fb45daa6 | 27 | #define DATA(offset) (KEXEC_CONTROL_CODE_MAX_SIZE+(offset)) |
3ab83521 HY |
28 | |
29 | /* Minimal CPU state */ | |
30 | #define ESP DATA(0x0) | |
31 | #define CR0 DATA(0x4) | |
32 | #define CR3 DATA(0x8) | |
33 | #define CR4 DATA(0xc) | |
34 | ||
35 | /* other data */ | |
36 | #define CP_VA_CONTROL_PAGE DATA(0x10) | |
37 | #define CP_PA_PGD DATA(0x14) | |
38 | #define CP_PA_SWAP_PAGE DATA(0x18) | |
39 | #define CP_PA_BACKUP_PAGES_MAP DATA(0x1c) | |
40 | ||
3566561b | 41 | .text |
288621e3 | 42 | .align PAGE_SIZE |
3566561b MD |
43 | .globl relocate_kernel |
44 | relocate_kernel: | |
3ab83521 HY |
45 | /* Save the CPU context, used for jumping back */ |
46 | ||
47 | pushl %ebx | |
48 | pushl %esi | |
49 | pushl %edi | |
50 | pushl %ebp | |
51 | pushf | |
52 | ||
53 | movl 20+8(%esp), %ebp /* list of pages */ | |
54 | movl PTR(VA_CONTROL_PAGE)(%ebp), %edi | |
55 | movl %esp, ESP(%edi) | |
56 | movl %cr0, %eax | |
57 | movl %eax, CR0(%edi) | |
58 | movl %cr3, %eax | |
59 | movl %eax, CR3(%edi) | |
60 | movl %cr4, %eax | |
61 | movl %eax, CR4(%edi) | |
3566561b MD |
62 | |
63 | #ifdef CONFIG_X86_PAE | |
64 | /* map the control page at its virtual address */ | |
65 | ||
66 | movl PTR(VA_PGD)(%ebp), %edi | |
67 | movl PTR(VA_CONTROL_PAGE)(%ebp), %eax | |
68 | andl $0xc0000000, %eax | |
69 | shrl $27, %eax | |
70 | addl %edi, %eax | |
71 | ||
72 | movl PTR(PA_PMD_0)(%ebp), %edx | |
73 | orl $PAE_PGD_ATTR, %edx | |
74 | movl %edx, (%eax) | |
75 | ||
76 | movl PTR(VA_PMD_0)(%ebp), %edi | |
77 | movl PTR(VA_CONTROL_PAGE)(%ebp), %eax | |
78 | andl $0x3fe00000, %eax | |
79 | shrl $18, %eax | |
80 | addl %edi, %eax | |
81 | ||
82 | movl PTR(PA_PTE_0)(%ebp), %edx | |
83 | orl $PAGE_ATTR, %edx | |
84 | movl %edx, (%eax) | |
85 | ||
86 | movl PTR(VA_PTE_0)(%ebp), %edi | |
87 | movl PTR(VA_CONTROL_PAGE)(%ebp), %eax | |
88 | andl $0x001ff000, %eax | |
89 | shrl $9, %eax | |
90 | addl %edi, %eax | |
91 | ||
92 | movl PTR(PA_CONTROL_PAGE)(%ebp), %edx | |
93 | orl $PAGE_ATTR, %edx | |
94 | movl %edx, (%eax) | |
95 | ||
96 | /* identity map the control page at its physical address */ | |
97 | ||
98 | movl PTR(VA_PGD)(%ebp), %edi | |
99 | movl PTR(PA_CONTROL_PAGE)(%ebp), %eax | |
100 | andl $0xc0000000, %eax | |
101 | shrl $27, %eax | |
102 | addl %edi, %eax | |
103 | ||
104 | movl PTR(PA_PMD_1)(%ebp), %edx | |
105 | orl $PAE_PGD_ATTR, %edx | |
106 | movl %edx, (%eax) | |
107 | ||
108 | movl PTR(VA_PMD_1)(%ebp), %edi | |
109 | movl PTR(PA_CONTROL_PAGE)(%ebp), %eax | |
110 | andl $0x3fe00000, %eax | |
111 | shrl $18, %eax | |
112 | addl %edi, %eax | |
113 | ||
114 | movl PTR(PA_PTE_1)(%ebp), %edx | |
115 | orl $PAGE_ATTR, %edx | |
116 | movl %edx, (%eax) | |
117 | ||
118 | movl PTR(VA_PTE_1)(%ebp), %edi | |
119 | movl PTR(PA_CONTROL_PAGE)(%ebp), %eax | |
120 | andl $0x001ff000, %eax | |
121 | shrl $9, %eax | |
122 | addl %edi, %eax | |
123 | ||
124 | movl PTR(PA_CONTROL_PAGE)(%ebp), %edx | |
125 | orl $PAGE_ATTR, %edx | |
126 | movl %edx, (%eax) | |
127 | #else | |
128 | /* map the control page at its virtual address */ | |
129 | ||
130 | movl PTR(VA_PGD)(%ebp), %edi | |
131 | movl PTR(VA_CONTROL_PAGE)(%ebp), %eax | |
132 | andl $0xffc00000, %eax | |
133 | shrl $20, %eax | |
134 | addl %edi, %eax | |
135 | ||
136 | movl PTR(PA_PTE_0)(%ebp), %edx | |
137 | orl $PAGE_ATTR, %edx | |
138 | movl %edx, (%eax) | |
139 | ||
140 | movl PTR(VA_PTE_0)(%ebp), %edi | |
141 | movl PTR(VA_CONTROL_PAGE)(%ebp), %eax | |
142 | andl $0x003ff000, %eax | |
143 | shrl $10, %eax | |
144 | addl %edi, %eax | |
145 | ||
146 | movl PTR(PA_CONTROL_PAGE)(%ebp), %edx | |
147 | orl $PAGE_ATTR, %edx | |
148 | movl %edx, (%eax) | |
149 | ||
150 | /* identity map the control page at its physical address */ | |
151 | ||
152 | movl PTR(VA_PGD)(%ebp), %edi | |
153 | movl PTR(PA_CONTROL_PAGE)(%ebp), %eax | |
154 | andl $0xffc00000, %eax | |
155 | shrl $20, %eax | |
156 | addl %edi, %eax | |
157 | ||
158 | movl PTR(PA_PTE_1)(%ebp), %edx | |
159 | orl $PAGE_ATTR, %edx | |
160 | movl %edx, (%eax) | |
161 | ||
162 | movl PTR(VA_PTE_1)(%ebp), %edi | |
163 | movl PTR(PA_CONTROL_PAGE)(%ebp), %eax | |
164 | andl $0x003ff000, %eax | |
165 | shrl $10, %eax | |
166 | addl %edi, %eax | |
167 | ||
168 | movl PTR(PA_CONTROL_PAGE)(%ebp), %edx | |
169 | orl $PAGE_ATTR, %edx | |
170 | movl %edx, (%eax) | |
171 | #endif | |
5033cba0 | 172 | |
5033cba0 EB |
173 | relocate_new_kernel: |
174 | /* read the arguments and say goodbye to the stack */ | |
3ab83521 HY |
175 | movl 20+4(%esp), %ebx /* page_list */ |
176 | movl 20+8(%esp), %ebp /* list of pages */ | |
177 | movl 20+12(%esp), %edx /* start address */ | |
178 | movl 20+16(%esp), %ecx /* cpu_has_pae */ | |
179 | movl 20+20(%esp), %esi /* preserve_context */ | |
5033cba0 EB |
180 | |
181 | /* zero out flags, and disable interrupts */ | |
182 | pushl $0 | |
183 | popfl | |
184 | ||
3ab83521 HY |
185 | /* save some information for jumping back */ |
186 | movl PTR(VA_CONTROL_PAGE)(%ebp), %edi | |
187 | movl %edi, CP_VA_CONTROL_PAGE(%edi) | |
188 | movl PTR(PA_PGD)(%ebp), %eax | |
189 | movl %eax, CP_PA_PGD(%edi) | |
190 | movl PTR(PA_SWAP_PAGE)(%ebp), %eax | |
191 | movl %eax, CP_PA_SWAP_PAGE(%edi) | |
192 | movl %ebx, CP_PA_BACKUP_PAGES_MAP(%edi) | |
193 | ||
3566561b MD |
194 | /* get physical address of control page now */ |
195 | /* this is impossible after page table switch */ | |
196 | movl PTR(PA_CONTROL_PAGE)(%ebp), %edi | |
5033cba0 | 197 | |
3566561b MD |
198 | /* switch to new set of page tables */ |
199 | movl PTR(PA_PGD)(%ebp), %eax | |
200 | movl %eax, %cr3 | |
201 | ||
202 | /* setup a new stack at the end of the physical control page */ | |
a7bba17b | 203 | lea PAGE_SIZE(%edi), %esp |
3566561b MD |
204 | |
205 | /* jump to identity mapped page */ | |
206 | movl %edi, %eax | |
207 | addl $(identity_mapped - relocate_kernel), %eax | |
208 | pushl %eax | |
209 | ret | |
210 | ||
211 | identity_mapped: | |
212 | /* store the start address on the stack */ | |
213 | pushl %edx | |
5033cba0 EB |
214 | |
215 | /* Set cr0 to a known state: | |
fd3af531 | 216 | * - Paging disabled |
217 | * - Alignment check disabled | |
218 | * - Write protect disabled | |
219 | * - No task switch | |
220 | * - Don't do FP software emulation. | |
221 | * - Proctected mode enabled | |
5033cba0 EB |
222 | */ |
223 | movl %cr0, %eax | |
fd3af531 | 224 | andl $~(X86_CR0_PG | X86_CR0_AM | X86_CR0_WP | X86_CR0_TS | X86_CR0_EM), %eax |
225 | orl $(X86_CR0_PE), %eax | |
5033cba0 EB |
226 | movl %eax, %cr0 |
227 | ||
228 | /* clear cr4 if applicable */ | |
229 | testl %ecx, %ecx | |
230 | jz 1f | |
231 | /* Set cr4 to a known state: | |
232 | * Setting everything to zero seems safe. | |
233 | */ | |
4039ae53 | 234 | xorl %eax, %eax |
5033cba0 EB |
235 | movl %eax, %cr4 |
236 | ||
237 | jmp 1f | |
238 | 1: | |
239 | ||
240 | /* Flush the TLB (needed?) */ | |
241 | xorl %eax, %eax | |
242 | movl %eax, %cr3 | |
243 | ||
3ab83521 HY |
244 | movl CP_PA_SWAP_PAGE(%edi), %eax |
245 | pushl %eax | |
246 | pushl %ebx | |
247 | call swap_pages | |
248 | addl $8, %esp | |
249 | ||
250 | /* To be certain of avoiding problems with self-modifying code | |
251 | * I need to execute a serializing instruction here. | |
252 | * So I flush the TLB, it's handy, and not processor dependent. | |
253 | */ | |
254 | xorl %eax, %eax | |
255 | movl %eax, %cr3 | |
256 | ||
257 | /* set all of the registers to known values */ | |
258 | /* leave %esp alone */ | |
259 | ||
260 | testl %esi, %esi | |
261 | jnz 1f | |
262 | xorl %edi, %edi | |
263 | xorl %eax, %eax | |
264 | xorl %ebx, %ebx | |
265 | xorl %ecx, %ecx | |
266 | xorl %edx, %edx | |
267 | xorl %esi, %esi | |
268 | xorl %ebp, %ebp | |
269 | ret | |
270 | 1: | |
271 | popl %edx | |
272 | movl CP_PA_SWAP_PAGE(%edi), %esp | |
273 | addl $PAGE_SIZE, %esp | |
274 | 2: | |
275 | call *%edx | |
276 | ||
277 | /* get the re-entry point of the peer system */ | |
278 | movl 0(%esp), %ebp | |
279 | call 1f | |
280 | 1: | |
281 | popl %ebx | |
282 | subl $(1b - relocate_kernel), %ebx | |
283 | movl CP_VA_CONTROL_PAGE(%ebx), %edi | |
284 | lea PAGE_SIZE(%ebx), %esp | |
285 | movl CP_PA_SWAP_PAGE(%ebx), %eax | |
286 | movl CP_PA_BACKUP_PAGES_MAP(%ebx), %edx | |
287 | pushl %eax | |
288 | pushl %edx | |
289 | call swap_pages | |
290 | addl $8, %esp | |
291 | movl CP_PA_PGD(%ebx), %eax | |
292 | movl %eax, %cr3 | |
293 | movl %cr0, %eax | |
294 | orl $(1<<31), %eax | |
295 | movl %eax, %cr0 | |
296 | lea PAGE_SIZE(%edi), %esp | |
297 | movl %edi, %eax | |
298 | addl $(virtual_mapped - relocate_kernel), %eax | |
299 | pushl %eax | |
300 | ret | |
301 | ||
302 | virtual_mapped: | |
303 | movl CR4(%edi), %eax | |
304 | movl %eax, %cr4 | |
305 | movl CR3(%edi), %eax | |
306 | movl %eax, %cr3 | |
307 | movl CR0(%edi), %eax | |
308 | movl %eax, %cr0 | |
309 | movl ESP(%edi), %esp | |
310 | movl %ebp, %eax | |
311 | ||
312 | popf | |
313 | popl %ebp | |
314 | popl %edi | |
315 | popl %esi | |
316 | popl %ebx | |
317 | ret | |
318 | ||
5033cba0 | 319 | /* Do the copies */ |
3ab83521 HY |
320 | swap_pages: |
321 | movl 8(%esp), %edx | |
322 | movl 4(%esp), %ecx | |
323 | pushl %ebp | |
324 | pushl %ebx | |
325 | pushl %edi | |
326 | pushl %esi | |
327 | movl %ecx, %ebx | |
5033cba0 EB |
328 | jmp 1f |
329 | ||
330 | 0: /* top, read another word from the indirection page */ | |
331 | movl (%ebx), %ecx | |
332 | addl $4, %ebx | |
333 | 1: | |
334 | testl $0x1, %ecx /* is it a destination page */ | |
335 | jz 2f | |
336 | movl %ecx, %edi | |
337 | andl $0xfffff000, %edi | |
338 | jmp 0b | |
339 | 2: | |
340 | testl $0x2, %ecx /* is it an indirection page */ | |
341 | jz 2f | |
342 | movl %ecx, %ebx | |
343 | andl $0xfffff000, %ebx | |
344 | jmp 0b | |
345 | 2: | |
346 | testl $0x4, %ecx /* is it the done indicator */ | |
347 | jz 2f | |
348 | jmp 3f | |
349 | 2: | |
350 | testl $0x8, %ecx /* is it the source indicator */ | |
351 | jz 0b /* Ignore it otherwise */ | |
352 | movl %ecx, %esi /* For every source page do a copy */ | |
353 | andl $0xfffff000, %esi | |
354 | ||
3ab83521 HY |
355 | movl %edi, %eax |
356 | movl %esi, %ebp | |
357 | ||
358 | movl %edx, %edi | |
5033cba0 EB |
359 | movl $1024, %ecx |
360 | rep ; movsl | |
5033cba0 | 361 | |
3ab83521 HY |
362 | movl %ebp, %edi |
363 | movl %eax, %esi | |
364 | movl $1024, %ecx | |
365 | rep ; movsl | |
5033cba0 | 366 | |
3ab83521 HY |
367 | movl %eax, %edi |
368 | movl %edx, %esi | |
369 | movl $1024, %ecx | |
370 | rep ; movsl | |
5033cba0 | 371 | |
3ab83521 HY |
372 | lea PAGE_SIZE(%ebp), %esi |
373 | jmp 0b | |
374 | 3: | |
375 | popl %esi | |
376 | popl %edi | |
377 | popl %ebx | |
378 | popl %ebp | |
5033cba0 | 379 | ret |
fb45daa6 HY |
380 | |
381 | .globl kexec_control_code_size | |
382 | .set kexec_control_code_size, . - relocate_kernel |