[U-Boot] [PATCH 17/17] SPARC, LEON3: added support for multiprocessing, tested Linux 2.6.21.1 SMP and RTEMS-4.10 AMP.

Daniel Hellstrom daniel at gaisler.com
Thu Jan 28 13:16:36 CET 2010


Signed-off-by: Daniel Hellstrom <daniel at gaisler.com>
---
 board/gaisler/gr_cpci_ax2000/u-boot.lds |    7 ++
 board/gaisler/gr_ep2s60/u-boot.lds      |    7 ++
 board/gaisler/gr_xc3s_1500/u-boot.lds   |    7 ++
 board/gaisler/grsim/u-boot.lds          |    7 ++
 cpu/leon3/Makefile                      |    2 +-
 cpu/leon3/cpu.c                         |   10 ++-
 cpu/leon3/cpu_mp.c                      |   80 ++++++++++++++
 cpu/leon3/prom.c                        |   32 +++---
 cpu/leon3/start.S                       |   70 ++++++++++++
 include/asm-sparc/boot_mp.h             |   70 ++++++++++++
 include/configs/gr_cpci_ax2000.h        |   12 ++-
 include/configs/gr_ep2s60.h             |   12 ++-
 include/configs/gr_xc3s_1500.h          |   14 ++-
 include/configs/grsim.h                 |   12 ++-
 lib_sparc/Makefile                      |    2 +-
 lib_sparc/boot_mp.c                     |  177 +++++++++++++++++++++++++++++++
 lib_sparc/bootm.c                       |   57 ++++++++--
 17 files changed, 540 insertions(+), 38 deletions(-)
 create mode 100644 cpu/leon3/cpu_mp.c
 create mode 100644 include/asm-sparc/boot_mp.h
 create mode 100644 lib_sparc/boot_mp.c

diff --git a/board/gaisler/gr_cpci_ax2000/u-boot.lds b/board/gaisler/gr_cpci_ax2000/u-boot.lds
index d5d7842..17a0fc3 100644
--- a/board/gaisler/gr_cpci_ax2000/u-boot.lds
+++ b/board/gaisler/gr_cpci_ax2000/u-boot.lds
@@ -72,6 +72,13 @@ SECTIONS
 		*(.prom.text)
 		. = ALIGN(16);
 		__prom_end = .;
+		/* Align MP section to the same as the MAX size of the MP section */
+		. = ALIGN(512);
+		__mp_start = .;
+		*(.mp.data)
+		*(.mp.text)
+		. = ALIGN(16);
+		__mp_end = .;
 		*(.text)
 		*(.fixup)
 		*(.gnu.warning)
diff --git a/board/gaisler/gr_ep2s60/u-boot.lds b/board/gaisler/gr_ep2s60/u-boot.lds
index 99aa0ad..c02a6d2 100644
--- a/board/gaisler/gr_ep2s60/u-boot.lds
+++ b/board/gaisler/gr_ep2s60/u-boot.lds
@@ -72,6 +72,13 @@ SECTIONS
 		*(.prom.text)
 		. = ALIGN(16);
 		__prom_end = .;
+		/* Align MP section to the same as the MAX size of the MP section */
+		. = ALIGN(512);
+		__mp_start = .;
+		*(.mp.data)
+		*(.mp.text)
+		. = ALIGN(16);
+		__mp_end = .;
 		*(.text)
 		*(.fixup)
 		*(.gnu.warning)
diff --git a/board/gaisler/gr_xc3s_1500/u-boot.lds b/board/gaisler/gr_xc3s_1500/u-boot.lds
index 3b13190..de7ea6b 100644
--- a/board/gaisler/gr_xc3s_1500/u-boot.lds
+++ b/board/gaisler/gr_xc3s_1500/u-boot.lds
@@ -72,6 +72,13 @@ SECTIONS
 		*(.prom.text)
 		. = ALIGN(16);
 		__prom_end = .;
+		/* Align MP section to the same as the MAX size of the MP section */
+		. = ALIGN(512);
+		__mp_start = .;
+		*(.mp.data)
+		*(.mp.text)
+		. = ALIGN(16);
+		__mp_end = .;
 		*(.text)
 		*(.fixup)
 		*(.gnu.warning)
diff --git a/board/gaisler/grsim/u-boot.lds b/board/gaisler/grsim/u-boot.lds
index 0fa6627..ac7dbec 100644
--- a/board/gaisler/grsim/u-boot.lds
+++ b/board/gaisler/grsim/u-boot.lds
@@ -71,6 +71,13 @@ SECTIONS
 		*(.prom.text)
 		. = ALIGN(16);
 		__prom_end = .;
+		/* Align MP section to the same as the MAX size of the MP section */
+		. = ALIGN(512);
+		__mp_start = .;
+		*(.mp.data)
+		*(.mp.text)
+		. = ALIGN(16);
+		__mp_end = .;
 		*(.text)
 		*(.fixup)
 		*(.gnu.warning)
diff --git a/cpu/leon3/Makefile b/cpu/leon3/Makefile
index f1bb808..4d36061 100644
--- a/cpu/leon3/Makefile
+++ b/cpu/leon3/Makefile
@@ -28,7 +28,7 @@ LIB	= $(obj)lib$(CPU).a
 START	= start.o
 SOBJS	= ambapp_low.o ambapp_low_c.o memcfg_low.o
 COBJS	= cpu_init.o serial.o cpu.o ambapp.o interrupts.o prom.o usb_uhci.o \
-	memcfg.o
+	memcfg.o cpu_mp.o
 
 SRCS	:= $(START:.o=.S) $(SOBJS:.o=.S) $(COBJS:.o=.c)
 OBJS	:= $(addprefix $(obj),$(SOBJS) $(COBJS))
diff --git a/cpu/leon3/cpu.c b/cpu/leon3/cpu.c
index 5cc9513..13d3dd7 100644
--- a/cpu/leon3/cpu.c
+++ b/cpu/leon3/cpu.c
@@ -83,8 +83,14 @@ int checkcpu(void)
 
 /* ------------------------------------------------------------------------- */
 
-void cpu_reset(void)
+int cpu_reset(int nr)
 {
+	if ( nr > 0 ) {
+		puts(" CPU[N] RESET NOT SUPPORTED BY ARCHITECTURE (N>0), "
+		     "SYSTEM RESET IS POSSIBLE.");
+		return -1;
+	}
+
 	/* Interrupts off */
 	disable_interrupts();
 
@@ -94,7 +100,7 @@ void cpu_reset(void)
 
 int do_reset(cmd_tbl_t * cmdtp, int flag, int argc, char *argv[])
 {
-	cpu_reset();
+	cpu_reset(0);
 
 	return 1;
 
diff --git a/cpu/leon3/cpu_mp.c b/cpu/leon3/cpu_mp.c
new file mode 100644
index 0000000..5cd5a6a
--- /dev/null
+++ b/cpu/leon3/cpu_mp.c
@@ -0,0 +1,80 @@
+/* Interface implementation for cmd_mp.c on multi processor LEON
+ * CPUs
+ *
+ * (C) Copyright 2010
+ * Daniel Hellstrom, Gaisler Research, daniel at gaisler.com
+ *
+ * See file CREDITS for list of people who contributed to this
+ * 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; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * 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., 59 Temple Place, Suite 330, Boston,
+ * MA 02111-1307 USA
+ */
+
+#include <common.h>
+#include <config.h>
+
+#ifdef CONFIG_MP
+
+#include <grlib/irqmp.h>
+
+extern int leon_cpu_cnt;
+extern ambapp_dev_irqmp *irqmp;
+
+int cpu_numcores(void)
+{
+	return leon_cpu_cnt;
+}
+
+int cpu_status(int nr)
+{
+	printf("LEON CPUs available: %d\n", leon_cpu_cnt);
+
+	return 0;
+}
+
+int cpu_release(int nr, int argc, char *argv[])
+{
+	unsigned int ep, stack, arg0, arg1;
+
+	/* Get entry point, stack and argument */
+	if ( argc < 2 ) {
+		printf(	" At least 5 arguments must be given.\n"
+			" Argument 4 is entry point\n"
+			" Argument 5 is stack\n"
+			" Argument 6-7 is kernel arg 0 and 1 (OPTIONAL)\n");
+		return -1;
+	}
+	ep = simple_strtoul(argv[0], NULL, 16);
+	stack = simple_strtoul(argv[1], NULL, 16);
+	arg0 = arg1 = 0;
+
+	if ( argc > 2 ) {
+		arg0 = simple_strtoul(argv[2], NULL, 16);
+	}
+	if ( argc > 3 ) {
+		arg1 = simple_strtoul(argv[3], NULL, 16);
+	}
+
+	/* Register CPU start up options into MP table */
+	boot_mp_cpu_setup(nr, ep, stack, (void *)arg0, (void *)arg1);
+
+	/* Release CPU by writing to IRQ controller MP register */
+	irqmp->mstatus = (1<<nr);
+
+	return 0;
+}
+
+#endif
diff --git a/cpu/leon3/prom.c b/cpu/leon3/prom.c
index 6cd2281..2a4fc2d 100644
--- a/cpu/leon3/prom.c
+++ b/cpu/leon3/prom.c
@@ -31,6 +31,7 @@
 #include <asm/processor.h>
 #include <asm/irq.h>
 #include <asm/leon.h>
+#include <asm/boot_mp.h>
 #include <ambapp.h>
 #include <grlib/apbuart.h>
 #include <grlib/irqmp.h>
@@ -931,21 +932,9 @@ void leon_prom_init(struct leon_prom_info *pspi)
 	/* Set the pointer to the Console UART in romvec */
 	pspi->reloc_funcs.leon3_apbuart = leon3_apbuart;
 
-	{
-		int j = 1;
-#ifdef CONFIG_SMP
-		ambapp_dev_irqmp *b;
-		b = (ambapp_dev_irqmp *) leon3_getapbbase(VENDOR_GAISLER,
-							  GAISLER_IRQMP);
-		if (b) {
-			j = 1 + ((LEON3_BYPASS_LOAD_PA(&(b->mpstatus))
-				  >> LEON3_IRQMPSTATUS_CPUNR) & 0xf);
-		}
-#endif
 #undef nodes
-		pspi->nodes[2 + j].level = -1;
-		pspi->nodes[2 + j].properties = __va(spi.root_properties + 3);
-	}
+	pspi->nodes[2 + leon_cpu_cnt].level = -1;
+	pspi->nodes[2 + leon_cpu_cnt].properties = __va(spi.root_properties + 3);
 
 	/* Set Ethernet MAC address from environment */
 	if ((addr_str = getenv("ethaddr")) != NULL) {
@@ -1024,7 +1013,7 @@ void prepare_bootargs(char *bootargs)
 	}
 }
 
-void srmmu_init_cpu(unsigned int entry)
+void srmmu_init(unsigned int entry)
 {
 	sparc_srmmu_setup *psrmmu_tables = (void *)
 	    ((((unsigned int)&srmmu_tables) & PROM_SIZE_MASK) +
@@ -1079,6 +1068,19 @@ void srmmu_init_cpu(unsigned int entry)
 	/* convert rom vec pointer to virtual address */
 	kernel_arg_promvec = (struct linux_romvec *)
 	    (((unsigned int)kernel_arg_promvec & 0x0fffffff) | 0xf0000000);
+}
+
+#ifdef CONFIG_MP
+/* This function must be located in the MP or PROM part of the application because
+ * it will be called from CPU1, CPU2 ...  after CPU0 has made it into the kernel 
+ * and started the other CPUs
+ */
+void MP_TEXT boot_mp_linux_cpu_preinit(int cpu)
+#else
+void boot_mp_linux_cpu_preinit(int cpu)
+#endif
+{
+	sparc_srmmu_setup *psrmmu_tables = (void *)CONFIG_SYS_PROM_OFFSET;
 
 	/* Set Context pointer to point to context table
 	 * 256 contexts supported.
diff --git a/cpu/leon3/start.S b/cpu/leon3/start.S
index d9faa86..1f30150 100644
--- a/cpu/leon3/start.S
+++ b/cpu/leon3/start.S
@@ -27,6 +27,7 @@
 #include <asm/psr.h>
 #include <asm/stack.h>
 #include <asm/leon.h>
+#include <asm/boot_mp.h>
 #include <timestamp.h>
 #include <version.h>
 #include <ambapp.h>
@@ -258,6 +259,20 @@ wininit:
 	set	WIM_INIT, %g3
 	mov	%g3, %wim
 
+#ifdef CONFIG_MP
+/* In a multi CPU system (and the slave CPUs have been started) the slaves
+ * have a special boot up sequence. It is expected that CPU0 has already run
+ * u-boot and it has loaded an OS which now have activated one or more slave 
+ * CPUs.
+ */
+multi_cpu_detect:
+	rd	%asr17, %g3
+	srl	%g3, 28, %g3
+	cmp	%g3, %g0
+	bne	slave_cpu_init
+	 nop
+#endif
+
 stackp:
 	set	CONFIG_SYS_INIT_SP_OFFSET, %fp
 	andn	%fp, 0x0f, %fp
@@ -386,6 +401,23 @@ prom_relocate_loop:
 	bne	prom_relocate_loop
 	inc	16,%g4
 
+#ifdef CONFIG_MP
+mp_relocate:
+	set	__mp_start, %g2
+	set	__mp_end, %g3
+	set	CONFIG_SYS_MP_OFFSET, %g4
+
+mp_relocate_loop:
+	ldd	[%g2],%l0
+	ldd	[%g2+8],%l2
+	std	%l0,[%g4]
+	std	%l2,[%g4+8]
+	inc	16,%g2
+	subcc	%g3,%g2,%g0
+	bne	mp_relocate_loop
+	inc	16,%g4
+#endif
+
 /* Trap table has been moved, lets tell CPU about
  * the new trap table address
  */
@@ -651,3 +683,41 @@ _reset_reloc:
 	set	start, %l0
 	call	%l0
 	nop
+
+#ifdef CONFIG_MP
+/* Slave CPUs reach here */
+slave_cpu_init:
+
+	/* Get Index into cpu slave struct */
+	sll	%g3, 4, %i0
+
+	set	(CONFIG_SYS_MP_OFFSET+0x4), %o1	/* cpu_table is mp_data+0x4 */
+	add	%i0, %o1, %i0
+
+	/* Setup Stack Pointer from config */
+	ld	[%i0 + BOOT_MP_CPU_STACK], %fp
+	andn	%fp, 0x0f, %fp
+	sub	%fp, 64, %sp
+
+	/* Call OS-dependent CPU init routine */
+	set	CONFIG_SYS_MP_OFFSET, %o1	/* cpu_table is mp_data+0x0 */
+	ld	[%o1], %o1
+	cmp	%o1, 0
+	beq	slave_cpu_boot_kernel
+	 nop
+	call	%o1
+	 clr	%o0
+
+	/* Call Kernel */
+slave_cpu_boot_kernel:
+	ld	[%i0 + BOOT_MP_CPU_ARG0], %o0	/* ARG0 */
+	ld	[%i0 + BOOT_MP_CPU_ARG1], %o1	/* ARG1 */
+	ld	[%i0 + BOOT_MP_CPU_EP], %o3	/* ENTRY POINT */
+	call	%o3
+	 clr	%o2
+
+dead_slave:
+	/* Kernel Failed or no support for MP */
+	ta 0x1
+	 nop
+#endif
diff --git a/include/asm-sparc/boot_mp.h b/include/asm-sparc/boot_mp.h
new file mode 100644
index 0000000..e033b9c
--- /dev/null
+++ b/include/asm-sparc/boot_mp.h
@@ -0,0 +1,70 @@
+/* Multiprocessor boot setup functions.
+ *
+ * (C) Copyright 2010
+ * Daniel Hellstrom, Gaisler Research, daniel at gaisler.com.
+ *
+ * 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.
+ *
+ * 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., 59 Temple Place, Suite 330, Boston,
+ * MA 02111-1307 USA
+ *
+ */
+
+#ifndef __ASM_SPARC_BOOT_MP_H__
+#define __ASM_SPARC_BOOT_MP_H__
+
+#define MP_TEXT __attribute__ ((__section__ (".mp.text")))
+#define MP_DATA __attribute__ ((__section__ (".mp.data")))
+
+#define BOOT_MP_CPU_EP 0x00
+#define BOOT_MP_CPU_STACK 0x04
+#define BOOT_MP_CPU_ARG0 0x08
+#define BOOT_MP_CPU_ARG1 0x0C
+
+#ifndef __ASSEMBLER__
+
+/* Allow for arch specific CPU initialization before RTEMS boot */
+extern void boot_mp_rtems_cpu_preinit(int cpu);
+
+/* Allow for arch specific CPU initialization before VxWorks boot */
+extern void boot_mp_vxworks_cpu_preinit(int cpu);
+
+/* Allow for arch specific CPU initialization before Linux boot */
+extern void boot_mp_linux_cpu_preinit(int cpu);
+
+struct boot_mp_cpu {
+	unsigned int entry_point;
+	unsigned int stack;
+	void *arg0;
+	void *arg1;
+};
+
+/* All CPU entry points and stacks */
+extern struct boot_mp_cpu boot_mp_cpu_table[];
+
+extern void boot_mp_os_setup(int os);
+
+extern void boot_mp_cpu_setup(
+	int cpu,
+	unsigned int entry_point,
+	unsigned int stack,
+	void *arg0,
+	void *arg1
+	);
+
+/* Init a CPU before entering the kernel */
+extern void boot_mp_cpu_preinit(int cpu);
+
+#endif
+
+#endif
diff --git a/include/configs/gr_cpci_ax2000.h b/include/configs/gr_cpci_ax2000.h
index b9d45dd..a6d669d 100644
--- a/include/configs/gr_cpci_ax2000.h
+++ b/include/configs/gr_cpci_ax2000.h
@@ -264,8 +264,16 @@
 #define CONFIG_SYS_GBL_DATA_SIZE	128	/* size in bytes reserved for initial data */
 #define CONFIG_SYS_GBL_DATA_OFFSET	(CONFIG_SYS_RAM_END - CONFIG_SYS_GBL_DATA_SIZE)
 
-#define CONFIG_SYS_PROM_SIZE		(8192-CONFIG_SYS_GBL_DATA_SIZE)
-#define CONFIG_SYS_PROM_OFFSET		(CONFIG_SYS_GBL_DATA_OFFSET-CONFIG_SYS_PROM_SIZE)
+#ifdef CONFIG_MP
+#define CONFIG_SYS_MP_SIZE		512
+#define CONFIG_SYS_MP_OFFSET		(CONFIG_SYS_GBL_DATA_OFFSET-CONFIG_SYS_MP_SIZE)
+#else
+#define CONFIG_SYS_MP_SIZE		0
+#define CONFIG_SYS_MP_OFFSET		CONFIG_SYS_GBL_DATA_OFFSET
+#endif
+
+#define CONFIG_SYS_PROM_SIZE		(8192-CONFIG_SYS_MP_SIZE-CONFIG_SYS_GBL_DATA_SIZE)
+#define CONFIG_SYS_PROM_OFFSET		(CONFIG_SYS_MP_OFFSET-CONFIG_SYS_PROM_SIZE)
 
 #define CONFIG_SYS_INIT_SP_OFFSET	(CONFIG_SYS_PROM_OFFSET-32)
 #define CONFIG_SYS_STACK_SIZE		(0x10000-32)
diff --git a/include/configs/gr_ep2s60.h b/include/configs/gr_ep2s60.h
index 6edb92c..10afca2 100644
--- a/include/configs/gr_ep2s60.h
+++ b/include/configs/gr_ep2s60.h
@@ -232,8 +232,16 @@
 #define CONFIG_SYS_GBL_DATA_SIZE	128	/* size in bytes reserved for initial data */
 #define CONFIG_SYS_GBL_DATA_OFFSET	(CONFIG_SYS_SDRAM_END - CONFIG_SYS_GBL_DATA_SIZE)
 
-#define CONFIG_SYS_PROM_SIZE		(8192-CONFIG_SYS_GBL_DATA_SIZE)
-#define CONFIG_SYS_PROM_OFFSET		(CONFIG_SYS_GBL_DATA_OFFSET-CONFIG_SYS_PROM_SIZE)
+#ifdef CONFIG_MP
+#define CONFIG_SYS_MP_SIZE		512
+#define CONFIG_SYS_MP_OFFSET		(CONFIG_SYS_GBL_DATA_OFFSET-CONFIG_SYS_MP_SIZE)
+#else
+#define CONFIG_SYS_MP_SIZE		0
+#define CONFIG_SYS_MP_OFFSET		CONFIG_SYS_GBL_DATA_OFFSET
+#endif
+
+#define CONFIG_SYS_PROM_SIZE		(8192-CONFIG_SYS_MP_SIZE-CONFIG_SYS_GBL_DATA_SIZE)
+#define CONFIG_SYS_PROM_OFFSET		(CONFIG_SYS_MP_OFFSET-CONFIG_SYS_PROM_SIZE)
 
 #define CONFIG_SYS_INIT_SP_OFFSET	(CONFIG_SYS_PROM_OFFSET-32)
 #define CONFIG_SYS_STACK_SIZE		(0x10000-32)
diff --git a/include/configs/gr_xc3s_1500.h b/include/configs/gr_xc3s_1500.h
index 1f9dd4a..b57640a 100644
--- a/include/configs/gr_xc3s_1500.h
+++ b/include/configs/gr_xc3s_1500.h
@@ -54,6 +54,8 @@
 #define CONFIG_DOS_PARTITION
 #define CONFIG_MAC_PARTITION
 #define CONFIG_ISO_PARTITION
+#define CONFIG_MP
+#define CONFIG_MP_MAX_CPUS 4
 
 /*
  * Supported commands
@@ -209,8 +211,16 @@
 #define CONFIG_SYS_GBL_DATA_SIZE	128	/* size in bytes reserved for initial data */
 #define CONFIG_SYS_GBL_DATA_OFFSET	(CONFIG_SYS_RAM_END - CONFIG_SYS_GBL_DATA_SIZE)
 
-#define CONFIG_SYS_PROM_SIZE		(8192-CONFIG_SYS_GBL_DATA_SIZE)
-#define CONFIG_SYS_PROM_OFFSET		(CONFIG_SYS_GBL_DATA_OFFSET-CONFIG_SYS_PROM_SIZE)
+#ifdef CONFIG_MP
+#define CONFIG_SYS_MP_SIZE		512
+#define CONFIG_SYS_MP_OFFSET		(CONFIG_SYS_GBL_DATA_OFFSET-CONFIG_SYS_MP_SIZE)
+#else
+#define CONFIG_SYS_MP_SIZE		0
+#define CONFIG_SYS_MP_OFFSET		CONFIG_SYS_GBL_DATA_OFFSET
+#endif
+
+#define CONFIG_SYS_PROM_SIZE		(8192-CONFIG_SYS_MP_SIZE-CONFIG_SYS_GBL_DATA_SIZE)
+#define CONFIG_SYS_PROM_OFFSET		(CONFIG_SYS_MP_OFFSET-CONFIG_SYS_PROM_SIZE)
 
 #define CONFIG_SYS_INIT_SP_OFFSET	(CONFIG_SYS_PROM_OFFSET-32)
 #define CONFIG_SYS_STACK_SIZE		(0x10000-32)
diff --git a/include/configs/grsim.h b/include/configs/grsim.h
index f815672..9e177e6 100644
--- a/include/configs/grsim.h
+++ b/include/configs/grsim.h
@@ -234,8 +234,16 @@
 #define CONFIG_SYS_GBL_DATA_SIZE	128	/* size in bytes reserved for initial data */
 #define CONFIG_SYS_GBL_DATA_OFFSET	(CONFIG_SYS_RAM_END - CONFIG_SYS_GBL_DATA_SIZE)
 
-#define CONFIG_SYS_PROM_SIZE		(8192-CONFIG_SYS_GBL_DATA_SIZE)
-#define CONFIG_SYS_PROM_OFFSET		(CONFIG_SYS_GBL_DATA_OFFSET-CONFIG_SYS_PROM_SIZE)
+#ifdef CONFIG_MP
+#define CONFIG_SYS_MP_SIZE		512
+#define CONFIG_SYS_MP_OFFSET		(CONFIG_SYS_GBL_DATA_OFFSET-CONFIG_SYS_MP_SIZE)
+#else
+#define CONFIG_SYS_MP_SIZE		0
+#define CONFIG_SYS_MP_OFFSET		CONFIG_SYS_GBL_DATA_OFFSET
+#endif
+
+#define CONFIG_SYS_PROM_SIZE		(8192-CONFIG_SYS_MP_SIZE-CONFIG_SYS_GBL_DATA_SIZE)
+#define CONFIG_SYS_PROM_OFFSET		(CONFIG_SYS_MP_OFFSET-CONFIG_SYS_PROM_SIZE)
 
 #define CONFIG_SYS_INIT_SP_OFFSET	(CONFIG_SYS_PROM_OFFSET-32)
 #define CONFIG_SYS_STACK_SIZE		(0x10000-32)
diff --git a/lib_sparc/Makefile b/lib_sparc/Makefile
index 040ca10..3860fdc 100644
--- a/lib_sparc/Makefile
+++ b/lib_sparc/Makefile
@@ -27,7 +27,7 @@ LIB	= $(obj)lib$(ARCH).a
 
 SOBJS	=
 
-COBJS	= board.o cache.o interrupts.o time.o bootm.o
+COBJS	= board.o cache.o interrupts.o time.o bootm.o boot_mp.o
 
 SRCS	:= $(SOBJS:.o=.S) $(COBJS:.o=.c)
 OBJS	:= $(addprefix $(obj),$(SOBJS) $(COBJS))
diff --git a/lib_sparc/boot_mp.c b/lib_sparc/boot_mp.c
new file mode 100644
index 0000000..3154fd1
--- /dev/null
+++ b/lib_sparc/boot_mp.c
@@ -0,0 +1,177 @@
+/* SPARC Multi-Processor initialization
+ *
+ * (C) Copyright 2010
+ * Daniel Hellstrom, Aeroflex Gaisler, daniel at gaisler.com.
+ *
+ * See file CREDITS for list of people who contributed to this
+ * 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; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * 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., 59 Temple Place, Suite 330, Boston,
+ * MA 02111-1307 USA
+ */
+
+#include <common.h>
+#include <asm/boot_mp.h>
+#include <config.h>
+
+#ifdef CONFIG_MP
+
+#ifndef CONFIG_MP_MAX_CPUS
+ #error CONFIG_MP_MAX_CPUS must be defined
+#endif
+
+typedef int (*cpu_preinit_func) (int cpu);
+
+/* Structure of all MP DATA section */
+struct boot_mp_data {
+	/* Function called for each CPU before entering kernel */
+	cpu_preinit_func func;
+
+	/* All CPU entry points and so on */
+	struct boot_mp_cpu cpu_table[CONFIG_MP_MAX_CPUS];
+
+	/* OS to boot */
+	int os;
+};
+
+struct boot_mp_data MP_DATA mp_data;
+
+/* Allow for specific RTEMS CPU initialization before RTEMS AMP boot */
+void MP_TEXT __boot_mp_rtems_cpu_preinit(int cpu)
+{
+
+}
+void boot_mp_rtems_cpu_preinit(int cpu)
+	__attribute__((weak, alias("__boot_mp_rtems_cpu_preinit")));
+
+/* Allow for specific VxWorks CPU initialization before slave CPUs boot */
+void MP_TEXT __boot_mp_vxworks_cpu_preinit(int cpu)
+{
+
+}
+void boot_mp_vxworks_cpu_preinit(int cpu)
+	__attribute__((weak, alias("__boot_mp_vxworks_cpu_preinit")));
+
+/* Allow for specific Linux CPU initialization before slave CPUs boot */
+void MP_TEXT __boot_mp_linux_cpu_preinit(int cpu)
+{
+
+}
+void boot_mp_linux_cpu_preinit(int cpu)
+	__attribute__((weak, alias("__boot_mp_linux_cpu_preinit")));
+
+static cpu_preinit_func os_cpu_preinit[] =
+{
+	[IH_OS_LINUX] = boot_mp_linux_cpu_preinit,
+	[IH_OS_RTEMS] = boot_mp_rtems_cpu_preinit,
+	[IH_OS_VXWORKS] = boot_mp_vxworks_cpu_preinit,
+};
+
+void boot_mp_os_setup(int os)
+{
+	struct boot_mp_data *mpd = (void *)CONFIG_SYS_MP_OFFSET;
+	unsigned int func;
+
+	mpd->os = os;
+
+	func = (unsigned int)os_cpu_preinit[os];
+	if ( func == 0 ) {
+		/* We assume that the OS booting does not support MP and will 
+		 * therefore not start the other CPUs.
+		 */
+		mpd->func = 0;
+	} else {
+		mpd->func = CONFIG_SYS_MP_OFFSET + (func & (CONFIG_SYS_MP_SIZE-1));
+	}
+
+	debug("boot_mp_os_setup:  0x%x, 0x%x, 0x%x, 0x%x, 0x%x, 0x%x\n",
+		os,
+		os_cpu_preinit,
+		&os_cpu_preinit[os],
+		(unsigned int)os_cpu_preinit[os],
+		CONFIG_SYS_MP_SIZE,
+		((unsigned int)os_cpu_preinit[os] & (CONFIG_SYS_MP_SIZE-1))
+		);
+}
+
+void boot_mp_cpu_setup(
+	int cpu,
+	unsigned int entry_point,
+	unsigned int stack,
+	void *arg0,
+	void *arg1
+	)
+{
+	struct boot_mp_data *mpd = (void *)CONFIG_SYS_MP_OFFSET;
+
+	if ( cpu >= CONFIG_MP_MAX_CPUS )
+		return;
+
+	mpd->cpu_table[cpu].entry_point = entry_point;
+	mpd->cpu_table[cpu].stack = stack;
+	mpd->cpu_table[cpu].arg0 = arg0;
+	mpd->cpu_table[cpu].arg1 = arg1;
+
+	debug("boot_mp_cpu_setup(%d): ep=0x%x stack=0x%x, arg=[0x%x,0x%x]\n",
+		cpu, entry_point, stack, arg0, arg1);
+}
+
+/* In a RTEMS AMP system all CPUs have different entry points and stacks,
+ * the addresses are taken from the environment variables:
+ * cpu0_entry and cpu0_stack
+ */
+void boot_mp_rtems_setup(void)
+{
+	char *str;
+	char env_str[16];
+	int cpu;
+	unsigned int entry, stack;
+
+	for(cpu=0; cpu<CONFIG_MP_MAX_CPUS; cpu++) {
+
+		entry = 0;
+		stack = 0;
+
+		strcpy(env_str, "cpuX_entry");
+		env_str[3] = '0' + cpu;
+		if ( (str = getenv(env_str)) != NULL ) {
+			entry = simple_strtoul(str, NULL, 16);
+		}
+
+		strcpy(env_str, "cpuX_stack");
+		env_str[3] = '0' + cpu;
+		if ( (str = getenv(env_str)) != NULL ) {
+			stack = simple_strtoul(str, NULL, 16);
+		}
+
+		boot_mp_cpu_setup(
+			cpu,
+			(unsigned int)entry,
+			(unsigned int)stack,
+			NULL,
+			NULL);
+	}
+}
+
+/* Prepare boot, called from bootm */
+void arch_preboot_os(int os)
+{
+	boot_mp_os_setup(os);
+
+	if ( os == IH_OS_RTEMS )
+		boot_mp_rtems_setup();
+}
+
+#endif
diff --git a/lib_sparc/bootm.c b/lib_sparc/bootm.c
index f517325..428ad97 100644
--- a/lib_sparc/bootm.c
+++ b/lib_sparc/bootm.c
@@ -28,11 +28,12 @@
 #include <asm/prom.h>
 #include <asm/cache.h>
 #include <image.h>
+#include <asm-sparc/boot_mp.h>
 
 #define PRINT_KERNEL_HEADER
 
 extern image_header_t header;
-extern void srmmu_init_cpu(unsigned int entry);
+extern void srmmu_init(unsigned int entry);
 extern void prepare_bootargs(char *bootargs);
 
 /* sparc kernel argument (the ROM vector) */
@@ -43,7 +44,8 @@ struct linux_romvec *kernel_arg_promvec;
 #define RAMDISK_IMAGE_START_MASK	0x07FF
 #define RAMDISK_PROMPT_FLAG		0x8000
 #define RAMDISK_LOAD_FLAG		0x4000
-struct __attribute__ ((packed)) {
+/* Linux Single CPU Header */
+struct linux_up_hdr {
 	char traptable[PAGE_SIZE];
 	char swapper_pg_dir[PAGE_SIZE];
 	char pg0[PAGE_SIZE];
@@ -73,7 +75,13 @@ struct __attribute__ ((packed)) {
 			unsigned int end;
 		} ver_0203;
 	} hdr_input;
-} *linux_hdr;
+} __attribute__ ((packed)) ;
+
+/* Linux SMP Header */
+struct linux_smp_hdr {
+	char traptable[3][PAGE_SIZE];
+	struct linux_up_hdr single_hdr;
+} __attribute__ ((packed));
 
 /* temporary initrd image holder */
 image_header_t ihdr;
@@ -98,23 +106,34 @@ int do_bootm_linux(int flag, int argc, char *argv[], bootm_headers_t * images)
 	void (*kernel) (struct linux_romvec *, void *);
 	struct lmb *lmb = &images->lmb;
 	int ret;
+	int i;
+	struct linux_up_hdr *linux_hdr;
+	struct linux_smp_hdr *linux_smp_hdr;
 
 	if ((flag != 0) && (flag != BOOTM_STATE_OS_GO))
 		return 1;
 
-	/* Get virtual address of kernel start */
-	linux_hdr = (void *)images->os.load;
-
-	/* */
+	/* Kernel Entry Point */
 	kernel = (void (*)(struct linux_romvec *, void *))images->ep;
 
+	/* Get virtual address of kernel start */
+	linux_hdr = (struct linux_up_hdr *)images->os.load;
+
 	/* check for a SPARC kernel */
 	if ((linux_hdr->hdr[0] != 'H') ||
 	    (linux_hdr->hdr[1] != 'd') ||
 	    (linux_hdr->hdr[2] != 'r') || (linux_hdr->hdr[3] != 'S')) {
-		puts("Error reading header of SPARC Linux kernel, aborting\n");
-		goto error;
+		/* Not a valid Linux Header, check if Linux SMP header */
+		linux_smp_hdr = (struct linux_smp_hdr *)images->os.load;
+		linux_hdr = (struct linux_up_hdr *)&linux_smp_hdr->single_hdr;
+		if ((linux_hdr->hdr[0] != 'H') ||
+		    (linux_hdr->hdr[1] != 'd') ||
+		    (linux_hdr->hdr[2] != 'r') || (linux_hdr->hdr[3] != 'S')) {
+			puts("Error reading header of SPARC Linux kernel, aborting\n");
+			goto error;
+   		}
 	}
+
 #ifdef PRINT_KERNEL_HEADER
 	printf("## Found SPARC Linux kernel %d.%d.%d ...\n",
 	       linux_hdr->linuxver_major,
@@ -164,8 +183,24 @@ int do_bootm_linux(int flag, int argc, char *argv[], bootm_headers_t * images)
 	bootargs = getenv("bootargs");
 	prepare_bootargs(bootargs);
 
-	/* turn on mmu & setup context table & page table for process 0 (kernel) */
-	srmmu_init_cpu((unsigned int)kernel);
+	/* Init MMU table of SRMMU and more */
+	srmmu_init((unsigned int)kernel);
+
+#ifdef CONFIG_MP
+	for(i=0; i<CONFIG_MP_MAX_CPUS; i++) {
+		boot_mp_cpu_setup(
+			i,
+			(unsigned int)kernel,
+			(unsigned int)CONFIG_SYS_INIT_SP_OFFSET,
+			kernel_arg_promvec,
+			NULL);
+	}
+#endif
+
+	/* turn on mmu & setup context table & page table for process 0 (kernel).
+	 * This function is later called by each CPU in a multiprocessor system.
+	 */	
+	boot_mp_linux_cpu_preinit(0);
 
 	/* Enter SPARC Linux kernel
 	 * From now on the only code in u-boot that will be
-- 
1.5.4



More information about the U-Boot mailing list