wakeup_asm.S 3.6 KB
Newer Older
1 2 3
/*
 * ACPI wakeup real mode startup stub
 */
4
#include <linux/linkage.h>
5 6
#include <asm/segment.h>
#include <asm/msr-index.h>
7 8
#include <asm/page_types.h>
#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 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
	.section ".data", "aw"

	.balign	16
GLOBAL(wakeup_header)
	video_mode:	.short	0	/* Video mode number */
	pmode_entry:	.long	0
	pmode_cs:	.short	__KERNEL_CS
	pmode_cr0:	.long	0	/* Saved %cr0 */
	pmode_cr3:	.long	0	/* Saved %cr3 */
	pmode_cr4:	.long	0	/* Saved %cr4 */
	pmode_efer:	.quad	0	/* Saved EFER */
	pmode_gdt:	.quad	0
	pmode_misc_en:	.quad	0	/* Saved MISC_ENABLE MSR */
	pmode_behavior:	.long	0	/* Wakeup behavior flags */
	realmode_flags:	.long	0
	real_magic:	.long	0
	signature:	.long	WAKEUP_HEADER_SIGNATURE
END(wakeup_header)
34 35 36

	.text
	.code16
37 38 39

	.balign	16
ENTRY(wakeup_start)
40
	cli
41 42
	cld

43
	LJMPW_RM(3f)
44
3:
45 46 47 48 49 50 51 52 53
	/* Apparently some dimwit BIOS programmers don't know how to
	   program a PM to RM transition, and we might end up here with
	   junk in the data segment descriptor registers.  The only way
	   to repair that is to go into PM and fix it ourselves... */
	movw	$16, %cx
	lgdtl	%cs:wakeup_gdt
	movl	%cr0, %eax
	orb	$X86_CR0_PE, %al
	movl	%eax, %cr0
54
	ljmpw	$8, $2f
55 56 57 58 59 60 61 62 63
2:
	movw	%cx, %ds
	movw	%cx, %es
	movw	%cx, %ss
	movw	%cx, %fs
	movw	%cx, %gs

	andb	$~X86_CR0_PE, %al
	movl	%eax, %cr0
64
	LJMPW_RM(3f)
65
3:
66 67
	/* Set up segments */
	movw	%cs, %ax
68 69
	movw	%ax, %ss
	movl	$rm_stack_end, %esp
70 71
	movw	%ax, %ds
	movw	%ax, %es
72 73
	movw	%ax, %fs
	movw	%ax, %gs
74

75
	lidtl	wakeup_idt
76

77
	/* Clear the EFLAGS */
78 79
	pushl $0
	popfl
80 81 82

	/* Check header signature... */
	movl	signature, %eax
83
	cmpl	$WAKEUP_HEADER_SIGNATURE, %eax
84 85 86 87
	jne	bogus_real_magic

	/* Check we really have everything... */
	movl	end_signature, %eax
88
	cmpl	$REALMODE_END_SIGNATURE, %eax
89 90 91 92 93
	jne	bogus_real_magic

	/* Call the C code */
	calll	main

94 95
	/* Restore MISC_ENABLE before entering protected mode, in case
	   BIOS decided to clear XD_DISABLE during S3. */
96 97
	movl	pmode_behavior, %edi
	btl	$WAKEUP_BEHAVIOR_RESTORE_MISC_ENABLE, %edi
98 99 100 101 102 103 104 105
	jnc	1f

	movl	pmode_misc_en, %eax
	movl	pmode_misc_en + 4, %edx
	movl	$MSR_IA32_MISC_ENABLE, %ecx
	wrmsr
1:

106 107 108 109 110 111 112
	/* Do any other stuff... */

#ifndef CONFIG_64BIT
	/* This could also be done in C code... */
	movl	pmode_cr3, %eax
	movl	%eax, %cr3

113
	btl	$WAKEUP_BEHAVIOR_RESTORE_CR4, %edi
114
	jnc	1f
115 116
	movl	pmode_cr4, %eax
	movl	%eax, %cr4
117
1:
118
	btl	$WAKEUP_BEHAVIOR_RESTORE_EFER, %edi
119
	jnc	1f
120 121
	movl	pmode_efer, %eax
	movl	pmode_efer + 4, %edx
B
Brian Gerst 已提交
122
	movl	$MSR_EFER, %ecx
123 124 125 126 127 128
	wrmsr
1:

	lgdtl	pmode_gdt

	/* This really couldn't... */
129 130 131 132 133
	movl	pmode_entry, %eax
	movl	pmode_cr0, %ecx
	movl	%ecx, %cr0
	ljmpl	$__KERNEL_CS, $pa_startup_32
	/* -> jmp *%eax in trampoline_32.S */
134
#else
135
	jmp	trampoline_start
136 137 138 139 140 141 142
#endif

bogus_real_magic:
1:
	hlt
	jmp	1b

143 144 145 146 147 148 149 150 151 152 153
	.section ".rodata","a"

	/*
	 * Set up the wakeup GDT.  We set these up as Big Real Mode,
	 * that is, with limits set to 4 GB.  At least the Lenovo
	 * Thinkpad X61 is known to need this for the video BIOS
	 * initialization quirk to work; this is likely to also
	 * be the case for other laptops or integrated video devices.
	 */

	.balign	16
154
GLOBAL(wakeup_gdt)
155 156 157 158 159 160 161 162 163 164 165
	.word	3*8-1		/* Self-descriptor */
	.long	pa_wakeup_gdt
	.word	0

	.word	0xffff		/* 16-bit code segment @ real_mode_base */
	.long	0x9b000000 + pa_real_mode_base
	.word	0x008f		/* big real mode */

	.word	0xffff		/* 16-bit data segment @ real_mode_base */
	.long	0x93000000 + pa_real_mode_base
	.word	0x008f		/* big real mode */
166
END(wakeup_gdt)
167

168
	.section ".rodata","a"
169 170 171
	.balign	8

	/* This is the standard real-mode IDT */
172 173
	.balign	16
GLOBAL(wakeup_idt)
174 175 176
	.word	0xffff		/* limit */
	.long	0		/* address */
	.word	0
177
END(wakeup_idt)