Support code to move cpus around, both a spin loop and c code to
move the cpus before uncompressing and copying the kernel to 0.

The low level code is designed to be included in a crt0 or other
assembly file because it may need to be at a fixed location or there
may be other entry point requirements.

Note: this code works with kernel head_64.S.   head_6xx.S needs the
0x60 entry point (it currently mentions something at address 0xC0; but
the similar code is at 0xC4); the other heads don't appear to support
SMP.

Signed-off-by: Milton Miller <[EMAIL PROTECTED]>
--- 
vs 12171
get barrier from io.h instead of adding to reg.h
rediff ops.h, Makefile


Index: kernel/arch/powerpc/boot/marshal_low.S
===================================================================
--- /dev/null   1970-01-01 00:00:00.000000000 +0000
+++ kernel/arch/powerpc/boot/marshal_low.S      2007-09-17 22:13:14.000000000 
-0500
@@ -0,0 +1,103 @@
+/*
+ * 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, 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ *
+ * Copyright 2007 IBM Corporation.
+ *
+ * Authors: Milton Miller <[EMAIL PROTECTED]>
+ *
+ */
+
+#include "ppc_asm.h"
+
+       .text
+       /*
+        * This code is designed to be a kexec entry point block.
+        * That is, it has both code for the master cpu that begins
+        * at offset 0 as linked into the image, and a sequence of
+        * 0x100 bytes that, when copied to address 0, forms the
+        * wait loop for slave cpus.  Each slave should have its
+        * unique hardware cpu identifier in r3 before entering
+        * this code.
+        */
+       .globl  master
+master: b      _zimage_start_plat
+
+       .global slave_wait
+slave_wait:
+       /* r3 cpu id, r4 slaves_wait, r5 cpu bit, r6 cpu mask word offset */
+
+       /* set our bit in the slaves mask  */
+98:    lwarx   r7,r4,r6
+       or      r8,r7,r5
+       stwcx.  r8,r4,r6
+       bne     98b
+
+       and.    r8,r7,r5
+       bnel-   err_slave
+
+99:    lwz     r7,gohere-slave_wait(r4)
+       cmpwi   0,r7,0
+       beq     99b
+       mtctr   r7
+       mr      r4,r7
+       bctr
+
+
+       .global gohere
+gohere:        .long   0                       /* when set the slave moves */
+
+
+err_slave:
+       stw     r5,slave_error-slave_wait(4)    /* no locking */
+       blr
+
+       .globl  slave_error             /* set when slave detects error */
+slave_error:
+       .long   0
+
+       /*
+        * The slaves may be in 32 or 64 bit mode, we don't care
+        * r3 is the slave cpu number, matching the device tree.
+        */
+       .org    master+0x60
+       .globl  slave
+slave: bl      1f
+1:     mflr    r4
+       addi    r4,r4,slave_wait-1b     /* code assumes r4=slave_wait */
+       li      r5,1
+       rlwnm   r5,r5,r3,0,31           /* bit within word */
+       rlwinm  r6,r3,32-5+2,4,29       /* word in array */
+       addi    r6,r6,slaves-slave_wait /* relative to r4, slave_wait */
+       b       slave_wait
+
+       .org    master+0x80     /* put locked bitmask data in another line */
+       .global slaves
+slaves:
+
+       .globl slaves_end;
+slaves_end = 0f
+
+#if 0
+       /* today, the 32 bit kernel starts slaves at 0xc0
+        * but this limits us to cpu to 512 vs 1024
+        */
+       .org    master+0xc0
+0:     b       slave
+#endif
+
+
+       .org    master+0x100            /* we must fit in 0x100 bytes */
+0:
+
Index: kernel/arch/powerpc/boot/marshal.c
===================================================================
--- /dev/null   1970-01-01 00:00:00.000000000 +0000
+++ kernel/arch/powerpc/boot/marshal.c  2007-09-17 22:13:14.000000000 -0500
@@ -0,0 +1,276 @@
+/*
+ * 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, 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ *
+ * Copyright (C) 2007 IBM Corporation.
+ *
+ * Authors: Milton Miller <[EMAIL PROTECTED]>
+ */
+
+#include "ops.h"
+#include "stdio.h"
+#include "reg.h"
+#include "io.h"
+
+extern unsigned int gohere[], master[], slave_wait[], slaves[], slaves_end[];
+extern unsigned int slave_error[1];
+
+static unsigned int slaves_run_here[SMP_SLAVE_SIZE / sizeof(unsigned int)];
+static unsigned int *slaves_were_here = master;
+static unsigned int *slaves_goto_here = master;
+
+/**
+ * check_slave_errors - check if the slaves have set the error flag.
+ * @slaves_here ... the location that the slaves should be spinning.
+ */
+static void check_slave_errors(unsigned int *slaves_here)
+{
+       unsigned int *error = slave_error - master + slaves_here;
+
+       if (*error) {
+               printf("WARNING: error detected by one or more slave cpus!!\n\r"
+               "WARNING: This probably means you have duplicate cpu ids\n\r");
+               /* exit() */
+       }
+}
+
+/**
+ * wait_slaves_moved - wait for the slaves to catch up
+ *
+ * Wait until all slaves that checked in the previous location have
+ * checked into the current location.  Seperate so we can do other
+ * work while we wait for them to catch up.
+ */
+void wait_slaves_moved(void)
+{
+       int offset = slaves - master;
+       int len = sizeof(slaves_end[0]) * (slaves_end - slaves);
+       int printed = 0;
+       unsigned int *to = slaves_goto_here;
+       unsigned int *from = slaves_were_here;
+
+       from += offset;
+       to += offset;
+
+       if (from == to)
+               return;
+
+       while (memcmp(from, to, len)) {
+               if (!printed) {
+                       printf("waiting for slave cpus to move...");
+                       printed = 1;
+                       HMT_LOW;
+                       barrier();
+               }
+               /* check from is superset of to */
+       }
+       if (printed) {
+               HMT_MEDIUM;
+               printf("done.\n\r");
+       }
+
+       slaves_were_here = slaves_goto_here;
+}
+
+/**
+ * move_slaves_here - move slaves to a specified address.
+ * @addr: location of %SMP_SLAVE_SIZE buffer to place code and spin
+ *
+ * Tell slaves to go from their current location to a buffer @addr
+ * of %SMP_SLAVE_SIZE bytes somewhere in memory.
+ */
+void move_slaves_here(void *addr)
+{
+       unsigned int *move_slaves_here = addr;
+       unsigned int *tell_them = gohere - master + slaves_goto_here;
+       unsigned int *goto_here = slave_wait - master + move_slaves_here;
+       unsigned int *wait_here = gohere - master + move_slaves_here;
+
+       if (move_slaves_here == slaves_goto_here)
+               return;                         /* already there */
+
+       wait_slaves_moved();                    /* one move at a time */
+
+       printf("moving slave cpus from %p to %p\n\r", slaves_goto_here,
+               move_slaves_here);
+
+       memcpy(move_slaves_here, master, SMP_SLAVE_SIZE);
+       memset(move_slaves_here + (slaves - master), 0,
+               (slaves_end - slaves) * sizeof(slaves_end[0]));
+       *wait_here = 0;
+
+       flush_cache(move_slaves_here, SMP_SLAVE_SIZE);
+
+       check_slave_errors(slaves_were_here);
+
+       *tell_them = (unsigned int)goto_here;
+       slaves_goto_here = move_slaves_here;
+}
+
+/**
+ * move_slaves_up - move slaves from somewhere low to our bss.
+ * Call before decompressing the kernel to address 0.
+ */
+void move_slaves_up(void)
+{
+       move_slaves_here(slaves_run_here);
+}
+
+/**
+ * slaves_are_low - Assert that the slaves are spinning at 0, and move them
+ * Assert that the slaves are running in a copy of the marshall code
+ * that was copied to address 0.  Ask them to go up to our bss, as we
+ * know we have to move them away from 0.
+ */
+void slaves_are_low(void)
+{
+       slaves_goto_here = slaves_were_here = (void *)0;
+       move_slaves_up();
+}
+
+/**
+ * wait_slave_checkout - wait for slaves to execute checkout store.
+ * @checkout - slave checkout flag array
+ *
+ * Wait for every slave who checked in at slaves_were_here to
+ * perform the stb to @checkout before the branch to self spin loop.
+ */
+static void wait_slave_checkout(char *checkout)
+{
+       unsigned int *end = slaves_end - master + slaves_were_here;
+       unsigned int *from = slaves - master + slaves_were_here;;
+       unsigned int bit;
+       int i, ncpus = 0;
+       char *waiting = "waiting on slaves to go to kernel...";
+
+       for (i=0; from < end; from++)
+               for (bit = 1; bit; i++, bit <<= 1)
+                       if (*from & bit) {
+                               ncpus++;
+                               while (!checkout[i]) {
+                                       if (waiting) {
+                                               printf(waiting);
+                                               waiting = NULL;
+                                       }
+                                       HMT_LOW;
+                                       barrier();
+                               }
+                       }
+
+       if (waiting == NULL)
+               printf("done.\n\r");
+
+       printf("moved %d slaves to the kernel.\n\r", ncpus);
+}
+
+/* The slave checkin code ... used by checkout_slaves_to_kernel below */
+extern unsigned int slave_checkout_begin[], slave_checkout_spin[];
+asm ("\
+       .globl  slave_checkout_begin    ;\
+       .globl  slave_checkout_spin     ;\
+slave_checkout_begin:                  ;\
+       lwz     7,0(0)                  ;\
+       li      8,1                     ;\
+       stbx    8,7,3                   ;\
+slave_checkout_spin:                   ;\
+       b       $                       ;\
+");
+
+
+/**
+ * checkout_slaves_to_kernel - send SMP slaves to the kernel
+ * @tell_them - the expected marshalling buffer for the slaves
+ *
+ * Actively move slaves spinning on @tell_them to 0x60.  Since we
+ * don't know what code is there, replace it with our one code that
+ * ends with a byte store and branch to self, with the branch at 0x60.
+ * After the stores complete, we can restore the rest of the line,
+ * flush, then restore the remaining line.
+ */
+static void checkout_slaves_to_kernel(unsigned int *tell_them)
+{
+       int to, spin;
+       unsigned int *from, *low, save[SMP_SLAVE_SIZE/sizeof(unsigned int)];
+       char *checkout;
+
+       checkout = malloc(1024);
+       if (checkout == NULL)
+               fatal("can't malloc slave checkout buffer");
+       memset(checkout, 0, 1024);
+
+       low = (unsigned int *)0;
+       memcpy(save, low, SMP_SLAVE_SIZE);
+
+       to = spin = 0x60 / sizeof(int);
+
+       to++;
+       from = slave_checkout_spin;
+       while (from >= slave_checkout_begin)
+               low[--to] = *from--;
+
+       low[0] = (unsigned int)checkout;
+       flush_cache(low, SMP_SLAVE_SIZE);
+
+       *tell_them = (unsigned int)(low + to);
+
+       wait_slave_checkout(checkout);
+
+       /* at this point, all have completed the store at %0x5c and are at
+        * the branch to self at %0x60.   Restore the rest of the vector,
+        * flush cache, then do the final store replacing the spin and
+        * flush again.
+        */
+       low[0] = save[0];
+       for (;to < spin; to++)
+               low[to] = save[to];
+       flush_cache(low, SMP_SLAVE_SIZE);
+       low[to] = save[to];
+       flush_cache(low, SMP_SLAVE_SIZE);
+
+}
+
+/**
+ * send_slaves_to_kernel - send SMP slaves to the kernel
+ * @vmlinux_addr: address vmlinux was decompressed to (where to get slave loop)
+ *
+ * Send slaves currently running in the marshalling system to the slave code
+ * in the next kernel which has been uncompressed at address @vmlinux_addr.
+ * Copies the first %SMP_SLAVE_SIZE bytes of the image to address %0 and
+ * then tells the slaves to go to %0x60.
+ */
+void send_slaves_to_kernel(void *vmlinux_addr)
+{
+       unsigned int *tell_them = gohere - master + slaves_goto_here;
+
+       if ((unsigned long)slaves_goto_here < SMP_SLAVE_SIZE) {
+               if ((unsigned long)vmlinux_addr < SMP_SLAVE_SIZE)
+                       fatal("ERROR: slaves were not marshaled before "
+                                       "decompressing the kernel to 0!\n");
+               move_slaves_up();
+               send_slaves_to_kernel(vmlinux_addr);
+               return;
+       }
+
+       wait_slaves_moved();
+
+       if (vmlinux_addr) {
+               memcpy((void *)0, vmlinux_addr, SMP_SLAVE_SIZE);
+               flush_cache((void *)0, SMP_SLAVE_SIZE);
+       } else {
+               printf("kernel was decompressed to 0\n\r");
+       }
+       check_slave_errors(slaves_goto_here);
+
+       checkout_slaves_to_kernel(tell_them);
+}
Index: kernel/arch/powerpc/boot/Makefile
===================================================================
--- kernel.orig/arch/powerpc/boot/Makefile      2007-09-17 22:12:41.000000000 
-0500
+++ kernel/arch/powerpc/boot/Makefile   2007-09-17 22:13:14.000000000 -0500
@@ -42,6 +42,7 @@ $(addprefix $(obj)/,$(zlib) gunzip_util.
        $(addprefix $(obj)/,$(zliblinuxheader)) $(addprefix 
$(obj)/,$(zlibheader))
 
 src-wlib := string.S crt0.S stdio.c main.c flatdevtree.c flatdevtree_misc.c \
+               marshal.c \
                ns16550.c serial.c simple_alloc.c div64.S util.S \
                gunzip_util.c elf_util.c $(zlib) devtree.c oflib.c ofconsole.c \
                4xx.c ebony.c mv64x60.c mpsc.c mv64x60_i2c.c cuboot.c bamboo.c \
Index: kernel/arch/powerpc/boot/reg.h
===================================================================
--- kernel.orig/arch/powerpc/boot/reg.h 2007-09-17 22:12:41.000000000 -0500
+++ kernel/arch/powerpc/boot/reg.h      2007-09-17 22:13:14.000000000 -0500
@@ -19,4 +19,8 @@ static inline u32 mfpvr(void)
 register void *__stack_pointer asm("r1");
 #define get_sp()       (__stack_pointer)
 
+#define HMT_MEDIUM     asm volatile("or 2,2,2")
+#define HMT_LOW                asm volatile("or 1,1,1")
+
+
 #endif /* _PPC_BOOT_REG_H */
Index: kernel/arch/powerpc/boot/ops.h
===================================================================
--- kernel.orig/arch/powerpc/boot/ops.h 2007-09-17 22:12:51.000000000 -0500
+++ kernel/arch/powerpc/boot/ops.h      2007-09-17 22:13:14.000000000 -0500
@@ -18,6 +18,7 @@
 #define        COMMAND_LINE_SIZE       512
 #define        MAX_PATH_LEN            256
 #define        MAX_PROP_LEN            256 /* What should this be? */
+#define        SMP_SLAVE_SIZE          256 /* Size of SMP slave block, 
kexec/kernel */
 
 typedef void (*kernel_entry_t)(unsigned long r3, unsigned long r4, void *r5);
 
@@ -86,7 +87,13 @@ int mpsc_console_init(void *devp, struct
 int cpm_console_init(void *devp, struct serial_console_data *scdp);
 void *simple_alloc_init(char *base, unsigned long heap_size,
                        unsigned long granularity, unsigned long max_allocs);
-extern void flush_cache(void *, unsigned long);
+void flush_cache(void *, unsigned long);
+void move_slaves_up(void);
+void move_slaves_here(void *where);
+void send_slaves_to_kernel(void *vmlinux_addr);
+void slaves_are_low(void);
+void wait_slaves_moved(void);
+
 int dt_xlate_reg(void *node, int res, unsigned long *addr, unsigned long 
*size);
 int dt_xlate_addr(void *node, u32 *buf, int buflen, unsigned long 
*xlated_addr);
 int dt_is_compatible(void *node, const char *compat);
_______________________________________________
Linuxppc-dev mailing list
Linuxppc-dev@ozlabs.org
https://ozlabs.org/mailman/listinfo/linuxppc-dev

Reply via email to