efi_thunk_64.S 3.6 KB

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