summaryrefslogtreecommitdiffstats
path: root/sys/mips/mips/mp_machdep.c
diff options
context:
space:
mode:
Diffstat (limited to 'sys/mips/mips/mp_machdep.c')
-rw-r--r--sys/mips/mips/mp_machdep.c313
1 files changed, 313 insertions, 0 deletions
diff --git a/sys/mips/mips/mp_machdep.c b/sys/mips/mips/mp_machdep.c
new file mode 100644
index 0000000..897ab07
--- /dev/null
+++ b/sys/mips/mips/mp_machdep.c
@@ -0,0 +1,313 @@
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include "opt_kstack_pages.h"
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/ktr.h>
+#include <sys/proc.h>
+#include <sys/cons.h>
+#include <sys/lock.h>
+#include <sys/malloc.h>
+#include <sys/mutex.h>
+#include <sys/kernel.h>
+#include <sys/pcpu.h>
+#include <sys/smp.h>
+#include <sys/sysctl.h>
+#include <sys/bus.h>
+
+#include <vm/vm.h>
+#include <vm/pmap.h>
+#include <vm/vm_map.h>
+
+#include <machine/atomic.h>
+#include <machine/clock.h>
+#include <machine/md_var.h>
+#include <machine/pcb.h>
+#include <machine/pmap.h>
+#include <machine/smp.h>
+
+static struct mtx ap_boot_mtx;
+extern struct pcpu __pcpu[];
+extern int num_tlbentries;
+void mips_start_timer(void);
+static volatile int aps_ready = 0;
+
+u_int32_t boot_cpu_id;
+
+
+void
+cpu_mp_announce(void)
+{
+}
+
+/*
+ * To implement IPIs on MIPS CPU, we use the Interrupt Line 2 ( bit 4 of cause
+ * register) and a bitmap to avoid redundant IPI interrupts. To interrupt a
+ * set of CPUs, the sender routine runs in a ' loop ' sending interrupts to
+ * all the specified CPUs. A single Mutex (smp_ipi_mtx) is used for all IPIs
+ * that spinwait for delivery. This includes the following IPIs
+ * IPI_RENDEZVOUS
+ * IPI_INVLPG
+ * IPI_INVLTLB
+ * IPI_INVLRNG
+ */
+
+/*
+ * send an IPI to a set of cpus.
+ */
+void
+ipi_selected(u_int32_t cpus, u_int ipi)
+{
+ struct pcpu *pcpu;
+ u_int cpuid, new_pending, old_pending;
+
+ CTR3(KTR_SMP, "%s: cpus: %x, ipi: %x\n", __func__, cpus, ipi);
+
+ while ((cpuid = ffs(cpus)) != 0) {
+ cpuid--;
+ cpus &= ~(1 << cpuid);
+ pcpu = pcpu_find(cpuid);
+
+ if (pcpu) {
+ do {
+ old_pending = pcpu->pc_pending_ipis;
+ new_pending = old_pending | ipi;
+ } while (!atomic_cmpset_int(&pcpu->pc_pending_ipis,
+ old_pending, new_pending));
+
+ if (old_pending)
+ continue;
+
+ mips_ipi_send (cpuid);
+ }
+ }
+}
+
+/*
+ * send an IPI INTerrupt containing 'vector' to all CPUs, including myself
+ */
+void
+ipi_all(u_int ipi)
+{
+
+ ipi_selected(all_cpus, ipi);
+}
+
+/*
+ * send an IPI to all CPUs EXCEPT myself
+ */
+void
+ipi_all_but_self(u_int ipi)
+{
+
+ ipi_selected(PCPU_GET(other_cpus), ipi);
+}
+
+/*
+ * send an IPI to myself
+ */
+void
+ipi_self(u_int ipi)
+{
+
+ ipi_selected(PCPU_GET(cpumask), ipi);
+}
+
+/*
+ * Handle an IPI sent to this processor.
+ */
+intrmask_t
+smp_handle_ipi(struct trapframe *frame)
+{
+ cpumask_t cpumask; /* This cpu mask */
+ u_int ipi, ipi_bitmap;
+
+ ipi_bitmap = atomic_readandclear_int(PCPU_PTR(pending_ipis));
+ cpumask = PCPU_GET(cpumask);
+
+ CTR1(KTR_SMP, "smp_handle_ipi(), ipi_bitmap=%x", ipi_bitmap);
+ while (ipi_bitmap) {
+ /*
+ * Find the lowest set bit.
+ */
+ ipi = ipi_bitmap & ~(ipi_bitmap - 1);
+ ipi_bitmap &= ~ipi;
+ switch (ipi) {
+ case IPI_INVLTLB:
+ CTR0(KTR_SMP, "IPI_INVLTLB");
+ break;
+
+ case IPI_RENDEZVOUS:
+ CTR0(KTR_SMP, "IPI_RENDEZVOUS");
+ smp_rendezvous_action();
+ break;
+
+ case IPI_AST:
+ CTR0(KTR_SMP, "IPI_AST");
+ break;
+
+ case IPI_STOP:
+ CTR0(KTR_SMP, "IPI_STOP");
+ atomic_set_int(&stopped_cpus, cpumask);
+
+ while ((started_cpus & cpumask) == 0)
+ ;
+ atomic_clear_int(&started_cpus, cpumask);
+ atomic_clear_int(&stopped_cpus, cpumask);
+ break;
+ }
+ }
+ return CR_INT_IPI;
+ }
+
+void
+cpu_mp_setmaxid(void)
+{
+
+ mp_maxid = MAXCPU - 1;
+}
+
+void
+smp_init_secondary(u_int32_t cpuid)
+{
+
+ if (cpuid >= MAXCPU)
+ panic ("cpu id exceeds MAXCPU\n");
+
+ /* tlb init */
+ R4K_SetWIRED(0);
+ R4K_TLBFlush(num_tlbentries);
+ R4K_SetWIRED(VMWIRED_ENTRIES);
+ MachSetPID(0);
+
+ Mips_SyncCache();
+
+ mips_cp0_status_write(0);
+ while (!aps_ready)
+ ;
+
+ mips_sync(); mips_sync();
+ /* Initialize curthread. */
+ KASSERT(PCPU_GET(idlethread) != NULL, ("no idle thread"));
+ PCPU_SET(curthread, PCPU_GET(idlethread));
+
+ mtx_lock_spin(&ap_boot_mtx);
+
+ smp_cpus++;
+
+ CTR1(KTR_SMP, "SMP: AP CPU #%d Launched", PCPU_GET(cpuid));
+
+ /* Build our map of 'other' CPUs. */
+ PCPU_SET(other_cpus, all_cpus & ~PCPU_GET(cpumask));
+
+ printf("SMP: AP CPU #%d Launched!\n", PCPU_GET(cpuid));
+
+ if (smp_cpus == mp_ncpus) {
+ smp_started = 1;
+ smp_active = 1;
+ }
+
+ mtx_unlock_spin(&ap_boot_mtx);
+
+ while (smp_started == 0)
+ ; /* nothing */
+ /* Enable Interrupt */
+ mips_cp0_status_write(SR_INT_ENAB);
+ /* ok, now grab sched_lock and enter the scheduler */
+ mtx_lock_spin(&sched_lock);
+
+ /*
+ * Correct spinlock nesting. The idle thread context that we are
+ * borrowing was created so that it would start out with a single
+ * spin lock (sched_lock) held in fork_trampoline(). Since we've
+ * explicitly acquired locks in this function, the nesting count
+ * is now 2 rather than 1. Since we are nested, calling
+ * spinlock_exit() will simply adjust the counts without allowing
+ * spin lock using code to interrupt us.
+ */
+ spinlock_exit();
+ KASSERT(curthread->td_md.md_spinlock_count == 1, ("invalid count"));
+
+ binuptime(PCPU_PTR(switchtime));
+ PCPU_SET(switchticks, ticks);
+
+ /* kick off the clock on this cpu */
+ mips_start_timer();
+ cpu_throw(NULL, choosethread()); /* doesn't return */
+
+ panic("scheduler returned us to %s", __func__);
+}
+
+static int
+smp_start_secondary(int cpuid)
+{
+ struct pcpu *pcpu;
+ int i;
+
+ if (bootverbose)
+ printf("smp_start_secondary: starting cpu %d\n", cpuid);
+
+ pcpu_init(&__pcpu[cpuid], cpuid, sizeof(struct pcpu));
+
+ if (bootverbose)
+ printf("smp_start_secondary: cpu %d started\n", cpuid);
+
+ return 1;
+}
+
+int
+cpu_mp_probe(void)
+{
+ int i, cpus;
+
+ /* XXX: Need to check for valid platforms here. */
+
+ boot_cpu_id = PCPU_GET(cpuid);
+ KASSERT(boot_cpu_id == 0, ("cpu_mp_probe() called on non-primary CPU"));
+ all_cpus = PCPU_GET(cpumask);
+ mp_ncpus = 1;
+
+ /* Make sure we have at least one secondary CPU. */
+ cpus = 0;
+ for (i = 0; i < MAXCPU; i++) {
+ cpus++;
+ }
+ return (cpus);
+}
+
+void
+cpu_mp_start(void)
+{
+ int i, cpuid;
+
+ mtx_init(&ap_boot_mtx, "ap boot", NULL, MTX_SPIN);
+
+ cpuid = 1;
+ for (i = 0; i < MAXCPU; i++) {
+
+ if (i == boot_cpu_id)
+ continue;
+ if (smp_start_secondary(i)) {
+ all_cpus |= (1 << cpuid);
+ mp_ncpus++;
+ cpuid++;
+ }
+ }
+ idle_mask |= CR_INT_IPI;
+ PCPU_SET(other_cpus, all_cpus & ~PCPU_GET(cpumask));
+}
+
+static void
+release_aps(void *dummy __unused)
+{
+ if (bootverbose && mp_ncpus > 1)
+ printf("%s: releasing secondary CPUs\n", __func__);
+ atomic_store_rel_int(&aps_ready, 1);
+
+ while (mp_ncpus > 1 && smp_started == 0)
+ ; /* nothing */
+}
+
+SYSINIT(start_aps, SI_SUB_SMP, SI_ORDER_FIRST, release_aps, NULL);
OpenPOWER on IntegriCloud