AGESA: Implement POSTCAR_STAGE
Move all boards that have moved away from AGESA_LEGACY_WRAPPER
or BINARYPI_LEGACY_WRAPPER to use POSTCAR_STAGE.
We use POSTCAR_STAGE as a conditional in CAR teardown to tell
our MTRR setup is prepared such that invalidation without
writeback is a valid operation.
Change-Id: I3f4e2170054bdb84c72d2f7c956f8d51a6d7f0ca
Signed-off-by: Kyösti Mälkki <kyosti.malkki@gmail.com>
Reviewed-on: https://review.coreboot.org/21384
Tested-by: build bot (Jenkins) <no-reply@coreboot.org>
Reviewed-by: Aaron Durbin <adurbin@chromium.org>
diff --git a/src/cpu/amd/agesa/Kconfig b/src/cpu/amd/agesa/Kconfig
index 95db82f..6a7ec87 100644
--- a/src/cpu/amd/agesa/Kconfig
+++ b/src/cpu/amd/agesa/Kconfig
@@ -29,6 +29,7 @@
select UDELAY_LAPIC
select LAPIC_MONOTONIC_TIMER
select SPI_FLASH if HAVE_ACPI_RESUME
+ select POSTCAR_STAGE if !AGESA_LEGACY_WRAPPER
if CPU_AMD_AGESA
diff --git a/src/cpu/amd/agesa/Makefile.inc b/src/cpu/amd/agesa/Makefile.inc
index c8e125e..f4ec2ce 100644
--- a/src/cpu/amd/agesa/Makefile.inc
+++ b/src/cpu/amd/agesa/Makefile.inc
@@ -24,11 +24,13 @@
cpu_incs-y += $(src)/cpu/amd/agesa/cache_as_ram_legacy.inc
else
cpu_incs-y += $(src)/cpu/amd/agesa/cache_as_ram.S
-romstage-y += romstage.c
+romstage-y += romstage.c mtrr_fixme.c
endif
romstage-$(CONFIG_AGESA_LEGACY_WRAPPER) += heapmanager.c
+postcar-y += cache_as_ram.S
+
ramstage-y += heapmanager.c
ramstage-$(CONFIG_AGESA_LEGACY_WRAPPER) += amd_late_init.c
diff --git a/src/cpu/amd/agesa/cache_as_ram.S b/src/cpu/amd/agesa/cache_as_ram.S
index b96a5e7..50242f7 100644
--- a/src/cpu/amd/agesa/cache_as_ram.S
+++ b/src/cpu/amd/agesa/cache_as_ram.S
@@ -29,6 +29,7 @@
.code32
.globl _cache_as_ram_setup, _cache_as_ram_setup_end
+.globl chipset_teardown_car
_cache_as_ram_setup:
@@ -105,20 +106,44 @@
movd %mm0, %eax /* bist */
pushl %eax
call romstage_main
+
+#if IS_ENABLED(CONFIG_POSTCAR_STAGE)
+
+/* We do not return. Execution continues with run_postcar_phase()
+ * calling to chipset_teardown_car below.
+ */
+ jmp postcar_entry_failure
+
+chipset_teardown_car:
+
+/*
+ * Retrieve return address from stack as it will get trashed below if
+ * execution is utilizing the cache-as-ram stack.
+ */
+ pop %esp
+
+#else
+
movl %eax, %esp
-/* Register %esp is new stacktop for remaining of romstage.
- * It is the only register preserved in AMD_DISABLE_STACK.
- */
+/* Register %esp is new stacktop for remaining of romstage. */
-disable_cache_as_ram:
+#endif
+
/* Disable cache */
movl %cr0, %eax
orl $CR0_CacheDisable, %eax
movl %eax, %cr0
+/* Register %esp is preserved in AMD_DISABLE_STACK. */
AMD_DISABLE_STACK
+#if IS_ENABLED(CONFIG_POSTCAR_STAGE)
+
+ jmp *%esp
+
+#else
+
/* enable cache */
movl %cr0, %eax
andl $0x9fffffff, %eax
@@ -126,9 +151,22 @@
call romstage_after_car
+#endif
+
/* Should never see this postcode */
post_code(0xaf)
+
stop:
+ hlt
+ jmp stop
+
+/* These are here for linking purposes. */
+.weak early_all_cores, romstage_main
+early_all_cores:
+romstage_main:
+postcar_entry_failure:
+ /* Should never see this postcode */
+ post_code(0xae)
jmp stop
_cache_as_ram_setup_end:
diff --git a/src/cpu/amd/agesa/mtrr_fixme.c b/src/cpu/amd/agesa/mtrr_fixme.c
new file mode 100644
index 0000000..1fbb553
--- /dev/null
+++ b/src/cpu/amd/agesa/mtrr_fixme.c
@@ -0,0 +1,100 @@
+/*
+ * This file is part of the coreboot project.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <arch/cpu.h>
+#include <cbmem.h>
+#include <console/console.h>
+#include <cpu/amd/mtrr.h>
+#include <cpu/cpu.h>
+#include <cpu/x86/cache.h>
+#include <cpu/x86/msr.h>
+#include <cpu/x86/mtrr.h>
+#include <northbridge/amd/agesa/agesa_helper.h>
+
+static void set_range_uc(u32 base, u32 size)
+{
+ int i, max_var_mtrrs;
+ msr_t msr;
+ msr = rdmsr(MTRR_CAP_MSR);
+ max_var_mtrrs = msr.lo & MTRR_CAP_VCNT;
+
+ for (i = 0; i < max_var_mtrrs; i++) {
+ msr = rdmsr(MTRR_PHYS_MASK(i));
+ if (!(msr.lo & MTRR_PHYS_MASK_VALID))
+ break;
+ }
+ if (i == max_var_mtrrs)
+ die("Run out of unused MTRRs\n");
+
+ msr.hi = 0;
+ msr.lo = base | MTRR_TYPE_UNCACHEABLE;
+ wrmsr(MTRR_PHYS_BASE(i), msr);
+
+ msr.hi = (1 << (cpu_phys_address_size() - 32)) - 1;
+ msr.lo = ~(size - 1) | MTRR_PHYS_MASK_VALID;
+ wrmsr(MTRR_PHYS_MASK(i), msr);
+}
+
+void fixup_cbmem_to_UC(int s3resume)
+{
+ if (s3resume)
+ return;
+
+ /* For normal path, INIT_POST has returned with all
+ * memory set WB cacheable. But we need CBMEM as UC
+ * to make CAR teardown with invalidation without
+ * writeback possible.
+ */
+
+ uintptr_t top_of_ram = (uintptr_t) cbmem_top();
+ top_of_ram = ALIGN_UP(top_of_ram, 4 * MiB);
+
+ set_range_uc(top_of_ram - 4 * MiB, 4 * MiB);
+ set_range_uc(top_of_ram - 8 * MiB, 4 * MiB);
+}
+
+void recover_postcar_frame(struct postcar_frame *pcf, int s3resume)
+{
+ msr_t base, mask;
+ int i;
+
+ /* Replicate non-UC MTRRs as left behind by AGESA.
+ */
+ for (i = 0; i < pcf->max_var_mtrrs; i++) {
+ mask = rdmsr(MTRR_PHYS_MASK(i));
+ base = rdmsr(MTRR_PHYS_BASE(i));
+ u32 size = ~(mask.lo & ~0xfff) + 1;
+ u8 type = base.lo & 0x7;
+ base.lo &= ~0xfff;
+
+ if (!(mask.lo & MTRR_PHYS_MASK_VALID) ||
+ (type == MTRR_TYPE_UNCACHEABLE))
+ continue;
+
+ postcar_frame_add_mtrr(pcf, base.lo, size, type);
+ }
+
+ /* For S3 resume path, INIT_RESUME does not return with
+ * memory covering CBMEM set as WB cacheable. For better
+ * speed make them WB after CAR teardown.
+ */
+ if (s3resume) {
+ uintptr_t top_of_ram = (uintptr_t) cbmem_top();
+ top_of_ram = ALIGN_DOWN(top_of_ram, 4*MiB);
+
+ postcar_frame_add_mtrr(pcf, top_of_ram - 4*MiB, 4*MiB,
+ MTRR_TYPE_WRBACK);
+ postcar_frame_add_mtrr(pcf, top_of_ram - 8*MiB, 4*MiB,
+ MTRR_TYPE_WRBACK);
+ }
+}
diff --git a/src/cpu/amd/agesa/romstage.c b/src/cpu/amd/agesa/romstage.c
index f8b1e43..1b9757a 100644
--- a/src/cpu/amd/agesa/romstage.c
+++ b/src/cpu/amd/agesa/romstage.c
@@ -18,7 +18,6 @@
#include <cbmem.h>
#include <cpu/amd/car.h>
#include <cpu/x86/bist.h>
-#include <cpu/x86/mtrr.h>
#include <console/console.h>
#include <halt.h>
#include <program_loading.h>
@@ -54,6 +53,7 @@
void * asmlinkage romstage_main(unsigned long bist)
{
+ struct postcar_frame pcf;
struct sysinfo romstage_state;
struct sysinfo *cb = &romstage_state;
u8 initial_apic_id = (u8) (cpuid_ebx(1) >> 24);
@@ -100,6 +100,9 @@
}
+ if (IS_ENABLED(CONFIG_POSTCAR_STAGE))
+ fixup_cbmem_to_UC(cb->s3resume);
+
cbmem_initted = !cbmem_recovery(cb->s3resume);
if (cb->s3resume && !cbmem_initted) {
@@ -107,16 +110,25 @@
halt();
}
- uintptr_t stack_top = romstage_ram_stack_base(HIGH_ROMSTAGE_STACK_SIZE,
- ROMSTAGE_STACK_CBMEM);
- stack_top += HIGH_ROMSTAGE_STACK_SIZE;
-
romstage_handoff_init(cb->s3resume);
- printk(BIOS_DEBUG, "Move CAR stack.\n");
- return (void*)stack_top;
+ if (!IS_ENABLED(CONFIG_POSTCAR_STAGE)) {
+ uintptr_t stack_top = romstage_ram_stack_base(
+ HIGH_ROMSTAGE_STACK_SIZE, ROMSTAGE_STACK_CBMEM);
+ stack_top += HIGH_ROMSTAGE_STACK_SIZE;
+ printk(BIOS_DEBUG, "Move CAR stack.\n");
+ return (void*)stack_top;
+ }
+
+ postcar_frame_init(&pcf, HIGH_ROMSTAGE_STACK_SIZE);
+ recover_postcar_frame(&pcf, cb->s3resume);
+
+ run_postcar_phase(&pcf);
+ /* We do not return. */
+ return NULL;
}
+#if !IS_ENABLED(CONFIG_POSTCAR_STAGE)
void asmlinkage romstage_after_car(void)
{
struct sysinfo romstage_state;
@@ -131,3 +143,4 @@
run_ramstage();
}
+#endif
diff --git a/src/cpu/amd/pi/Kconfig b/src/cpu/amd/pi/Kconfig
index 51897b6..d71b832 100644
--- a/src/cpu/amd/pi/Kconfig
+++ b/src/cpu/amd/pi/Kconfig
@@ -27,6 +27,7 @@
select UDELAY_LAPIC
select LAPIC_MONOTONIC_TIMER
select SPI_FLASH if HAVE_ACPI_RESUME
+ select POSTCAR_STAGE if !BINARYPI_LEGACY_WRAPPER
if CPU_AMD_PI
diff --git a/src/cpu/amd/pi/Makefile.inc b/src/cpu/amd/pi/Makefile.inc
index df79c82..ba9ac26 100644
--- a/src/cpu/amd/pi/Makefile.inc
+++ b/src/cpu/amd/pi/Makefile.inc
@@ -18,6 +18,7 @@
subdirs-$(CONFIG_CPU_AMD_PI_00660F01) += 00660F01
cpu_incs-y += $(src)/cpu/amd/agesa/cache_as_ram.S
+postcar-y += ../agesa/cache_as_ram.S
ifeq ($(CONFIG_BINARYPI_LEGACY_WRAPPER), y)
romstage-y += romstage.c
@@ -25,6 +26,7 @@
romstage-y += ../agesa/heapmanager.c
else
romstage-y += ../agesa/romstage.c
+romstage-y += ../agesa/mtrr_fixme.c
endif
ramstage-y += ../agesa/heapmanager.c
diff --git a/src/northbridge/amd/agesa/agesa_helper.h b/src/northbridge/amd/agesa/agesa_helper.h
index 6b7275c..044393d 100644
--- a/src/northbridge/amd/agesa/agesa_helper.h
+++ b/src/northbridge/amd/agesa/agesa_helper.h
@@ -17,6 +17,7 @@
#define _AGESA_HELPER_H_
#include <stddef.h>
+#include <arch/cpu.h>
enum {
PICK_DMI, /* DMI Interface */
@@ -53,5 +54,7 @@
#define HIGH_MEMORY_SCRATCH 0x30000
+void fixup_cbmem_to_UC(int s3resume);
+void recover_postcar_frame(struct postcar_frame *pcf, int s3resume);
#endif /* _AGESA_HELPER_H_ */
diff --git a/src/northbridge/amd/pi/Makefile.inc b/src/northbridge/amd/pi/Makefile.inc
index b4d8c95..93c87ec 100644
--- a/src/northbridge/amd/pi/Makefile.inc
+++ b/src/northbridge/amd/pi/Makefile.inc
@@ -35,5 +35,6 @@
ramstage-y += ../agesa/def_callouts.c ../agesa/eventlog.c ../agesa/acpi_tables.c
romstage-y += ramtop.c
+postcar-y += ramtop.c
ramstage-y += ramtop.c
endif
diff --git a/src/southbridge/amd/agesa/hudson/Makefile.inc b/src/southbridge/amd/agesa/hudson/Makefile.inc
index 89236a6..2bf6f02 100644
--- a/src/southbridge/amd/agesa/hudson/Makefile.inc
+++ b/src/southbridge/amd/agesa/hudson/Makefile.inc
@@ -24,6 +24,7 @@
ramstage-y += resume.c ramtop.c
romstage-y += ramtop.c
+postcar-y += ramtop.c
romstage-y += imc.c
ramstage-y += imc.c
diff --git a/src/southbridge/amd/cimx/sb700/Makefile.inc b/src/southbridge/amd/cimx/sb700/Makefile.inc
index 0b9ee9c..0b7614b 100644
--- a/src/southbridge/amd/cimx/sb700/Makefile.inc
+++ b/src/southbridge/amd/cimx/sb700/Makefile.inc
@@ -21,6 +21,8 @@
romstage-y += reset.c
romstage-y += ramtop.c
+postcar-y += ramtop.c
+
ramstage-y += late.c
ramstage-y += reset.c
ramstage-y += ramtop.c
diff --git a/src/southbridge/amd/cimx/sb800/Makefile.inc b/src/southbridge/amd/cimx/sb800/Makefile.inc
index 0511fb3..a5287fa 100644
--- a/src/southbridge/amd/cimx/sb800/Makefile.inc
+++ b/src/southbridge/amd/cimx/sb800/Makefile.inc
@@ -30,6 +30,7 @@
ramstage-$(CONFIG_SPI_FLASH) += spi.c
ramstage-$(CONFIG_HAVE_ACPI_TABLES) += fadt.c
+postcar-y += ramtop.c
romstage-y += ramtop.c
ramstage-y += ramtop.c
diff --git a/src/southbridge/amd/cimx/sb900/Makefile.inc b/src/southbridge/amd/cimx/sb900/Makefile.inc
index b09180c..ff9ada6 100644
--- a/src/southbridge/amd/cimx/sb900/Makefile.inc
+++ b/src/southbridge/amd/cimx/sb900/Makefile.inc
@@ -22,6 +22,8 @@
romstage-y += reset.c
romstage-y += ramtop.c
+postcar-y += ramtop.c
+
ramstage-y += cfg.c
ramstage-y += early.c
ramstage-y += late.c
diff --git a/src/vendorcode/amd/agesa/f12/gcccar.inc b/src/vendorcode/amd/agesa/f12/gcccar.inc
index 6a81fc7..c08c9f1 100644
--- a/src/vendorcode/amd/agesa/f12/gcccar.inc
+++ b/src/vendorcode/amd/agesa/f12/gcccar.inc
@@ -678,7 +678,11 @@
# This shouldn't be used with S3 resume IF the stack/cache area is
# not reserved and over system memory.
#--------------------------------------------------------------------------
+#if !IS_ENABLED(CONFIG_POSTCAR_STAGE)
wbinvd
+#else
+ invd
+#endif
mov %bx, %ax # Restore INVD -> WBINVD bit
_WRMSR
diff --git a/src/vendorcode/amd/agesa/f14/gcccar.inc b/src/vendorcode/amd/agesa/f14/gcccar.inc
index 10214a8..678990f 100644
--- a/src/vendorcode/amd/agesa/f14/gcccar.inc
+++ b/src/vendorcode/amd/agesa/f14/gcccar.inc
@@ -826,7 +826,11 @@
# This shouldn't be used with S3 resume IF the stack/cache area is
# not reserved and over system memory.
#--------------------------------------------------------------------------
+#if !IS_ENABLED(CONFIG_POSTCAR_STAGE)
wbinvd
+#else
+ invd
+#endif
bts $INVD_WBINVD, %eax # Turn on Conversion of INVD to WBINVD
_WRMSR
diff --git a/src/vendorcode/amd/agesa/f15/gcccar.inc b/src/vendorcode/amd/agesa/f15/gcccar.inc
index 427c7e5..5076272 100644
--- a/src/vendorcode/amd/agesa/f15/gcccar.inc
+++ b/src/vendorcode/amd/agesa/f15/gcccar.inc
@@ -433,7 +433,11 @@
# This shouldn't be used with S3 resume IF the stack/cache area is
# not reserved and over system memory.
#--------------------------------------------------------------------------
+#if !IS_ENABLED(CONFIG_POSTCAR_STAGE)
wbinvd
+#else
+ invd
+#endif
mov %bx, %ax # Restore INVD -> WBINVD bit
_WRMSR
diff --git a/src/vendorcode/amd/agesa/f15tn/gcccar.inc b/src/vendorcode/amd/agesa/f15tn/gcccar.inc
index 7ac9613..8e15503 100644
--- a/src/vendorcode/amd/agesa/f15tn/gcccar.inc
+++ b/src/vendorcode/amd/agesa/f15tn/gcccar.inc
@@ -1263,7 +1263,11 @@
# This shouldn't be used with S3 resume IF the stack/cache area is
# not reserved and over system memory.
#--------------------------------------------------------------------------
+#if !IS_ENABLED(CONFIG_POSTCAR_STAGE)
wbinvd
+#else
+ invd
+#endif
#.if (bh == 01h) || (bh == 03h) ; Is this TN or KM?
cmp $01, %bh
diff --git a/src/vendorcode/amd/agesa/f16kb/gcccar.inc b/src/vendorcode/amd/agesa/f16kb/gcccar.inc
index 26e61da..8ad4d1d 100644
--- a/src/vendorcode/amd/agesa/f16kb/gcccar.inc
+++ b/src/vendorcode/amd/agesa/f16kb/gcccar.inc
@@ -615,7 +615,11 @@
# This shouldn't be used with S3 resume IF the stack/cache area is
# not reserved and over system memory.
#--------------------------------------------------------------------------
+#if !IS_ENABLED(CONFIG_POSTCAR_STAGE)
wbinvd
+#else
+ invd
+#endif
#Do Standard Family 16 work
mov $HWCR, %ecx # MSR:C001_0015h
diff --git a/src/vendorcode/amd/pi/00630F01/binaryPI/gcccar.inc b/src/vendorcode/amd/pi/00630F01/binaryPI/gcccar.inc
index 4092dbf..fc7d0e4 100644
--- a/src/vendorcode/amd/pi/00630F01/binaryPI/gcccar.inc
+++ b/src/vendorcode/amd/pi/00630F01/binaryPI/gcccar.inc
@@ -916,7 +916,11 @@
# This shouldn't be used with S3 resume IF the stack/cache area is
# not reserved and over system memory.
#--------------------------------------------------------------------------
+#if !IS_ENABLED(CONFIG_POSTCAR_STAGE)
wbinvd
+#else
+ invd
+#endif
#.if (bh == 01h) || (bh == 03h) ; Is this TN or KV?
cmp $01, %bh
diff --git a/src/vendorcode/amd/pi/00660F01/binaryPI/gcccar.inc b/src/vendorcode/amd/pi/00660F01/binaryPI/gcccar.inc
index 8d208d6..45ed294 100644
--- a/src/vendorcode/amd/pi/00660F01/binaryPI/gcccar.inc
+++ b/src/vendorcode/amd/pi/00660F01/binaryPI/gcccar.inc
@@ -651,7 +651,11 @@
# This shouldn't be used with S3 resume IF the stack/cache area is
# not reserved and over system memory.
#--------------------------------------------------------------------------
+#if !IS_ENABLED(CONFIG_POSTCAR_STAGE)
wbinvd
+#else
+ invd
+#endif
# #.if (bh == 01h) || (bh == 03h) ; Is this TN or KM?
# cmp $01, %bh
diff --git a/src/vendorcode/amd/pi/00730F01/binaryPI/gcccar.inc b/src/vendorcode/amd/pi/00730F01/binaryPI/gcccar.inc
index 4761e48..5a4f7b9 100644
--- a/src/vendorcode/amd/pi/00730F01/binaryPI/gcccar.inc
+++ b/src/vendorcode/amd/pi/00730F01/binaryPI/gcccar.inc
@@ -615,7 +615,11 @@
# This shouldn't be used with S3 resume IF the stack/cache area is
# not reserved and over system memory.
#--------------------------------------------------------------------------
+#if !IS_ENABLED(CONFIG_POSTCAR_STAGE)
wbinvd
+#else
+ invd
+#endif
#Do Standard Family 16 work
mov $HWCR, %ecx # MSR:C001_0015h
diff --git a/src/vendorcode/amd/pi/Makefile.inc b/src/vendorcode/amd/pi/Makefile.inc
index bcf078d..0192faa 100644
--- a/src/vendorcode/amd/pi/Makefile.inc
+++ b/src/vendorcode/amd/pi/Makefile.inc
@@ -85,7 +85,9 @@
CC_bootblock := $(CC_bootblock) $(AGESA_INC) $(AGESA_CFLAGS)
CC_romstage := $(CC_romstage) $(AGESA_INC) $(AGESA_CFLAGS)
+CC_postcar:= $(CC_postcar) -I$(AGESA_ROOT)/binaryPI
CC_ramstage := $(CC_ramstage) $(AGESA_INC) $(AGESA_CFLAGS)
+
CC_x86_32 := $(CC_x86_32) $(AGESA_INC) $(AGESA_CFLAGS)
CC_x86_64 := $(CC_x86_64) $(AGESA_INC) $(AGESA_CFLAGS)