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