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