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