Alexandru Gagniuc (mr.nuke.me(a)gmail.com) just uploaded a new patch set to gerrit, which you can find at https://review.coreboot.org/13364
-gerrit
commit 833b1785033aacce72063e8c0626885a6e03e2e9
Author: Alexandru Gagniuc <alexandrux.gagniuc(a)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(a)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(a)intel.com> for Intel Corp.)
+ * (Written by Andrey Petrov <andrey.petrov(a)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);
Alexandru Gagniuc (mr.nuke.me(a)gmail.com) just uploaded a new patch set to gerrit, which you can find at https://review.coreboot.org/13361
-gerrit
commit 78a0dab27e65370cc51fde56479dcfa8ff6bf359
Author: Alexandru Gagniuc <alexandrux.gagniuc(a)intel.com>
Date: Thu Nov 12 11:01:31 2015 -0800
soc/apollolake/uart: Keep track of console UART base address
After the MMIO region for the console UART is relocated,
uart_platform_base() no longer returns the updated location. The new
base address is now tracked and updated before set_resources(), so
that console remains operational.
Change-Id: I136959391798b9c3b376b1173d6b7b78063bd013
Signed-off-by: Alexandru Gagniuc <alexandrux.gagniuc(a)intel.com>
---
src/soc/intel/apollolake/uart.c | 44 ++++++++++++++++++++++++++++++++++++++---
1 file changed, 41 insertions(+), 3 deletions(-)
diff --git a/src/soc/intel/apollolake/uart.c b/src/soc/intel/apollolake/uart.c
index 00bc45f..ec4b68c 100644
--- a/src/soc/intel/apollolake/uart.c
+++ b/src/soc/intel/apollolake/uart.c
@@ -11,14 +11,20 @@
*/
#include <console/uart.h>
+#include <device/device.h>
+#include <device/pci.h>
+#include <device/pci_ids.h>
/*
- * TODO: We need a mechanism to return the new BAR once the resource allocator
- * gives us a new location.
+ * Shadow BAR for console UART:
+ * This allows us to keep track of the actual MMIO region without needing to
+ * pci_dev_locate()/pci_read_config32() on every uart_platform_base() call.
*/
+static uintptr_t uart_bar = CONFIG_CONSOLE_UART_BASE_ADDRESS;
+
uintptr_t uart_platform_base(int idx)
{
- return (CONFIG_CONSOLE_UART_BASE_ADDRESS);
+ return uart_bar;
}
unsigned int uart_platform_refclk(void)
@@ -26,3 +32,35 @@ unsigned int uart_platform_refclk(void)
/* That's within 0.5% of the actual value we've set earlier */
return 115200 * 16;
}
+
+static void update_uart_bar(struct device *dev)
+{
+ uart_bar = find_resource(dev, PCI_BASE_ADDRESS_0)->base;
+}
+
+static void uart_set_resources(struct device *dev)
+{
+ if (dev->path.pci.devfn == PCI_DEVFN(0x18, CONFIG_UART_FOR_CONSOLE))
+ update_uart_bar(dev);
+
+ pci_dev_set_resources(dev);
+}
+
+static struct device_operations uart_ops = {
+ .read_resources = pci_dev_read_resources,
+ .set_resources = uart_set_resources,
+ .enable_resources = pci_dev_enable_resources,
+ .init = pci_dev_init,
+ .enable = DEVICE_NOOP
+};
+
+static const unsigned short uart_ids[] = {
+ 0x5abc, 0x5abe, 0x5ac0, 0x5aee,
+ 0
+};
+
+static const struct pci_driver uart_driver __pci_driver = {
+ .ops = &uart_ops,
+ .vendor = PCI_VENDOR_ID_INTEL,
+ .devices = uart_ids
+};