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