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