1/* SPDX-License-Identifier: GPL-2.0 */ 2/* 3 * Copyright (c) 2015 Google, Inc 4 * 5 * Taken from coreboot file of the same name 6 */ 7 8/* 9 * The SIPI vector is responsible for initializing the APs in the sytem. It 10 * loads microcode, sets up MSRs, and enables caching before calling into 11 * C code 12 */ 13 14#include <asm/msr-index.h> 15#include <asm/processor.h> 16#include <asm/processor-flags.h> 17#include <asm/sipi.h> 18 19#define CODE_SEG (X86_GDT_ENTRY_32BIT_CS * X86_GDT_ENTRY_SIZE) 20#define DATA_SEG (X86_GDT_ENTRY_32BIT_DS * X86_GDT_ENTRY_SIZE) 21 22/* 23 * First we have the 16-bit section. Every AP process starts here. 24 * The simple task is to load U-Boot's Global Descriptor Table (GDT) to allow 25 * U-Boot's 32-bit code to become visible, then jump to ap_start. 26 * 27 * Note that this code is copied to RAM below 1MB in mp_init.c, and runs from 28 * there, but the 32-bit code (ap_start and onwards) is part of U-Boot and 29 * is therefore relocated to the top of RAM with other U-Boot code. This 30 * means that for the 16-bit code we must write relocatable code, but for the 31 * rest, we can do what we like. 32 */ 33.text 34.code16 35.globl ap_start16 36ap_start16: 37 cli 38 xorl %eax, %eax 39 movl %eax, %cr3 /* Invalidate TLB */ 40 41 /* setup the data segment */ 42 movw %cs, %ax 43 movw %ax, %ds 44 45 /* Use an address relative to the data segment for the GDT */ 46 movl $gdtaddr, %ebx 47 subl $ap_start16, %ebx 48 49 data32 lgdt (%ebx) 50 51 movl %cr0, %eax 52 andl $(~(X86_CR0_PG | X86_CR0_AM | X86_CR0_WP | X86_CR0_NE | \ 53 X86_CR0_TS | X86_CR0_EM | X86_CR0_MP)), %eax 54 orl $(X86_CR0_NW | X86_CR0_CD | X86_CR0_PE), %eax 55 movl %eax, %cr0 56 57 movl $ap_start_jmp, %eax 58 subl $ap_start16, %eax 59 movw %ax, %bp 60 61 /* Jump to ap_start within U-Boot */ 62data32 cs ljmp *(%bp) 63 64 .align 4 65.globl sipi_params_16bit 66sipi_params_16bit: 67 /* 48-bit far pointer */ 68ap_start_jmp: 69 .long 0 /* offset set to ap_start by U-Boot */ 70 .word CODE_SEG /* segment */ 71 72 .word 0 /* padding */ 73gdtaddr: 74 .word 0 /* limit */ 75 .long 0 /* table */ 76 .word 0 /* unused */ 77 78.globl ap_start16_code_end 79ap_start16_code_end: 80 81/* 82 * Set up the special 'fs' segment for global_data. Then jump to ap_continue 83 * to set up the AP. 84 */ 85.globl ap_start 86ap_start: 87 .code32 88 movw $DATA_SEG, %ax 89 movw %ax, %ds 90 movw %ax, %es 91 movw %ax, %ss 92 movw %ax, %gs 93 94 movw $(X86_GDT_ENTRY_32BIT_FS * X86_GDT_ENTRY_SIZE), %ax 95 movw %ax, %fs 96 97 /* Load the Interrupt descriptor table */ 98 mov idt_ptr, %ebx 99 lidt (%ebx) 100 101 /* Obtain cpu number */ 102 movl ap_count, %eax 1031: 104 movl %eax, %ecx 105 inc %ecx 106 lock cmpxchg %ecx, ap_count 107 jnz 1b 108 109 /* Setup stacks for each CPU */ 110 movl stack_size, %eax 111 mul %ecx 112 movl stack_top, %edx 113 subl %eax, %edx 114 mov %edx, %esp 115 /* Save cpu number */ 116 mov %ecx, %esi 117 118 /* Determine if one should check microcode versions */ 119 mov microcode_ptr, %edi 120 test %edi, %edi 121 jz microcode_done /* Bypass if no microde exists */ 122 123 /* Get the Microcode version */ 124 mov $1, %eax 125 cpuid 126 mov $MSR_IA32_UCODE_REV, %ecx 127 rdmsr 128 /* If something already loaded skip loading again */ 129 test %edx, %edx 130 jnz microcode_done 131 132 /* Determine if parallel microcode loading is allowed */ 133 cmpl $0xffffffff, microcode_lock 134 je load_microcode 135 136 /* Protect microcode loading */ 137lock_microcode: 138 lock btsl $0, microcode_lock 139 jc lock_microcode 140 141load_microcode: 142 /* Load new microcode */ 143 mov $MSR_IA32_UCODE_WRITE, %ecx 144 xor %edx, %edx 145 mov %edi, %eax 146 /* 147 * The microcode pointer is passed in pointing to the header. Adjust 148 * pointer to reflect the payload (header size is 48 bytes) 149 */ 150 add $UCODE_HEADER_LEN, %eax 151 pusha 152 wrmsr 153 popa 154 155 /* Unconditionally unlock microcode loading */ 156 cmpl $0xffffffff, microcode_lock 157 je microcode_done 158 159 xor %eax, %eax 160 mov %eax, microcode_lock 161 162microcode_done: 163 /* 164 * Load MSRs. Each entry in the table consists of: 165 * 0: index, 166 * 4: value[31:0] 167 * 8: value[63:32] 168 * See struct saved_msr in mp_init.c. 169 */ 170 mov msr_table_ptr, %edi 171 mov msr_count, %ebx 172 test %ebx, %ebx 173 jz 1f 174load_msr: 175 mov (%edi), %ecx 176 mov 4(%edi), %eax 177 mov 8(%edi), %edx 178 wrmsr 179 add $12, %edi 180 dec %ebx 181 jnz load_msr 182 1831: 184 /* Enable caching */ 185 mov %cr0, %eax 186 andl $(~(X86_CR0_CD | X86_CR0_NW)), %eax 187 mov %eax, %cr0 188 189 /* c_handler(cpu_num) */ 190 movl %esi, %eax /* cpu_num */ 191 mov c_handler, %esi 192 call *%esi 193 194 /* This matches struct sipi_param */ 195 .align 4 196.globl sipi_params 197sipi_params: 198idt_ptr: 199 .long 0 200stack_top: 201 .long 0 202stack_size: 203 .long 0 204microcode_lock: 205 .long 0 206microcode_ptr: 207 .long 0 208msr_table_ptr: 209 .long 0 210msr_count: 211 .long 0 212c_handler: 213 .long 0 214ap_count: 215 .long 0 216