/* $NetBSD: cpu.c,v 1.108 2024/03/06 07:22:45 thorpej Exp $ */ /*- * Copyright (c) 1998, 1999, 2000, 2001, 2020 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.108 2024/03/06 07:22:45 thorpej Exp $"); #include "opt_ddb.h" #include "opt_multiprocessor.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include struct cpu_info cpu_info_primary __cacheline_aligned = { .ci_curlwp = &lwp0, .ci_flags = CPUF_PRIMARY|CPUF_RUNNING, }; struct cpu_info *cpu_info_list __read_mostly = &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 __read_mostly; volatile u_long cpus_running __read_mostly; volatile u_long cpus_paused __read_mostly; void cpu_boot_secondary(struct cpu_info *); #endif /* MULTIPROCESSOR */ static void cpu_idle_default(void) { /* * Default is to do nothing. Platform code can overwrite * as needed. */ } void cpu_idle_wtint(void) { /* * Some PALcode versions implement the WTINT call to idle * in a low power mode. */ alpha_pal_wtint(0); } void (*cpu_idle_fn)(void) __read_mostly = cpu_idle_default; /* * 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 __read_mostly; u_long cpu_amask __read_mostly; /* 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", NULL }; const struct cputable_struct { const char *cpu_evname; const char *cpu_major_name; const char * const *cpu_minor_names; } cpunametable[] = { [PCS_PROC_EV3] ={ "EV3", NULL, NULL }, [PCS_PROC_EV4] ={ "EV4", "21064", NULL }, [PCS_PROC_SIMULATION]={ "Sim", NULL, NULL }, [PCS_PROC_LCA4] ={ "LCA4", NULL, lcaminor }, [PCS_PROC_EV5] ={ "EV5", "21164", NULL }, [PCS_PROC_EV45] ={ "EV45", "21064A", NULL }, [PCS_PROC_EV56] ={ "EV56", "21164A", NULL }, [PCS_PROC_EV6] ={ "EV6", "21264", NULL }, [PCS_PROC_PCA56] ={ "PCA56", "21164PC", NULL }, [PCS_PROC_PCA57] ={ "PCA57", "21164PC"/*XXX*/,NULL }, [PCS_PROC_EV67] ={ "EV67", "21264A", NULL }, [PCS_PROC_EV68CB] ={ "EV68CB", "21264C", NULL }, [PCS_PROC_EV68AL] ={ "EV68AL", "21264B", NULL }, [PCS_PROC_EV68CX] ={ "EV68CX", "21264D", NULL }, [PCS_PROC_EV7] ={ "EV7", "21364", NULL }, [PCS_PROC_EV79] ={ "EV79", NULL, NULL }, [PCS_PROC_EV69] ={ "EV69", NULL, NULL }, }; static bool cpu_description(const struct cpu_softc * const sc, char * const buf, size_t const buflen) { const char * const *s; const char *ev; int i; const uint32_t major = sc->sc_major_type; const uint32_t minor = sc->sc_minor_type; if (major < __arraycount(cpunametable) && (ev = cpunametable[major].cpu_evname) != NULL) { s = cpunametable[major].cpu_minor_names; for (i = 0; s != NULL && s[i] != NULL; i++) { if (i == minor && strlen(s[i]) != 0) { break; } } if (s == NULL || s[i] == NULL) { s = &cpunametable[major].cpu_major_name; i = 0; if (s[i] == NULL) { s = NULL; } } /* * Example strings: * * Sim-0 * 21068-3 (LCA4) [uses minor table] * 21264C-5 (EV68CB) * 21164PC-1 (PCA56) */ if (s != NULL) { snprintf(buf, buflen, "%s-%d (%s)", s[i], minor, ev); } else { snprintf(buf, buflen, "%s-%d", ev, minor); } return true; } snprintf(buf, buflen, "UNKNOWN CPU TYPE (%u:%u)", major, minor); return false; } static int cpu_sysctl_model(SYSCTLFN_ARGS) { struct sysctlnode node = *rnode; const struct cpu_softc * const sc = node.sysctl_data; char model[32]; cpu_description(sc, model, sizeof(model)); node.sysctl_data = model; return sysctl_lookup(SYSCTLFN_CALL(&node)); } static int cpu_sysctl_amask_bit(SYSCTLFN_ARGS, unsigned long const bit) { struct sysctlnode node = *rnode; const struct cpu_softc * const sc = node.sysctl_data; bool result = (sc->sc_amask & bit) ? true : false; node.sysctl_data = &result; return sysctl_lookup(SYSCTLFN_CALL(&node)); } static int cpu_sysctl_bwx(SYSCTLFN_ARGS) { return cpu_sysctl_amask_bit(SYSCTLFN_CALL(rnode), ALPHA_AMASK_BWX); } static int cpu_sysctl_fix(SYSCTLFN_ARGS) { return cpu_sysctl_amask_bit(SYSCTLFN_CALL(rnode), ALPHA_AMASK_FIX); } static int cpu_sysctl_cix(SYSCTLFN_ARGS) { return cpu_sysctl_amask_bit(SYSCTLFN_CALL(rnode), ALPHA_AMASK_CIX); } static int cpu_sysctl_mvi(SYSCTLFN_ARGS) { return cpu_sysctl_amask_bit(SYSCTLFN_CALL(rnode), ALPHA_AMASK_MVI); } static int cpu_sysctl_pat(SYSCTLFN_ARGS) { return cpu_sysctl_amask_bit(SYSCTLFN_CALL(rnode), ALPHA_AMASK_PAT); } static int cpu_sysctl_pmi(SYSCTLFN_ARGS) { return cpu_sysctl_amask_bit(SYSCTLFN_CALL(rnode), ALPHA_AMASK_PMI); } static int cpu_sysctl_primary(SYSCTLFN_ARGS) { struct sysctlnode node = *rnode; const struct cpu_softc * const sc = node.sysctl_data; bool result = CPU_IS_PRIMARY(sc->sc_ci); node.sysctl_data = &result; return sysctl_lookup(SYSCTLFN_CALL(&node)); } /* * 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); const struct mainbus_attach_args * const ma = aux; struct cpu_info *ci; char model[32]; const bool primary = ma->ma_slot == hwrpb->rpb_primary_cpu_id; sc->sc_dev = self; const struct pcs * const p = LOCATE_PCS(hwrpb, ma->ma_slot); sc->sc_major_type = PCS_CPU_MAJORTYPE(p); sc->sc_minor_type = PCS_CPU_MINORTYPE(p); const bool recognized = cpu_description(sc, model, sizeof(model)); aprint_normal(": ID %d%s, ", ma->ma_slot, primary ? " (primary)" : ""); if (recognized) { aprint_normal("%s", model); } else { aprint_error("%s", model); } aprint_naive("\n"); aprint_normal("\n"); 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) { sc->sc_vax_fp = true; vaxfp = "VAX FP support"; needcomma = true; } if (p->pcs_proc_var & PCS_VAR_IEEEFP) { sc->sc_ieee_fp = true; ieeefp = ", IEEE FP support"; if (!needcomma) ieeefp += 2; needcomma = true; } if (p->pcs_proc_var & PCS_VAR_PE) { sc->sc_primary_eligible = true; 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"); } if (ma->ma_slot > ALPHA_WHAMI_MAXID) { if (primary) panic("cpu_attach: primary CPU ID too large"); aprint_error_dev(sc->sc_dev, "processor ID too large, ignoring\n"); return; } if (primary) { ci = &cpu_info_primary; } else { /* * kmem_zalloc() will guarante cache line alignment for * all allocations >= CACHE_LINE_SIZE. */ ci = kmem_zalloc(sizeof(*ci), KM_SLEEP); KASSERT(((uintptr_t)ci & (CACHE_LINE_SIZE - 1)) == 0); } #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; sc->sc_ci = ci; #if defined(MULTIPROCESSOR) /* * Make sure the processor is available for use. */ if ((p->pcs_flags & PCS_PA) == 0) { if (primary) 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 (primary) 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 (primary) { cpu_announce_extensions(ci); #if defined(MULTIPROCESSOR) KASSERT(ci->ci_flags & CPUF_PRIMARY); KASSERT(ci->ci_flags & 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 struct sysctllog **log = &sc->sc_sysctllog; const struct sysctlnode *rnode, *cnode; int error; error = sysctl_createv(log, 0, NULL, &rnode, CTLFLAG_PERMANENT, CTLTYPE_NODE, device_xname(sc->sc_dev), SYSCTL_DESCR("cpu properties"), NULL, 0, NULL, 0, CTL_HW, CTL_CREATE, CTL_EOL); if (error) return; error = sysctl_createv(log, 0, &rnode, &cnode, CTLFLAG_PERMANENT, CTLTYPE_STRING, "model", SYSCTL_DESCR("cpu model"), cpu_sysctl_model, 0, (void *)sc, 0, CTL_CREATE, CTL_EOL); if (error) return; error = sysctl_createv(log, 0, &rnode, &cnode, CTLFLAG_PERMANENT, CTLTYPE_INT, "major", SYSCTL_DESCR("cpu major type"), NULL, 0, &sc->sc_major_type, 0, CTL_CREATE, CTL_EOL); if (error) return; error = sysctl_createv(log, 0, &rnode, &cnode, CTLFLAG_PERMANENT, CTLTYPE_INT, "minor", SYSCTL_DESCR("cpu minor type"), NULL, 0, &sc->sc_minor_type, 0, CTL_CREATE, CTL_EOL); if (error) return; error = sysctl_createv(log, 0, &rnode, &cnode, CTLFLAG_PERMANENT, CTLTYPE_LONG, "implver", SYSCTL_DESCR("cpu implementation version"), NULL, 0, &sc->sc_implver, 0, CTL_CREATE, CTL_EOL); if (error) return; error = sysctl_createv(log, 0, &rnode, &cnode, CTLFLAG_PERMANENT|CTLFLAG_HEX, CTLTYPE_LONG, "amask", SYSCTL_DESCR("architecture extensions mask"), NULL, 0, &sc->sc_amask, 0, CTL_CREATE, CTL_EOL); if (error) return; error = sysctl_createv(log, 0, &rnode, &cnode, CTLFLAG_PERMANENT, CTLTYPE_BOOL, "bwx", SYSCTL_DESCR("cpu supports BWX extension"), cpu_sysctl_bwx, 0, (void *)sc, 0, CTL_CREATE, CTL_EOL); if (error) return; error = sysctl_createv(log, 0, &rnode, &cnode, CTLFLAG_PERMANENT, CTLTYPE_BOOL, "fix", SYSCTL_DESCR("cpu supports FIX extension"), cpu_sysctl_fix, 0, (void *)sc, 0, CTL_CREATE, CTL_EOL); if (error) return; error = sysctl_createv(log, 0, &rnode, &cnode, CTLFLAG_PERMANENT, CTLTYPE_BOOL, "cix", SYSCTL_DESCR("cpu supports CIX extension"), cpu_sysctl_cix, 0, (void *)sc, 0, CTL_CREATE, CTL_EOL); if (error) return; error = sysctl_createv(log, 0, &rnode, &cnode, CTLFLAG_PERMANENT, CTLTYPE_BOOL, "mvi", SYSCTL_DESCR("cpu supports MVI extension"), cpu_sysctl_mvi, 0, (void *)sc, 0, CTL_CREATE, CTL_EOL); if (error) return; error = sysctl_createv(log, 0, &rnode, &cnode, CTLFLAG_PERMANENT, CTLTYPE_BOOL, "pat", SYSCTL_DESCR("cpu supports PAT extension"), cpu_sysctl_pat, 0, (void *)sc, 0, CTL_CREATE, CTL_EOL); if (error) return; error = sysctl_createv(log, 0, &rnode, &cnode, CTLFLAG_PERMANENT, CTLTYPE_BOOL, "pmi", SYSCTL_DESCR("cpu supports PMI extension"), cpu_sysctl_pmi, 0, (void *)sc, 0, CTL_CREATE, CTL_EOL); if (error) return; error = sysctl_createv(log, 0, &rnode, &cnode, CTLFLAG_PERMANENT, CTLTYPE_BOOL, "vax_fp", SYSCTL_DESCR("cpu supports VAX FP"), NULL, 0, &sc->sc_vax_fp, 0, CTL_CREATE, CTL_EOL); if (error) return; error = sysctl_createv(log, 0, &rnode, &cnode, CTLFLAG_PERMANENT, CTLTYPE_BOOL, "ieee_fp", SYSCTL_DESCR("cpu supports IEEE FP"), NULL, 0, &sc->sc_ieee_fp, 0, CTL_CREATE, CTL_EOL); if (error) return; error = sysctl_createv(log, 0, &rnode, &cnode, CTLFLAG_PERMANENT, CTLTYPE_BOOL, "primary_eligible", SYSCTL_DESCR("cpu is primary-eligible"), NULL, 0, &sc->sc_primary_eligible, 0, CTL_CREATE, CTL_EOL); if (error) return; error = sysctl_createv(log, 0, &rnode, &cnode, CTLFLAG_PERMANENT, CTLTYPE_BOOL, "primary", SYSCTL_DESCR("cpu is the primary cpu"), cpu_sysctl_primary, 0, (void *)sc, 0, CTL_CREATE, CTL_EOL); if (error) return; error = sysctl_createv(log, 0, &rnode, &cnode, CTLFLAG_PERMANENT, CTLTYPE_LONG, "cpu_id", SYSCTL_DESCR("hardware cpu ID"), NULL, 0, &sc->sc_ci->ci_cpuid, 0, CTL_CREATE, CTL_EOL); if (error) return; error = sysctl_createv(log, 0, &rnode, &cnode, CTLFLAG_PERMANENT, CTLTYPE_LONG, "pcc_freq", SYSCTL_DESCR("PCC frequency"), NULL, 0, &sc->sc_ci->ci_pcc_freq, 0, CTL_CREATE, CTL_EOL); if (error) return; } 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; ci->ci_softc->sc_implver = implver; ci->ci_softc->sc_amask = amask; 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, 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 (CPU_IS_PRIMARY(ci)) 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); /* pmap initialization for this processor. */ pmap_init_cpu(ci); /* 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 * because we haven't been getting IPIs while we've * been spinning. */ ALPHA_TBIA(); alpha_pal_imb(); if (alpha_use_cctr) { cc_init_secondary(ci); } cpu_initclocks_secondary(); } 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. Ensure the buffer is initialized before we * set the rxrdy bits, as a store-release. */ strcpy(pcsp->pcs_iccb.iccb_rxbuf, msg); pcsp->pcs_iccb.iccb_rxlen = strlen(msg); membar_release(); atomic_or_ulong(&hwrpb->rpb_rxrdy, cpumask); /* 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 */