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