the following patch was just integrated into master:
commit 38c326d041218e65d156ce3dd3bfee39e73ceffa
Author: Aaron Durbin <adurbin(a)chromium.org>
Date: Mon May 6 12:22:23 2013 -0500
x86: add thread support
Thread support is added for the x86 architecture. Both
the local apic and the tsc udelay() functions have a
call to thread_yield_microseconds() so as to provide an
opportunity to run pending threads.
Change-Id: Ie39b9eb565eb189676c06645bdf2a8720fe0636a
Signed-off-by: Aaron Durbin <adurbin(a)chromium.org>
Reviewed-on: http://review.coreboot.org/3207
Tested-by: build bot (Jenkins)
Reviewed-by: Ronald G. Minnich <rminnich(a)gmail.com>
See http://review.coreboot.org/3207 for details.
-gerrit
the following patch was just integrated into master:
commit 4409a5eef6d1d669caad1bfe3fbefee87ea7734e
Author: Aaron Durbin <adurbin(a)chromium.org>
Date: Mon May 6 12:20:52 2013 -0500
coreboot: add thread cooperative multitasking
The cooperative multitasking support allows the boot state machine
to be ran cooperatively with other threads of work. The main thread
still continues to run the boot state machine
(src/lib/hardwaremain.c). All callbacks from the state machine are
still ran synchronously from within the main thread's context.
Without any other code added the only change to the boot sequence
when cooperative multitasking is enabled is the queueing of an idlle
thread. The idle thread is responsible for ensuring progress is made
by calling timer callbacks.
The main thread can yield to any other threads in the system. That
means that anyone that spins up a thread must ensure no shared
resources are used from 2 or more execution contexts. The support
is originally intentioned to allow for long work itesm with busy
loops to occur in parallel during a boot.
Note that the intention on when to yield a thread will be on
calls to udelay().
Change-Id: Ia4d67a38665b12ce2643474843a93babd8a40c77
Signed-off-by: Aaron Durbin <adurbin(a)chromium.org>
Reviewed-on: http://review.coreboot.org/3206
Tested-by: build bot (Jenkins)
Reviewed-by: Ronald G. Minnich <rminnich(a)gmail.com>
See http://review.coreboot.org/3206 for details.
-gerrit
Ronald G. Minnich (rminnich(a)gmail.com) just uploaded a new patch set to gerrit, which you can find at http://review.coreboot.org/3242
-gerrit
commit e70f90c125b86bd968e82b1a22a4116bbd6e14bf
Author: Paul Menzel <paulepanter(a)users.sourceforge.net>
Date: Wed May 8 17:08:55 2013 +0200
Clean up usage of multiply_to_tsc
multiply_to_tsc was being copied everywhere, which is bad
practice. Put it in the tsc.h include file where it belongs.
Delete the copies of it.
We will leave it to secunet to tell us if they want a copyright
notice on a piece of code that gets implemented all over the
place; we need not make this decision for them.
This might be a good time to get a copyright notice into tsc.h anyway.
Change-Id: Ied0013ad4b1a9e5e2b330614bb867fd806f9a407
Signed-off-by: Paul Menzel <paulepanter(a)users.sourceforge.net>
Signed-off-by: Ronald G. Minnich <rminnich(a)gmail.com>
---
src/include/cpu/x86/tsc.h | 13 +++++++++++++
src/northbridge/intel/gm45/delay.c | 12 ------------
src/northbridge/intel/i5000/udelay.c | 12 ------------
src/northbridge/intel/i945/udelay.c | 12 ------------
src/northbridge/intel/sandybridge/udelay.c | 13 -------------
5 files changed, 13 insertions(+), 49 deletions(-)
diff --git a/src/include/cpu/x86/tsc.h b/src/include/cpu/x86/tsc.h
index 8e49a66..66451ad 100644
--- a/src/include/cpu/x86/tsc.h
+++ b/src/include/cpu/x86/tsc.h
@@ -27,6 +27,19 @@ static inline tsc_t rdtsc(void)
}
#if !defined(__ROMCC__)
+/* Simple 32- to 64-bit multiplication. Uses 16-bit words to avoid overflow.
+ * This code is used to prevent use of libgcc's umoddi3.
+ */
+static inline void multiply_to_tsc(tsc_t *const tsc, const u32 a, const u32 b)
+{
+ tsc->lo = (a & 0xffff) * (b & 0xffff);
+ tsc->hi = ((tsc->lo >> 16)
+ + ((a & 0xffff) * (b >> 16))
+ + ((b & 0xffff) * (a >> 16)));
+ tsc->lo = ((tsc->hi & 0xffff) << 16) | (tsc->lo & 0xffff);
+ tsc->hi = ((a >> 16) * (b >> 16)) + (tsc->hi >> 16);
+}
+
/* Too many registers for ROMCC */
static inline unsigned long long rdtscll(void)
{
diff --git a/src/northbridge/intel/gm45/delay.c b/src/northbridge/intel/gm45/delay.c
index 9f49c6e..a861e25 100644
--- a/src/northbridge/intel/gm45/delay.c
+++ b/src/northbridge/intel/gm45/delay.c
@@ -2,7 +2,6 @@
* This file is part of the coreboot project.
*
* Copyright (C) 2007-2008 coresystems GmbH
- * 2012 secunet Security Networks AG
*
* 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
@@ -24,17 +23,6 @@
#include <cpu/intel/speedstep.h>
#include "delay.h"
-/* Simple 32- to 64-bit multiplication. Uses 16-bit words to avoid overflow. */
-static inline void multiply_to_tsc(tsc_t *const tsc, const u32 a, const u32 b)
-{
- tsc->lo = (a & 0xffff) * (b & 0xffff);
- tsc->hi = ((tsc->lo >> 16)
- + ((a & 0xffff) * (b >> 16))
- + ((b & 0xffff) * (a >> 16)));
- tsc->lo = ((tsc->hi & 0xffff) << 16) | (tsc->lo & 0xffff);
- tsc->hi = ((a >> 16) * (b >> 16)) + (tsc->hi >> 16);
-}
-
/**
* Intel Core(tm) cpus always run the TSC at the maximum possible CPU clock
*/
diff --git a/src/northbridge/intel/i5000/udelay.c b/src/northbridge/intel/i5000/udelay.c
index 3768e16..f57f320 100644
--- a/src/northbridge/intel/i5000/udelay.c
+++ b/src/northbridge/intel/i5000/udelay.c
@@ -2,7 +2,6 @@
* This file is part of the coreboot project.
*
* Copyright (C) 2007-2008 coresystems GmbH
- * 2012 secunet Security Networks AG
*
* 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
@@ -24,17 +23,6 @@
#include <cpu/x86/msr.h>
#include <cpu/intel/speedstep.h>
-/* Simple 32- to 64-bit multiplication. Uses 16-bit words to avoid overflow. */
-static inline void multiply_to_tsc(tsc_t *const tsc, const u32 a, const u32 b)
-{
- tsc->lo = (a & 0xffff) * (b & 0xffff);
- tsc->hi = ((tsc->lo >> 16)
- + ((a & 0xffff) * (b >> 16))
- + ((b & 0xffff) * (a >> 16)));
- tsc->lo = ((tsc->hi & 0xffff) << 16) | (tsc->lo & 0xffff);
- tsc->hi = ((a >> 16) * (b >> 16)) + (tsc->hi >> 16);
-}
-
/**
* Intel Core(tm) cpus always run the TSC at the maximum possible CPU clock
*/
diff --git a/src/northbridge/intel/i945/udelay.c b/src/northbridge/intel/i945/udelay.c
index 780c730..3d5d6c6 100644
--- a/src/northbridge/intel/i945/udelay.c
+++ b/src/northbridge/intel/i945/udelay.c
@@ -2,7 +2,6 @@
* This file is part of the coreboot project.
*
* Copyright (C) 2007-2008 coresystems GmbH
- * 2012 secunet Security Networks AG
*
* 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
@@ -24,17 +23,6 @@
#include <cpu/x86/msr.h>
#include <cpu/intel/speedstep.h>
-/* Simple 32- to 64-bit multiplication. Uses 16-bit words to avoid overflow. */
-static inline void multiply_to_tsc(tsc_t *const tsc, const u32 a, const u32 b)
-{
- tsc->lo = (a & 0xffff) * (b & 0xffff);
- tsc->hi = ((tsc->lo >> 16)
- + ((a & 0xffff) * (b >> 16))
- + ((b & 0xffff) * (a >> 16)));
- tsc->lo = ((tsc->hi & 0xffff) << 16) | (tsc->lo & 0xffff);
- tsc->hi = ((a >> 16) * (b >> 16)) + (tsc->hi >> 16);
-}
-
/**
* Intel Core(tm) cpus always run the TSC at the maximum possible CPU clock
*/
diff --git a/src/northbridge/intel/sandybridge/udelay.c b/src/northbridge/intel/sandybridge/udelay.c
index 3edd69d..a2ce0d8 100644
--- a/src/northbridge/intel/sandybridge/udelay.c
+++ b/src/northbridge/intel/sandybridge/udelay.c
@@ -26,19 +26,6 @@
* Intel SandyBridge/IvyBridge CPUs always run the TSC at BCLK=100MHz
*/
-/* Simple 32- to 64-bit multiplication. Uses 16-bit words to avoid overflow.
- * This code is used to prevent use of libgcc's umoddi3.
- */
-static inline void multiply_to_tsc(tsc_t *const tsc, const u32 a, const u32 b)
-{
- tsc->lo = (a & 0xffff) * (b & 0xffff);
- tsc->hi = ((tsc->lo >> 16)
- + ((a & 0xffff) * (b >> 16))
- + ((b & 0xffff) * (a >> 16)));
- tsc->lo = ((tsc->hi & 0xffff) << 16) | (tsc->lo & 0xffff);
- tsc->hi = ((a >> 16) * (b >> 16)) + (tsc->hi >> 16);
-}
-
void udelay(u32 us)
{
u32 dword;
the following patch was just integrated into master:
commit 8c8af592ca20e6c2dc48bea2c3ae66aa92c9dca7
Author: Paul Menzel <paulepanter(a)users.sourceforge.net>
Date: Fri May 10 09:23:42 2013 +0200
AMD Brazos/Trinity boards: PlatformGnbPcie.c: Reserve correct amount of memory
In `PlatformGnbPcie.c` AGESA functions are used to reserve memory
space to save the PCIe configuration to. This is the
With the following definitions in `AGESA.h`
$ more src/vendorcode/amd/agesa/f14/AGESA.h
[…]
/// PCIe port descriptor
typedef struct {
IN UINT32 Flags; /**< Descriptor flags
* @li @b Bit31 - last descriptor in complex
*/
IN PCIe_ENGINE_DATA EngineData; ///< Engine data
IN PCIe_PORT_DATA Port; ///< PCIe port specific configuration info
} PCIe_PORT_DESCRIPTOR;
/// DDI descriptor
typedef struct {
IN UINT32 Flags; /**< Descriptor flags
* @li @b Bit31 - last descriptor in complex
*/
IN PCIe_ENGINE_DATA EngineData; ///< Engine data
IN PCIe_DDI_DATA Ddi; ///< DDI port specific configuration info
} PCIe_DDI_DESCRIPTOR;
/// PCIe Complex descriptor
typedef struct {
IN UINT32 Flags; /**< Descriptor flags
* @li @b Bit31 - last descriptor in topology
*/
IN UINT32 SocketId; ///< Socket Id
IN PCIe_PORT_DESCRIPTOR *PciePortList; ///< Pointer to array of PCIe port descriptors or NULL (Last element of array must be terminated with DESCRIPTOR_TERMINATE_LIST).
IN PCIe_DDI_DESCRIPTOR *DdiLinkList; ///< Pointer to array DDI link descriptors (Last element of array must be terminated with DESCRIPTOR_TERMINATE_LIST).
IN VOID *Reserved; ///< Reserved for future use
} PCIe_COMPLEX_DESCRIPTOR;
[…]
memory has to be reserved for the `PCIe_COMPLEX_DESCRIPTOR` and,
as two struct members are pointers to arrays with elements of type
`PCIe_PORT_DESCRIPTOR` and `PCIe_DDI_DESCRIPTOR`, space for these
times the number of array elements have to be reserved:
a + b * 5 + c * 2.
sizeof(PCIe_COMPLEX_DESCRIPTOR)
+ sizeof(PCIe_PORT_DESCRIPTOR) * 5
+ sizeof(PCIe_DDI_DESCRIPTOR) * 2;
But for whatever reason parentheses were put in there making this
calculation incorrect and reserving too much memory.
(a + b * 5 + c) * 2
So, remove the parentheses to reserve the exact amount of memory
needed.
The ASRock E350M1 still boots with these changes. No changes were
observed as expected.
Rudolf Marek made this change as part of his patch »ASUS F2A85-M:
Correct and clean up PCIe config« [1]. Factor this hunk out as it
affects all AMD Brazos and Trinity based boards.
[1] http://review.coreboot.org/#/c/3194/
Change-Id: I32e8c8a3dfc5e87eb119eb17719d612e57e0817a
Signed-off-by: Paul Menzel <paulepanter(a)users.sourceforge.net>
Reviewed-on: http://review.coreboot.org/3239
Tested-by: build bot (Jenkins)
Reviewed-by: Ronald G. Minnich <rminnich(a)gmail.com>
Reviewed-by: Jens Rottmann <JRottmann(a)LiPPERTembedded.de>
Reviewed-by: Bruce Griffith <Bruce.Griffith(a)se-eng.com>
See http://review.coreboot.org/3239 for details.
-gerrit
Aaron Durbin (adurbin(a)google.com) just uploaded a new patch set to gerrit, which you can find at http://review.coreboot.org/3206
-gerrit
commit 677d29fda2ef3052bb5fa146fe4a521bbca59986
Author: Aaron Durbin <adurbin(a)chromium.org>
Date: Mon May 6 12:20:52 2013 -0500
coreboot: add thread cooperative multitasking
The cooperative multitasking support allows the boot state machine
to be ran cooperatively with other threads of work. The main thread
still continues to run the boot state machine
(src/lib/hardwaremain.c). All callbacks from the state machine are
still ran synchronously from within the main thread's context.
Without any other code added the only change to the boot sequence
when cooperative multitasking is enabled is the queueing of an idlle
thread. The idle thread is responsible for ensuring progress is made
by calling timer callbacks.
The main thread can yield to any other threads in the system. That
means that anyone that spins up a thread must ensure no shared
resources are used from 2 or more execution contexts. The support
is originally intentioned to allow for long work itesm with busy
loops to occur in parallel during a boot.
Note that the intention on when to yield a thread will be on
calls to udelay().
Change-Id: Ia4d67a38665b12ce2643474843a93babd8a40c77
Signed-off-by: Aaron Durbin <adurbin(a)chromium.org>
---
src/Kconfig | 16 ++
src/arch/armv7/include/arch/cpu.h | 5 +
src/arch/x86/include/arch/cpu.h | 5 +
src/include/thread.h | 83 +++++++++
src/lib/Makefile.inc | 1 +
src/lib/hardwaremain.c | 3 +
src/lib/thread.c | 376 ++++++++++++++++++++++++++++++++++++++
7 files changed, 489 insertions(+)
diff --git a/src/Kconfig b/src/Kconfig
index ce5a048..4366bc8 100644
--- a/src/Kconfig
+++ b/src/Kconfig
@@ -324,6 +324,22 @@ config TIMER_QUEUE
help
Provide a timer queue for performing time-based callbacks.
+config COOP_MULTITASKING
+ def_bool n
+ depends on TIMER_QUEUE
+ help
+ Cooperative multitasking allows callbacks to be multiplexed on the
+ main thread of ramstage. With this enabled it allows for multiple
+ execution paths to take place when they have udelay() calls within
+ their code.
+
+config NUM_THREADS
+ int
+ default 4
+ depends on COOP_MULTITASKING
+ help
+ How many execution threads to cooperatively multitask with.
+
config HIGH_SCRATCH_MEMORY_SIZE
hex
default 0x0
diff --git a/src/arch/armv7/include/arch/cpu.h b/src/arch/armv7/include/arch/cpu.h
index 20a12c9..5621aed 100644
--- a/src/arch/armv7/include/arch/cpu.h
+++ b/src/arch/armv7/include/arch/cpu.h
@@ -30,9 +30,14 @@ struct cpu_driver {
struct cpu_device_id *id_table;
};
+struct thread;
+
struct cpu_info {
device_t cpu;
unsigned long index;
+#if CONFIG_COOP_MULTITASKING
+ struct thread *thread;
+#endif
};
struct cpuinfo_arm {
diff --git a/src/arch/x86/include/arch/cpu.h b/src/arch/x86/include/arch/cpu.h
index 890c77f..1fe12e5 100644
--- a/src/arch/x86/include/arch/cpu.h
+++ b/src/arch/x86/include/arch/cpu.h
@@ -159,9 +159,14 @@ struct cpu_driver {
struct cpu_driver *find_cpu_driver(struct device *cpu);
+struct thread;
+
struct cpu_info {
device_t cpu;
unsigned int index;
+#if CONFIG_COOP_MULTITASKING
+ struct thread *thread;
+#endif
};
static inline struct cpu_info *cpu_info(void)
diff --git a/src/include/thread.h b/src/include/thread.h
new file mode 100644
index 0000000..148c448
--- /dev/null
+++ b/src/include/thread.h
@@ -0,0 +1,83 @@
+/*
+ * This file is part of the coreboot project.
+ *
+ * Copyright (C) 2013 Google, Inc.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+#ifndef THREAD_H_
+#define THREAD_H_
+
+#include <stddef.h>
+#include <stdint.h>
+#include <bootstate.h>
+#include <timer.h>
+#include <arch/cpu.h>
+
+#if CONFIG_COOP_MULTITASKING && !defined(__SMM__) && !defined(__PRE_RAM__)
+
+struct thread {
+ int id;
+ uintptr_t stack_current;
+ uintptr_t stack_orig;
+ struct thread *next;
+ void (*entry)(void *);
+ void *entry_arg;
+ int can_yield;
+};
+
+void threads_initialize(void);
+/* Run func(arrg) on a new thread. Return 0 on successful start of thread, < 0
+ * when thread could not be started. Note that the thread will block the
+ * current state in the boot state machine until it is complete. */
+int thread_run(void (*func)(void *), void *arg);
+/* thread_run_until is the same as thread_run() except that it blocks state
+ * transitions from occuring in the (state, seq) pair of the boot state
+ * machine. */
+int thread_run_until(void (*func)(void *), void *arg,
+ boot_state_t state, boot_state_sequence_t seq);
+/* Return 0 on successful yield for the given amount of time, < 0 when thread
+ * did not yield. */
+int thread_yield_microseconds(unsigned microsecs);
+
+/* Allow and prevent thread cooperation on current running thread. By default
+ * all threads are marked to be cooperative. That means a thread can yeild
+ * to another thread at a pre-determined switch point. Current there is
+ * only a single place where switching may occur: a call to udelay(). */
+void thread_cooperate(void);
+void thread_prevent_coop(void);
+
+static inline void thread_init_cpu_info_non_bsp(struct cpu_info *ci)
+{
+ ci->thread = NULL;
+}
+
+/* Architecture specific thread functions. */
+void asmlinkage switch_to_thread(uintptr_t new_stack, uintptr_t *saved_stack);
+/* Set up the stack frame for a new thread so that a switch_to_thread() call
+ * will enter the thread_entry() function with arg as a parameter. The
+ * saved_stack field in the struct thread needs to be updated accordingly. */
+void arch_prepare_thread(struct thread *t,
+ void asmlinkage (*thread_entry)(void *), void *arg);
+#else
+static inline void threads_initialize(void) {}
+static inline int thread_run(void (*func)(void *), void *arg) { return -1; }
+static inline int thread_yield_microseconds(unsigned microsecs) { return -1; }
+static inline void thread_cooperate(void) {}
+static inline void thread_prevent_coop(void) {}
+struct cpu_info;
+static inline void thread_init_cpu_info_non_bsp(struct cpu_info *ci) { }
+#endif
+
+#endif /* THREAD_H_ */
diff --git a/src/lib/Makefile.inc b/src/lib/Makefile.inc
index 7306e6d..2600aa5 100644
--- a/src/lib/Makefile.inc
+++ b/src/lib/Makefile.inc
@@ -90,6 +90,7 @@ ramstage-$(CONFIG_COLLECT_TIMESTAMPS) += timestamp.c
ramstage-$(CONFIG_COVERAGE) += libgcov.c
ramstage-$(CONFIG_MAINBOARD_DO_NATIVE_VGA_INIT) += edid.c
ramstage-y += memrange.c
+ramstage-$(CONFIG_COOP_MULTITASKING) += thread.c
ramstage-$(CONFIG_TIMER_QUEUE) += timer_queue.c
# The CBMEM implementations are chosen based on CONFIG_DYNAMIC_CBMEM.
diff --git a/src/lib/hardwaremain.c b/src/lib/hardwaremain.c
index 99b4a06..14b3fff 100644
--- a/src/lib/hardwaremain.c
+++ b/src/lib/hardwaremain.c
@@ -39,6 +39,7 @@
#endif
#include <timer.h>
#include <timestamp.h>
+#include <thread.h>
#if BOOT_STATE_DEBUG
#define BS_DEBUG_LVL BIOS_DEBUG
@@ -459,6 +460,8 @@ void hardwaremain(void)
post_code(POST_CONSOLE_BOOT_MSG);
+ threads_initialize();
+
/* Schedule the static boot state entries. */
boot_state_schedule_static_entries();
diff --git a/src/lib/thread.c b/src/lib/thread.c
new file mode 100644
index 0000000..6508bfa
--- /dev/null
+++ b/src/lib/thread.c
@@ -0,0 +1,376 @@
+/*
+ * This file is part of the coreboot project.
+ *
+ * Copyright (C) 2013 Google, Inc.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+#include <stddef.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <arch/cpu.h>
+#include <bootstate.h>
+#include <console/console.h>
+#include <thread.h>
+
+static void idle_thread_init(void);
+
+/* There needs to be at least one thread to run the ramstate state machine. */
+#define TOTAL_NUM_THREADS (CONFIG_NUM_THREADS + 1)
+extern char thread_stacks[CONFIG_NUM_THREADS*CONFIG_STACK_SIZE];
+
+/* Storage space for the thread structs .*/
+static struct thread all_threads[TOTAL_NUM_THREADS];
+
+/* All runnable (but not running) and free threads are kept on their
+ * respective lists. */
+static struct thread *runnable_threads;
+static struct thread *free_threads;
+
+static inline struct cpu_info *thread_cpu_info(const struct thread *t)
+{
+ return (void *)(t->stack_orig);
+}
+
+static inline int thread_can_yield(const struct thread *t)
+{
+ return (t != NULL && t->can_yield);
+}
+
+/* Assumes current cpu info can switch. */
+static inline struct thread *cpu_info_to_thread(const struct cpu_info *ci)
+{
+ return ci->thread;
+}
+
+static inline struct thread *current_thread(void)
+{
+ return cpu_info_to_thread(cpu_info());
+}
+
+static inline int thread_list_empty(struct thread **list)
+{
+ return *list == NULL;
+}
+
+static inline struct thread *pop_thread(struct thread **list)
+{
+ struct thread *t;
+
+ t = *list;
+ *list = t->next;
+ t->next = NULL;
+ return t;
+}
+
+static inline void push_thread(struct thread **list, struct thread *t)
+{
+ t->next = *list;
+ *list = t;
+}
+
+static inline void push_runnable(struct thread *t)
+{
+ push_thread(&runnable_threads, t);
+}
+
+static inline struct thread *pop_runnable(void)
+{
+ return pop_thread(&runnable_threads);
+}
+
+static inline struct thread *get_free_thread(void)
+{
+ struct thread *t;
+ struct cpu_info *ci;
+ struct cpu_info *new_ci;
+
+ if (thread_list_empty(&free_threads))
+ return NULL;
+
+ t = pop_thread(&free_threads);
+
+ ci = cpu_info();
+
+ /* Initialize the cpu_info structure on the new stack. */
+ new_ci = thread_cpu_info(t);
+ *new_ci = *ci;
+ new_ci->thread = t;
+
+ /* Reset the current stack value to the original. */
+ t->stack_current = t->stack_orig;
+
+ return t;
+}
+
+static inline void free_thread(struct thread *t)
+{
+ push_thread(&free_threads, t);
+}
+
+/* The idle thread is ran whenever there isn't anything else that is runnable.
+ * It's sole responsibility is to ensure progress is made by running the timer
+ * callbacks. */
+static void idle_thread(void *unused)
+{
+ /* This thread never voluntarily yields. */
+ thread_prevent_coop();
+ while (1) {
+ timers_run();
+ }
+}
+
+static void schedule(struct thread *t)
+{
+ struct thread *current = current_thread();
+
+ /* If t is NULL need to find new runnable thread. */
+ if (t == NULL) {
+ if (thread_list_empty(&runnable_threads))
+ die("Runnable thread list is empty!\n");
+ t = pop_runnable();
+ } else {
+ /* current is still runnable. */
+ push_runnable(current);
+ }
+ switch_to_thread(t->stack_current, ¤t->stack_current);
+}
+
+static void terminate_thread(struct thread *t)
+{
+ free_thread(t);
+ schedule(NULL);
+}
+
+static void asmlinkage call_wrapper(void *unused)
+{
+ struct thread *current = current_thread();
+
+ current->entry(current->entry_arg);
+ terminate_thread(current);
+}
+
+/* Block the current state transitions until thread is complete. */
+static void asmlinkage call_wrapper_block_current(void *unused)
+{
+ struct thread *current = current_thread();
+
+ boot_state_current_block();
+ current->entry(current->entry_arg);
+ boot_state_current_unblock();
+ terminate_thread(current);
+}
+
+struct block_boot_state {
+ boot_state_t state;
+ boot_state_sequence_t seq;
+};
+
+/* Block the provided state until thread is complete. */
+static void asmlinkage call_wrapper_block_state(void *arg)
+{
+ struct block_boot_state *bbs = arg;
+ struct thread *current = current_thread();
+
+ boot_state_block(bbs->state, bbs->seq);
+ current->entry(current->entry_arg);
+ boot_state_unblock(bbs->state, bbs->seq);
+ terminate_thread(current);
+}
+
+/* Prepare a thread so that it starts by executing thread_entry(thread_arg).
+ * Within thread_entry() it will call func(arg). */
+static void prepare_thread(struct thread *t, void *func, void *arg,
+ void asmlinkage (*thread_entry)(void *),
+ void *thread_arg)
+{
+ /* Stash the function and argument to run. */
+ t->entry = func;
+ t->entry_arg = arg;
+
+ /* All new threads can yield by default. */
+ t->can_yield = 1;
+
+ arch_prepare_thread(t, thread_entry, thread_arg);
+}
+
+static void thread_resume_from_timeout(struct timeout_callback *tocb)
+{
+ struct thread *to;
+
+ to = tocb->priv;
+ schedule(to);
+}
+
+static void idle_thread_init(void)
+{
+ struct thread *t;
+
+ t = get_free_thread();
+
+ if (t == NULL) {
+ die("No threads available for idle thread!\n");
+ }
+
+ /* Queue idle thread to run once all other threads have yielded. */
+ prepare_thread(t, idle_thread, NULL, call_wrapper, NULL);
+ push_runnable(t);
+ /* Mark the currently executing thread to cooperate. */
+ thread_cooperate();
+}
+
+/* Don't inline this function so the timeout_callback won't have its storage
+ * space on the stack cleaned up before the call to schedule(). */
+static int __attribute__((noinline))
+thread_yield_timed_callback(struct timeout_callback *tocb, unsigned microsecs)
+{
+ tocb->priv = current_thread();
+ tocb->callback = thread_resume_from_timeout;
+
+ if (timer_sched_callback(tocb, microsecs))
+ return -1;
+
+ /* The timer callback will wake up the current thread. */
+ schedule(NULL);
+ return 0;
+}
+
+static void *thread_alloc_space(struct thread *t, size_t bytes)
+{
+ /* Allocate the amount of space on the stack keeping the stack
+ * aligned to the pointer size. */
+ t->stack_current -= ALIGN_UP(bytes, sizeof(uintptr_t));
+
+ return (void *)t->stack_current;
+}
+
+void threads_initialize(void)
+{
+ int i;
+ struct thread *t;
+ char *stack_top;
+ struct cpu_info *ci;
+
+ /* Initialize the BSP thread first. The cpu_info structure is assumed
+ * to be just under the top of the stack. */
+ t = &all_threads[0];
+ ci = cpu_info();
+ ci->thread = t;
+ t->stack_orig = (uintptr_t)ci;
+ t->id = 0;
+
+ stack_top = &thread_stacks[CONFIG_STACK_SIZE] - sizeof(struct cpu_info);
+ for (i = 1; i < TOTAL_NUM_THREADS; i++) {
+ t = &all_threads[i];
+ t->stack_orig = (uintptr_t)stack_top;
+ t->id = i;
+ stack_top += CONFIG_STACK_SIZE;
+ free_thread(t);
+ }
+
+ idle_thread_init();
+}
+
+int thread_run(void (*func)(void *), void *arg)
+{
+ struct thread *current;
+ struct thread *t;
+
+ current = current_thread();
+
+ if (!thread_can_yield(current)) {
+ printk(BIOS_ERR,
+ "thread_run() called from non-yielding context!\n");
+ return -1;
+ }
+
+ t = get_free_thread();
+
+ if (t == NULL) {
+ printk(BIOS_ERR, "thread_run() No more threads!\n");
+ return -1;
+ }
+
+ prepare_thread(t, func, arg, call_wrapper_block_current, NULL);
+ schedule(t);
+
+ return 0;
+}
+
+int thread_run_until(void (*func)(void *), void *arg,
+ boot_state_t state, boot_state_sequence_t seq)
+{
+ struct thread *current;
+ struct thread *t;
+ struct block_boot_state *bbs;
+
+ current = current_thread();
+
+ if (!thread_can_yield(current)) {
+ printk(BIOS_ERR,
+ "thread_run() called from non-yielding context!\n");
+ return -1;
+ }
+
+ t = get_free_thread();
+
+ if (t == NULL) {
+ printk(BIOS_ERR, "thread_run() No more threads!\n");
+ return -1;
+ }
+
+ bbs = thread_alloc_space(t, sizeof(*bbs));
+ bbs->state = state;
+ bbs->seq = seq;
+ prepare_thread(t, func, arg, call_wrapper_block_state, bbs);
+ schedule(t);
+
+ return 0;
+}
+
+int thread_yield_microseconds(unsigned microsecs)
+{
+ struct thread *current;
+ struct timeout_callback tocb;
+
+ current = current_thread();
+
+ if (!thread_can_yield(current))
+ return -1;
+
+ if (thread_yield_timed_callback(&tocb, microsecs))
+ return -1;
+
+ return 0;
+}
+
+void thread_cooperate(void)
+{
+ struct thread *current;
+
+ current = current_thread();
+
+ if (current != NULL)
+ current->can_yield = 1;
+}
+
+void thread_prevent_coop(void)
+{
+ struct thread *current;
+
+ current = current_thread();
+
+ if (current != NULL)
+ current->can_yield = 0;
+}
Aaron Durbin (adurbin(a)google.com) just uploaded a new patch set to gerrit, which you can find at http://review.coreboot.org/3207
-gerrit
commit 8b0307421e93e2dcd964d4f9253cb0b60a9cecd8
Author: Aaron Durbin <adurbin(a)chromium.org>
Date: Mon May 6 12:22:23 2013 -0500
x86: add thread support
Thread support is added for the x86 architecture. Both
the local apic and the tsc udelay() functions have a
call to thread_yield_microseconds() so as to provide an
opportunity to run pending threads.
Change-Id: Ie39b9eb565eb189676c06645bdf2a8720fe0636a
Signed-off-by: Aaron Durbin <adurbin(a)chromium.org>
---
src/Kconfig | 2 +-
src/arch/x86/lib/Makefile.inc | 2 ++
src/arch/x86/lib/c_start.S | 9 ++++++
src/arch/x86/lib/thread.c | 58 ++++++++++++++++++++++++++++++++++++++
src/arch/x86/lib/thread_switch.S | 58 ++++++++++++++++++++++++++++++++++++++
src/cpu/intel/haswell/mp_init.c | 2 ++
src/cpu/x86/lapic/apic_timer.c | 4 +++
src/cpu/x86/lapic/lapic_cpu_init.c | 2 ++
src/cpu/x86/tsc/delay_tsc.c | 4 +++
9 files changed, 140 insertions(+), 1 deletion(-)
diff --git a/src/Kconfig b/src/Kconfig
index 4366bc8..308deaa 100644
--- a/src/Kconfig
+++ b/src/Kconfig
@@ -326,7 +326,7 @@ config TIMER_QUEUE
config COOP_MULTITASKING
def_bool n
- depends on TIMER_QUEUE
+ depends on TIMER_QUEUE && ARCH_X86
help
Cooperative multitasking allows callbacks to be multiplexed on the
main thread of ramstage. With this enabled it allows for multiple
diff --git a/src/arch/x86/lib/Makefile.inc b/src/arch/x86/lib/Makefile.inc
index 82f4e62..7cdd3c6 100644
--- a/src/arch/x86/lib/Makefile.inc
+++ b/src/arch/x86/lib/Makefile.inc
@@ -9,6 +9,8 @@ ramstage-y += memset.c
ramstage-y += memcpy.c
ramstage-y += ebda.c
ramstage-y += rom_media.c
+ramstage-$(CONFIG_COOP_MULTITASKING) += thread.c
+ramstage-$(CONFIG_COOP_MULTITASKING) += thread_switch.S
romstage-$(CONFIG_EARLY_CONSOLE) += romstage_console.c
romstage-y += cbfs_and_run.c
diff --git a/src/arch/x86/lib/c_start.S b/src/arch/x86/lib/c_start.S
index 1e38aca..c725f82 100644
--- a/src/arch/x86/lib/c_start.S
+++ b/src/arch/x86/lib/c_start.S
@@ -10,6 +10,11 @@
_stack:
.space CONFIG_MAX_CPUS*CONFIG_STACK_SIZE
_estack:
+#if CONFIG_COOP_MULTITASKING
+.global thread_stacks
+thread_stacks:
+.space CONFIG_STACK_SIZE*CONFIG_NUM_THREADS
+#endif
.section ".textfirst", "ax", @progbits
.code32
@@ -45,6 +50,10 @@ _start:
/* set new stack */
movl $_estack, %esp
+#if CONFIG_COOP_MULTITASKING
+ /* Push the thread pointer. */
+ pushl $0
+#endif
/* Push the cpu index and struct cpu */
pushl $0
pushl $0
diff --git a/src/arch/x86/lib/thread.c b/src/arch/x86/lib/thread.c
new file mode 100644
index 0000000..06f8a15
--- /dev/null
+++ b/src/arch/x86/lib/thread.c
@@ -0,0 +1,58 @@
+/*
+ * This file is part of the coreboot project.
+ *
+ * Copyright (C) 2013 Google, Inc.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <thread.h>
+
+/* The stack frame looks like the following after a pushad instruction. */
+struct pushad_regs {
+ uint32_t edi; /* Offset 0x00 */
+ uint32_t esi; /* Offset 0x04 */
+ uint32_t ebp; /* Offset 0x08 */
+ uint32_t esp; /* Offset 0x0c */
+ uint32_t ebx; /* Offset 0x10 */
+ uint32_t edx; /* Offset 0x14 */
+ uint32_t ecx; /* Offset 0x18 */
+ uint32_t eax; /* Offset 0x1c */
+};
+
+static inline uintptr_t push_stack(uintptr_t cur_stack, uintptr_t value)
+{
+ uintptr_t *addr;
+
+ cur_stack -= sizeof(value);
+ addr = (uintptr_t *)cur_stack;
+ *addr = value;
+ return cur_stack;
+}
+
+void arch_prepare_thread(struct thread *t,
+ void asmlinkage (*thread_entry)(void *), void *arg)
+{
+ uintptr_t stack = t->stack_current;
+
+ /* Imitate thread_entry(t) with return address of 0. thread_entry()
+ * is assumed to never return. */
+ stack = push_stack(stack, (uintptr_t)arg);
+ stack = push_stack(stack, (uintptr_t)0);
+ stack = push_stack(stack, (uintptr_t)thread_entry);
+ /* Make room for the registers. Ignore intial values. */
+ stack -= sizeof(struct pushad_regs);
+
+ t->stack_current = stack;
+}
diff --git a/src/arch/x86/lib/thread_switch.S b/src/arch/x86/lib/thread_switch.S
new file mode 100644
index 0000000..8de1948
--- /dev/null
+++ b/src/arch/x86/lib/thread_switch.S
@@ -0,0 +1,58 @@
+/*
+ * This file is part of the coreboot project.
+ *
+ * Copyright (C) 2013 Google, Inc.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+.code32
+.text
+
+/*
+ * stack layout after pushad:
+ * +------------+
+ * | save stack | <-- esp + 0x28
+ * +------------+
+ * | new stack | <-- esp + 0x24
+ * +------------+
+ * | ret addr | <-- esp + 0x20
+ * +------------+
+ * | eax | <-- esp + 0x1c
+ * +------------+
+ * | ecx | <-- esp + 0x18
+ * +------------+
+ * | edx | <-- esp + 0x14
+ * +------------+
+ * | ebx | <-- esp + 0x10
+ * +------------+
+ * | orig esp | <-- esp + 0x0c
+ * +------------+
+ * | ebp | <-- esp + 0x08
+ * +------------+
+ * | esi | <-- esp + 0x04
+ * +------------+
+ * | edi | <-- esp + 0x00
+ * +------------+
+ */
+.globl switch_to_thread
+switch_to_thread:
+ pusha
+ /* Save the current stack */
+ movl 0x28(%esp), %ebx
+ movl %esp, (%ebx)
+ /* Switch to the new stack. */
+ movl 0x24(%esp), %eax
+ movl %eax, %esp
+ popa
+ ret
diff --git a/src/cpu/intel/haswell/mp_init.c b/src/cpu/intel/haswell/mp_init.c
index deba629..357fbb2 100644
--- a/src/cpu/intel/haswell/mp_init.c
+++ b/src/cpu/intel/haswell/mp_init.c
@@ -36,6 +36,7 @@
#include <lib.h>
#include <smp/atomic.h>
#include <smp/spinlock.h>
+#include <thread.h>
#include "haswell.h"
/* This needs to match the layout in the .module_parametrs section. */
@@ -163,6 +164,7 @@ static void asmlinkage ap_init(unsigned int cpu, void *microcode_ptr)
info = cpu_info();
info->index = cpu;
info->cpu = cpu_devs[cpu];
+ thread_init_cpu_info_non_bsp(info);
apic_id_table[info->index] = lapicid();
info->cpu->path.apic.apic_id = apic_id_table[info->index];
diff --git a/src/cpu/x86/lapic/apic_timer.c b/src/cpu/x86/lapic/apic_timer.c
index 749fef0..e5ce62f 100644
--- a/src/cpu/x86/lapic/apic_timer.c
+++ b/src/cpu/x86/lapic/apic_timer.c
@@ -21,6 +21,7 @@
#include <stdint.h>
#include <console/console.h>
#include <delay.h>
+#include <thread.h>
#include <arch/io.h>
#include <arch/cpu.h>
#include <cpu/x86/car.h>
@@ -95,6 +96,9 @@ void udelay(u32 usecs)
{
u32 start, value, ticks;
+ if (!thread_yield_microseconds(usecs))
+ return;
+
if (!timer_fsb || (lapic_read(LAPIC_LVTT) &
(LAPIC_LVT_TIMER_PERIODIC | LAPIC_LVT_MASKED)) !=
(LAPIC_LVT_TIMER_PERIODIC | LAPIC_LVT_MASKED))
diff --git a/src/cpu/x86/lapic/lapic_cpu_init.c b/src/cpu/x86/lapic/lapic_cpu_init.c
index 69430d5..fbc8aa4 100644
--- a/src/cpu/x86/lapic/lapic_cpu_init.c
+++ b/src/cpu/x86/lapic/lapic_cpu_init.c
@@ -32,6 +32,7 @@
#include <smp/spinlock.h>
#include <cpu/cpu.h>
#include <cpu/intel/speedstep.h>
+#include <thread.h>
#if CONFIG_SMP && CONFIG_MAX_CPUS > 1
/* This is a lot more paranoid now, since Linux can NOT handle
@@ -292,6 +293,7 @@ int start_cpu(device_t cpu)
info = (struct cpu_info *)stack_end;
info->index = index;
info->cpu = cpu;
+ thread_init_cpu_info_non_bsp(info);
/* Advertise the new stack and index to start_cpu */
secondary_stack = stack_end;
diff --git a/src/cpu/x86/tsc/delay_tsc.c b/src/cpu/x86/tsc/delay_tsc.c
index 0e2a9c0..b8f2503 100644
--- a/src/cpu/x86/tsc/delay_tsc.c
+++ b/src/cpu/x86/tsc/delay_tsc.c
@@ -4,6 +4,7 @@
#include <cpu/x86/tsc.h>
#include <smp/spinlock.h>
#include <delay.h>
+#include <thread.h>
#if !defined(__PRE_RAM__)
@@ -176,6 +177,9 @@ void udelay(unsigned us)
unsigned long long current;
unsigned long long clocks;
+ if (!thread_yield_microseconds(us))
+ return;
+
start = rdtscll();
clocks = us;
clocks *= get_clocks_per_usec();
Aaron Durbin (adurbin(a)google.com) just uploaded a new patch set to gerrit, which you can find at http://review.coreboot.org/3236
-gerrit
commit 8e669770ad7906bbb2421ce9f3cac56980dee0a1
Author: Aaron Durbin <adurbin(a)chromium.org>
Date: Fri May 10 00:51:43 2013 -0500
haswell: enable cache-as-ram migration
The haswell code allows for vboot ramstage verification.
However, that code path relies on accessing global cache-as-ram
variables after cache-as-ram is torn down. In order to avoid
that situation enable cache-as-ram migration.
cbmemc_reinit() no longer needs to be called from romstage
because it is invoked automatically by the cache-as-ram
migration infrastructure.
Change-Id: I08998dca579c167699030e1e24ea0af8802c0758
Signed-off-by: Aaron Durbin <adurbin(a)chromium.org>
---
src/cpu/intel/haswell/Kconfig | 1 +
src/cpu/intel/haswell/romstage.c | 5 -----
2 files changed, 1 insertion(+), 5 deletions(-)
diff --git a/src/cpu/intel/haswell/Kconfig b/src/cpu/intel/haswell/Kconfig
index 152059f..4c61b2d 100644
--- a/src/cpu/intel/haswell/Kconfig
+++ b/src/cpu/intel/haswell/Kconfig
@@ -18,6 +18,7 @@ config CPU_SPECIFIC_OPTIONS
#select AP_IN_SIPI_WAIT
select TSC_SYNC_MFENCE
select CPU_INTEL_FIRMWARE_INTERFACE_TABLE
+ select CAR_MIGRATION
config BOOTBLOCK_CPU_INIT
string
diff --git a/src/cpu/intel/haswell/romstage.c b/src/cpu/intel/haswell/romstage.c
index 1093e6b..8196273 100644
--- a/src/cpu/intel/haswell/romstage.c
+++ b/src/cpu/intel/haswell/romstage.c
@@ -188,11 +188,6 @@ void * asmlinkage romstage_main(unsigned long bist)
/* Get the stack to use after cache-as-ram is torn down. */
romstage_stack_after_car = setup_romstage_stack_after_car();
-#if CONFIG_CONSOLE_CBMEM
- /* Keep this the last thing this function does. */
- cbmemc_reinit();
-#endif
-
return romstage_stack_after_car;
}
Aaron Durbin (adurbin(a)google.com) just uploaded a new patch set to gerrit, which you can find at http://review.coreboot.org/3232
-gerrit
commit 9da7d26e20325e07abc95d1dce09dc82d6398cb4
Author: Aaron Durbin <adurbin(a)chromium.org>
Date: Fri May 10 00:33:32 2013 -0500
x86: add cache-as-ram migration option
There are some boards that do a significant amount of
work after cache-as-ram is torn down but before ramstage
is loaded. For example, using vboot to verify the ramstage
is one such operation. However, there are pieces of code
that are executed that reference global variables that
are linked in the cache-as-ram region. If those variables
are referenced after cache-as-ram is torn down then the
values observed will most likely be incorrect.
Therefore provide a Kconfig option to select cache-as-ram
migration to memory using cbmem. This option is named
CAR_MIGRATION. When enabled, the address of cache-as-ram
variables may be obtained dynamically. Additionally,
when cache-as-ram migration occurs the cache-as-ram
data region for global variables is copied into cbmem.
There are also automatic callbacks for other modules
to perform their own migration, if necessary.
Change-Id: I2e77219647c2bd2b1aa845b262be3b2543f1fcb7
Signed-off-by: Aaron Durbin <adurbin(a)chromium.org>
---
src/arch/x86/init/romstage.ld | 12 +++++
src/cpu/Makefile.inc | 1 +
src/cpu/x86/Kconfig | 9 ++++
src/cpu/x86/Makefile.inc | 1 +
src/cpu/x86/car.c | 101 ++++++++++++++++++++++++++++++++++++++++++
src/include/cbmem.h | 1 +
src/include/cpu/x86/car.h | 29 ++++++++++++
src/lib/cbmem.c | 4 ++
src/lib/cbmem_info.c | 1 +
src/lib/dynamic_cbmem.c | 9 ++++
10 files changed, 168 insertions(+)
diff --git a/src/arch/x86/init/romstage.ld b/src/arch/x86/init/romstage.ld
index 88c5657..f44185f 100644
--- a/src/arch/x86/init/romstage.ld
+++ b/src/arch/x86/init/romstage.ld
@@ -35,6 +35,10 @@ SECTIONS
*(.rodata.*);
*(.rom.data.*);
. = ALIGN(16);
+ _car_migrate_start = .;
+ *(.car.migrate);
+ _car_migrate_end = .;
+ . = ALIGN(16);
_erom = .;
}
@@ -48,8 +52,16 @@ SECTIONS
. = CONFIG_DCACHE_RAM_BASE;
.car.data . (NOLOAD) : {
+ _car_data_start = .;
*(.car.global_data);
+ /* The cbmem_console section comes last to take advantage of
+ * a zero-sized array to hold the memconsole contents that
+ * grows to a bound of CONFIG_CONSOLE_CAR_BUFFER_SIZE. However,
+ * collisions within the cache-as-ram region cannot be
+ * statically checked because the cache-as-ram region usage is
+ * cpu/chipset dependent. */
*(.car.cbmem_console);
+ _car_data_end = .;
}
_bogus = ASSERT((SIZEOF(.car.data) <= CONFIG_DCACHE_RAM_SIZE), "Cache as RAM area is too full");
diff --git a/src/cpu/Makefile.inc b/src/cpu/Makefile.inc
index b48a803..8d93756 100644
--- a/src/cpu/Makefile.inc
+++ b/src/cpu/Makefile.inc
@@ -6,6 +6,7 @@ subdirs-y += armltd
subdirs-y += intel
subdirs-y += samsung
subdirs-y += via
+subdirs-y += x86
################################################################################
## Rules for building the microcode blob in CBFS
diff --git a/src/cpu/x86/Kconfig b/src/cpu/x86/Kconfig
index c64a8e4..8bf98f1 100644
--- a/src/cpu/x86/Kconfig
+++ b/src/cpu/x86/Kconfig
@@ -115,3 +115,12 @@ config X86_AMD_FIXED_MTRRS
help
This option informs the MTRR code to use the RdMem and WrMem fields
in the fixed MTRR MSRs.
+
+config CAR_MIGRATION
+ def_bool n
+ depends on DYNAMIC_CBMEM || EARLY_CBMEM_INIT
+ help
+ Migrate the cache-as-ram variables to CBMEM once CBMEM is set up
+ in romstage. This option is only needed if one will be doing more
+ work in romstage after the cache-as-ram is torn down that aside from
+ loading ramstage.
diff --git a/src/cpu/x86/Makefile.inc b/src/cpu/x86/Makefile.inc
new file mode 100644
index 0000000..fe8648c
--- /dev/null
+++ b/src/cpu/x86/Makefile.inc
@@ -0,0 +1 @@
+romstage-$(CONFIG_CAR_MIGRATION) += car.c
diff --git a/src/cpu/x86/car.c b/src/cpu/x86/car.c
new file mode 100644
index 0000000..31fc67c
--- /dev/null
+++ b/src/cpu/x86/car.c
@@ -0,0 +1,101 @@
+/*
+ * This file is part of the coreboot project.
+ *
+ * Copyright (C) 2013 Google, Inc.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <string.h>
+#include <stddef.h>
+#include <console/console.h>
+#include <cbmem.h>
+#include <cpu/x86/car.h>
+
+typedef void (* const car_migration_func_t)(void);
+
+extern car_migration_func_t _car_migrate_start;
+extern car_migration_func_t _car_migrate_end;
+
+extern char _car_data_start[];
+extern char _car_data_end[];
+
+/*
+ * The car_migrated global variable determines if the cache-as-ram space has
+ * been migrated to real RAM. It does this by asumming the following things:
+ * 1. cache-as-ram space is zero'd out once it is set up.
+ * 2. Either the cache-as-ram space is memory-backed after getting torn down
+ * or the space returns 0xff's for each byte read.
+ * Based on these 2 attributes there is the ability to tell when the
+ * cache-as-ram region has been migrated.
+ */
+static int car_migrated CAR_GLOBAL;
+
+
+void *car_get_var_ptr(void *var)
+{
+ char *migrated_base;
+ int offset;
+ void * _car_start = &_car_data_start;
+ void * _car_end = &_car_data_end;
+
+ /* If the cache-as-ram has not been migrated return the pointer
+ * passed in. */
+ if (!car_migrated)
+ return var;
+
+ if (var < _car_start || var >= _car_end) {
+ printk(BIOS_ERR,
+ "Requesting CAR variable outside of CAR region: %p\n",
+ var);
+ return var;
+ }
+
+ migrated_base = cbmem_find(CBMEM_ID_CAR_GLOBALS);
+
+ if (migrated_base == NULL) {
+ printk(BIOS_ERR, "CAR: Could not find migration base!\n");
+ return var;
+ }
+
+ offset = (char *)var - (char *)_car_start;
+
+ return &migrated_base[offset];
+}
+
+void car_migrate_variables(void)
+{
+ void *migrated_base;
+ car_migration_func_t *migrate_func;
+ size_t car_data_size = &_car_data_end[0] - &_car_data_start[0];
+
+ migrated_base = cbmem_add(CBMEM_ID_CAR_GLOBALS, car_data_size);
+
+ if (migrated_base == NULL) {
+ printk(BIOS_ERR, "Could not migrate CAR data!\n");
+ return;
+ }
+
+ memcpy(migrated_base, &_car_data_start[0], car_data_size);
+
+ /* Mark that the data has been moved. */
+ car_migrated = ~0;
+
+ /* Call all the migration functions. */
+ migrate_func = &_car_migrate_start;
+ while (migrate_func != &_car_migrate_end) {
+ (*migrate_func)();
+ migrate_func++;
+ }
+}
diff --git a/src/include/cbmem.h b/src/include/cbmem.h
index 67cb1cb..baec780 100644
--- a/src/include/cbmem.h
+++ b/src/include/cbmem.h
@@ -69,6 +69,7 @@
#define CBMEM_ID_RAMSTAGE_CACHE 0x9a3ca54e
#define CBMEM_ID_ROOT 0xff4007ff
#define CBMEM_ID_VBOOT_HANDOFF 0x780074f0
+#define CBMEM_ID_CAR_GLOBALS 0xcac4e6a3
#define CBMEM_ID_NONE 0x00000000
#ifndef __ASSEMBLER__
diff --git a/src/include/cpu/x86/car.h b/src/include/cpu/x86/car.h
index 2d2af03..7b5cedf 100644
--- a/src/include/cpu/x86/car.h
+++ b/src/include/cpu/x86/car.h
@@ -28,4 +28,33 @@
#define CAR_CBMEM
#endif
+#if CONFIG_CAR_MIGRATION && defined(__PRE_RAM__)
+#define CAR_MIGRATE_ATTR __attribute__ ((used,section (".car.migrate")))
+
+/* Call migrate_fn_() when CAR globals are migrated. */
+#define CAR_MIGRATE(migrate_fn_) \
+ static void (* const migrate_fn_ ## _ptr)(void) CAR_MIGRATE_ATTR = \
+ migrate_fn_;
+
+/* Get the correct pointer for the CAR global variable. */
+void *car_get_var_ptr(void *var);
+
+/* Get and set a primitive type global variable. */
+#define car_get_var(var) \
+ *(typeof(var) *)car_get_var_ptr(&(var))
+#define car_set_var(var, val) \
+ do { car_get_var(var) = (val); } while(0)
+
+/* Migrate the CAR variables to memory. */
+void car_migrate_variables(void);
+
+#else
+#define CAR_MIGRATE(migrate_fn_)
+static inline void *car_get_var_ptr(void *var) { return var; }
+#define car_get_var(var) (var)
+#define car_set_var(var, val) do { (var) = (val); } while (0)
+static inline void car_migrate_variables(void) { }
+#endif
+
+
#endif
diff --git a/src/lib/cbmem.c b/src/lib/cbmem.c
index e8200b6..3702da1 100644
--- a/src/lib/cbmem.c
+++ b/src/lib/cbmem.c
@@ -22,6 +22,7 @@
#include <bootstate.h>
#include <cbmem.h>
#include <console/console.h>
+#include <cpu/x86/car.h>
#if CONFIG_HAVE_ACPI_RESUME && !defined(__PRE_RAM__)
#include <arch/acpi.h>
#endif
@@ -228,6 +229,9 @@ int cbmem_initialize(void)
#ifndef __PRE_RAM__
cbmem_arch_init();
#endif
+ /* Migrate cache-as-ram variables. */
+ car_migrate_variables();
+
return rv;
}
#endif
diff --git a/src/lib/cbmem_info.c b/src/lib/cbmem_info.c
index ad8c890..7031a70 100644
--- a/src/lib/cbmem_info.c
+++ b/src/lib/cbmem_info.c
@@ -46,6 +46,7 @@ static struct cbmem_id_to_name {
{ CBMEM_ID_RAMSTAGE_CACHE, "RAMSTAGE $ " },
{ CBMEM_ID_ROOT, "CBMEM ROOT " },
{ CBMEM_ID_VBOOT_HANDOFF, "VBOOT " },
+ { CBMEM_ID_CAR_GLOBALS, "CAR GLOBALS" },
};
void cbmem_print_entry(int n, u32 id, u64 base, u64 size)
diff --git a/src/lib/dynamic_cbmem.c b/src/lib/dynamic_cbmem.c
index 5c269a0..ba7760d 100644
--- a/src/lib/dynamic_cbmem.c
+++ b/src/lib/dynamic_cbmem.c
@@ -23,6 +23,7 @@
#include <cbmem.h>
#include <string.h>
#include <stdlib.h>
+#include <cpu/x86/car.h>
#if CONFIG_HAVE_ACPI_RESUME && !defined(__PRE_RAM__)
#include <arch/acpi.h>
#endif
@@ -182,12 +183,17 @@ void cbmem_initialize_empty(void)
root, root->max_entries);
cbmem_arch_init();
+
+ /* Migrate cache-as-ram variables. */
+ car_migrate_variables();
}
static inline int cbmem_fail_recovery(void)
{
cbmem_initialize_empty();
cbmem_handle_acpi_resume();
+ /* Migrate cache-as-ram variables. */
+ car_migrate_variables();
return 1;
}
@@ -256,6 +262,9 @@ int cbmem_initialize(void)
cbmem_arch_init();
+ /* Migrate cache-as-ram variables. */
+ car_migrate_variables();
+
/* Recovery successful. */
return 0;
}
Aaron Durbin (adurbin(a)google.com) just uploaded a new patch set to gerrit, which you can find at http://review.coreboot.org/3233
-gerrit
commit 8139065652ecfb721838af176533eeaab0066772
Author: Aaron Durbin <adurbin(a)chromium.org>
Date: Fri May 10 00:40:56 2013 -0500
pc80/tpm: allow for cache-as-ram migration
As the TPM driver can be accessed in romstage after
cache-as-ram is torn down use the cache-as-ram migration
API to dynamically determine the global variable address.
Change-Id: I149d7c130bc3677ed52282095670c07a76c34439
Signed-off-by: Aaron Durbin <adurbin(a)chromium.org>
---
src/drivers/pc80/tpm.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/src/drivers/pc80/tpm.c b/src/drivers/pc80/tpm.c
index c7b5081..9a4fc09 100644
--- a/src/drivers/pc80/tpm.c
+++ b/src/drivers/pc80/tpm.c
@@ -278,7 +278,7 @@ static u32 tis_probe(void)
u16 vid, did;
int i;
- if (vendor_dev_id)
+ if (car_get_var(vendor_dev_id))
return 0; /* Already probed. */
didvid = tpm_read(0, TIS_REG_DID_VID);
@@ -287,7 +287,7 @@ static u32 tis_probe(void)
return TPM_DRIVER_ERR;
}
- vendor_dev_id = didvid;
+ car_set_var(vendor_dev_id, didvid);
vid = didvid & 0xffff;
did = (didvid >> 16) & 0xffff;
Aaron Durbin (adurbin(a)google.com) just uploaded a new patch set to gerrit, which you can find at http://review.coreboot.org/3234
-gerrit
commit 44a42ecce99fcd6ba895fadee70a8f0788e7f91b
Author: Aaron Durbin <adurbin(a)chromium.org>
Date: Fri May 10 00:42:14 2013 -0500
chromeos: use cache-as-ram migration API for vbnv
It's possible that the vbnv global variables may be accessed
in romstage after cache-as-ram is torn down. Therefore use
the cache-as-ram migration API. Wrappers were written to
wrap the API to keep the existing code as close as possible.
Change-Id: Ia1d8932f98e00def0a44444a1ead0018a59d3d98
Signed-off-by: Aaron Durbin <adurbin(a)chromium.org>
---
src/vendorcode/google/chromeos/vbnv.c | 32 +++++++++++++++++++++++++-------
1 file changed, 25 insertions(+), 7 deletions(-)
diff --git a/src/vendorcode/google/chromeos/vbnv.c b/src/vendorcode/google/chromeos/vbnv.c
index 2a2faf9..d94203a 100644
--- a/src/vendorcode/google/chromeos/vbnv.c
+++ b/src/vendorcode/google/chromeos/vbnv.c
@@ -53,7 +53,25 @@
#define CRC_OFFSET 15
static int vbnv_initialized CAR_GLOBAL;
-uint8_t vbnv[CONFIG_VBNV_SIZE] CAR_GLOBAL;
+static uint8_t vbnv[CONFIG_VBNV_SIZE] CAR_GLOBAL;
+
+/* Wrappers for accessing the variables marked as CAR_GLOBAL. */
+static inline int is_vbnv_initialized(void)
+{
+ return car_get_var(vbnv_initialized);
+}
+
+static inline uint8_t *vbnv_data_addr(int index)
+{
+ uint8_t *vbnv_arr = car_get_var_ptr(vbnv);
+
+ return &vbnv_arr[index];
+}
+
+static inline uint8_t vbnv_data(int index)
+{
+ return *vbnv_data_addr(index);
+}
/* Return CRC-8 of the data, using x^8 + x^2 + x + 1 polynomial. A
* table-based algorithm would be faster, but for only 15 bytes isn't
@@ -109,20 +127,20 @@ void save_vbnv(const uint8_t *vbnv_copy)
static void vbnv_setup(void)
{
- read_vbnv(vbnv);
- vbnv_initialized = 1;
+ read_vbnv(vbnv_data_addr(0));
+ car_set_var(vbnv_initialized, 1);
}
int get_recovery_mode_from_vbnv(void)
{
- if (!vbnv_initialized)
+ if (!is_vbnv_initialized())
vbnv_setup();
- return vbnv[RECOVERY_OFFSET];
+ return vbnv_data(RECOVERY_OFFSET);
}
int vboot_wants_oprom(void)
{
- if (!vbnv_initialized)
+ if (!is_vbnv_initialized())
vbnv_setup();
/* FIXME(crosbug.com/p/8789). The following commented-out line does the
@@ -130,6 +148,6 @@ int vboot_wants_oprom(void)
* rebooted if it finds that it's needed but not loaded. At the moment,
* it doesn't yet do that, so we must always say we want it. */
- /* return (vbnv[BOOT_OFFSET] & BOOT_OPROM_NEEDED) ? 1 : 0; */
+ /* return (vbnv_data(BOOT_OFFSET) & BOOT_OPROM_NEEDED) ? 1 : 0; */
return 1;
}