nb/intel/haswell: Add native raminit scaffolding
Implement some scaffolding for Haswell native raminit, like bootmode selection, handling of MRC cache and CPU detection. Change-Id: Icd96649fa045ea7f0f32ae9bfe1e60498d93975b Signed-off-by: Angel Pons <th3fanbus@gmail.com> Reviewed-on: https://review.coreboot.org/c/coreboot/+/64182 Tested-by: build bot (Jenkins) <no-reply@coreboot.org> Reviewed-by: Arthur Heymans <arthur@aheymans.xyz>
This commit is contained in:
parent
49509189dc
commit
1c9a8d8083
|
@ -1,3 +1,4 @@
|
||||||
## SPDX-License-Identifier: GPL-2.0-or-later
|
## SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
romstage-y += raminit_main.c
|
||||||
romstage-y += raminit_native.c
|
romstage-y += raminit_native.c
|
||||||
|
|
|
@ -0,0 +1,104 @@
|
||||||
|
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||||
|
|
||||||
|
#include <assert.h>
|
||||||
|
#include <console/console.h>
|
||||||
|
#include <cpu/intel/haswell/haswell.h>
|
||||||
|
#include <delay.h>
|
||||||
|
#include <device/pci_ops.h>
|
||||||
|
#include <northbridge/intel/haswell/chip.h>
|
||||||
|
#include <northbridge/intel/haswell/haswell.h>
|
||||||
|
#include <northbridge/intel/haswell/raminit.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <types.h>
|
||||||
|
|
||||||
|
#include "raminit_native.h"
|
||||||
|
|
||||||
|
struct task_entry {
|
||||||
|
enum raminit_status (*task)(struct sysinfo *);
|
||||||
|
bool is_enabled;
|
||||||
|
const char *name;
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct task_entry cold_boot[] = {
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Return a generic stepping value to make stepping checks simpler */
|
||||||
|
static enum generic_stepping get_stepping(const uint32_t cpuid)
|
||||||
|
{
|
||||||
|
switch (cpuid) {
|
||||||
|
case CPUID_HASWELL_A0:
|
||||||
|
die("Haswell stepping A0 is not supported\n");
|
||||||
|
case CPUID_HASWELL_B0:
|
||||||
|
case CPUID_HASWELL_ULT_B0:
|
||||||
|
case CPUID_CRYSTALWELL_B0:
|
||||||
|
return STEPPING_B0;
|
||||||
|
case CPUID_HASWELL_C0:
|
||||||
|
case CPUID_HASWELL_ULT_C0:
|
||||||
|
case CPUID_CRYSTALWELL_C0:
|
||||||
|
return STEPPING_C0;
|
||||||
|
default:
|
||||||
|
/** TODO: Add Broadwell support someday **/
|
||||||
|
die("Unknown CPUID 0x%x\n", cpuid);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void initialize_ctrl(struct sysinfo *ctrl)
|
||||||
|
{
|
||||||
|
const struct northbridge_intel_haswell_config *cfg = config_of_soc();
|
||||||
|
const enum raminit_boot_mode bootmode = ctrl->bootmode;
|
||||||
|
|
||||||
|
memset(ctrl, 0, sizeof(*ctrl));
|
||||||
|
|
||||||
|
ctrl->cpu = cpu_get_cpuid();
|
||||||
|
ctrl->stepping = get_stepping(ctrl->cpu);
|
||||||
|
ctrl->dq_pins_interleaved = cfg->dq_pins_interleaved;
|
||||||
|
ctrl->bootmode = bootmode;
|
||||||
|
}
|
||||||
|
|
||||||
|
static enum raminit_status try_raminit(struct sysinfo *ctrl)
|
||||||
|
{
|
||||||
|
const struct task_entry *const schedule = cold_boot;
|
||||||
|
const size_t length = ARRAY_SIZE(cold_boot);
|
||||||
|
|
||||||
|
enum raminit_status status = RAMINIT_STATUS_UNSPECIFIED_ERROR;
|
||||||
|
|
||||||
|
for (size_t i = 0; i < length; i++) {
|
||||||
|
const struct task_entry *const entry = &schedule[i];
|
||||||
|
assert(entry);
|
||||||
|
assert(entry->name);
|
||||||
|
if (!entry->is_enabled)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
assert(entry->task);
|
||||||
|
printk(RAM_DEBUG, "\nExecuting raminit task %s\n", entry->name);
|
||||||
|
status = entry->task(ctrl);
|
||||||
|
printk(RAM_DEBUG, "\n");
|
||||||
|
if (status) {
|
||||||
|
printk(BIOS_ERR, "raminit failed on step %s\n", entry->name);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
void raminit_main(const enum raminit_boot_mode bootmode)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* The mighty_ctrl struct. Will happily nuke the pre-RAM stack
|
||||||
|
* if left unattended. Make it static and pass pointers to it.
|
||||||
|
*/
|
||||||
|
static struct sysinfo mighty_ctrl;
|
||||||
|
|
||||||
|
mighty_ctrl.bootmode = bootmode;
|
||||||
|
initialize_ctrl(&mighty_ctrl);
|
||||||
|
|
||||||
|
/** TODO: Try more than once **/
|
||||||
|
enum raminit_status status = try_raminit(&mighty_ctrl);
|
||||||
|
|
||||||
|
if (status != RAMINIT_STATUS_SUCCESS)
|
||||||
|
die("Memory initialization was met with utmost failure and misery\n");
|
||||||
|
|
||||||
|
/** TODO: Implement the required magic **/
|
||||||
|
die("NATIVE RAMINIT: More Magic (tm) required.\n");
|
||||||
|
}
|
|
@ -1,13 +1,45 @@
|
||||||
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||||
|
|
||||||
|
#include <arch/cpu.h>
|
||||||
|
#include <assert.h>
|
||||||
|
#include <cbmem.h>
|
||||||
|
#include <cf9_reset.h>
|
||||||
#include <console/console.h>
|
#include <console/console.h>
|
||||||
|
#include <cpu/x86/msr.h>
|
||||||
#include <delay.h>
|
#include <delay.h>
|
||||||
|
#include <device/pci_ops.h>
|
||||||
|
#include <mrc_cache.h>
|
||||||
#include <northbridge/intel/haswell/haswell.h>
|
#include <northbridge/intel/haswell/haswell.h>
|
||||||
#include <northbridge/intel/haswell/raminit.h>
|
#include <northbridge/intel/haswell/raminit.h>
|
||||||
#include <southbridge/intel/lynxpoint/me.h>
|
#include <southbridge/intel/lynxpoint/me.h>
|
||||||
#include <southbridge/intel/lynxpoint/pch.h>
|
#include <southbridge/intel/lynxpoint/pch.h>
|
||||||
#include <types.h>
|
#include <types.h>
|
||||||
|
|
||||||
|
#include "raminit_native.h"
|
||||||
|
|
||||||
|
static void wait_txt_clear(void)
|
||||||
|
{
|
||||||
|
const struct cpuid_result cpuid = cpuid_ext(1, 0);
|
||||||
|
|
||||||
|
/* Check if TXT is supported */
|
||||||
|
if (!(cpuid.ecx & BIT(6)))
|
||||||
|
return;
|
||||||
|
|
||||||
|
/* Some TXT public bit */
|
||||||
|
if (!(read32p(0xfed30010) & 1))
|
||||||
|
return;
|
||||||
|
|
||||||
|
/* Wait for TXT clear */
|
||||||
|
do {} while (!(read8p(0xfed40000) & (1 << 7)));
|
||||||
|
}
|
||||||
|
|
||||||
|
static enum raminit_boot_mode get_boot_mode(void)
|
||||||
|
{
|
||||||
|
const uint16_t pmcon_2 = pci_read_config16(PCH_LPC_DEV, GEN_PMCON_2);
|
||||||
|
const uint16_t bitmask = GEN_PMCON_2_DISB | GEN_PMCON_2_MEM_SR;
|
||||||
|
return (pmcon_2 & bitmask) == bitmask ? BOOTMODE_WARM : BOOTMODE_COLD;
|
||||||
|
}
|
||||||
|
|
||||||
static bool early_init_native(int s3resume)
|
static bool early_init_native(int s3resume)
|
||||||
{
|
{
|
||||||
printk(BIOS_DEBUG, "Starting native platform initialisation\n");
|
printk(BIOS_DEBUG, "Starting native platform initialisation\n");
|
||||||
|
@ -24,6 +56,120 @@ static bool early_init_native(int s3resume)
|
||||||
return cpu_replaced;
|
return cpu_replaced;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#define MRC_CACHE_VERSION 1
|
||||||
|
|
||||||
|
struct mrc_data {
|
||||||
|
const void *buffer;
|
||||||
|
size_t buffer_len;
|
||||||
|
};
|
||||||
|
|
||||||
|
static void save_mrc_data(struct mrc_data *md)
|
||||||
|
{
|
||||||
|
mrc_cache_stash_data(MRC_TRAINING_DATA, MRC_CACHE_VERSION, md->buffer, md->buffer_len);
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct mrc_data prepare_mrc_cache(void)
|
||||||
|
{
|
||||||
|
struct mrc_data md = {0};
|
||||||
|
md.buffer = mrc_cache_current_mmap_leak(MRC_TRAINING_DATA,
|
||||||
|
MRC_CACHE_VERSION,
|
||||||
|
&md.buffer_len);
|
||||||
|
return md;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const char *const bm_names[] = {
|
||||||
|
"BOOTMODE_COLD",
|
||||||
|
"BOOTMODE_WARM",
|
||||||
|
"BOOTMODE_S3",
|
||||||
|
"BOOTMODE_FAST",
|
||||||
|
};
|
||||||
|
|
||||||
|
static void clear_disb(void)
|
||||||
|
{
|
||||||
|
pci_and_config16(PCH_LPC_DEV, GEN_PMCON_2, ~GEN_PMCON_2_DISB);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void raminit_reset(void)
|
||||||
|
{
|
||||||
|
clear_disb();
|
||||||
|
system_reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
static enum raminit_boot_mode do_actual_raminit(
|
||||||
|
struct mrc_data *md,
|
||||||
|
const bool s3resume,
|
||||||
|
const bool cpu_replaced,
|
||||||
|
const enum raminit_boot_mode orig_bootmode)
|
||||||
|
{
|
||||||
|
enum raminit_boot_mode bootmode = orig_bootmode;
|
||||||
|
|
||||||
|
bool save_data_valid = md->buffer && md->buffer_len == USHRT_MAX; /** TODO: sizeof() **/
|
||||||
|
|
||||||
|
if (s3resume) {
|
||||||
|
if (bootmode == BOOTMODE_COLD) {
|
||||||
|
printk(BIOS_EMERG, "Memory may not be in self-refresh for S3 resume\n");
|
||||||
|
printk(BIOS_EMERG, "S3 resume and cold boot are mutually exclusive\n");
|
||||||
|
raminit_reset();
|
||||||
|
}
|
||||||
|
/* Only a true mad hatter would replace a CPU in S3 */
|
||||||
|
if (cpu_replaced) {
|
||||||
|
printk(BIOS_EMERG, "Oh no, CPU was replaced during S3\n");
|
||||||
|
/*
|
||||||
|
* No reason to continue, memory consistency is most likely lost
|
||||||
|
* and ME will probably request a reset through DID response too.
|
||||||
|
*/
|
||||||
|
/** TODO: Figure out why past self commented this out **/
|
||||||
|
//raminit_reset();
|
||||||
|
}
|
||||||
|
bootmode = BOOTMODE_S3;
|
||||||
|
if (!save_data_valid) {
|
||||||
|
printk(BIOS_EMERG, "No training data, S3 resume is impossible\n");
|
||||||
|
/* Failed S3 resume, reset to come up cleanly */
|
||||||
|
raminit_reset();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!s3resume && cpu_replaced) {
|
||||||
|
printk(BIOS_NOTICE, "CPU was replaced, forcing a cold boot\n");
|
||||||
|
/*
|
||||||
|
* Looks like the ME will get angry if raminit takes too long.
|
||||||
|
* It will report that the CPU has been replaced on next boot.
|
||||||
|
* Try to continue anyway. This should not happen in most cases.
|
||||||
|
*/
|
||||||
|
/** TODO: Figure out why past self commented this out **/
|
||||||
|
//save_data_valid = false;
|
||||||
|
}
|
||||||
|
if (bootmode == BOOTMODE_COLD) {
|
||||||
|
/* If possible, promote to a fast boot */
|
||||||
|
if (save_data_valid)
|
||||||
|
bootmode = BOOTMODE_FAST;
|
||||||
|
|
||||||
|
clear_disb();
|
||||||
|
} else if (bootmode == BOOTMODE_WARM) {
|
||||||
|
/* If a warm reset happened before raminit is done, force a cold boot */
|
||||||
|
if (mchbar_read32(SSKPD) == 0 && mchbar_read32(SSKPD + 4) == 0) {
|
||||||
|
printk(BIOS_NOTICE, "Warm reset occurred early in cold boot\n");
|
||||||
|
save_data_valid = false;
|
||||||
|
}
|
||||||
|
if (!save_data_valid)
|
||||||
|
bootmode = BOOTMODE_COLD;
|
||||||
|
}
|
||||||
|
assert(save_data_valid != (bootmode == BOOTMODE_COLD));
|
||||||
|
if (save_data_valid) {
|
||||||
|
printk(BIOS_INFO, "Using cached memory parameters\n");
|
||||||
|
die("RAMINIT: Fast boot is not yet implemented\n");
|
||||||
|
}
|
||||||
|
printk(RAM_DEBUG, "Initial bootmode: %s\n", bm_names[orig_bootmode]);
|
||||||
|
printk(RAM_DEBUG, "Current bootmode: %s\n", bm_names[bootmode]);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* And now, the actual memory initialization thing.
|
||||||
|
*/
|
||||||
|
printk(RAM_DEBUG, "\nStarting native raminit\n");
|
||||||
|
raminit_main(bootmode);
|
||||||
|
|
||||||
|
return bootmode;
|
||||||
|
}
|
||||||
|
|
||||||
void perform_raminit(const int s3resume)
|
void perform_raminit(const int s3resume)
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
|
@ -32,17 +178,46 @@ void perform_raminit(const int s3resume)
|
||||||
*/
|
*/
|
||||||
const bool cpu_replaced = early_init_native(s3resume);
|
const bool cpu_replaced = early_init_native(s3resume);
|
||||||
|
|
||||||
(void)cpu_replaced;
|
wait_txt_clear();
|
||||||
|
wrmsr(0x2e6, (msr_t) {.lo = 0, .hi = 0});
|
||||||
|
|
||||||
|
const enum raminit_boot_mode orig_bootmode = get_boot_mode();
|
||||||
|
|
||||||
|
struct mrc_data md = prepare_mrc_cache();
|
||||||
|
|
||||||
|
const enum raminit_boot_mode bootmode =
|
||||||
|
do_actual_raminit(&md, s3resume, cpu_replaced, orig_bootmode);
|
||||||
|
|
||||||
|
/** TODO: report_memory_config **/
|
||||||
|
|
||||||
/** TODO: Move after raminit */
|
|
||||||
if (intel_early_me_uma_size() > 0) {
|
if (intel_early_me_uma_size() > 0) {
|
||||||
/** TODO: Update status once raminit is implemented **/
|
/*
|
||||||
uint8_t me_status = ME_INIT_STATUS_ERROR;
|
* The 'other' success value is to report loss of memory
|
||||||
|
* consistency to ME if warm boot was downgraded to cold.
|
||||||
|
*/
|
||||||
|
uint8_t me_status;
|
||||||
|
if (BOOTMODE_WARM == orig_bootmode && BOOTMODE_COLD == bootmode)
|
||||||
|
me_status = ME_INIT_STATUS_SUCCESS_OTHER;
|
||||||
|
else
|
||||||
|
me_status = ME_INIT_STATUS_SUCCESS;
|
||||||
|
|
||||||
|
/** TODO: Remove this once raminit is implemented **/
|
||||||
|
me_status = ME_INIT_STATUS_ERROR;
|
||||||
intel_early_me_init_done(me_status);
|
intel_early_me_init_done(me_status);
|
||||||
}
|
}
|
||||||
|
|
||||||
intel_early_me_status();
|
intel_early_me_status();
|
||||||
|
|
||||||
/** TODO: Implement the required magic **/
|
const bool cbmem_was_initted = !cbmem_recovery(s3resume);
|
||||||
die("NATIVE RAMINIT: More Magic (tm) required.\n");
|
if (s3resume && !cbmem_was_initted) {
|
||||||
|
/* Failed S3 resume, reset to come up cleanly */
|
||||||
|
printk(BIOS_CRIT, "Failed to recover CBMEM in S3 resume.\n");
|
||||||
|
system_reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Save training data on non-S3 resumes */
|
||||||
|
if (!s3resume)
|
||||||
|
save_mrc_data(&md);
|
||||||
|
|
||||||
|
/** TODO: setup_sdram_meminfo **/
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,34 @@
|
||||||
|
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||||
|
|
||||||
|
#ifndef HASWELL_RAMINIT_NATIVE_H
|
||||||
|
#define HASWELL_RAMINIT_NATIVE_H
|
||||||
|
|
||||||
|
enum raminit_boot_mode {
|
||||||
|
BOOTMODE_COLD,
|
||||||
|
BOOTMODE_WARM,
|
||||||
|
BOOTMODE_S3,
|
||||||
|
BOOTMODE_FAST,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum raminit_status {
|
||||||
|
RAMINIT_STATUS_SUCCESS = 0,
|
||||||
|
RAMINIT_STATUS_UNSPECIFIED_ERROR, /** TODO: Deprecated in favor of specific values **/
|
||||||
|
};
|
||||||
|
|
||||||
|
enum generic_stepping {
|
||||||
|
STEPPING_A0 = 1,
|
||||||
|
STEPPING_B0 = 2,
|
||||||
|
STEPPING_C0 = 3,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct sysinfo {
|
||||||
|
enum raminit_boot_mode bootmode;
|
||||||
|
enum generic_stepping stepping;
|
||||||
|
uint32_t cpu; /* CPUID value */
|
||||||
|
|
||||||
|
bool dq_pins_interleaved;
|
||||||
|
};
|
||||||
|
|
||||||
|
void raminit_main(enum raminit_boot_mode bootmode);
|
||||||
|
|
||||||
|
#endif
|
Loading…
Reference in New Issue