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