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