wakeup_asm.S 3.8 KB

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