root/arch/x86/platform/efi/efi_stub_32.S

/* [<][>][^][v][top][bottom][index][help] */
   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
  25 ENTRY(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
  43 1:
  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
  67 1:
  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 
  79 2:
  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
  93 1:
  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
 100 1:
 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
 117 ENDPROC(efi_call_phys)
 118 .previous
 119 
 120 .data
 121 saved_return_addr:
 122         .long 0
 123 efi_rt_function_ptr:
 124         .long 0

/* [<][>][^][v][top][bottom][index][help] */