blob: 2fed93de5b6760ada6e48753ceea5c6c167305f4 [file] [log] [blame]
/* 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 <cpu/x86/msr.h>
#include <device/pci_ops.h>
#include <mrc_cache.h>
#include <northbridge/intel/haswell/haswell.h>
#include <northbridge/intel/haswell/raminit.h>
#include <southbridge/intel/lynxpoint/me.h>
#include <southbridge/intel/lynxpoint/pch.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(enum raminit_boot_mode bootmode)
{
printk(BIOS_DEBUG, "Starting native platform initialisation\n");
intel_early_me_init();
bool cpu_replaced = bootmode == BOOTMODE_COLD && intel_early_me_cpu_replacement_check();
early_pch_init_native(bootmode == BOOTMODE_S3);
if (!CONFIG(INTEL_LYNXPOINT_LP))
dmi_early_init();
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)
{
/*
* See, this function's name is a lie. There are more things to
* do that memory initialisation, but they are relatively easy.
*/
const enum raminit_boot_mode orig_bootmode = get_boot_mode();
const bool cpu_replaced = early_init_native(s3resume ? BOOTMODE_S3 : orig_bootmode);
wait_txt_clear();
wrmsr(0x2e6, (msr_t) {.lo = 0, .hi = 0});
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 **/
if (intel_early_me_uma_size() > 0) {
/*
* 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_status();
const bool cbmem_was_initted = !cbmem_recovery(s3resume);
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 **/
}