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> | |
0341c14d | 10 | #include <asm/page_types.h> |
3566561b | 11 | #include <asm/kexec.h> |
fd3af531 | 12 | #include <asm/processor-flags.h> |
3566561b MD |
13 | |
14 | /* | |
15 | * Must be relocatable PIC code callable as a C function | |
16 | */ | |
17 | ||
18 | #define PTR(x) (x << 2) | |
3566561b | 19 | |
fef3a7a1 HY |
20 | /* |
21 | * control_page + KEXEC_CONTROL_CODE_MAX_SIZE | |
fb45daa6 HY |
22 | * ~ control_page + PAGE_SIZE are used as data storage and stack for |
23 | * jumping back | |
3ab83521 | 24 | */ |
fb45daa6 | 25 | #define DATA(offset) (KEXEC_CONTROL_CODE_MAX_SIZE+(offset)) |
3ab83521 HY |
26 | |
27 | /* Minimal CPU state */ | |
28 | #define ESP DATA(0x0) | |
29 | #define CR0 DATA(0x4) | |
30 | #define CR3 DATA(0x8) | |
31 | #define CR4 DATA(0xc) | |
32 | ||
33 | /* other data */ | |
34 | #define CP_VA_CONTROL_PAGE DATA(0x10) | |
35 | #define CP_PA_PGD DATA(0x14) | |
36 | #define CP_PA_SWAP_PAGE DATA(0x18) | |
37 | #define CP_PA_BACKUP_PAGES_MAP DATA(0x1c) | |
38 | ||
3566561b | 39 | .text |
3566561b MD |
40 | .globl relocate_kernel |
41 | relocate_kernel: | |
3ab83521 HY |
42 | /* Save the CPU context, used for jumping back */ |
43 | ||
44 | pushl %ebx | |
45 | pushl %esi | |
46 | pushl %edi | |
47 | pushl %ebp | |
48 | pushf | |
49 | ||
50 | movl 20+8(%esp), %ebp /* list of pages */ | |
51 | movl PTR(VA_CONTROL_PAGE)(%ebp), %edi | |
52 | movl %esp, ESP(%edi) | |
53 | movl %cr0, %eax | |
54 | movl %eax, CR0(%edi) | |
55 | movl %cr3, %eax | |
56 | movl %eax, CR3(%edi) | |
57 | movl %cr4, %eax | |
58 | movl %eax, CR4(%edi) | |
3566561b | 59 | |
5033cba0 | 60 | /* read the arguments and say goodbye to the stack */ |
3ab83521 HY |
61 | movl 20+4(%esp), %ebx /* page_list */ |
62 | movl 20+8(%esp), %ebp /* list of pages */ | |
63 | movl 20+12(%esp), %edx /* start address */ | |
64 | movl 20+16(%esp), %ecx /* cpu_has_pae */ | |
65 | movl 20+20(%esp), %esi /* preserve_context */ | |
5033cba0 EB |
66 | |
67 | /* zero out flags, and disable interrupts */ | |
68 | pushl $0 | |
69 | popfl | |
70 | ||
3ab83521 HY |
71 | /* save some information for jumping back */ |
72 | movl PTR(VA_CONTROL_PAGE)(%ebp), %edi | |
73 | movl %edi, CP_VA_CONTROL_PAGE(%edi) | |
74 | movl PTR(PA_PGD)(%ebp), %eax | |
75 | movl %eax, CP_PA_PGD(%edi) | |
76 | movl PTR(PA_SWAP_PAGE)(%ebp), %eax | |
77 | movl %eax, CP_PA_SWAP_PAGE(%edi) | |
78 | movl %ebx, CP_PA_BACKUP_PAGES_MAP(%edi) | |
79 | ||
fef3a7a1 HY |
80 | /* |
81 | * get physical address of control page now | |
82 | * this is impossible after page table switch | |
83 | */ | |
3566561b | 84 | movl PTR(PA_CONTROL_PAGE)(%ebp), %edi |
5033cba0 | 85 | |
3566561b MD |
86 | /* switch to new set of page tables */ |
87 | movl PTR(PA_PGD)(%ebp), %eax | |
88 | movl %eax, %cr3 | |
89 | ||
90 | /* setup a new stack at the end of the physical control page */ | |
a7bba17b | 91 | lea PAGE_SIZE(%edi), %esp |
3566561b MD |
92 | |
93 | /* jump to identity mapped page */ | |
94 | movl %edi, %eax | |
95 | addl $(identity_mapped - relocate_kernel), %eax | |
96 | pushl %eax | |
97 | ret | |
98 | ||
99 | identity_mapped: | |
100 | /* store the start address on the stack */ | |
101 | pushl %edx | |
5033cba0 | 102 | |
fef3a7a1 HY |
103 | /* |
104 | * Set cr0 to a known state: | |
fd3af531 | 105 | * - Paging disabled |
106 | * - Alignment check disabled | |
107 | * - Write protect disabled | |
108 | * - No task switch | |
109 | * - Don't do FP software emulation. | |
110 | * - Proctected mode enabled | |
5033cba0 EB |
111 | */ |
112 | movl %cr0, %eax | |
fd3af531 | 113 | andl $~(X86_CR0_PG | X86_CR0_AM | X86_CR0_WP | X86_CR0_TS | X86_CR0_EM), %eax |
114 | orl $(X86_CR0_PE), %eax | |
5033cba0 EB |
115 | movl %eax, %cr0 |
116 | ||
117 | /* clear cr4 if applicable */ | |
118 | testl %ecx, %ecx | |
119 | jz 1f | |
fef3a7a1 HY |
120 | /* |
121 | * Set cr4 to a known state: | |
5033cba0 EB |
122 | * Setting everything to zero seems safe. |
123 | */ | |
4039ae53 | 124 | xorl %eax, %eax |
5033cba0 EB |
125 | movl %eax, %cr4 |
126 | ||
127 | jmp 1f | |
128 | 1: | |
129 | ||
130 | /* Flush the TLB (needed?) */ | |
131 | xorl %eax, %eax | |
132 | movl %eax, %cr3 | |
133 | ||
3ab83521 HY |
134 | movl CP_PA_SWAP_PAGE(%edi), %eax |
135 | pushl %eax | |
136 | pushl %ebx | |
137 | call swap_pages | |
138 | addl $8, %esp | |
139 | ||
fef3a7a1 HY |
140 | /* |
141 | * To be certain of avoiding problems with self-modifying code | |
3ab83521 HY |
142 | * I need to execute a serializing instruction here. |
143 | * So I flush the TLB, it's handy, and not processor dependent. | |
144 | */ | |
145 | xorl %eax, %eax | |
146 | movl %eax, %cr3 | |
147 | ||
fef3a7a1 HY |
148 | /* |
149 | * set all of the registers to known values | |
150 | * leave %esp alone | |
151 | */ | |
3ab83521 HY |
152 | |
153 | testl %esi, %esi | |
154 | jnz 1f | |
155 | xorl %edi, %edi | |
156 | xorl %eax, %eax | |
157 | xorl %ebx, %ebx | |
158 | xorl %ecx, %ecx | |
159 | xorl %edx, %edx | |
160 | xorl %esi, %esi | |
161 | xorl %ebp, %ebp | |
162 | ret | |
163 | 1: | |
164 | popl %edx | |
165 | movl CP_PA_SWAP_PAGE(%edi), %esp | |
166 | addl $PAGE_SIZE, %esp | |
167 | 2: | |
168 | call *%edx | |
169 | ||
170 | /* get the re-entry point of the peer system */ | |
171 | movl 0(%esp), %ebp | |
172 | call 1f | |
173 | 1: | |
174 | popl %ebx | |
175 | subl $(1b - relocate_kernel), %ebx | |
176 | movl CP_VA_CONTROL_PAGE(%ebx), %edi | |
177 | lea PAGE_SIZE(%ebx), %esp | |
178 | movl CP_PA_SWAP_PAGE(%ebx), %eax | |
179 | movl CP_PA_BACKUP_PAGES_MAP(%ebx), %edx | |
180 | pushl %eax | |
181 | pushl %edx | |
182 | call swap_pages | |
183 | addl $8, %esp | |
184 | movl CP_PA_PGD(%ebx), %eax | |
185 | movl %eax, %cr3 | |
186 | movl %cr0, %eax | |
187 | orl $(1<<31), %eax | |
188 | movl %eax, %cr0 | |
189 | lea PAGE_SIZE(%edi), %esp | |
190 | movl %edi, %eax | |
191 | addl $(virtual_mapped - relocate_kernel), %eax | |
192 | pushl %eax | |
193 | ret | |
194 | ||
195 | virtual_mapped: | |
196 | movl CR4(%edi), %eax | |
197 | movl %eax, %cr4 | |
198 | movl CR3(%edi), %eax | |
199 | movl %eax, %cr3 | |
200 | movl CR0(%edi), %eax | |
201 | movl %eax, %cr0 | |
202 | movl ESP(%edi), %esp | |
203 | movl %ebp, %eax | |
204 | ||
205 | popf | |
206 | popl %ebp | |
207 | popl %edi | |
208 | popl %esi | |
209 | popl %ebx | |
210 | ret | |
211 | ||
5033cba0 | 212 | /* Do the copies */ |
3ab83521 HY |
213 | swap_pages: |
214 | movl 8(%esp), %edx | |
215 | movl 4(%esp), %ecx | |
216 | pushl %ebp | |
217 | pushl %ebx | |
218 | pushl %edi | |
219 | pushl %esi | |
220 | movl %ecx, %ebx | |
5033cba0 EB |
221 | jmp 1f |
222 | ||
223 | 0: /* top, read another word from the indirection page */ | |
224 | movl (%ebx), %ecx | |
225 | addl $4, %ebx | |
226 | 1: | |
227 | testl $0x1, %ecx /* is it a destination page */ | |
228 | jz 2f | |
229 | movl %ecx, %edi | |
230 | andl $0xfffff000, %edi | |
231 | jmp 0b | |
232 | 2: | |
233 | testl $0x2, %ecx /* is it an indirection page */ | |
234 | jz 2f | |
235 | movl %ecx, %ebx | |
236 | andl $0xfffff000, %ebx | |
237 | jmp 0b | |
238 | 2: | |
239 | testl $0x4, %ecx /* is it the done indicator */ | |
240 | jz 2f | |
241 | jmp 3f | |
242 | 2: | |
243 | testl $0x8, %ecx /* is it the source indicator */ | |
244 | jz 0b /* Ignore it otherwise */ | |
245 | movl %ecx, %esi /* For every source page do a copy */ | |
246 | andl $0xfffff000, %esi | |
247 | ||
3ab83521 HY |
248 | movl %edi, %eax |
249 | movl %esi, %ebp | |
250 | ||
251 | movl %edx, %edi | |
5033cba0 EB |
252 | movl $1024, %ecx |
253 | rep ; movsl | |
5033cba0 | 254 | |
3ab83521 HY |
255 | movl %ebp, %edi |
256 | movl %eax, %esi | |
257 | movl $1024, %ecx | |
258 | rep ; movsl | |
5033cba0 | 259 | |
3ab83521 HY |
260 | movl %eax, %edi |
261 | movl %edx, %esi | |
262 | movl $1024, %ecx | |
263 | rep ; movsl | |
5033cba0 | 264 | |
3ab83521 HY |
265 | lea PAGE_SIZE(%ebp), %esi |
266 | jmp 0b | |
267 | 3: | |
268 | popl %esi | |
269 | popl %edi | |
270 | popl %ebx | |
271 | popl %ebp | |
5033cba0 | 272 | ret |
fb45daa6 HY |
273 | |
274 | .globl kexec_control_code_size | |
275 | .set kexec_control_code_size, . - relocate_kernel |