Matt Fleming | 291f363 | 2011-12-12 21:27:52 +0000 | [diff] [blame] | 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. Note that this implementation is different from the one in |
| 6 | * arch/x86/platform/efi/efi_stub_32.S because we're _already_ in physical |
| 7 | * mode at this point. |
| 8 | */ |
| 9 | |
| 10 | #include <linux/linkage.h> |
| 11 | #include <asm/page_types.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 |
| 26 | ENTRY(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. Because we haven't been relocated by this point we need to |
| 37 | * use relative addressing. |
| 38 | */ |
| 39 | call 1f |
| 40 | 1: popl %edx |
| 41 | subl $1b, %edx |
| 42 | |
| 43 | /* |
| 44 | * 2. Now on the top of stack is the return |
| 45 | * address in the caller of efi_call_phys(), then parameter 1, |
| 46 | * parameter 2, ..., param n. To make things easy, we save the return |
| 47 | * address of efi_call_phys in a global variable. |
| 48 | */ |
| 49 | popl %ecx |
| 50 | movl %ecx, saved_return_addr(%edx) |
| 51 | /* get the function pointer into ECX*/ |
| 52 | popl %ecx |
| 53 | movl %ecx, efi_rt_function_ptr(%edx) |
| 54 | |
| 55 | /* |
| 56 | * 3. Call the physical function. |
| 57 | */ |
| 58 | call *%ecx |
| 59 | |
| 60 | /* |
| 61 | * 4. Balance the stack. And because EAX contain the return value, |
| 62 | * we'd better not clobber it. We need to calculate our address |
| 63 | * again because %ecx and %edx are not preserved across EFI function |
| 64 | * calls. |
| 65 | */ |
| 66 | call 1f |
| 67 | 1: popl %edx |
| 68 | subl $1b, %edx |
| 69 | |
| 70 | movl efi_rt_function_ptr(%edx), %ecx |
| 71 | pushl %ecx |
| 72 | |
| 73 | /* |
| 74 | * 10. Push the saved return address onto the stack and return. |
| 75 | */ |
| 76 | movl saved_return_addr(%edx), %ecx |
| 77 | pushl %ecx |
| 78 | ret |
| 79 | ENDPROC(efi_call_phys) |
| 80 | .previous |
| 81 | |
| 82 | .data |
| 83 | saved_return_addr: |
| 84 | .long 0 |
| 85 | efi_rt_function_ptr: |
| 86 | .long 0 |