xref: /openbmc/linux/arch/x86/kernel/relocate_kernel_32.S (revision f15cbe6f1a4b4d9df59142fc8e4abb973302cf44)
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>
10#include <asm/page.h>
11#include <asm/kexec.h>
12#include <asm/processor-flags.h>
13#include <asm/pgtable.h>
14
15/*
16 * Must be relocatable PIC code callable as a C function
17 */
18
19#define PTR(x) (x << 2)
20#define PAGE_ATTR (_PAGE_PRESENT | _PAGE_RW | _PAGE_ACCESSED | _PAGE_DIRTY)
21#define PAE_PGD_ATTR (_PAGE_PRESENT)
22
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
40	.text
41	.align PAGE_SIZE
42	.globl relocate_kernel
43relocate_kernel:
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)
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
171
172relocate_new_kernel:
173	/* read the arguments and say goodbye to the stack */
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 */
179
180	/* zero out flags, and disable interrupts */
181	pushl $0
182	popfl
183
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
193	/* get physical address of control page now */
194	/* this is impossible after page table switch */
195	movl	PTR(PA_CONTROL_PAGE)(%ebp), %edi
196
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 */
202	lea	PAGE_SIZE(%edi), %esp
203
204	/* jump to identity mapped page */
205	movl    %edi, %eax
206	addl    $(identity_mapped - relocate_kernel), %eax
207	pushl   %eax
208	ret
209
210identity_mapped:
211	/* store the start address on the stack */
212	pushl   %edx
213
214	/* Set cr0 to a known state:
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
221	 */
222	movl	%cr0, %eax
223	andl	$~(X86_CR0_PG | X86_CR0_AM | X86_CR0_WP | X86_CR0_TS | X86_CR0_EM), %eax
224	orl	$(X86_CR0_PE), %eax
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	 */
233	xorl	%eax, %eax
234	movl	%eax, %cr4
235
236	jmp 1f
2371:
238
239	/* Flush the TLB (needed?) */
240	xorl	%eax, %eax
241	movl	%eax, %cr3
242
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
2691:
270	popl	%edx
271	movl	CP_PA_SWAP_PAGE(%edi), %esp
272	addl	$PAGE_SIZE, %esp
2732:
274	call	*%edx
275
276	/* get the re-entry point of the peer system */
277	movl	0(%esp), %ebp
278	call	1f
2791:
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
301virtual_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
318	/* Do the copies */
319swap_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
327	jmp	1f
328
3290:	/* top, read another word from the indirection page */
330	movl	(%ebx), %ecx
331	addl	$4, %ebx
3321:
333	testl	$0x1,   %ecx  /* is it a destination page */
334	jz	2f
335	movl	%ecx,	%edi
336	andl	$0xfffff000, %edi
337	jmp     0b
3382:
339	testl	$0x2,	%ecx  /* is it an indirection page */
340	jz	2f
341	movl	%ecx,	%ebx
342	andl	$0xfffff000, %ebx
343	jmp     0b
3442:
345	testl   $0x4,   %ecx /* is it the done indicator */
346	jz      2f
347	jmp     3f
3482:
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
354	movl	%edi, %eax
355	movl	%esi, %ebp
356
357	movl	%edx, %edi
358	movl    $1024, %ecx
359	rep ; movsl
360
361	movl	%ebp, %edi
362	movl	%eax, %esi
363	movl	$1024, %ecx
364	rep ; movsl
365
366	movl	%eax, %edi
367	movl	%edx, %esi
368	movl	$1024, %ecx
369	rep ; movsl
370
371	lea	PAGE_SIZE(%ebp), %esi
372	jmp     0b
3733:
374	popl	%esi
375	popl	%edi
376	popl	%ebx
377	popl	%ebp
378	ret
379