xref: /freebsd/sys/amd64/acpica/acpi_wakecode.S (revision 7fdf597e96a02165cfe22ff357b857d5fa15ed8a)
1/*-
2 * Copyright (c) 2001 Takanori Watanabe <takawata@jp.freebsd.org>
3 * Copyright (c) 2001 Mitsuru IWASAKI <iwasaki@jp.freebsd.org>
4 * Copyright (c) 2003 Peter Wemm
5 * Copyright (c) 2008-2012 Jung-uk Kim <jkim@FreeBSD.org>
6 * All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 *    notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 *    notice, this list of conditions and the following disclaimer in the
15 *    documentation and/or other materials provided with the distribution.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27 * SUCH DAMAGE.
28 */
29
30#include <machine/asmacros.h>
31#include <machine/specialreg.h>
32#include <x86/ppireg.h>
33#include <x86/timerreg.h>
34
35#include "assym.inc"
36
37/*
38 * Resume entry point for real mode.
39 *
40 * If XFirmwareWakingVector is zero and FirmwareWakingVector is non-zero
41 * in FACS, the BIOS enters here in real mode after POST with CS set to
42 * (FirmwareWakingVector >> 4) and IP set to (FirmwareWakingVector & 0xf).
43 * Depending on the previous sleep state, we may need to initialize more
44 * of the system (i.e., S3 suspend-to-RAM vs. S4 suspend-to-disk).
45 *
46 * Note: If XFirmwareWakingVector is non-zero, it should disable address
47 * translation/paging and interrupts, load all segment registers with
48 * a flat 4 GB address space, and set EFLAGS.IF to zero.  Currently
49 * this mode is not supported by this code.
50 */
51
52	.data				/* So we can modify it */
53
54	ALIGN_TEXT
55	.code16
56wakeup_start:
57	/*
58	 * Set up segment registers for real mode, a small stack for
59	 * any calls we make, and clear any flags.
60	 */
61	cli				/* make sure no interrupts */
62	mov	%cs, %ax		/* copy %cs to %ds.  Remember these */
63	mov	%ax, %ds		/* are offsets rather than selectors */
64	mov	%ax, %ss
65	movw	$PAGE_SIZE, %sp
66	xorw	%ax, %ax
67	pushw	%ax
68	popfw
69
70	/* To debug resume hangs, beep the speaker if the user requested. */
71	testb	$~0, resume_beep - wakeup_start
72	jz	1f
73	movb	$0, resume_beep - wakeup_start
74
75	/* Set PIC timer2 to beep. */
76	movb	$(TIMER_SEL2 | TIMER_SQWAVE | TIMER_16BIT), %al
77	outb	%al, $TIMER_MODE
78
79	/* Turn on speaker. */
80	inb	$IO_PPI, %al
81	orb	$PIT_SPKR, %al
82	outb	%al, $IO_PPI
83
84	/* Set frequency. */
85	movw	$0x4c0, %ax
86	outb	%al, $TIMER_CNTR2
87	shrw	$8, %ax
88	outb	%al, $TIMER_CNTR2
891:
90
91	/* Re-initialize video BIOS if the reset_video tunable is set. */
92	testb	$~0, reset_video - wakeup_start
93	jz	1f
94	movb	$0, reset_video - wakeup_start
95	lcall	$0xc000, $3
96
97	/* When we reach here, int 0x10 should be ready.  Hide cursor. */
98	movb	$0x01, %ah
99	movb	$0x20, %ch
100	int	$0x10
101
102	/* Re-start in case the previous BIOS call clobbers them. */
103	jmp	wakeup_start
1041:
105
106	/*
107	 * Find relocation base and patch the gdt descript and ljmp targets
108	 */
109	xorl	%ebx, %ebx
110	mov	%cs, %bx
111	sall	$4, %ebx		/* %ebx is now our relocation base */
112
113	/*
114	 * Load the descriptor table pointer.  We'll need it when running
115	 * in 16-bit protected mode.
116	 */
117	lgdtl	bootgdtdesc - wakeup_start
118
119	/* Enable protected mode */
120	movl	$CR0_PE, %eax
121	mov	%eax, %cr0
122
123	/*
124	 * Now execute a far jump to turn on protected mode.  This
125	 * causes the segment registers to turn into selectors and causes
126	 * %cs to be loaded from the gdt.
127	 *
128	 * The following instruction is:
129	 * ljmpl $bootcode32 - bootgdt, $wakeup_32 - wakeup_start
130	 * but gas cannot assemble that.  And besides, we patch the targets
131	 * in early startup and its a little clearer what we are patching.
132	 */
133wakeup_sw32:
134	.byte	0x66			/* size override to 32 bits */
135	.byte	0xea			/* opcode for far jump */
136	.long	wakeup_32 - wakeup_start /* offset in segment */
137	.word	bootcode32 - bootgdt	/* index in gdt for 32 bit code */
138
139	/*
140	 * At this point, we are running in 32 bit legacy protected mode.
141	 */
142	ALIGN_TEXT
143	.code32
144wakeup_32:
145
146	mov	$bootdata32 - bootgdt, %eax
147	mov	%ax, %ds
148
149	/*
150	 * Turn on the PAE bit and optionally the LA57 bit for when paging
151	 * is later enabled.
152	 */
153	mov	%cr4, %eax
154	orl	$CR4_PAE, %eax
155	leal	wakeup_pagetables - wakeup_start(%ebx), %ecx
156	movl	(%ecx), %ecx
157	testl	$0x1, %ecx
158	je	1f
159	orl	$CR4_LA57, %eax
1601:	mov	%eax, %cr4
161
162	/*
163	 * Enable EFER.LME so that we get long mode when all the prereqs are
164	 * in place.  In this case, it turns on when CR0_PG is finally enabled.
165	 * Also it picks up a few other EFER bits that we'll use need we're
166	 * here, like SYSCALL and NX enable.
167	 */
168	movl	$MSR_EFER, %ecx
169	movl	wakeup_efer - wakeup_start(%ebx), %eax
170	movl	wakeup_efer + 4 - wakeup_start(%ebx), %edx
171	wrmsr
172
173	/*
174	 * Point to the embedded page tables for startup.  Note that this
175	 * only gets accessed after we're actually in 64 bit mode, however
176	 * we can only set the bottom 32 bits of %cr3 in this state.  This
177	 * means we are required to use a temporary page table that is below
178	 * the 4GB limit.  %ebx is still our relocation base.  We could just
179	 * subtract 3 * PAGE_SIZE, but that would be too easy.
180	 */
181	leal	wakeup_pagetables - wakeup_start(%ebx), %eax
182	movl	(%eax), %eax
183	andl	$~0x1, %eax
184	mov	%eax, %cr3
185
186	/*
187	 * Finally, switch to long bit mode by enabling paging.  We have
188	 * to be very careful here because all the segmentation disappears
189	 * out from underneath us.  The spec says we can depend on the
190	 * subsequent pipelined branch to execute, but *only if* everything
191	 * is still identity mapped.  If any mappings change, the pipeline
192	 * will flush.
193	 */
194	mov	%cr0, %eax
195	orl	$CR0_PG, %eax
196	mov	%eax, %cr0
197
198	/*
199	 * At this point paging is enabled, and we are in "compatibility" mode.
200	 * We do another far jump to reload %cs with the 64 bit selector.
201	 * %cr3 points to a 4-level page table page.
202	 * We cannot yet jump all the way to the kernel because we can only
203	 * specify a 32 bit linear address.  So, yet another trampoline.
204	 *
205	 * The following instruction is:
206	 * ljmp $bootcode64 - bootgdt, $wakeup_64 - wakeup_start
207	 * but gas cannot assemble that.  And besides, we patch the targets
208	 * in early startup and its a little clearer what we are patching.
209	 */
210wakeup_sw64:
211	.byte	0xea			/* opcode for far jump */
212	.long	wakeup_64 - wakeup_start /* offset in segment */
213	.word	bootcode64 - bootgdt	/* index in gdt for 64 bit code */
214
215	/*
216	 * Yeehar!  We're running in 64-bit mode!  We can mostly ignore our
217	 * segment registers, and get on with it.
218	 * Note that we are running at the correct virtual address, but with
219	 * a 1:1 1GB mirrored mapping over entire address space.  We had better
220	 * switch to a real %cr3 promptly so that we can get to the direct map
221	 * space. Remember that jmp is relative and that we've been relocated,
222	 * so use an indirect jump.
223	 */
224	ALIGN_TEXT
225	.code64
226wakeup_64:
227	mov	$bootdata64 - bootgdt, %eax
228	mov	%ax, %ds
229
230	/* Restore arguments. */
231	movq	wakeup_pcb - wakeup_start(%rbx), %rdi
232	movq	wakeup_ret - wakeup_start(%rbx), %rax
233
234	/* Restore GDT. */
235	lgdt	wakeup_gdt - wakeup_start(%rbx)
236
237	/* Jump to return address. */
238	jmp	*%rax
239
240	.data
241
242resume_beep:
243	.byte	0
244reset_video:
245	.byte	0
246
247	ALIGN_DATA
248bootgdt:
249	.long	0x00000000
250	.long	0x00000000
251	.long	0x00000000
252	.long	0x00000000
253	.long	0x00000000
254	.long	0x00000000
255	.long	0x00000000
256	.long	0x00000000
257
258bootcode64:
259	.long	0x0000ffff
260	.long	0x00af9b00
261
262bootdata64:
263	.long	0x0000ffff
264	.long	0x00af9300
265
266bootcode32:
267	.long	0x0000ffff
268	.long	0x00cf9b00
269
270bootdata32:
271	.long	0x0000ffff
272	.long	0x00cf9300
273bootgdtend:
274
275wakeup_pagetables:
276	.long	0
277
278bootgdtdesc:
279	.word	bootgdtend - bootgdt	/* Length */
280	.long	bootgdt - wakeup_start	/* Offset plus %ds << 4 */
281
282	ALIGN_DATA
283wakeup_pcb:
284	.quad	0
285wakeup_ret:
286	.quad	0
287wakeup_efer:
288	.quad	0
289wakeup_gdt:
290	.word	0
291	.quad	0
292dummy:
293