From 46cdec8cbce15ca11ad9a49a3ee415a78f781997 Mon Sep 17 00:00:00 2001 From: Angel Pons <th3fanbus@gmail.com> Date: Sat, 7 May 2022 00:26:10 +0200 Subject: [PATCH 08/26] 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> --- .../intel/haswell/native_raminit/Makefile.inc | 1 + .../haswell/native_raminit/raminit_main.c | 104 ++++++++++ .../haswell/native_raminit/raminit_native.c | 189 +++++++++++++++++- .../haswell/native_raminit/raminit_native.h | 34 ++++ 4 files changed, 322 insertions(+), 6 deletions(-) create mode 100644 src/northbridge/intel/haswell/native_raminit/raminit_main.c create mode 100644 src/northbridge/intel/haswell/native_raminit/raminit_native.h diff --git a/src/northbridge/intel/haswell/native_raminit/Makefile.inc b/src/northbridge/intel/haswell/native_raminit/Makefile.inc index 8cfb4fb33e..90af951c5a 100644 --- a/src/northbridge/intel/haswell/native_raminit/Makefile.inc +++ b/src/northbridge/intel/haswell/native_raminit/Makefile.inc @@ -1,3 +1,4 @@ ## SPDX-License-Identifier: GPL-2.0-or-later +romstage-y += raminit_main.c romstage-y += raminit_native.c diff --git a/src/northbridge/intel/haswell/native_raminit/raminit_main.c b/src/northbridge/intel/haswell/native_raminit/raminit_main.c new file mode 100644 index 0000000000..9b42c25b40 --- /dev/null +++ b/src/northbridge/intel/haswell/native_raminit/raminit_main.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"); +} diff --git a/src/northbridge/intel/haswell/native_raminit/raminit_native.c b/src/northbridge/intel/haswell/native_raminit/raminit_native.c index b6efb6b40d..0869db3902 100644 --- a/src/northbridge/intel/haswell/native_raminit/raminit_native.c +++ b/src/northbridge/intel/haswell/native_raminit/raminit_native.c @@ -1,13 +1,45 @@ /* 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 <delay.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(int s3resume) { printk(BIOS_DEBUG, "Starting native platform initialisation\n"); @@ -24,6 +56,120 @@ static bool early_init_native(int s3resume) 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) { /* @@ -32,17 +178,48 @@ void perform_raminit(const int 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) { - /** 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); } + post_code(0x3b); + intel_early_me_status(); - /** TODO: Implement the required magic **/ - die("NATIVE RAMINIT: More Magic (tm) required.\n"); + 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 **/ } diff --git a/src/northbridge/intel/haswell/native_raminit/raminit_native.h b/src/northbridge/intel/haswell/native_raminit/raminit_native.h new file mode 100644 index 0000000000..885f0184f4 --- /dev/null +++ b/src/northbridge/intel/haswell/native_raminit/raminit_native.h @@ -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 -- 2.39.2