]> CyberLeo.Net >> Repos - FreeBSD/releng/8.1.git/blob - sys/amd64/acpica/acpi_wakecode.S
Copy stable/8 to releng/8.1 in preparation for 8.1-RC1.
[FreeBSD/releng/8.1.git] / sys / amd64 / acpica / acpi_wakecode.S
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-2009 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  * $FreeBSD$
30  */
31
32 #define LOCORE
33
34 #include <machine/asmacros.h>
35 #include <machine/specialreg.h>
36
37 #include "assym.s"
38
39 /*
40  * Resume entry point for real mode.
41  *
42  * If XFirmwareWakingVector is zero and FirmwareWakingVector is non-zero
43  * in FACS, the BIOS enters here in real mode after POST with CS set to
44  * (FirmwareWakingVector >> 4) and IP set to (FirmwareWakingVector & 0xf).
45  * Depending on the previous sleep state, we may need to initialize more
46  * of the system (i.e., S3 suspend-to-RAM vs. S4 suspend-to-disk).
47  *
48  * Note: If XFirmwareWakingVector is non-zero, it should disable address
49  * translation/paging and interrupts, load all segment registers with
50  * a flat 4 GB address space, and set EFLAGS.IF to zero.  Currently
51  * this mode is not supported by this code.
52  */
53
54         .data                           /* So we can modify it */
55
56         ALIGN_TEXT
57 wakeup_start:
58         .code16
59         /*
60          * Set up segment registers for real mode, a small stack for
61          * any calls we make, and clear any flags.
62          */
63         cli                             /* make sure no interrupts */
64         cld
65         mov     %cs, %ax                /* copy %cs to %ds.  Remember these */
66         mov     %ax, %ds                /* are offsets rather than selectors */
67         mov     %ax, %ss
68         movw    $PAGE_SIZE - 8, %sp
69         xorw    %ax, %ax
70         pushw   %ax
71         popfw
72
73         /* To debug resume hangs, beep the speaker if the user requested. */
74         testb   $~0, resume_beep - wakeup_start
75         jz      1f
76         movb    $0, resume_beep - wakeup_start
77         movb    $0xc0, %al
78         outb    %al, $0x42
79         movb    $0x04, %al
80         outb    %al, $0x42
81         inb     $0x61, %al
82         orb     $0x3, %al
83         outb    %al, $0x61
84 1:
85
86         /* Re-initialize video BIOS if the reset_video tunable is set. */
87         testb   $~0, reset_video - wakeup_start
88         jz      1f
89         movb    $0, reset_video - wakeup_start
90         lcall   $0xc000, $3
91
92         /* Re-start in case the previous BIOS call clobbers them. */
93         jmp     wakeup_start
94 1:
95
96         /*
97          * Find relocation base and patch the gdt descript and ljmp targets
98          */
99         xorl    %ebx, %ebx
100         mov     %cs, %bx
101         sall    $4, %ebx                /* %ebx is now our relocation base */
102
103         /*
104          * Load the descriptor table pointer.  We'll need it when running
105          * in 16-bit protected mode.
106          */
107         lgdtl   bootgdtdesc - wakeup_start
108
109         /* Enable protected mode */
110         movl    $CR0_PE, %eax
111         mov     %eax, %cr0
112
113         /*
114          * Now execute a far jump to turn on protected mode.  This
115          * causes the segment registers to turn into selectors and causes
116          * %cs to be loaded from the gdt.
117          *
118          * The following instruction is:
119          * ljmpl $bootcode32 - bootgdt, $wakeup_32 - wakeup_start
120          * but gas cannot assemble that.  And besides, we patch the targets
121          * in early startup and its a little clearer what we are patching.
122          */
123 wakeup_sw32:
124         .byte   0x66                    /* size override to 32 bits */
125         .byte   0xea                    /* opcode for far jump */
126         .long   wakeup_32 - wakeup_start /* offset in segment */
127         .word   bootcode32 - bootgdt    /* index in gdt for 32 bit code */
128
129         /*
130          * At this point, we are running in 32 bit legacy protected mode.
131          */
132         .code32
133 wakeup_32:
134
135         mov     $bootdata32 - bootgdt, %eax
136         mov     %ax, %ds
137
138         /* Turn on the PAE and PSE bits for when paging is enabled */
139         mov     %cr4, %eax
140         orl     $(CR4_PAE | CR4_PSE), %eax
141         mov     %eax, %cr4
142
143         /*
144          * Enable EFER.LME so that we get long mode when all the prereqs are
145          * in place.  In this case, it turns on when CR0_PG is finally enabled.
146          * Pick up a few other EFER bits that we'll use need we're here.
147          */
148         movl    $MSR_EFER, %ecx
149         rdmsr
150         orl     $EFER_LME | EFER_SCE, %eax
151         wrmsr
152
153         /*
154          * Point to the embedded page tables for startup.  Note that this
155          * only gets accessed after we're actually in 64 bit mode, however
156          * we can only set the bottom 32 bits of %cr3 in this state.  This
157          * means we are required to use a temporary page table that is below
158          * the 4GB limit.  %ebx is still our relocation base.  We could just
159          * subtract 3 * PAGE_SIZE, but that would be too easy.
160          */
161         leal    wakeup_pagetables - wakeup_start(%ebx), %eax
162         movl    (%eax), %eax
163         mov     %eax, %cr3
164
165         /*
166          * Finally, switch to long bit mode by enabling paging.  We have
167          * to be very careful here because all the segmentation disappears
168          * out from underneath us.  The spec says we can depend on the
169          * subsequent pipelined branch to execute, but *only if* everthing
170          * is still identity mapped.  If any mappings change, the pipeline
171          * will flush.
172          */
173         mov     %cr0, %eax
174         orl     $CR0_PG, %eax
175         mov     %eax, %cr0
176
177         /*
178          * At this point paging is enabled, and we are in "compatability" mode.
179          * We do another far jump to reload %cs with the 64 bit selector.
180          * %cr3 points to a 4-level page table page.
181          * We cannot yet jump all the way to the kernel because we can only
182          * specify a 32 bit linear address.  So, yet another trampoline.
183          *
184          * The following instruction is:
185          * ljmp $bootcode64 - bootgdt, $wakeup_64 - wakeup_start
186          * but gas cannot assemble that.  And besides, we patch the targets
187          * in early startup and its a little clearer what we are patching.
188          */
189 wakeup_sw64:
190         .byte   0xea                    /* opcode for far jump */
191         .long   wakeup_64 - wakeup_start /* offset in segment */
192         .word   bootcode64 - bootgdt    /* index in gdt for 64 bit code */
193
194         /*
195          * Yeehar!  We're running in 64-bit mode!  We can mostly ignore our
196          * segment registers, and get on with it.
197          * Note that we are running at the correct virtual address, but with
198          * a 1:1 1GB mirrored mapping over entire address space.  We had better
199          * switch to a real %cr3 promptly so that we can get to the direct map
200          * space. Remember that jmp is relative and that we've been relocated,
201          * so use an indirect jump.
202          */
203         ALIGN_TEXT
204         .code64
205 wakeup_64:
206         mov     $bootdata64 - bootgdt, %eax
207         mov     %ax, %ds
208
209         /* Restore arguments and return. */
210         movq    wakeup_ctx - wakeup_start(%rbx), %rdi
211         movq    wakeup_kpml4 - wakeup_start(%rbx), %rsi
212         movq    wakeup_retaddr - wakeup_start(%rbx), %rax
213         jmp     *%rax
214
215         .data
216
217 resume_beep:
218         .byte   0
219 reset_video:
220         .byte   0
221
222         ALIGN_DATA
223 bootgdt:
224         .long   0x00000000
225         .long   0x00000000
226         .long   0x00000000
227         .long   0x00000000
228         .long   0x00000000
229         .long   0x00000000
230         .long   0x00000000
231         .long   0x00000000
232
233 bootcode64:
234         .long   0x0000ffff
235         .long   0x00af9b00
236
237 bootdata64:
238         .long   0x0000ffff
239         .long   0x00af9300
240
241 bootcode32:
242         .long   0x0000ffff
243         .long   0x00cf9b00
244
245 bootdata32:
246         .long   0x0000ffff
247         .long   0x00cf9300
248 bootgdtend:
249
250 wakeup_pagetables:
251         .long   0
252
253 bootgdtdesc:
254         .word   bootgdtend - bootgdt    /* Length */
255         .long   bootgdt - wakeup_start  /* Offset plus %ds << 4 */
256
257         ALIGN_DATA
258 wakeup_retaddr:
259         .quad   0
260 wakeup_kpml4:
261         .quad   0
262
263 wakeup_ctx:
264         .quad   0
265 wakeup_xpcb:
266         .quad   0
267 wakeup_gdt:
268         .word   0
269         .quad   0
270
271         ALIGN_DATA
272 wakeup_efer:
273         .quad   0
274 wakeup_pat:
275         .quad   0
276 wakeup_star:
277         .quad   0
278 wakeup_lstar:
279         .quad   0
280 wakeup_cstar:
281         .quad   0
282 wakeup_sfmask:
283         .quad   0
284 wakeup_cpu:
285         .long   0
286 dummy: