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