/* $NetBSD: cpu.c,v 1.96 2015/10/10 17:00:12 martin Exp $ */ /*- * Copyright (c) 1998, 1999, 2000, 2001 The NetBSD Foundation, Inc. * All rights reserved. * * This code is derived from software contributed to The NetBSD Foundation * by Jason R. Thorpe of the Numerical Aerospace Simulation Facility, * NASA Ames Research Center. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ /* * Copyright (c) 1994, 1995, 1996 Carnegie-Mellon University. * All rights reserved. * * Author: Chris G. Demetriou * * Permission to use, copy, modify and distribute this software and * its documentation is hereby granted, provided that both the copyright * notice and this permission notice appear in all copies of the * software, derivative works or modified versions, and any portions * thereof, and that both notices appear in supporting documentation. * * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS" * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND * FOR ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. * * Carnegie Mellon requests users of this software to return to * * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU * School of Computer Science * Carnegie Mellon University * Pittsburgh PA 15213-3890 * * any improvements or extensions that they make and grant Carnegie the * rights to redistribute these changes. */ #include /* RCS ID & Copyright macro defns */ __KERNEL_RCSID(0, "$NetBSD: cpu.c,v 1.96 2015/10/10 17:00:12 martin Exp $"); #include "opt_ddb.h" #include "opt_multiprocessor.h" #include #include #include #include #include #include #include #include #include #include #include #include #include struct cpu_info cpu_info_primary = { .ci_curlwp = &lwp0 }; struct cpu_info *cpu_info_list = &cpu_info_primary; #if defined(MULTIPROCESSOR) /* * Array of CPU info structures. Must be statically-allocated because * curproc, etc. are used early. */ struct cpu_info *cpu_info[ALPHA_MAXPROCS]; /* Bitmask of CPUs booted, currently running, and paused. */ volatile u_long cpus_booted; volatile u_long cpus_running; volatile u_long cpus_paused; void cpu_boot_secondary(struct cpu_info *); #endif /* MULTIPROCESSOR */ /* * The Implementation Version and the Architecture Mask must be * consistent across all CPUs in the system, so we set it for the * primary and announce the AMASK extensions if they exist. * * Note, we invert the AMASK so that if a bit is set, it means "has * extension". */ u_long cpu_implver, cpu_amask; /* Definition of the driver for autoconfig. */ static int cpumatch(device_t, cfdata_t, void *); static void cpuattach(device_t, device_t, void *); CFATTACH_DECL_NEW(cpu, sizeof(struct cpu_softc), cpumatch, cpuattach, NULL, NULL); static void cpu_announce_extensions(struct cpu_info *); extern struct cfdriver cpu_cd; static const char * const lcaminor[] = { "", "21066", "21066", "21068", "21068", "21066A", "21068A", 0 }; const struct cputable_struct { int cpu_major_code; const char *cpu_major_name; const char * const *cpu_minor_names; } cpunametable[] = { { PCS_PROC_EV3, "EV3", NULL }, { PCS_PROC_EV4, "21064", NULL }, { PCS_PROC_SIMULATION, "Sim", NULL }, { PCS_PROC_LCA4, "LCA", lcaminor }, { PCS_PROC_EV5, "21164", NULL }, { PCS_PROC_EV45, "21064A", NULL }, { PCS_PROC_EV56, "21164A", NULL }, { PCS_PROC_EV6, "21264", NULL }, { PCS_PROC_PCA56, "PCA56", NULL }, { PCS_PROC_PCA57, "PCA57", NULL }, { PCS_PROC_EV67, "21264A", NULL }, { PCS_PROC_EV68CB, "21264C", NULL }, { PCS_PROC_EV68AL, "21264B", NULL }, { PCS_PROC_EV68CX, "21264D", NULL }, }; /* * The following is an attempt to map out how booting secondary CPUs * works. * * As we find processors during the autoconfiguration sequence, all * processors have idle stacks and PCBs created for them, including * the primary (although the primary idles on lwp0's PCB until its * idle PCB is created). * * Right before calling uvm_scheduler(), main() calls, on lwp0's * context, cpu_boot_secondary_processors(). This is our key to * actually spin up the additional processor's we've found. We * run through our cpu_info[] array looking for secondary processors * with idle PCBs, and spin them up. * * The spinup involves switching the secondary processor to the * OSF/1 PALcode, setting the entry point to cpu_spinup_trampoline(), * and sending a "START" message to the secondary's console. * * Upon successful processor bootup, the cpu_spinup_trampoline will call * cpu_hatch(), which will print a message indicating that the processor * is running, and will set the "hatched" flag in its softc. At the end * of cpu_hatch() is a spin-forever loop; we do not yet attempt to schedule * anything on secondary CPUs. */ static int cpumatch(device_t parent, cfdata_t cfdata, void *aux) { struct mainbus_attach_args *ma = aux; /* make sure that we're looking for a CPU. */ if (strcmp(ma->ma_name, cpu_cd.cd_name) != 0) return (0); /* XXX CHECK SLOT? */ /* XXX CHECK PRIMARY? */ return (1); } static void cpuattach(device_t parent, device_t self, void *aux) { struct cpu_softc * const sc = device_private(self); struct mainbus_attach_args *ma = aux; int i; const char * const *s; struct pcs *p; uint32_t major, minor; struct cpu_info *ci; sc->sc_dev = self; p = LOCATE_PCS(hwrpb, ma->ma_slot); major = PCS_CPU_MAJORTYPE(p); minor = PCS_CPU_MINORTYPE(p); aprint_normal(": ID %d%s, ", ma->ma_slot, ma->ma_slot == hwrpb->rpb_primary_cpu_id ? " (primary)" : ""); for(i = 0; i < __arraycount(cpunametable); ++i) { if (cpunametable[i].cpu_major_code == major) { aprint_normal("%s-%d", cpunametable[i].cpu_major_name, minor); s = cpunametable[i].cpu_minor_names; for(i = 0; s && s[i]; ++i) { if (i == minor && strlen(s[i]) != 0) { aprint_normal(" (%s)", s[i]); goto recognized; } } goto recognized; } } aprint_error("UNKNOWN CPU TYPE (%d:%d)", major, minor); recognized: aprint_naive("\n"); aprint_normal("\n"); #ifdef DEBUG if (p->pcs_proc_var != 0) { bool needcomma = false; const char *vaxfp = ""; const char *ieeefp = ""; const char *pe = ""; if (p->pcs_proc_var & PCS_VAR_VAXFP) { vaxfp = "VAX FP support"; needcomma = true; } if (p->pcs_proc_var & PCS_VAR_IEEEFP) { ieeefp = ", IEEE FP support"; if (!needcomma) ieeefp += 2; needcomma = true; } if (p->pcs_proc_var & PCS_VAR_PE) { pe = ", Primary Eligible"; if (!needcomma) pe += 2; needcomma = true; } aprint_debug_dev(sc->sc_dev, "%s%s%s", vaxfp, ieeefp, pe); if (p->pcs_proc_var & PCS_VAR_RESERVED) aprint_debug("%sreserved bits: %#lx", needcomma ? ", " : "", p->pcs_proc_var & PCS_VAR_RESERVED); aprint_debug("\n"); } #endif if (ma->ma_slot > ALPHA_WHAMI_MAXID) { if (ma->ma_slot == hwrpb->rpb_primary_cpu_id) panic("cpu_attach: primary CPU ID too large"); aprint_error_dev(sc->sc_dev, "processor ID too large, ignoring\n"); return; } if (ma->ma_slot == hwrpb->rpb_primary_cpu_id) ci = &cpu_info_primary; else { ci = kmem_zalloc(sizeof(*ci), KM_SLEEP); } #if defined(MULTIPROCESSOR) cpu_info[ma->ma_slot] = ci; #endif ci->ci_cpuid = ma->ma_slot; ci->ci_softc = sc; ci->ci_pcc_freq = hwrpb->rpb_cc_freq; /* * Though we could (should?) attach the LCA cpus' PCI * bus here there is no good reason to do so, and * the bus attachment code is easier to understand * and more compact if done the 'normal' way. */ #if defined(MULTIPROCESSOR) /* * Make sure the processor is available for use. */ if ((p->pcs_flags & PCS_PA) == 0) { if (ma->ma_slot == hwrpb->rpb_primary_cpu_id) panic("cpu_attach: primary not available?!"); aprint_normal_dev(sc->sc_dev, "processor not available for use\n"); return; } /* Make sure the processor has valid PALcode. */ if ((p->pcs_flags & PCS_PV) == 0) { if (ma->ma_slot == hwrpb->rpb_primary_cpu_id) panic("cpu_attach: primary has invalid PALcode?!"); aprint_error_dev(sc->sc_dev, "PALcode not valid\n"); return; } #endif /* MULTIPROCESSOR */ /* * If we're the primary CPU, no more work to do; we're already * running! */ if (ma->ma_slot == hwrpb->rpb_primary_cpu_id) { cpu_announce_extensions(ci); #if defined(MULTIPROCESSOR) ci->ci_flags |= CPUF_PRIMARY|CPUF_RUNNING; atomic_or_ulong(&cpus_booted, (1UL << ma->ma_slot)); atomic_or_ulong(&cpus_running, (1UL << ma->ma_slot)); #endif /* MULTIPROCESSOR */ } else { #if defined(MULTIPROCESSOR) int error; error = mi_cpu_attach(ci); if (error != 0) { aprint_error_dev(sc->sc_dev, "mi_cpu_attach failed with %d\n", error); return; } /* * Boot the secondary processor. It will announce its * extensions, and then spin until we tell it to go * on its merry way. */ cpu_boot_secondary(ci); /* * Link the processor into the list. */ ci->ci_next = cpu_info_list->ci_next; cpu_info_list->ci_next = ci; #else /* ! MULTIPROCESSOR */ aprint_normal_dev(sc->sc_dev, "processor off-line; " "multiprocessor support not present in kernel\n"); #endif /* MULTIPROCESSOR */ } evcnt_attach_dynamic(&sc->sc_evcnt_clock, EVCNT_TYPE_INTR, NULL, device_xname(sc->sc_dev), "clock"); evcnt_attach_dynamic(&sc->sc_evcnt_device, EVCNT_TYPE_INTR, NULL, device_xname(sc->sc_dev), "device"); #if defined(MULTIPROCESSOR) alpha_ipi_init(ci); #endif } static void cpu_announce_extensions(struct cpu_info *ci) { u_long implver, amask = 0; char bits[64]; implver = alpha_implver(); if (implver >= ALPHA_IMPLVER_EV5) amask = (~alpha_amask(ALPHA_AMASK_ALL)) & ALPHA_AMASK_ALL; if (ci->ci_cpuid == hwrpb->rpb_primary_cpu_id) { cpu_implver = implver; cpu_amask = amask; } else { if (implver < cpu_implver) aprint_error_dev(ci->ci_softc->sc_dev, "WARNING: IMPLVER %lu < %lu\n", implver, cpu_implver); /* * Cap the system architecture mask to the intersection * of features supported by all processors in the system. */ cpu_amask &= amask; } if (amask) { snprintb(bits, sizeof(bits), ALPHA_AMASK_BITS, cpu_amask); aprint_normal_dev(ci->ci_softc->sc_dev, "Architecture extensions: %s\n", bits); } } #if defined(MULTIPROCESSOR) void cpu_boot_secondary_processors(void) { struct cpu_info *ci; u_long i; bool did_patch = false; for (i = 0; i < ALPHA_MAXPROCS; i++) { ci = cpu_info[i]; if (ci == NULL || ci->ci_data.cpu_idlelwp == NULL) continue; if (ci->ci_flags & CPUF_PRIMARY) continue; if ((cpus_booted & (1UL << i)) == 0) continue; /* Patch MP-criticial kernel routines. */ if (did_patch == false) { alpha_patch(true); did_patch = true; } /* * Launch the processor. */ atomic_or_ulong(&ci->ci_flags, CPUF_RUNNING); atomic_or_ulong(&cpus_running, (1U << i)); } } void cpu_boot_secondary(struct cpu_info *ci) { long timeout; struct pcs *pcsp, *primary_pcsp; struct pcb *pcb; u_long cpumask; pcb = lwp_getpcb(ci->ci_data.cpu_idlelwp); primary_pcsp = LOCATE_PCS(hwrpb, hwrpb->rpb_primary_cpu_id); pcsp = LOCATE_PCS(hwrpb, ci->ci_cpuid); cpumask = (1UL << ci->ci_cpuid); /* * Set up the PCS's HWPCB to match ours. */ memcpy(pcsp->pcs_hwpcb, &pcb->pcb_hw, sizeof(pcb->pcb_hw)); /* * Set up the HWRPB to restart the secondary processor * with our spin-up trampoline. */ hwrpb->rpb_restart = (uint64_t) cpu_spinup_trampoline; hwrpb->rpb_restart_val = (uint64_t) ci; hwrpb->rpb_checksum = hwrpb_checksum(); /* * Configure the CPU to start in OSF/1 PALcode by copying * the primary CPU's PALcode revision info to the secondary * CPUs PCS. */ memcpy(&pcsp->pcs_pal_rev, &primary_pcsp->pcs_pal_rev, sizeof(pcsp->pcs_pal_rev)); pcsp->pcs_flags |= (PCS_CV|PCS_RC); pcsp->pcs_flags &= ~PCS_BIP; /* Make sure the secondary console sees all this. */ alpha_mb(); /* Send a "START" command to the secondary CPU's console. */ if (cpu_iccb_send(ci->ci_cpuid, "START\r\n")) { aprint_error_dev(ci->ci_softc->sc_dev, "unable to issue `START' command\n"); return; } /* Wait for the processor to boot. */ for (timeout = 10000; timeout != 0; timeout--) { alpha_mb(); if (pcsp->pcs_flags & PCS_BIP) break; delay(1000); } if (timeout == 0) aprint_error_dev(ci->ci_softc->sc_dev, "processor failed to boot\n"); /* * ...and now wait for verification that it's running kernel * code. */ for (timeout = 10000; timeout != 0; timeout--) { alpha_mb(); if (cpus_booted & cpumask) break; delay(1000); } if (timeout == 0) aprint_error_dev(ci->ci_softc->sc_dev, "processor failed to hatch\n"); } void cpu_pause_resume(u_long cpu_id, int pause) { u_long cpu_mask = (1UL << cpu_id); if (pause) { atomic_or_ulong(&cpus_paused, cpu_mask); alpha_send_ipi(cpu_id, ALPHA_IPI_PAUSE); } else atomic_and_ulong(&cpus_paused, ~cpu_mask); } void cpu_pause_resume_all(int pause) { struct cpu_info *ci, *self = curcpu(); CPU_INFO_ITERATOR cii; for (CPU_INFO_FOREACH(cii, ci)) { if (ci == self) continue; cpu_pause_resume(ci->ci_cpuid, pause); } } void cpu_halt(void) { struct cpu_info *ci = curcpu(); u_long cpu_id = cpu_number(); struct pcs *pcsp = LOCATE_PCS(hwrpb, cpu_id); aprint_normal_dev(ci->ci_softc->sc_dev, "shutting down...\n"); pcsp->pcs_flags &= ~(PCS_RC | PCS_HALT_REQ); pcsp->pcs_flags |= PCS_HALT_STAY_HALTED; atomic_and_ulong(&cpus_running, ~(1UL << cpu_id)); atomic_and_ulong(&cpus_booted, ~(1U << cpu_id)); alpha_pal_halt(); /* NOTREACHED */ } void cpu_hatch(struct cpu_info *ci) { u_long cpu_id = cpu_number(); u_long cpumask = (1UL << cpu_id); /* Mark the kernel pmap active on this processor. */ atomic_or_ulong(&pmap_kernel()->pm_cpus, cpumask); /* Initialize trap vectors for this processor. */ trap_init(); /* Yahoo! We're running kernel code! Announce it! */ cpu_announce_extensions(ci); atomic_or_ulong(&cpus_booted, cpumask); /* * Spin here until we're told we can start. */ while ((cpus_running & cpumask) == 0) /* spin */ ; /* * Invalidate the TLB and sync the I-stream before we * jump into the kernel proper. We have to do this * beacause we haven't been getting IPIs while we've * been spinning. */ ALPHA_TBIA(); alpha_pal_imb(); cc_calibrate_cpu(ci); } int cpu_iccb_send(long cpu_id, const char *msg) { struct pcs *pcsp = LOCATE_PCS(hwrpb, cpu_id); int timeout; u_long cpumask = (1UL << cpu_id); /* Wait for the ICCB to become available. */ for (timeout = 10000; timeout != 0; timeout--) { alpha_mb(); if ((hwrpb->rpb_rxrdy & cpumask) == 0) break; delay(1000); } if (timeout == 0) return (EIO); /* * Copy the message into the ICCB, and tell the secondary console * that it's there. */ strcpy(pcsp->pcs_iccb.iccb_rxbuf, msg); pcsp->pcs_iccb.iccb_rxlen = strlen(msg); atomic_or_ulong(&hwrpb->rpb_rxrdy, cpumask); membar_sync(); /* Wait for the message to be received. */ for (timeout = 10000; timeout != 0; timeout--) { alpha_mb(); if ((hwrpb->rpb_rxrdy & cpumask) == 0) break; delay(1000); } if (timeout == 0) return (EIO); return (0); } void cpu_iccb_receive(void) { #if 0 /* Don't bother... we don't get any important messages anyhow. */ uint64_t txrdy; char *cp1, *cp2, buf[80]; struct pcs *pcsp; u_int cnt; long cpu_id; txrdy = hwrpb->rpb_txrdy; for (cpu_id = 0; cpu_id < hwrpb->rpb_pcs_cnt; cpu_id++) { if (txrdy & (1UL << cpu_id)) { pcsp = LOCATE_PCS(hwrpb, cpu_id); printf("Inter-console message from CPU %lu " "HALT REASON = 0x%lx, FLAGS = 0x%lx\n", cpu_id, pcsp->pcs_halt_reason, pcsp->pcs_flags); cnt = pcsp->pcs_iccb.iccb_txlen; if (cnt >= 80) { printf("Malformed inter-console message\n"); continue; } cp1 = pcsp->pcs_iccb.iccb_txbuf; cp2 = buf; while (cnt--) { if (*cp1 != '\r' && *cp1 != '\n') *cp2++ = *cp1; cp1++; } *cp2 = '\0'; printf("Message from CPU %lu: %s\n", cpu_id, buf); } } #endif /* 0 */ hwrpb->rpb_txrdy = 0; alpha_mb(); } #if defined(DDB) #include #include /* * Dump CPU information from DDB. */ void cpu_debug_dump(void) { struct cpu_info *ci; CPU_INFO_ITERATOR cii; db_printf("addr dev id flags ipis curproc\n"); for (CPU_INFO_FOREACH(cii, ci)) { db_printf("%p %s %lu %lx %lx %p\n", ci, device_xname(ci->ci_softc->sc_dev), ci->ci_cpuid, ci->ci_flags, ci->ci_ipis, ci->ci_curlwp); } } #endif /* DDB */ #endif /* MULTIPROCESSOR */