1/*
2 * Copyright (C) 2018-2022 Intel Corporation.
3 *
4 * SPDX-License-Identifier: BSD-3-Clause
5 *
6 * This is entry for AP startup and BSP S3 wakeup
7 *
8 * When system jump to trampoline_start16, the CPU is in x86 real
9 * mode with no stack setup. CS:IP points to trampoline_start16.
10 *
11 * The CPU will be changed to long mode finally with temperay
12 * page table and gdt in this file. Then jump to different C main
13 * entry according to whether it's AP startup or BSP S3 resume.
14 * The real page table and gdt will be setup in C main entry.
15 */
16
17/* NOTE:
18 *
19 * MISRA C requires that all unsigned constants should have the suffix 'U'
20 * (e.g. 0xffU), but the assembler may not accept such C-style constants. For
21 * example, binutils 2.26 fails to compile assembly in that case. To work this
22 * around, all unsigned constants must be explicitly spells out in assembly
23 * with a comment tracking the original expression from which the magic
24 * number is calculated. As an example:
25 *
26 *    /* 0x00000668 =
27 *     *    (CR4_DE | CR4_PAE | CR4_MCE | CR4_OSFXSR | CR4_OSXMMEXCPT) *\/
28 *    movl    $0x00000668, %eax
29 *
30 * Make sure that these numbers are updated accordingly if the definition of
31 * the macros involved are changed.
32 */
33
34    .extern     init_secondary_pcpu
35
36    .section     .trampoline_reset,"ax"
37
38   .align   4
39   .code16
40   .global      trampoline_start16
41   .org 0
42trampoline_start16:
43
44    /* Disable local interrupts */
45    cli
46
47    /*
48     * There are two paths system could come here:
49     *   - AP startup
50     *     Silicon will set AP to real mode and setup CS:IP before
51     *     jmp to trampoline_start16. And the IP is always 0 for sure.
52     *   - BSP wakeup from S3
53     *     Some bootloader (like ABL) doesn't guarante IP is set to
54     *     zero before jump to trampoline_start16 after system resume
55     *     from S3.
56     *
57     * To make trampoline code could work with all these cases, a far
58     * jump is issued here as fixup. It will update the CS:IP according
59     * to where the trampoline code is located.
60     *
61     * Here, we issue a far jump with "JMP ptr16:16" format (please refer
62     * sdm vol2A - JMP instruction description). The jump target is set
63     * to trampoline_fixup_target_addr. From trampoline_fixup_target_addr,
64     * The CS has same value for both AP startup and BSP wakeup from S3.
65     *
66     * Because the limitation of real mode (can't access ip register
67     * directly. So can't setup the trampoline_fixup_ip and
68     * trampoline_fixup_cs), we have to update the trampoline_fixup_ip
69     * and trampoline_fixup_cs when we preparing the trampoline code.
70     *
71     * Refer to preparing_trampoline() for fixup CS:IP setup
72     */
73    .byte 0xea	/* Opcode of "JMP ptr16:16" */
74    .global trampoline_fixup_ip
75trampoline_fixup_ip:
76    .word   0	/* "EIP is intruction following JUMP instruction" */
77    .global trampoline_fixup_cs
78trampoline_fixup_cs:
79    .word   0	/* CS */
80
81    .global trampoline_fixup_target
82trampoline_fixup_target:
83    mov %cs, %ax
84    mov %ax, %ds
85
86    /* Set DE, PAE, MCE and OS support bits in CR4 */
87
88    /* 0x00000668 =
89     *    (CR4_DE | CR4_PAE | CR4_MCE | CR4_OSFXSR | CR4_OSXMMEXCPT) */
90    movl    $0x00000668, %eax
91    mov     %eax, %cr4
92
93    /* Set CR3 to PML4 table address */
94
95    movl    $cpu_boot_page_tables_ptr, %ebx
96    mov     (%ebx), %eax
97    mov     %eax, %cr3
98
99    /* Set LME bit in EFER */
100
101    /* 0xc0000080 = MSR_IA32_EFER */
102    movl    $0xc0000080, %ecx
103    rdmsr
104    /* 0x00000100 = MSR_IA32_EFER_LME_BIT */
105    orl     $0x00000100, %eax
106    wrmsr
107
108    /* 0xc0000080 = MSR_IA32_EFER */
109    movl    $0xc0000080, %ecx
110    rdmsr
111    /* 0x00000800 = MSR_IA32_EFER_NXE_BIT */
112    orl     $0x00000800, %eax
113    wrmsr
114
115    /* Enable paging, protection, numeric error and co-processor
116       monitoring in CR0 to enter long mode */
117
118    mov     %cr0, %ebx
119    /* 0x80000023 = (CR0_PG | CR0_PE | CR0_MP | CR0_NE) */
120    orl     $0x80000023, %ebx
121    mov     %ebx, %cr0
122
123    /* Load temportary GDT pointer value */
124    lgdt    (trampoline_gdt_ptr - trampoline_start16)
125
126    /* Perform a long jump based to start executing in 64-bit mode */
127
128    movl    $trampoline_start64_fixup, %ebx
129    ljmpl   *(%ebx)
130
131    .align 8
132    .global trampoline_start64_fixup
133trampoline_start64_fixup:
134    .long   trampoline_start64
135    /* 0x0008 = HOST_GDT_RING0_CODE_SEL */
136    .word   0x0008
137
138    .code64
139trampoline_start64:
140
141    /* Set up all other data segment registers */
142    /* 0x0010 = HOST_GDT_RING0_DATA_SEL */
143    movl    $0x0010, %eax
144    mov     %eax, %ss
145    mov     %eax, %ds
146    mov     %eax, %es
147    mov     %eax, %fs
148    mov     %eax, %gs
149
150    movq    secondary_cpu_stack(%rip), %rsp
151
152    /* Jump to C entry */
153    movq    main_entry(%rip), %rax
154    jmp     *%rax
155
156
157/* main entry */
158    .align  8
159    .global main_entry
160main_entry:
161    .quad   init_secondary_pcpu /* default entry is AP start entry */
162
163    .global secondary_cpu_stack
164secondary_cpu_stack:
165    .quad   0
166
167/* GDT table */
168    .align  4
169trampoline_gdt:
170    .quad   0x0000000000000000
171    .quad   0x00af9b000000ffff
172    .quad   0x00cf93000000ffff
173trampoline_gdt_end:
174
175/* GDT pointer */
176    .align  2
177    .global trampoline_gdt_ptr
178trampoline_gdt_ptr:
179    .short  (trampoline_gdt_end - trampoline_gdt) - 1
180    .quad   trampoline_gdt
181
182/* PML4, PDPT, and PD tables initialized to map first 4 GBytes of memory */
183    .align  4
184    .global cpu_boot_page_tables_ptr
185cpu_boot_page_tables_ptr:
186    .long cpu_boot_page_tables_start
187
188    /*0x1000 = PAGE_SIZE*/
189    .align  0x1000
190    .global cpu_boot_page_tables_start
191cpu_boot_page_tables_start:
192    /* 0x3 = (PAGE_PRESENT | PAGE_RW) */
193    .quad   trampoline_pdpt_addr + 0x3
194    /*0x1000 = PAGE_SIZE*/
195    .align  0x1000
196    .global trampoline_pdpt_addr
197trampoline_pdpt_addr:
198    address = 0
199    .rept   4
200    /* 0x3 = (PAGE_PRESENT | PAGE_RW) */
201    .quad   trampoline_pdt_addr + address + 0x3
202    /*0x1000 = PAGE_SIZE*/
203    address = address + 0x1000
204    .endr
205    /*0x1000 = PAGE_SIZE*/
206    .align  0x1000
207trampoline_pdt_addr:
208    address = 0
209    .rept  2048
210    /* 0x83 = (PAGE_PSE | PAGE_PRESENT | PAGE_RW) */
211    .quad  address + 0x83
212    address = address + 0x200000
213    .endr
214
215    .end
216