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