xref: /freebsd/sys/i386/acpica/acpi_wakecode.S (revision 39beb93c)
1/*-
2 * Copyright (c) 2001 Takanori Watanabe <takawata@jp.freebsd.org>
3 * Copyright (c) 2001 Mitsuru IWASAKI <iwasaki@jp.freebsd.org>
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 *    notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 *    notice, this list of conditions and the following disclaimer in the
13 *    documentation and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25 * SUCH DAMAGE.
26 *
27 * $FreeBSD$
28 */
29
30#define LOCORE
31
32#include <machine/asmacros.h>
33#include <machine/specialreg.h>
34
35#include "assym.s"
36
37/*
38 * Resume entry point.  The BIOS enters here in real mode after POST with
39 * CS set to the page where we stored this code.  It should configure the
40 * segment registers with a flat 4 GB address space and EFLAGS.IF = 0.
41 * Depending on the previous sleep state, we may need to initialize more
42 * of the system (i.e., S3 suspend-to-RAM vs. S4 suspend-to-disk).
43 */
44	.align 4
45	.code16
46wakeup_16:
47	nop
48	cli
49	cld
50
51	/*
52	 * Set up segment registers for real mode, a small stack for
53	 * any calls we make, and clear any flags.
54	 */
55	movw	%cs,%ax
56	movw	%ax,%ds
57	movw	%ax,%ss
58	movw	$PAGE_SIZE,%sp
59	pushl	$0
60	popfl
61
62	/* To debug resume hangs, beep the speaker if the user requested. */
63	cmpl	$1,resume_beep
64	jne	nobeep
65	movb	$0xc0,%al
66	outb	%al,$0x42
67	movb	$0x04,%al
68	outb	%al,$0x42
69	inb	$0x61,%al
70	orb	$0x3,%al
71	outb	%al,$0x61
72nobeep:
73
74	/* Re-initialize video BIOS if the reset_video tunable is set. */
75	cmpl	$1,reset_video
76	jne	nobiosreset
77	lcall	$0xc000,$3
78
79	/*
80	 * Set up segment registers for real mode again in case the
81	 * previous BIOS call clobbers them.
82	 */
83	movw	%cs,%ax
84	movw	%ax,%ds
85	movw	%ax,%ss
86nobiosreset:
87
88	/* Load GDT for real mode.  Use 32 bit prefix for addresses >16 MB. */
89	lgdtl	physical_gdt
90
91	/* Restore CR2, CR3 and CR4 */
92	movl	previous_cr2,%eax
93	movl	%eax,%cr2
94	movl	previous_cr3,%eax
95	movl	%eax,%cr3
96	movl	previous_cr4,%eax
97	movl	%eax,%cr4
98
99	/* Transfer some values to protected mode with an inline stack */
100#define NVALUES	9
101#define TRANSFER_STACK32(val, idx)	\
102	movl	val,%eax;		\
103	movl	%eax,wakeup_32stack+(idx+1)+(idx*4)
104
105	TRANSFER_STACK32(previous_ss,		(NVALUES - 9))
106	TRANSFER_STACK32(previous_fs,		(NVALUES - 8))
107	TRANSFER_STACK32(previous_ds,		(NVALUES - 7))
108	TRANSFER_STACK32(physical_gdt+2,	(NVALUES - 6))
109	TRANSFER_STACK32(where_to_recover,	(NVALUES - 5))
110	TRANSFER_STACK32(previous_idt+2,	(NVALUES - 4))
111	TRANSFER_STACK32(previous_ldt,		(NVALUES - 3))
112	TRANSFER_STACK32(previous_gdt+2,	(NVALUES - 2))
113	TRANSFER_STACK32(previous_tr,		(NVALUES - 1))
114	TRANSFER_STACK32(previous_cr0,		(NVALUES - 0))
115
116	mov	physical_esp,%esi	/* to be used in 32bit code */
117
118	/* Enable protected mode */
119	movl	%cr0,%eax
120	orl	$(CR0_PE),%eax
121	movl	%eax,%cr0
122
123wakeup_sw32:
124	/* Switch to protected mode by intersegmental jump */
125	ljmpl	$KCSEL,$0x12345678	/* Code location, to be replaced */
126
127	/*
128	 * Now switched to protected mode without paging enabled.
129	 *	%esi: KERNEL stack pointer (physical address)
130	 */
131	.code32
132wakeup_32:
133	nop
134
135	/* Set up segment registers for protected mode */
136	movw	$KDSEL,%ax		/* KDSEL to segment registers */
137	movw	%ax,%ds
138	movw	%ax,%es
139	movw	%ax,%gs
140	movw	%ax,%ss
141	movw	$KPSEL,%ax		/* KPSEL to %fs */
142	movw	%ax,%fs
143	movl	%esi,%esp		/* physical address stack pointer */
144
145wakeup_32stack:
146	/* Operands are overwritten in 16 bit code by TRANSFER_STACK32 macro */
147	pushl	$0xabcdef09		/* ss + dummy */
148	pushl	$0xabcdef08		/* fs + gs */
149	pushl	$0xabcdef07		/* ds + es */
150	pushl	$0xabcdef06		/* gdt:base (physical address) */
151	pushl	$0xabcdef05		/* recover address */
152	pushl	$0xabcdef04		/* idt:base */
153	pushl	$0xabcdef03		/* ldt + idt:limit */
154	pushl	$0xabcdef02		/* gdt:base */
155	pushl	$0xabcdef01		/* TR + gdt:limit */
156	pushl	$0xabcdef00		/* CR0 */
157
158	movl	%esp,%ebp
159#define CR0_REGISTER		0(%ebp)
160#define TASK_REGISTER		4(%ebp)
161#define PREVIOUS_GDT		6(%ebp)
162#define PREVIOUS_LDT		12(%ebp)
163#define PREVIOUS_IDT		14(%ebp)
164#define RECOVER_ADDR		20(%ebp)
165#define PHYSICAL_GDT_BASE	24(%ebp)
166#define PREVIOUS_DS		28(%ebp)
167#define PREVIOUS_ES		30(%ebp)
168#define PREVIOUS_FS		32(%ebp)
169#define PREVIOUS_GS		34(%ebp)
170#define PREVIOUS_SS		36(%ebp)
171
172	/* Fixup TSS type field */
173#define TSS_TYPEFIX_MASK	0xf9
174	xorl	%esi,%esi
175	movl	PHYSICAL_GDT_BASE,%ebx
176	movw	TASK_REGISTER,%si
177	leal	(%ebx,%esi),%eax	/* get TSS segment descriptor */
178	andb	$TSS_TYPEFIX_MASK,5(%eax)
179
180	/* Prepare to return to sleep/wakeup code point */
181	lgdtl	PREVIOUS_GDT
182	lidtl	PREVIOUS_IDT
183
184	/* Pack values from the GDT to be loaded into segment registers. */
185	movl	PREVIOUS_DS,%ebx
186	movl	PREVIOUS_FS,%ecx
187	movl	PREVIOUS_SS,%edx
188	movw	TASK_REGISTER,%si
189	shll	$16,%esi
190	movw	PREVIOUS_LDT,%si
191	movl	RECOVER_ADDR,%edi
192
193	/* Enable paging and etc. */
194	movl	CR0_REGISTER,%eax
195	movl	%eax,%cr0
196
197	/* Flush the prefetch queue */
198	jmp	1f
1991:	jmp	1f
2001:
201
202	/*
203	 * Now we are in kernel virtual memory addressing with the following
204	 * original register values:
205	 *	%ebx: ds + es
206	 *	%ecx: fs + gs
207	 *	%edx: ss + dummy
208	 *	%esi: LDTR + TR
209	 *	%edi: recover address
210	 * We'll load these back into the segment registers now.
211	 */
212	nop
213
214	movl	%esi,%eax		/* LDTR + TR */
215	lldt	%ax			/* load LDT register */
216	shrl	$16,%eax
217	ltr	%ax			/* load task register */
218
219	/* Restore segment registers */
220	movl	%ebx,%eax		/* ds + es */
221	movw	%ax,%ds
222	shrl	$16,%eax
223	movw	%ax,%es
224	movl	%ecx,%eax		/* fs + gs */
225	movw	%ax,%fs
226	shrl	$16,%eax
227	movw	%ax,%gs
228	movl	%edx,%eax		/* ss */
229	movw	%ax,%ss
230
231	/* Jump to acpi_restorecpu() */
232	jmp	*%edi
233
234/* used in real mode */
235physical_gdt:		.word 0
236			.long 0
237physical_esp:		.long 0
238previous_cr2:		.long 0
239previous_cr3:		.long 0
240previous_cr4:		.long 0
241resume_beep:		.long 0
242reset_video:		.long 0
243
244/*
245 * Transfer from real mode to protected mode.  The order of these variables
246 * is very important, DO NOT INSERT OR CHANGE unless you know why.
247 */
248previous_cr0:		.long 0
249previous_tr:		.word 0
250previous_gdt:		.word 0
251			.long 0
252previous_ldt:		.word 0
253previous_idt:		.word 0
254			.long 0
255where_to_recover:	.long 0
256previous_ds:		.word 0
257previous_es:		.word 0
258previous_fs:		.word 0
259previous_gs:		.word 0
260previous_ss:		.word 0
261dummy:			.word 0
262