217 lines
4.4 KiB
ArmAsm
217 lines
4.4 KiB
ArmAsm
/* 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
|