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