summaryrefslogtreecommitdiff
path: root/sys
diff options
context:
space:
mode:
authorMiod Vallat <miod@cvs.openbsd.org>2010-04-24 18:46:56 +0000
committerMiod Vallat <miod@cvs.openbsd.org>2010-04-24 18:46:56 +0000
commit2cc5b2d5ea4e4207a448f7192d0a59cef474df9a (patch)
tree63f27fae1e6ea1ec68f30b73c2ea515abfdbd367 /sys
parent743a79e922aded7a34218322df7491c49049127d (diff)
SMP support for models 4600 and 530, adapted from the MVME188 code. Models
4000 and 4300 will need more work, because they don't have as many distinct software interrupt sources as required by this implementation, so a different IPI scheme will be necessary. Tested on dual-processor 4625 (AV530 family) and single processor 4300 (AV400 family).
Diffstat (limited to 'sys')
-rw-r--r--sys/arch/aviion/aviion/av400_machdep.c174
-rw-r--r--sys/arch/aviion/aviion/av530_machdep.c159
-rw-r--r--sys/arch/aviion/aviion/cio_clock.c11
-rw-r--r--sys/arch/aviion/aviion/locore.S67
-rw-r--r--sys/arch/aviion/aviion/machdep.c126
-rw-r--r--sys/arch/aviion/aviion/prom.c25
-rw-r--r--sys/arch/aviion/aviion/rtc_clock.c10
-rw-r--r--sys/arch/aviion/conf/GENERIC.MP15
-rw-r--r--sys/arch/aviion/dev/dart.c4
-rw-r--r--sys/arch/aviion/dev/mainbus.c11
-rw-r--r--sys/arch/aviion/include/av530.h18
-rw-r--r--sys/arch/aviion/include/board.h8
-rw-r--r--sys/arch/aviion/include/prom.h11
13 files changed, 585 insertions, 54 deletions
diff --git a/sys/arch/aviion/aviion/av400_machdep.c b/sys/arch/aviion/aviion/av400_machdep.c
index 761c8861e0f..1bf0012b93b 100644
--- a/sys/arch/aviion/aviion/av400_machdep.c
+++ b/sys/arch/aviion/aviion/av400_machdep.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: av400_machdep.c,v 1.17 2010/04/24 18:44:25 miod Exp $ */
+/* $OpenBSD: av400_machdep.c,v 1.18 2010/04/24 18:46:51 miod Exp $ */
/*
* Copyright (c) 2006, 2007, Miodrag Vallat.
*
@@ -159,7 +159,13 @@
#include <aviion/dev/sysconvar.h>
#include <aviion/dev/vmevar.h>
+#ifdef MULTIPROCESSOR0
+#include <machine/db_machdep.h>
+#endif
+
u_int av400_safe_level(u_int, u_int);
+void av400_clock_ipi_handler(struct trapframe *);
+void av400_ipi_handler(struct trapframe *);
const pmap_table_entry
av400_ptable[] = {
@@ -193,6 +199,10 @@ const struct board board_av400 = {
av400_getipl,
av400_setipl,
av400_raiseipl,
+#ifdef MULTIPROCESSOR
+ NULL, /* av400_send_ipi, */
+ m88100_smp_setup,
+#endif
av400_intsrc,
av400_get_vme_ranges,
@@ -216,11 +226,11 @@ u_int32_t av400_int_mask_reg[] = { 0, 0, 0, 0 };
u_int av400_curspl[] = { IPL_HIGH, IPL_HIGH, IPL_HIGH, IPL_HIGH };
-#ifdef MULTIPROCESSOR
+#ifdef MULTIPROCESSOR0
/*
* Interrupts allowed on secondary processors.
*/
-#define SLAVE_MASK 0 /* AV400_IRQ_SWI0 | AV400_IRQ_SWI1 */
+#define SLAVE_MASK 0
#endif
/*
@@ -288,6 +298,11 @@ av400_safe_level(u_int mask, u_int curlevel)
{
int i;
+#ifdef MULTIPROCESSOR0
+ if (mask & AV400_CLOCK_IPI_MASK)
+ curlevel = max(IPL_CLOCK, curlevel);
+ mask &= ~(AV400_IPI_MASK | AV400_CLOCK_IPI_MASK);
+#endif
for (i = curlevel; i < NIPLS; i++)
if ((int_mask_val[i] & mask) == 0)
return i;
@@ -312,9 +327,12 @@ av400_setipl(u_int level)
curspl = av400_curspl[cpu];
mask = int_mask_val[level];
-#ifdef MULTIPROCESSOR
+#ifdef MULTIPROCESSOR0
if (cpu != master_cpu)
mask &= SLAVE_MASK;
+ mask |= AV400_SWI_IPI_MASK(cpu);
+ if (level < IPL_CLOCK)
+ mask |= AV400_SWI_CLOCK_IPI_MASK(cpu);
#endif
av400_curspl[cpu] = level;
@@ -339,9 +357,12 @@ av400_raiseipl(u_int level)
curspl = av400_curspl[cpu];
if (curspl < level) {
mask = int_mask_val[level];
-#ifdef MULTIPROCESSOR
+#ifdef MULTIPROCESSOR0
if (cpu != master_cpu)
mask &= SLAVE_MASK;
+ mask |= AV400_SWI_IPI_MASK(cpu);
+ if (level < IPL_CLOCK)
+ mask |= AV400_SWI_CLOCK_IPI_MASK(cpu);
#endif
av400_curspl[cpu] = level;
@@ -356,6 +377,89 @@ av400_raiseipl(u_int level)
return curspl;
}
+#ifdef MULTIPROCESSOR0
+
+void
+av400_send_ipi(int ipi, cpuid_t cpu)
+{
+ struct cpu_info *ci = &m88k_cpus[cpu];
+ uint32_t bits = 0;
+
+ if (ci->ci_ipi & ipi)
+ return;
+
+ atomic_setbits_int(&ci->ci_ipi, ipi);
+ if (ipi & ~(CI_IPI_HARDCLOCK | CI_IPI_STATCLOCK))
+ bits |= AV400_SWI_IPI_BIT(cpu);
+ if (ipi & (CI_IPI_HARDCLOCK | CI_IPI_STATCLOCK))
+ bits |= AV400_SWI_CLOCK_IPI_BIT(cpu);
+ *(volatile u_int32_t *)AV400_SETSWI = bits;
+}
+
+/*
+ * Process inter-processor interrupts.
+ */
+
+/*
+ * Unmaskable IPIs - those are processed with interrupts disabled,
+ * and no lock held.
+ */
+void
+av400_ipi_handler(struct trapframe *eframe)
+{
+ struct cpu_info *ci = curcpu();
+ int ipi = ci->ci_ipi & (CI_IPI_DDB | CI_IPI_NOTIFY);
+
+ *(volatile u_int32_t *)AV400_CLRSWI = AV400_SWI_IPI_BIT(ci->ci_cpuid);
+ atomic_clearbits_int(&ci->ci_ipi, ipi);
+
+ if (ipi & CI_IPI_DDB) {
+#ifdef DDB
+ /*
+ * Another processor has entered DDB. Spin on the ddb lock
+ * until it is done.
+ */
+ extern struct __mp_lock ddb_mp_lock;
+
+ __mp_lock(&ddb_mp_lock);
+ __mp_unlock(&ddb_mp_lock);
+
+ /*
+ * If ddb is hoping to us, it's our turn to enter ddb now.
+ */
+ if (ci->ci_cpuid == ddb_mp_nextcpu)
+ Debugger();
+#endif
+ }
+ if (ipi & CI_IPI_NOTIFY) {
+ /* nothing to do */
+ }
+}
+
+/*
+ * Maskable IPIs
+ */
+void
+av400_clock_ipi_handler(struct trapframe *eframe)
+{
+ struct cpu_info *ci = curcpu();
+ int ipi = ci->ci_ipi & (CI_IPI_HARDCLOCK | CI_IPI_STATCLOCK);
+
+ /* clear clock ipi interrupt */
+ *(volatile u_int32_t *)AV400_CLRSWI =
+ AV400_SWI_CLOCK_IPI_BIT(ci->ci_cpuid);
+ atomic_clearbits_int(&ci->ci_ipi, ipi);
+
+ if (ipi & CI_IPI_HARDCLOCK)
+ hardclock((struct clockframe *)eframe);
+#if 0 /* no separate statclock yet */
+ if (ipi & CI_IPI_STATCLOCK)
+ statclock((struct clockframe *)eframe);
+#endif
+}
+
+#endif
+
/*
* Provide the interrupt masks for a given logical interrupt source.
*/
@@ -437,7 +541,7 @@ static const u_int av400_obio_vec[32] = {
void
av400_intr(struct trapframe *eframe)
{
- int cpu = cpu_number();
+ u_int cpu = cpu_number();
u_int32_t cur_mask, ign_mask;
u_int level, old_spl;
struct intrhand *intr;
@@ -460,19 +564,56 @@ av400_intr(struct trapframe *eframe)
* Spurious interrupts - may be caused by debug output clearing
* DUART interrupts.
*/
+#ifdef MULTIPROCESSOR0
+ if (cpu != master_cpu) {
+ if (++problems >= 10) {
+ printf("cpu%d: interrupt pin won't clear, "
+ "disabling processor\n", cpu);
+ cpu_emergency_disable();
+ /* NOTREACHED */
+ }
+ }
+#endif
flush_pipeline();
goto out;
}
uvmexp.intrs++;
+#ifdef MULTIPROCESSOR0
+ /*
+ * Handle unmaskable IPIs immediately, so that we can reenable
+ * interrupts before further processing. We rely on the interrupt
+ * mask to make sure that if we get an IPI, it's really for us
+ * and no other processor.
+ */
+ if (cur_mask & AV400_IPI_MASK) {
+ av400_ipi_handler(eframe);
+ cur_mask &= ~AV400_IPI_MASK;
+ if (cur_mask == 0)
+ goto out;
+ }
+#endif
+
+#ifdef MULTIPROCESSOR
+ if (old_spl < IPL_SCHED)
+ __mp_lock(&kernel_lock);
+#endif
+
/*
* We want to service all interrupts marked in the IST register
* They are all valid because the mask would have prevented them
* from being generated otherwise. We will service them in order of
* priority.
*/
- do {
+ for (;;) {
+ cur_mask = ISR_GET_CURRENT_MASK(cpu);
+#ifdef MULTIPROCESSOR0
+ cur_mask &= ~AV400_IPI_MASK;
+#endif
+ if ((cur_mask & ~ign_mask) == 0)
+ break;
+
level = av400_safe_level(cur_mask, old_spl);
av400_setipl(level);
@@ -481,6 +622,18 @@ av400_intr(struct trapframe *eframe)
unmasked = 1;
}
+#ifdef MULTIPROCESSOR0
+ /*
+ * Handle pending maskable IPIs first.
+ */
+ if (cur_mask & AV400_CLOCK_IPI_MASK) {
+ av400_clock_ipi_handler(eframe);
+ cur_mask &= ~AV400_CLOCK_IPI_MASK;
+ if ((cur_mask & ~ign_mask) == 0)
+ break;
+ }
+#endif
+
/* find the first bit set in the current mask */
warn = 0;
intbit = ff1(cur_mask);
@@ -550,7 +703,7 @@ av400_intr(struct trapframe *eframe)
warn == 1 ? "spurious" : "unclaimed",
level, intbit, cur_mask, AV400_IST_STRING);
}
- } while (((cur_mask = ISR_GET_CURRENT_MASK(cpu)) & ~ign_mask) != 0);
+ }
#ifdef DIAGNOSTIC
if (ign_mask != 0) {
@@ -560,6 +713,11 @@ av400_intr(struct trapframe *eframe)
problems = 0;
#endif
+#ifdef MULTIPROCESSOR
+ if (old_spl < IPL_SCHED)
+ __mp_unlock(&kernel_lock);
+#endif
+
out:
/*
* process any remaining data access exceptions before
diff --git a/sys/arch/aviion/aviion/av530_machdep.c b/sys/arch/aviion/aviion/av530_machdep.c
index 1da9e0bad35..70a2dc4bee1 100644
--- a/sys/arch/aviion/aviion/av530_machdep.c
+++ b/sys/arch/aviion/aviion/av530_machdep.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: av530_machdep.c,v 1.3 2010/04/24 18:44:25 miod Exp $ */
+/* $OpenBSD: av530_machdep.c,v 1.4 2010/04/24 18:46:51 miod Exp $ */
/*
* Copyright (c) 2006, 2007, 2010 Miodrag Vallat.
*
@@ -48,7 +48,13 @@
#include <aviion/dev/sysconvar.h>
#include <aviion/dev/vmevar.h>
+#ifdef MULTIPROCESSOR
+#include <machine/db_machdep.h>
+#endif
+
u_int av530_safe_level(u_int, u_int, u_int);
+void av530_clock_ipi_handler(struct trapframe *);
+void av530_ipi_handler(struct trapframe *);
const pmap_table_entry av530_ptable[] = {
{ AV530_PROM, AV530_PROM, AV530_PROM_SIZE,
@@ -81,6 +87,10 @@ const struct board board_av530 = {
av530_getipl,
av530_setipl,
av530_raiseipl,
+#ifdef MULTIPROCESSOR
+ av530_send_ipi,
+ m88100_smp_setup,
+#endif
av530_intsrc,
av530_get_vme_ranges,
@@ -107,7 +117,7 @@ u_int av530_curspl[] = { IPL_HIGH, IPL_HIGH, IPL_HIGH, IPL_HIGH };
/*
* Interrupts allowed on secondary processors.
*/
-#define SLAVE_MASK 0 /* AV530_IRQ_SWI0 | AV530_IRQ_SWI1 */
+#define SLAVE_MASK 0
#define SLAVE_EXMASK 0
#endif
@@ -200,6 +210,11 @@ av530_safe_level(u_int mask, u_int exmask, u_int curlevel)
{
int i;
+#ifdef MULTIPROCESSOR
+ if (mask & AV530_CLOCK_IPI_MASK)
+ curlevel = max(IPL_CLOCK, curlevel);
+ mask &= ~(AV530_IPI_MASK | AV530_CLOCK_IPI_MASK);
+#endif
for (i = curlevel; i < NIPLS; i++)
if ((int_mask_val[i] & mask) == 0 &&
(ext_int_mask_val[i] & exmask) == 0)
@@ -231,6 +246,9 @@ av530_setipl(u_int level)
mask &= SLAVE_MASK;
exmask &= SLAVE_EXMASK;
}
+ mask |= AV530_SWI_IPI_MASK(cpu);
+ if (level < IPL_CLOCK)
+ mask |= AV530_SWI_CLOCK_IPI_MASK(cpu);
#endif
av530_curspl[cpu] = level;
@@ -262,6 +280,9 @@ av530_raiseipl(u_int level)
mask &= SLAVE_MASK;
exmask &= SLAVE_EXMASK;
}
+ mask |= AV530_SWI_IPI_MASK(cpu);
+ if (level < IPL_CLOCK)
+ mask |= AV530_SWI_CLOCK_IPI_MASK(cpu);
#endif
av530_curspl[cpu] = level;
@@ -278,6 +299,89 @@ av530_raiseipl(u_int level)
return curspl;
}
+#ifdef MULTIPROCESSOR
+
+void
+av530_send_ipi(int ipi, cpuid_t cpu)
+{
+ struct cpu_info *ci = &m88k_cpus[cpu];
+ uint32_t bits = 0;
+
+ if (ci->ci_ipi & ipi)
+ return;
+
+ atomic_setbits_int(&ci->ci_ipi, ipi);
+ if (ipi & ~(CI_IPI_HARDCLOCK | CI_IPI_STATCLOCK))
+ bits |= AV530_SWI_IPI_BIT(cpu);
+ if (ipi & (CI_IPI_HARDCLOCK | CI_IPI_STATCLOCK))
+ bits |= AV530_SWI_CLOCK_IPI_BIT(cpu);
+ *(volatile u_int32_t *)AV530_SETSWI = bits;
+}
+
+/*
+ * Process inter-processor interrupts.
+ */
+
+/*
+ * Unmaskable IPIs - those are processed with interrupts disabled,
+ * and no lock held.
+ */
+void
+av530_ipi_handler(struct trapframe *eframe)
+{
+ struct cpu_info *ci = curcpu();
+ int ipi = ci->ci_ipi & (CI_IPI_DDB | CI_IPI_NOTIFY);
+
+ *(volatile u_int32_t *)AV530_CLRSWI = AV530_SWI_IPI_BIT(ci->ci_cpuid);
+ atomic_clearbits_int(&ci->ci_ipi, ipi);
+
+ if (ipi & CI_IPI_DDB) {
+#ifdef DDB
+ /*
+ * Another processor has entered DDB. Spin on the ddb lock
+ * until it is done.
+ */
+ extern struct __mp_lock ddb_mp_lock;
+
+ __mp_lock(&ddb_mp_lock);
+ __mp_unlock(&ddb_mp_lock);
+
+ /*
+ * If ddb is hoping to us, it's our turn to enter ddb now.
+ */
+ if (ci->ci_cpuid == ddb_mp_nextcpu)
+ Debugger();
+#endif
+ }
+ if (ipi & CI_IPI_NOTIFY) {
+ /* nothing to do */
+ }
+}
+
+/*
+ * Maskable IPIs
+ */
+void
+av530_clock_ipi_handler(struct trapframe *eframe)
+{
+ struct cpu_info *ci = curcpu();
+ int ipi = ci->ci_ipi & (CI_IPI_HARDCLOCK | CI_IPI_STATCLOCK);
+
+ /* clear clock ipi interrupt */
+ *(volatile u_int32_t *)AV530_CLRSWI =
+ AV530_SWI_CLOCK_IPI_BIT(ci->ci_cpuid);
+ atomic_clearbits_int(&ci->ci_ipi, ipi);
+
+ if (ipi & CI_IPI_HARDCLOCK)
+ hardclock((struct clockframe *)eframe);
+#if 0 /* no separate statclock yet */
+ if (ipi & CI_IPI_STATCLOCK)
+ statclock((struct clockframe *)eframe);
+#endif
+}
+
+#endif
+
/*
* Provide the interrupt masks for a given logical interrupt source.
*/
@@ -443,12 +547,42 @@ av530_intr(struct trapframe *eframe)
* Spurious interrupts - may be caused by debug output clearing
* DUART interrupts.
*/
+#ifdef MULTIPROCESSOR
+ if (cpu != master_cpu) {
+ if (++problems >= 10) {
+ printf("cpu%d: interrupt pin won't clear, "
+ "disabling processor\n", cpu);
+ cpu_emergency_disable();
+ /* NOTREACHED */
+ }
+ }
+#endif
flush_pipeline();
goto out;
}
uvmexp.intrs++;
+#ifdef MULTIPROCESSOR
+ /*
+ * Handle unmaskable IPIs immediately, so that we can reenable
+ * interrupts before further processing. We rely on the interrupt
+ * mask to make sure that if we get an IPI, it's really for us
+ * and no other processor.
+ */
+ if (cur_mask & AV530_IPI_MASK) {
+ av530_ipi_handler(eframe);
+ cur_mask &= ~AV530_IPI_MASK;
+ if (cur_mask == 0 && cur_exmask == 0)
+ goto out;
+ }
+#endif
+
+#ifdef MULTIPROCESSOR
+ if (old_spl < IPL_SCHED)
+ __mp_lock(&kernel_lock);
+#endif
+
/*
* We want to service all interrupts marked in the IST register
* They are all valid because the mask would have prevented them
@@ -457,6 +591,9 @@ av530_intr(struct trapframe *eframe)
*/
for (;;) {
cur_mask = ISR_GET_CURRENT_MASK(cpu);
+#ifdef MULTIPROCESSOR
+ cur_mask &= ~AV530_IPI_MASK;
+#endif
cur_exmask = EXISR_GET_CURRENT_MASK(cpu);
if ((cur_mask & ~ign_mask) == 0 &&
(cur_exmask & ~ign_exmask) == 0)
@@ -470,6 +607,19 @@ av530_intr(struct trapframe *eframe)
unmasked = 1;
}
+#ifdef MULTIPROCESSOR
+ /*
+ * Handle pending maskable IPIs first.
+ */
+ if (cur_mask & AV530_CLOCK_IPI_MASK) {
+ av530_clock_ipi_handler(eframe);
+ cur_mask &= ~AV530_CLOCK_IPI_MASK;
+ if ((cur_mask & ~ign_mask) == 0 &&
+ (cur_exmask & ~ign_exmask) == 0)
+ break;
+ }
+#endif
+
/* find the first bit set in the current mask */
warn = 0;
if (cur_mask != 0) {
@@ -575,6 +725,11 @@ av530_intr(struct trapframe *eframe)
problems = 0;
#endif
+#ifdef MULTIPROCESSOR
+ if (old_spl < IPL_SCHED)
+ __mp_unlock(&kernel_lock);
+#endif
+
out:
/*
* process any remaining data access exceptions before
diff --git a/sys/arch/aviion/aviion/cio_clock.c b/sys/arch/aviion/aviion/cio_clock.c
index 7d405cefdc6..25a9aa720dc 100644
--- a/sys/arch/aviion/aviion/cio_clock.c
+++ b/sys/arch/aviion/aviion/cio_clock.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: cio_clock.c,v 1.2 2010/04/21 19:33:45 miod Exp $ */
+/* $OpenBSD: cio_clock.c,v 1.3 2010/04/24 18:46:51 miod Exp $ */
/*
* Copyright (c) 2006, 2007, 2009 Miodrag Vallat.
*
@@ -143,6 +143,7 @@
#include <uvm/uvm_extern.h>
+#include <machine/asm_macro.h>
#include <machine/board.h>
#include <machine/avcommon.h>
@@ -272,6 +273,14 @@ cio_clockintr(void *eframe)
hardclock(eframe);
+#ifdef MULTIPROCESSOR
+ /*
+ * Send an IPI to all other processors, so they can get their
+ * own ticks.
+ */
+ m88k_broadcast_ipi(CI_IPI_HARDCLOCK);
+#endif
+
return (1);
}
diff --git a/sys/arch/aviion/aviion/locore.S b/sys/arch/aviion/aviion/locore.S
index 21d3be97b7c..0bb7e917820 100644
--- a/sys/arch/aviion/aviion/locore.S
+++ b/sys/arch/aviion/aviion/locore.S
@@ -1,4 +1,4 @@
-/* $OpenBSD: locore.S,v 1.12 2010/04/18 15:04:51 miod Exp $ */
+/* $OpenBSD: locore.S,v 1.13 2010/04/24 18:46:51 miod Exp $ */
/*
* Copyright (c) 2005, Miodrag Vallat.
* Copyright (c) 1998 Steve Murphree, Jr.
@@ -205,6 +205,35 @@ ASLOCAL(main_panic)
* to the comments there for details.
*/
GLOBAL(secondary_start)
+ /*
+ * We have been started early, but there is nothing we can do yet.
+ * We'll just spin until we can get the hatching mutex.
+ */
+ or.u r11, r0, hi16(_C_LABEL(cpu_hatch_mutex))
+ or r11, r11, lo16(_C_LABEL(cpu_hatch_mutex))
+1:
+ or r22, r0, 1
+ xmem r22, r11, r0 /* if r22 becomes zero, we own the lock... */
+ bcnd eq0, r22, 4f /* ... but if not, we must wait */
+2:
+ /* just watch the lock until it looks clear */
+ ld r22, r11, r0
+ bcnd eq0, r22, 1b
+ /* wait a bit to avoid overloading the bus */
+ or.u r2, r0, 1
+3:
+ subu r2, r2, 1
+ bcnd ne0, r2, 3b
+ br 2b
+4:
+
+ /*
+ * We are now running free with cpu_hatch_mutex held; other
+ * secondary processors are waiting for the lock, and the main
+ * processor is waiting for us to decrease the hatch counter,
+ * which we'll do in secondary_main() later.
+ */
+
or.u r31, r0, hi16(_ASM_LABEL(slavestack_end))
or r31, r31, lo16(_ASM_LABEL(slavestack_end))
@@ -224,29 +253,6 @@ GLOBAL(secondary_start)
stcr r11, CPU
/*
- * Since there may be more than one secondary MPU, compete with them
- * to initialize safely.
- */
- or.u r11, r0, hi16(_C_LABEL(cpu_mutex))
- or r11, r11, lo16(_C_LABEL(cpu_mutex))
-1:
- or r22, r0, 1
- xmem r22, r11, r0 /* If r22 gets 0, we have the lock... */
- bcnd eq0, r22, 4f /* ...but if not, we must wait */
-2:
- /* just watch the lock until it looks clear */
- ld r22, r11, r0
- bcnd eq0, r22, 1b
- /* since we can be here with caches off, add a few nops to
- keep the bus from getting overloaded */
- or r2, r0, lo16(1000)
-3:
- subu r2, r2, 1
- bcnd ne0, r2, 3b
- br 1b
-4:
-
- /*
* While holding the cpu_mutex, the secondary cpu can use the slavestack
* to call secondary_pre_main() to determine its cpu number.
* After that, however, it should allocate its own stack and switch
@@ -261,6 +267,14 @@ GLOBAL(secondary_start)
bsr.n _C_LABEL(secondary_main)
addu r31, r2, USPACE /* switch to startup stack */
+ /*
+ * Dummy mp_atomic_begin() and mp_atomic_end() routine, so that
+ * we can interact with ddb if things go wrong very early during
+ * bootstrap. Of course this should never happen (-:
+ */
+ASLOCAL(dummy_mplock)
+ jmp r1
+
#endif /* MULTIPROCESSOR */
/*
@@ -317,7 +331,12 @@ ASLOCAL(dummy_cpu)
word 3 /* CIF_PRIMARY | CIF_ALIVE */ /* ci_alive */
word 0 /* ci_curproc */
word 0 /* ci_curpcb */
+ word 0 /* ci_curpmap */
word 0 /* ci_cpuid */
+ word _ASM_LABEL(dummy_mplock) /* ci_mp_atomic_begin */
+ word _ASM_LABEL(dummy_mplock) /* ci_mp_atomic_end */
+
+ space CPU_INFO_SIZEOF - 7 * 4
#endif /* MULTIPROCESSOR */
#if defined(DDB) || NKSYMS > 0
diff --git a/sys/arch/aviion/aviion/machdep.c b/sys/arch/aviion/aviion/machdep.c
index aa28b7f6848..4d58c533bb5 100644
--- a/sys/arch/aviion/aviion/machdep.c
+++ b/sys/arch/aviion/aviion/machdep.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: machdep.c,v 1.36 2010/04/24 18:44:25 miod Exp $ */
+/* $OpenBSD: machdep.c,v 1.37 2010/04/24 18:46:51 miod Exp $ */
/*
* Copyright (c) 2007 Miodrag Vallat.
*
@@ -114,6 +114,8 @@
void aviion_bootstrap(void);
void aviion_identify(void);
void consinit(void);
+void cpu_hatch_secondary_processors(void);
+void cpu_setup_secondary_processors(void);
__dead void doboot(void);
void dumpconf(void);
void dumpsys(void);
@@ -127,7 +129,9 @@ struct vm_map *exec_map = NULL;
struct vm_map *phys_map = NULL;
#ifdef MULTIPROCESSOR
-__cpu_simple_lock_t cpu_mutex = __SIMPLELOCK_UNLOCKED;
+u_int hatch_pending_count = 0;
+__cpu_simple_lock_t cpu_hatch_mutex = __SIMPLELOCK_LOCKED;
+__cpu_simple_lock_t cpu_boot_mutex = __SIMPLELOCK_LOCKED;
#endif
/*
@@ -535,10 +539,12 @@ vaddr_t
secondary_pre_main()
{
struct cpu_info *ci;
+ vaddr_t init_stack;
set_cpu_number(cmmu_cpu_number()); /* Determine cpu number by CMMU */
ci = curcpu();
ci->ci_curproc = &proc0;
+ platform->smp_setup(ci);
splhigh();
@@ -554,6 +560,11 @@ secondary_pre_main()
if (init_stack == (vaddr_t)NULL) {
printf("cpu%d: unable to allocate startup stack\n",
ci->ci_cpuid);
+ /*
+ * Release cpu_hatch_mutex to let other secondary processors
+ * have a chance to run.
+ */
+ __cpu_simple_unlock(&cpu_hatch_mutex);
for (;;) ;
}
@@ -573,17 +584,28 @@ secondary_main()
int s;
cpu_configuration_print(0);
- sched_init_cpu(ci);
ncpus++;
- __cpu_simple_unlock(&cpu_mutex);
+ sched_init_cpu(ci);
microuptime(&ci->ci_schedstate.spc_runtime);
ci->ci_curproc = NULL;
+ ci->ci_randseed = random();
+ SET(ci->ci_flags, CIF_ALIVE);
- set_psr(get_psr() & ~PSR_IND);
- spl0();
+ /*
+ * Release cpu_hatch_mutex to let other secondary processors
+ * have a chance to run.
+ */
+ hatch_pending_count--;
+ __cpu_simple_unlock(&cpu_hatch_mutex);
+
+ /* wait for cpu_boot_secondary_processors() */
+ __cpu_simple_lock(&cpu_boot_mutex);
+ __cpu_simple_unlock(&cpu_boot_mutex);
+ spl0();
SCHED_LOCK(s);
+ set_psr(get_psr() & ~PSR_IND);
cpu_switchto(NULL, sched_chooseproc());
}
@@ -708,6 +730,9 @@ aviion_bootstrap()
setup_board_config();
master_cpu = cmmu_init();
set_cpu_number(master_cpu);
+#ifdef MULTIPROCESSOR
+ platform->smp_setup(curcpu());
+#endif
SET(curcpu()->ci_flags, CIF_ALIVE | CIF_PRIMARY);
#ifdef M88100
@@ -716,6 +741,14 @@ aviion_bootstrap()
}
#endif
+#ifdef MULTIPROCESSOR
+ /*
+ * We need to start secondary processors while it is still
+ * possible to invoke SCM functions.
+ */
+ cpu_hatch_secondary_processors();
+#endif
+
/*
* Now that set_cpu_number() set us with a valid cpu_info pointer,
* we need to initialize p_addr and curpcb before autoconf, for the
@@ -748,21 +781,64 @@ aviion_bootstrap()
}
#ifdef MULTIPROCESSOR
+/*
+ * Spin processors while we can use the PROM, and have them wait for
+ * cpu_hatch_mutex.
+ */
void
-cpu_boot_secondary_processors()
+cpu_hatch_secondary_processors()
{
+ struct cpu_info *ci = curcpu();
cpuid_t cpu;
int rc;
extern void secondary_start(void);
+ /* we might not have a working SMP implementation on this system. */
+ if (platform->send_ipi == NULL)
+ return;
+
for (cpu = 0; cpu < ncpusfound; cpu++) {
- if (cpu != curcpu()->ci_cpuid) {
- rc = scm_spincpu(cpu, (vaddr_t)secondary_start);
- if (rc != 0)
- printf("cpu%d: spin_cpu error %d\n", cpu, rc);
+ if (cpu != ci->ci_cpuid) {
+ rc = scm_jpstart(cpu, (vaddr_t)secondary_start);
+ switch (rc) {
+ case JPSTART_OK:
+ hatch_pending_count++;
+ break;
+ case JPSTART_NO_JP:
+ break;
+ case JPSTART_SINGLE_JP:
+ /* this should never happen, but just in case */
+ ncpusfound = 1;
+ return;
+ default:
+ printf("CPU%d failed to start, error %d\n",
+ cpu, rc);
+ break;
+ }
}
}
}
+
+/*
+ * Release cpu_hatch_mutex to let secondary processors initialize.
+ */
+void
+cpu_setup_secondary_processors()
+{
+ __cpu_simple_unlock(&cpu_hatch_mutex);
+ while (hatch_pending_count != 0)
+ delay(100000);
+}
+
+/*
+ * Release cpu_boot_mutex to let secondary processors start running
+ * processes.
+ */
+void
+cpu_boot_secondary_processors()
+{
+ __cpu_simple_unlock(&cpu_boot_mutex);
+}
#endif
/*
@@ -820,6 +896,34 @@ raiseipl(int level)
return (int)platform->raiseipl((u_int)level);
}
+#ifdef MULTIPROCESSOR
+void
+m88k_send_ipi(int ipi, cpuid_t cpu)
+{
+ struct cpu_info *ci;
+
+ ci = &m88k_cpus[cpu];
+ if (ISSET(ci->ci_flags, CIF_ALIVE))
+ platform->send_ipi(ipi, cpu);
+}
+
+void
+m88k_broadcast_ipi(int ipi)
+{
+ struct cpu_info *us = curcpu();
+ struct cpu_info *ci;
+ CPU_INFO_ITERATOR cii;
+
+ CPU_INFO_FOREACH(cii, ci) {
+ if (ci == us)
+ continue;
+
+ if (ISSET(ci->ci_flags, CIF_ALIVE))
+ platform->send_ipi(ipi, ci->ci_cpuid);
+ }
+}
+#endif
+
void
intsrc_enable(u_int intsrc, int ipl)
{
diff --git a/sys/arch/aviion/aviion/prom.c b/sys/arch/aviion/aviion/prom.c
index 29d0390b013..2a3f1707dc2 100644
--- a/sys/arch/aviion/aviion/prom.c
+++ b/sys/arch/aviion/aviion/prom.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: prom.c,v 1.3 2007/12/20 21:19:32 miod Exp $ */
+/* $OpenBSD: prom.c,v 1.4 2010/04/24 18:46:51 miod Exp $ */
/*
* Copyright (c) 2006, Miodrag Vallat.
@@ -286,3 +286,26 @@ scm_sysid()
return (ret);
}
+
+#ifdef MULTIPROCESSOR
+u_int
+scm_jpstart(cpuid_t cpu, vaddr_t addr)
+{
+ SCM_DECL;
+ u_int ret;
+
+ psr = get_psr();
+ set_psr(psr | PSR_IND);
+ SCM_CONTEXT();
+ SCM_VBR();
+ __asm__ __volatile__ ("or r2, r0, %0; or r3, r0, %1" : :
+ "r" (cpu), "r" (addr));
+ SCM_CALL(SCM_JPSTART);
+ __asm__ __volatile__ ("or %0, r0, r2" : "=r" (ret));
+ OS_CONTEXT();
+ OS_VBR();
+ set_psr(psr);
+
+ return (ret);
+}
+#endif
diff --git a/sys/arch/aviion/aviion/rtc_clock.c b/sys/arch/aviion/aviion/rtc_clock.c
index 696ad7861a4..dc153a6a842 100644
--- a/sys/arch/aviion/aviion/rtc_clock.c
+++ b/sys/arch/aviion/aviion/rtc_clock.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: rtc_clock.c,v 1.1 2010/04/21 19:33:45 miod Exp $ */
+/* $OpenBSD: rtc_clock.c,v 1.2 2010/04/24 18:46:51 miod Exp $ */
/*
* Copyright (c) 2010 Miodrag Vallat.
@@ -113,6 +113,14 @@ rtc_clockintr(void *frame)
*(volatile uint32_t *)AV530_PIT0_CS = AV530_PIT_CTEN;
hardclock(frame);
+#ifdef MULTIPROCESSOR
+ /*
+ * Send an IPI to all other processors, so they can get their
+ * own ticks.
+ */
+ m88k_broadcast_ipi(CI_IPI_HARDCLOCK);
+#endif
+
return 1;
}
diff --git a/sys/arch/aviion/conf/GENERIC.MP b/sys/arch/aviion/conf/GENERIC.MP
new file mode 100644
index 00000000000..fcbddc61acf
--- /dev/null
+++ b/sys/arch/aviion/conf/GENERIC.MP
@@ -0,0 +1,15 @@
+# $OpenBSD: GENERIC.MP,v 1.1 2010/04/24 18:46:53 miod Exp $
+#
+# For further information on compiling OpenBSD kernels, see the config(8)
+# man page.
+#
+# For further information on hardware support for this architecture, see
+# the intro(4) man page. For further information about kernel options
+# for this architecture, see the options(4) man page. For an explanation
+# of each device driver in this file see the section 4 man page for the
+# device.
+
+include "arch/aviion/conf/GENERIC"
+
+option MULTIPROCESSOR
+#option MP_LOCKDEBUG
diff --git a/sys/arch/aviion/dev/dart.c b/sys/arch/aviion/dev/dart.c
index abbe4d799b3..fe83d35661e 100644
--- a/sys/arch/aviion/dev/dart.c
+++ b/sys/arch/aviion/dev/dart.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: dart.c,v 1.8 2010/04/12 12:57:51 tedu Exp $ */
+/* $OpenBSD: dart.c,v 1.9 2010/04/24 18:46:55 miod Exp $ */
/*
* Mach Operating System
@@ -812,7 +812,7 @@ dartintr(void *arg)
* ready change on a disabled port). This should not happen,
* but we have to claim the interrupt anyway.
*/
-#ifdef DIAGNOSTIC
+#if defined(DIAGNOSTIC) && !defined(MULTIPROCESSOR)
printf("%s: spurious interrupt, isr %x imr %x\n",
sc->sc_dev.dv_xname, isr, imr);
#endif
diff --git a/sys/arch/aviion/dev/mainbus.c b/sys/arch/aviion/dev/mainbus.c
index 18ae8edb097..0c8047ce701 100644
--- a/sys/arch/aviion/dev/mainbus.c
+++ b/sys/arch/aviion/dev/mainbus.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: mainbus.c,v 1.5 2010/04/24 18:44:27 miod Exp $ */
+/* $OpenBSD: mainbus.c,v 1.6 2010/04/24 18:46:55 miod Exp $ */
/*
* Copyright (c) 1998 Steve Murphree, Jr.
* Copyright (c) 2004, Miodrag Vallat.
@@ -128,6 +128,7 @@ mainbus_match(struct device *parent, void *cf, void *args)
void
mainbus_attach(struct device *parent, struct device *self, void *args)
{
+ extern void cpu_setup_secondary_processors(void);
extern char cpu_model[];
printf(": %s, cpuid 0x%04x", cpu_model, cpuid);
@@ -138,6 +139,14 @@ mainbus_attach(struct device *parent, struct device *self, void *args)
*/
cpu_configuration_print(1);
+#ifdef MULTIPROCESSOR
+ /*
+ * Let secondary processor initialize further and print their
+ * configuration information now.
+ */
+ cpu_setup_secondary_processors();
+#endif
+
(void)config_search(mainbus_scan, self, args);
}
diff --git a/sys/arch/aviion/include/av530.h b/sys/arch/aviion/include/av530.h
index 73965e000b9..62f4b907b8e 100644
--- a/sys/arch/aviion/include/av530.h
+++ b/sys/arch/aviion/include/av530.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: av530.h,v 1.2 2010/04/21 19:33:47 miod Exp $ */
+/* $OpenBSD: av530.h,v 1.3 2010/04/24 18:46:55 miod Exp $ */
/*
* Copyright (c) 2006, 2010 Miodrag Vallat
*
@@ -106,6 +106,22 @@
"\10SIGLPI\7IRQ2\5IRQ1\4SWI3\3SWI2\2SWI1\1SWI0"
/*
+ * Software interrupts 0 to 3, and 4 to 7, are used to deliver IPIs to
+ * CPU0-3. We use two software interrupts per CPU because we want clock
+ * IPIs to be maskable.
+ */
+#define AV530_CLOCK_IPI_MASK (AV530_IRQ_SWI7 | AV530_IRQ_SWI6 | \
+ AV530_IRQ_SWI5 | AV530_IRQ_SWI4)
+#define AV530_IPI_MASK (AV530_IRQ_SWI3 | AV530_IRQ_SWI2 | \
+ AV530_IRQ_SWI1 | AV530_IRQ_SWI0)
+/* values for SETSWI and CLRSWI registers */
+#define AV530_SWI_IPI_BIT(cpu) (0x01 << (cpu))
+#define AV530_SWI_CLOCK_IPI_BIT(cpu) (0x10 << (cpu))
+/* values for IEN and IST registers */
+#define AV530_SWI_IPI_MASK(cpu) (AV530_IRQ_SWI0 << (cpu))
+#define AV530_SWI_CLOCK_IPI_MASK(cpu) (AV530_IRQ_SWI4 << (cpu))
+
+/*
* Extended interrupts
*/
diff --git a/sys/arch/aviion/include/board.h b/sys/arch/aviion/include/board.h
index b4c6384b873..8424da73872 100644
--- a/sys/arch/aviion/include/board.h
+++ b/sys/arch/aviion/include/board.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: board.h,v 1.7 2010/04/24 18:44:27 miod Exp $ */
+/* $OpenBSD: board.h,v 1.8 2010/04/24 18:46:55 miod Exp $ */
/*
* Copyright (c) 2006, 2007, Miodrag Vallat
*
@@ -84,6 +84,10 @@ struct board {
u_int (*getipl)(void);
u_int (*setipl)(u_int);
u_int (*raiseipl)(u_int);
+#ifdef MULTIPROCESSOR
+ void (*send_ipi)(int, cpuid_t);
+ void (*smp_setup)(struct cpu_info *);
+#endif
u_int64_t (*intsrc)(int);
const struct vme_range *(*get_vme_ranges)(void);
@@ -103,6 +107,8 @@ void av##b##_init_clocks(void); \
u_int av##b##_getipl(void); \
u_int av##b##_setipl(u_int); \
u_int av##b##_raiseipl(u_int); \
+void av##b##_send_ipi(int, cpuid_t); \
+void av##b##_smp_setup(struct cpu_info *); \
u_int64_t av##b##_intsrc(int); \
const struct vme_range *av##b##_get_vme_ranges(void);
diff --git a/sys/arch/aviion/include/prom.h b/sys/arch/aviion/include/prom.h
index 4ce7f0f6574..1783cb20ee2 100644
--- a/sys/arch/aviion/include/prom.h
+++ b/sys/arch/aviion/include/prom.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: prom.h,v 1.2 2006/05/20 11:57:04 miod Exp $ */
+/* $OpenBSD: prom.h,v 1.3 2010/04/24 18:46:55 miod Exp $ */
/*
* Copyright (c) 2006, Miodrag Vallat.
*
@@ -39,6 +39,7 @@
#define SCM_OCRLF 0x26
#define SCM_HALT 0x63
#define SCM_STDIO 0x70
+#define SCM_JPSTART 0x100
#define SCM_REBOOT 0x101
#define SCM_CPUID 0x102
#define SCM_MSIZE 0x103
@@ -51,6 +52,13 @@
#define SCM_SYSID 0x31
#define SCM_CPUCONFIG 0x107
+/* SCM_JPSTART return values */
+#define JPSTART_OK 0
+#define JPSTART_NO_JP 1
+#define JPSTART_SINGLE_JP 2
+#define JPSTART_NOT_IDLE 3
+#define JPSTART_NO_ANSWER 4
+
struct scm_cpuconfig {
u_int32_t version;
#define SCM_CPUCONFIG_VERSION 0
@@ -65,6 +73,7 @@ u_int scm_cpuid(void);
int scm_getc(void);
void scm_getenaddr(u_char *);
__dead void scm_halt(void);
+u_int scm_jpstart(cpuid_t, vaddr_t);
u_int scm_memsize(int);
void scm_printf(const char *);
u_int scm_promver(void);