linux/arch/i386/kernel/efi_stub.S
<<
>>
Prefs
   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
  11/*
  12 * efi_call_phys(void *, ...) is a function with variable parameters.
  13 * All the callers of this function assure that all the parameters are 4-bytes.
  14 */
  15
  16/*
  17 * In gcc calling convention, EBX, ESP, EBP, ESI and EDI are all callee save.
  18 * So we'd better save all of them at the beginning of this function and restore
  19 * at the end no matter how many we use, because we can not assure EFI runtime
  20 * service functions will comply with gcc calling convention, too.
  21 */
  22
  23.text
  24ENTRY(efi_call_phys)
  25        /*
  26         * 0. The function can only be called in Linux kernel. So CS has been
  27         * set to 0x0010, DS and SS have been set to 0x0018. In EFI, I found
  28         * the values of these registers are the same. And, the corresponding
  29         * GDT entries are identical. So I will do nothing about segment reg
  30         * and GDT, but change GDT base register in prelog and epilog.
  31         */
  32
  33        /*
  34         * 1. Now I am running with EIP = <physical address> + PAGE_OFFSET.
  35         * But to make it smoothly switch from virtual mode to flat mode.
  36         * The mapping of lower virtual memory has been created in prelog and
  37         * epilog.
  38         */
  39        movl    $1f, %edx
  40        subl    $__PAGE_OFFSET, %edx
  41        jmp     *%edx
  421:
  43
  44        /*
  45         * 2. Now on the top of stack is the return
  46         * address in the caller of efi_call_phys(), then parameter 1,
  47         * parameter 2, ..., param n. To make things easy, we save the return
  48         * address of efi_call_phys in a global variable.
  49         */
  50        popl    %edx
  51        movl    %edx, saved_return_addr
  52        /* get the function pointer into ECX*/
  53        popl    %ecx
  54        movl    %ecx, efi_rt_function_ptr
  55        movl    $2f, %edx
  56        subl    $__PAGE_OFFSET, %edx
  57        pushl   %edx
  58
  59        /*
  60         * 3. Clear PG bit in %CR0.
  61         */
  62        movl    %cr0, %edx
  63        andl    $0x7fffffff, %edx
  64        movl    %edx, %cr0
  65        jmp     1f
  661:
  67
  68        /*
  69         * 4. Adjust stack pointer.
  70         */
  71        subl    $__PAGE_OFFSET, %esp
  72
  73        /*
  74         * 5. Call the physical function.
  75         */
  76        jmp     *%ecx
  77
  782:
  79        /*
  80         * 6. After EFI runtime service returns, control will return to
  81         * following instruction. We'd better readjust stack pointer first.
  82         */
  83        addl    $__PAGE_OFFSET, %esp
  84
  85        /*
  86         * 7. Restore PG bit
  87         */
  88        movl    %cr0, %edx
  89        orl     $0x80000000, %edx
  90        movl    %edx, %cr0
  91        jmp     1f
  921:
  93        /*
  94         * 8. Now restore the virtual mode from flat mode by
  95         * adding EIP with PAGE_OFFSET.
  96         */
  97        movl    $1f, %edx
  98        jmp     *%edx
  991:
 100
 101        /*
 102         * 9. Balance the stack. And because EAX contain the return value,
 103         * we'd better not clobber it.
 104         */
 105        leal    efi_rt_function_ptr, %edx
 106        movl    (%edx), %ecx
 107        pushl   %ecx
 108
 109        /*
 110         * 10. Push the saved return address onto the stack and return.
 111         */
 112        leal    saved_return_addr, %edx
 113        movl    (%edx), %ecx
 114        pushl   %ecx
 115        ret
 116.previous
 117
 118.data
 119saved_return_addr:
 120        .long 0
 121efi_rt_function_ptr:
 122        .long 0
 123
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.