Linux-2.6.12-rc2
[linux-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
78         lgdt    real_save_gdt - wakeup_code
79
80         movl    real_save_cr0 - wakeup_code, %eax
81         movl    %eax, %cr0
82         jmp 1f
83 1:
84         movw    $0x0e00 + 'n', %fs:(0x14)
85
86         movl    real_magic - wakeup_code, %eax
87         cmpl    $0x12345678, %eax
88         jne     bogus_real_magic
89
90         ljmpl   $__KERNEL_CS,$wakeup_pmode_return
91
92 real_save_gdt:  .word 0
93                 .long 0
94 real_save_cr0:  .long 0
95 real_save_cr3:  .long 0
96 real_save_cr4:  .long 0
97 real_magic:     .long 0
98 video_mode:     .long 0
99 video_flags:    .long 0
100 real_efer_save_restore: .long 0
101 real_save_efer_edx:     .long 0
102 real_save_efer_eax:     .long 0
103
104 bogus_real_magic:
105         movw    $0x0e00 + 'B', %fs:(0x12)
106         jmp bogus_real_magic
107
108 /* This code uses an extended set of video mode numbers. These include:
109  * Aliases for standard modes
110  *      NORMAL_VGA (-1)
111  *      EXTENDED_VGA (-2)
112  *      ASK_VGA (-3)
113  * Video modes numbered by menu position -- NOT RECOMMENDED because of lack
114  * of compatibility when extending the table. These are between 0x00 and 0xff.
115  */
116 #define VIDEO_FIRST_MENU 0x0000
117
118 /* Standard BIOS video modes (BIOS number + 0x0100) */
119 #define VIDEO_FIRST_BIOS 0x0100
120
121 /* VESA BIOS video modes (VESA number + 0x0200) */
122 #define VIDEO_FIRST_VESA 0x0200
123
124 /* Video7 special modes (BIOS number + 0x0900) */
125 #define VIDEO_FIRST_V7 0x0900
126
127 # Setting of user mode (AX=mode ID) => CF=success
128 mode_set:
129         movw    %ax, %bx
130 #if 0
131         cmpb    $0xff, %ah
132         jz      setalias
133
134         testb   $VIDEO_RECALC>>8, %ah
135         jnz     _setrec
136
137         cmpb    $VIDEO_FIRST_RESOLUTION>>8, %ah
138         jnc     setres
139         
140         cmpb    $VIDEO_FIRST_SPECIAL>>8, %ah
141         jz      setspc
142
143         cmpb    $VIDEO_FIRST_V7>>8, %ah
144         jz      setv7
145 #endif
146         
147         cmpb    $VIDEO_FIRST_VESA>>8, %ah
148         jnc     check_vesa
149 #if 0   
150         orb     %ah, %ah
151         jz      setmenu
152 #endif
153         
154         decb    %ah
155 #       jz      setbios                           Add bios modes later
156
157 setbad: clc
158         ret
159
160 check_vesa:
161         subb    $VIDEO_FIRST_VESA>>8, %bh
162         orw     $0x4000, %bx                    # Use linear frame buffer
163         movw    $0x4f02, %ax                    # VESA BIOS mode set call
164         int     $0x10
165         cmpw    $0x004f, %ax                    # AL=4f if implemented
166         jnz     _setbad                         # AH=0 if OK
167
168         stc
169         ret
170
171 _setbad: jmp setbad
172
173         .code32
174         ALIGN
175
176 .org    0x800
177 wakeup_stack_begin:     # Stack grows down
178
179 .org    0xff0           # Just below end of page
180 wakeup_stack:
181 ENTRY(wakeup_end)
182         
183 .org    0x1000
184
185 wakeup_pmode_return:
186         movw    $__KERNEL_DS, %ax
187         movw    %ax, %ss
188         movw    %ax, %ds
189         movw    %ax, %es
190         movw    %ax, %fs
191         movw    %ax, %gs
192         movw    $0x0e00 + 'u', 0xb8016
193
194         # reload the gdt, as we need the full 32 bit address
195         lgdt    saved_gdt
196         lidt    saved_idt
197         lldt    saved_ldt
198         ljmp    $(__KERNEL_CS),$1f
199 1:
200         movl    %cr3, %eax
201         movl    %eax, %cr3
202         wbinvd
203
204         # and restore the stack ... but you need gdt for this to work
205         movl    saved_context_esp, %esp
206
207         movl    %cs:saved_magic, %eax
208         cmpl    $0x12345678, %eax
209         jne     bogus_magic
210
211         # jump to place where we left off
212         movl    saved_eip,%eax
213         jmp     *%eax
214
215 bogus_magic:
216         movw    $0x0e00 + 'B', 0xb8018
217         jmp     bogus_magic
218
219
220 ##
221 # acpi_copy_wakeup_routine
222 #
223 # Copy the above routine to low memory.
224 #
225 # Parameters:
226 # %eax: place to copy wakeup routine to
227 #
228 # Returned address is location of code in low memory (past data and stack)
229 #
230 ENTRY(acpi_copy_wakeup_routine)
231
232         sgdt    saved_gdt
233         sidt    saved_idt
234         sldt    saved_ldt
235         str     saved_tss
236
237         movl    nx_enabled, %edx
238         movl    %edx, real_efer_save_restore - wakeup_start (%eax)
239         testl   $1, real_efer_save_restore - wakeup_start (%eax)
240         jz      2f
241         # save efer setting
242         pushl   %eax
243         movl    %eax, %ebx
244         mov     $0xc0000080, %ecx
245         rdmsr
246         movl    %edx, real_save_efer_edx - wakeup_start (%ebx)
247         movl    %eax, real_save_efer_eax - wakeup_start (%ebx)
248         popl    %eax
249 2:
250
251         movl    %cr3, %edx
252         movl    %edx, real_save_cr3 - wakeup_start (%eax)
253         movl    %cr4, %edx
254         movl    %edx, real_save_cr4 - wakeup_start (%eax)
255         movl    %cr0, %edx
256         movl    %edx, real_save_cr0 - wakeup_start (%eax)
257         sgdt    real_save_gdt - wakeup_start (%eax)
258
259         movl    saved_videomode, %edx
260         movl    %edx, video_mode - wakeup_start (%eax)
261         movl    acpi_video_flags, %edx
262         movl    %edx, video_flags - wakeup_start (%eax)
263         movl    $0x12345678, real_magic - wakeup_start (%eax)
264         movl    $0x12345678, saved_magic
265         ret
266
267 .data
268 ALIGN
269 ENTRY(saved_magic)      .long   0
270 ENTRY(saved_eip)        .long   0
271
272 save_registers:
273         leal    4(%esp), %eax
274         movl    %eax, saved_context_esp
275         movl %ebx, saved_context_ebx
276         movl %ebp, saved_context_ebp
277         movl %esi, saved_context_esi
278         movl %edi, saved_context_edi
279         pushfl ; popl saved_context_eflags
280
281         movl $ret_point, saved_eip
282         ret
283
284
285 restore_registers:
286         movl saved_context_ebp, %ebp
287         movl saved_context_ebx, %ebx
288         movl saved_context_esi, %esi
289         movl saved_context_edi, %edi
290         pushl saved_context_eflags ; popfl
291         ret     
292
293 ENTRY(do_suspend_lowlevel)
294         call    save_processor_state
295         call    save_registers
296         pushl   $3
297         call    acpi_enter_sleep_state
298         addl    $4, %esp
299         ret
300         .p2align 4,,7
301 ret_point:
302         call    restore_registers
303         call    restore_processor_state
304         ret
305
306 ENTRY(do_suspend_lowlevel_s4bios)
307         call save_processor_state
308         call save_registers
309         call acpi_enter_sleep_state_s4bios
310         ret
311
312 ALIGN
313 # saved registers
314 saved_gdt:      .long   0,0
315 saved_idt:      .long   0,0
316 saved_ldt:      .long   0
317 saved_tss:      .long   0
318