summaryrefslogtreecommitdiff
path: root/usr/src/uts/sun4u/ngdr/io/dr_cpu.c
diff options
context:
space:
mode:
Diffstat (limited to 'usr/src/uts/sun4u/ngdr/io/dr_cpu.c')
-rw-r--r--usr/src/uts/sun4u/ngdr/io/dr_cpu.c934
1 files changed, 934 insertions, 0 deletions
diff --git a/usr/src/uts/sun4u/ngdr/io/dr_cpu.c b/usr/src/uts/sun4u/ngdr/io/dr_cpu.c
new file mode 100644
index 0000000000..d9f1f17247
--- /dev/null
+++ b/usr/src/uts/sun4u/ngdr/io/dr_cpu.c
@@ -0,0 +1,934 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * CPU support routines for DR
+ */
+
+#include <sys/note.h>
+#include <sys/debug.h>
+#include <sys/types.h>
+#include <sys/errno.h>
+#include <sys/cred.h>
+#include <sys/dditypes.h>
+#include <sys/devops.h>
+#include <sys/modctl.h>
+#include <sys/poll.h>
+#include <sys/conf.h>
+#include <sys/ddi.h>
+#include <sys/sunddi.h>
+#include <sys/sunndi.h>
+#include <sys/ndi_impldefs.h>
+#include <sys/stat.h>
+#include <sys/kmem.h>
+#include <sys/processor.h>
+#include <sys/cpuvar.h>
+#include <sys/mem_config.h>
+#include <sys/promif.h>
+#include <sys/x_call.h>
+#include <sys/cpu_sgnblk_defs.h>
+#include <sys/membar.h>
+#include <sys/stack.h>
+#include <sys/sysmacros.h>
+#include <sys/machsystm.h>
+#include <sys/spitregs.h>
+
+#include <sys/archsystm.h>
+#include <vm/hat_sfmmu.h>
+#include <sys/pte.h>
+#include <sys/mmu.h>
+#include <sys/x_call.h>
+#include <sys/cpu_module.h>
+#include <sys/cheetahregs.h>
+
+#include <sys/autoconf.h>
+#include <sys/cmn_err.h>
+
+#include <sys/dr.h>
+#include <sys/dr_util.h>
+
+#ifdef _STARFIRE
+#include <sys/starfire.h>
+extern struct cpu *SIGBCPU;
+#else
+/* for the DR*INTERNAL_ERROR macros. see sys/dr.h. */
+static char *dr_ie_fmt = "dr_cpu.c %d";
+#endif /* _STARFIRE */
+
+int
+dr_cpu_unit_is_sane(dr_board_t *bp, dr_cpu_unit_t *cp)
+{
+#ifdef DEBUG
+ processorid_t cpuid;
+
+ /*
+ * cpuid and unit number should never be different
+ * than they were at discovery/connect time
+ */
+ ASSERT(drmach_cpu_get_id(cp->sbc_cm.sbdev_id, &cpuid) == 0);
+
+ ASSERT(cp->sbc_cm.sbdev_bp == bp);
+ ASSERT(cp->sbc_cm.sbdev_type == SBD_COMP_CPU);
+ ASSERT(cp->sbc_cpu_id == cpuid);
+#else
+ _NOTE(ARGUNUSED(bp))
+ _NOTE(ARGUNUSED(cp))
+#endif
+
+ return (1);
+}
+
+static int
+dr_errno2ecode(int error)
+{
+ int rv;
+
+ switch (error) {
+ case EBUSY:
+ rv = ESBD_BUSY;
+ break;
+ case EINVAL:
+ rv = ESBD_INVAL;
+ break;
+ case EALREADY:
+ rv = ESBD_ALREADY;
+ break;
+ case ENODEV:
+ rv = ESBD_NODEV;
+ break;
+ case ENOMEM:
+ rv = ESBD_NOMEM;
+ break;
+ default:
+ rv = ESBD_INVAL;
+ }
+
+ return (rv);
+}
+
+static void
+dr_cpu_set_prop(dr_cpu_unit_t *cp)
+{
+ sbd_error_t *err;
+ dev_info_t *dip;
+ uint64_t clock_freq;
+ int ecache_size = 0;
+ char *cache_str = NULL;
+
+ err = drmach_get_dip(cp->sbc_cm.sbdev_id, &dip);
+ if (err) {
+ DRERR_SET_C(&cp->sbc_cm.sbdev_error, &err);
+ return;
+ }
+
+ if (dip == NULL) {
+#ifndef _STARFIRE
+ /*
+ * Do not report an error on Starfire since
+ * the dip will not be created until after
+ * the CPU has been configured.
+ */
+ DR_DEV_INTERNAL_ERROR(&cp->sbc_cm);
+#endif /* !_STARFIRE */
+ return;
+ }
+
+ /* read in the CPU speed */
+ clock_freq = (unsigned int)ddi_prop_get_int(DDI_DEV_T_ANY, dip,
+ DDI_PROP_DONTPASS, "clock-frequency", 0);
+
+ ASSERT(clock_freq != 0);
+
+ /*
+ * The ecache property string is not the same
+ * for all CPU implementations.
+ */
+ switch (cp->sbc_cpu_impl) {
+ case BLACKBIRD_IMPL:
+ case CHEETAH_IMPL:
+ case CHEETAH_PLUS_IMPL:
+ cache_str = "ecache-size";
+ break;
+ case JAGUAR_IMPL:
+ cache_str = "l2-cache-size";
+ break;
+ case PANTHER_IMPL:
+ cache_str = "l3-cache-size";
+ break;
+ default:
+ cmn_err(CE_WARN, "Unknown cpu implementation=0x%x",
+ cp->sbc_cpu_impl);
+ ASSERT(0);
+ break;
+ }
+
+ if (cache_str != NULL) {
+ /* read in the ecache size */
+ ecache_size = ddi_prop_get_int(DDI_DEV_T_ANY, dip,
+ DDI_PROP_DONTPASS, cache_str, 0);
+ }
+
+ ASSERT(ecache_size != 0);
+
+ /* convert to the proper units */
+ cp->sbc_speed = (clock_freq + 500000) / 1000000;
+ cp->sbc_ecache = ecache_size / (1024 * 1024);
+}
+
+void
+dr_init_cpu_unit(dr_cpu_unit_t *cp)
+{
+ sbd_error_t *err;
+ dr_state_t new_state;
+ int cpuid;
+ int impl;
+
+ if (DR_DEV_IS_ATTACHED(&cp->sbc_cm)) {
+ new_state = DR_STATE_CONFIGURED;
+ cp->sbc_cm.sbdev_cond = SBD_COND_OK;
+ } else if (DR_DEV_IS_PRESENT(&cp->sbc_cm)) {
+ new_state = DR_STATE_CONNECTED;
+ cp->sbc_cm.sbdev_cond = SBD_COND_OK;
+ } else {
+ new_state = DR_STATE_EMPTY;
+ cp->sbc_cm.sbdev_cond = SBD_COND_UNKNOWN;
+ }
+
+ if (DR_DEV_IS_PRESENT(&cp->sbc_cm)) {
+ err = drmach_cpu_get_id(cp->sbc_cm.sbdev_id, &cpuid);
+ if (err) {
+ DRERR_SET_C(&cp->sbc_cm.sbdev_error, &err);
+ new_state = DR_STATE_FATAL;
+ goto done;
+ }
+
+ err = drmach_cpu_get_impl(cp->sbc_cm.sbdev_id, &impl);
+ if (err) {
+ DRERR_SET_C(&cp->sbc_cm.sbdev_error, &err);
+ new_state = DR_STATE_FATAL;
+ goto done;
+ }
+ } else {
+ cp->sbc_cpu_id = -1;
+ cp->sbc_cpu_impl = -1;
+ goto done;
+ }
+
+ cp->sbc_cpu_id = cpuid;
+ cp->sbc_cpu_impl = impl;
+
+ /* if true at init time, it must always be true */
+ ASSERT(dr_cpu_unit_is_sane(cp->sbc_cm.sbdev_bp, cp));
+
+ mutex_enter(&cpu_lock);
+ if ((cpuid >= 0) && cpu[cpuid])
+ cp->sbc_cpu_flags = cpu[cpuid]->cpu_flags;
+ else
+ cp->sbc_cpu_flags = P_OFFLINE | P_POWEROFF;
+ mutex_exit(&cpu_lock);
+
+ dr_cpu_set_prop(cp);
+
+done:
+ /* delay transition until fully initialized */
+ dr_device_transition(&cp->sbc_cm, new_state);
+}
+
+int
+dr_pre_attach_cpu(dr_handle_t *hp, dr_common_unit_t **devlist, int devnum)
+{
+ int i;
+ int curr_cpu;
+ int next_cpu;
+ static fn_t f = "dr_pre_attach_cpu";
+
+ PR_CPU("%s...\n", f);
+
+ for (next_cpu = 0, i = 0; i < devnum; i++) {
+ dr_cpu_unit_t *up = (dr_cpu_unit_t *)devlist[i];
+
+ ASSERT(dr_cpu_unit_is_sane(hp->h_bd, up));
+
+ /*
+ * Print a console message for each attachment
+ * point. For CMP devices, this means that only
+ * one message should be printed, no matter how
+ * many cores are actually present.
+ */
+ curr_cpu = DR_UNUM2SBD_UNUM(up->sbc_cm.sbdev_unum);
+ if (curr_cpu >= next_cpu) {
+ cmn_err(CE_CONT, "OS configure %s",
+ up->sbc_cm.sbdev_path);
+ next_cpu = curr_cpu + 1;
+ }
+
+ if (up->sbc_cm.sbdev_state == DR_STATE_UNCONFIGURED) {
+ /*
+ * If we're coming from the UNCONFIGURED
+ * state then the cpu's sigblock will
+ * still be mapped in. Need to unmap it
+ * before continuing with attachment.
+ */
+ PR_CPU("%s: unmapping sigblk for cpu %d\n",
+ f, up->sbc_cpu_id);
+
+ CPU_SGN_MAPOUT(up->sbc_cpu_id);
+ }
+ }
+
+ /*
+ * Block out status threads while creating
+ * devinfo tree branches
+ */
+ dr_lock_status(hp->h_bd);
+ mutex_enter(&cpu_lock);
+
+ return (0);
+}
+
+/*ARGSUSED*/
+void
+dr_attach_cpu(dr_handle_t *hp, dr_common_unit_t *cp)
+{
+ sbd_error_t *err;
+ processorid_t cpuid;
+ int rv;
+
+ ASSERT(MUTEX_HELD(&cpu_lock));
+
+ err = drmach_configure(cp->sbdev_id, 0);
+ if (err) {
+ DRERR_SET_C(&cp->sbdev_error, &err);
+ return;
+ }
+
+ err = drmach_cpu_get_id(cp->sbdev_id, &cpuid);
+ if (err) {
+ DRERR_SET_C(&cp->sbdev_error, &err);
+
+ err = drmach_unconfigure(cp->sbdev_id, DRMACH_DEVI_REMOVE);
+ if (err)
+ sbd_err_clear(&err);
+ } else if ((rv = cpu_configure(cpuid)) != 0) {
+ dr_dev_err(CE_WARN, cp, dr_errno2ecode(rv));
+ err = drmach_unconfigure(cp->sbdev_id,
+ DRMACH_DEVI_REMOVE);
+ if (err)
+ sbd_err_clear(&err);
+ }
+}
+
+/*
+ * dr_post_attach_cpu
+ *
+ * sbd error policy: Does not stop on error. Processes all units in list.
+ */
+int
+dr_post_attach_cpu(dr_handle_t *hp, dr_common_unit_t **devlist, int devnum)
+{
+ int i;
+ int errflag = 0;
+ static fn_t f = "dr_post_attach_cpu";
+
+ PR_CPU("%s...\n", f);
+ hp->h_ndi = 0;
+
+ /* Startup and online newly-attached CPUs */
+ for (i = 0; i < devnum; i++) {
+ dr_cpu_unit_t *up = (dr_cpu_unit_t *)devlist[i];
+ struct cpu *cp;
+
+ ASSERT(dr_cpu_unit_is_sane(hp->h_bd, up));
+
+ cp = cpu_get(up->sbc_cpu_id);
+ if (cp == NULL) {
+ cmn_err(CE_WARN, "%s: cpu_get failed for cpu %d",
+ f, up->sbc_cpu_id);
+ continue;
+ }
+
+ if (cpu_is_poweredoff(cp)) {
+ if (cpu_poweron(cp) != 0) {
+ dr_dev_err(CE_WARN, &up->sbc_cm, ESBD_CPUSTART);
+ errflag = 1;
+ }
+ PR_CPU("%s: cpu %d powered ON\n", f, up->sbc_cpu_id);
+ }
+
+ if (cpu_is_offline(cp)) {
+ PR_CPU("%s: onlining cpu %d...\n", f, up->sbc_cpu_id);
+
+ if (cpu_online(cp) != 0) {
+ dr_dev_err(CE_WARN, &up->sbc_cm, ESBD_ONLINE);
+ errflag = 1;
+ }
+ }
+
+ }
+
+ mutex_exit(&cpu_lock);
+ dr_unlock_status(hp->h_bd);
+
+ if (errflag)
+ return (-1);
+ else
+ return (0);
+}
+
+/*
+ * dr_pre_release_cpu
+ *
+ * sbd error policy: Stops on first error.
+ */
+int
+dr_pre_release_cpu(dr_handle_t *hp, dr_common_unit_t **devlist, int devnum)
+{
+ int c, cix, i, lastoffline = -1, rv = 0;
+ processorid_t cpuid;
+ struct cpu *cp;
+ dr_cpu_unit_t *up;
+ dr_devset_t devset;
+ sbd_dev_stat_t *ds;
+ static fn_t f = "dr_pre_release_cpu";
+ int cpu_flags = 0;
+
+ devset = DR_DEVS_PRESENT(hp->h_bd);
+
+ /* allocate status struct storage. */
+ ds = (sbd_dev_stat_t *) kmem_zalloc(sizeof (sbd_dev_stat_t) *
+ MAX_CPU_UNITS_PER_BOARD, KM_SLEEP);
+
+ cix = dr_cpu_status(hp, devset, ds);
+
+ mutex_enter(&cpu_lock);
+
+ for (i = 0; i < devnum; i++) {
+ up = (dr_cpu_unit_t *)devlist[i];
+ ASSERT(dr_cpu_unit_is_sane(hp->h_bd, up));
+
+ /*
+ * The STARCAT platform borrows cpus for use by POST in
+ * iocage testing. These cpus cannot be unconfigured
+ * while they are in use for the iocage.
+ * This check determines if a CPU is currently in use
+ * for iocage testing, and if so, returns a "Device busy"
+ * error.
+ */
+ for (c = 0; c < cix; c++) {
+ if (ds[c].d_cpu.cs_unit == up->sbc_cm.sbdev_unum) {
+ if (ds[c].d_cpu.cs_busy) {
+ dr_dev_err(CE_WARN,
+ &up->sbc_cm, ESBD_BUSY);
+ rv = -1;
+ break;
+ }
+ }
+ }
+ if (c < cix)
+ break;
+ cpuid = up->sbc_cpu_id;
+ if ((cp = cpu_get(cpuid)) == NULL) {
+ dr_dev_err(CE_WARN, &up->sbc_cm, ESBD_OFFLINE);
+ rv = -1;
+ break;
+ }
+
+ /* used by dr_cancel_cpu during error flow */
+ up->sbc_cpu_flags = cp->cpu_flags;
+
+ if (CPU_ACTIVE(cp)) {
+ if (dr_cmd_flags(hp) & SBD_FLAG_FORCE)
+ cpu_flags = CPU_FORCED;
+
+ PR_CPU("%s: offlining cpu %d\n", f, cpuid);
+ if (cpu_offline(cp, cpu_flags)) {
+ PR_CPU("%s: failed to offline cpu %d\n",
+ f, cpuid);
+ dr_dev_err(CE_WARN, &up->sbc_cm, ESBD_OFFLINE);
+ if (disp_bound_threads(cp, 0)) {
+ cmn_err(CE_WARN, "%s: thread(s) "
+ "bound to cpu %d",
+ f, cp->cpu_id);
+ }
+ rv = -1;
+ break;
+ } else
+ lastoffline = i;
+ }
+
+ if (!rv) {
+ sbd_error_t *err;
+
+ err = drmach_release(up->sbc_cm.sbdev_id);
+ if (err) {
+ DRERR_SET_C(&up->sbc_cm.sbdev_error, &err);
+ rv = -1;
+ break;
+ }
+ }
+ }
+
+ mutex_exit(&cpu_lock);
+
+ if (rv) {
+ /*
+ * Need to unwind others since at this level (pre-release)
+ * the device state has not yet transitioned and failures
+ * will prevent us from reaching the "post" release
+ * function where states are normally transitioned.
+ */
+ for (i = lastoffline; i >= 0; i--) {
+ up = (dr_cpu_unit_t *)devlist[i];
+ (void) dr_cancel_cpu(up);
+ }
+ }
+
+ kmem_free(ds, sizeof (sbd_dev_stat_t) * MAX_CPU_UNITS_PER_BOARD);
+ return (rv);
+}
+
+/*
+ * dr_pre_detach_cpu
+ *
+ * sbd error policy: Stops on first error.
+ */
+int
+dr_pre_detach_cpu(dr_handle_t *hp, dr_common_unit_t **devlist, int devnum)
+{
+ _NOTE(ARGUNUSED(hp))
+
+ int i;
+ int curr_cpu;
+ int next_cpu;
+ int cpu_flags = 0;
+ static fn_t f = "dr_pre_detach_cpu";
+
+ PR_CPU("%s...\n", f);
+
+ /*
+ * Block out status threads while destroying devinfo tree
+ * branches
+ */
+ dr_lock_status(hp->h_bd);
+ mutex_enter(&cpu_lock);
+
+ for (next_cpu = 0, i = 0; i < devnum; i++) {
+ dr_cpu_unit_t *up = (dr_cpu_unit_t *)devlist[i];
+ struct cpu *cp;
+
+ ASSERT(dr_cpu_unit_is_sane(hp->h_bd, up));
+
+ cp = cpu_get(up->sbc_cpu_id);
+ if (cp == NULL)
+ continue;
+
+ /*
+ * Print a console message for each attachment
+ * point. For CMP devices, this means that only
+ * one message should be printed, no matter how
+ * many cores are actually present.
+ */
+ curr_cpu = DR_UNUM2SBD_UNUM(up->sbc_cm.sbdev_unum);
+ if (curr_cpu >= next_cpu) {
+ cmn_err(CE_CONT, "OS unconfigure %s\n",
+ up->sbc_cm.sbdev_path);
+ next_cpu = curr_cpu + 1;
+ }
+
+ /*
+ * CPUs were offlined during Release.
+ */
+ if (cpu_is_poweredoff(cp)) {
+ PR_CPU("%s: cpu %d already powered OFF\n",
+ f, up->sbc_cpu_id);
+ continue;
+ }
+
+ if (!cpu_is_offline(cp)) {
+ if (dr_cmd_flags(hp) & SBD_FLAG_FORCE)
+ cpu_flags = CPU_FORCED;
+ /* cpu was onlined after release. Offline it again */
+ PR_CPU("%s: offlining cpu %d\n", f, up->sbc_cpu_id);
+ if (cpu_offline(cp, cpu_flags)) {
+ PR_CPU("%s: failed to offline cpu %d\n",
+ f, up->sbc_cpu_id);
+ dr_dev_err(CE_WARN, &up->sbc_cm, ESBD_OFFLINE);
+ if (disp_bound_threads(cp, 0)) {
+ cmn_err(CE_WARN, "%s: thread(s) "
+ "bound to cpu %d",
+ f, cp->cpu_id);
+ }
+ goto err;
+ }
+ }
+ if (cpu_poweroff(cp) != 0) {
+ dr_dev_err(CE_WARN, &up->sbc_cm, ESBD_CPUSTOP);
+ goto err;
+ } else {
+ PR_CPU("%s: cpu %d powered OFF\n", f, up->sbc_cpu_id);
+ }
+ }
+
+ return (0);
+
+err:
+ mutex_exit(&cpu_lock);
+ dr_unlock_status(hp->h_bd);
+ return (-1);
+}
+
+/*ARGSUSED*/
+void
+dr_detach_cpu(dr_handle_t *hp, dr_common_unit_t *cp)
+{
+ sbd_error_t *err;
+ processorid_t cpuid;
+ int rv;
+
+ ASSERT(MUTEX_HELD(&cpu_lock));
+
+ err = drmach_cpu_get_id(cp->sbdev_id, &cpuid);
+ if (err) {
+ DRERR_SET_C(&cp->sbdev_error, &err);
+ } else if ((rv = cpu_unconfigure(cpuid)) != 0) {
+ dr_dev_err(CE_IGNORE, cp, dr_errno2ecode(rv));
+ } else {
+ err = drmach_unconfigure(cp->sbdev_id, DRMACH_DEVI_REMOVE);
+ if (err) {
+ DRERR_SET_C(&cp->sbdev_error, &err);
+ }
+ }
+}
+
+/*ARGSUSED1*/
+int
+dr_post_detach_cpu(dr_handle_t *hp, dr_common_unit_t **devlist, int devnum)
+{
+ static fn_t f = "dr_post_detach_cpu";
+
+ PR_CPU("%s...\n", f);
+ hp->h_ndi = 0;
+
+ mutex_exit(&cpu_lock);
+ dr_unlock_status(hp->h_bd);
+
+ return (0);
+}
+
+static void
+dr_fill_cpu_stat(dr_cpu_unit_t *cp, drmach_status_t *pstat, sbd_cpu_stat_t *csp)
+{
+ ASSERT(cp && pstat && csp);
+
+ /* Fill in the common status information */
+ bzero((caddr_t)csp, sizeof (*csp));
+ csp->cs_type = cp->sbc_cm.sbdev_type;
+ csp->cs_unit = cp->sbc_cm.sbdev_unum;
+ strncpy(csp->cs_name, pstat->type, sizeof (csp->cs_name));
+ csp->cs_cond = cp->sbc_cm.sbdev_cond;
+ csp->cs_busy = cp->sbc_cm.sbdev_busy | pstat->busy;
+ csp->cs_time = cp->sbc_cm.sbdev_time;
+ csp->cs_ostate = cp->sbc_cm.sbdev_ostate;
+ csp->cs_suspend = 0;
+
+ /* CPU specific status data */
+ csp->cs_cpuid = cp->sbc_cpu_id;
+
+#ifdef _STARFIRE
+ csp->cs_isbootproc = (SIGBCPU->cpu_id == cp->sbc_cpu_id) ? 1 : 0;
+#endif /* _STARFIRE */
+
+ /*
+ * If the speed and ecache properties have not been
+ * cached yet, read them in from the device tree.
+ */
+ if ((cp->sbc_speed == 0) || (cp->sbc_ecache == 0))
+ dr_cpu_set_prop(cp);
+
+ /* use the cached speed and ecache values */
+ csp->cs_speed = cp->sbc_speed;
+ csp->cs_ecache = cp->sbc_ecache;
+
+ mutex_enter(&cpu_lock);
+ if (!cpu_get(csp->cs_cpuid)) {
+ /* ostate must be UNCONFIGURED */
+ csp->cs_cm.c_ostate = SBD_STAT_UNCONFIGURED;
+ }
+ mutex_exit(&cpu_lock);
+}
+
+static void
+dr_fill_cmp_stat(sbd_cpu_stat_t *csp, int ncores, int impl, sbd_cmp_stat_t *psp)
+{
+ int core;
+
+ ASSERT(csp && psp && (ncores >= 1));
+
+ bzero((caddr_t)psp, sizeof (*psp));
+
+ /*
+ * Fill in the common status information based
+ * on the data for the first core.
+ */
+ psp->ps_type = SBD_COMP_CMP;
+ psp->ps_unit = DR_UNUM2SBD_UNUM(csp->cs_unit);
+ strncpy(psp->ps_name, csp->cs_name, sizeof (psp->ps_name));
+ psp->ps_cond = csp->cs_cond;
+ psp->ps_busy = csp->cs_busy;
+ psp->ps_time = csp->cs_time;
+ psp->ps_ostate = csp->cs_ostate;
+ psp->ps_suspend = csp->cs_suspend;
+
+ /* CMP specific status data */
+ *psp->ps_cpuid = csp->cs_cpuid;
+ psp->ps_ncores = 1;
+ psp->ps_speed = csp->cs_speed;
+ psp->ps_ecache = csp->cs_ecache;
+
+ /*
+ * Walk through the data for the remaining cores.
+ * Make any adjustments to the common status data,
+ * or the shared CMP specific data if necessary.
+ */
+ for (core = 1; core < ncores; core++) {
+
+ /*
+ * The following properties should be the same
+ * for all the cores of the CMP.
+ */
+ ASSERT(psp->ps_unit == DR_UNUM2SBD_UNUM(csp[core].cs_unit));
+ ASSERT(psp->ps_speed == csp[core].cs_speed);
+
+ psp->ps_cpuid[core] = csp[core].cs_cpuid;
+ psp->ps_ncores++;
+
+ /*
+ * Jaguar has a split ecache, so the ecache
+ * for each core must be added together to
+ * get the total ecache for the whole chip.
+ */
+ if (IS_JAGUAR(impl)) {
+ psp->ps_ecache += csp[core].cs_ecache;
+ }
+
+ /* adjust time if necessary */
+ if (csp[core].cs_time > psp->ps_time) {
+ psp->ps_time = csp[core].cs_time;
+ }
+
+ psp->ps_busy |= csp[core].cs_busy;
+
+ /*
+ * If any of the cores are configured, the
+ * entire CMP is marked as configured.
+ */
+ if (csp[core].cs_ostate == SBD_STAT_CONFIGURED) {
+ psp->ps_ostate = csp[core].cs_ostate;
+ }
+ }
+}
+
+int
+dr_cpu_status(dr_handle_t *hp, dr_devset_t devset, sbd_dev_stat_t *dsp)
+{
+ int cmp;
+ int core;
+ int ncpu;
+ dr_board_t *bp;
+ sbd_cpu_stat_t cstat[MAX_CORES_PER_CMP];
+
+ bp = hp->h_bd;
+ ncpu = 0;
+
+ devset &= DR_DEVS_PRESENT(bp);
+
+ /*
+ * Treat every CPU as a CMP. In the case where the
+ * device is not a CMP, treat it as a CMP with only
+ * one core.
+ */
+ for (cmp = 0; cmp < MAX_CMP_UNITS_PER_BOARD; cmp++) {
+
+ int ncores;
+ dr_cpu_unit_t *cp;
+ drmach_status_t pstat;
+ sbd_error_t *err;
+ sbd_cmp_stat_t *psp;
+
+ if ((devset & DEVSET(SBD_COMP_CMP, cmp)) == 0) {
+ continue;
+ }
+
+ ncores = 0;
+
+ for (core = 0; core < MAX_CORES_PER_CMP; core++) {
+
+ cp = dr_get_cpu_unit(bp, DR_CMP_CORE_UNUM(cmp, core));
+
+ if (cp->sbc_cm.sbdev_state == DR_STATE_EMPTY) {
+ /* present, but not fully initialized */
+ continue;
+ }
+
+ ASSERT(dr_cpu_unit_is_sane(hp->h_bd, cp));
+
+ /* skip if not present */
+ if (cp->sbc_cm.sbdev_id == (drmachid_t)0) {
+ continue;
+ }
+
+ /* fetch platform status */
+ err = drmach_status(cp->sbc_cm.sbdev_id, &pstat);
+ if (err) {
+ DRERR_SET_C(&cp->sbc_cm.sbdev_error, &err);
+ continue;
+ }
+
+ dr_fill_cpu_stat(cp, &pstat, &cstat[ncores++]);
+ }
+
+ if (ncores == 0) {
+ continue;
+ }
+
+ /*
+ * Store the data to the outgoing array. If the
+ * device is a CMP, combine all the data for the
+ * cores into a single stat structure.
+ *
+ * The check for a CMP device uses the last core
+ * found, assuming that all cores will have the
+ * same implementation.
+ */
+ if (CPU_IMPL_IS_CMP(cp->sbc_cpu_impl)) {
+ psp = (sbd_cmp_stat_t *)dsp;
+ dr_fill_cmp_stat(cstat, ncores, cp->sbc_cpu_impl, psp);
+ } else {
+ ASSERT(ncores == 1);
+ bcopy(cstat, dsp, sizeof (sbd_cpu_stat_t));
+ }
+
+ dsp++;
+ ncpu++;
+ }
+
+ return (ncpu);
+}
+
+/*
+ * Cancel previous release operation for cpu.
+ * For cpus this means simply bringing cpus that
+ * were offline back online. Note that they had
+ * to have been online at the time there were
+ * released.
+ */
+int
+dr_cancel_cpu(dr_cpu_unit_t *up)
+{
+ int rv = 0;
+ static fn_t f = "dr_cancel_cpu";
+
+ ASSERT(dr_cpu_unit_is_sane(up->sbc_cm.sbdev_bp, up));
+
+ if (cpu_flagged_active(up->sbc_cpu_flags)) {
+ struct cpu *cp;
+
+ /*
+ * CPU had been online, go ahead
+ * bring it back online.
+ */
+ PR_CPU("%s: bringing cpu %d back ONLINE\n",
+ f, up->sbc_cpu_id);
+
+ mutex_enter(&cpu_lock);
+ cp = cpu[up->sbc_cpu_id];
+
+ if (cpu_is_poweredoff(cp)) {
+ if (cpu_poweron(cp)) {
+ cmn_err(CE_WARN, "%s: failed to power-on "
+ "cpu %d", f, up->sbc_cpu_id);
+ rv = -1;
+ }
+ }
+
+ if (cpu_is_offline(cp)) {
+ if (cpu_online(cp)) {
+ cmn_err(CE_WARN, "%s: failed to online cpu %d",
+ f, up->sbc_cpu_id);
+ rv = -1;
+ }
+ }
+
+ if (cpu_is_online(cp)) {
+ if (cpu_flagged_nointr(up->sbc_cpu_flags)) {
+ if (cpu_intr_disable(cp) != 0) {
+ cmn_err(CE_WARN, "%s: failed to "
+ "disable interrupts on cpu %d",
+ f, up->sbc_cpu_id);
+ }
+ }
+ }
+
+ mutex_exit(&cpu_lock);
+ }
+
+ return (rv);
+}
+
+int
+dr_disconnect_cpu(dr_cpu_unit_t *up)
+{
+ sbd_error_t *err;
+ static fn_t f = "dr_disconnect_cpu";
+
+ PR_CPU("%s...\n", f);
+
+ ASSERT((up->sbc_cm.sbdev_state == DR_STATE_CONNECTED) ||
+ (up->sbc_cm.sbdev_state == DR_STATE_UNCONFIGURED));
+
+ ASSERT(dr_cpu_unit_is_sane(up->sbc_cm.sbdev_bp, up));
+
+ if (up->sbc_cm.sbdev_state == DR_STATE_CONNECTED) {
+ /*
+ * Cpus were never brought in and so are still
+ * effectively disconnected, so nothing to do here.
+ */
+ PR_CPU("%s: cpu %d never brought in\n",
+ f, up->sbc_cpu_id);
+ return (0);
+ }
+
+ err = drmach_cpu_disconnect(up->sbc_cm.sbdev_id);
+ if (err == NULL)
+ return (0);
+ else {
+ DRERR_SET_C(&up->sbc_cm.sbdev_error, &err);
+ return (-1);
+ }
+ /*NOTREACHED*/
+}