| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [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. | 
 | 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 | 
 | 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. 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 | 
 | 44 | 1: | 
 | 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 | 
 | 68 | 1: | 
 | 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 |  | 
 | 80 | 2: | 
 | 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 | 
 | 94 | 1: | 
 | 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 | 
 | 101 | 1: | 
 | 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 | 
 | 121 | saved_return_addr: | 
 | 122 | 	.long 0 | 
 | 123 | efi_rt_function_ptr: | 
 | 124 | 	.long 0 |