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