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 */ 8 9#include <linux/linkage.h> 10#include <asm/page_types.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 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 31 * and GDT, but change GDT base register in prolog 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 prolog and 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 117ENDPROC(efi_call_phys) 118.previous 119 120.data 121saved_return_addr: 122 .long 0 123efi_rt_function_ptr: 124 .long 0 125