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