diff options
Diffstat (limited to 'arch/x86/cpu/sipi_vector.S')
-rw-r--r-- | arch/x86/cpu/sipi_vector.S | 216 |
1 files changed, 216 insertions, 0 deletions
diff --git a/arch/x86/cpu/sipi_vector.S b/arch/x86/cpu/sipi_vector.S new file mode 100644 index 0000000000..bcef12c6f1 --- /dev/null +++ b/arch/x86/cpu/sipi_vector.S @@ -0,0 +1,216 @@ +/* + * Copyright (c) 2015 Google, Inc + * + * SPDX-License-Identifier: GPL-2.0 + * + * 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, %eax + call *%eax + + .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 |