xref: /aosp_15_r20/external/coreboot/src/arch/x86/wakeup.S (revision b9411a12aaaa7e1e6a6fb7c5e057f44ee179a49c)
1/* SPDX-License-Identifier: GPL-2.0-only */
2
3#define WAKEUP_BASE		0x600
4#define RELOCATED(x)	(x - __wakeup + WAKEUP_BASE)
5
6/* CR0 bits */
7#define PE		(1 << 0)
8
9#if ENV_X86_64
10	.code64
11#else
12	.code32
13#endif
14
15	.globl __wakeup
16__wakeup:
17#if ENV_X86_64
18	/* When called in x86_64 mode, the resume vector is in %rdi
19	 * instead of the stack, save it in 4(%rsp) for the 32-bit code.
20	 * It's OK to overwrite the return address at (%rsp) because this
21	 * function doesn't return.
22	 */
23	mov	%edi, 4(%rsp)
24
25	xor	%rax,%rax
26	mov	%ss, %ax
27	push	%rax
28	mov	%rsp, %rax
29	add	$8, %rax
30	push	%rax
31	pushfq
32	push	$0x10
33	lea	3(%rip), %rax
34	push	%rax
35	iretq
36
37	.code32
38
39	/* disable paging */
40	mov	%cr0, %eax
41	btc	$31, %eax
42	mov	%eax, %cr0
43
44	/* disable long mode */
45	mov	$0xC0000080, %ecx
46	rdmsr
47	btc	$8, %eax
48	wrmsr
49#endif
50	/* First prepare the jmp to the resume vector */
51	mov	0x4(%esp), %eax	/* vector */
52	/* last 4 bits of linear addr are taken as offset */
53	andw	$0x0f, %ax
54	movw	%ax, (__wakeup_offset)
55	mov	0x4(%esp), %eax
56	/* the rest is taken as segment */
57	shr	$4, %eax
58	movw	%ax, (__wakeup_segment)
59
60	/* Activate the right segment descriptor real mode. */
61	ljmp	$0x28, $RELOCATED(1f)
621:
63.code16
64	/* 16 bit code from here on... */
65
66	/* Load the segment registers w/ properly configured
67	 * segment descriptors. They will retain these
68	 * configurations (limits, writability, etc.) once
69	 * protected mode is turned off.
70	 */
71	mov	$0x30, %ax
72	mov	%ax, %ds
73	mov	%ax, %es
74	mov	%ax, %fs
75	mov	%ax, %gs
76	mov	%ax, %ss
77
78	/* Turn off protection */
79	movl	%cr0, %eax
80	andl	$~PE, %eax
81	movl	%eax, %cr0
82
83	/* Now really going into real mode */
84	ljmp	$0, $RELOCATED(1f)
851:
86	movw	$0x0, %ax
87	movw	%ax, %ds
88	movw	%ax, %es
89	movw	%ax, %ss
90	movw	%ax, %fs
91	movw	%ax, %gs
92
93	/* This is a FAR JMP to the OS waking vector. The C code changed
94	 * the address to be correct.
95	 */
96	.byte 0xea
97
98__wakeup_offset = RELOCATED(.)
99	.word 0x0000
100
101__wakeup_segment = RELOCATED(.)
102	.word 0x0000
103
104	.globl __wakeup_size
105__wakeup_size:
106	.long . - __wakeup
107