xref: /linux/arch/x86/platform/efi/efi_stub_32.S (revision ca55b2fef3a9373fcfc30f82fd26bc7fccbda732)
1/*
2 * EFI call stub for IA32.
3 *
4 * This stub allows us to make EFI calls in physical mode with interrupts
5 * turned off.
6 */
7
8#include <linux/linkage.h>
9#include <asm/page_types.h>
10
11/*
12 * efi_call_phys(void *, ...) is a function with variable parameters.
13 * All the callers of this function assure that all the parameters are 4-bytes.
14 */
15
16/*
17 * In gcc calling convention, EBX, ESP, EBP, ESI and EDI are all callee save.
18 * So we'd better save all of them at the beginning of this function and restore
19 * at the end no matter how many we use, because we can not assure EFI runtime
20 * service functions will comply with gcc calling convention, too.
21 */
22
23.text
24ENTRY(efi_call_phys)
25	/*
26	 * 0. The function can only be called in Linux kernel. So CS has been
27	 * set to 0x0010, DS and SS have been set to 0x0018. In EFI, I found
28	 * the values of these registers are the same. And, the corresponding
29	 * GDT entries are identical. So I will do nothing about segment reg
30	 * and GDT, but change GDT base register in prolog and epilog.
31	 */
32
33	/*
34	 * 1. Now I am running with EIP = <physical address> + PAGE_OFFSET.
35	 * But to make it smoothly switch from virtual mode to flat mode.
36	 * The mapping of lower virtual memory has been created in prolog and
37	 * epilog.
38	 */
39	movl	$1f, %edx
40	subl	$__PAGE_OFFSET, %edx
41	jmp	*%edx
421:
43
44	/*
45	 * 2. Now on the top of stack is the return
46	 * address in the caller of efi_call_phys(), then parameter 1,
47	 * parameter 2, ..., param n. To make things easy, we save the return
48	 * address of efi_call_phys in a global variable.
49	 */
50	popl	%edx
51	movl	%edx, saved_return_addr
52	/* get the function pointer into ECX*/
53	popl	%ecx
54	movl	%ecx, efi_rt_function_ptr
55	movl	$2f, %edx
56	subl	$__PAGE_OFFSET, %edx
57	pushl	%edx
58
59	/*
60	 * 3. Clear PG bit in %CR0.
61	 */
62	movl	%cr0, %edx
63	andl	$0x7fffffff, %edx
64	movl	%edx, %cr0
65	jmp	1f
661:
67
68	/*
69	 * 4. Adjust stack pointer.
70	 */
71	subl	$__PAGE_OFFSET, %esp
72
73	/*
74	 * 5. Call the physical function.
75	 */
76	jmp	*%ecx
77
782:
79	/*
80	 * 6. After EFI runtime service returns, control will return to
81	 * following instruction. We'd better readjust stack pointer first.
82	 */
83	addl	$__PAGE_OFFSET, %esp
84
85	/*
86	 * 7. Restore PG bit
87	 */
88	movl	%cr0, %edx
89	orl	$0x80000000, %edx
90	movl	%edx, %cr0
91	jmp	1f
921:
93	/*
94	 * 8. Now restore the virtual mode from flat mode by
95	 * adding EIP with PAGE_OFFSET.
96	 */
97	movl	$1f, %edx
98	jmp	*%edx
991:
100
101	/*
102	 * 9. Balance the stack. And because EAX contain the return value,
103	 * we'd better not clobber it.
104	 */
105	leal	efi_rt_function_ptr, %edx
106	movl	(%edx), %ecx
107	pushl	%ecx
108
109	/*
110	 * 10. Push the saved return address onto the stack and return.
111	 */
112	leal	saved_return_addr, %edx
113	movl	(%edx), %ecx
114	pushl	%ecx
115	ret
116ENDPROC(efi_call_phys)
117.previous
118
119.data
120saved_return_addr:
121	.long 0
122efi_rt_function_ptr:
123	.long 0
124