summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/arch/x86/c_start.S35
-rw-r--r--src/arch/x86/include/arch/cpu.h29
-rw-r--r--src/cpu/x86/Kconfig8
-rw-r--r--src/cpu/x86/cpu_info.S.inc47
-rw-r--r--src/cpu/x86/mp_init.c16
-rw-r--r--src/cpu/x86/sipi_vector.S22
-rw-r--r--src/cpu/x86/smm/smm_stub.S30
-rw-r--r--src/include/cpu/x86/gdt.h2
8 files changed, 186 insertions, 3 deletions
diff --git a/src/arch/x86/c_start.S b/src/arch/x86/c_start.S
index cb7d5045ff..9e718fcab0 100644
--- a/src/arch/x86/c_start.S
+++ b/src/arch/x86/c_start.S
@@ -80,6 +80,20 @@ _start:
push_cpu_info
+#if CONFIG(CPU_INFO_V2)
+ /* Allocate the per_cpu_segment_data on the stack */
+ push_per_cpu_segment_data
+
+ /*
+ * Update the BSP's per_cpu_segment_descriptor to point to the
+ * per_cpu_segment_data that was allocated on the stack.
+ */
+ set_segment_descriptor_base $per_cpu_segment_descriptors, %esp
+
+ mov per_cpu_segment_selector, %eax
+ mov %eax, %gs
+#endif
+
/*
* Now we are finished. Memory is up, data is copied and
* bss is cleared. Now we call the main routine and
@@ -127,6 +141,7 @@ gdb_stub_breakpoint:
#endif
.globl gdt, gdt_end
+ .global per_cpu_segment_descriptors, per_cpu_segment_selector
gdtaddr:
.word gdt_end - gdt - 1
@@ -136,7 +151,7 @@ gdtaddr:
.long gdt /* we know the offset */
#endif
- .data
+ .data
/* This is the gdt for GCC part of coreboot.
* It is different from the gdt in ASM part of coreboot
@@ -206,8 +221,26 @@ gdt:
.word 0xffff, 0x0000
.byte 0x00, 0x9b, 0xaf, 0x00
#endif
+#if CONFIG(CPU_INFO_V2)
+per_cpu_segment_descriptors:
+ .rept CONFIG_MAX_CPUS
+ /* flat data segment */
+ .word 0xffff, 0x0000
+#if ENV_X86_64
+ .byte 0x00, 0x92, 0xcf, 0x00
+#else
+ .byte 0x00, 0x93, 0xcf, 0x00
+#endif
+ .endr
+#endif /* CPU_INFO_V2 */
gdt_end:
+#if CONFIG(CPU_INFO_V2)
+/* Segment selector pointing to the first per_cpu_segment_descriptor. */
+per_cpu_segment_selector:
+ .long per_cpu_segment_descriptors - gdt
+#endif /* CPU_INFO_V2 */
+
.section ".text._start", "ax", @progbits
#if ENV_X86_64
SetCodeSelector:
diff --git a/src/arch/x86/include/arch/cpu.h b/src/arch/x86/include/arch/cpu.h
index f736a147fe..8e96fae2c2 100644
--- a/src/arch/x86/include/arch/cpu.h
+++ b/src/arch/x86/include/arch/cpu.h
@@ -236,11 +236,40 @@ struct cpu_info {
#endif
};
+/*
+ * This structure describes the data allocated in the %gs segment for each CPU.
+ * In order to read from this structure you will need to use assembly to
+ * reference the segment.
+ *
+ * e.g., Reading the cpu_info pointer:
+ * %%gs:0
+ */
+struct per_cpu_segment_data {
+ /*
+ * Instead of keeping a `struct cpu_info`, we actually keep a pointer
+ * pointing to the cpu_info struct located in %ds. This prevents
+ * needing specific access functions to read the fields in the cpu_info.
+ */
+ struct cpu_info *cpu_info;
+};
+
static inline struct cpu_info *cpu_info(void)
{
+/* We use a #if because we don't want to mess with the &s below. */
+#if CONFIG(CPU_INFO_V2)
+ struct cpu_info *ci = NULL;
+
+ __asm__("mov %%gs:%c[offset], %[ci]"
+ : [ci] "=r" (ci)
+ : [offset] "i" (offsetof(struct per_cpu_segment_data, cpu_info))
+ );
+
+ return ci;
+#else
char s;
uintptr_t info = ALIGN_UP((uintptr_t)&s, CONFIG_STACK_SIZE) - sizeof(struct cpu_info);
return (struct cpu_info *)info;
+#endif /* CPU_INFO_V2 */
}
struct cpuinfo_x86 {
diff --git a/src/cpu/x86/Kconfig b/src/cpu/x86/Kconfig
index a5c2a4df47..99e33bb570 100644
--- a/src/cpu/x86/Kconfig
+++ b/src/cpu/x86/Kconfig
@@ -193,3 +193,11 @@ config RESERVE_MTRRS_FOR_OS
the system BIOS and the last 2 are to be reserved for OS usage.
However, modern OSes use PAT to control cacheability instead of
using MTRRs.
+
+config CPU_INFO_V2
+ bool
+ depends on PARALLEL_MP
+ help
+ Enables the new method of locating struct cpu_info. This new method
+ uses the %gs segment to locate the cpu_info pointer. The old method
+ relied on the stack being CONFIG_STACK_SIZE aligned.
diff --git a/src/cpu/x86/cpu_info.S.inc b/src/cpu/x86/cpu_info.S.inc
index 62b47ca52a..dffd1bc346 100644
--- a/src/cpu/x86/cpu_info.S.inc
+++ b/src/cpu/x86/cpu_info.S.inc
@@ -8,3 +8,50 @@
push \index /* index */
push $0 /* *cpu */
.endm
+
+/* Push struct per_cpu_segment_data */
+.macro push_per_cpu_segment_data cpu_info_pointer=%esp
+ push \cpu_info_pointer /* *cpu_info */
+.endm
+
+/*
+ * Sets the base address in the segment descriptor array.
+ *
+ * A segment descriptor has the following structure:
+ * struct {
+ * uint16_t segment_limit_0_15;
+ * uint16_t base_address_0_15;
+ * uint8_t base_address_16_23;
+ * uint8_t attrs[2];
+ * uint8_t base_address_24_31;
+ * };
+ *
+ * @desc_array: Address of the descriptor table
+ * @base: Address to set in the descriptor
+ * @desc_index: Index of the descriptor in the table. Defaults to 0. Must be a
+ * register if specified.
+ *
+ * Clobbers %eax.
+ */
+.macro set_segment_descriptor_base desc_array:req, base:req, desc_index
+ mov \base, %eax
+
+ push %ebx /* preserve ebx */
+ mov \desc_array, %ebx
+
+.ifb \desc_index
+ movw %ax, 2(%ebx)
+ shr $16, %eax
+ movb %al, 4(%ebx)
+ shr $8, %eax
+ movb %al, 7(%ebx)
+.else
+ movw %ax, 2(%ebx, \desc_index, 8)
+ shr $16, %eax
+ movb %al, 4(%ebx, \desc_index, 8)
+ shr $8, %eax
+ movb %al, 7(%ebx, \desc_index, 8)
+.endif
+
+ pop %ebx
+.endm
diff --git a/src/cpu/x86/mp_init.c b/src/cpu/x86/mp_init.c
index 93c143ec85..123b2b5eb5 100644
--- a/src/cpu/x86/mp_init.c
+++ b/src/cpu/x86/mp_init.c
@@ -89,6 +89,8 @@ struct sipi_params {
uint32_t gdt;
uint16_t unused;
uint32_t idt_ptr;
+ uint32_t per_cpu_segment_descriptors;
+ uint32_t per_cpu_segment_selector;
uint32_t stack_top;
uint32_t stack_size;
uint32_t microcode_lock; /* 0xffffffff means parallel loading. */
@@ -215,10 +217,20 @@ static void setup_default_sipi_vector_params(struct sipi_params *sp)
sp->gdt = (uintptr_t)&gdt;
sp->gdtlimit = (uintptr_t)&gdt_end - (uintptr_t)&gdt - 1;
sp->idt_ptr = (uintptr_t)&idtarg;
+ if (CONFIG(CPU_INFO_V2)) {
+ sp->per_cpu_segment_descriptors = (uintptr_t)&per_cpu_segment_descriptors;
+ sp->per_cpu_segment_selector = per_cpu_segment_selector;
+ }
sp->stack_size = CONFIG_STACK_SIZE;
sp->stack_top = ALIGN_DOWN((uintptr_t)&_estack, CONFIG_STACK_SIZE);
- /* Adjust the stack top to take into account cpu_info. */
- sp->stack_top -= sizeof(struct cpu_info);
+ /*
+ * In the CPU_INFO_V2 case, we don't need to pre-allocate the space on the stack.
+ * Instead we push them onto the top of the stack in the sipi vector.
+ */
+ if (!CONFIG(CPU_INFO_V2)) {
+ /* Adjust the stack top to take into account cpu_info. */
+ sp->stack_top -= sizeof(struct cpu_info);
+ }
}
#define NUM_FIXED_MTRRS 11
diff --git a/src/cpu/x86/sipi_vector.S b/src/cpu/x86/sipi_vector.S
index 496fd345eb..491f1de435 100644
--- a/src/cpu/x86/sipi_vector.S
+++ b/src/cpu/x86/sipi_vector.S
@@ -1,5 +1,6 @@
/* SPDX-License-Identifier: GPL-2.0-only */
+#include <cpu/x86/cpu_info.S.inc>
#include <cpu/x86/cr.h>
#include <cpu/amd/mtrr.h>
#include <cpu/x86/msr.h>
@@ -19,6 +20,10 @@ gdtaddr:
.word 0 /* unused */
idt_ptr:
.long 0
+per_cpu_segment_descriptors:
+.long 0
+per_cpu_segment_selector:
+.long 0
stack_top:
.long 0
stack_size:
@@ -98,6 +103,23 @@ _start:
movl stack_top, %edx
subl %eax, %edx
mov %edx, %esp
+
+#if CONFIG(CPU_INFO_V2)
+ push_cpu_info index=%ecx
+ push_per_cpu_segment_data
+
+ /*
+ * Update the AP's per_cpu_segment_descriptor to point to the
+ * per_cpu_segment_data that was allocated on the stack.
+ */
+ set_segment_descriptor_base per_cpu_segment_descriptors, %esp, %ecx
+
+ mov %ecx, %eax
+ shl $3, %eax /* The index is << 3 in the segment selector */
+ add per_cpu_segment_selector, %eax
+ mov %eax, %gs
+#endif
+
andl $0xfffffff0, %esp /* ensure stack alignment */
/* Save CPU number. */
diff --git a/src/cpu/x86/smm/smm_stub.S b/src/cpu/x86/smm/smm_stub.S
index aa48ab69ec..e409983eae 100644
--- a/src/cpu/x86/smm/smm_stub.S
+++ b/src/cpu/x86/smm/smm_stub.S
@@ -9,6 +9,7 @@
* found in smm.h.
*/
+#include <cpu/x86/cpu_info.S.inc>
#include <cpu/x86/cr.h>
#include <cpu/x86/msr.h>
#include <cpu/x86/lapic_def.h>
@@ -82,8 +83,21 @@ smm_relocate_gdt:
/* gdt selector 0x20 tss segment */
.word 0xffff, 0x0000
.byte 0x00, 0x8b, 0x80, 0x00
+
+#if CONFIG(CPU_INFO_V2)
+per_cpu_segment_descriptors:
+ .rept CONFIG_MAX_CPUS
+ /* selgdt 0x28, flat data segment */
+ .word 0xffff, 0x0000
+ .byte 0x00, 0x93, 0xcf, 0x00
+ .endr
+#endif /* CPU_INFO_V2 */
smm_relocate_gdt_end:
+#if CONFIG(CPU_INFO_V2)
+.set per_cpu_segment_selector, per_cpu_segment_descriptors - smm_relocate_gdt
+#endif /* CPU_INFO_V2 */
+
.align 4
.code32
.global smm_trampoline32
@@ -153,6 +167,22 @@ apicid_end:
movl $0, 4(%ebx)
#endif
+#if CONFIG(CPU_INFO_V2)
+ push_cpu_info index=%ecx
+ push_per_cpu_segment_data
+
+ /*
+ * Update the AP's per_cpu_segment_descriptor to point to the
+ * per_cpu_segment_data that was allocated on the stack.
+ */
+ set_segment_descriptor_base $per_cpu_segment_descriptors, %esp, %ecx
+
+ mov %ecx, %eax
+ shl $3, %eax /* The index is << 3 in the segment selector */
+ add $per_cpu_segment_selector, %eax
+ mov %eax, %gs
+#endif
+
/* Create stack frame by pushing a NULL stack base pointer */
pushl $0x0
mov %esp, %ebp
diff --git a/src/include/cpu/x86/gdt.h b/src/include/cpu/x86/gdt.h
index bc4eaad0ca..27a863ee33 100644
--- a/src/include/cpu/x86/gdt.h
+++ b/src/include/cpu/x86/gdt.h
@@ -5,6 +5,8 @@
/* These symbols are defined in c_start.S. */
extern char gdt[];
+extern char per_cpu_segment_descriptors[];
+extern uint32_t per_cpu_segment_selector;
extern char gdt_end[];
extern char idtarg[];