wakeup_asm.S 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177
  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 */
  69. pushl $0
  70. popfl
  71. /* Check header signature... */
  72. movl signature, %eax
  73. cmpl $WAKEUP_HEADER_SIGNATURE, %eax
  74. jne bogus_real_magic
  75. /* Check we really have everything... */
  76. movl end_signature, %eax
  77. cmpl $REALMODE_END_SIGNATURE, %eax
  78. jne bogus_real_magic
  79. /* Call the C code */
  80. calll main
  81. /* Restore MISC_ENABLE before entering protected mode, in case
  82. BIOS decided to clear XD_DISABLE during S3. */
  83. movl pmode_behavior, %edi
  84. btl $WAKEUP_BEHAVIOR_RESTORE_MISC_ENABLE, %edi
  85. jnc 1f
  86. movl pmode_misc_en, %eax
  87. movl pmode_misc_en + 4, %edx
  88. movl $MSR_IA32_MISC_ENABLE, %ecx
  89. wrmsr
  90. 1:
  91. /* Do any other stuff... */
  92. #ifndef CONFIG_64BIT
  93. /* This could also be done in C code... */
  94. movl pmode_cr3, %eax
  95. movl %eax, %cr3
  96. btl $WAKEUP_BEHAVIOR_RESTORE_CR4, %edi
  97. jnc 1f
  98. movl pmode_cr4, %eax
  99. movl %eax, %cr4
  100. 1:
  101. btl $WAKEUP_BEHAVIOR_RESTORE_EFER, %edi
  102. jnc 1f
  103. movl pmode_efer, %eax
  104. movl pmode_efer + 4, %edx
  105. movl $MSR_EFER, %ecx
  106. wrmsr
  107. 1:
  108. lgdtl pmode_gdt
  109. /* This really couldn't... */
  110. movl pmode_entry, %eax
  111. movl pmode_cr0, %ecx
  112. movl %ecx, %cr0
  113. ljmpl $__KERNEL_CS, $pa_startup_32
  114. /* -> jmp *%eax in trampoline_32.S */
  115. #else
  116. jmp trampoline_start
  117. #endif
  118. bogus_real_magic:
  119. 1:
  120. hlt
  121. jmp 1b
  122. .section ".rodata","a"
  123. /*
  124. * Set up the wakeup GDT. We set these up as Big Real Mode,
  125. * that is, with limits set to 4 GB. At least the Lenovo
  126. * Thinkpad X61 is known to need this for the video BIOS
  127. * initialization quirk to work; this is likely to also
  128. * be the case for other laptops or integrated video devices.
  129. */
  130. .balign 16
  131. GLOBAL(wakeup_gdt)
  132. .word 3*8-1 /* Self-descriptor */
  133. .long pa_wakeup_gdt
  134. .word 0
  135. .word 0xffff /* 16-bit code segment @ real_mode_base */
  136. .long 0x9b000000 + pa_real_mode_base
  137. .word 0x008f /* big real mode */
  138. .word 0xffff /* 16-bit data segment @ real_mode_base */
  139. .long 0x93000000 + pa_real_mode_base
  140. .word 0x008f /* big real mode */
  141. END(wakeup_gdt)
  142. .section ".rodata","a"
  143. .balign 8
  144. /* This is the standard real-mode IDT */
  145. .balign 16
  146. GLOBAL(wakeup_idt)
  147. .word 0xffff /* limit */
  148. .long 0 /* address */
  149. .word 0
  150. END(wakeup_idt)