1/*
2 * ACPI wakeup real mode startup stub
3 */
4#include <asm/segment.h>
5#include <asm/msr-index.h>
6#include <asm/page_types.h>
7#include <asm/pgtable_types.h>
8#include <asm/processor-flags.h>
9#include "wakeup.h"
10
11	.code16
12	.section ".jump", "ax"
13	.globl	_start
14_start:
15	cli
16	jmp	wakeup_code
17
18/* This should match the structure in wakeup.h */
19		.section ".header", "a"
20		.globl	wakeup_header
21wakeup_header:
22video_mode:	.short	0	/* Video mode number */
23pmode_return:	.byte	0x66, 0xea	/* ljmpl */
24		.long	0	/* offset goes here */
25		.short	__KERNEL_CS
26pmode_cr0:	.long	0	/* Saved %cr0 */
27pmode_cr3:	.long	0	/* Saved %cr3 */
28pmode_cr4:	.long	0	/* Saved %cr4 */
29pmode_efer:	.quad	0	/* Saved EFER */
30pmode_gdt:	.quad	0
31pmode_misc_en:	.quad	0	/* Saved MISC_ENABLE MSR */
32pmode_behavior:	.long	0	/* Wakeup behavior flags */
33realmode_flags:	.long	0
34real_magic:	.long	0
35trampoline_segment:	.word 0
36_pad1:		.byte	0
37wakeup_jmp:	.byte	0xea	/* ljmpw */
38wakeup_jmp_off:	.word	3f
39wakeup_jmp_seg:	.word	0
40wakeup_gdt:	.quad	0, 0, 0
41signature:	.long	WAKEUP_HEADER_SIGNATURE
42
43	.text
44	.code16
45wakeup_code:
46	cld
47
48	/* Apparently some dimwit BIOS programmers don't know how to
49	   program a PM to RM transition, and we might end up here with
50	   junk in the data segment descriptor registers.  The only way
51	   to repair that is to go into PM and fix it ourselves... */
52	movw	$16, %cx
53	lgdtl	%cs:wakeup_gdt
54	movl	%cr0, %eax
55	orb	$X86_CR0_PE, %al
56	movl	%eax, %cr0
57	jmp	1f
581:	ljmpw	$8, $2f
592:
60	movw	%cx, %ds
61	movw	%cx, %es
62	movw	%cx, %ss
63	movw	%cx, %fs
64	movw	%cx, %gs
65
66	andb	$~X86_CR0_PE, %al
67	movl	%eax, %cr0
68	jmp	wakeup_jmp
693:
70	/* Set up segments */
71	movw	%cs, %ax
72	movw	%ax, %ds
73	movw	%ax, %es
74	movw	%ax, %ss
75	lidtl	wakeup_idt
76
77	movl	$wakeup_stack_end, %esp
78
79	/* Clear the EFLAGS */
80	pushl	$0
81	popfl
82
83	/* Check header signature... */
84	movl	signature, %eax
85	cmpl	$WAKEUP_HEADER_SIGNATURE, %eax
86	jne	bogus_real_magic
87
88	/* Check we really have everything... */
89	movl	end_signature, %eax
90	cmpl	$WAKEUP_END_SIGNATURE, %eax
91	jne	bogus_real_magic
92
93	/* Call the C code */
94	calll	main
95
96	/* Restore MISC_ENABLE before entering protected mode, in case
97	   BIOS decided to clear XD_DISABLE during S3. */
98	movl	pmode_behavior, %eax
99	btl	$WAKEUP_BEHAVIOR_RESTORE_MISC_ENABLE, %eax
100	jnc	1f
101
102	movl	pmode_misc_en, %eax
103	movl	pmode_misc_en + 4, %edx
104	movl	$MSR_IA32_MISC_ENABLE, %ecx
105	wrmsr
1061:
107
108	/* Do any other stuff... */
109
110#ifndef CONFIG_64BIT
111	/* This could also be done in C code... */
112	movl	pmode_cr3, %eax
113	movl	%eax, %cr3
114
115	movl	pmode_cr4, %ecx
116	jecxz	1f
117	movl	%ecx, %cr4
1181:
119	movl	pmode_efer, %eax
120	movl	pmode_efer + 4, %edx
121	movl	%eax, %ecx
122	orl	%edx, %ecx
123	jz	1f
124	movl	$MSR_EFER, %ecx
125	wrmsr
1261:
127
128	lgdtl	pmode_gdt
129
130	/* This really couldn't... */
131	movl	pmode_cr0, %eax
132	movl	%eax, %cr0
133	jmp	pmode_return
134#else
135	pushw	$0
136	pushw	trampoline_segment
137	pushw	$0
138	lret
139#endif
140
141bogus_real_magic:
1421:
143	hlt
144	jmp	1b
145
146	.data
147	.balign	8
148
149	/* This is the standard real-mode IDT */
150wakeup_idt:
151	.word	0xffff		/* limit */
152	.long	0		/* address */
153	.word	0
154
155	.globl	HEAP, heap_end
156HEAP:
157	.long	wakeup_heap
158heap_end:
159	.long	wakeup_stack
160
161	.bss
162wakeup_heap:
163	.space	2048
164wakeup_stack:
165	.space	2048
166wakeup_stack_end:
167
168	.section ".signature","a"
169end_signature:
170	.long	WAKEUP_END_SIGNATURE
171