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