Linux 4.18.10
[linux/fpc-iii.git] / arch / x86 / platform / efi / efi_stub_32.S
blobab2e91e7689425f40eeb46a2b0cd49d34c4bbf20
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  */
9 #include <linux/linkage.h>
10 #include <asm/page_types.h>
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  */
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  */
24 .text
25 ENTRY(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          */
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
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
60         /*
61          * 3. Clear PG bit in %CR0.
62          */
63         movl    %cr0, %edx
64         andl    $0x7fffffff, %edx
65         movl    %edx, %cr0
66         jmp     1f
69         /*
70          * 4. Adjust stack pointer.
71          */
72         subl    $__PAGE_OFFSET, %esp
74         /*
75          * 5. Call the physical function.
76          */
77         jmp     *%ecx
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
86         /*
87          * 7. Restore PG bit
88          */
89         movl    %cr0, %edx
90         orl     $0x80000000, %edx
91         movl    %edx, %cr0
92         jmp     1f
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
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
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
117 ENDPROC(efi_call_phys)
118 .previous
120 .data
121 saved_return_addr:
122         .long 0
123 efi_rt_function_ptr:
124         .long 0