blob: 40cc27f1e1d0db20cb23d9c0e59c6d8289bc780a [file] [log] [blame]
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (c) 2015 Google, Inc
*
* Taken from coreboot file of the same name
*/
/*
* The SIPI vector is responsible for initializing the APs in the sytem. It
* loads microcode, sets up MSRs, and enables caching before calling into
* C code
*/
#include <asm/global_data.h>
#include <asm/msr-index.h>
#include <asm/processor.h>
#include <asm/processor-flags.h>
#include <asm/sipi.h>
#define CODE_SEG (X86_GDT_ENTRY_32BIT_CS * X86_GDT_ENTRY_SIZE)
#define DATA_SEG (X86_GDT_ENTRY_32BIT_DS * X86_GDT_ENTRY_SIZE)
/*
* First we have the 16-bit section. Every AP process starts here.
* The simple task is to load U-Boot's Global Descriptor Table (GDT) to allow
* U-Boot's 32-bit code to become visible, then jump to ap_start.
*
* Note that this code is copied to RAM below 1MB in mp_init.c, and runs from
* there, but the 32-bit code (ap_start and onwards) is part of U-Boot and
* is therefore relocated to the top of RAM with other U-Boot code. This
* means that for the 16-bit code we must write relocatable code, but for the
* rest, we can do what we like.
*/
.text
.code16
.globl ap_start16
ap_start16:
cli
xorl %eax, %eax
movl %eax, %cr3 /* Invalidate TLB */
/* setup the data segment */
movw %cs, %ax
movw %ax, %ds
/* Use an address relative to the data segment for the GDT */
movl $gdtaddr, %ebx
subl $ap_start16, %ebx
data32 lgdt (%ebx)
movl %cr0, %eax
andl $(~(X86_CR0_PG | X86_CR0_AM | X86_CR0_WP | X86_CR0_NE | \
X86_CR0_TS | X86_CR0_EM | X86_CR0_MP)), %eax
orl $(X86_CR0_NW | X86_CR0_CD | X86_CR0_PE), %eax
movl %eax, %cr0
movl $ap_start_jmp, %eax
subl $ap_start16, %eax
movw %ax, %bp
/* Jump to ap_start within U-Boot */
data32 cs ljmp *(%bp)
.align 4
.globl sipi_params_16bit
sipi_params_16bit:
/* 48-bit far pointer */
ap_start_jmp:
.long 0 /* offset set to ap_start by U-Boot */
.word CODE_SEG /* segment */
.word 0 /* padding */
gdtaddr:
.word 0 /* limit */
.long 0 /* table */
.word 0 /* unused */
.globl ap_start16_code_end
ap_start16_code_end:
/*
* Set up the special 'fs' segment for global_data. Then jump to ap_continue
* to set up the AP.
*/
.globl ap_start
ap_start:
.code32
movw $DATA_SEG, %ax
movw %ax, %ds
movw %ax, %es
movw %ax, %ss
movw %ax, %gs
movw $(X86_GDT_ENTRY_32BIT_FS * X86_GDT_ENTRY_SIZE), %ax
movw %ax, %fs
/* Load the Interrupt descriptor table */
mov idt_ptr, %ebx
lidt (%ebx)
/* Obtain cpu number */
movl ap_count, %eax
1:
movl %eax, %ecx
inc %ecx
lock cmpxchg %ecx, ap_count
jnz 1b
/* Setup stacks for each CPU */
movl stack_size, %eax
mul %ecx
movl stack_top, %edx
subl %eax, %edx
mov %edx, %esp
/* Save cpu number */
mov %ecx, %esi
/* Determine if one should check microcode versions */
mov microcode_ptr, %edi
test %edi, %edi
jz microcode_done /* Bypass if no microde exists */
/* Get the Microcode version */
mov $1, %eax
cpuid
mov $MSR_IA32_UCODE_REV, %ecx
rdmsr
/* If something already loaded skip loading again */
test %edx, %edx
jnz microcode_done
/* Determine if parallel microcode loading is allowed */
cmp $0xffffffff, microcode_lock
je load_microcode
/* Protect microcode loading */
lock_microcode:
lock bts $0, microcode_lock
jc lock_microcode
load_microcode:
/* Load new microcode */
mov $MSR_IA32_UCODE_WRITE, %ecx
xor %edx, %edx
mov %edi, %eax
/*
* The microcode pointer is passed in pointing to the header. Adjust
* pointer to reflect the payload (header size is 48 bytes)
*/
add $UCODE_HEADER_LEN, %eax
pusha
wrmsr
popa
/* Unconditionally unlock microcode loading */
cmp $0xffffffff, microcode_lock
je microcode_done
xor %eax, %eax
mov %eax, microcode_lock
microcode_done:
/*
* Load MSRs. Each entry in the table consists of:
* 0: index,
* 4: value[31:0]
* 8: value[63:32]
* See struct saved_msr in mp_init.c.
*/
mov msr_table_ptr, %edi
mov msr_count, %ebx
test %ebx, %ebx
jz 1f
load_msr:
mov (%edi), %ecx
mov 4(%edi), %eax
mov 8(%edi), %edx
wrmsr
add $12, %edi
dec %ebx
jnz load_msr
1:
/* Enable caching */
mov %cr0, %eax
andl $(~(X86_CR0_CD | X86_CR0_NW)), %eax
mov %eax, %cr0
/* c_handler(cpu_num) */
movl %esi, %eax /* cpu_num */
mov c_handler, %esi
call *%esi
/* This matches struct sipi_param */
.align 4
.globl sipi_params
sipi_params:
idt_ptr:
.long 0
stack_top:
.long 0
stack_size:
.long 0
microcode_lock:
.long 0
microcode_ptr:
.long 0
msr_table_ptr:
.long 0
msr_count:
.long 0
c_handler:
.long 0
ap_count:
.long 0