Merge Linux 2.6.23
[sfrench/cifs-2.6.git] / 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         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
155 # For now, we only handle VESA modes (0x0200..0x03ff).  To handle other
156 # modes, we should probably compile in the video code from the boot
157 # directory.
158 mode_set:
159         movw    %ax, %bx
160         subb    $VIDEO_FIRST_VESA>>8, %bh
161         cmpb    $2, %bh
162         jb      check_vesa
163
164 setbad:
165         clc
166         ret
167
168 check_vesa:
169         orw     $0x4000, %bx                    # Use linear frame buffer
170         movw    $0x4f02, %ax                    # VESA BIOS mode set call
171         int     $0x10
172         cmpw    $0x004f, %ax                    # AL=4f if implemented
173         jnz     setbad                          # AH=0 if OK
174
175         stc
176         ret
177
178         .code32
179         ALIGN
180
181 .org    0x800
182 wakeup_stack_begin:     # Stack grows down
183
184 .org    0xff0           # Just below end of page
185 wakeup_stack:
186 ENTRY(wakeup_end)
187         
188 .org    0x1000
189
190 wakeup_pmode_return:
191         movw    $__KERNEL_DS, %ax
192         movw    %ax, %ss
193         movw    %ax, %ds
194         movw    %ax, %es
195         movw    %ax, %fs
196         movw    %ax, %gs
197         movw    $0x0e00 + 'u', 0xb8016
198
199         # reload the gdt, as we need the full 32 bit address
200         lgdt    saved_gdt
201         lidt    saved_idt
202         lldt    saved_ldt
203         ljmp    $(__KERNEL_CS),$1f
204 1:
205         movl    %cr3, %eax
206         movl    %eax, %cr3
207         wbinvd
208
209         # and restore the stack ... but you need gdt for this to work
210         movl    saved_context_esp, %esp
211
212         movl    %cs:saved_magic, %eax
213         cmpl    $0x12345678, %eax
214         jne     bogus_magic
215
216         # jump to place where we left off
217         movl    saved_eip,%eax
218         jmp     *%eax
219
220 bogus_magic:
221         movw    $0x0e00 + 'B', 0xb8018
222         jmp     bogus_magic
223
224
225 ##
226 # acpi_copy_wakeup_routine
227 #
228 # Copy the above routine to low memory.
229 #
230 # Parameters:
231 # %eax: place to copy wakeup routine to
232 #
233 # Returned address is location of code in low memory (past data and stack)
234 #
235 ENTRY(acpi_copy_wakeup_routine)
236
237         pushl   %ebx
238         sgdt    saved_gdt
239         sidt    saved_idt
240         sldt    saved_ldt
241         str     saved_tss
242
243         movl    nx_enabled, %edx
244         movl    %edx, real_efer_save_restore - wakeup_start (%eax)
245         testl   $1, real_efer_save_restore - wakeup_start (%eax)
246         jz      2f
247         # save efer setting
248         pushl   %eax
249         movl    %eax, %ebx
250         mov     $0xc0000080, %ecx
251         rdmsr
252         movl    %edx, real_save_efer_edx - wakeup_start (%ebx)
253         movl    %eax, real_save_efer_eax - wakeup_start (%ebx)
254         popl    %eax
255 2:
256
257         movl    %cr3, %edx
258         movl    %edx, real_save_cr3 - wakeup_start (%eax)
259         movl    %cr4, %edx
260         movl    %edx, real_save_cr4 - wakeup_start (%eax)
261         movl    %cr0, %edx
262         movl    %edx, real_save_cr0 - wakeup_start (%eax)
263         sgdt    real_save_gdt - wakeup_start (%eax)
264
265         movl    saved_videomode, %edx
266         movl    %edx, video_mode - wakeup_start (%eax)
267         movl    acpi_realmode_flags, %edx
268         movl    %edx, realmode_flags - wakeup_start (%eax)
269         movl    $0x12345678, real_magic - wakeup_start (%eax)
270         movl    $0x12345678, saved_magic
271         popl    %ebx
272         ret
273
274 save_registers:
275         leal    4(%esp), %eax
276         movl    %eax, saved_context_esp
277         movl %ebx, saved_context_ebx
278         movl %ebp, saved_context_ebp
279         movl %esi, saved_context_esi
280         movl %edi, saved_context_edi
281         pushfl ; popl saved_context_eflags
282
283         movl $ret_point, saved_eip
284         ret
285
286
287 restore_registers:
288         movl saved_context_ebp, %ebp
289         movl saved_context_ebx, %ebx
290         movl saved_context_esi, %esi
291         movl saved_context_edi, %edi
292         pushl saved_context_eflags ; popfl
293         ret     
294
295 ENTRY(do_suspend_lowlevel)
296         call    save_processor_state
297         call    save_registers
298         pushl   $3
299         call    acpi_enter_sleep_state
300         addl    $4, %esp
301
302 #       In case of S3 failure, we'll emerge here.  Jump
303 #       to ret_point to recover
304         jmp     ret_point
305         .p2align 4,,7
306 ret_point:
307         call    restore_registers
308         call    restore_processor_state
309         ret
310
311 .data
312 ALIGN
313 ENTRY(saved_magic)      .long   0
314 ENTRY(saved_eip)        .long   0
315
316 # saved registers
317 saved_gdt:      .long   0,0
318 saved_idt:      .long   0,0
319 saved_ldt:      .long   0
320 saved_tss:      .long   0
321