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