PageRenderTime 23ms CodeModel.GetById 13ms app.highlight 7ms RepoModel.GetById 1ms app.codeStats 1ms

/arch/i386/kernel/efi_stub.S

https://bitbucket.org/evzijst/gittest
Assembly | 124 lines | 119 code | 5 blank | 0 comment | 1 complexity | 58a4ca5b00f2271dd898041df5c56265 MD5 | raw file
  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
 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