[coreboot-gerrit] Patch set updated for coreboot: soc/apollolake: Tear down cache-as-ram very early in ramstage

Alexandru Gagniuc (mr.nuke.me@gmail.com) gerrit at coreboot.org
Mon Jan 25 06:40:12 CET 2016


Alexandru Gagniuc (mr.nuke.me at gmail.com) just uploaded a new patch set to gerrit, which you can find at https://review.coreboot.org/13364

-gerrit

commit 35d7192e6f463ee7a41dd74c26d524d386a21635
Author: Alexandru Gagniuc <alexandrux.gagniuc at intel.com>
Date:   Fri Nov 13 16:42:10 2015 -0800

    soc/apollolake: Tear down cache-as-ram very early in ramstage
    
    Because of the reasons explained in the file, teardown of CAR needs to
    be done in ramstage. This patch addresses that issues in a way
    designed to minimize boot time. Teardown currently takes about 7 ms,
    but that time can be cut to half a millisecond by excluding the fsp
    reserved memory range from the flushed ranges.
    
    Change-Id: I6a0cb07f5b35ad42537da688e376ac96612990a6
    Signed-off-by: Alexandru Gagniuc <alexandrux.gagniuc at intel.com>
---
 src/soc/intel/apollolake/Makefile.inc   |   1 +
 src/soc/intel/apollolake/car_teardown.c | 146 ++++++++++++++++++++++++++++++++
 2 files changed, 147 insertions(+)

diff --git a/src/soc/intel/apollolake/Makefile.inc b/src/soc/intel/apollolake/Makefile.inc
index 23a2151..bdcacf2 100644
--- a/src/soc/intel/apollolake/Makefile.inc
+++ b/src/soc/intel/apollolake/Makefile.inc
@@ -24,6 +24,7 @@ romstage-y += tsc_freq.c
 romstage-y += uart_early.c
 
 ramstage-$(CONFIG_HAVE_ACPI_TABLES) += acpi.c
+ramstage-y += car_teardown.c
 ramstage-y += chip.c
 ramstage-y += cpu.c
 ramstage-y += gpio.c
diff --git a/src/soc/intel/apollolake/car_teardown.c b/src/soc/intel/apollolake/car_teardown.c
new file mode 100644
index 0000000..75c2e18
--- /dev/null
+++ b/src/soc/intel/apollolake/car_teardown.c
@@ -0,0 +1,146 @@
+/*
+ * Cache-as-ram teardown for apollolake SOC. See detailed explanation below.
+ *
+ * This file is part of the coreboot project.
+ *
+ * Copyright (C) 2015 Intel Corp.
+ * (Written by Alexandru Gagniuc <alexandrux.gagniuc at intel.com> for Intel Corp.)
+ * (Written by Andrey Petrov <andrey.petrov at intel.com> for Intel Corp.)
+ *
+ * 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; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <bootstate.h>
+#include <console/console.h>
+#include <cpu/cpu.h>
+#include <cpu/x86/cache.h>
+#include <cpu/x86/mtrr.h>
+#include <cpu/x86/msr.h>
+#include <cpu/x86/tsc.h>
+#include <cbmem.h>
+#include <symbols.h>
+
+#define EVICT_CTL_MSR		0x2e0
+
+/*
+ * The CAR teardown on apollolake is more complicated than the standard x86
+ * teardown paths because romstage executes in CAR. That means romstage cannot
+ * do the teardown. For performance reasons, the cache is also used as a backing
+ * store for RAM after raminit.
+ *
+ * This results in a cache with both a CAR region, and memory-backed regions:
+ *   - Invalidating the entire cache will evict stack and code that has not yet
+ *     been commited, to RAM, resulting in a crash.
+ *   - A 'wbinvd' will result in a crash during the write-back phase because
+ *     the CAR region has no backing store.
+ *
+ * This teardown path resolves these problems by first flushing important data
+ * out of cache, to memory. After this operation, it is safe to invalidate the
+ * entire cache, which is then followed by standard MTRR housekeeping.
+ *
+ * For performance reasons, only the memory regions which may have been written
+ * to are flushed. This includes ramstage code and cbmem. A complete flush of
+ * all RAM ranges would take several seconds.
+ */
+
+struct mem_region {
+	uintptr_t start;
+	uintptr_t end;
+};
+
+/* Returns true if two memory regions overlap, or one contains the other */
+static bool mem_regions_overlap(const struct mem_region *a,
+				const struct mem_region *b)
+{
+	if ((a->end < b->start) || (b->end < a->start))
+		return false;
+
+	return true;
+}
+
+/* Merge two regions into one. Assumes regions overlap */
+static void merge_regions(struct mem_region *a, struct mem_region *b)
+{
+	/* Merge second region into first */
+	a->start = MIN(a->start, b->start);
+	a->end = MAX(a->end, b->end);
+
+	/* Disable the second region */
+	b->start = b->end = 0;
+}
+
+static void clflush(uintptr_t addr)
+{
+	__asm__ volatile ("clflush (%0)"::"r" (addr));
+}
+
+static void clflush_mem_range(uintptr_t start, uintptr_t end)
+{
+	end = ALIGN_UP(end, 64);
+
+	for (start = ALIGN_DOWN(start, 64); start < end; start += 64)
+		clflush(start);
+}
+
+static void flush_ram_cache_and_invalidate(void)
+{
+	size_t cbmem_size;
+	struct mem_region ramstage, cbmem;
+
+	/* Figure out where ramstage and cbmem reside */
+	ramstage.start = (uintptr_t)&_program;
+	ramstage.end = (uintptr_t)&_eprogram;
+
+	cbmem_region_used(&cbmem.start, &cbmem_size);
+	cbmem.end = cbmem.start + cbmem_size;
+
+	if (mem_regions_overlap(&cbmem, &ramstage))
+		merge_regions(&cbmem, &ramstage);
+
+	/* TODO: Carve FSP reserved mem region out of flushable ranges */
+
+	/* Flush ramstage code and stack */
+	clflush_mem_range(cbmem.start, cbmem.end);
+	clflush_mem_range(ramstage.start, ramstage.end);
+
+	/* Now that important data is flushed, invalidate all caches */
+	invd();
+}
+
+static void disable_car_region_and_no_evict_mode(void)
+{
+	msr_t msr;
+
+	/* Disable variable and fixed MTRRs */
+	msr.lo = msr.hi = 0;
+	wrmsr(MTRR_DEF_TYPE_MSR, msr);
+	/* Disable CAR MTRR */
+	wrmsr(MTRR_PHYS_BASE(0), msr);
+	wrmsr(MTRR_PHYS_MASK(0), msr);
+
+	/* Switch out of "no evict" mode */
+	msr = rdmsr(EVICT_CTL_MSR);
+	msr.lo &= ~2;
+	wrmsr(EVICT_CTL_MSR, msr);
+
+	msr.lo &= ~1;
+	wrmsr(EVICT_CTL_MSR, msr);
+
+	/* Re-enable remaining MSRs */
+	msr.lo = MTRR_DEF_TYPE_EN;
+	msr.hi = 0;
+	wrmsr(MTRR_DEF_TYPE_MSR, msr);
+}
+
+static void tear_down_car(void *unused_arg)
+{
+	(void) unused_arg;
+
+	flush_ram_cache_and_invalidate();
+	disable_car_region_and_no_evict_mode();
+}
+
+BOOT_STATE_INIT_ENTRY(BS_PRE_DEVICE, BS_ON_ENTRY, tear_down_car, NULL);



More information about the coreboot-gerrit mailing list