[coreboot] New patch to review for coreboot: d8f2b2f Add multicore support to coreboot.

Ronald G. Minnich (rminnich@gmail.com) gerrit at coreboot.org
Fri Apr 6 21:41:26 CEST 2012


Ronald G. Minnich (rminnich at gmail.com) just uploaded a new patch set to gerrit, which you can find at http://review.coreboot.org/877

-gerrit

commit d8f2b2f51ab249977430685dec0093b8e0256106
Author: Ron Minnich <rminnich at gmail.com>
Date:   Thu Apr 5 23:51:18 2012 -0700

    Add multicore support to coreboot.
    
    This set of changes supports the ability for multicore support in coreboot.
    To give people a chance to examine the structure, and to make bisecting
    for bugs easier, we are implementing the patch in 3 stages. This first stage
    introduces the basic mechanism but does not make any visible change in
    behavior. The APs, instead of being immediately spun down, are asked to
    run one debug print function and then spun down.
    
    The means by which APs are tasked was implemented in the NIX operating
    system in 2011, see: http://code.google.com/p/nix-os/
    The APs come alive and spin on a memory location, contained
    in a per-AP structure. Tasking is accomplished
    by setting parameters into an argument array and then writing a function
    pointer into the memory location. The AP indicates completion by writing
    zero to the memory location. The BSP can wait for the AP to finish
    (synchronous) or going off to do other work (asynchronous).
    
    This way of tasking APs is incredibly cheap and fast: in the minimal
    case, one memory write suffices to launch an AP into doing work.
    
    This code has been tested on a sandybridge system (chromebook)
    and on another 4-core sandybridge system.
    
    Change-Id: I19f8587562fd499e98457aa9f11b52400c105697
---
 src/arch/x86/include/arch/cpu.h    |    5 ++
 src/arch/x86/lib/cpu.c             |   30 ++++++++--
 src/cpu/x86/lapic/lapic_cpu_init.c |  117 +++++++++++++++++++++++++++++++++---
 src/include/cpu/cpu.h              |   10 +++-
 4 files changed, 146 insertions(+), 16 deletions(-)

diff --git a/src/arch/x86/include/arch/cpu.h b/src/arch/x86/include/arch/cpu.h
index 604abde..34d0856 100644
--- a/src/arch/x86/include/arch/cpu.h
+++ b/src/arch/x86/include/arch/cpu.h
@@ -157,9 +157,14 @@ struct cpu_driver {
 struct device;
 struct cpu_driver *find_cpu_driver(struct device *cpu);
 
+typedef u32 (*workfunc)(u32, u32, u32);
+
 struct cpu_info {
 	device_t cpu;
 	unsigned long index;
+	workfunc work;
+	u32 params[3];
+	u32 result;
 };
 
 static inline struct cpu_info *cpu_info(void)
diff --git a/src/arch/x86/lib/cpu.c b/src/arch/x86/lib/cpu.c
index 98ede06..5e2683e 100644
--- a/src/arch/x86/lib/cpu.c
+++ b/src/arch/x86/lib/cpu.c
@@ -234,7 +234,7 @@ static void set_cpu_ops(struct device *cpu)
 	cpu->ops = driver ? driver->ops : NULL;
 }
 
-void cpu_initialize(void)
+void cpu_initialize(struct cpu_info *info)
 {
 	/* Because we busy wait at the printk spinlock.
 	 * It is important to keep the number of printed messages
@@ -242,12 +242,9 @@ void cpu_initialize(void)
 	 * disabled.
 	 */
 	struct device *cpu;
-	struct cpu_info *info;
 	struct cpuinfo_x86 c;
 
-	info = cpu_info();
-
-	printk(BIOS_INFO, "Initializing CPU #%ld\n", info->index);
+	printk(BIOS_INFO, "cpu_initialize: CPU #%ld, info %p\n", info->index, info);
 
 	cpu = info->cpu;
 	if (!cpu) {
@@ -289,3 +286,26 @@ void cpu_initialize(void)
 	return;
 }
 
+void cpu_work(struct cpu_info *info)
+{
+	workfunc f;
+	volatile workfunc *ptr = &info->work;
+	volatile u32 *params = info->params;
+
+	printk(BIOS_INFO, "CPU #%ld ready to work\n", info->index);
+
+	while (!*ptr)
+		;
+	f = *ptr;
+
+	printk(BIOS_SPEW, "CPU #%ld is asked to do %p\n", info->index, f);
+	f(params[0], params[1], params[2]);
+
+	printk(BIOS_SPEW, "CPU #%ld finishes %p, mark %p\n", info->index, f, ptr);
+	*ptr = 0;
+
+	printk(BIOS_INFO, "CPU #%ld leaving cpu_work()\n", info->index);
+
+	return;
+}
+
diff --git a/src/cpu/x86/lapic/lapic_cpu_init.c b/src/cpu/x86/lapic/lapic_cpu_init.c
index ed9940c..02924e2 100644
--- a/src/cpu/x86/lapic/lapic_cpu_init.c
+++ b/src/cpu/x86/lapic/lapic_cpu_init.c
@@ -16,6 +16,21 @@
 #include <cpu/cpu.h>
 
 #if CONFIG_SMP == 1
+
+/* we do not want this struct visible outside this file.
+ * The external interface is via functions.
+ */
+
+struct apcore {
+	u32 stack[1024 - sizeof(struct cpu_info)];
+	struct cpu_info info;
+};
+
+#define TOS(x) (&apcores[(x)].stack[ARRAY_SIZE(apcores[x].stack)-1])
+typedef struct apcore apcore_t;
+
+apcore_t apcores[CONFIG_MAX_CPUS];
+
 /* This is a lot more paranoid now, since Linux can NOT handle
  * being told there is a CPU when none exists. So any errors
  * will return 0, meaning no CPU.
@@ -224,9 +239,11 @@ static spinlock_t start_cpu_lock = SPIN_LOCK_UNLOCKED;
 static unsigned last_cpu_index = 0;
 volatile unsigned long secondary_stack;
 
+static void testfunc(u32 a, u32 b, u32 c){
+	printk(a, "testfunc: %s %d\n", (char *)b, (int)c);
+}
 int start_cpu(device_t cpu)
 {
-	extern unsigned char _estack[];
 	struct cpu_info *info;
 	unsigned long stack_end;
 	unsigned long apicid;
@@ -243,16 +260,17 @@ int start_cpu(device_t cpu)
 	index = ++last_cpu_index;
 
 	/* Find end of the new processors stack */
-	stack_end = ((unsigned long)_estack) - (CONFIG_STACK_SIZE*index) - sizeof(struct cpu_info);
+	stack_end = (unsigned long) TOS(index);
 
 	/* Record the index and which cpu structure we are using */
-	info = (struct cpu_info *)stack_end;
+	info = &apcores[index].info;
 	info->index = index;
 	info->cpu   = cpu;
 
 	/* Advertise the new stack to start_cpu */
 	secondary_stack = stack_end;
-
+	printk(BIOS_SPEW, "start_cpu CPU %ld secondary_stack %#lx info %p\n",
+		index, secondary_stack, info);
 	/* Until the cpu starts up report the cpu is not enabled */
 	cpu->enabled = 0;
 	cpu->initialized = 0;
@@ -273,6 +291,12 @@ int start_cpu(device_t cpu)
 	}
 	secondary_stack = 0;
 	spin_unlock(&start_cpu_lock);
+	if (result) { /* test work */
+		printk(BIOS_SPEW, "BSP tests work\n");
+		run_work(index, (workfunc)testfunc, BIOS_SPEW,
+			(u32)"Hello; run_work works", 555, NULL, 5000000);
+		printk(BIOS_SPEW, "Done core %ld\n", index);
+	}
 	return result;
 }
 
@@ -380,8 +404,11 @@ static __inline__ __attribute__((always_inline)) void writecr4(unsigned long Dat
 #endif
 
 /* C entry point of secondary cpus */
-void secondary_cpu_init(void)
-{
+void secondary_cpu_init(u32 x)
+ {
+	/* note: we tried (((u32 *)&x)[1]) but that failed. Compiler? */
+	struct cpu_info *info = (struct cpu_info *)(((u8*)&x)+4);
+
 	atomic_inc(&active_cpus);
 #if CONFIG_SERIAL_CPU_INIT == 1
 	spin_lock(&start_cpu_lock);
@@ -397,11 +424,11 @@ void secondary_cpu_init(void)
 	cr4_val |= (1 << 9 | 1 << 10);
 	writecr4(cr4_val);
 #endif
-	cpu_initialize();
+	cpu_initialize(info);
 #if CONFIG_SERIAL_CPU_INIT == 1
 	spin_unlock(&start_cpu_lock);
 #endif
-
+	cpu_work(info);
 	atomic_dec(&active_cpus);
 
 	stop_this_cpu();
@@ -498,6 +525,7 @@ void initialize_cpus(struct bus *cpu_bus)
 	info->cpu = alloc_find_dev(cpu_bus, &cpu_path);
 
 #if CONFIG_SMP == 1
+	memset(apcores, 0, sizeof(*apcores));
 	copy_secondary_start_to_1m_below(); // why here? In case some day we can start core1 in amd_sibling_init
 #endif
 
@@ -515,7 +543,7 @@ void initialize_cpus(struct bus *cpu_bus)
 #endif
 
 	/* Initialize the bootstrap processor */
-	cpu_initialize();
+	cpu_initialize(info);
 
 #if CONFIG_SMP == 1
 	#if CONFIG_SERIAL_CPU_INIT == 1
@@ -527,3 +555,74 @@ void initialize_cpus(struct bus *cpu_bus)
 #endif
 }
 
+#if CONFIG_SMP == 1
+/* work primitives */
+int start_work(unsigned int core, workfunc f, u32 a, u32 b, u32 c)
+{
+	struct cpu_info *info;
+	volatile workfunc *ptr;
+	volatile u32 *params;
+
+	if (core > CONFIG_MAX_CPUS){
+		printk(BIOS_EMERG, "start_work: invalid core %d\n", core);
+		return -1;
+	}
+	info = &apcores[core].info;
+	if (! info->cpu->initialized){
+		printk(BIOS_EMERG, "start_work: core not initialized %d\n", core);
+		return -1;
+	}
+	ptr = &info->work;
+	if (*ptr){
+		printk(BIOS_EMERG, "start_work: core is busy %d\n", core);
+		return -1;
+	}
+	params = (u32 *)&info->params;
+	params[0] = a;
+	params[1] = b;
+	params[2] = c;
+	printk(BIOS_INFO, "BSP starts work on core %d\n", core);
+	*ptr = f;
+	return 0;
+}
+
+int wait_work(unsigned int core, u32 *retval, unsigned int maxwait)
+{
+	int i = 0;
+	struct cpu_info *info;
+	volatile workfunc *ptr;
+	volatile u32 *result;
+
+	if (core > CONFIG_MAX_CPUS){
+		printk(BIOS_EMERG, "start_work: invalid core %d\n", core);
+		return -1;
+	}
+	info = &apcores[core].info;
+	if (! info->cpu->initialized){
+		printk(BIOS_EMERG, "start_work: core not initialized %d\n", core);
+		return -1;
+	}
+	ptr = &info->work;
+	while (i++ < maxwait && *ptr)
+		if (i%100000 == 0) printk(BIOS_SPEW, "still waiting on %p at %d\n",
+			ptr, i);
+	if (*ptr){
+		printk(BIOS_INFO, "core %d still running after %d iterations\n",
+			core, i);
+		return -1;
+	}
+	result = &info->result;
+	printk(BIOS_INFO, "info->work after result is %#lx\n", (unsigned long)*result);
+	if (retval)
+		*retval = *result;
+	return 0;
+}
+
+int run_work(unsigned int core, workfunc f, u32 a, u32 b, u32 c, u32 *retval,
+	unsigned int timeout)
+{
+	if (start_work(core, f, a, b, c))
+		return -1;
+	return wait_work(core, retval, timeout);
+}
+#endif /* CONFIG_SMP == 1 */
diff --git a/src/include/cpu/cpu.h b/src/include/cpu/cpu.h
index cca2be1..39f2fa2 100644
--- a/src/include/cpu/cpu.h
+++ b/src/include/cpu/cpu.h
@@ -5,9 +5,15 @@ struct device;
 struct bus;
 #include <arch/cpu.h>
 
-void cpu_initialize(void);
+void cpu_initialize(struct cpu_info *info);
 void initialize_cpus(struct bus *cpu_bus);
-void secondary_cpu_init(void);
+void secondary_cpu_init(u32 unused);
+int start_work(unsigned int core, workfunc f, u32 a, u32 b, u32 c);
+int wait_work(unsigned int core, u32 *retval, unsigned int maxwait);
+int run_work(unsigned int core, workfunc f, u32 a, u32 b, u32 c, u32 *retval,
+	unsigned int timeout);
+void cpu_work(struct cpu_info *info);
+
 
 #if !CONFIG_WAIT_BEFORE_CPUS_INIT
 	#define cpus_ready_for_init() do {} while(0)




More information about the coreboot mailing list