arch/x86,cpu/x86: Introduce new method for accessing cpu_info

There is currently a fundamental flaw in the current cpu_info()
implementation. It assumes that current stack is CONFIG_STACK_SIZE
aligned. This assumption breaks down when performing SMM relocation.

The first step in performing SMM relocation is changing the SMBASE. This
is accomplished by installing the smmstub at 0x00038000, which is the
default SMM entry point. The stub is configured to set up a new stack
with the size of 1 KiB (CONFIG_SMM_STUB_STACK_SIZE), and an entry point
of smm_do_relocation located in RAMSTAGE RAM.

This means that when smm_do_relocation is executed, it is running in SMM
with a different sized stack. When cpu_info() gets called it will be
using CONFIG_STACK_SIZE to calculate the location of the cpu_info
struct. This results in reading random memory. Since cpu_info() has to
run in multiple environments, we can't use a compile time constant to
locate the cpu_info struct.

This CL introduces a new way of locating cpu_info. It uses a per-cpu
segment descriptor that points to a per-cpu segment that is allocated on
the stack. By using a segment descriptor to point to the per-cpu data,
we no longer need to calculate the location of the cpu_info struct. This
has the following advantages:
* Stacks no longer need to be CONFIG_STACK_SIZE aligned.
* Accessing an unconfigured segment will result in an exception. This
  ensures no one can call cpu_info() from an unsupported environment.
* Segment selectors are cleared when entering SMM and restored when
  leaving SMM.
* There is a 1:1 mapping between cpu and cpu_info. When using
  COOP_MULTITASKING, a new cpu_info is currently allocated at the top of
  each thread's stack. This no longer needs to happen.

This CL guards most of the code with CONFIG(CPU_INFO_V2). I did this so
reviewers can feel more comfortable knowing most of the CL is a no-op. I
would eventually like to remove most of the guards though.

This CL does not touch the LEGACY_SMP_INIT code path. I don't have any
way of testing it.

The %gs segment was chosen over the %fs segment because it's what the
linux kernel uses for per-cpu data in x86_64 mode.

BUG=b:194391185, b:179699789
TEST=Boot guybrush with CPU_INFO_V2 and verify BSP and APs have correct
%gs segment. Verify cpu_info looks sane. Verify booting to the OS
works correctly with COOP_MULTITASKING enabled.

Signed-off-by: Raul E Rangel <rrangel@chromium.org>
Change-Id: I79dce9597cb784acb39a96897fb3c2f2973bfd98
Reviewed-on: https://review.coreboot.org/c/coreboot/+/57627
Tested-by: build bot (Jenkins) <no-reply@coreboot.org>
Reviewed-by: Eric Peers <epeers@google.com>
Reviewed-by: Karthik Ramasubramanian <kramasub@google.com>
This commit is contained in:
Raul E Rangel
2021-09-22 14:56:51 -06:00
committed by Raul Rangel
parent 9990866fcf
commit b2346a56f1
8 changed files with 186 additions and 3 deletions

View File

@@ -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