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