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