wakeup_asm.S 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179
  1. /* SPDX-License-Identifier: GPL-2.0 */
  2. /*
  3. * ACPI wakeup real mode startup stub
  4. */
  5. #include <linux/linkage.h>
  6. #include <asm/segment.h>
  7. #include <asm/msr-index.h>
  8. #include <asm/page_types.h>
  9. #include <asm/pgtable_types.h>
  10. #include <asm/processor-flags.h>
  11. #include "realmode.h"
  12. #include "wakeup.h"
  13. .code16
  14. /* This should match the structure in wakeup.h */
  15. .section ".data", "aw"
  16. .balign 16
  17. GLOBAL(wakeup_header)
  18. video_mode: .short 0 /* Video mode number */
  19. pmode_entry: .long 0
  20. pmode_cs: .short __KERNEL_CS
  21. pmode_cr0: .long 0 /* Saved %cr0 */
  22. pmode_cr3: .long 0 /* Saved %cr3 */
  23. pmode_cr4: .long 0 /* Saved %cr4 */
  24. pmode_efer: .quad 0 /* Saved EFER */
  25. pmode_gdt: .quad 0
  26. pmode_misc_en: .quad 0 /* Saved MISC_ENABLE MSR */
  27. pmode_behavior: .long 0 /* Wakeup behavior flags */
  28. realmode_flags: .long 0
  29. real_magic: .long 0
  30. signature: .long WAKEUP_HEADER_SIGNATURE
  31. END(wakeup_header)
  32. .text
  33. .code16
  34. .balign 16
  35. ENTRY(wakeup_start)
  36. cli
  37. cld
  38. LJMPW_RM(3f)
  39. 3:
  40. /* Apparently some dimwit BIOS programmers don't know how to
  41. program a PM to RM transition, and we might end up here with
  42. junk in the data segment descriptor registers. The only way
  43. to repair that is to go into PM and fix it ourselves... */
  44. movw $16, %cx
  45. lgdtl %cs:wakeup_gdt
  46. movl %cr0, %eax
  47. orb $X86_CR0_PE, %al
  48. movl %eax, %cr0
  49. ljmpw $8, $2f
  50. 2:
  51. movw %cx, %ds
  52. movw %cx, %es
  53. movw %cx, %ss
  54. movw %cx, %fs
  55. movw %cx, %gs
  56. andb $~X86_CR0_PE, %al
  57. movl %eax, %cr0
  58. LJMPW_RM(3f)
  59. 3:
  60. /* Set up segments */
  61. movw %cs, %ax
  62. movw %ax, %ss
  63. movl $rm_stack_end, %esp
  64. movw %ax, %ds
  65. movw %ax, %es
  66. movw %ax, %fs
  67. movw %ax, %gs
  68. lidtl wakeup_idt
  69. /* Clear the EFLAGS */
  70. pushl $0
  71. popfl
  72. /* Check header signature... */
  73. movl signature, %eax
  74. cmpl $WAKEUP_HEADER_SIGNATURE, %eax
  75. jne bogus_real_magic
  76. /* Check we really have everything... */
  77. movl end_signature, %eax
  78. cmpl $REALMODE_END_SIGNATURE, %eax
  79. jne bogus_real_magic
  80. /* Call the C code */
  81. calll main
  82. /* Restore MISC_ENABLE before entering protected mode, in case
  83. BIOS decided to clear XD_DISABLE during S3. */
  84. movl pmode_behavior, %edi
  85. btl $WAKEUP_BEHAVIOR_RESTORE_MISC_ENABLE, %edi
  86. jnc 1f
  87. movl pmode_misc_en, %eax
  88. movl pmode_misc_en + 4, %edx
  89. movl $MSR_IA32_MISC_ENABLE, %ecx
  90. wrmsr
  91. 1:
  92. /* Do any other stuff... */
  93. #ifndef CONFIG_64BIT
  94. /* This could also be done in C code... */
  95. movl pmode_cr3, %eax
  96. movl %eax, %cr3
  97. btl $WAKEUP_BEHAVIOR_RESTORE_CR4, %edi
  98. jnc 1f
  99. movl pmode_cr4, %eax
  100. movl %eax, %cr4
  101. 1:
  102. btl $WAKEUP_BEHAVIOR_RESTORE_EFER, %edi
  103. jnc 1f
  104. movl pmode_efer, %eax
  105. movl pmode_efer + 4, %edx
  106. movl $MSR_EFER, %ecx
  107. wrmsr
  108. 1:
  109. lgdtl pmode_gdt
  110. /* This really couldn't... */
  111. movl pmode_entry, %eax
  112. movl pmode_cr0, %ecx
  113. movl %ecx, %cr0
  114. ljmpl $__KERNEL_CS, $pa_startup_32
  115. /* -> jmp *%eax in trampoline_32.S */
  116. #else
  117. jmp trampoline_start
  118. #endif
  119. bogus_real_magic:
  120. 1:
  121. hlt
  122. jmp 1b
  123. .section ".rodata","a"
  124. /*
  125. * Set up the wakeup GDT. We set these up as Big Real Mode,
  126. * that is, with limits set to 4 GB. At least the Lenovo
  127. * Thinkpad X61 is known to need this for the video BIOS
  128. * initialization quirk to work; this is likely to also
  129. * be the case for other laptops or integrated video devices.
  130. */
  131. .balign 16
  132. GLOBAL(wakeup_gdt)
  133. .word 3*8-1 /* Self-descriptor */
  134. .long pa_wakeup_gdt
  135. .word 0
  136. .word 0xffff /* 16-bit code segment @ real_mode_base */
  137. .long 0x9b000000 + pa_real_mode_base
  138. .word 0x008f /* big real mode */
  139. .word 0xffff /* 16-bit data segment @ real_mode_base */
  140. .long 0x93000000 + pa_real_mode_base
  141. .word 0x008f /* big real mode */
  142. END(wakeup_gdt)
  143. .section ".rodata","a"
  144. .balign 8
  145. /* This is the standard real-mode IDT */
  146. .balign 16
  147. GLOBAL(wakeup_idt)
  148. .word 0xffff /* limit */
  149. .long 0 /* address */
  150. .word 0
  151. END(wakeup_idt)