Pull kmalloc into release branch
[pandora-kernel.git] / arch / i386 / kernel / efi_stub.S
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.h>
10 #include <asm/pgtable.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
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 prelog 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 prelog and
38          * epilog.
39          */
40         movl    $1f, %edx
41         subl    $__PAGE_OFFSET, %edx
42         jmp     *%edx
43 1:
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
67 1:
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
79 2:
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
93 1:
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
100 1:
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
117 .previous
118
119 .data
120 saved_return_addr:
121         .long 0
122 efi_rt_function_ptr:
123         .long 0