Merge ssh://master.kernel.org/pub/scm/linux/kernel/git/tglx/linux-2.6-hrt
[linux-2.6] / arch / x86 / kernel / acpi / wakeup_32.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
56         pushl   $0                                              # Kill any dangerous flags
57         popfl
58
59         movl    real_magic - wakeup_code, %eax
60         cmpl    $0x12345678, %eax
61         jne     bogus_real_magic
62
63         testl   $1, realmode_flags - wakeup_code
64         jz      1f
65         lcall   $0xc000,$3
66         movw    %cs, %ax
67         movw    %ax, %ds                                        # Bios might have played with that
68         movw    %ax, %ss
69 1:
70
71         testl   $2, realmode_flags - wakeup_code
72         jz      1f
73         mov     video_mode - wakeup_code, %ax
74         call    mode_set
75 1:
76
77         # set up page table
78         movl    $swsusp_pg_dir-__PAGE_OFFSET, %eax
79         movl    %eax, %cr3
80
81         testl   $1, real_efer_save_restore - wakeup_code
82         jz      4f
83         # restore efer setting
84         movl    real_save_efer_edx - wakeup_code, %edx
85         movl    real_save_efer_eax - wakeup_code, %eax
86         mov     $0xc0000080, %ecx
87         wrmsr
88 4:
89         # make sure %cr4 is set correctly (features, etc)
90         movl    real_save_cr4 - wakeup_code, %eax
91         movl    %eax, %cr4
92         
93         # need a gdt -- use lgdtl to force 32-bit operands, in case
94         # the GDT is located past 16 megabytes.
95         lgdtl   real_save_gdt - wakeup_code
96
97         movl    real_save_cr0 - wakeup_code, %eax
98         movl    %eax, %cr0
99         jmp 1f
100 1:
101         movl    real_magic - wakeup_code, %eax
102         cmpl    $0x12345678, %eax
103         jne     bogus_real_magic
104
105         testl   $8, realmode_flags - wakeup_code
106         jz      1f
107         BEEP
108 1:
109         ljmpl   $__KERNEL_CS, $wakeup_pmode_return
110
111 real_save_gdt:  .word 0
112                 .long 0
113 real_save_cr0:  .long 0
114 real_save_cr3:  .long 0
115 real_save_cr4:  .long 0
116 real_magic:     .long 0
117 video_mode:     .long 0
118 realmode_flags: .long 0
119 real_efer_save_restore: .long 0
120 real_save_efer_edx:     .long 0
121 real_save_efer_eax:     .long 0
122
123 bogus_real_magic:
124         jmp bogus_real_magic
125
126 /* This code uses an extended set of video mode numbers. These include:
127  * Aliases for standard modes
128  *      NORMAL_VGA (-1)
129  *      EXTENDED_VGA (-2)
130  *      ASK_VGA (-3)
131  * Video modes numbered by menu position -- NOT RECOMMENDED because of lack
132  * of compatibility when extending the table. These are between 0x00 and 0xff.
133  */
134 #define VIDEO_FIRST_MENU 0x0000
135
136 /* Standard BIOS video modes (BIOS number + 0x0100) */
137 #define VIDEO_FIRST_BIOS 0x0100
138
139 /* VESA BIOS video modes (VESA number + 0x0200) */
140 #define VIDEO_FIRST_VESA 0x0200
141
142 /* Video7 special modes (BIOS number + 0x0900) */
143 #define VIDEO_FIRST_V7 0x0900
144
145 # Setting of user mode (AX=mode ID) => CF=success
146
147 # For now, we only handle VESA modes (0x0200..0x03ff).  To handle other
148 # modes, we should probably compile in the video code from the boot
149 # directory.
150 mode_set:
151         movw    %ax, %bx
152         subb    $VIDEO_FIRST_VESA>>8, %bh
153         cmpb    $2, %bh
154         jb      check_vesa
155
156 setbad:
157         clc
158         ret
159
160 check_vesa:
161         orw     $0x4000, %bx                    # Use linear frame buffer
162         movw    $0x4f02, %ax                    # VESA BIOS mode set call
163         int     $0x10
164         cmpw    $0x004f, %ax                    # AL=4f if implemented
165         jnz     setbad                          # AH=0 if OK
166
167         stc
168         ret
169
170         .code32
171         ALIGN
172
173 .org    0x800
174 wakeup_stack_begin:     # Stack grows down
175
176 .org    0xff0           # Just below end of page
177 wakeup_stack:
178 ENTRY(wakeup_end)
179         
180 .org    0x1000
181
182 wakeup_pmode_return:
183         movw    $__KERNEL_DS, %ax
184         movw    %ax, %ss
185         movw    %ax, %ds
186         movw    %ax, %es
187         movw    %ax, %fs
188         movw    %ax, %gs
189
190         # reload the gdt, as we need the full 32 bit address
191         lgdt    saved_gdt
192         lidt    saved_idt
193         lldt    saved_ldt
194         ljmp    $(__KERNEL_CS),$1f
195 1:
196         movl    %cr3, %eax
197         movl    %eax, %cr3
198         wbinvd
199
200         # and restore the stack ... but you need gdt for this to work
201         movl    saved_context_esp, %esp
202
203         movl    %cs:saved_magic, %eax
204         cmpl    $0x12345678, %eax
205         jne     bogus_magic
206
207         # jump to place where we left off
208         movl    saved_eip,%eax
209         jmp     *%eax
210
211 bogus_magic:
212         jmp     bogus_magic
213
214
215 ##
216 # acpi_copy_wakeup_routine
217 #
218 # Copy the above routine to low memory.
219 #
220 # Parameters:
221 # %eax: place to copy wakeup routine to
222 #
223 # Returned address is location of code in low memory (past data and stack)
224 #
225 ENTRY(acpi_copy_wakeup_routine)
226
227         pushl   %ebx
228         sgdt    saved_gdt
229         sidt    saved_idt
230         sldt    saved_ldt
231         str     saved_tss
232
233         movl    nx_enabled, %edx
234         movl    %edx, real_efer_save_restore - wakeup_start (%eax)
235         testl   $1, real_efer_save_restore - wakeup_start (%eax)
236         jz      2f
237         # save efer setting
238         pushl   %eax
239         movl    %eax, %ebx
240         mov     $0xc0000080, %ecx
241         rdmsr
242         movl    %edx, real_save_efer_edx - wakeup_start (%ebx)
243         movl    %eax, real_save_efer_eax - wakeup_start (%ebx)
244         popl    %eax
245 2:
246
247         movl    %cr3, %edx
248         movl    %edx, real_save_cr3 - wakeup_start (%eax)
249         movl    %cr4, %edx
250         movl    %edx, real_save_cr4 - wakeup_start (%eax)
251         movl    %cr0, %edx
252         movl    %edx, real_save_cr0 - wakeup_start (%eax)
253         sgdt    real_save_gdt - wakeup_start (%eax)
254
255         movl    saved_videomode, %edx
256         movl    %edx, video_mode - wakeup_start (%eax)
257         movl    acpi_realmode_flags, %edx
258         movl    %edx, realmode_flags - wakeup_start (%eax)
259         movl    $0x12345678, real_magic - wakeup_start (%eax)
260         movl    $0x12345678, saved_magic
261         popl    %ebx
262         ret
263
264 save_registers:
265         leal    4(%esp), %eax
266         movl    %eax, saved_context_esp
267         movl %ebx, saved_context_ebx
268         movl %ebp, saved_context_ebp
269         movl %esi, saved_context_esi
270         movl %edi, saved_context_edi
271         pushfl ; popl saved_context_eflags
272
273         movl $ret_point, saved_eip
274         ret
275
276
277 restore_registers:
278         movl saved_context_ebp, %ebp
279         movl saved_context_ebx, %ebx
280         movl saved_context_esi, %esi
281         movl saved_context_edi, %edi
282         pushl saved_context_eflags ; popfl
283         ret     
284
285 ENTRY(do_suspend_lowlevel)
286         call    save_processor_state
287         call    save_registers
288         pushl   $3
289         call    acpi_enter_sleep_state
290         addl    $4, %esp
291
292 #       In case of S3 failure, we'll emerge here.  Jump
293 #       to ret_point to recover
294         jmp     ret_point
295         .p2align 4,,7
296 ret_point:
297         call    restore_registers
298         call    restore_processor_state
299         ret
300
301 .data
302 ALIGN
303 ENTRY(saved_magic)      .long   0
304 ENTRY(saved_eip)        .long   0
305
306 # saved registers
307 saved_gdt:      .long   0,0
308 saved_idt:      .long   0,0
309 saved_ldt:      .long   0
310 saved_tss:      .long   0
311