xref: /linux/arch/x86/realmode/rm/wakeup_asm.S (revision ca55b2fef3a9373fcfc30f82fd26bc7fccbda732)
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 */
78	pushl $0
79	popfl
80
81	/* Check header signature... */
82	movl	signature, %eax
83	cmpl	$WAKEUP_HEADER_SIGNATURE, %eax
84	jne	bogus_real_magic
85
86	/* Check we really have everything... */
87	movl	end_signature, %eax
88	cmpl	$REALMODE_END_SIGNATURE, %eax
89	jne	bogus_real_magic
90
91	/* Call the C code */
92	calll	main
93
94	/* Restore MISC_ENABLE before entering protected mode, in case
95	   BIOS decided to clear XD_DISABLE during S3. */
96	movl	pmode_behavior, %edi
97	btl	$WAKEUP_BEHAVIOR_RESTORE_MISC_ENABLE, %edi
98	jnc	1f
99
100	movl	pmode_misc_en, %eax
101	movl	pmode_misc_en + 4, %edx
102	movl	$MSR_IA32_MISC_ENABLE, %ecx
103	wrmsr
1041:
105
106	/* Do any other stuff... */
107
108#ifndef CONFIG_64BIT
109	/* This could also be done in C code... */
110	movl	pmode_cr3, %eax
111	movl	%eax, %cr3
112
113	btl	$WAKEUP_BEHAVIOR_RESTORE_CR4, %edi
114	jnc	1f
115	movl	pmode_cr4, %eax
116	movl	%eax, %cr4
1171:
118	btl	$WAKEUP_BEHAVIOR_RESTORE_EFER, %edi
119	jnc	1f
120	movl	pmode_efer, %eax
121	movl	pmode_efer + 4, %edx
122	movl	$MSR_EFER, %ecx
123	wrmsr
1241:
125
126	lgdtl	pmode_gdt
127
128	/* This really couldn't... */
129	movl	pmode_entry, %eax
130	movl	pmode_cr0, %ecx
131	movl	%ecx, %cr0
132	ljmpl	$__KERNEL_CS, $pa_startup_32
133	/* -> jmp *%eax in trampoline_32.S */
134#else
135	jmp	trampoline_start
136#endif
137
138bogus_real_magic:
1391:
140	hlt
141	jmp	1b
142
143	.section ".rodata","a"
144
145	/*
146	 * Set up the wakeup GDT.  We set these up as Big Real Mode,
147	 * that is, with limits set to 4 GB.  At least the Lenovo
148	 * Thinkpad X61 is known to need this for the video BIOS
149	 * initialization quirk to work; this is likely to also
150	 * be the case for other laptops or integrated video devices.
151	 */
152
153	.balign	16
154GLOBAL(wakeup_gdt)
155	.word	3*8-1		/* Self-descriptor */
156	.long	pa_wakeup_gdt
157	.word	0
158
159	.word	0xffff		/* 16-bit code segment @ real_mode_base */
160	.long	0x9b000000 + pa_real_mode_base
161	.word	0x008f		/* big real mode */
162
163	.word	0xffff		/* 16-bit data segment @ real_mode_base */
164	.long	0x93000000 + pa_real_mode_base
165	.word	0x008f		/* big real mode */
166END(wakeup_gdt)
167
168	.section ".rodata","a"
169	.balign	8
170
171	/* This is the standard real-mode IDT */
172	.balign	16
173GLOBAL(wakeup_idt)
174	.word	0xffff		/* limit */
175	.long	0		/* address */
176	.word	0
177END(wakeup_idt)
178