efi_thunk_64.S 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197
  1. /* SPDX-License-Identifier: GPL-2.0 */
  2. /*
  3. * Copyright (C) 2014, 2015 Intel Corporation; author Matt Fleming
  4. *
  5. * Early support for invoking 32-bit EFI services from a 64-bit kernel.
  6. *
  7. * Because this thunking occurs before ExitBootServices() we have to
  8. * restore the firmware's 32-bit GDT before we make EFI serivce calls,
  9. * since the firmware's 32-bit IDT is still currently installed and it
  10. * needs to be able to service interrupts.
  11. *
  12. * On the plus side, we don't have to worry about mangling 64-bit
  13. * addresses into 32-bits because we're executing with an identify
  14. * mapped pagetable and haven't transitioned to 64-bit virtual addresses
  15. * yet.
  16. */
  17. #include <linux/linkage.h>
  18. #include <asm/msr.h>
  19. #include <asm/page_types.h>
  20. #include <asm/processor-flags.h>
  21. #include <asm/segment.h>
  22. .code64
  23. .text
  24. ENTRY(efi64_thunk)
  25. push %rbp
  26. push %rbx
  27. subq $8, %rsp
  28. leaq efi_exit32(%rip), %rax
  29. movl %eax, 4(%rsp)
  30. leaq efi_gdt64(%rip), %rax
  31. movl %eax, (%rsp)
  32. movl %eax, 2(%rax) /* Fixup the gdt base address */
  33. movl %ds, %eax
  34. push %rax
  35. movl %es, %eax
  36. push %rax
  37. movl %ss, %eax
  38. push %rax
  39. /*
  40. * Convert x86-64 ABI params to i386 ABI
  41. */
  42. subq $32, %rsp
  43. movl %esi, 0x0(%rsp)
  44. movl %edx, 0x4(%rsp)
  45. movl %ecx, 0x8(%rsp)
  46. movq %r8, %rsi
  47. movl %esi, 0xc(%rsp)
  48. movq %r9, %rsi
  49. movl %esi, 0x10(%rsp)
  50. sgdt save_gdt(%rip)
  51. leaq 1f(%rip), %rbx
  52. movq %rbx, func_rt_ptr(%rip)
  53. /*
  54. * Switch to gdt with 32-bit segments. This is the firmware GDT
  55. * that was installed when the kernel started executing. This
  56. * pointer was saved at the EFI stub entry point in head_64.S.
  57. */
  58. leaq efi32_boot_gdt(%rip), %rax
  59. lgdt (%rax)
  60. pushq $__KERNEL_CS
  61. leaq efi_enter32(%rip), %rax
  62. pushq %rax
  63. lretq
  64. 1: addq $32, %rsp
  65. lgdt save_gdt(%rip)
  66. pop %rbx
  67. movl %ebx, %ss
  68. pop %rbx
  69. movl %ebx, %es
  70. pop %rbx
  71. movl %ebx, %ds
  72. /*
  73. * Convert 32-bit status code into 64-bit.
  74. */
  75. test %rax, %rax
  76. jz 1f
  77. movl %eax, %ecx
  78. andl $0x0fffffff, %ecx
  79. andl $0xf0000000, %eax
  80. shl $32, %rax
  81. or %rcx, %rax
  82. 1:
  83. addq $8, %rsp
  84. pop %rbx
  85. pop %rbp
  86. ret
  87. ENDPROC(efi64_thunk)
  88. ENTRY(efi_exit32)
  89. movq func_rt_ptr(%rip), %rax
  90. push %rax
  91. mov %rdi, %rax
  92. ret
  93. ENDPROC(efi_exit32)
  94. .code32
  95. /*
  96. * EFI service pointer must be in %edi.
  97. *
  98. * The stack should represent the 32-bit calling convention.
  99. */
  100. ENTRY(efi_enter32)
  101. movl $__KERNEL_DS, %eax
  102. movl %eax, %ds
  103. movl %eax, %es
  104. movl %eax, %ss
  105. /* Reload pgtables */
  106. movl %cr3, %eax
  107. movl %eax, %cr3
  108. /* Disable paging */
  109. movl %cr0, %eax
  110. btrl $X86_CR0_PG_BIT, %eax
  111. movl %eax, %cr0
  112. /* Disable long mode via EFER */
  113. movl $MSR_EFER, %ecx
  114. rdmsr
  115. btrl $_EFER_LME, %eax
  116. wrmsr
  117. call *%edi
  118. /* We must preserve return value */
  119. movl %eax, %edi
  120. /*
  121. * Some firmware will return with interrupts enabled. Be sure to
  122. * disable them before we switch GDTs.
  123. */
  124. cli
  125. movl 56(%esp), %eax
  126. movl %eax, 2(%eax)
  127. lgdtl (%eax)
  128. movl %cr4, %eax
  129. btsl $(X86_CR4_PAE_BIT), %eax
  130. movl %eax, %cr4
  131. movl %cr3, %eax
  132. movl %eax, %cr3
  133. movl $MSR_EFER, %ecx
  134. rdmsr
  135. btsl $_EFER_LME, %eax
  136. wrmsr
  137. xorl %eax, %eax
  138. lldt %ax
  139. movl 60(%esp), %eax
  140. pushl $__KERNEL_CS
  141. pushl %eax
  142. /* Enable paging */
  143. movl %cr0, %eax
  144. btsl $X86_CR0_PG_BIT, %eax
  145. movl %eax, %cr0
  146. lret
  147. ENDPROC(efi_enter32)
  148. .data
  149. .balign 8
  150. .global efi32_boot_gdt
  151. efi32_boot_gdt: .word 0
  152. .quad 0
  153. save_gdt: .word 0
  154. .quad 0
  155. func_rt_ptr: .quad 0
  156. .global efi_gdt64
  157. efi_gdt64:
  158. .word efi_gdt64_end - efi_gdt64
  159. .long 0 /* Filled out by user */
  160. .word 0
  161. .quad 0x0000000000000000 /* NULL descriptor */
  162. .quad 0x00af9a000000ffff /* __KERNEL_CS */
  163. .quad 0x00cf92000000ffff /* __KERNEL_DS */
  164. .quad 0x0080890000000000 /* TS descriptor */
  165. .quad 0x0000000000000000 /* TS continued */
  166. efi_gdt64_end: