acpi_wakecode.S revision 189903
1232366Sdavide/*-
2232366Sdavide * Copyright (c) 2001 Takanori Watanabe <takawata@jp.freebsd.org>
3232366Sdavide * Copyright (c) 2001 Mitsuru IWASAKI <iwasaki@jp.freebsd.org>
4232366Sdavide * Copyright (c) 2003 Peter Wemm
5232366Sdavide * Copyright (c) 2008 Jung-uk Kim <jkim@FreeBSD.org>
6232366Sdavide * All rights reserved.
7232366Sdavide *
8232366Sdavide * Redistribution and use in source and binary forms, with or without
9232366Sdavide * modification, are permitted provided that the following conditions
10232366Sdavide * are met:
11232366Sdavide * 1. Redistributions of source code must retain the above copyright
12232366Sdavide *    notice, this list of conditions and the following disclaimer.
13232366Sdavide * 2. Redistributions in binary form must reproduce the above copyright
14232366Sdavide *    notice, this list of conditions and the following disclaimer in the
15232366Sdavide *    documentation and/or other materials provided with the distribution.
16232366Sdavide *
17232366Sdavide * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18232366Sdavide * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19232366Sdavide * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20232366Sdavide * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21232366Sdavide * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22232366Sdavide * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23232366Sdavide * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24232366Sdavide * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25232366Sdavide * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26232366Sdavide * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27241741Ssbruno * SUCH DAMAGE.
28232366Sdavide *
29232366Sdavide * $FreeBSD: head/sys/amd64/acpica/acpi_wakecode.S 189903 2009-03-17 00:48:11Z jkim $
30232366Sdavide */
31232366Sdavide
32232366Sdavide#define LOCORE
33232366Sdavide
34232366Sdavide#include <machine/asmacros.h>
35232366Sdavide#include <machine/specialreg.h>
36232366Sdavide
37232366Sdavide#include "assym.s"
38232366Sdavide
39232366Sdavide/*
40232366Sdavide * Resume entry point for real mode.
41232366Sdavide *
42232366Sdavide * If XFirmwareWakingVector is zero and FirmwareWakingVector is non-zero
43232366Sdavide * in FACS, the BIOS enters here in real mode after POST with CS set to
44232366Sdavide * (FirmwareWakingVector >> 4) and IP set to (FirmwareWakingVector & 0xf).
45232366Sdavide * Depending on the previous sleep state, we may need to initialize more
46232366Sdavide * of the system (i.e., S3 suspend-to-RAM vs. S4 suspend-to-disk).
47232366Sdavide *
48232366Sdavide * Note: If XFirmwareWakingVector is non-zero, it should disable address
49232366Sdavide * translation/paging and interrupts, load all segment registers with
50232366Sdavide * a flat 4 GB address space, and set EFLAGS.IF to zero.  Currently
51232366Sdavide * this mode is not supported by this code.
52232366Sdavide */
53232366Sdavide
54232366Sdavide	.data				/* So we can modify it */
55232366Sdavide
56232366Sdavide	ALIGN_TEXT
57232366Sdavidewakeup_start:
58232366Sdavide	.code16
59232366Sdavide	/*
60232366Sdavide	 * Set up segment registers for real mode, a small stack for
61232366Sdavide	 * any calls we make, and clear any flags.
62232366Sdavide	 */
63232366Sdavide	cli				/* make sure no interrupts */
64232366Sdavide	cld
65232366Sdavide	mov	%cs, %ax		/* copy %cs to %ds.  Remember these */
66232366Sdavide	mov	%ax, %ds		/* are offsets rather than selectors */
67232366Sdavide	mov	%ax, %ss
68232366Sdavide	movw	$PAGE_SIZE - 8, %sp
69232366Sdavide	pushw	$0
70232366Sdavide	popfw
71232366Sdavide
72232366Sdavide	/* To debug resume hangs, beep the speaker if the user requested. */
73232366Sdavide	cmpw	$0, resume_beep - wakeup_start
74232366Sdavide	je	1f
75232366Sdavide	movb	$0xc0, %al
76232366Sdavide	outb	%al, $0x42
77232366Sdavide	movb	$0x04, %al
78232366Sdavide	outb	%al, $0x42
79232366Sdavide	inb	$0x61, %al
80232366Sdavide	orb	$0x3, %al
81232366Sdavide	outb	%al, $0x61
82232366Sdavide	movw	$0, resume_beep - wakeup_start
83232366Sdavide1:
84232366Sdavide
85232366Sdavide	/* Re-initialize video BIOS if the reset_video tunable is set. */
86232366Sdavide	cmpw	$0, reset_video - wakeup_start
87232366Sdavide	je	1f
88232366Sdavide	lcall	$0xc000, $3
89232366Sdavide	movw	$0, reset_video - wakeup_start
90232366Sdavide
91232366Sdavide	/*
92232366Sdavide	 * Set up segment registers for real mode again in case the
93232366Sdavide	 * previous BIOS call clobbers them.
94232366Sdavide	 */
95232366Sdavide	mov	%cs, %ax
96232366Sdavide	mov	%ax, %ds
97232366Sdavide	mov	%ax, %ss
98232366Sdavide1:
99232366Sdavide
100232366Sdavide	/*
101232366Sdavide	 * Find relocation base and patch the gdt descript and ljmp targets
102232366Sdavide	 */
103232366Sdavide	xorl	%ebx, %ebx
104232366Sdavide	mov	%cs, %bx
105232366Sdavide	sall	$4, %ebx		/* %ebx is now our relocation base */
106232366Sdavide
107232366Sdavide	/*
108232366Sdavide	 * Load the descriptor table pointer.  We'll need it when running
109232366Sdavide	 * in 16-bit protected mode.
110232366Sdavide	 */
111232366Sdavide	lgdtl	bootgdtdesc - wakeup_start
112232366Sdavide
113232366Sdavide	/* Enable protected mode */
114232377Spluknet	movl	$CR0_PE, %eax
115232366Sdavide	mov	%eax, %cr0
116232366Sdavide
117232366Sdavide	/*
118232366Sdavide	 * Now execute a far jump to turn on protected mode.  This
119232366Sdavide	 * causes the segment registers to turn into selectors and causes
120232366Sdavide	 * %cs to be loaded from the gdt.
121232366Sdavide	 *
122232366Sdavide	 * The following instruction is:
123232366Sdavide	 * ljmpl $bootcode32 - bootgdt, $wakeup_32 - wakeup_start
124232366Sdavide	 * but gas cannot assemble that.  And besides, we patch the targets
125232366Sdavide	 * in early startup and its a little clearer what we are patching.
126232366Sdavide	 */
127232366Sdavidewakeup_sw32:
128232366Sdavide	.byte	0x66			/* size override to 32 bits */
129232377Spluknet	.byte	0xea			/* opcode for far jump */
130232366Sdavide	.long	wakeup_32 - wakeup_start /* offset in segment */
131232366Sdavide	.word	bootcode32 - bootgdt	/* index in gdt for 32 bit code */
132232366Sdavide
133232377Spluknet	/*
134232366Sdavide	 * At this point, we are running in 32 bit legacy protected mode.
135232366Sdavide	 */
136232366Sdavide	.code32
137232377Spluknetwakeup_32:
138232366Sdavide
139232366Sdavide	mov	$bootdata32 - bootgdt, %eax
140232366Sdavide	mov	%ax, %ds
141232366Sdavide
142232366Sdavide	/* Turn on the PAE and PSE bits for when paging is enabled */
143232366Sdavide	mov	%cr4, %eax
144232377Spluknet	orl	$(CR4_PAE | CR4_PSE), %eax
145232366Sdavide	mov	%eax, %cr4
146232366Sdavide
147232366Sdavide	/*
148232377Spluknet	 * Enable EFER.LME so that we get long mode when all the prereqs are
149232366Sdavide	 * in place.  In this case, it turns on when CR0_PG is finally enabled.
150232366Sdavide	 * Pick up a few other EFER bits that we'll use need we're here.
151232366Sdavide	 */
152232377Spluknet	movl	$MSR_EFER, %ecx
153232366Sdavide	rdmsr
154232366Sdavide	orl	$EFER_LME | EFER_SCE, %eax
155232366Sdavide	wrmsr
156232366Sdavide
157232377Spluknet	/*
158232366Sdavide	 * Point to the embedded page tables for startup.  Note that this
159232366Sdavide	 * only gets accessed after we're actually in 64 bit mode, however
160232366Sdavide	 * we can only set the bottom 32 bits of %cr3 in this state.  This
161232377Spluknet	 * means we are required to use a temporary page table that is below
162232366Sdavide	 * the 4GB limit.  %ebx is still our relocation base.  We could just
163232366Sdavide	 * subtract 3 * PAGE_SIZE, but that would be too easy.
164232366Sdavide	 */
165232377Spluknet	leal	wakeup_pagetables - wakeup_start(%ebx), %eax
166232366Sdavide	movl	(%eax), %eax
167232366Sdavide	mov	%eax, %cr3
168232366Sdavide
169232377Spluknet	/*
170232377Spluknet	 * Finally, switch to long bit mode by enabling paging.  We have
171232366Sdavide	 * to be very careful here because all the segmentation disappears
172232366Sdavide	 * out from underneath us.  The spec says we can depend on the
173232366Sdavide	 * subsequent pipelined branch to execute, but *only if* everthing
174232377Spluknet	 * is still identity mapped.  If any mappings change, the pipeline
175232377Spluknet	 * will flush.
176232366Sdavide	 */
177232366Sdavide	mov	%cr0, %eax
178232366Sdavide	orl	$CR0_PG, %eax
179232366Sdavide	mov	%eax, %cr0
180232377Spluknet
181232366Sdavide	/*
182232366Sdavide	 * At this point paging is enabled, and we are in "compatability" mode.
183232366Sdavide	 * We do another far jump to reload %cs with the 64 bit selector.
184232377Spluknet	 * %cr3 points to a 4-level page table page.
185232366Sdavide	 * We cannot yet jump all the way to the kernel because we can only
186232366Sdavide	 * specify a 32 bit linear address.  So, yet another trampoline.
187232366Sdavide	 *
188232366Sdavide	 * The following instruction is:
189232366Sdavide	 * ljmp $bootcode64 - bootgdt, $wakeup_64 - wakeup_start
190232366Sdavide	 * but gas cannot assemble that.  And besides, we patch the targets
191232366Sdavide	 * in early startup and its a little clearer what we are patching.
192232366Sdavide	 */
193232366Sdavidewakeup_sw64:
194232366Sdavide	.byte	0xea			/* opcode for far jump */
195232366Sdavide	.long	wakeup_64 - wakeup_start /* offset in segment */
196232366Sdavide	.word	bootcode64 - bootgdt	/* index in gdt for 64 bit code */
197232366Sdavide
198232366Sdavide	/*
199232366Sdavide	 * Yeehar!  We're running in 64-bit mode!  We can mostly ignore our
200232366Sdavide	 * segment registers, and get on with it.
201232366Sdavide	 * Note that we are running at the correct virtual address, but with
202232366Sdavide	 * a 1:1 1GB mirrored mapping over entire address space.  We had better
203232366Sdavide	 * switch to a real %cr3 promptly so that we can get to the direct map
204232366Sdavide	 * space. Remember that jmp is relative and that we've been relocated,
205232366Sdavide	 * so use an indirect jump.
206232366Sdavide	 */
207232366Sdavide	.code64
208232366Sdavidewakeup_64:
209232366Sdavide	mov	$bootdata64 - bootgdt, %eax
210232366Sdavide	mov	%ax, %ds
211241741Ssbruno
212233628Sfabient	/* Restore arguments and return. */
213232366Sdavide	movq	wakeup_ctx - wakeup_start(%rbx), %rdi
214232366Sdavide	movq	wakeup_kpml4 - wakeup_start(%rbx), %rsi
215232366Sdavide	movq	wakeup_retaddr - wakeup_start(%rbx), %rax
216232366Sdavide	jmp	*%rax
217232366Sdavide
218232366Sdavide	ALIGN_DATA
219232366Sdavidebootgdt:
220232366Sdavide	.long	0x00000000
221232366Sdavide	.long	0x00000000
222232366Sdavide
223232366Sdavidebootcode64:
224232366Sdavide	.long	0x0000ffff
225232366Sdavide	.long	0x00af9b00
226232366Sdavide
227232366Sdavidebootdata64:
228232366Sdavide	.long	0x0000ffff
229232366Sdavide	.long	0x00af9300
230232366Sdavide
231232377Spluknetbootcode32:
232232366Sdavide	.long	0x0000ffff
233232366Sdavide	.long	0x00cf9b00
234232366Sdavide
235bootdata32:
236	.long	0x0000ffff
237	.long	0x00cf9300
238bootgdtend:
239
240wakeup_pagetables:
241	.long	0
242
243bootgdtdesc:
244	.word	bootgdtend - bootgdt	/* Length */
245	.long	bootgdt - wakeup_start	/* Offset plus %ds << 4 */
246
247	ALIGN_DATA
248resume_beep:
249	.long	0
250reset_video:
251	.long	0
252wakeup_retaddr:
253	.quad	0
254wakeup_kpml4:
255	.quad	0
256
257wakeup_ctx:
258	.quad	0
259wakeup_xpcb:
260	.quad	0
261wakeup_gdt:
262	.word	0
263	.quad	0
264wakeup_efer:
265	.quad	0
266wakeup_pat:
267	.quad	0
268wakeup_star:
269	.quad	0
270wakeup_lstar:
271	.quad	0
272wakeup_cstar:
273	.quad	0
274wakeup_sfmask:
275	.quad	0
276wakeup_cpu:
277	.long	0
278dummy:
279