xref: /openbmc/linux/arch/x86/realmode/rm/wakeup_asm.S (revision fb960bd2)
1/* SPDX-License-Identifier: GPL-2.0 */
2/*
3 * ACPI wakeup real mode startup stub
4 */
5#include <linux/linkage.h>
6#include <asm/segment.h>
7#include <asm/msr-index.h>
8#include <asm/page_types.h>
9#include <asm/pgtable_types.h>
10#include <asm/processor-flags.h>
11#include "realmode.h"
12#include "wakeup.h"
13
14	.code16
15
16/* This should match the structure in wakeup.h */
17	.section ".data", "aw"
18
19	.balign	16
20GLOBAL(wakeup_header)
21	video_mode:	.short	0	/* Video mode number */
22	pmode_entry:	.long	0
23	pmode_cs:	.short	__KERNEL_CS
24	pmode_cr0:	.long	0	/* Saved %cr0 */
25	pmode_cr3:	.long	0	/* Saved %cr3 */
26	pmode_cr4:	.long	0	/* Saved %cr4 */
27	pmode_efer:	.quad	0	/* Saved EFER */
28	pmode_gdt:	.quad	0
29	pmode_misc_en:	.quad	0	/* Saved MISC_ENABLE MSR */
30	pmode_behavior:	.long	0	/* Wakeup behavior flags */
31	realmode_flags:	.long	0
32	real_magic:	.long	0
33	signature:	.long	WAKEUP_HEADER_SIGNATURE
34END(wakeup_header)
35
36	.text
37	.code16
38
39	.balign	16
40ENTRY(wakeup_start)
41	cli
42	cld
43
44	LJMPW_RM(3f)
453:
46	/* Apparently some dimwit BIOS programmers don't know how to
47	   program a PM to RM transition, and we might end up here with
48	   junk in the data segment descriptor registers.  The only way
49	   to repair that is to go into PM and fix it ourselves... */
50	movw	$16, %cx
51	lgdtl	%cs:wakeup_gdt
52	movl	%cr0, %eax
53	orb	$X86_CR0_PE, %al
54	movl	%eax, %cr0
55	ljmpw	$8, $2f
562:
57	movw	%cx, %ds
58	movw	%cx, %es
59	movw	%cx, %ss
60	movw	%cx, %fs
61	movw	%cx, %gs
62
63	andb	$~X86_CR0_PE, %al
64	movl	%eax, %cr0
65	LJMPW_RM(3f)
663:
67	/* Set up segments */
68	movw	%cs, %ax
69	movw	%ax, %ss
70	movl	$rm_stack_end, %esp
71	movw	%ax, %ds
72	movw	%ax, %es
73	movw	%ax, %fs
74	movw	%ax, %gs
75
76	lidtl	wakeup_idt
77
78	/* Clear the EFLAGS */
79	pushl $0
80	popfl
81
82	/* Check header signature... */
83	movl	signature, %eax
84	cmpl	$WAKEUP_HEADER_SIGNATURE, %eax
85	jne	bogus_real_magic
86
87	/* Check we really have everything... */
88	movl	end_signature, %eax
89	cmpl	$REALMODE_END_SIGNATURE, %eax
90	jne	bogus_real_magic
91
92	/* Call the C code */
93	calll	main
94
95	/* Restore MISC_ENABLE before entering protected mode, in case
96	   BIOS decided to clear XD_DISABLE during S3. */
97	movl	pmode_behavior, %edi
98	btl	$WAKEUP_BEHAVIOR_RESTORE_MISC_ENABLE, %edi
99	jnc	1f
100
101	movl	pmode_misc_en, %eax
102	movl	pmode_misc_en + 4, %edx
103	movl	$MSR_IA32_MISC_ENABLE, %ecx
104	wrmsr
1051:
106
107	/* Do any other stuff... */
108
109#ifndef CONFIG_64BIT
110	/* This could also be done in C code... */
111	movl	pmode_cr3, %eax
112	movl	%eax, %cr3
113
114	btl	$WAKEUP_BEHAVIOR_RESTORE_CR4, %edi
115	jnc	1f
116	movl	pmode_cr4, %eax
117	movl	%eax, %cr4
1181:
119	btl	$WAKEUP_BEHAVIOR_RESTORE_EFER, %edi
120	jnc	1f
121	movl	pmode_efer, %eax
122	movl	pmode_efer + 4, %edx
123	movl	$MSR_EFER, %ecx
124	wrmsr
1251:
126
127	lgdtl	pmode_gdt
128
129	/* This really couldn't... */
130	movl	pmode_entry, %eax
131	movl	pmode_cr0, %ecx
132	movl	%ecx, %cr0
133	ljmpl	$__KERNEL_CS, $pa_startup_32
134	/* -> jmp *%eax in trampoline_32.S */
135#else
136	jmp	trampoline_start
137#endif
138
139bogus_real_magic:
1401:
141	hlt
142	jmp	1b
143
144	.section ".rodata","a"
145
146	/*
147	 * Set up the wakeup GDT.  We set these up as Big Real Mode,
148	 * that is, with limits set to 4 GB.  At least the Lenovo
149	 * Thinkpad X61 is known to need this for the video BIOS
150	 * initialization quirk to work; this is likely to also
151	 * be the case for other laptops or integrated video devices.
152	 */
153
154	.balign	16
155GLOBAL(wakeup_gdt)
156	.word	3*8-1		/* Self-descriptor */
157	.long	pa_wakeup_gdt
158	.word	0
159
160	.word	0xffff		/* 16-bit code segment @ real_mode_base */
161	.long	0x9b000000 + pa_real_mode_base
162	.word	0x008f		/* big real mode */
163
164	.word	0xffff		/* 16-bit data segment @ real_mode_base */
165	.long	0x93000000 + pa_real_mode_base
166	.word	0x008f		/* big real mode */
167END(wakeup_gdt)
168
169	.section ".rodata","a"
170	.balign	8
171
172	/* This is the standard real-mode IDT */
173	.balign	16
174GLOBAL(wakeup_idt)
175	.word	0xffff		/* limit */
176	.long	0		/* address */
177	.word	0
178END(wakeup_idt)
179