Merge tag 'keys-namespace-20190627' of git://git.kernel.org/pub/scm/linux/kernel...
[linux-block.git] / arch / x86 / platform / efi / efi_stub_32.S
CommitLineData
b2441318 1/* SPDX-License-Identifier: GPL-2.0 */
1da177e4
LT
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
1da177e4 9#include <linux/linkage.h>
0341c14d 10#include <asm/page_types.h>
1da177e4
LT
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
25ENTRY(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
4e78eb05 31 * and GDT, but change GDT base register in prolog and epilog.
1da177e4
LT
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.
4e78eb05 37 * The mapping of lower virtual memory has been created in prolog and
1da177e4
LT
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
9f331119 117ENDPROC(efi_call_phys)
1da177e4
LT
118.previous
119
120.data
121saved_return_addr:
122 .long 0
123efi_rt_function_ptr:
124 .long 0