arch/x86/postcar: Set up postcar MTRR in C code

Setting up postcar MTRRs is done when invd is already called so there
is no reason to do this in assembly anymore.

This also drops the custom code for Quark to set up MTRRs.

TESTED on foxconn/g41m and hermes/prodrive that MTRR are properly set
in postcar & ramstage.

Change-Id: I5ec10e84118197a04de0a5194336ef8bb049bba4
Signed-off-by: Arthur Heymans <arthur@aheymans.xyz>
Reviewed-on: https://review.coreboot.org/c/coreboot/+/54299
Reviewed-by: Elyes Haouas <ehaouas@noos.fr>
Reviewed-by: Raul Rangel <rrangel@chromium.org>
Tested-by: build bot (Jenkins) <no-reply@coreboot.org>
This commit is contained in:
Arthur Heymans 2021-05-14 13:19:43 +02:00
parent 645dde7794
commit 46b409da48
12 changed files with 82 additions and 333 deletions

View file

@ -4,37 +4,6 @@
#include <cpu/x86/cr.h>
#include <cpu/x86/cache.h>
.section ".module_parameters", "aw", @progbits
/* stack_top indicates the stack to pull MTRR information from. */
.global post_car_stack_top
post_car_stack_top:
.long 0
.long 0
#if ENV_X86_64
.code64
.macro pop_eax_edx
pop %rax
mov %rax, %rdx
shr $32, %rdx
.endm
.macro pop_ebx_esi
pop %rbx
mov %rbx, %rsi
shr $32, %rsi
.endm
#else
.code32
.macro pop_eax_edx
pop %eax
pop %edx
.endm
.macro pop_ebx_esi
pop %ebx
pop %esi
.endm
#endif
/* Place the stack in the bss section. It's not necessary to define it in the
* the linker script. */
.section .bss, "aw", @nobits
@ -89,107 +58,22 @@ skip_clflush:
mov %cr0, %rax
and $(~(CR0_CD | CR0_NW)), %eax
mov %rax, %cr0
/* Ensure cache is clean. */
invd
/* Set up new stack. */
movabs post_car_stack_top, %rax
mov %rax, %rsp
#else
mov %cr0, %eax
and $(~(CR0_CD | CR0_NW)), %eax
mov %eax, %cr0
#endif
/* Ensure cache is clean. */
invd
/* Set up new stack. */
mov post_car_stack_top, %esp
#endif
/*
* Honor variable MTRR information pushed on the stack with the
* following layout:
*
* Offset: Value
* ...
* 0x14: MTRR mask 0 63:32
* 0x10: MTRR mask 0 31:0
* 0x0c: MTRR base 0 63:32
* 0x08: MTRR base 0 31:0
* 0x04: Number of variable MTRRs to set
* 0x00: Number of variable MTRRs to clear
*/
#if CONFIG(SOC_SETS_MSRS)
mov %esp, %ebp
/* Need to align stack to 16 bytes at the call instruction. Therefore
account for the 1 push. */
andl $0xfffffff0, %esp
#if ENV_X86_64
mov %rbp, %rdi
#else
sub $12, %esp
push %ebp
#endif
call soc_set_mtrrs
/* Ignore fixing up %esp since we're setting it a new value. */
/* eax: new top_of_stack with setup_stack_and_mtrrs data removed */
movl %eax, %esp
/* Align stack to 16 bytes at call instruction. */
andl $0xfffffff0, %esp
call soc_enable_mtrrs
#else /* CONFIG_SOC_SETS_MSRS */
/* Clear variable MTRRs. */
pop_ebx_esi /* ebx: Number to clear, esi: Number to set */
test %ebx, %ebx
jz 2f
xor %eax, %eax
xor %edx, %edx
mov $(MTRR_PHYS_BASE(0)), %ecx
1:
wrmsr
inc %ecx
wrmsr
inc %ecx
dec %ebx
jnz 1b
2:
/* Set Variable MTRRs based on stack contents. */
test %esi, %esi
jz 2f
mov $(MTRR_PHYS_BASE(0)), %ecx
1:
/* Write MTRR base. */
pop_eax_edx
wrmsr
inc %ecx
/* Write MTRR mask. */
pop_eax_edx
wrmsr
inc %ecx
dec %esi
jnz 1b
2:
/* Enable MTRR. */
mov $(MTRR_DEF_TYPE_MSR), %ecx
rdmsr
/* Make default type uncacheable. */
and $(~(MTRR_DEF_TYPE_MASK)), %eax
or $(MTRR_DEF_TYPE_EN), %eax
wrmsr
#endif /* CONFIG_SOC_SETS_MSRS */
movl $_estack, %esp
/* Align stack to 16 bytes at call instruction. */
andl $0xfffffff0, %esp
/* Call this in assembly as some platforms like to mess with the bootflow and
call into main directly from chipset_teardown_car. */
call postcar_mtrr_setup
/* Call into main for postcar. */
call main
/* Should never return. */

View file

@ -16,17 +16,15 @@ void mainboard_romstage_entry(void);
*/
struct postcar_frame {
uintptr_t stack;
int skip_common_mtrr;
struct var_mtrr_context ctx;
struct var_mtrr_context *mtrr;
};
/*
* Initialize postcar_frame object allocating stack from cbmem,
* with stack_size == 0, default 4 KiB is allocated.
* Initialize postcar_frame object.
* Returns 0 on success, < 0 on error.
*/
int postcar_frame_init(struct postcar_frame *pcf, size_t stack_size);
int postcar_frame_init(struct postcar_frame *pcf);
/*
* Add variable MTRR covering the provided range with MTRR type.

View file

@ -13,65 +13,32 @@
#include <timestamp.h>
#include <security/vboot/vboot_common.h>
static inline void stack_push(struct postcar_frame *pcf, uint32_t val)
static size_t var_mtrr_ctx_size(void)
{
uint32_t *ptr;
pcf->stack -= sizeof(val);
ptr = (void *)pcf->stack;
*ptr = val;
int mtrr_count = get_var_mtrr_count();
return sizeof(struct var_mtrr_context) + mtrr_count * 2 * sizeof(msr_t);
}
static void postcar_frame_prepare(struct postcar_frame *pcf)
int postcar_frame_init(struct postcar_frame *pcf)
{
var_mtrr_context_init(&pcf->ctx, pcf);
}
struct var_mtrr_context *ctx;
int postcar_frame_init(struct postcar_frame *pcf, size_t stack_size)
{
void *stack;
/*
* Use default postcar stack size of 4 KiB. This value should
* not be decreased, because if mainboards use vboot, 1 KiB will
* not be enough anymore.
*/
if (stack_size == 0)
stack_size = 4 * KiB;
stack = cbmem_add(CBMEM_ID_ROMSTAGE_RAM_STACK, stack_size);
if (stack == NULL) {
printk(BIOS_ERR, "Couldn't add %zd byte stack in cbmem.\n",
stack_size);
ctx = cbmem_add(CBMEM_ID_ROMSTAGE_RAM_STACK, var_mtrr_ctx_size());
if (ctx == NULL) {
printk(BIOS_ERR, "Couldn't add var_mtrr_ctx setup in cbmem.\n");
return -1;
}
postcar_frame_prepare(pcf);
pcf->stack = (uintptr_t)stack;
pcf->stack += stack_size;
pcf->mtrr = ctx;
var_mtrr_context_init(pcf->mtrr);
return 0;
}
static void postcar_var_mtrr_set(const struct var_mtrr_context *ctx,
uintptr_t addr, size_t size,
msr_t base, msr_t mask)
{
struct postcar_frame *pcf = ctx->arg;
printk(BIOS_DEBUG, "MTRR Range: Start=%lx End=%lx (Size %zx)\n",
addr, addr + size - 1, size);
stack_push(pcf, mask.hi);
stack_push(pcf, mask.lo);
stack_push(pcf, base.hi);
stack_push(pcf, base.lo);
}
void postcar_frame_add_mtrr(struct postcar_frame *pcf,
uintptr_t addr, size_t size, int type)
{
var_mtrr_set_with_cb(&pcf->ctx, addr, size, type, postcar_var_mtrr_set);
var_mtrr_set(pcf->mtrr, addr, size, type);
}
void postcar_frame_add_romcache(struct postcar_frame *pcf, int type)
@ -94,7 +61,7 @@ static void postcar_frame_common_mtrrs(struct postcar_frame *pcf)
* cache-as-ram is torn down as well as the MTRR settings to use. */
void prepare_and_run_postcar(struct postcar_frame *pcf)
{
if (postcar_frame_init(pcf, 0))
if (postcar_frame_init(pcf))
die("Unable to initialize postcar frame.\n");
fill_postcar_frame(pcf);
@ -105,25 +72,15 @@ void prepare_and_run_postcar(struct postcar_frame *pcf)
/* We do not return here. */
}
static void postcar_commit_mtrrs(struct postcar_frame *pcf)
static void finalize_load(uintptr_t *reloc_params, uintptr_t mtrr_frame_ptr)
{
/*
* Place the number of used variable MTRRs on stack then max number
* of variable MTRRs supported in the system.
*/
stack_push(pcf, pcf->ctx.used_var_mtrrs);
stack_push(pcf, pcf->ctx.max_var_mtrrs);
}
static void finalize_load(uintptr_t *stack_top_ptr, uintptr_t stack_top)
{
*stack_top_ptr = stack_top;
*reloc_params = mtrr_frame_ptr;
/*
* Signal to rest of system that another update was made to the
* postcar program prior to running it.
*/
prog_segment_loaded((uintptr_t)stack_top_ptr, sizeof(uintptr_t),
SEG_FINAL);
prog_segment_loaded((uintptr_t)reloc_params, sizeof(uintptr_t), SEG_FINAL);
prog_segment_loaded((uintptr_t)mtrr_frame_ptr, var_mtrr_ctx_size(), SEG_FINAL);
}
static void load_postcar_cbfs(struct prog *prog, struct postcar_frame *pcf)
@ -144,7 +101,7 @@ static void load_postcar_cbfs(struct prog *prog, struct postcar_frame *pcf)
die_with_post_code(POST_INVALID_ROM,
"No parameters found in after CAR program.\n");
finalize_load(rsl.params, pcf->stack);
finalize_load(rsl.params, (uintptr_t)pcf->mtrr);
stage_cache_add(STAGE_POSTCAR, prog);
}
@ -177,14 +134,12 @@ void run_postcar_phase(struct postcar_frame *pcf)
struct prog prog =
PROG_INIT(PROG_POSTCAR, CONFIG_CBFS_PREFIX "/postcar");
postcar_commit_mtrrs(pcf);
if (resume_from_stage_cache()) {
stage_cache_load_stage(STAGE_POSTCAR, &prog);
/* This is here to allow platforms to pass different stack
parameters between S3 resume and normal boot. On the
platforms where the values are the same it's a nop. */
finalize_load(prog.arg, pcf->stack);
finalize_load(prog.arg, (uintptr_t)pcf->mtrr);
if (prog_entry(&prog) == NULL)
postcar_cache_invalid();

View file

@ -1,6 +1,7 @@
ramstage-y += mtrr.c
ramstage-y += mtrrlib.c
postcar-y += mtrrlib.c
romstage-y += mtrrlib.c
bootblock-y += mtrrlib.c
verstage_x86-y += mtrrlib.c
@ -8,6 +9,7 @@ verstage_x86-y += mtrrlib.c
romstage-y += earlymtrr.c
bootblock-y += earlymtrr.c
verstage_x86-y += earlymtrr.c
postcar-y += earlymtrr.c
bootblock-y += debug.c
romstage-y += debug.c

View file

@ -5,20 +5,17 @@
#include <cpu/x86/msr.h>
#include <console/console.h>
#include <commonlib/bsd/helpers.h>
#include <stdint.h>
void var_mtrr_context_init(struct var_mtrr_context *ctx, void *arg)
void var_mtrr_context_init(struct var_mtrr_context *ctx)
{
ctx->upper_mask = (1U << (cpu_phys_address_size() - 32)) - 1;
ctx->max_var_mtrrs = get_var_mtrr_count();
ctx->used_var_mtrrs = 0;
ctx->arg = arg;
}
int var_mtrr_set_with_cb(struct var_mtrr_context *ctx, uintptr_t addr, size_t size,
int type, void (*callback)(const struct var_mtrr_context *ctx,
uintptr_t base_addr, size_t size,
msr_t base, msr_t mask))
int var_mtrr_set(struct var_mtrr_context *ctx, uintptr_t addr, size_t size, int type)
{
const uint32_t upper_mask = (1U << (cpu_phys_address_size() - 32)) - 1;
/* Utilize additional MTRRs if the specified size is greater than the
base address alignment. */
while (size != 0) {
@ -47,9 +44,10 @@ int var_mtrr_set_with_cb(struct var_mtrr_context *ctx, uintptr_t addr, size_t si
base.hi = (uint64_t)addr >> 32;
base.lo = addr | type;
mask.hi = ctx->upper_mask;
mask.hi = upper_mask;
mask.lo = ~(mtrr_size - 1) | MTRR_PHYS_MASK_VALID;
callback(ctx, addr, mtrr_size, base, mask);
ctx->mtrr[ctx->used_var_mtrrs].base = base;
ctx->mtrr[ctx->used_var_mtrrs].mask = mask;
ctx->used_var_mtrrs++;
size -= mtrr_size;
@ -59,16 +57,25 @@ int var_mtrr_set_with_cb(struct var_mtrr_context *ctx, uintptr_t addr, size_t si
return 0;
}
static void set_mtrr(const struct var_mtrr_context *ctx, uintptr_t base_addr, size_t size,
msr_t base, msr_t mask)
{
int i = var_mtrr_context_current_mtrr(ctx);
/* Romstage sets up a MTRR context in cbmem and sets up this pointer in postcar stage. */
__attribute__((used, __section__(".module_parameters"))) const volatile uintptr_t post_car_mtrrs;
wrmsr(MTRR_PHYS_BASE(i), base);
wrmsr(MTRR_PHYS_MASK(i), mask);
void commit_mtrr_setup(const struct var_mtrr_context *ctx)
{
clear_all_var_mtrr();
for (int i = 0; i < ctx->used_var_mtrrs; i++) {
wrmsr(MTRR_PHYS_BASE(i), ctx->mtrr[i].base);
wrmsr(MTRR_PHYS_MASK(i), ctx->mtrr[i].mask);
}
/* Enable MTRR */
msr_t mtrr_def_type = rdmsr(MTRR_DEF_TYPE_MSR);
mtrr_def_type.lo &= MTRR_DEF_TYPE_MASK;
mtrr_def_type.lo |= MTRR_DEF_TYPE_EN;
wrmsr(MTRR_DEF_TYPE_MSR, mtrr_def_type);
}
int var_mtrr_set(struct var_mtrr_context *ctx, uintptr_t addr, size_t size, int type)
void postcar_mtrr_setup(void)
{
return var_mtrr_set_with_cb(ctx, addr, size, type, set_mtrr);
commit_mtrr_setup((const struct var_mtrr_context *)post_car_mtrrs);
}

View file

@ -58,7 +58,7 @@ void recover_postcar_frame(struct postcar_frame *pcf, int s3resume)
/* Replicate non-UC MTRRs as left behind by AGESA.
*/
for (i = 0; i < pcf->ctx.max_var_mtrrs; i++) {
for (i = 0; i < pcf->mtrr->max_var_mtrrs; i++) {
mask = rdmsr(MTRR_PHYS_MASK(i));
base = rdmsr(MTRR_PHYS_BASE(i));
u32 size = ~(mask.lo & ~0xfff) + 1;

View file

@ -78,7 +78,7 @@ static void romstage_main(void)
romstage_handoff_init(cb->s3resume);
postcar_frame_init(&pcf, HIGH_ROMSTAGE_STACK_SIZE);
postcar_frame_init(&pcf);
recover_postcar_frame(&pcf, cb->s3resume);
run_postcar_phase(&pcf);

View file

@ -114,43 +114,19 @@ void clear_all_var_mtrr(void);
asmlinkage void display_mtrrs(void);
/* Variable MTRR structure to help track and set MTRRs prior to ramstage. This
and the following APIs can be used to set up more complex MTRR solutions
instead of open coding get_free_var_mtrr() and set_var_mtrr() or for determining
a future solution, such as postcar_loader. */
struct var_mtrr_context {
uint32_t upper_mask;
int max_var_mtrrs;
int used_var_mtrrs;
void *arg; /* optional callback parameter */
uint32_t max_var_mtrrs;
uint32_t used_var_mtrrs;
struct {
msr_t base;
msr_t mask;
} mtrr[];
};
/* Returns 0-relative MTRR from context. Use MTRR_PHYS_BASE|MASK macros for calculating
MSR address value. */
static inline int var_mtrr_context_current_mtrr(const struct var_mtrr_context *ctx)
{
return ctx->used_var_mtrrs;
}
/* Initialize var_mtrr_context object. Assumes all variable MTRRs are not yet used. */
void var_mtrr_context_init(struct var_mtrr_context *ctx, void *arg);
/* Allocate a variable mtrr base and mask, calling the provided callback for each MTRR
MSR base-mask pair needed to accommodate the address and size request.
Returns < 0 on error and 0 on success. */
int var_mtrr_set_with_cb(struct var_mtrr_context *ctx,
uintptr_t addr, size_t size, int type,
void (*callback)(const struct var_mtrr_context *ctx,
uintptr_t base_addr, size_t size,
msr_t base, msr_t mask));
/* Same as var_mtrr_set_with_cb() but just write the MSRs directly. */
void var_mtrr_context_init(struct var_mtrr_context *ctx);
int var_mtrr_set(struct var_mtrr_context *ctx, uintptr_t addr, size_t size, int type);
/*
* Set the MTRRs using the data on the stack from setup_stack_and_mtrrs.
* Return a new top_of_stack value which removes the setup_stack_and_mtrrs data.
*/
asmlinkage void *soc_set_mtrrs(void *top_of_stack);
asmlinkage void soc_enable_mtrrs(void);
void commit_mtrr_setup(const struct var_mtrr_context *ctx);
void postcar_mtrr_setup(void);
/* fms: find most significant bit set, stolen from Linux Kernel Source. */
static inline unsigned int fms(unsigned int x)

View file

@ -1,11 +1,16 @@
/* SPDX-License-Identifier: GPL-2.0-only */
#include <amdblocks/cpu.h>
#include <assert.h>
#include <cpu/amd/mtrr.h>
#include <cpu/x86/cache.h>
#include <cpu/x86/msr.h>
#include <cpu/x86/mtrr.h>
#include <soc/iomap.h>
#include <stdint.h>
/* Allocate a static amount of stack for the MTRR context. */
#define MAX_VAR_MTRR_USE 10
/*
* PSP performs the memory training and setting up DRAM map prior to x86 cores being released.
@ -19,9 +24,15 @@ void early_cache_setup(void)
msr_t mtrr_def_type;
msr_t fixed_mtrr_ram;
msr_t fixed_mtrr_mmio;
struct var_mtrr_context mtrr_ctx;
union mtrr_ctx {
struct var_mtrr_context ctx;
char buffer[sizeof(struct var_mtrr_context)
+ 2 * MAX_VAR_MTRR_USE * sizeof(msr_t)];
var_mtrr_context_init(&mtrr_ctx, NULL);
} mtrr_ctx;
var_mtrr_context_init(&mtrr_ctx.ctx);
mtrr_ctx.ctx.max_var_mtrrs = MIN(MAX_VAR_MTRR_USE, mtrr_ctx.ctx.max_var_mtrrs);
top_mem = rdmsr(TOP_MEM);
/* Enable RdDram and WrDram attributes in fixed MTRRs. */
sys_cfg = rdmsr(SYSCFG_MSR);
@ -49,11 +60,11 @@ void early_cache_setup(void)
wrmsr(SYSCFG_MSR, sys_cfg);
clear_all_var_mtrr();
var_mtrr_set(&mtrr_ctx, 0, ALIGN_DOWN(top_mem.lo, 8*MiB), MTRR_TYPE_WRBACK);
var_mtrr_set(&mtrr_ctx.ctx, 0, ALIGN_DOWN(top_mem.lo, 8*MiB), MTRR_TYPE_WRBACK);
/* TODO: check if we should always mark 16 MByte below 4 GByte as WRPROT */
var_mtrr_set(&mtrr_ctx, FLASH_BASE_ADDR, CONFIG_ROM_SIZE, MTRR_TYPE_WRPROT);
var_mtrr_set(&mtrr_ctx.ctx, FLASH_BASE_ADDR, CONFIG_ROM_SIZE, MTRR_TYPE_WRPROT);
commit_mtrr_setup(&mtrr_ctx.ctx);
/* Set up RAM caching for everything below 1MiB except for 0xa0000-0xc0000 . */
wrmsr(MTRR_FIX_64K_00000, fixed_mtrr_ram);

View file

@ -121,7 +121,7 @@ asmlinkage void car_stage_entry(void)
smm_list_regions();
post_code(0x44);
if (postcar_frame_init(&pcf, 0))
if (postcar_frame_init(&pcf))
die("Unable to initialize postcar frame.\n");
/*

View file

@ -3,10 +3,7 @@
romstage-y += car.c
romstage-$(CONFIG_DISPLAY_UPD_DATA) += debug.c
romstage-y += fsp_params.c
romstage-y += mtrr.c
romstage-y += pcie.c
romstage-y += report_platform.c
romstage-y += romstage.c
romstage-y += ../../../../cpu/intel/car/romstage.c
postcar-y += mtrr.c

View file

@ -1,81 +0,0 @@
/* SPDX-License-Identifier: GPL-2.0-only */
#include <cpu/x86/msr.h>
#include <cpu/x86/mtrr.h>
#include <soc/pci_devs.h>
#include <soc/reg_access.h>
asmlinkage void *soc_set_mtrrs(void *top_of_stack)
{
union {
uint32_t u32[2];
uint64_t u64;
msr_t msr;
} data;
uint32_t mtrr_count;
uint32_t *mtrr_data;
uint32_t mtrr_reg;
/*
* The stack contents are initialized in src/soc/intel/common/stack.c
* to be the following:
*
* *
* *
* *
* +36: MTRR mask 1 63:32
* +32: MTRR mask 1 31:0
* +28: MTRR base 1 63:32
* +24: MTRR base 1 31:0
* +20: MTRR mask 0 63:32
* +16: MTRR mask 0 31:0
* +12: MTRR base 0 63:32
* +8: MTRR base 0 31:0
* +4: Number of MTRRs to setup (described above)
* top_of_stack --> +0: Number of variable MTRRs to clear
*
* This routine:
* * Clears all of the variable MTRRs
* * Initializes the variable MTRRs with the data passed in
* * Returns the new top of stack after removing all of the
* data passed in.
*/
/* Clear all of the variable MTRRs (base and mask). */
mtrr_reg = MTRR_PHYS_BASE(0);
mtrr_data = top_of_stack;
mtrr_count = (*mtrr_data++) * 2;
data.u64 = 0;
while (mtrr_count-- > 0)
soc_msr_write(mtrr_reg++, data.msr);
/* Setup the specified variable MTRRs */
mtrr_reg = MTRR_PHYS_BASE(0);
mtrr_count = *mtrr_data++;
while (mtrr_count-- > 0) {
data.u32[0] = *mtrr_data++;
data.u32[1] = *mtrr_data++;
soc_msr_write(mtrr_reg++, data.msr); /* Base */
data.u32[0] = *mtrr_data++;
data.u32[1] = *mtrr_data++;
soc_msr_write(mtrr_reg++, data.msr); /* Mask */
}
/* Remove setup_stack_and_mtrrs data and return the new top_of_stack */
top_of_stack = mtrr_data;
return top_of_stack;
}
asmlinkage void soc_enable_mtrrs(void)
{
union {
uint32_t u32[2];
uint64_t u64;
msr_t msr;
} data;
/* Enable MTRR. */
data.msr = soc_msr_read(MTRR_DEF_TYPE_MSR);
data.u32[0] |= MTRR_DEF_TYPE_EN;
soc_msr_write(MTRR_DEF_TYPE_MSR, data.msr);
}