Add smp support
Signed-off-by: Richard Henderson <rth@twiddle.net>
diff --git a/Makefile b/Makefile
index 2025599..ec3c55f 100644
--- a/Makefile
+++ b/Makefile
@@ -27,7 +27,7 @@
rm -f palcode-*
pal.o: pal.S osf.h sys-$(SYSTEM).h core-$(CORE).h
-init.o: init.c hwrpb.h osf.h uart.h sys-$(SYSTEM).h core-$(CORE).h
+init.o: init.c protos.h hwrpb.h osf.h uart.h sys-$(SYSTEM).h core-$(CORE).h
printf.o: printf.c uart.h
uart.o: uart.c uart.h protos.h
crb.o: crb.c hwrpb.h protos.h console.h uart.h
diff --git a/init.c b/init.c
index 324bc91..e7b506f 100644
--- a/init.c
+++ b/init.c
@@ -40,7 +40,7 @@
struct hwrpb_combine {
struct hwrpb_struct hwrpb;
- struct percpu_struct processor;
+ struct percpu_struct processor[4];
struct memdesc_struct md;
struct memclust_struct mc[2];
struct crb_struct crb;
@@ -138,10 +138,11 @@
}
static void
-init_hwrpb (unsigned long memsize)
+init_hwrpb (unsigned long memsize, unsigned long cpus)
{
unsigned long pal_pages;
unsigned long amask;
+ unsigned long i;
hwrpb.hwrpb.phys_addr = PA(&hwrpb);
@@ -186,14 +187,19 @@
hwrpb.hwrpb.sys_type = SYS_TYPE;
hwrpb.hwrpb.sys_variation = SYS_VARIATION;
hwrpb.hwrpb.sys_revision = SYS_REVISION;
- hwrpb.processor.type = hwrpb.hwrpb.cpuid;
+ for (i = 0; i < cpus; ++i)
+ {
+ /* ??? Look up these bits. Snagging the value examined by the kernel. */
+ hwrpb.processor[i].flags = 0x1cc;
+ hwrpb.processor[i].type = hwrpb.hwrpb.cpuid;
+ }
hwrpb.hwrpb.intr_freq = HZ * 4096;
hwrpb.hwrpb.cycle_freq = 250000000; /* QEMU architects 250MHz. */
hwrpb.hwrpb.vptb = VPTPTR;
- hwrpb.hwrpb.nr_processors = 1;
+ hwrpb.hwrpb.nr_processors = cpus;
hwrpb.hwrpb.processor_size = sizeof(struct percpu_struct);
hwrpb.hwrpb.processor_offset = offsetof(struct hwrpb_combine, processor);
@@ -268,13 +274,25 @@
outb(0x20, PORT_PIC1_CMD);
}
+static void __attribute__((noreturn))
+swppal(void *entry, void *pcb)
+{
+ register int variant __asm__("$16") = 2; /* OSF/1 PALcode */
+ register void *pc __asm__("$17") = entry;
+ register unsigned long pa_pcb __asm__("$18") = PA(pcb);
+ register unsigned long vptptr __asm__("$19") = VPTPTR;
+
+ asm("call_pal 0x0a" : : "r"(variant), "r"(pc), "r"(pa_pcb), "r"(vptptr));
+ __builtin_unreachable ();
+}
+
void
-do_start(unsigned long memsize, void (*kernel_entry)(void), long cpus)
+do_start(unsigned long memsize, void (*kernel_entry)(void), unsigned long cpus)
{
last_alloc = _end;
init_page_table();
- init_hwrpb(memsize);
+ init_hwrpb(memsize, cpus);
init_pcb();
init_i8259();
uart_init();
@@ -282,29 +300,38 @@
pci_setup();
vgahw_init();
- {
- register int variant __asm__("$16") = 2; /* OSF/1 PALcode */
- register void (*pc)(void) __asm__("$17");
- register unsigned long pa_pcb __asm__("$18");
- register unsigned long vptptr __asm__("$19");
-
- pc = (kernel_entry ? kernel_entry : do_console);
- pa_pcb = PA(&pcb);
- vptptr = VPTPTR;
- asm("call_pal 0x0a" : : "r"(variant), "r"(pc), "r"(pa_pcb), "r"(vptptr));
- }
- __builtin_unreachable ();
+ swppal(kernel_entry, &pcb);
}
void
-do_start_wait(void)
+do_start_wait(unsigned long cpuid)
{
while (1)
{
- // WtInt with interrupts off. Rely on the fact that QEMU will
- // un-halt the CPU when an interrupt arrives.
- asm("lda $16,-1\n\tcall_pal 0x3e" : : : "$0", "$16");
+ /* Wait 1ms for the kernel to wake us. */
+ ndelay(1000000);
- // FIXME do something with the IPI.
+ if (hwrpb.hwrpb.rxrdy & (1ull << cpuid))
+ {
+ /* ??? The only message I know of is "START\r\n".
+ I can't be bothered to verify more than 4 characters. */
+ /* ??? The Linux kernel fills in, but does not require,
+ CPU_restart_data. It just sets that to the same address
+ as CPU_restart itself. Our swppal *does* put the PC into
+ $26 and $27, the latter of which the kernel does rely upon. */
+
+ unsigned int len = hwrpb.processor[cpuid].ipc_buffer[0];
+ unsigned int msg = hwrpb.processor[cpuid].ipc_buffer[1];
+ void *CPU_restart = hwrpb.hwrpb.CPU_restart;
+ __sync_synchronize();
+ hwrpb.hwrpb.rxrdy = 0;
+
+ if (len == 7 && msg == ('S' | 'T' << 8 | 'A' << 16 | 'R' << 24))
+ {
+ /* Set bootstrap in progress */
+ hwrpb.processor[cpuid].flags |= 1;
+ swppal(CPU_restart, hwrpb.processor[cpuid].hwpcb);
+ }
+ }
}
}
diff --git a/pal.S b/pal.S
index 1befc9f..1781c4b 100644
--- a/pal.S
+++ b/pal.S
@@ -97,9 +97,9 @@
bne a0, 1f
// Load boot arguments
- mfpr a0, qemu_trap_arg0
- mfpr a1, qemu_trap_arg1
- mfpr a2, qemu_trap_arg2
+ mfpr a0, qemu_trap_arg0 // memsize
+ mfpr a1, qemu_trap_arg1 // kernel entry
+ mfpr a2, qemu_trap_arg2 // ncpus
// Continue in do_start, outside PALmode.
ldah $27, do_start($gp) !gprelhigh
@@ -400,10 +400,25 @@
hw_rei
ENDFN CallPal_Draina
+/*
+ * Delay for N nanoseconds.
+ *
+ * This is unique to QEMU, used only within PALcode itself.
+ */
ORG_CALL_PAL_PRIV(0x03)
-CallPal_OpcDec03:
- br CallPal_OpcDec
-ENDFN CallPal_OpcDec03
+CallPal_Ndelay:
+ mfpr p0, qemu_vmtime
+ addq p0, a0, p0
+ mtpr p0, qemu_alarm
+
+ mtpr $31, qemu_wait
+
+ SYS_ACK_CLK p2, p3, p4
+
+ mfpr v0, qemu_vmtime
+ subq p0, v0, v0
+ hw_rei
+ENDFN CallPal_Ndelay
ORG_CALL_PAL_PRIV(0x04)
CallPal_OpcDec04:
@@ -1416,6 +1431,7 @@
*/
ORG_CALL_PAL_UNPRIV(0x86)
CallPal_Imb:
+ mb
hw_rei
ENDFN CallPal_Imb
@@ -1914,5 +1930,5 @@
.align 3
.globl stack
.type stack,@object
- .size stack,STACK_SIZE
-stack: .skip STACK_SIZE
+ .size stack,STACK_SIZE * 4
+stack: .skip STACK_SIZE * 4
diff --git a/util.c b/util.c
index 1444dd6..0e943be 100644
--- a/util.c
+++ b/util.c
@@ -20,20 +20,20 @@
#include "protos.h"
+static inline long
+ndelay_with_int(unsigned long nsec)
+{
+ register long a0 __asm__("16") = nsec;
+ register long v0 __asm__("0");
+ asm volatile ("call_pal 3" : "=r"(v0) : "r"(a0));
+ return v0;
+}
void
ndelay(unsigned long nsec)
{
- unsigned long target, now;
-
- /* ??? Fix race between setting an alarm and waiting for an interrupt,
- so that we can use wtint here. This isn't used much except for
- during startup, so it probably doesn't matter much. */
-
- now = get_wall_time();
- target = now + nsec;
-
- do
- now = get_wall_time();
- while (now < target);
+ long left = nsec;
+ do {
+ left = ndelay_with_int(left);
+ } while (left > 0);
}