xref: /linux/arch/x86/kernel/acpi/wakeup_64.S (revision 0de80bcc)
1.text
2#include <linux/linkage.h>
3#include <asm/segment.h>
4#include <asm/pgtable.h>
5#include <asm/page.h>
6#include <asm/msr.h>
7#include <asm/asm-offsets.h>
8
9# Copyright 2003 Pavel Machek <pavel@suse.cz>, distribute under GPLv2
10#
11# wakeup_code runs in real mode, and at unknown address (determined at run-time).
12# Therefore it must only use relative jumps/calls.
13#
14# Do we need to deal with A20? It is okay: ACPI specs says A20 must be enabled
15#
16# If physical address of wakeup_code is 0x12345, BIOS should call us with
17# cs = 0x1234, eip = 0x05
18#
19
20#define BEEP \
21	inb	$97, %al; 	\
22	outb	%al, $0x80; 	\
23	movb	$3, %al; 	\
24	outb	%al, $97; 	\
25	outb	%al, $0x80; 	\
26	movb	$-74, %al; 	\
27	outb	%al, $67; 	\
28	outb	%al, $0x80; 	\
29	movb	$-119, %al; 	\
30	outb	%al, $66; 	\
31	outb	%al, $0x80; 	\
32	movb	$15, %al; 	\
33	outb	%al, $66;
34
35
36ALIGN
37	.align	16
38ENTRY(wakeup_start)
39wakeup_code:
40	wakeup_code_start = .
41	.code16
42
43# Running in *copy* of this code, somewhere in low 1MB.
44
45	cli
46	cld
47	# setup data segment
48	movw	%cs, %ax
49	movw	%ax, %ds		# Make ds:0 point to wakeup_start
50	movw	%ax, %ss
51
52	# Data segment must be set up before we can see whether to beep.
53	testl   $4, realmode_flags - wakeup_code
54	jz      1f
55	BEEP
561:
57
58					# Private stack is needed for ASUS board
59	mov	$(wakeup_stack - wakeup_code), %sp
60
61	pushl	$0			# Kill any dangerous flags
62	popfl
63
64	movl	real_magic - wakeup_code, %eax
65	cmpl	$0x12345678, %eax
66	jne	bogus_real_magic
67
68	testl	$1, realmode_flags - wakeup_code
69	jz	1f
70	lcall   $0xc000,$3
71	movw	%cs, %ax
72	movw	%ax, %ds		# Bios might have played with that
73	movw	%ax, %ss
741:
75
76	testl	$2, realmode_flags - wakeup_code
77	jz	1f
78	mov	video_mode - wakeup_code, %ax
79	call	mode_set
801:
81
82	mov	%ds, %ax			# Find 32bit wakeup_code addr
83	movzx   %ax, %esi			# (Convert %ds:gdt to a liner ptr)
84	shll    $4, %esi
85						# Fix up the vectors
86	addl    %esi, wakeup_32_vector - wakeup_code
87	addl    %esi, wakeup_long64_vector - wakeup_code
88	addl    %esi, gdt_48a + 2 - wakeup_code # Fixup the gdt pointer
89
90	lidtl	%ds:idt_48a - wakeup_code
91	lgdtl	%ds:gdt_48a - wakeup_code	# load gdt with whatever is
92						# appropriate
93
94	movl	$1, %eax			# protected mode (PE) bit
95	lmsw	%ax				# This is it!
96	jmp	1f
971:
98
99	ljmpl   *(wakeup_32_vector - wakeup_code)
100
101	.balign 4
102wakeup_32_vector:
103	.long   wakeup_32 - wakeup_code
104	.word   __KERNEL32_CS, 0
105
106	.code32
107wakeup_32:
108# Running in this code, but at low address; paging is not yet turned on.
109
110	movl	$__KERNEL_DS, %eax
111	movl	%eax, %ds
112
113	/*
114	 * Prepare for entering 64bits mode
115	 */
116
117	/* Enable PAE */
118	xorl	%eax, %eax
119	btsl	$5, %eax
120	movl	%eax, %cr4
121
122	/* Setup early boot stage 4 level pagetables */
123	leal    (wakeup_level4_pgt - wakeup_code)(%esi), %eax
124	movl	%eax, %cr3
125
126        /* Check if nx is implemented */
127        movl    $0x80000001, %eax
128        cpuid
129        movl    %edx,%edi
130
131	/* Enable Long Mode */
132	xorl    %eax, %eax
133	btsl	$_EFER_LME, %eax
134
135	/* No Execute supported? */
136	btl	$20,%edi
137	jnc     1f
138	btsl	$_EFER_NX, %eax
139
140	/* Make changes effective */
1411:	movl    $MSR_EFER, %ecx
142	xorl    %edx, %edx
143	wrmsr
144
145	xorl	%eax, %eax
146	btsl	$31, %eax			/* Enable paging and in turn activate Long Mode */
147	btsl	$0, %eax			/* Enable protected mode */
148
149	/* Make changes effective */
150	movl	%eax, %cr0
151
152	/* At this point:
153		CR4.PAE must be 1
154		CS.L must be 0
155		CR3 must point to PML4
156		Next instruction must be a branch
157		This must be on identity-mapped page
158	*/
159	/*
160	 * At this point we're in long mode but in 32bit compatibility mode
161	 * with EFER.LME = 1, CS.L = 0, CS.D = 1 (and in turn
162	 * EFER.LMA = 1). Now we want to jump in 64bit mode, to do that we load
163	 * the new gdt/idt that has __KERNEL_CS with CS.L = 1.
164	 */
165
166	/* Finally jump in 64bit mode */
167        ljmp    *(wakeup_long64_vector - wakeup_code)(%esi)
168
169	.balign 4
170wakeup_long64_vector:
171	.long   wakeup_long64 - wakeup_code
172	.word   __KERNEL_CS, 0
173
174.code64
175
176	/* Hooray, we are in Long 64-bit mode (but still running in
177	 * low memory)
178	 */
179wakeup_long64:
180	/*
181	 * We must switch to a new descriptor in kernel space for the GDT
182	 * because soon the kernel won't have access anymore to the userspace
183	 * addresses where we're currently running on. We have to do that here
184	 * because in 32bit we couldn't load a 64bit linear address.
185	 */
186	lgdt	cpu_gdt_descr
187
188	movq    saved_magic, %rax
189	movq    $0x123456789abcdef0, %rdx
190	cmpq    %rdx, %rax
191	jne     bogus_64_magic
192
193	nop
194	nop
195	movw	$__KERNEL_DS, %ax
196	movw	%ax, %ss
197	movw	%ax, %ds
198	movw	%ax, %es
199	movw	%ax, %fs
200	movw	%ax, %gs
201	movq	saved_rsp, %rsp
202
203	movq	saved_rbx, %rbx
204	movq	saved_rdi, %rdi
205	movq	saved_rsi, %rsi
206	movq	saved_rbp, %rbp
207
208	movq	saved_rip, %rax
209	jmp	*%rax
210
211.code32
212
213	.align	64
214gdta:
215	/* Its good to keep gdt in sync with one in trampoline.S */
216	.word	0, 0, 0, 0			# dummy
217	/* ??? Why I need the accessed bit set in order for this to work? */
218	.quad   0x00cf9b000000ffff              # __KERNEL32_CS
219	.quad   0x00af9b000000ffff              # __KERNEL_CS
220	.quad   0x00cf93000000ffff              # __KERNEL_DS
221
222idt_48a:
223	.word	0				# idt limit = 0
224	.word	0, 0				# idt base = 0L
225
226gdt_48a:
227	.word	0x800				# gdt limit=2048,
228						#  256 GDT entries
229	.long   gdta - wakeup_code              # gdt base (relocated in later)
230
231real_magic:	.quad 0
232video_mode:	.quad 0
233realmode_flags:	.quad 0
234
235.code16
236bogus_real_magic:
237	jmp bogus_real_magic
238
239.code64
240bogus_64_magic:
241	jmp bogus_64_magic
242
243/* This code uses an extended set of video mode numbers. These include:
244 * Aliases for standard modes
245 *	NORMAL_VGA (-1)
246 *	EXTENDED_VGA (-2)
247 *	ASK_VGA (-3)
248 * Video modes numbered by menu position -- NOT RECOMMENDED because of lack
249 * of compatibility when extending the table. These are between 0x00 and 0xff.
250 */
251#define VIDEO_FIRST_MENU 0x0000
252
253/* Standard BIOS video modes (BIOS number + 0x0100) */
254#define VIDEO_FIRST_BIOS 0x0100
255
256/* VESA BIOS video modes (VESA number + 0x0200) */
257#define VIDEO_FIRST_VESA 0x0200
258
259/* Video7 special modes (BIOS number + 0x0900) */
260#define VIDEO_FIRST_V7 0x0900
261
262# Setting of user mode (AX=mode ID) => CF=success
263
264# For now, we only handle VESA modes (0x0200..0x03ff).  To handle other
265# modes, we should probably compile in the video code from the boot
266# directory.
267.code16
268mode_set:
269	movw	%ax, %bx
270	subb	$VIDEO_FIRST_VESA>>8, %bh
271	cmpb	$2, %bh
272	jb	check_vesa
273
274setbad:
275	clc
276	ret
277
278check_vesa:
279	orw	$0x4000, %bx			# Use linear frame buffer
280	movw	$0x4f02, %ax			# VESA BIOS mode set call
281	int	$0x10
282	cmpw	$0x004f, %ax			# AL=4f if implemented
283	jnz	setbad				# AH=0 if OK
284
285	stc
286	ret
287
288wakeup_stack_begin:	# Stack grows down
289
290.org	0xff0
291wakeup_stack:		# Just below end of page
292
293.org   0x1000
294ENTRY(wakeup_level4_pgt)
295	.quad   level3_ident_pgt - __START_KERNEL_map + _KERNPG_TABLE
296	.fill   510,8,0
297	/* (2^48-(2*1024*1024*1024))/(2^39) = 511 */
298	.quad   level3_kernel_pgt - __START_KERNEL_map + _KERNPG_TABLE
299
300ENTRY(wakeup_end)
301
302##
303# acpi_copy_wakeup_routine
304#
305# Copy the above routine to low memory.
306#
307# Parameters:
308# %rdi:	place to copy wakeup routine to
309#
310# Returned address is location of code in low memory (past data and stack)
311#
312	.code64
313ENTRY(acpi_copy_wakeup_routine)
314	pushq	%rax
315	pushq	%rdx
316
317	movl	saved_video_mode, %edx
318	movl	%edx, video_mode - wakeup_start (,%rdi)
319	movl	acpi_realmode_flags, %edx
320	movl	%edx, realmode_flags - wakeup_start (,%rdi)
321	movq	$0x12345678, real_magic - wakeup_start (,%rdi)
322	movq	$0x123456789abcdef0, %rdx
323	movq	%rdx, saved_magic
324
325	movq    saved_magic, %rax
326	movq    $0x123456789abcdef0, %rdx
327	cmpq    %rdx, %rax
328	jne     bogus_64_magic
329
330	# restore the regs we used
331	popq	%rdx
332	popq	%rax
333ENTRY(do_suspend_lowlevel_s4bios)
334	ret
335
336	.align 2
337	.p2align 4,,15
338.globl do_suspend_lowlevel
339	.type	do_suspend_lowlevel,@function
340do_suspend_lowlevel:
341.LFB5:
342	subq	$8, %rsp
343	xorl	%eax, %eax
344	call	save_processor_state
345
346	movq	$saved_context, %rax
347	movq	%rsp, pt_regs_rsp(%rax)
348	movq	%rbp, pt_regs_rbp(%rax)
349	movq	%rsi, pt_regs_rsi(%rax)
350	movq	%rdi, pt_regs_rdi(%rax)
351	movq	%rbx, pt_regs_rbx(%rax)
352	movq	%rcx, pt_regs_rcx(%rax)
353	movq	%rdx, pt_regs_rdx(%rax)
354	movq	%r8, pt_regs_r8(%rax)
355	movq	%r9, pt_regs_r9(%rax)
356	movq	%r10, pt_regs_r10(%rax)
357	movq	%r11, pt_regs_r11(%rax)
358	movq	%r12, pt_regs_r12(%rax)
359	movq	%r13, pt_regs_r13(%rax)
360	movq	%r14, pt_regs_r14(%rax)
361	movq	%r15, pt_regs_r15(%rax)
362	pushfq
363	popq	pt_regs_eflags(%rax)
364
365	movq	$.L97, saved_rip(%rip)
366
367	movq	%rsp, saved_rsp
368	movq	%rbp, saved_rbp
369	movq	%rbx, saved_rbx
370	movq	%rdi, saved_rdi
371	movq	%rsi, saved_rsi
372
373	addq	$8, %rsp
374	movl	$3, %edi
375	xorl	%eax, %eax
376	jmp	acpi_enter_sleep_state
377.L97:
378	.p2align 4,,7
379.L99:
380	.align 4
381	movl	$24, %eax
382	movw	%ax, %ds
383
384	/* We don't restore %rax, it must be 0 anyway */
385	movq	$saved_context, %rax
386	movq	saved_context_cr4(%rax), %rbx
387	movq	%rbx, %cr4
388	movq	saved_context_cr3(%rax), %rbx
389	movq	%rbx, %cr3
390	movq	saved_context_cr2(%rax), %rbx
391	movq	%rbx, %cr2
392	movq	saved_context_cr0(%rax), %rbx
393	movq	%rbx, %cr0
394	pushq	pt_regs_eflags(%rax)
395	popfq
396	movq	pt_regs_rsp(%rax), %rsp
397	movq	pt_regs_rbp(%rax), %rbp
398	movq	pt_regs_rsi(%rax), %rsi
399	movq	pt_regs_rdi(%rax), %rdi
400	movq	pt_regs_rbx(%rax), %rbx
401	movq	pt_regs_rcx(%rax), %rcx
402	movq	pt_regs_rdx(%rax), %rdx
403	movq	pt_regs_r8(%rax), %r8
404	movq	pt_regs_r9(%rax), %r9
405	movq	pt_regs_r10(%rax), %r10
406	movq	pt_regs_r11(%rax), %r11
407	movq	pt_regs_r12(%rax), %r12
408	movq	pt_regs_r13(%rax), %r13
409	movq	pt_regs_r14(%rax), %r14
410	movq	pt_regs_r15(%rax), %r15
411
412	xorl	%eax, %eax
413	addq	$8, %rsp
414	jmp	restore_processor_state
415.LFE5:
416.Lfe5:
417	.size	do_suspend_lowlevel,.Lfe5-do_suspend_lowlevel
418
419.data
420ALIGN
421ENTRY(saved_rbp)	.quad	0
422ENTRY(saved_rsi)	.quad	0
423ENTRY(saved_rdi)	.quad	0
424ENTRY(saved_rbx)	.quad	0
425
426ENTRY(saved_rip)	.quad	0
427ENTRY(saved_rsp)	.quad	0
428
429ENTRY(saved_magic)	.quad	0
430