145b5a378SSimon Glass/* 245b5a378SSimon Glass * Copyright (c) 2015 Google, Inc 345b5a378SSimon Glass * 445b5a378SSimon Glass * SPDX-License-Identifier: GPL-2.0 545b5a378SSimon Glass * 645b5a378SSimon Glass * Taken from coreboot file of the same name 745b5a378SSimon Glass */ 845b5a378SSimon Glass 945b5a378SSimon Glass/* 1045b5a378SSimon Glass * The SIPI vector is responsible for initializing the APs in the sytem. It 1145b5a378SSimon Glass * loads microcode, sets up MSRs, and enables caching before calling into 1245b5a378SSimon Glass * C code 1345b5a378SSimon Glass */ 1445b5a378SSimon Glass 1545b5a378SSimon Glass#include <asm/global_data.h> 1645b5a378SSimon Glass#include <asm/msr-index.h> 1745b5a378SSimon Glass#include <asm/processor.h> 1845b5a378SSimon Glass#include <asm/processor-flags.h> 1945b5a378SSimon Glass#include <asm/sipi.h> 2045b5a378SSimon Glass 2145b5a378SSimon Glass#define CODE_SEG (X86_GDT_ENTRY_32BIT_CS * X86_GDT_ENTRY_SIZE) 2245b5a378SSimon Glass#define DATA_SEG (X86_GDT_ENTRY_32BIT_DS * X86_GDT_ENTRY_SIZE) 2345b5a378SSimon Glass 2445b5a378SSimon Glass/* 2545b5a378SSimon Glass * First we have the 16-bit section. Every AP process starts here. 2645b5a378SSimon Glass * The simple task is to load U-Boot's Global Descriptor Table (GDT) to allow 2745b5a378SSimon Glass * U-Boot's 32-bit code to become visible, then jump to ap_start. 2845b5a378SSimon Glass * 2945b5a378SSimon Glass * Note that this code is copied to RAM below 1MB in mp_init.c, and runs from 3045b5a378SSimon Glass * there, but the 32-bit code (ap_start and onwards) is part of U-Boot and 3145b5a378SSimon Glass * is therefore relocated to the top of RAM with other U-Boot code. This 3245b5a378SSimon Glass * means that for the 16-bit code we must write relocatable code, but for the 3345b5a378SSimon Glass * rest, we can do what we like. 3445b5a378SSimon Glass */ 3545b5a378SSimon Glass.text 3645b5a378SSimon Glass.code16 3745b5a378SSimon Glass.globl ap_start16 3845b5a378SSimon Glassap_start16: 3945b5a378SSimon Glass cli 4045b5a378SSimon Glass xorl %eax, %eax 4145b5a378SSimon Glass movl %eax, %cr3 /* Invalidate TLB */ 4245b5a378SSimon Glass 4345b5a378SSimon Glass /* setup the data segment */ 4445b5a378SSimon Glass movw %cs, %ax 4545b5a378SSimon Glass movw %ax, %ds 4645b5a378SSimon Glass 4745b5a378SSimon Glass /* Use an address relative to the data segment for the GDT */ 4845b5a378SSimon Glass movl $gdtaddr, %ebx 4945b5a378SSimon Glass subl $ap_start16, %ebx 5045b5a378SSimon Glass 5145b5a378SSimon Glass data32 lgdt (%ebx) 5245b5a378SSimon Glass 5345b5a378SSimon Glass movl %cr0, %eax 5445b5a378SSimon Glass andl $(~(X86_CR0_PG | X86_CR0_AM | X86_CR0_WP | X86_CR0_NE | \ 5545b5a378SSimon Glass X86_CR0_TS | X86_CR0_EM | X86_CR0_MP)), %eax 5645b5a378SSimon Glass orl $(X86_CR0_NW | X86_CR0_CD | X86_CR0_PE), %eax 5745b5a378SSimon Glass movl %eax, %cr0 5845b5a378SSimon Glass 5945b5a378SSimon Glass movl $ap_start_jmp, %eax 6045b5a378SSimon Glass subl $ap_start16, %eax 6145b5a378SSimon Glass movw %ax, %bp 6245b5a378SSimon Glass 6345b5a378SSimon Glass /* Jump to ap_start within U-Boot */ 6445b5a378SSimon Glassdata32 cs ljmp *(%bp) 6545b5a378SSimon Glass 6645b5a378SSimon Glass .align 4 6745b5a378SSimon Glass.globl sipi_params_16bit 6845b5a378SSimon Glasssipi_params_16bit: 6945b5a378SSimon Glass /* 48-bit far pointer */ 7045b5a378SSimon Glassap_start_jmp: 7145b5a378SSimon Glass .long 0 /* offset set to ap_start by U-Boot */ 7245b5a378SSimon Glass .word CODE_SEG /* segment */ 7345b5a378SSimon Glass 7445b5a378SSimon Glass .word 0 /* padding */ 7545b5a378SSimon Glassgdtaddr: 7645b5a378SSimon Glass .word 0 /* limit */ 7745b5a378SSimon Glass .long 0 /* table */ 7845b5a378SSimon Glass .word 0 /* unused */ 7945b5a378SSimon Glass 8045b5a378SSimon Glass.globl ap_start16_code_end 8145b5a378SSimon Glassap_start16_code_end: 8245b5a378SSimon Glass 8345b5a378SSimon Glass/* 8445b5a378SSimon Glass * Set up the special 'fs' segment for global_data. Then jump to ap_continue 8545b5a378SSimon Glass * to set up the AP. 8645b5a378SSimon Glass */ 8745b5a378SSimon Glass.globl ap_start 8845b5a378SSimon Glassap_start: 8945b5a378SSimon Glass .code32 9045b5a378SSimon Glass movw $DATA_SEG, %ax 9145b5a378SSimon Glass movw %ax, %ds 9245b5a378SSimon Glass movw %ax, %es 9345b5a378SSimon Glass movw %ax, %ss 9445b5a378SSimon Glass movw %ax, %gs 9545b5a378SSimon Glass 9645b5a378SSimon Glass movw $(X86_GDT_ENTRY_32BIT_FS * X86_GDT_ENTRY_SIZE), %ax 9745b5a378SSimon Glass movw %ax, %fs 9845b5a378SSimon Glass 9945b5a378SSimon Glass /* Load the Interrupt descriptor table */ 10045b5a378SSimon Glass mov idt_ptr, %ebx 10145b5a378SSimon Glass lidt (%ebx) 10245b5a378SSimon Glass 10345b5a378SSimon Glass /* Obtain cpu number */ 10445b5a378SSimon Glass movl ap_count, %eax 10545b5a378SSimon Glass1: 10645b5a378SSimon Glass movl %eax, %ecx 10745b5a378SSimon Glass inc %ecx 10845b5a378SSimon Glass lock cmpxchg %ecx, ap_count 10945b5a378SSimon Glass jnz 1b 11045b5a378SSimon Glass 11145b5a378SSimon Glass /* Setup stacks for each CPU */ 11245b5a378SSimon Glass movl stack_size, %eax 11345b5a378SSimon Glass mul %ecx 11445b5a378SSimon Glass movl stack_top, %edx 11545b5a378SSimon Glass subl %eax, %edx 11645b5a378SSimon Glass mov %edx, %esp 11745b5a378SSimon Glass /* Save cpu number */ 11845b5a378SSimon Glass mov %ecx, %esi 11945b5a378SSimon Glass 12045b5a378SSimon Glass /* Determine if one should check microcode versions */ 12145b5a378SSimon Glass mov microcode_ptr, %edi 12245b5a378SSimon Glass test %edi, %edi 12345b5a378SSimon Glass jz microcode_done /* Bypass if no microde exists */ 12445b5a378SSimon Glass 12545b5a378SSimon Glass /* Get the Microcode version */ 12645b5a378SSimon Glass mov $1, %eax 12745b5a378SSimon Glass cpuid 12845b5a378SSimon Glass mov $MSR_IA32_UCODE_REV, %ecx 12945b5a378SSimon Glass rdmsr 13045b5a378SSimon Glass /* If something already loaded skip loading again */ 13145b5a378SSimon Glass test %edx, %edx 13245b5a378SSimon Glass jnz microcode_done 13345b5a378SSimon Glass 13445b5a378SSimon Glass /* Determine if parallel microcode loading is allowed */ 13545b5a378SSimon Glass cmp $0xffffffff, microcode_lock 13645b5a378SSimon Glass je load_microcode 13745b5a378SSimon Glass 13845b5a378SSimon Glass /* Protect microcode loading */ 13945b5a378SSimon Glasslock_microcode: 14045b5a378SSimon Glass lock bts $0, microcode_lock 14145b5a378SSimon Glass jc lock_microcode 14245b5a378SSimon Glass 14345b5a378SSimon Glassload_microcode: 14445b5a378SSimon Glass /* Load new microcode */ 14545b5a378SSimon Glass mov $MSR_IA32_UCODE_WRITE, %ecx 14645b5a378SSimon Glass xor %edx, %edx 14745b5a378SSimon Glass mov %edi, %eax 14845b5a378SSimon Glass /* 14945b5a378SSimon Glass * The microcode pointer is passed in pointing to the header. Adjust 15045b5a378SSimon Glass * pointer to reflect the payload (header size is 48 bytes) 15145b5a378SSimon Glass */ 15245b5a378SSimon Glass add $UCODE_HEADER_LEN, %eax 15345b5a378SSimon Glass pusha 15445b5a378SSimon Glass wrmsr 15545b5a378SSimon Glass popa 15645b5a378SSimon Glass 15745b5a378SSimon Glass /* Unconditionally unlock microcode loading */ 15845b5a378SSimon Glass cmp $0xffffffff, microcode_lock 15945b5a378SSimon Glass je microcode_done 16045b5a378SSimon Glass 16145b5a378SSimon Glass xor %eax, %eax 16245b5a378SSimon Glass mov %eax, microcode_lock 16345b5a378SSimon Glass 16445b5a378SSimon Glassmicrocode_done: 16545b5a378SSimon Glass /* 16645b5a378SSimon Glass * Load MSRs. Each entry in the table consists of: 16745b5a378SSimon Glass * 0: index, 16845b5a378SSimon Glass * 4: value[31:0] 16945b5a378SSimon Glass * 8: value[63:32] 17045b5a378SSimon Glass * See struct saved_msr in mp_init.c. 17145b5a378SSimon Glass */ 17245b5a378SSimon Glass mov msr_table_ptr, %edi 17345b5a378SSimon Glass mov msr_count, %ebx 17445b5a378SSimon Glass test %ebx, %ebx 17545b5a378SSimon Glass jz 1f 17645b5a378SSimon Glassload_msr: 17745b5a378SSimon Glass mov (%edi), %ecx 17845b5a378SSimon Glass mov 4(%edi), %eax 17945b5a378SSimon Glass mov 8(%edi), %edx 18045b5a378SSimon Glass wrmsr 18145b5a378SSimon Glass add $12, %edi 18245b5a378SSimon Glass dec %ebx 18345b5a378SSimon Glass jnz load_msr 18445b5a378SSimon Glass 18545b5a378SSimon Glass1: 18645b5a378SSimon Glass /* Enable caching */ 18745b5a378SSimon Glass mov %cr0, %eax 18845b5a378SSimon Glass andl $(~(X86_CR0_CD | X86_CR0_NW)), %eax 18945b5a378SSimon Glass mov %eax, %cr0 19045b5a378SSimon Glass 19145b5a378SSimon Glass /* c_handler(cpu_num) */ 19245b5a378SSimon Glass movl %esi, %eax /* cpu_num */ 193ef1683d5SBin Meng mov c_handler, %esi 194ef1683d5SBin Meng call *%esi 19545b5a378SSimon Glass 196*d116b53fSSimon Glass /* This matches struct sipi_param */ 19745b5a378SSimon Glass .align 4 19845b5a378SSimon Glass.globl sipi_params 19945b5a378SSimon Glasssipi_params: 20045b5a378SSimon Glassidt_ptr: 20145b5a378SSimon Glass .long 0 20245b5a378SSimon Glassstack_top: 20345b5a378SSimon Glass .long 0 20445b5a378SSimon Glassstack_size: 20545b5a378SSimon Glass .long 0 20645b5a378SSimon Glassmicrocode_lock: 20745b5a378SSimon Glass .long 0 20845b5a378SSimon Glassmicrocode_ptr: 20945b5a378SSimon Glass .long 0 21045b5a378SSimon Glassmsr_table_ptr: 21145b5a378SSimon Glass .long 0 21245b5a378SSimon Glassmsr_count: 21345b5a378SSimon Glass .long 0 21445b5a378SSimon Glassc_handler: 21545b5a378SSimon Glass .long 0 21645b5a378SSimon Glassap_count: 21745b5a378SSimon Glass .long 0 218