xref: /aosp_15_r20/external/coreboot/src/cpu/x86/entry16.S (revision b9411a12aaaa7e1e6a6fb7c5e057f44ee179a49c)
1/* SPDX-License-Identifier: BSD-3-Clause */
2
3/*
4 * This software and ancillary information (herein called SOFTWARE)
5 * called LinuxBIOS is made available under the terms described here.
6 *
7 * The SOFTWARE has been approved for release with associated
8 * LA-CC Number 00-34. Unless otherwise indicated, this SOFTWARE has
9 * been authored by an employee or employees of the University of
10 * California, operator of the Los Alamos National Laboratory under
11 * Contract No. W-7405-ENG-36 with the U.S. Department of Energy.
12 *
13 * The U.S. Government has rights to use, reproduce, and distribute this
14 * SOFTWARE. The public may copy, distribute, prepare derivative works
15 * and publicly display this SOFTWARE without charge, provided that this
16 * Notice and any statement of authorship are reproduced on all copies.
17 *
18 * Neither the Government nor the University makes any warranty, express
19 * or implied, or assumes any liability or responsibility for the use of
20 * this SOFTWARE.  If SOFTWARE is modified to produce derivative works,
21 * such modified SOFTWARE should be clearly marked, so as not to confuse
22 * it with the version available from LANL.
23 *
24 */
25
26
27/* Start code to put an i386 or later processor into 32-bit protected mode.
28 */
29
30#include <arch/rom_segs.h>
31#include <cpu/x86/post_code.h>
32
33.section .init._start, "ax", @progbits
34
35/* Symbol _start16bit must reachable from the reset vector, and be aligned to
36 * 4kB to start AP CPUs with Startup IPI message without RAM.
37 */
38.code16
39.globl _start16bit
40.type _start16bit, @function
41
42_start16bit:
43	cli
44	/* Save the BIST result */
45	movl	%eax, %ebp
46	post_code(POSTCODE_RESET_VECTOR_CORRECT)
47
48	/* IMMEDIATELY invalidate the translation lookaside buffer (TLB) before
49	 * executing any further code. Even though paging is disabled we
50	 * could still get false address translations due to the TLB if we
51	 * didn't invalidate it. Thanks to [email protected] for this TLB fix.
52	 */
53
54	xorl	%eax, %eax
55	movl	%eax, %cr3    /* Invalidate TLB*/
56
57	/* Invalidating the cache here seems to be a bad idea on
58	 * modern processors.  Don't.
59	 * If we are hyperthreaded or we have multiple cores it is bad,
60	 * for SMP startup.  On Opterons it causes a 5 second delay.
61	 * Invalidating the cache was pure paranoia in any event.
62	 * If your CPU needs it you can write a CPU dependent version of
63	 * entry16.inc.
64	 */
65
66	/* Note: gas handles memory addresses in 16 bit code very poorly.
67	 * In particular it doesn't appear to have a directive allowing you
68	 * associate a section or even an absolute offset with a segment register.
69	 *
70	 * This means that anything except cs:ip relative offsets are
71	 * a real pain in 16 bit mode.  And explains why it is almost
72	 * impossible to get gas to do lgdt correctly.
73	 *
74	 * One way to work around this is to have the linker do the
75	 * math instead of the assembler.  This solves the very
76	 * practical problem of being able to write code that can
77	 * be relocated.
78	 *
79	 * An lgdt call before we have memory enabled cannot be
80	 * position independent, as we cannot execute a call
81	 * instruction to get our current instruction pointer.
82	 * So while this code is relocatable it isn't arbitrarily
83	 * relocatable.
84	 *
85	 * The criteria for relocation have been relaxed to their
86	 * utmost, so that we can use the same code for both
87	 * our initial entry point and startup of the second CPU.
88	 * The code assumes when executing at _start16bit that:
89	 * (((cs & 0xfff) == 0) and (ip == _start16bit & 0xffff))
90	 * or
91	 * ((cs == anything) and (ip == 0)).
92	 *
93	 * The restrictions in reset16.inc mean that _start16bit initially
94	 * must be loaded at or above 0xffff0000 or below 0x100000.
95	 *
96	 * The linker scripts computes gdtptr16_offset by simply returning
97	 * the low 16 bits.  This means that the initial segment used
98	 * when start is called must be 64K aligned.  This should not
99	 * restrict the address as the ip address can be anything.
100	 *
101	 * Also load an IDT with NULL limit to prevent the 16bit IDT being used
102	 * in protected mode before c_start.S sets up a 32bit IDT when entering
103	 * RAM stage. In practise: CPU will shutdown on any exception.
104	 * See IA32 manual Vol 3A 19.26 Interrupts.
105	 */
106
107	movw	%cs, %ax
108	shlw	$4, %ax
109	movw	$nullidt_offset, %bx
110	subw	%ax, %bx
111	lidt	%cs:(%bx)
112	movw	$gdtptr_offset, %bx
113	subw	%ax, %bx
114	lgdtl	%cs:(%bx)
115
116#if CONFIG(INTEL_CBNT_SUPPORT)
117#include <cpu/intel/msr.h>
118	movl	$MSR_BOOT_GUARD_SACM_INFO, %ecx
119	rdmsr
120	andl	$B_BOOT_GUARD_SACM_INFO_NEM_ENABLED, %eax
121	jz	1f
122	movl	%cr0, %eax
123	andl	$0x7FFAFFD1, %eax /* PG,AM,WP,NE,TS,EM,MP = 0 */
124	orl	$0x01, %eax /* PE = 1 */
125	movl	%eax, %cr0
126	jmp	2f
127#endif
1281:
129	movl	%cr0, %eax
130	andl	$0x7FFAFFD1, %eax /* PG,AM,WP,NE,TS,EM,MP = 0 */
131	orl	$0x60000001, %eax /* CD, NW, PE = 1 */
132	movl	%eax, %cr0
1332:
134
135	/* Restore BIST to %eax */
136	movl	%ebp, %eax
137
138	/* Now that we are in protected mode jump to a 32 bit code segment. */
139	ljmpl	$ROM_CODE_SEG, $bootblock_protected_mode_entry
140
141	/**
142	 * The gdt is defined in gdt_init.S, it has a 4 Gb code segment
143	 * at 0x08, and a 4 GB data segment at 0x10;
144	 */
145__gdtptr:
146	.long	gdtptr
147
148.align	4
149.globl nullidt
150nullidt:
151	.word	0	/* limit */
152	.long	0
153	.word	0
154