xref: /openbmc/u-boot/arch/x86/cpu/sipi_vector.S (revision 83d290c56fab2d38cd1ab4c4cc7099559c1d5046)
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