Merge git://git.kernel.org/pub/scm/linux/kernel/git/wim/linux-2.6-watchdog
[linux-2.6] / 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    $swsusp_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         pushl   %ebx
234         sgdt    saved_gdt
235         sidt    saved_idt
236         sldt    saved_ldt
237         str     saved_tss
238
239         movl    nx_enabled, %edx
240         movl    %edx, real_efer_save_restore - wakeup_start (%eax)
241         testl   $1, real_efer_save_restore - wakeup_start (%eax)
242         jz      2f
243         # save efer setting
244         pushl   %eax
245         movl    %eax, %ebx
246         mov     $0xc0000080, %ecx
247         rdmsr
248         movl    %edx, real_save_efer_edx - wakeup_start (%ebx)
249         movl    %eax, real_save_efer_eax - wakeup_start (%ebx)
250         popl    %eax
251 2:
252
253         movl    %cr3, %edx
254         movl    %edx, real_save_cr3 - wakeup_start (%eax)
255         movl    %cr4, %edx
256         movl    %edx, real_save_cr4 - wakeup_start (%eax)
257         movl    %cr0, %edx
258         movl    %edx, real_save_cr0 - wakeup_start (%eax)
259         sgdt    real_save_gdt - wakeup_start (%eax)
260
261         movl    saved_videomode, %edx
262         movl    %edx, video_mode - wakeup_start (%eax)
263         movl    acpi_video_flags, %edx
264         movl    %edx, video_flags - wakeup_start (%eax)
265         movl    $0x12345678, real_magic - wakeup_start (%eax)
266         movl    $0x12345678, saved_magic
267         popl    %ebx
268         ret
269
270 save_registers:
271         leal    4(%esp), %eax
272         movl    %eax, saved_context_esp
273         movl %ebx, saved_context_ebx
274         movl %ebp, saved_context_ebp
275         movl %esi, saved_context_esi
276         movl %edi, saved_context_edi
277         pushfl ; popl saved_context_eflags
278
279         movl $ret_point, saved_eip
280         ret
281
282
283 restore_registers:
284         movl saved_context_ebp, %ebp
285         movl saved_context_ebx, %ebx
286         movl saved_context_esi, %esi
287         movl saved_context_edi, %edi
288         pushl saved_context_eflags ; popfl
289         ret     
290
291 ENTRY(do_suspend_lowlevel)
292         call    save_processor_state
293         call    save_registers
294         pushl   $3
295         call    acpi_enter_sleep_state
296         addl    $4, %esp
297
298 #       In case of S3 failure, we'll emerge here.  Jump
299 #       to ret_point to recover
300         jmp     ret_point
301         .p2align 4,,7
302 ret_point:
303         call    restore_registers
304         call    restore_processor_state
305         ret
306
307 .data
308 ALIGN
309 ENTRY(saved_magic)      .long   0
310 ENTRY(saved_eip)        .long   0
311
312 # saved registers
313 saved_gdt:      .long   0,0
314 saved_idt:      .long   0,0
315 saved_ldt:      .long   0
316 saved_tss:      .long   0
317