efi_stub.S 2.7 KB

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