1/*
2 * Copyright (c) 2000 Apple Computer, Inc. All rights reserved.
3 *
4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. The rights granted to you under the License
10 * may not be used to create, or enable the creation or redistribution of,
11 * unlawful or unlicensed copies of an Apple operating system, or to
12 * circumvent, violate, or enable the circumvention or violation of, any
13 * terms of an Apple operating system software license agreement.
14 *
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
17 *
18 * The Original Code and all software distributed under the License are
19 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
20 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
22 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
23 * Please see the License for the specific language governing rights and
24 * limitations under the License.
25 *
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
27 */
28
29#include <i386/asm.h>
30#include <i386/proc_reg.h>
31#include <i386/postcode.h>
32#include <i386/acpi.h>
33#include <assym.s>
34
35	.file "acpi_wakeup.s"
36
37	.text
38	.align	12	/* Page align for single bcopy_phys() */
39
40#define LJMP(segment, address)			 \
41	.byte	0xea				;\
42	.long	address - EXT(acpi_wake_start)	;\
43	.word	segment
44
45#define	PA(addr)	(addr)
46
47/*
48 * acpi_wake_start
49 *
50 * The code from acpi_wake_start to acpi_wake_end is copied to
51 * memory below 1MB.  The firmware waking vector is updated to
52 * point at acpi_wake_start in low memory before sleeping.
53 */
54
55ENTRY(acpi_wake_start)
56	/*
57	 * CPU woke up from sleep, and is back in real mode.
58	 * Initialize it just enough to get back to protected mode.
59	 */
60	cli
61
62	POSTCODE(ACPI_WAKE_START_ENTRY)
63
64	/* set up DS to match CS */
65	movw	%cs, %ax
66	movw	%ax, %ds
67
68	/*
69	 * Must initialize GDTR before entering protected mode.
70	 * Use a temporary GDT that is 0 based, 4GB limit, code and data.
71	 * Restoring the actual GDT will come later.
72	 */
73	addr16
74	data16
75	lgdt	EXT(acpi_gdtr) - EXT(acpi_wake_start)
76
77	/* set CR0.PE to enter protected mode */
78	mov	%cr0, %eax
79	data16
80	or	$(CR0_PE), %eax
81	mov	%eax, %cr0
82
83	/*
84	 * Make intra-segment jump to flush pipeline and reload CS register.
85	 * If GDT is bogus, it will blow up here.
86	 */
87	data16
88	LJMP(0x8, acpi_wake_prot + ACPI_WAKE_ADDR)
89
90acpi_wake_prot:
91
92	/* protected mode, paging disabled */
93
94	/* setup the protected mode segment registers */
95	mov	$0x10, %eax
96	movw	%ax, %ds
97	movw	%ax, %es
98	movw	%ax, %ss
99	movw	%ax, %fs
100	movw	%ax, %gs
101
102	/* jump back to the sleep function in the kernel */
103	movl	PA(saved_eip), %eax
104	jmp	*%eax
105
106/*  Segment Descriptor
107 *
108 * 31          24         19   16                 7           0
109 * ------------------------------------------------------------
110 * |             | |B| |A|       | |   |1|0|E|W|A|            |
111 * | BASE 31..24 |G|/|0|V| LIMIT |P|DPL|  TYPE   | BASE 23:16 |
112 * |             | |D| |L| 19..16| |   |1|1|C|R|A|            |
113 * ------------------------------------------------------------
114 * |                             |                            |
115 * |        BASE 15..0           |       LIMIT 15..0          |
116 * |                             |                            |
117 * ------------------------------------------------------------
118 */
119ENTRY(acpi_gdt)
120	.word	0, 0		/* 0x0  : null */
121	.byte	0, 0, 0, 0
122
123	.word	0xffff, 0x0000	/* 0x8  : code */
124	.byte	0, 0x9e, 0xcf, 0
125
126	.word	0xffff, 0x0000	/* 0x10 : data */
127	.byte	0, 0x92, 0xcf, 0
128
129ENTRY(acpi_gdtr)
130	.word	24		/* limit (8*3 segs) */
131	.long	EXT(acpi_gdt) - EXT(acpi_wake_start) + ACPI_WAKE_ADDR
132
133ENTRY(acpi_wake_end)
134
135
136/*
137 * acpi_sleep_cpu(acpi_sleep_callback func, void * refcon)
138 *
139 * Save CPU state before platform sleep. Restore CPU state
140 * following wake up.
141 */
142
143ENTRY(acpi_sleep_cpu)
144	pushl	%ebp
145	movl	%esp, %ebp
146
147	/* save flags */
148	pushfl
149
150	/* save general purpose registers */
151	pushal
152	movl	%esp, saved_esp
153
154	/* make sure tlb is flushed */
155	movl	%cr3,%eax
156	movl	%eax,%cr3
157
158	/* save control registers */
159	movl	%cr0, %eax
160	movl	%eax, saved_cr0
161	movl	%cr2, %eax
162	movl	%eax, saved_cr2
163	movl	%cr3, %eax
164	movl	%eax, saved_cr3
165	movl	%cr4, %eax
166	movl	%eax, saved_cr4
167
168	/* save segment registers */
169	movw	%es, saved_es
170	movw	%fs, saved_fs
171	movw	%gs, saved_gs
172	movw	%ss, saved_ss
173
174	/* save descriptor table registers */
175	sgdt	saved_gdt
176	sldt	saved_ldt
177	sidt	saved_idt
178	str	saved_tr
179
180	/*
181	 * When system wakes up, the real mode wake handler will revert to
182	 * protected mode, then jump to the address stored at saved_eip.
183	 */
184	movl	$(PA(wake_prot)), saved_eip
185
186	/*
187	 * Call ACPI function provided by the caller to sleep the platform.
188	 * This call will not return on success.
189	 */
190	pushl	B_ARG1
191	movl	B_ARG0, %edi
192	call	*%edi
193	popl	%edi
194
195	/* sleep failed, no cpu context lost */
196	jmp	wake_restore
197
198wake_prot:
199	/* protected mode, paging disabled */
200	POSTCODE(ACPI_WAKE_PROT_ENTRY)
201
202	movl	PA(saved_cr3), %ebx
203	movl	PA(saved_cr4), %ecx
204	/*
205	 * restore cr3, PAE and NXE states in an orderly fashion
206	 */
207	movl	%ebx, %cr3
208	movl	%ecx, %cr4
209
210	movl	$(MSR_IA32_EFER), %ecx			/* MSR number in ecx */
211	rdmsr						/* MSR value return in edx: eax */
212	orl	$(MSR_IA32_EFER_NXE), %eax		/* Set NXE bit in low 32-bits */
213	wrmsr						/* Update Extended Feature Enable reg */
214
215	/* restore kernel GDT */
216	lgdt	PA(saved_gdt)
217
218	movl	PA(saved_cr2), %eax
219	movl	%eax, %cr2
220
221	/* restore CR0, paging enabled */
222	movl	PA(saved_cr0), %eax
223	movl	%eax, %cr0
224
225	/* switch to kernel code segment */
226	ljmpl	$(KERNEL_CS), $wake_paged
227
228wake_paged:
229
230	/* protected mode, paging enabled */
231	POSTCODE(ACPI_WAKE_PAGED_ENTRY)
232
233	/* switch to kernel data segment */
234	movw	$(KERNEL_DS), %ax
235	movw	%ax, %ds
236
237	/* restore local and interrupt descriptor tables */
238	lldt	saved_ldt
239	lidt	saved_idt
240
241	/* restore segment registers */
242	movw	saved_es, %es
243	movw	saved_fs, %fs
244	movw	saved_gs, %gs
245	movw	saved_ss, %ss
246
247	/*
248	 * Restore task register. Before doing this, clear the busy flag
249	 * in the TSS descriptor set by the CPU.
250	 */
251	movl	$saved_gdt, %eax
252	movl	2(%eax), %edx			/* GDT base, skip limit word */
253	movl	$(KERNEL_TSS), %eax		/* TSS segment selector */
254	movb	$(K_TSS), 5(%edx, %eax)		/* clear busy flag */
255	ltr	saved_tr			/* restore TR */
256
257wake_restore:
258
259	/* restore general purpose registers */
260	movl	saved_esp, %esp
261	popal
262
263	/* restore flags */
264	popfl
265
266	leave
267	ret
268
269
270        .section __HIB, __text
271	.align 2
272
273        .globl EXT(acpi_wake_prot_entry)
274ENTRY(acpi_wake_prot_entry)
275	/* protected mode, paging enabled */
276
277	POSTCODE(ACPI_WAKE_PAGED_ENTRY)
278
279	/* restore kernel GDT */
280	lgdt	saved_gdt
281
282	POSTCODE(0x40)
283
284	/* restore control registers */
285
286	movl	saved_cr0, %eax
287	movl	%eax, %cr0
288
289	movl	saved_cr2, %eax
290	movl	%eax, %cr2
291
292        POSTCODE(0x3E)
293
294	/* restore real PDE base */
295	movl	saved_cr3, %eax
296	movl	saved_cr4, %edx
297	movl	%eax, %cr3
298	movl	%edx, %cr4
299	movl	%eax, %cr3
300
301	/* switch to kernel data segment */
302	movw	$(KERNEL_DS), %ax
303	movw	%ax, %ds
304
305	POSTCODE(0x3C)
306	/* restore local and interrupt descriptor tables */
307	lldt	saved_ldt
308	lidt	saved_idt
309
310        POSTCODE(0x3B)
311	/* restore segment registers */
312	movw	saved_es, %es
313	movw	saved_fs, %fs
314	movw	saved_gs, %gs
315	movw	saved_ss, %ss
316
317        POSTCODE(0x3A)
318	/*
319	 * Restore task register. Before doing this, clear the busy flag
320	 * in the TSS descriptor set by the CPU.
321	 */
322	movl	$saved_gdt, %eax
323	movl	2(%eax), %edx			/* GDT base, skip limit word */
324	movl	$(KERNEL_TSS), %eax		/* TSS segment selector */
325	movb	$(K_TSS), 5(%edx, %eax)		/* clear busy flag */
326	ltr	saved_tr			/* restore TR */
327
328	/* restore general purpose registers */
329	movl	saved_esp, %esp
330	popal
331
332	/* restore flags */
333	popfl
334
335        /* make sure interrupts are disabled */
336        cli
337
338        movl   $2, %eax
339
340        leave
341	ret
342
343
344	.data
345        .section __HIB, __data
346	.align	2
347
348
349/*
350 * CPU registers saved across sleep/wake.
351 */
352
353saved_esp:	.long 0
354saved_es:	.word 0
355saved_fs:	.word 0
356saved_gs:	.word 0
357saved_ss:	.word 0
358saved_cr0:	.long 0
359saved_cr2:	.long 0
360saved_cr3:	.long 0
361saved_cr4:	.long 0
362saved_gdt:	.word 0
363		.long 0
364saved_idt:	.word 0
365		.long 0
366saved_ldt:	.word 0
367saved_tr:	.word 0
368saved_eip:	.long 0
369
370