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