efi_stub.S 2.7 KB

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