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