File: [cvs.NetBSD.org] / src / sys / arch / alpha / alpha / cpu.c (download)
Revision 1.103, Thu Oct 15 01:00:01 2020 UTC (3 years, 6 months ago) by thorpej
Branch: MAIN
CVS Tags: thorpej-futex-base, thorpej-futex, thorpej-cfargs-base, thorpej-cfargs Branch point for: thorpej-i2c-spi-conf
Changes since 1.102: +318 -64
lines
Expose a bunch of CPU details, including implementation version and
architecture extensions, via sysctl:
hw.cpu0.model = 21264A-0 (EV67)
hw.cpu0.major = 11
hw.cpu0.minor = 0
hw.cpu0.implver = 2
hw.cpu0.amask = 0x1307
hw.cpu0.bwx = 1
hw.cpu0.fix = 1
hw.cpu0.cix = 1
hw.cpu0.mvi = 1
hw.cpu0.pat = 1
hw.cpu0.pmi = 1
hw.cpu0.vax_fp = 1
hw.cpu0.ieee_fp = 1
hw.cpu0.primary_eligible = 1
hw.cpu0.primary = 1
hw.cpu0.cpu_id = 0
hw.cpu0.pcc_freq = 239990688
as well as some potentially interesting system-level variables:
machdep.cctr = 0
machdep.is_qemu = 1
Should address the basic concern in PR port-alpha/15835.
|
/* $NetBSD: cpu.c,v 1.103 2020/10/15 01:00:01 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 <sys/cdefs.h> /* RCS ID & Copyright macro defns */
__KERNEL_RCSID(0, "$NetBSD: cpu.c,v 1.103 2020/10/15 01:00:01 thorpej Exp $");
#include "opt_ddb.h"
#include "opt_multiprocessor.h"
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/device.h>
#include <sys/kmem.h>
#include <sys/proc.h>
#include <sys/atomic.h>
#include <sys/cpu.h>
#include <sys/sysctl.h>
#include <uvm/uvm_extern.h>
#include <machine/autoconf.h>
#include <machine/cpuvar.h>
#include <machine/rpb.h>
#include <machine/prom.h>
#include <machine/alpha.h>
struct cpu_info cpu_info_primary __cacheline_aligned = {
.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 __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)
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
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
* beacause 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.
*/
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 <ddb/db_output.h>
#include <machine/db_machdep.h>
/*
* 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 */