Merge master.kernel.org:/pub/scm/linux/kernel/git/wim/linux-2.6-watchdog
[sfrench/cifs-2.6.git] / arch / i386 / kernel / acpi / wakeup.S
1 .text
2 #include <linux/linkage.h>
3 #include <asm/segment.h>
4 #include <asm/page.h>
5
6 #
7 # wakeup_code runs in real mode, and at unknown address (determined at run-time).
8 # Therefore it must only use relative jumps/calls. 
9 #
10 # Do we need to deal with A20? It is okay: ACPI specs says A20 must be enabled
11 #
12 # If physical address of wakeup_code is 0x12345, BIOS should call us with
13 # cs = 0x1234, eip = 0x05
14
15
16 ALIGN
17         .align  4096
18 ENTRY(wakeup_start)
19 wakeup_code:
20         wakeup_code_start = .
21         .code16
22
23         movw    $0xb800, %ax
24         movw    %ax,%fs
25         movw    $0x0e00 + 'L', %fs:(0x10)
26
27         cli
28         cld
29
30         # setup data segment
31         movw    %cs, %ax
32         movw    %ax, %ds                                        # Make ds:0 point to wakeup_start
33         movw    %ax, %ss
34         mov     $(wakeup_stack - wakeup_code), %sp              # Private stack is needed for ASUS board
35         movw    $0x0e00 + 'S', %fs:(0x12)
36
37         pushl   $0                                              # Kill any dangerous flags
38         popfl
39
40         movl    real_magic - wakeup_code, %eax
41         cmpl    $0x12345678, %eax
42         jne     bogus_real_magic
43
44         testl   $1, video_flags - wakeup_code
45         jz      1f
46         lcall   $0xc000,$3
47         movw    %cs, %ax
48         movw    %ax, %ds                                        # Bios might have played with that
49         movw    %ax, %ss
50 1:
51
52         testl   $2, video_flags - wakeup_code
53         jz      1f
54         mov     video_mode - wakeup_code, %ax
55         call    mode_set
56 1:
57
58         # set up page table
59         movl    $swapper_pg_dir-__PAGE_OFFSET, %eax
60         movl    %eax, %cr3
61
62         testl   $1, real_efer_save_restore - wakeup_code
63         jz      4f
64         # restore efer setting
65         movl    real_save_efer_edx - wakeup_code, %edx
66         movl    real_save_efer_eax - wakeup_code, %eax
67         mov     $0xc0000080, %ecx
68         wrmsr
69 4:
70         # make sure %cr4 is set correctly (features, etc)
71         movl    real_save_cr4 - wakeup_code, %eax
72         movl    %eax, %cr4
73         movw    $0xb800, %ax
74         movw    %ax,%fs
75         movw    $0x0e00 + 'i', %fs:(0x12)
76         
77         # need a gdt -- use lgdtl to force 32-bit operands, in case
78         # the GDT is located past 16 megabytes.
79         lgdtl   real_save_gdt - wakeup_code
80
81         movl    real_save_cr0 - wakeup_code, %eax
82         movl    %eax, %cr0
83         jmp 1f
84 1:
85         movw    $0x0e00 + 'n', %fs:(0x14)
86
87         movl    real_magic - wakeup_code, %eax
88         cmpl    $0x12345678, %eax
89         jne     bogus_real_magic
90
91         ljmpl   $__KERNEL_CS,$wakeup_pmode_return
92
93 real_save_gdt:  .word 0
94                 .long 0
95 real_save_cr0:  .long 0
96 real_save_cr3:  .long 0
97 real_save_cr4:  .long 0
98 real_magic:     .long 0
99 video_mode:     .long 0
100 video_flags:    .long 0
101 real_efer_save_restore: .long 0
102 real_save_efer_edx:     .long 0
103 real_save_efer_eax:     .long 0
104
105 bogus_real_magic:
106         movw    $0x0e00 + 'B', %fs:(0x12)
107         jmp bogus_real_magic
108
109 /* This code uses an extended set of video mode numbers. These include:
110  * Aliases for standard modes
111  *      NORMAL_VGA (-1)
112  *      EXTENDED_VGA (-2)
113  *      ASK_VGA (-3)
114  * Video modes numbered by menu position -- NOT RECOMMENDED because of lack
115  * of compatibility when extending the table. These are between 0x00 and 0xff.
116  */
117 #define VIDEO_FIRST_MENU 0x0000
118
119 /* Standard BIOS video modes (BIOS number + 0x0100) */
120 #define VIDEO_FIRST_BIOS 0x0100
121
122 /* VESA BIOS video modes (VESA number + 0x0200) */
123 #define VIDEO_FIRST_VESA 0x0200
124
125 /* Video7 special modes (BIOS number + 0x0900) */
126 #define VIDEO_FIRST_V7 0x0900
127
128 # Setting of user mode (AX=mode ID) => CF=success
129 mode_set:
130         movw    %ax, %bx
131 #if 0
132         cmpb    $0xff, %ah
133         jz      setalias
134
135         testb   $VIDEO_RECALC>>8, %ah
136         jnz     _setrec
137
138         cmpb    $VIDEO_FIRST_RESOLUTION>>8, %ah
139         jnc     setres
140         
141         cmpb    $VIDEO_FIRST_SPECIAL>>8, %ah
142         jz      setspc
143
144         cmpb    $VIDEO_FIRST_V7>>8, %ah
145         jz      setv7
146 #endif
147         
148         cmpb    $VIDEO_FIRST_VESA>>8, %ah
149         jnc     check_vesa
150 #if 0   
151         orb     %ah, %ah
152         jz      setmenu
153 #endif
154         
155         decb    %ah
156 #       jz      setbios                           Add bios modes later
157
158 setbad: clc
159         ret
160
161 check_vesa:
162         subb    $VIDEO_FIRST_VESA>>8, %bh
163         orw     $0x4000, %bx                    # Use linear frame buffer
164         movw    $0x4f02, %ax                    # VESA BIOS mode set call
165         int     $0x10
166         cmpw    $0x004f, %ax                    # AL=4f if implemented
167         jnz     _setbad                         # AH=0 if OK
168
169         stc
170         ret
171
172 _setbad: jmp setbad
173
174         .code32
175         ALIGN
176
177 .org    0x800
178 wakeup_stack_begin:     # Stack grows down
179
180 .org    0xff0           # Just below end of page
181 wakeup_stack:
182 ENTRY(wakeup_end)
183         
184 .org    0x1000
185
186 wakeup_pmode_return:
187         movw    $__KERNEL_DS, %ax
188         movw    %ax, %ss
189         movw    %ax, %ds
190         movw    %ax, %es
191         movw    %ax, %fs
192         movw    %ax, %gs
193         movw    $0x0e00 + 'u', 0xb8016
194
195         # reload the gdt, as we need the full 32 bit address
196         lgdt    saved_gdt
197         lidt    saved_idt
198         lldt    saved_ldt
199         ljmp    $(__KERNEL_CS),$1f
200 1:
201         movl    %cr3, %eax
202         movl    %eax, %cr3
203         wbinvd
204
205         # and restore the stack ... but you need gdt for this to work
206         movl    saved_context_esp, %esp
207
208         movl    %cs:saved_magic, %eax
209         cmpl    $0x12345678, %eax
210         jne     bogus_magic
211
212         # jump to place where we left off
213         movl    saved_eip,%eax
214         jmp     *%eax
215
216 bogus_magic:
217         movw    $0x0e00 + 'B', 0xb8018
218         jmp     bogus_magic
219
220
221 ##
222 # acpi_copy_wakeup_routine
223 #
224 # Copy the above routine to low memory.
225 #
226 # Parameters:
227 # %eax: place to copy wakeup routine to
228 #
229 # Returned address is location of code in low memory (past data and stack)
230 #
231 ENTRY(acpi_copy_wakeup_routine)
232
233         sgdt    saved_gdt
234         sidt    saved_idt
235         sldt    saved_ldt
236         str     saved_tss
237
238         movl    nx_enabled, %edx
239         movl    %edx, real_efer_save_restore - wakeup_start (%eax)
240         testl   $1, real_efer_save_restore - wakeup_start (%eax)
241         jz      2f
242         # save efer setting
243         pushl   %eax
244         movl    %eax, %ebx
245         mov     $0xc0000080, %ecx
246         rdmsr
247         movl    %edx, real_save_efer_edx - wakeup_start (%ebx)
248         movl    %eax, real_save_efer_eax - wakeup_start (%ebx)
249         popl    %eax
250 2:
251
252         movl    %cr3, %edx
253         movl    %edx, real_save_cr3 - wakeup_start (%eax)
254         movl    %cr4, %edx
255         movl    %edx, real_save_cr4 - wakeup_start (%eax)
256         movl    %cr0, %edx
257         movl    %edx, real_save_cr0 - wakeup_start (%eax)
258         sgdt    real_save_gdt - wakeup_start (%eax)
259
260         movl    saved_videomode, %edx
261         movl    %edx, video_mode - wakeup_start (%eax)
262         movl    acpi_video_flags, %edx
263         movl    %edx, video_flags - wakeup_start (%eax)
264         movl    $0x12345678, real_magic - wakeup_start (%eax)
265         movl    $0x12345678, saved_magic
266         ret
267
268 .data
269 ALIGN
270 ENTRY(saved_magic)      .long   0
271 ENTRY(saved_eip)        .long   0
272
273 save_registers:
274         leal    4(%esp), %eax
275         movl    %eax, saved_context_esp
276         movl %ebx, saved_context_ebx
277         movl %ebp, saved_context_ebp
278         movl %esi, saved_context_esi
279         movl %edi, saved_context_edi
280         pushfl ; popl saved_context_eflags
281
282         movl $ret_point, saved_eip
283         ret
284
285
286 restore_registers:
287         movl saved_context_ebp, %ebp
288         movl saved_context_ebx, %ebx
289         movl saved_context_esi, %esi
290         movl saved_context_edi, %edi
291         pushl saved_context_eflags ; popfl
292         ret     
293
294 ENTRY(do_suspend_lowlevel)
295         call    save_processor_state
296         call    save_registers
297         pushl   $3
298         call    acpi_enter_sleep_state
299         addl    $4, %esp
300         ret
301         .p2align 4,,7
302 ret_point:
303         call    restore_registers
304         call    restore_processor_state
305         ret
306
307 ALIGN
308 # saved registers
309 saved_gdt:      .long   0,0
310 saved_idt:      .long   0,0
311 saved_ldt:      .long   0
312 saved_tss:      .long   0
313