]> git.kernelconcepts.de Git - karo-tx-linux.git/blob - arch/x86/realmode/rm/wakeup_asm.S
e56479e580534ffd0cd5b3eb70531d4dd716869f
[karo-tx-linux.git] / arch / x86 / realmode / rm / wakeup_asm.S
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
13         .code16
14
15 /* This should match the structure in wakeup.h */
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)
34
35         .text
36         .code16
37
38         .balign 16
39 ENTRY(wakeup_start)
40         cli
41         cld
42
43         LJMPW_RM(3f)
44 3:
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
54         ljmpw   $8, $2f
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
64         LJMPW_RM(3f)
65 3:
66         /* Set up segments */
67         movw    %cs, %ax
68         movw    %ax, %ss
69         movl    $rm_stack_end, %esp
70         movw    %ax, %ds
71         movw    %ax, %es
72         movw    %ax, %fs
73         movw    %ax, %gs
74
75         lidtl   wakeup_idt
76
77         /* Clear the EFLAGS but remember if we have EFLAGS.ID */
78         movl $X86_EFLAGS_ID, %ecx
79         pushl %ecx
80         popfl
81         pushfl
82         popl %edi
83         pushl $0
84         popfl
85         pushfl
86         popl %edx
87         xorl %edx, %edi
88         andl %ecx, %edi         /* %edi is zero iff CPUID & %cr4 are missing */
89
90         /* Check header signature... */
91         movl    signature, %eax
92         cmpl    $WAKEUP_HEADER_SIGNATURE, %eax
93         jne     bogus_real_magic
94
95         /* Check we really have everything... */
96         movl    end_signature, %eax
97         cmpl    $REALMODE_END_SIGNATURE, %eax
98         jne     bogus_real_magic
99
100         /* Call the C code */
101         calll   main
102
103         /* Restore MISC_ENABLE before entering protected mode, in case
104            BIOS decided to clear XD_DISABLE during S3. */
105         movl    pmode_behavior, %edi
106         btl     $WAKEUP_BEHAVIOR_RESTORE_MISC_ENABLE, %edi
107         jnc     1f
108
109         movl    pmode_misc_en, %eax
110         movl    pmode_misc_en + 4, %edx
111         movl    $MSR_IA32_MISC_ENABLE, %ecx
112         wrmsr
113 1:
114
115         /* Do any other stuff... */
116
117 #ifndef CONFIG_64BIT
118         /* This could also be done in C code... */
119         movl    pmode_cr3, %eax
120         movl    %eax, %cr3
121
122         btl     $WAKEUP_BEHAVIOR_RESTORE_CR4, %edi
123         jz      1f
124         movl    pmode_cr4, %eax
125         movl    %eax, %cr4
126 1:
127         btl     $WAKEUP_BEHAVIOR_RESTORE_EFER, %edi
128         jz      1f
129         movl    pmode_efer, %eax
130         movl    pmode_efer + 4, %edx
131         movl    $MSR_EFER, %ecx
132         wrmsr
133 1:
134
135         lgdtl   pmode_gdt
136
137         /* This really couldn't... */
138         movl    pmode_entry, %eax
139         movl    pmode_cr0, %ecx
140         movl    %ecx, %cr0
141         ljmpl   $__KERNEL_CS, $pa_startup_32
142         /* -> jmp *%eax in trampoline_32.S */
143 #else
144         jmp     trampoline_start
145 #endif
146
147 bogus_real_magic:
148 1:
149         hlt
150         jmp     1b
151
152         .section ".rodata","a"
153
154         /*
155          * Set up the wakeup GDT.  We set these up as Big Real Mode,
156          * that is, with limits set to 4 GB.  At least the Lenovo
157          * Thinkpad X61 is known to need this for the video BIOS
158          * initialization quirk to work; this is likely to also
159          * be the case for other laptops or integrated video devices.
160          */
161
162         .balign 16
163 GLOBAL(wakeup_gdt)
164         .word   3*8-1           /* Self-descriptor */
165         .long   pa_wakeup_gdt
166         .word   0
167
168         .word   0xffff          /* 16-bit code segment @ real_mode_base */
169         .long   0x9b000000 + pa_real_mode_base
170         .word   0x008f          /* big real mode */
171
172         .word   0xffff          /* 16-bit data segment @ real_mode_base */
173         .long   0x93000000 + pa_real_mode_base
174         .word   0x008f          /* big real mode */
175 END(wakeup_gdt)
176
177         .section ".rodata","a"
178         .balign 8
179
180         /* This is the standard real-mode IDT */
181         .balign 16
182 GLOBAL(wakeup_idt)
183         .word   0xffff          /* limit */
184         .long   0               /* address */
185         .word   0
186 END(wakeup_idt)