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 | |
3ab83521 HY |
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 | ||
3566561b | 40 | .text |
288621e3 | 41 | .align PAGE_SIZE |
3566561b MD |
42 | .globl relocate_kernel |
43 | relocate_kernel: | |
3ab83521 HY |
44 | /* Save the CPU context, used for jumping back */ |
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) | |
3566561b MD |
61 | |
62 | #ifdef CONFIG_X86_PAE | |
63 | /* map the control page at its virtual address */ | |
64 | ||
65 | movl PTR(VA_PGD)(%ebp), %edi | |
66 | movl PTR(VA_CONTROL_PAGE)(%ebp), %eax | |
67 | andl $0xc0000000, %eax | |
68 | shrl $27, %eax | |
69 | addl %edi, %eax | |
70 | ||
71 | movl PTR(PA_PMD_0)(%ebp), %edx | |
72 | orl $PAE_PGD_ATTR, %edx | |
73 | movl %edx, (%eax) | |
74 | ||
75 | movl PTR(VA_PMD_0)(%ebp), %edi | |
76 | movl PTR(VA_CONTROL_PAGE)(%ebp), %eax | |
77 | andl $0x3fe00000, %eax | |
78 | shrl $18, %eax | |
79 | addl %edi, %eax | |
80 | ||
81 | movl PTR(PA_PTE_0)(%ebp), %edx | |
82 | orl $PAGE_ATTR, %edx | |
83 | movl %edx, (%eax) | |
84 | ||
85 | movl PTR(VA_PTE_0)(%ebp), %edi | |
86 | movl PTR(VA_CONTROL_PAGE)(%ebp), %eax | |
87 | andl $0x001ff000, %eax | |
88 | shrl $9, %eax | |
89 | addl %edi, %eax | |
90 | ||
91 | movl PTR(PA_CONTROL_PAGE)(%ebp), %edx | |
92 | orl $PAGE_ATTR, %edx | |
93 | movl %edx, (%eax) | |
94 | ||
95 | /* identity map the control page at its physical address */ | |
96 | ||
97 | movl PTR(VA_PGD)(%ebp), %edi | |
98 | movl PTR(PA_CONTROL_PAGE)(%ebp), %eax | |
99 | andl $0xc0000000, %eax | |
100 | shrl $27, %eax | |
101 | addl %edi, %eax | |
102 | ||
103 | movl PTR(PA_PMD_1)(%ebp), %edx | |
104 | orl $PAE_PGD_ATTR, %edx | |
105 | movl %edx, (%eax) | |
106 | ||
107 | movl PTR(VA_PMD_1)(%ebp), %edi | |
108 | movl PTR(PA_CONTROL_PAGE)(%ebp), %eax | |
109 | andl $0x3fe00000, %eax | |
110 | shrl $18, %eax | |
111 | addl %edi, %eax | |
112 | ||
113 | movl PTR(PA_PTE_1)(%ebp), %edx | |
114 | orl $PAGE_ATTR, %edx | |
115 | movl %edx, (%eax) | |
116 | ||
117 | movl PTR(VA_PTE_1)(%ebp), %edi | |
118 | movl PTR(PA_CONTROL_PAGE)(%ebp), %eax | |
119 | andl $0x001ff000, %eax | |
120 | shrl $9, %eax | |
121 | addl %edi, %eax | |
122 | ||
123 | movl PTR(PA_CONTROL_PAGE)(%ebp), %edx | |
124 | orl $PAGE_ATTR, %edx | |
125 | movl %edx, (%eax) | |
126 | #else | |
127 | /* map the control page at its virtual address */ | |
128 | ||
129 | movl PTR(VA_PGD)(%ebp), %edi | |
130 | movl PTR(VA_CONTROL_PAGE)(%ebp), %eax | |
131 | andl $0xffc00000, %eax | |
132 | shrl $20, %eax | |
133 | addl %edi, %eax | |
134 | ||
135 | movl PTR(PA_PTE_0)(%ebp), %edx | |
136 | orl $PAGE_ATTR, %edx | |
137 | movl %edx, (%eax) | |
138 | ||
139 | movl PTR(VA_PTE_0)(%ebp), %edi | |
140 | movl PTR(VA_CONTROL_PAGE)(%ebp), %eax | |
141 | andl $0x003ff000, %eax | |
142 | shrl $10, %eax | |
143 | addl %edi, %eax | |
144 | ||
145 | movl PTR(PA_CONTROL_PAGE)(%ebp), %edx | |
146 | orl $PAGE_ATTR, %edx | |
147 | movl %edx, (%eax) | |
148 | ||
149 | /* identity map the control page at its physical address */ | |
150 | ||
151 | movl PTR(VA_PGD)(%ebp), %edi | |
152 | movl PTR(PA_CONTROL_PAGE)(%ebp), %eax | |
153 | andl $0xffc00000, %eax | |
154 | shrl $20, %eax | |
155 | addl %edi, %eax | |
156 | ||
157 | movl PTR(PA_PTE_1)(%ebp), %edx | |
158 | orl $PAGE_ATTR, %edx | |
159 | movl %edx, (%eax) | |
160 | ||
161 | movl PTR(VA_PTE_1)(%ebp), %edi | |
162 | movl PTR(PA_CONTROL_PAGE)(%ebp), %eax | |
163 | andl $0x003ff000, %eax | |
164 | shrl $10, %eax | |
165 | addl %edi, %eax | |
166 | ||
167 | movl PTR(PA_CONTROL_PAGE)(%ebp), %edx | |
168 | orl $PAGE_ATTR, %edx | |
169 | movl %edx, (%eax) | |
170 | #endif | |
5033cba0 | 171 | |
5033cba0 EB |
172 | relocate_new_kernel: |
173 | /* read the arguments and say goodbye to the stack */ | |
3ab83521 HY |
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 */ | |
5033cba0 EB |
179 | |
180 | /* zero out flags, and disable interrupts */ | |
181 | pushl $0 | |
182 | popfl | |
183 | ||
3ab83521 HY |
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 | ||
3566561b MD |
193 | /* get physical address of control page now */ |
194 | /* this is impossible after page table switch */ | |
195 | movl PTR(PA_CONTROL_PAGE)(%ebp), %edi | |
5033cba0 | 196 | |
3566561b MD |
197 | /* switch to new set of page tables */ |
198 | movl PTR(PA_PGD)(%ebp), %eax | |
199 | movl %eax, %cr3 | |
200 | ||
201 | /* setup a new stack at the end of the physical control page */ | |
a7bba17b | 202 | lea PAGE_SIZE(%edi), %esp |
3566561b MD |
203 | |
204 | /* jump to identity mapped page */ | |
205 | movl %edi, %eax | |
206 | addl $(identity_mapped - relocate_kernel), %eax | |
207 | pushl %eax | |
208 | ret | |
209 | ||
210 | identity_mapped: | |
211 | /* store the start address on the stack */ | |
212 | pushl %edx | |
5033cba0 EB |
213 | |
214 | /* Set cr0 to a known state: | |
fd3af531 | 215 | * - Paging disabled |
216 | * - Alignment check disabled | |
217 | * - Write protect disabled | |
218 | * - No task switch | |
219 | * - Don't do FP software emulation. | |
220 | * - Proctected mode enabled | |
5033cba0 EB |
221 | */ |
222 | movl %cr0, %eax | |
fd3af531 | 223 | andl $~(X86_CR0_PG | X86_CR0_AM | X86_CR0_WP | X86_CR0_TS | X86_CR0_EM), %eax |
224 | orl $(X86_CR0_PE), %eax | |
5033cba0 EB |
225 | movl %eax, %cr0 |
226 | ||
227 | /* clear cr4 if applicable */ | |
228 | testl %ecx, %ecx | |
229 | jz 1f | |
230 | /* Set cr4 to a known state: | |
231 | * Setting everything to zero seems safe. | |
232 | */ | |
4039ae53 | 233 | xorl %eax, %eax |
5033cba0 EB |
234 | movl %eax, %cr4 |
235 | ||
236 | jmp 1f | |
237 | 1: | |
238 | ||
239 | /* Flush the TLB (needed?) */ | |
240 | xorl %eax, %eax | |
241 | movl %eax, %cr3 | |
242 | ||
3ab83521 HY |
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 | ||
5033cba0 | 318 | /* Do the copies */ |
3ab83521 HY |
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 | |
5033cba0 EB |
327 | jmp 1f |
328 | ||
329 | 0: /* top, read another word from the indirection page */ | |
330 | movl (%ebx), %ecx | |
331 | addl $4, %ebx | |
332 | 1: | |
333 | testl $0x1, %ecx /* is it a destination page */ | |
334 | jz 2f | |
335 | movl %ecx, %edi | |
336 | andl $0xfffff000, %edi | |
337 | jmp 0b | |
338 | 2: | |
339 | testl $0x2, %ecx /* is it an indirection page */ | |
340 | jz 2f | |
341 | movl %ecx, %ebx | |
342 | andl $0xfffff000, %ebx | |
343 | jmp 0b | |
344 | 2: | |
345 | testl $0x4, %ecx /* is it the done indicator */ | |
346 | jz 2f | |
347 | jmp 3f | |
348 | 2: | |
349 | testl $0x8, %ecx /* is it the source indicator */ | |
350 | jz 0b /* Ignore it otherwise */ | |
351 | movl %ecx, %esi /* For every source page do a copy */ | |
352 | andl $0xfffff000, %esi | |
353 | ||
3ab83521 HY |
354 | movl %edi, %eax |
355 | movl %esi, %ebp | |
356 | ||
357 | movl %edx, %edi | |
5033cba0 EB |
358 | movl $1024, %ecx |
359 | rep ; movsl | |
5033cba0 | 360 | |
3ab83521 HY |
361 | movl %ebp, %edi |
362 | movl %eax, %esi | |
363 | movl $1024, %ecx | |
364 | rep ; movsl | |
5033cba0 | 365 | |
3ab83521 HY |
366 | movl %eax, %edi |
367 | movl %edx, %esi | |
368 | movl $1024, %ecx | |
369 | rep ; movsl | |
5033cba0 | 370 | |
3ab83521 HY |
371 | lea PAGE_SIZE(%ebp), %esi |
372 | jmp 0b | |
373 | 3: | |
374 | popl %esi | |
375 | popl %edi | |
376 | popl %ebx | |
377 | popl %ebp | |
5033cba0 | 378 | ret |