summaryrefslogtreecommitdiff
path: root/usr/src/uts/sun4u/io/sbd.c
diff options
context:
space:
mode:
Diffstat (limited to 'usr/src/uts/sun4u/io/sbd.c')
-rw-r--r--usr/src/uts/sun4u/io/sbd.c5204
1 files changed, 5204 insertions, 0 deletions
diff --git a/usr/src/uts/sun4u/io/sbd.c b/usr/src/uts/sun4u/io/sbd.c
new file mode 100644
index 0000000000..933dbb5af1
--- /dev/null
+++ b/usr/src/uts/sun4u/io/sbd.c
@@ -0,0 +1,5204 @@
+/*
+ * 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 2005 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * safari system board DR module.
+ */
+
+#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/cpuvar.h>
+#include <sys/mem_config.h>
+#include <sys/mem_cage.h>
+
+#include <sys/autoconf.h>
+#include <sys/cmn_err.h>
+
+#include <sys/ddi_impldefs.h>
+#include <sys/machsystm.h>
+#include <sys/param.h>
+
+#include <sys/sbdpriv.h>
+#include <sys/sbd_io.h>
+
+/* start sbd includes */
+
+#include <sys/systm.h>
+#include <sys/sysmacros.h>
+#include <sys/x_call.h>
+#include <sys/membar.h>
+#include <vm/seg_kmem.h>
+
+extern int nulldev();
+extern int nodev();
+
+typedef struct { /* arg to sbd_get_handle */
+ dev_t dev;
+ int cmd;
+ int mode;
+ sbd_ioctl_arg_t *ioargp;
+} sbd_init_arg_t;
+
+
+/*
+ * sbd support operations.
+ */
+static void sbd_exec_op(sbd_handle_t *hp);
+static void sbd_dev_configure(sbd_handle_t *hp);
+static int sbd_dev_release(sbd_handle_t *hp);
+static int sbd_dev_unconfigure(sbd_handle_t *hp);
+static void sbd_attach_cpu(sbd_handle_t *hp, sbderror_t *ep,
+ dev_info_t *dip, int unit);
+static void sbd_detach_cpu(sbd_handle_t *hp, sbderror_t *ep,
+ dev_info_t *dip, int unit);
+static int sbd_detach_mem(sbd_handle_t *hp, sbderror_t *ep, int unit);
+static void sbd_cancel(sbd_handle_t *hp);
+void sbd_errno_decode(int err, sbderror_t *ep, dev_info_t *dip);
+int sbd_dealloc_instance(sbd_board_t *sbp, int max_boards);
+int sbd_errno2ecode(int error);
+#pragma weak sbdp_cpu_get_impl
+
+#ifdef DEBUG
+uint_t sbd_debug = (uint_t)0x0;
+
+#ifdef SBD_DEBUG_ERRS
+/* controls which errors are injected */
+uint_t sbd_err_debug = (uint_t)0x0;
+
+/* controls printing about error injection */
+uint_t sbd_print_errs = (uint_t)0x0;
+
+#endif /* SBD_DEBUG_ERRS */
+
+#endif /* DEBUG */
+
+char *sbd_state_str[] = {
+ "EMPTY", "OCCUPIED", "CONNECTED", "UNCONFIGURED",
+ "PARTIAL", "CONFIGURED", "RELEASE", "UNREFERENCED",
+ "FATAL"
+};
+
+/* Note: this must be changed in tandem with sbd_ioctl.h */
+char *sbd_ct_str[] = {
+ "NONE", "CPU", "MEM", "IO", "UNKNOWN"
+};
+
+/* Note: this must also be changed in tandem with sbd_ioctl.h */
+#define SBD_CMD_STR(c) \
+ (((c) == SBD_CMD_ASSIGN) ? "ASSIGN" : \
+ ((c) == SBD_CMD_UNASSIGN) ? "UNASSIGN" : \
+ ((c) == SBD_CMD_POWERON) ? "POWERON" : \
+ ((c) == SBD_CMD_POWEROFF) ? "POWEROFF" : \
+ ((c) == SBD_CMD_TEST) ? "TEST" : \
+ ((c) == SBD_CMD_CONNECT) ? "CONNECT" : \
+ ((c) == SBD_CMD_CONFIGURE) ? "CONFIGURE" : \
+ ((c) == SBD_CMD_UNCONFIGURE) ? "UNCONFIGURE" : \
+ ((c) == SBD_CMD_DISCONNECT) ? "DISCONNECT" : \
+ ((c) == SBD_CMD_STATUS) ? "STATUS" : \
+ ((c) == SBD_CMD_GETNCM) ? "GETNCM" : \
+ ((c) == SBD_CMD_PASSTHRU) ? "PASSTHRU" : "unknown")
+
+/*
+ * Defines and structures for device tree naming and mapping
+ * to node types
+ */
+
+sbd_devattr_t *sbd_devattr;
+
+/* defines to access the attribute struct */
+#define SBD_DEVNAME(i) sbd_devattr[i].s_devname
+#define SBD_OTYPE(i) sbd_devattr[(i)].s_obp_type
+#define SBD_COMP(i) sbd_devattr[i].s_dnodetype
+
+/*
+ * State transition table. States valid transitions for "board" state.
+ * Recall that non-zero return value terminates operation, however
+ * the herrno value is what really indicates an error , if any.
+ */
+static int
+_cmd2index(int c)
+{
+ /*
+ * Translate DR CMD to index into sbd_state_transition.
+ */
+ switch (c) {
+ case SBD_CMD_CONNECT: return (0);
+ case SBD_CMD_DISCONNECT: return (1);
+ case SBD_CMD_CONFIGURE: return (2);
+ case SBD_CMD_UNCONFIGURE: return (3);
+ case SBD_CMD_POWEROFF: return (4);
+ case SBD_CMD_POWERON: return (5);
+ case SBD_CMD_UNASSIGN: return (6);
+ case SBD_CMD_ASSIGN: return (7);
+ case SBD_CMD_TEST: return (8);
+ default: return (-1);
+ }
+}
+
+#define CMD2INDEX(c) _cmd2index(c)
+
+static struct sbd_state_trans {
+ int x_cmd;
+ struct {
+ int x_rv; /* return value of pre_op */
+ int x_err; /* errno, if any */
+ } x_op[SBD_NUM_STATES];
+} sbd_state_transition[] = {
+ { SBD_CMD_CONNECT,
+ {
+ { 0, 0 }, /* empty */
+ { 0, 0 }, /* occupied */
+ { 1, EIO }, /* connected */
+ { 1, EIO }, /* unconfigured */
+ { 1, EIO }, /* partial */
+ { 1, EIO }, /* configured */
+ { 1, EIO }, /* release */
+ { 1, EIO }, /* unreferenced */
+ { 1, EIO }, /* fatal */
+ }
+ },
+ { SBD_CMD_DISCONNECT,
+ {
+ { 1, EIO }, /* empty */
+ { 0, 0 }, /* occupied */
+ { 0, 0 }, /* connected */
+ { 0, 0 }, /* unconfigured */
+ { 1, EIO }, /* partial */
+ { 1, EIO }, /* configured */
+ { 1, EIO }, /* release */
+ { 1, EIO }, /* unreferenced */
+ { 1, EIO }, /* fatal */
+ }
+ },
+ { SBD_CMD_CONFIGURE,
+ {
+ { 1, EIO }, /* empty */
+ { 1, EIO }, /* occupied */
+ { 0, 0 }, /* connected */
+ { 0, 0 }, /* unconfigured */
+ { 0, 0 }, /* partial */
+ { 1, 0 }, /* configured */
+ { 0, 0 }, /* release */
+ { 0, 0 }, /* unreferenced */
+ { 1, EIO }, /* fatal */
+ }
+ },
+ { SBD_CMD_UNCONFIGURE,
+ {
+ { 1, EIO }, /* empty */
+ { 1, EIO }, /* occupied */
+ { 1, EIO }, /* connected */
+ { 1, EIO }, /* unconfigured */
+ { 1, EIO }, /* partial */
+ { 0, 0 }, /* configured */
+ { 0, 0 }, /* release */
+ { 0, 0 }, /* unreferenced */
+ { 1, EIO }, /* fatal */
+ }
+ },
+ { SBD_CMD_POWEROFF,
+ {
+ { 1, EIO }, /* empty */
+ { 0, 0 }, /* occupied */
+ { 1, EIO }, /* connected */
+ { 1, EIO }, /* unconfigured */
+ { 1, EIO }, /* partial */
+ { 1, EIO }, /* configured */
+ { 1, EIO }, /* release */
+ { 1, EIO }, /* unreferenced */
+ { 1, EIO }, /* fatal */
+ }
+ },
+ { SBD_CMD_POWERON,
+ {
+ { 1, EIO }, /* empty */
+ { 0, 0 }, /* occupied */
+ { 1, EIO }, /* connected */
+ { 1, EIO }, /* unconfigured */
+ { 1, EIO }, /* partial */
+ { 1, EIO }, /* configured */
+ { 1, EIO }, /* release */
+ { 1, EIO }, /* unreferenced */
+ { 1, EIO }, /* fatal */
+ }
+ },
+ { SBD_CMD_UNASSIGN,
+ {
+ { 1, EIO }, /* empty */
+ { 0, 0 }, /* occupied */
+ { 1, EIO }, /* connected */
+ { 1, EIO }, /* unconfigured */
+ { 1, EIO }, /* partial */
+ { 1, EIO }, /* configured */
+ { 1, EIO }, /* release */
+ { 1, EIO }, /* unreferenced */
+ { 1, EIO }, /* fatal */
+ }
+ },
+ { SBD_CMD_ASSIGN,
+ {
+ { 1, EIO }, /* empty */
+ { 0, 0 }, /* occupied */
+ { 1, EIO }, /* connected */
+ { 1, EIO }, /* unconfigured */
+ { 1, EIO }, /* partial */
+ { 1, EIO }, /* configured */
+ { 1, EIO }, /* release */
+ { 1, EIO }, /* unreferenced */
+ { 1, EIO }, /* fatal */
+ }
+ },
+ { SBD_CMD_TEST,
+ {
+ { 1, EIO }, /* empty */
+ { 0, 0 }, /* occupied */
+ { 1, EIO }, /* connected */
+ { 1, EIO }, /* unconfigured */
+ { 1, EIO }, /* partial */
+ { 1, EIO }, /* configured */
+ { 1, EIO }, /* release */
+ { 1, EIO }, /* unreferenced */
+ { 1, EIO }, /* fatal */
+ }
+ },
+};
+
+/*
+ * Global R/W lock to synchronize access across
+ * multiple boards. Users wanting multi-board access
+ * must grab WRITE lock, others must grab READ lock.
+ */
+krwlock_t sbd_grwlock;
+
+/*
+ * Global to determine if an event needs to be sent
+ */
+char send_event = 0;
+
+/*
+ * Required/Expected functions.
+ */
+
+static sbd_handle_t *sbd_get_handle(dev_t dev, sbd_softstate_t *softsp,
+ intptr_t arg, sbd_init_arg_t *iap);
+static void sbd_release_handle(sbd_handle_t *hp);
+static int sbd_pre_op(sbd_handle_t *hp);
+static void sbd_post_op(sbd_handle_t *hp);
+static int sbd_probe_board(sbd_handle_t *hp);
+static int sbd_deprobe_board(sbd_handle_t *hp);
+static void sbd_connect(sbd_handle_t *hp);
+static void sbd_assign_board(sbd_handle_t *hp);
+static void sbd_unassign_board(sbd_handle_t *hp);
+static void sbd_poweron_board(sbd_handle_t *hp);
+static void sbd_poweroff_board(sbd_handle_t *hp);
+static void sbd_test_board(sbd_handle_t *hp);
+
+static int sbd_disconnect(sbd_handle_t *hp);
+static sbd_devlist_t *sbd_get_attach_devlist(sbd_handle_t *hp,
+ int32_t *devnump, int32_t pass);
+static int sbd_pre_attach_devlist(sbd_handle_t *hp,
+ sbd_devlist_t *devlist, int32_t devnum);
+static int sbd_post_attach_devlist(sbd_handle_t *hp,
+ sbd_devlist_t *devlist, int32_t devnum);
+static sbd_devlist_t *sbd_get_release_devlist(sbd_handle_t *hp,
+ int32_t *devnump, int32_t pass);
+static int sbd_pre_release_devlist(sbd_handle_t *hp,
+ sbd_devlist_t *devlist, int32_t devnum);
+static int sbd_post_release_devlist(sbd_handle_t *hp,
+ sbd_devlist_t *devlist, int32_t devnum);
+static void sbd_release_done(sbd_handle_t *hp,
+ sbd_comp_type_t nodetype,
+ dev_info_t *dip);
+static sbd_devlist_t *sbd_get_detach_devlist(sbd_handle_t *hp,
+ int32_t *devnump, int32_t pass);
+static int sbd_pre_detach_devlist(sbd_handle_t *hp,
+ sbd_devlist_t *devlist, int32_t devnum);
+static int sbd_post_detach_devlist(sbd_handle_t *hp,
+ sbd_devlist_t *devlist, int32_t devnum);
+static void sbd_status(sbd_handle_t *hp);
+static void sbd_get_ncm(sbd_handle_t *hp);
+
+
+/*
+ * Support functions.
+ */
+static sbd_devset_t sbd_dev2devset(sbd_comp_id_t *cid);
+static int sbd_copyin_ioarg(sbd_handle_t *hp, int mode, int cmd,
+ sbd_cmd_t *cmdp, sbd_ioctl_arg_t *iap);
+static int sbd_copyout_errs(int mode, sbd_ioctl_arg_t *iap,
+ void *arg);
+static int sbd_copyout_ioarg(int mode, int cmd, sbd_cmd_t *scp,
+ sbd_ioctl_arg_t *iap);
+static int sbd_check_transition(sbd_board_t *sbp,
+ sbd_devset_t *devsetp,
+ struct sbd_state_trans *transp);
+static sbd_devlist_t *sbd_get_devlist(sbd_handle_t *hp,
+ sbd_board_t *sbp,
+ sbd_comp_type_t nodetype,
+ int max_units, uint_t uset,
+ int *count, int present_only);
+static int sbd_mem_status(sbd_handle_t *hp, sbd_devset_t devset,
+ sbd_dev_stat_t *dsp);
+
+static int sbd_init_devlists(sbd_board_t *sbp);
+static int sbd_name_to_idx(char *name);
+static int sbd_otype_to_idx(char *otpye);
+static int sbd_setup_devlists(dev_info_t *dip, void *arg);
+static void sbd_init_mem_devlists(sbd_board_t *sbp);
+static void sbd_init_cpu_unit(sbd_board_t *sbp, int unit);
+static void sbd_board_discovery(sbd_board_t *sbp);
+static void sbd_board_init(sbd_board_t *sbp,
+ sbd_softstate_t *softsp,
+ int bd, dev_info_t *dip, int wnode);
+static void sbd_board_destroy(sbd_board_t *sbp);
+static int sbd_check_unit_attached(sbd_board_t *sbp,
+ dev_info_t *dip, int unit,
+ sbd_comp_type_t nodetype, sbderror_t *ep);
+
+static sbd_state_t rstate_cvt(sbd_istate_t state);
+
+/*
+ * Autoconfiguration data structures
+ */
+
+extern struct mod_ops mod_miscops;
+
+static struct modlmisc modlmisc = {
+ &mod_miscops,
+ "System Board DR v%I%"
+};
+
+static struct modlinkage modlinkage = {
+ MODREV_1,
+ (void *)&modlmisc,
+ NULL
+};
+
+static int sbd_instances = 0;
+
+/*
+ * dr Global data elements
+ */
+sbd_global sbd_g;
+
+/*
+ * We want to be able to unload the module when we wish to do so, but we don't
+ * want anything else to unload it. Unloading cannot occur until
+ * sbd_teardown_instance is called by an explicit IOCTL into the parent node.
+ * This support is for debugging purposes and should it be expected to work
+ * on the field, it should be enhanced:
+ * Currently, there is still a window where sbd_teardow_instance gets called,
+ * sbd_prevent_unloading now = 0, the driver doesn't get unloaded, and
+ * sbd_setup_instance gets called. This may cause a panic.
+ */
+int sbd_prevent_unloading = 1;
+
+/*
+ * Driver entry points.
+ */
+int
+_init(void)
+{
+ int err;
+
+ /*
+ * If you need to support multiple nodes (instances), then
+ * whatever the maximum number of supported nodes is would
+ * need to passed as the third parameter to ddi_soft_state_init().
+ * Alternative would be to dynamically fini and re-init the
+ * soft state structure each time a node is attached.
+ */
+ err = ddi_soft_state_init((void **)&sbd_g.softsp,
+ sizeof (sbd_softstate_t), SBD_MAX_INSTANCES);
+ if (err)
+ return (err);
+
+ if ((err = mod_install(&modlinkage)) != 0) {
+ ddi_soft_state_fini((void **)&sbd_g.softsp);
+ return (err);
+ }
+
+ /* Get the array of names from platform helper routine */
+ sbd_devattr = sbdp_get_devattr();
+
+ return (err);
+}
+
+int
+_fini(void)
+{
+ int err;
+
+ if (sbd_prevent_unloading)
+ return (DDI_FAILURE);
+
+ ASSERT(sbd_instances == 0);
+
+ if ((err = mod_remove(&modlinkage)) != 0)
+ return (err);
+
+ ddi_soft_state_fini((void **)&sbd_g.softsp);
+
+ return (0);
+}
+
+int
+_info(struct modinfo *modinfop)
+{
+ return (mod_info(&modlinkage, modinfop));
+}
+
+int
+sbd_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, char *event)
+{
+ int rv = 0, instance;
+ sbd_handle_t *hp;
+ sbd_softstate_t *softsp;
+ sbd_init_arg_t init_arg;
+ static fn_t f = "sbd_ioctl";
+ int dr_avail;
+
+ PR_BYP("sbd_ioctl cmd=%x, arg=%x\n", cmd, arg);
+
+ /* Note: this must also be changed in tandem with sbd_ioctl.h */
+ switch (cmd) {
+ case SBD_CMD_ASSIGN:
+ case SBD_CMD_UNASSIGN:
+ case SBD_CMD_POWERON:
+ case SBD_CMD_POWEROFF:
+ case SBD_CMD_TEST:
+ case SBD_CMD_CONNECT:
+ case SBD_CMD_CONFIGURE:
+ case SBD_CMD_UNCONFIGURE:
+ case SBD_CMD_DISCONNECT:
+ case SBD_CMD_STATUS:
+ case SBD_CMD_GETNCM:
+ case SBD_CMD_PASSTHRU:
+ break;
+ default:
+ return (ENOTTY);
+ }
+
+ instance = SBD_GET_MINOR2INST(getminor(dev));
+ if ((softsp = (sbd_softstate_t *)GET_SOFTC(instance)) == NULL) {
+ cmn_err(CE_WARN,
+ "sbd:%s:%d: module not yet attached",
+ f, instance);
+ return (ENXIO);
+ }
+
+ init_arg.dev = dev;
+ init_arg.cmd = cmd;
+ init_arg.mode = mode;
+ init_arg.ioargp = (sbd_ioctl_arg_t *)arg;
+
+ hp = sbd_get_handle(dev, softsp, arg, &init_arg);
+ /* Check to see if we support dr */
+ dr_avail = sbdp_dr_avail();
+ if (dr_avail != 1) {
+ switch (hp->h_cmd) {
+ case SBD_CMD_STATUS:
+ case SBD_CMD_GETNCM:
+ case SBD_CMD_PASSTHRU:
+ break;
+ default:
+ sbd_release_handle(hp);
+ return (ENOTSUP);
+ }
+ }
+
+ switch (hp->h_cmd) {
+ case SBD_CMD_STATUS:
+ case SBD_CMD_GETNCM:
+ case SBD_CMD_PASSTHRU:
+ /* no locks needed for these commands */
+ break;
+
+ default:
+ rw_enter(&sbd_grwlock, RW_WRITER);
+ mutex_enter(&SBDH2BD(hp->h_sbd)->sb_mutex);
+
+ /*
+ * If we're dealing with memory at all, then we have
+ * to keep the "exclusive" global lock held. This is
+ * necessary since we will probably need to look at
+ * multiple board structs. Otherwise, we only have
+ * to deal with the board in question and so can drop
+ * the global lock to "shared".
+ */
+ /*
+ * XXX This is incorrect. The sh_devset has not
+ * been set at this point - it is 0.
+ */
+ rv = DEVSET_IN_SET(HD2MACHHD(hp)->sh_devset,
+ SBD_COMP_MEM, DEVSET_ANYUNIT);
+ if (rv == 0)
+ rw_downgrade(&sbd_grwlock);
+ break;
+ }
+
+ /*
+ * Before any operations happen, reset the event flag
+ */
+ send_event = 0;
+
+ if (sbd_pre_op(hp) == 0) {
+ sbd_exec_op(hp);
+ sbd_post_op(hp);
+ }
+
+ rv = SBD_GET_ERRNO(SBD_HD2ERR(hp));
+ *event = send_event;
+
+ /* undo locking, if any, done before sbd_pre_op */
+ switch (hp->h_cmd) {
+ case SBD_CMD_STATUS:
+ case SBD_CMD_GETNCM:
+ case SBD_CMD_PASSTHRU:
+ break;
+ default:
+ mutex_exit(&SBDH2BD(hp->h_sbd)->sb_mutex);
+ rw_exit(&sbd_grwlock);
+ }
+
+ sbd_release_handle(hp);
+
+ return (rv);
+}
+
+int
+sbd_setup_instance(int instance, dev_info_t *root, int max_boards, int wnode,
+ caddr_t sbdp_arg)
+{
+ int b;
+ sbd_softstate_t *softsp;
+ sbd_board_t *sbd_boardlist;
+ static fn_t f = "sbd_setup_instance";
+
+ sbd_instances++;
+
+ if (sbdp_setup_instance(sbdp_arg) != DDI_SUCCESS) {
+ sbd_instances--;
+ return (DDI_FAILURE);
+ }
+
+ if (ALLOC_SOFTC(instance) != DDI_SUCCESS) {
+ cmn_err(CE_WARN,
+ "sbd:%s:%d: failed to alloc soft-state",
+ f, instance);
+ sbdp_teardown_instance(sbdp_arg);
+ sbd_instances--;
+ return (DDI_FAILURE);
+ }
+
+ softsp = (sbd_softstate_t *)GET_SOFTC(instance);
+
+ if (softsp == NULL) {
+ cmn_err(CE_WARN,
+ "sbd:%s:%d: failed to get soft-state instance",
+ f, instance);
+ goto exit;
+ }
+
+ sbd_boardlist = GETSTRUCT(sbd_board_t, max_boards);
+ if (sbd_boardlist == NULL) {
+ cmn_err(CE_WARN,
+ "sbd:%s: failed to alloc board list %d",
+ f, instance);
+ goto exit;
+ }
+
+
+ softsp->sbd_boardlist = (void *)sbd_boardlist;
+ softsp->max_boards = max_boards;
+ softsp->wnode = wnode;
+
+
+ for (b = 0; b < max_boards; b++) {
+ sbd_board_init(sbd_boardlist++, softsp, b, root, wnode);
+ }
+
+
+ return (DDI_SUCCESS);
+exit:
+ (void) sbdp_teardown_instance(sbdp_arg);
+ FREE_SOFTC(instance);
+ sbd_instances--;
+ return (DDI_FAILURE);
+}
+
+int
+sbd_teardown_instance(int instance, caddr_t sbdp_arg)
+{
+ sbd_softstate_t *softsp;
+
+ if (sbdp_teardown_instance(sbdp_arg) != DDI_SUCCESS)
+ return (DDI_FAILURE);
+
+ softsp = (sbd_softstate_t *)GET_SOFTC(instance);
+ if (softsp == NULL) {
+ return (DDI_FAILURE);
+ }
+
+ (void) sbd_dealloc_instance((sbd_board_t *)softsp->sbd_boardlist,
+ softsp->max_boards);
+
+ FREE_SOFTC(instance);
+ sbd_instances--;
+ sbd_prevent_unloading = 0;
+
+ return (DDI_SUCCESS);
+}
+
+static void
+sbd_exec_op(sbd_handle_t *hp)
+{
+ sbd_board_t *sbp = SBDH2BD(hp->h_sbd);
+ static fn_t f = "sbd_exec_op";
+
+ switch (hp->h_cmd) {
+ int dev_canceled;
+
+ case SBD_CMD_CONNECT:
+ if (sbd_probe_board(hp))
+ break;
+
+ sbd_connect(hp);
+ break;
+
+ case SBD_CMD_CONFIGURE:
+ sbd_dev_configure(hp);
+ break;
+
+ case SBD_CMD_UNCONFIGURE:
+ if (((dev_canceled = sbd_dev_release(hp)) == 0) &&
+ (SBD_GET_ERRNO(SBD_HD2ERR(hp)) == 0 &&
+ SBD_GET_ERR(SBD_HD2ERR(hp)) == 0))
+ dev_canceled = sbd_dev_unconfigure(hp);
+
+ if (dev_canceled)
+ sbd_cancel(hp);
+ break;
+
+ case SBD_CMD_DISCONNECT:
+ mutex_enter(&sbp->sb_slock);
+ if (sbd_disconnect(hp) == 0)
+ (void) sbd_deprobe_board(hp);
+ mutex_exit(&sbp->sb_slock);
+ break;
+
+ case SBD_CMD_STATUS:
+ sbd_status(hp);
+ break;
+
+ case SBD_CMD_GETNCM:
+ sbd_get_ncm(hp);
+ break;
+
+ case SBD_CMD_ASSIGN:
+ sbd_assign_board(hp);
+ break;
+
+ case SBD_CMD_UNASSIGN:
+ sbd_unassign_board(hp);
+ break;
+
+ case SBD_CMD_POWEROFF:
+ sbd_poweroff_board(hp);
+ break;
+
+ case SBD_CMD_POWERON:
+ sbd_poweron_board(hp);
+ break;
+
+ case SBD_CMD_TEST:
+ sbd_test_board(hp);
+ break;
+
+ case SBD_CMD_PASSTHRU:
+ {
+ int rv;
+ sbdp_handle_t *hdp;
+ sbderror_t *ep = SBD_HD2ERR(hp);
+ sbdp_ioctl_arg_t ia, *iap;
+
+ iap = &ia;
+
+ iap->h_dev = hp->h_dev;
+ iap->h_cmd = hp->h_cmd;
+ iap->h_iap = (intptr_t)hp->h_iap;
+ iap->h_mode = hp->h_mode;
+
+ hdp = sbd_get_sbdp_handle(sbp, hp);
+ rv = sbdp_ioctl(hdp, iap);
+ if (rv != 0) {
+ SBD_GET_PERR(hdp->h_err, SBD_HD2ERR(hp));
+ ep->e_errno = rv;
+ }
+ sbd_release_sbdp_handle(hdp);
+ break;
+ }
+
+ default:
+ SBD_SET_ERRNO(SBD_HD2ERR(hp), ENOTTY);
+ cmn_err(CE_WARN,
+ "sbd:%s: unknown command (%d)",
+ f, hp->h_cmd);
+ break;
+
+ }
+
+ if (SBD_GET_ERR(SBD_HD2ERR(hp)))
+ PR_BYP("XXX e_code=%d", SBD_GET_ERR(SBD_HD2ERR(hp)));
+ if (SBD_GET_ERRNO(SBD_HD2ERR(hp)))
+ PR_BYP("XXX errno=%d", SBD_GET_ERRNO(SBD_HD2ERR(hp)));
+}
+
+sbd_comp_type_t
+sbd_get_devtype(sbd_handle_t *hp, dev_info_t *dip)
+{
+ sbd_board_t *sbp = hp ? SBDH2BD(hp->h_sbd) : NULL;
+ sbd_istate_t bstate;
+ dev_info_t **devlist;
+ int i;
+ char device[OBP_MAXDRVNAME];
+ int devicelen;
+
+ devicelen = sizeof (device);
+
+ bstate = sbp ? SBD_BOARD_STATE(sbp) : SBD_STATE_EMPTY;
+ /*
+ * if the board's connected or configured, search the
+ * devlists. Otherwise check the device tree
+ */
+ switch (bstate) {
+
+ case SBD_STATE_CONNECTED:
+ case SBD_STATE_CONFIGURED:
+ case SBD_STATE_UNREFERENCED:
+ case SBD_STATE_UNCONFIGURED:
+ devlist = sbp->sb_devlist[NIX(SBD_COMP_MEM)];
+ for (i = 0; i < MAX_MEM_UNITS_PER_BOARD; i++)
+ if (devlist[i] == dip)
+ return (SBD_COMP_MEM);
+
+ devlist = sbp->sb_devlist[NIX(SBD_COMP_CPU)];
+ for (i = 0; i < MAX_CPU_UNITS_PER_BOARD; i++)
+ if (devlist[i] == dip)
+ return (SBD_COMP_CPU);
+
+ devlist = sbp->sb_devlist[NIX(SBD_COMP_IO)];
+ for (i = 0; i < MAX_IO_UNITS_PER_BOARD; i++)
+ if (devlist[i] == dip)
+ return (SBD_COMP_IO);
+ /*FALLTHROUGH*/
+
+ default:
+ if (ddi_getlongprop_buf(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
+ OBP_DEVICETYPE, (caddr_t)device, &devicelen))
+ break;
+
+ for (i = 0; SBD_COMP(i) != SBD_COMP_UNKNOWN; i++) {
+ if (strcmp(device, SBD_OTYPE(i)) != 0)
+ continue;
+ return (SBD_COMP(i));
+ }
+
+ break;
+ }
+ return (SBD_COMP_UNKNOWN);
+}
+
+static void
+sbd_dev_configure(sbd_handle_t *hp)
+{
+ int n, unit;
+ int32_t pass, devnum;
+ dev_info_t *dip;
+ sbd_devlist_t *devlist;
+ sbdp_handle_t *hdp;
+ sbd_comp_type_t nodetype;
+ sbd_board_t *sbp = SBDH2BD(hp->h_sbd);
+
+ pass = 1;
+
+ hdp = sbd_get_sbdp_handle(sbp, hp);
+ while ((devlist = sbd_get_attach_devlist(hp, &devnum, pass)) != NULL) {
+ int err;
+
+ err = sbd_pre_attach_devlist(hp, devlist, devnum);
+ if (err < 0) {
+ break;
+ } else if (err > 0) {
+ pass++;
+ continue;
+ }
+
+ for (n = 0; n < devnum; n++) {
+ sbderror_t *ep;
+
+ ep = &devlist[n].dv_error;
+ SBD_SET_ERRNO(ep, 0);
+ SBD_SET_ERR(ep, 0);
+ dip = devlist[n].dv_dip;
+ nodetype = sbd_get_devtype(hp, dip);
+
+ unit = sbdp_get_unit_num(hdp, dip);
+ if (unit < 0) {
+ SBD_GET_PERR(hdp->h_err, SBD_HD2ERR(hp));
+ break;
+ }
+
+ switch (nodetype) {
+ case SBD_COMP_MEM:
+ sbd_attach_mem(hp, ep);
+ if (SBD_GET_ERR(ep) == ESBD_CPUONLINE) {
+ FREESTRUCT(devlist, sbd_devlist_t,
+ MAX_MEM_UNITS_PER_BOARD);
+ sbd_release_sbdp_handle(hdp);
+ return;
+ }
+ break;
+
+ case SBD_COMP_CPU:
+ sbd_attach_cpu(hp, ep, dip, unit);
+ break;
+
+ case SBD_COMP_IO:
+ sbd_attach_io(hp, ep, dip, unit);
+ break;
+
+ default:
+ SBD_SET_ERRNO(ep, ENOTTY);
+ break;
+ }
+
+ if (sbd_set_err_in_hdl(hp, ep) == 0)
+ continue;
+ }
+
+ err = sbd_post_attach_devlist(hp, devlist, devnum);
+ if (err < 0)
+ break;
+
+ pass++;
+ }
+ sbd_release_sbdp_handle(hdp);
+}
+
+static int
+sbd_dev_release(sbd_handle_t *hp)
+{
+ int n, unit;
+ int32_t pass, devnum;
+ dev_info_t *dip;
+ sbd_board_t *sbp = SBDH2BD(hp->h_sbd);
+ sbdp_handle_t *hdp;
+ sbd_devlist_t *devlist;
+ sbd_comp_type_t nodetype;
+ int err = 0;
+ int dev_canceled;
+
+ pass = 1;
+ hdp = sbd_get_sbdp_handle(sbp, hp);
+
+ sbp->sb_busy = 1;
+ while ((devlist =
+ sbd_get_release_devlist(hp, &devnum, pass)) != NULL) {
+
+ err = sbd_pre_release_devlist(hp, devlist, devnum);
+ if (err < 0) {
+ dev_canceled = 1;
+ break;
+ } else if (err > 0) {
+ pass++;
+ continue;
+ }
+
+ dev_canceled = 0;
+ for (n = 0; n < devnum; n++) {
+ dip = devlist[n].dv_dip;
+ nodetype = sbd_get_devtype(hp, dip);
+
+ unit = sbdp_get_unit_num(hdp, dip);
+ if (unit < 0) {
+ SBD_GET_PERR(hdp->h_err, SBD_HD2ERR(hp));
+ break;
+ }
+
+ if ((nodetype == SBD_COMP_MEM) &&
+ sbd_release_mem(hp, dip, unit)) {
+
+ dev_canceled++;
+ }
+
+ sbd_release_done(hp, nodetype, dip);
+ }
+
+ err = sbd_post_release_devlist(hp, devlist, devnum);
+
+ if (err < 0)
+ break;
+
+ if (dev_canceled)
+ break;
+
+ pass++;
+ }
+ sbp->sb_busy = 0;
+
+ sbd_release_sbdp_handle(hdp);
+
+ if (dev_canceled)
+ return (dev_canceled);
+
+ return (err);
+}
+
+static int
+sbd_dev_unconfigure(sbd_handle_t *hp)
+{
+ int n, unit;
+ int32_t pass, devnum;
+ dev_info_t *dip;
+ sbd_devlist_t *devlist;
+ sbdp_handle_t *hdp;
+ sbd_comp_type_t nodetype;
+ sbd_board_t *sbp = SBDH2BD(hp->h_sbd);
+ int dev_canceled = 0;
+ static fn_t f = "sbd_dev_unconfigure";
+
+ PR_ALL("%s...\n", f);
+
+ pass = 1;
+ hdp = sbd_get_sbdp_handle(sbp, hp);
+
+ while ((devlist = sbd_get_detach_devlist(hp, &devnum, pass)) != NULL) {
+ int err, detach_err = 0;
+
+ err = sbd_pre_detach_devlist(hp, devlist, devnum);
+ if (err) {
+ /*
+ * Only cancel the operation for memory in
+ * case of failure.
+ */
+ nodetype = sbd_get_devtype(hp, devlist->dv_dip);
+ if (nodetype == SBD_COMP_MEM)
+ dev_canceled = 1;
+ (void) sbd_post_detach_devlist(hp, devlist, devnum);
+ break;
+ }
+
+ for (n = 0; n < devnum; n++) {
+ sbderror_t *ep;
+
+ ep = &devlist[n].dv_error;
+ SBD_SET_ERRNO(ep, 0);
+ SBD_SET_ERR(ep, 0);
+ dip = devlist[n].dv_dip;
+ nodetype = sbd_get_devtype(hp, dip);
+
+ unit = sbdp_get_unit_num(hdp, dip);
+ if (unit < 0) {
+ SBD_GET_PERR(hdp->h_err, SBD_HD2ERR(hp));
+ break;
+ }
+
+ switch (nodetype) {
+ case SBD_COMP_MEM:
+ dev_canceled = sbd_detach_mem(hp, ep, unit);
+ break;
+
+ case SBD_COMP_CPU:
+ sbd_detach_cpu(hp, ep, dip, unit);
+ break;
+
+ case SBD_COMP_IO:
+ sbd_detach_io(hp, ep, dip, unit);
+ break;
+
+ default:
+ SBD_SET_ERRNO(ep, ENOTTY);
+ break;
+ }
+
+ if (sbd_set_err_in_hdl(hp, ep) == 0) {
+ detach_err = -1;
+ break;
+ }
+
+ }
+ err = sbd_post_detach_devlist(hp, devlist, devnum);
+ if ((err < 0) || (detach_err < 0))
+ break;
+
+ pass++;
+ }
+
+ sbd_release_sbdp_handle(hdp);
+ return (dev_canceled);
+}
+
+int
+sbd_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
+sbd_attach_cpu(sbd_handle_t *hp, sbderror_t *ep, dev_info_t *dip, int unit)
+{
+ int rv = 0;
+ processorid_t cpuid;
+ sbdp_handle_t *hdp;
+ sbd_board_t *sbp = SBDH2BD(hp->h_sbd);
+ static fn_t f = "sbd_attach_cpu";
+ char *pathname;
+
+ ASSERT(MUTEX_HELD(&cpu_lock));
+
+ ASSERT(dip);
+
+ /*
+ * With the introduction of CMP devices, the CPU nodes
+ * are no longer directly under the top node. Since
+ * there is no plan to support CPU attach in the near
+ * future, a branch configure operation is not required.
+ */
+
+ hdp = sbd_get_sbdp_handle(sbp, hp);
+ cpuid = sbdp_get_cpuid(hdp, dip);
+ if (cpuid < 0) {
+ rv = -1;
+ SBD_GET_PERR(hdp->h_err, ep);
+ } else if ((rv = cpu_configure(cpuid)) != 0) {
+ cmn_err(CE_WARN,
+ "sbd:%s: cpu_configure for cpuid %d failed",
+ f, cpuid);
+ SBD_SET_ERR(ep, sbd_errno2ecode(rv));
+ }
+ sbd_release_sbdp_handle(hdp);
+
+ if (rv == 0) {
+ ASSERT(sbp->sb_cpupath[unit] != NULL);
+ pathname = sbp->sb_cpupath[unit];
+ (void) ddi_pathname(dip, pathname);
+ }
+}
+
+/*
+ * translate errno
+ */
+void
+sbd_errno_decode(int err, sbderror_t *ep, dev_info_t *dip)
+{
+ ASSERT(err != 0);
+
+ switch (err) {
+ case ENOMEM:
+ SBD_SET_ERR(ep, ESBD_NOMEM);
+ break;
+
+ case EBUSY:
+ SBD_SET_ERR(ep, ESBD_BUSY);
+ break;
+
+ case EIO:
+ SBD_SET_ERR(ep, ESBD_IO);
+ break;
+
+ case ENXIO:
+ SBD_SET_ERR(ep, ESBD_NODEV);
+ break;
+
+ case EINVAL:
+ SBD_SET_ERR(ep, ESBD_INVAL);
+ break;
+
+ case EFAULT:
+ default:
+ SBD_SET_ERR(ep, ESBD_FAULT);
+ break;
+ }
+
+ (void) ddi_pathname(dip, SBD_GET_ERRSTR(ep));
+}
+
+static void
+sbd_detach_cpu(sbd_handle_t *hp, sbderror_t *ep, dev_info_t *dip, int unit)
+{
+ processorid_t cpuid;
+ int rv;
+ sbdp_handle_t *hdp;
+ sbd_board_t *sbp = SBDH2BD(hp->h_sbd);
+ sbd_error_t *spe;
+ static fn_t f = "sbd_detach_cpu";
+
+ ASSERT(MUTEX_HELD(&cpu_lock));
+
+ ASSERT(dip);
+ hdp = sbd_get_sbdp_handle(sbp, hp);
+ spe = hdp->h_err;
+ cpuid = sbdp_get_cpuid(hdp, dip);
+ if (cpuid < 0) {
+ SBD_GET_PERR(spe, ep);
+ sbd_release_sbdp_handle(hdp);
+ return;
+ }
+
+ if ((rv = cpu_unconfigure(cpuid)) != 0) {
+ SBD_SET_ERR(ep, sbd_errno2ecode(rv));
+ SBD_SET_ERRSTR(ep, sbp->sb_cpupath[unit]);
+ cmn_err(CE_WARN,
+ "sbd:%s: cpu_unconfigure for cpu %d failed",
+ f, cpuid);
+ sbd_release_sbdp_handle(hdp);
+ return;
+ }
+ sbd_release_sbdp_handle(hdp);
+
+ /*
+ * Since CPU nodes are no longer configured in CPU
+ * attach, the corresponding branch unconfigure
+ * operation that would be performed here is also
+ * no longer required.
+ */
+}
+
+
+int
+sbd_detach_mem(sbd_handle_t *hp, sbderror_t *ep, int unit)
+{
+ sbd_mem_unit_t *mp;
+ sbd_board_t *sbp = SBDH2BD(hp->h_sbd);
+ int i, rv;
+ static fn_t f = "sbd_detach_mem";
+
+ mp = SBD_GET_BOARD_MEMUNIT(sbp, unit);
+
+ if (sbd_detach_memory(hp, ep, mp, unit)) {
+ cmn_err(CE_WARN, "%s: detach fail", f);
+ return (-1);
+ }
+
+ /*
+ * Now detach mem devinfo nodes with status lock held.
+ */
+ for (i = 0; i < SBD_NUM_MC_PER_BOARD; i++) {
+ dev_info_t *fdip = NULL;
+
+ if (mp->sbm_dip[i] == NULL)
+ continue;
+ ASSERT(e_ddi_branch_held(mp->sbm_dip[i]));
+ mutex_enter(&sbp->sb_slock);
+ rv = e_ddi_branch_unconfigure(mp->sbm_dip[i], &fdip,
+ DEVI_BRANCH_EVENT);
+ mutex_exit(&sbp->sb_slock);
+ if (rv) {
+ /*
+ * If non-NULL, fdip is returned held and must be
+ * released.
+ */
+ if (fdip != NULL) {
+ sbd_errno_decode(rv, ep, fdip);
+ ddi_release_devi(fdip);
+ } else {
+ sbd_errno_decode(rv, ep, mp->sbm_dip[i]);
+ }
+ }
+ }
+
+ return (0);
+}
+
+/* start beginning of sbd.c */
+
+/*
+ * MDR memory support - somewhat disabled for now.
+ * UNSAFE unsafe driver code - I don't think we want this.
+ * need to check.
+ * DEVNODE This driver creates attachment points for individual
+ * components as well as boards. We only need board
+ * support.
+ * DEV2DEVSET Put only present devices in devset.
+ */
+
+
+static sbd_state_t
+rstate_cvt(sbd_istate_t state)
+{
+ sbd_state_t cs;
+
+ switch (state) {
+ case SBD_STATE_EMPTY:
+ cs = SBD_STAT_EMPTY;
+ break;
+ case SBD_STATE_OCCUPIED:
+ case SBD_STATE_FATAL:
+ cs = SBD_STAT_DISCONNECTED;
+ break;
+ case SBD_STATE_CONFIGURED:
+ case SBD_STATE_CONNECTED:
+ case SBD_STATE_UNCONFIGURED:
+ case SBD_STATE_PARTIAL:
+ case SBD_STATE_RELEASE:
+ case SBD_STATE_UNREFERENCED:
+ cs = SBD_STAT_CONNECTED;
+ break;
+ default:
+ cs = SBD_STAT_NONE;
+ break;
+ }
+
+ return (cs);
+}
+
+
+sbd_state_t
+ostate_cvt(sbd_istate_t state)
+{
+ sbd_state_t cs;
+
+ switch (state) {
+ case SBD_STATE_EMPTY:
+ case SBD_STATE_OCCUPIED:
+ case SBD_STATE_UNCONFIGURED:
+ case SBD_STATE_CONNECTED:
+ case SBD_STATE_FATAL:
+ cs = SBD_STAT_UNCONFIGURED;
+ break;
+ case SBD_STATE_PARTIAL:
+ case SBD_STATE_CONFIGURED:
+ case SBD_STATE_RELEASE:
+ case SBD_STATE_UNREFERENCED:
+ cs = SBD_STAT_CONFIGURED;
+ break;
+ default:
+ cs = SBD_STAT_NONE;
+ break;
+ }
+
+ return (cs);
+}
+
+int
+sbd_dealloc_instance(sbd_board_t *sbp, int max_boards)
+{
+ int b;
+ sbd_board_t *list = sbp;
+ static fn_t f = "sbd_dealloc_instance";
+
+ PR_ALL("%s...\n", f);
+
+ if (sbp == NULL) {
+ return (-1);
+ }
+
+ for (b = 0; b < max_boards; b++) {
+ sbd_board_destroy(sbp++);
+ }
+
+ FREESTRUCT(list, sbd_board_t, max_boards);
+
+ return (0);
+}
+
+static sbd_devset_t
+sbd_dev2devset(sbd_comp_id_t *cid)
+{
+ static fn_t f = "sbd_dev2devset";
+
+ sbd_devset_t devset;
+ int unit = cid->c_unit;
+
+ switch (cid->c_type) {
+ case SBD_COMP_NONE:
+ devset = DEVSET(SBD_COMP_CPU, DEVSET_ANYUNIT);
+ devset |= DEVSET(SBD_COMP_MEM, DEVSET_ANYUNIT);
+ devset |= DEVSET(SBD_COMP_IO, DEVSET_ANYUNIT);
+ break;
+
+ case SBD_COMP_CPU:
+ if ((unit > MAX_CPU_UNITS_PER_BOARD) || (unit < 0)) {
+ PR_ALL("%s: invalid cpu unit# = %d",
+ f, unit);
+ devset = 0;
+ } else
+ /*
+ * Generate a devset that includes all the
+ * cores of a CMP device. If this is not a
+ * CMP, the extra cores will be eliminated
+ * later since they are not present. This is
+ * also true for CMP devices that do not have
+ * all cores active.
+ */
+ devset = DEVSET(SBD_COMP_CMP, unit);
+
+ break;
+
+ case SBD_COMP_MEM:
+
+ if ((unit > MAX_MEM_UNITS_PER_BOARD) || (unit < 0)) {
+#ifdef XXX_jeffco
+ PR_ALL("%s: invalid mem unit# = %d",
+ f, unit);
+ devset = 0;
+#endif
+ devset = DEVSET(cid->c_type, 0);
+ PR_ALL("%s: adjusted MEM devset = 0x%x\n",
+ f, devset);
+ } else
+ devset = DEVSET(cid->c_type, unit);
+ break;
+
+ case SBD_COMP_IO:
+ if ((unit > MAX_IO_UNITS_PER_BOARD) || (unit < 0)) {
+ PR_ALL("%s: invalid io unit# = %d",
+ f, unit);
+ devset = 0;
+ } else
+ devset = DEVSET(cid->c_type, unit);
+
+ break;
+
+ default:
+ case SBD_COMP_UNKNOWN:
+ devset = 0;
+ break;
+ }
+
+ return (devset);
+}
+
+/*
+ * Simple mutex for covering handle list ops as it is only
+ * used "infrequently". No need to add another mutex to the sbd_board_t.
+ */
+static kmutex_t sbd_handle_list_mutex;
+
+static sbd_handle_t *
+sbd_get_handle(dev_t dev, sbd_softstate_t *softsp, intptr_t arg,
+ sbd_init_arg_t *iap)
+{
+ sbd_handle_t *hp;
+ sbderror_t *ep;
+ sbd_priv_handle_t *shp;
+ sbd_board_t *sbp = softsp->sbd_boardlist;
+ int board;
+
+ board = SBDGETSLOT(dev);
+ ASSERT(board < softsp->max_boards);
+ sbp += board;
+
+ /*
+ * Brand-new handle.
+ */
+ shp = kmem_zalloc(sizeof (sbd_priv_handle_t), KM_SLEEP);
+ shp->sh_arg = (void *)arg;
+
+ hp = MACHHD2HD(shp);
+
+ ep = &shp->sh_err;
+
+ hp->h_err = ep;
+ hp->h_sbd = (void *) sbp;
+ hp->h_dev = iap->dev;
+ hp->h_cmd = iap->cmd;
+ hp->h_mode = iap->mode;
+ sbd_init_err(ep);
+
+ mutex_enter(&sbd_handle_list_mutex);
+ shp->sh_next = sbp->sb_handle;
+ sbp->sb_handle = shp;
+ mutex_exit(&sbd_handle_list_mutex);
+
+ return (hp);
+}
+
+void
+sbd_init_err(sbderror_t *ep)
+{
+ ep->e_errno = 0;
+ ep->e_code = 0;
+ ep->e_rsc[0] = '\0';
+}
+
+int
+sbd_set_err_in_hdl(sbd_handle_t *hp, sbderror_t *ep)
+{
+ sbderror_t *hep = SBD_HD2ERR(hp);
+
+ /*
+ * If there is an error logged already, don't rewrite it
+ */
+ if (SBD_GET_ERR(hep) || SBD_GET_ERRNO(hep)) {
+ return (0);
+ }
+
+ if (SBD_GET_ERR(ep) || SBD_GET_ERRNO(ep)) {
+ SBD_SET_ERR(hep, SBD_GET_ERR(ep));
+ SBD_SET_ERRNO(hep, SBD_GET_ERRNO(ep));
+ SBD_SET_ERRSTR(hep, SBD_GET_ERRSTR(ep));
+ return (0);
+ }
+
+ return (-1);
+}
+
+static void
+sbd_release_handle(sbd_handle_t *hp)
+{
+ sbd_priv_handle_t *shp, **shpp;
+ sbd_board_t *sbp;
+ static fn_t f = "sbd_release_handle";
+
+ if (hp == NULL)
+ return;
+
+ sbp = SBDH2BD(hp->h_sbd);
+
+ shp = HD2MACHHD(hp);
+
+ mutex_enter(&sbd_handle_list_mutex);
+ /*
+ * Locate the handle in the board's reference list.
+ */
+ for (shpp = &sbp->sb_handle; (*shpp) && ((*shpp) != shp);
+ shpp = &((*shpp)->sh_next))
+ /* empty */;
+
+ if (*shpp == NULL) {
+ cmn_err(CE_PANIC,
+ "sbd:%s: handle not found in board %d",
+ f, sbp->sb_num);
+ /*NOTREACHED*/
+ } else {
+ *shpp = shp->sh_next;
+ }
+ mutex_exit(&sbd_handle_list_mutex);
+
+ if (hp->h_opts.copts != NULL) {
+ FREESTRUCT(hp->h_opts.copts, char, hp->h_opts.size);
+ }
+
+ FREESTRUCT(shp, sbd_priv_handle_t, 1);
+}
+
+sbdp_handle_t *
+sbd_get_sbdp_handle(sbd_board_t *sbp, sbd_handle_t *hp)
+{
+ sbdp_handle_t *hdp;
+
+ hdp = kmem_zalloc(sizeof (sbdp_handle_t), KM_SLEEP);
+ hdp->h_err = kmem_zalloc(sizeof (sbd_error_t), KM_SLEEP);
+ if (sbp == NULL) {
+ hdp->h_board = -1;
+ hdp->h_wnode = -1;
+ } else {
+ hdp->h_board = sbp->sb_num;
+ hdp->h_wnode = sbp->sb_wnode;
+ }
+
+ if (hp == NULL) {
+ hdp->h_flags = 0;
+ hdp->h_opts = NULL;
+ } else {
+ hdp->h_flags = SBD_2_SBDP_FLAGS(hp->h_flags);
+ hdp->h_opts = &hp->h_opts;
+ }
+
+ return (hdp);
+}
+
+void
+sbd_release_sbdp_handle(sbdp_handle_t *hdp)
+{
+ if (hdp == NULL)
+ return;
+
+ kmem_free(hdp->h_err, sizeof (sbd_error_t));
+ kmem_free(hdp, sizeof (sbdp_handle_t));
+}
+
+void
+sbd_reset_error_sbdph(sbdp_handle_t *hdp)
+{
+ if ((hdp != NULL) && (hdp->h_err != NULL)) {
+ bzero(hdp->h_err, sizeof (sbd_error_t));
+ }
+}
+
+static int
+sbd_copyin_ioarg(sbd_handle_t *hp, int mode, int cmd, sbd_cmd_t *cmdp,
+ sbd_ioctl_arg_t *iap)
+{
+ static fn_t f = "sbd_copyin_ioarg";
+
+ if (iap == NULL)
+ return (EINVAL);
+
+ bzero((caddr_t)cmdp, sizeof (sbd_cmd_t));
+
+#ifdef _MULTI_DATAMODEL
+ if (ddi_model_convert_from(mode & FMODELS) == DDI_MODEL_ILP32) {
+ sbd_cmd32_t scmd32;
+
+ bzero((caddr_t)&scmd32, sizeof (sbd_cmd32_t));
+
+ if (ddi_copyin((void *)iap, (void *)&scmd32,
+ sizeof (sbd_cmd32_t), mode)) {
+ cmn_err(CE_WARN,
+ "sbd:%s: (32bit) failed to copyin "
+ "sbdcmd-struct", f);
+ return (EFAULT);
+ }
+ cmdp->cmd_cm.c_id.c_type = scmd32.cmd_cm.c_id.c_type;
+ cmdp->cmd_cm.c_id.c_unit = scmd32.cmd_cm.c_id.c_unit;
+ bcopy(&scmd32.cmd_cm.c_id.c_name[0],
+ &cmdp->cmd_cm.c_id.c_name[0], OBP_MAXPROPNAME);
+ cmdp->cmd_cm.c_flags = scmd32.cmd_cm.c_flags;
+ cmdp->cmd_cm.c_len = scmd32.cmd_cm.c_len;
+ cmdp->cmd_cm.c_opts = (caddr_t)scmd32.cmd_cm.c_opts;
+
+ if (cmd == SBD_CMD_PASSTHRU) {
+ PR_BYP("passthru copyin: iap=%p, sz=%d", iap,
+ sizeof (sbd_cmd32_t));
+ PR_BYP("passthru copyin: c_opts=%p, c_len=%d",
+ scmd32.cmd_cm.c_opts,
+ scmd32.cmd_cm.c_len);
+ }
+
+ switch (cmd) {
+ case SBD_CMD_STATUS:
+ cmdp->cmd_stat.s_nbytes = scmd32.cmd_stat.s_nbytes;
+ cmdp->cmd_stat.s_statp =
+ (caddr_t)scmd32.cmd_stat.s_statp;
+ break;
+ default:
+ break;
+
+ }
+ } else
+#endif /* _MULTI_DATAMODEL */
+ if (ddi_copyin((void *)iap, (void *)cmdp,
+ sizeof (sbd_cmd_t), mode) != 0) {
+ cmn_err(CE_WARN,
+ "sbd:%s: failed to copyin sbd cmd_t struct", f);
+ return (EFAULT);
+ }
+ /*
+ * A user may set platform specific options so we need to
+ * copy them in
+ */
+ if ((cmd != SBD_CMD_STATUS) && ((hp->h_opts.size = cmdp->cmd_cm.c_len)
+ > 0)) {
+ hp->h_opts.size += 1; /* For null termination of string. */
+ hp->h_opts.copts = GETSTRUCT(char, hp->h_opts.size);
+ if (ddi_copyin((void *)cmdp->cmd_cm.c_opts,
+ (void *)hp->h_opts.copts,
+ cmdp->cmd_cm.c_len, hp->h_mode) != 0) {
+ /* copts is freed in sbd_release_handle(). */
+ cmn_err(CE_WARN,
+ "sbd:%s: failed to copyin options", f);
+ return (EFAULT);
+ }
+ }
+
+ return (0);
+}
+
+static int
+sbd_copyout_ioarg(int mode, int cmd, sbd_cmd_t *scp, sbd_ioctl_arg_t *iap)
+{
+ static fn_t f = "sbd_copyout_ioarg";
+
+ if ((iap == NULL) || (scp == NULL))
+ return (EINVAL);
+
+#ifdef _MULTI_DATAMODEL
+ if (ddi_model_convert_from(mode & FMODELS) == DDI_MODEL_ILP32) {
+ sbd_cmd32_t scmd32;
+
+ scmd32.cmd_cm.c_id.c_type = scp->cmd_cm.c_id.c_type;
+ scmd32.cmd_cm.c_id.c_unit = scp->cmd_cm.c_id.c_unit;
+ bcopy(scp->cmd_cm.c_id.c_name,
+ scmd32.cmd_cm.c_id.c_name, OBP_MAXPROPNAME);
+
+ scmd32.cmd_cm.c_flags = scp->cmd_cm.c_flags;
+
+ switch (cmd) {
+ case SBD_CMD_GETNCM:
+ scmd32.cmd_getncm.g_ncm = scp->cmd_getncm.g_ncm;
+ break;
+ default:
+ break;
+ }
+
+ if (ddi_copyout((void *)&scmd32, (void *)iap,
+ sizeof (sbd_cmd32_t), mode)) {
+ cmn_err(CE_WARN,
+ "sbd:%s: (32bit) failed to copyout "
+ "sbdcmd struct", f);
+ return (EFAULT);
+ }
+ } else
+#endif /* _MULTI_DATAMODEL */
+ if (ddi_copyout((void *)scp, (void *)iap,
+ sizeof (sbd_cmd_t), mode) != 0) {
+ cmn_err(CE_WARN,
+ "sbd:%s: failed to copyout sbdcmd struct", f);
+ return (EFAULT);
+ }
+
+ return (0);
+}
+
+static int
+sbd_copyout_errs(int mode, sbd_ioctl_arg_t *iap, void *arg)
+{
+ static fn_t f = "sbd_copyout_errs";
+ sbd_ioctl_arg_t *uap;
+
+ uap = (sbd_ioctl_arg_t *)arg;
+
+#ifdef _MULTI_DATAMODEL
+ if (ddi_model_convert_from(mode & FMODELS) == DDI_MODEL_ILP32) {
+ sbd_error32_t err32;
+ sbd_ioctl_arg32_t *uap32;
+
+ uap32 = (sbd_ioctl_arg32_t *)arg;
+
+ err32.e_code = iap->ie_code;
+ (void) strcpy(err32.e_rsc, iap->ie_rsc);
+
+ if (ddi_copyout((void *)&err32, (void *)&uap32->i_err,
+ sizeof (sbd_error32_t), mode)) {
+ cmn_err(CE_WARN,
+ "sbd:%s: failed to copyout ioctl32 errs",
+ f);
+ return (EFAULT);
+ }
+ } else
+#endif /* _MULTI_DATAMODEL */
+ if (ddi_copyout((void *)&iap->i_err, (void *)&uap->i_err,
+ sizeof (sbd_error_t), mode) != 0) {
+ cmn_err(CE_WARN,
+ "sbd:%s: failed to copyout ioctl errs", f);
+ return (EFAULT);
+ }
+
+ return (0);
+}
+
+/*
+ * State transition policy is that if at least one
+ * device cannot make the transition, then none of
+ * the requested devices are allowed to transition.
+ *
+ * Returns the state that is in error, if any.
+ */
+static int
+sbd_check_transition(sbd_board_t *sbp, sbd_devset_t *devsetp,
+ struct sbd_state_trans *transp)
+{
+ int s, ut;
+ int state_err = 0;
+ sbd_devset_t devset;
+ static fn_t f = "sbd_check_transition";
+
+ devset = *devsetp;
+
+ if (!devset) {
+ /*
+ * Transition does not deal with any components.
+ * This is the case for addboard/deleteboard.
+ */
+ PR_ALL("%s: no devs: requested devset = 0x%x,"
+ " final devset = 0x%x\n",
+ f, (uint_t)*devsetp, (uint_t)devset);
+
+ return (0);
+ }
+
+ if (DEVSET_IN_SET(devset, SBD_COMP_MEM, DEVSET_ANYUNIT)) {
+ for (ut = 0; ut < MAX_MEM_UNITS_PER_BOARD; ut++) {
+ if (DEVSET_IN_SET(devset, SBD_COMP_MEM, ut) == 0)
+ continue;
+ s = (int)SBD_DEVICE_STATE(sbp, SBD_COMP_MEM, ut);
+ if (transp->x_op[s].x_rv) {
+ if (!state_err)
+ state_err = s;
+ DEVSET_DEL(devset, SBD_COMP_MEM, ut);
+ }
+ }
+ }
+
+ if (DEVSET_IN_SET(devset, SBD_COMP_CPU, DEVSET_ANYUNIT)) {
+ for (ut = 0; ut < MAX_CPU_UNITS_PER_BOARD; ut++) {
+ if (DEVSET_IN_SET(devset, SBD_COMP_CPU, ut) == 0)
+ continue;
+ s = (int)SBD_DEVICE_STATE(sbp, SBD_COMP_CPU, ut);
+ if (transp->x_op[s].x_rv) {
+ if (!state_err)
+ state_err = s;
+ DEVSET_DEL(devset, SBD_COMP_CPU, ut);
+ }
+ }
+ }
+
+ if (DEVSET_IN_SET(devset, SBD_COMP_IO, DEVSET_ANYUNIT)) {
+ for (ut = 0; ut < MAX_IO_UNITS_PER_BOARD; ut++) {
+ if (DEVSET_IN_SET(devset, SBD_COMP_IO, ut) == 0)
+ continue;
+ s = (int)SBD_DEVICE_STATE(sbp, SBD_COMP_IO, ut);
+ if (transp->x_op[s].x_rv) {
+ if (!state_err)
+ state_err = s;
+ DEVSET_DEL(devset, SBD_COMP_IO, ut);
+ }
+ }
+ }
+
+ PR_ALL("%s: requested devset = 0x%x, final devset = 0x%x\n",
+ f, (uint_t)*devsetp, (uint_t)devset);
+
+ *devsetp = devset;
+ /*
+ * If there are some remaining components for which
+ * this state transition is valid, then allow them
+ * through, otherwise if none are left then return
+ * the state error.
+ */
+ return (devset ? 0 : state_err);
+}
+
+/*
+ * pre-op entry point must SET_ERRNO(), if needed.
+ * Return value of non-zero indicates failure.
+ */
+static int
+sbd_pre_op(sbd_handle_t *hp)
+{
+ int rv = 0, t;
+ int cmd, serr = 0;
+ sbd_devset_t devset;
+ sbd_board_t *sbp = SBDH2BD(hp->h_sbd);
+ sbd_priv_handle_t *shp = HD2MACHHD(hp);
+ sbderror_t *ep = SBD_HD2ERR(hp);
+ sbd_cmd_t *cmdp;
+ static fn_t f = "sbd_pre_op";
+
+ cmd = hp->h_cmd;
+ devset = shp->sh_devset;
+
+ switch (cmd) {
+ case SBD_CMD_CONNECT:
+ case SBD_CMD_DISCONNECT:
+ case SBD_CMD_UNCONFIGURE:
+ case SBD_CMD_CONFIGURE:
+ case SBD_CMD_ASSIGN:
+ case SBD_CMD_UNASSIGN:
+ case SBD_CMD_POWERON:
+ case SBD_CMD_POWEROFF:
+ case SBD_CMD_TEST:
+ /* ioctls allowed if caller has write permission */
+ if (!(hp->h_mode & FWRITE)) {
+ SBD_SET_ERRNO(ep, EPERM);
+ return (-1);
+ }
+
+ default:
+ break;
+ }
+
+ hp->h_iap = GETSTRUCT(sbd_ioctl_arg_t, 1);
+ rv = sbd_copyin_ioarg(hp, hp->h_mode, cmd,
+ (sbd_cmd_t *)hp->h_iap, shp->sh_arg);
+ if (rv) {
+ SBD_SET_ERRNO(ep, rv);
+ FREESTRUCT(hp->h_iap, sbd_ioctl_arg_t, 1);
+ hp->h_iap = NULL;
+ cmn_err(CE_WARN, "%s: copyin fail", f);
+ return (-1);
+ } else {
+ cmdp = (sbd_cmd_t *)hp->h_iap;
+ if (cmdp->cmd_cm.c_id.c_name[0] != '\0') {
+
+ cmdp->cmd_cm.c_id.c_type = SBD_COMP(sbd_name_to_idx(
+ cmdp->cmd_cm.c_id.c_name));
+ if (cmdp->cmd_cm.c_id.c_type == SBD_COMP_MEM) {
+ if (cmdp->cmd_cm.c_id.c_unit == -1)
+ cmdp->cmd_cm.c_id.c_unit = 0;
+ }
+ }
+ devset = shp->sh_orig_devset = shp->sh_devset =
+ sbd_dev2devset(&cmdp->cmd_cm.c_id);
+ if (devset == 0) {
+ SBD_SET_ERRNO(ep, EINVAL);
+ FREESTRUCT(hp->h_iap, sbd_ioctl_arg_t, 1);
+ hp->h_iap = NULL;
+ return (-1);
+ }
+ }
+
+ /*
+ * Always turn on these bits ala Sunfire DR.
+ */
+ hp->h_flags |= SBD_FLAG_DEVI_FORCE;
+
+ if (cmdp->cmd_cm.c_flags & SBD_FLAG_FORCE)
+ hp->h_flags |= SBD_IOCTL_FLAG_FORCE;
+
+ /*
+ * Check for valid state transitions.
+ */
+ if (!serr && ((t = CMD2INDEX(cmd)) != -1)) {
+ struct sbd_state_trans *transp;
+ int state_err;
+
+ transp = &sbd_state_transition[t];
+ ASSERT(transp->x_cmd == cmd);
+
+ state_err = sbd_check_transition(sbp, &devset, transp);
+
+ if (state_err < 0) {
+ /*
+ * Invalidate device.
+ */
+ SBD_SET_ERRNO(ep, ENOTTY);
+ serr = -1;
+ PR_ALL("%s: invalid devset (0x%x)\n",
+ f, (uint_t)devset);
+ } else if (state_err != 0) {
+ /*
+ * State transition is not a valid one.
+ */
+ SBD_SET_ERRNO(ep, transp->x_op[state_err].x_err);
+ serr = transp->x_op[state_err].x_rv;
+ PR_ALL("%s: invalid state %s(%d) for cmd %s(%d)\n",
+ f, sbd_state_str[state_err], state_err,
+ SBD_CMD_STR(cmd), cmd);
+ }
+ if (serr && SBD_GET_ERRNO(ep) != 0) {
+ /*
+ * A state transition error occurred.
+ */
+ if (serr < 0) {
+ SBD_SET_ERR(ep, ESBD_INVAL);
+ } else {
+ SBD_SET_ERR(ep, ESBD_STATE);
+ }
+ PR_ALL("%s: invalid state transition\n", f);
+ } else {
+ shp->sh_devset = devset;
+ }
+ }
+
+ if (serr && !rv && hp->h_iap) {
+
+ /*
+ * There was a state error. We successfully copied
+ * in the ioctl argument, so let's fill in the
+ * error and copy it back out.
+ */
+
+ if (SBD_GET_ERR(ep) && SBD_GET_ERRNO(ep) == 0)
+ SBD_SET_ERRNO(ep, EIO);
+
+ SBD_SET_IOCTL_ERR(&hp->h_iap->i_err,
+ ep->e_code,
+ ep->e_rsc);
+ (void) sbd_copyout_errs(hp->h_mode, hp->h_iap, shp->sh_arg);
+ FREESTRUCT(hp->h_iap, sbd_ioctl_arg_t, 1);
+ hp->h_iap = NULL;
+ rv = -1;
+ }
+
+ return (rv);
+}
+
+static void
+sbd_post_op(sbd_handle_t *hp)
+{
+ int cmd;
+ sbderror_t *ep = SBD_HD2ERR(hp);
+ sbd_priv_handle_t *shp = HD2MACHHD(hp);
+ sbd_board_t *sbp = SBDH2BD(hp->h_sbd);
+
+ cmd = hp->h_cmd;
+
+ switch (cmd) {
+ case SBD_CMD_CONFIGURE:
+ case SBD_CMD_UNCONFIGURE:
+ case SBD_CMD_CONNECT:
+ case SBD_CMD_DISCONNECT:
+ sbp->sb_time = gethrestime_sec();
+ break;
+
+ default:
+ break;
+ }
+
+ if (SBD_GET_ERR(ep) && SBD_GET_ERRNO(ep) == 0) {
+ SBD_SET_ERRNO(ep, EIO);
+ }
+
+ if (shp->sh_arg != NULL) {
+
+ if (SBD_GET_ERR(ep) != ESBD_NOERROR) {
+
+ SBD_SET_IOCTL_ERR(&hp->h_iap->i_err,
+ ep->e_code,
+ ep->e_rsc);
+
+ (void) sbd_copyout_errs(hp->h_mode, hp->h_iap,
+ shp->sh_arg);
+ }
+
+ if (hp->h_iap != NULL) {
+ FREESTRUCT(hp->h_iap, sbd_ioctl_arg_t, 1);
+ hp->h_iap = NULL;
+ }
+ }
+}
+
+static int
+sbd_probe_board(sbd_handle_t *hp)
+{
+ int rv;
+ sbd_board_t *sbp;
+ sbdp_handle_t *hdp;
+ static fn_t f = "sbd_probe_board";
+
+ sbp = SBDH2BD(hp->h_sbd);
+
+ ASSERT(sbp != NULL);
+ PR_ALL("%s for board %d", f, sbp->sb_num);
+
+
+ hdp = sbd_get_sbdp_handle(sbp, hp);
+
+ if ((rv = sbdp_connect_board(hdp)) != 0) {
+ sbderror_t *ep = SBD_HD2ERR(hp);
+
+ SBD_GET_PERR(hdp->h_err, ep);
+ }
+
+ /*
+ * We need to force a recache after the connect. The cached
+ * info may be incorrect
+ */
+ mutex_enter(&sbp->sb_flags_mutex);
+ sbp->sb_flags &= ~SBD_BOARD_STATUS_CACHED;
+ mutex_exit(&sbp->sb_flags_mutex);
+
+ SBD_INJECT_ERR(SBD_PROBE_BOARD_PSEUDO_ERR, hp->h_err, EIO,
+ ESGT_PROBE, NULL);
+
+ sbd_release_sbdp_handle(hdp);
+
+ return (rv);
+}
+
+static int
+sbd_deprobe_board(sbd_handle_t *hp)
+{
+ int rv;
+ sbdp_handle_t *hdp;
+ sbd_board_t *sbp;
+ static fn_t f = "sbd_deprobe_board";
+
+ PR_ALL("%s...\n", f);
+
+ sbp = SBDH2BD(hp->h_sbd);
+
+ hdp = sbd_get_sbdp_handle(sbp, hp);
+
+ if ((rv = sbdp_disconnect_board(hdp)) != 0) {
+ sbderror_t *ep = SBD_HD2ERR(hp);
+
+ SBD_GET_PERR(hdp->h_err, ep);
+ }
+
+ mutex_enter(&sbp->sb_flags_mutex);
+ sbp->sb_flags &= ~SBD_BOARD_STATUS_CACHED;
+ mutex_exit(&sbp->sb_flags_mutex);
+
+ SBD_INJECT_ERR(SBD_DEPROBE_BOARD_PSEUDO_ERR, hp->h_err, EIO,
+ ESGT_DEPROBE, NULL);
+
+ sbd_release_sbdp_handle(hdp);
+ return (rv);
+}
+
+/*
+ * Check if a CPU node is part of a CMP.
+ */
+int
+sbd_is_cmp_child(dev_info_t *dip)
+{
+ dev_info_t *pdip;
+
+ if (strcmp(ddi_node_name(dip), "cpu") != 0) {
+ return (0);
+ }
+
+ pdip = ddi_get_parent(dip);
+
+ ASSERT(pdip);
+
+ if (strcmp(ddi_node_name(pdip), "cmp") == 0) {
+ return (1);
+ }
+
+ return (0);
+}
+
+/*
+ * Returns the nodetype if dip is a top dip on the board of
+ * interest or SBD_COMP_UNKNOWN otherwise
+ */
+static sbd_comp_type_t
+get_node_type(sbd_board_t *sbp, dev_info_t *dip, int *unitp)
+{
+ int idx, unit;
+ sbd_handle_t *hp;
+ sbdp_handle_t *hdp;
+ char otype[OBP_MAXDRVNAME];
+ int otypelen;
+
+ ASSERT(sbp);
+
+ if (unitp)
+ *unitp = -1;
+
+ hp = MACHBD2HD(sbp);
+
+ hdp = sbd_get_sbdp_handle(sbp, hp);
+ if (sbdp_get_board_num(hdp, dip) != sbp->sb_num) {
+ sbd_release_sbdp_handle(hdp);
+ return (SBD_COMP_UNKNOWN);
+ }
+
+ /*
+ * sbdp_get_unit_num will return (-1) for cmp as there
+ * is no "device_type" property associated with cmp.
+ * Therefore we will just skip getting unit number for
+ * cmp. Callers of this function need to check the
+ * value set in unitp before using it to dereference
+ * an array.
+ */
+ if (strcmp(ddi_node_name(dip), "cmp") == 0) {
+ sbd_release_sbdp_handle(hdp);
+ return (SBD_COMP_CMP);
+ }
+
+ otypelen = sizeof (otype);
+ if (ddi_getlongprop_buf(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
+ OBP_DEVICETYPE, (caddr_t)otype, &otypelen)) {
+ sbd_release_sbdp_handle(hdp);
+ return (SBD_COMP_UNKNOWN);
+ }
+
+ idx = sbd_otype_to_idx(otype);
+
+ if (SBD_COMP(idx) == SBD_COMP_UNKNOWN) {
+ sbd_release_sbdp_handle(hdp);
+ return (SBD_COMP_UNKNOWN);
+ }
+
+ unit = sbdp_get_unit_num(hdp, dip);
+ if (unit == -1) {
+ cmn_err(CE_WARN,
+ "get_node_type: %s unit fail %p", otype, (void *)dip);
+ sbd_release_sbdp_handle(hdp);
+ return (SBD_COMP_UNKNOWN);
+ }
+
+ sbd_release_sbdp_handle(hdp);
+
+ if (unitp)
+ *unitp = unit;
+
+ return (SBD_COMP(idx));
+}
+
+typedef struct {
+ sbd_board_t *sbp;
+ int nmc;
+ int hold;
+} walk_tree_t;
+
+static int
+sbd_setup_devlists(dev_info_t *dip, void *arg)
+{
+ walk_tree_t *wp;
+ dev_info_t **devlist = NULL;
+ char *pathname = NULL;
+ sbd_mem_unit_t *mp;
+ static fn_t f = "sbd_setup_devlists";
+ sbd_board_t *sbp;
+ int unit;
+ sbd_comp_type_t nodetype;
+
+ ASSERT(dip);
+
+ wp = (walk_tree_t *)arg;
+
+ if (wp == NULL) {
+ PR_ALL("%s:bad arg\n", f);
+ return (DDI_WALK_TERMINATE);
+ }
+
+ sbp = wp->sbp;
+
+ nodetype = get_node_type(sbp, dip, &unit);
+
+ switch (nodetype) {
+
+ case SBD_COMP_CPU:
+ pathname = sbp->sb_cpupath[unit];
+ break;
+
+ case SBD_COMP_MEM:
+ pathname = sbp->sb_mempath[unit];
+ break;
+
+ case SBD_COMP_IO:
+ pathname = sbp->sb_iopath[unit];
+ break;
+
+ case SBD_COMP_CMP:
+ case SBD_COMP_UNKNOWN:
+ /*
+ * This dip is not of interest to us
+ */
+ return (DDI_WALK_CONTINUE);
+
+ default:
+ ASSERT(0);
+ return (DDI_WALK_CONTINUE);
+ }
+
+ /*
+ * dip's parent is being held busy by ddi_walk_devs(),
+ * so dip doesn't have to be held while calling ddi_pathname()
+ */
+ if (pathname) {
+ (void) ddi_pathname(dip, pathname);
+ }
+
+ devlist = sbp->sb_devlist[NIX(nodetype)];
+
+ /*
+ * The branch rooted at dip should already be held,
+ * unless we are dealing with a core of a CMP.
+ */
+ ASSERT(sbd_is_cmp_child(dip) || e_ddi_branch_held(dip));
+ devlist[unit] = dip;
+
+ /*
+ * This test is required if multiple devices are considered
+ * as one. This is the case for memory-controller nodes.
+ */
+ if (!SBD_DEV_IS_PRESENT(sbp, nodetype, unit)) {
+ sbp->sb_ndev++;
+ SBD_DEV_SET_PRESENT(sbp, nodetype, unit);
+ }
+
+ if (nodetype == SBD_COMP_MEM) {
+ mp = SBD_GET_BOARD_MEMUNIT(sbp, unit);
+ ASSERT(wp->nmc < SBD_NUM_MC_PER_BOARD);
+ mp->sbm_dip[wp->nmc++] = dip;
+ }
+
+ return (DDI_WALK_CONTINUE);
+}
+
+/*
+ * This routine is used to construct the memory devlist.
+ * In Starcat and Serengeti platforms, a system board can contain up to
+ * four memory controllers (MC). The MCs have been programmed by POST for
+ * optimum memory interleaving amongst their peers on the same board.
+ * This DR driver does not support deinterleaving. Therefore, the smallest
+ * unit of memory that can be manipulated by this driver is all of the
+ * memory on a board. Because of this restriction, a board's memory devlist
+ * is populated with only one of the four (possible) MC dnodes on that board.
+ * Care must be taken to ensure that the selected MC dnode represents the
+ * lowest physical address to which memory on the board will respond to.
+ * This is required in order to preserve the semantics of
+ * sbdp_get_base_physaddr() when applied to a MC dnode stored in the
+ * memory devlist.
+ */
+static void
+sbd_init_mem_devlists(sbd_board_t *sbp)
+{
+ dev_info_t **devlist;
+ sbd_mem_unit_t *mp;
+ dev_info_t *mc_dip;
+ sbdp_handle_t *hdp;
+ uint64_t mc_pa, lowest_pa;
+ int i;
+ sbd_handle_t *hp = MACHBD2HD(sbp);
+
+ devlist = sbp->sb_devlist[NIX(SBD_COMP_MEM)];
+
+ mp = SBD_GET_BOARD_MEMUNIT(sbp, 0);
+
+ mc_dip = mp->sbm_dip[0];
+ if (mc_dip == NULL)
+ return; /* No MC dips found for this board */
+
+ hdp = sbd_get_sbdp_handle(sbp, hp);
+
+ if (sbdphw_get_base_physaddr(hdp, mc_dip, &mc_pa)) {
+ /* TODO: log complaint about dnode */
+
+pretend_no_mem:
+ /*
+ * We are here because sbdphw_get_base_physaddr() failed.
+ * Although it is very unlikely to happen, it did. Lucky us.
+ * Since we can no longer examine _all_ of the MCs on this
+ * board to determine which one is programmed to the lowest
+ * physical address, we cannot involve any of the MCs on
+ * this board in DR operations. To ensure this, we pretend
+ * that this board does not contain any memory.
+ *
+ * Paranoia: clear the dev_present mask.
+ */
+ if (SBD_DEV_IS_PRESENT(sbp, SBD_COMP_MEM, 0)) {
+ ASSERT(sbp->sb_ndev != 0);
+ SBD_DEV_CLR_PRESENT(sbp, SBD_COMP_MEM, 0);
+ sbp->sb_ndev--;
+ }
+
+ for (i = 0; i < SBD_NUM_MC_PER_BOARD; i++) {
+ mp->sbm_dip[i] = NULL;
+ }
+
+ sbd_release_sbdp_handle(hdp);
+ return;
+ }
+
+ /* assume this one will win. */
+ devlist[0] = mc_dip;
+ mp->sbm_cm.sbdev_dip = mc_dip;
+ lowest_pa = mc_pa;
+
+ /*
+ * We know the base physical address of one of the MC devices. Now
+ * we will enumerate through all of the remaining MC devices on
+ * the board to find which of them is programmed to the lowest
+ * physical address.
+ */
+ for (i = 1; i < SBD_NUM_MC_PER_BOARD; i++) {
+ mc_dip = mp->sbm_dip[i];
+ if (mc_dip == NULL) {
+ break;
+ }
+
+ if (sbdphw_get_base_physaddr(hdp, mc_dip, &mc_pa)) {
+ cmn_err(CE_NOTE, "No mem on board %d unit %d",
+ sbp->sb_num, i);
+ break;
+ }
+ if (mc_pa < lowest_pa) {
+ mp->sbm_cm.sbdev_dip = mc_dip;
+ devlist[0] = mc_dip;
+ lowest_pa = mc_pa;
+ }
+ }
+
+ sbd_release_sbdp_handle(hdp);
+}
+
+static int
+sbd_name_to_idx(char *name)
+{
+ int idx;
+
+ for (idx = 0; SBD_COMP(idx) != SBD_COMP_UNKNOWN; idx++) {
+ if (strcmp(name, SBD_DEVNAME(idx)) == 0) {
+ break;
+ }
+ }
+
+ return (idx);
+}
+
+static int
+sbd_otype_to_idx(char *otype)
+{
+ int idx;
+
+ for (idx = 0; SBD_COMP(idx) != SBD_COMP_UNKNOWN; idx++) {
+
+ if (strcmp(otype, SBD_OTYPE(idx)) == 0) {
+ break;
+ }
+ }
+
+ return (idx);
+}
+
+static int
+sbd_init_devlists(sbd_board_t *sbp)
+{
+ int i;
+ sbd_dev_unit_t *dp;
+ sbd_mem_unit_t *mp;
+ walk_tree_t *wp, walk = {0};
+ dev_info_t *pdip;
+ static fn_t f = "sbd_init_devlists";
+
+ PR_ALL("%s (board = %d)...\n", f, sbp->sb_num);
+
+ wp = &walk;
+
+ SBD_DEVS_DISCONNECT(sbp, (uint_t)-1);
+
+ /*
+ * Clear out old entries, if any.
+ */
+
+ for (i = 0; i < MAX_MEM_UNITS_PER_BOARD; i++) {
+ sbp->sb_devlist[NIX(SBD_COMP_MEM)][i] = NULL;
+ dp = (sbd_dev_unit_t *)SBD_GET_BOARD_MEMUNIT(sbp, i);
+ dp->u_common.sbdev_sbp = sbp;
+ dp->u_common.sbdev_unum = i;
+ dp->u_common.sbdev_type = SBD_COMP_MEM;
+ }
+
+ mp = SBD_GET_BOARD_MEMUNIT(sbp, 0);
+ ASSERT(mp != NULL);
+ for (i = 0; i < SBD_NUM_MC_PER_BOARD; i++) {
+ mp->sbm_dip[i] = NULL;
+ }
+
+ for (i = 0; i < MAX_CPU_UNITS_PER_BOARD; i++) {
+ sbp->sb_devlist[NIX(SBD_COMP_CPU)][i] = NULL;
+ dp = (sbd_dev_unit_t *)SBD_GET_BOARD_CPUUNIT(sbp, i);
+ dp->u_common.sbdev_sbp = sbp;
+ dp->u_common.sbdev_unum = i;
+ dp->u_common.sbdev_type = SBD_COMP_CPU;
+ }
+ for (i = 0; i < MAX_IO_UNITS_PER_BOARD; i++) {
+ sbp->sb_devlist[NIX(SBD_COMP_IO)][i] = NULL;
+ dp = (sbd_dev_unit_t *)SBD_GET_BOARD_IOUNIT(sbp, i);
+ dp->u_common.sbdev_sbp = sbp;
+ dp->u_common.sbdev_unum = i;
+ dp->u_common.sbdev_type = SBD_COMP_IO;
+ }
+
+ wp->sbp = sbp;
+ wp->nmc = 0;
+ sbp->sb_ndev = 0;
+
+ /*
+ * ddi_walk_devs() requires that topdip's parent be held.
+ */
+ pdip = ddi_get_parent(sbp->sb_topdip);
+ if (pdip) {
+ ndi_hold_devi(pdip);
+ ndi_devi_enter(pdip, &i);
+ }
+ ddi_walk_devs(sbp->sb_topdip, sbd_setup_devlists, (void *) wp);
+ if (pdip) {
+ ndi_devi_exit(pdip, i);
+ ndi_rele_devi(pdip);
+ }
+
+ /*
+ * There is no point checking all the components if there
+ * are no devices.
+ */
+ if (sbp->sb_ndev == 0) {
+ sbp->sb_memaccess_ok = 0;
+ return (sbp->sb_ndev);
+ }
+
+ /*
+ * Initialize cpu sections before calling sbd_init_mem_devlists
+ * which will access the mmus.
+ */
+ sbp->sb_memaccess_ok = 1;
+ for (i = 0; i < MAX_CPU_UNITS_PER_BOARD; i++) {
+ if (SBD_DEV_IS_PRESENT(sbp, SBD_COMP_CPU, i)) {
+ sbd_init_cpu_unit(sbp, i);
+ if (sbd_connect_cpu(sbp, i)) {
+ SBD_SET_ERR(HD2MACHERR(MACHBD2HD(sbp)),
+ ESBD_CPUSTART);
+ }
+
+ }
+ }
+
+ if (sbp->sb_memaccess_ok) {
+ sbd_init_mem_devlists(sbp);
+ } else {
+ cmn_err(CE_WARN, "unable to access memory on board %d",
+ sbp->sb_num);
+ }
+
+ return (sbp->sb_ndev);
+}
+
+static void
+sbd_init_cpu_unit(sbd_board_t *sbp, int unit)
+{
+ sbd_istate_t new_state;
+ sbd_cpu_unit_t *cp;
+ int cpuid;
+ dev_info_t *dip;
+ sbdp_handle_t *hdp;
+ sbd_handle_t *hp = MACHBD2HD(sbp);
+ extern kmutex_t cpu_lock;
+
+ if (SBD_DEV_IS_ATTACHED(sbp, SBD_COMP_CPU, unit)) {
+ new_state = SBD_STATE_CONFIGURED;
+ } else if (SBD_DEV_IS_PRESENT(sbp, SBD_COMP_CPU, unit)) {
+ new_state = SBD_STATE_CONNECTED;
+ } else {
+ new_state = SBD_STATE_EMPTY;
+ }
+
+ dip = sbp->sb_devlist[NIX(SBD_COMP_CPU)][unit];
+
+ cp = SBD_GET_BOARD_CPUUNIT(sbp, unit);
+
+ hdp = sbd_get_sbdp_handle(sbp, hp);
+
+ cpuid = sbdp_get_cpuid(hdp, dip);
+
+ cp->sbc_cpu_id = cpuid;
+
+ if (&sbdp_cpu_get_impl)
+ cp->sbc_cpu_impl = sbdp_cpu_get_impl(hdp, dip);
+ else
+ cp->sbc_cpu_impl = -1;
+
+ mutex_enter(&cpu_lock);
+ if ((cpuid >= 0) && cpu[cpuid])
+ cp->sbc_cpu_flags = cpu[cpuid]->cpu_flags;
+ else
+ cp->sbc_cpu_flags = CPU_OFFLINE | CPU_POWEROFF;
+ mutex_exit(&cpu_lock);
+
+ sbd_cpu_set_prop(cp, dip);
+
+ cp->sbc_cm.sbdev_cond = sbd_get_comp_cond(dip);
+ sbd_release_sbdp_handle(hdp);
+
+ /*
+ * Any changes to the cpu should be performed above
+ * this call to ensure the cpu is fully initialized
+ * before transitioning to the new state.
+ */
+ SBD_DEVICE_TRANSITION(sbp, SBD_COMP_CPU, unit, new_state);
+}
+
+/*
+ * Only do work if called to operate on an entire board
+ * which doesn't already have components present.
+ */
+static void
+sbd_connect(sbd_handle_t *hp)
+{
+ sbd_board_t *sbp;
+ sbderror_t *ep;
+ static fn_t f = "sbd_connect";
+
+ sbp = SBDH2BD(hp->h_sbd);
+
+ PR_ALL("%s board %d\n", f, sbp->sb_num);
+
+ ep = HD2MACHERR(hp);
+
+ if (SBD_DEVS_PRESENT(sbp)) {
+ /*
+ * Board already has devices present.
+ */
+ PR_ALL("%s: devices already present (0x%x)\n",
+ f, SBD_DEVS_PRESENT(sbp));
+ SBD_SET_ERRNO(ep, EINVAL);
+ return;
+ }
+
+ if (sbd_init_devlists(sbp) == 0) {
+ cmn_err(CE_WARN, "%s: no devices present on board %d",
+ f, sbp->sb_num);
+ SBD_SET_ERR(ep, ESBD_NODEV);
+ return;
+ } else {
+ int i;
+
+ /*
+ * Initialize mem-unit section of board structure.
+ */
+ for (i = 0; i < MAX_MEM_UNITS_PER_BOARD; i++)
+ if (SBD_DEV_IS_PRESENT(sbp, SBD_COMP_MEM, i))
+ sbd_init_mem_unit(sbp, i, SBD_HD2ERR(hp));
+
+ /*
+ * Initialize sb_io sections.
+ */
+ for (i = 0; i < MAX_IO_UNITS_PER_BOARD; i++)
+ if (SBD_DEV_IS_PRESENT(sbp, SBD_COMP_IO, i))
+ sbd_init_io_unit(sbp, i);
+
+ SBD_BOARD_TRANSITION(sbp, SBD_STATE_CONNECTED);
+ sbp->sb_rstate = SBD_STAT_CONNECTED;
+ sbp->sb_ostate = SBD_STAT_UNCONFIGURED;
+ (void) drv_getparm(TIME, (void *)&sbp->sb_time);
+ SBD_INJECT_ERR(SBD_CONNECT_BOARD_PSEUDO_ERR, hp->h_err, EIO,
+ ESBD_INTERNAL, NULL);
+ }
+}
+
+static int
+sbd_disconnect(sbd_handle_t *hp)
+{
+ int i;
+ sbd_devset_t devset;
+ sbd_board_t *sbp;
+ static fn_t f = "sbd_disconnect it";
+
+ PR_ALL("%s ...\n", f);
+
+ sbp = SBDH2BD(hp->h_sbd);
+
+ /*
+ * Only devices which are present, but
+ * unattached can be disconnected.
+ */
+ devset = HD2MACHHD(hp)->sh_devset & SBD_DEVS_PRESENT(sbp) &
+ SBD_DEVS_UNATTACHED(sbp);
+
+ ASSERT((SBD_DEVS_ATTACHED(sbp) & devset) == 0);
+
+ /*
+ * Update per-device state transitions.
+ */
+
+ for (i = 0; i < MAX_MEM_UNITS_PER_BOARD; i++)
+ if (DEVSET_IN_SET(devset, SBD_COMP_MEM, i)) {
+ if (sbd_disconnect_mem(hp, i) == 0) {
+ SBD_DEVICE_TRANSITION(sbp, SBD_COMP_MEM, i,
+ SBD_STATE_EMPTY);
+ SBD_DEV_CLR_PRESENT(sbp, SBD_COMP_MEM, i);
+ }
+ }
+
+ for (i = 0; i < MAX_CPU_UNITS_PER_BOARD; i++)
+ if (DEVSET_IN_SET(devset, SBD_COMP_CPU, i)) {
+ if (sbd_disconnect_cpu(hp, i) == 0) {
+ SBD_DEVICE_TRANSITION(sbp, SBD_COMP_CPU, i,
+ SBD_STATE_EMPTY);
+ SBD_DEV_CLR_PRESENT(sbp, SBD_COMP_CPU, i);
+ }
+ }
+
+ for (i = 0; i < MAX_IO_UNITS_PER_BOARD; i++)
+ if (DEVSET_IN_SET(devset, SBD_COMP_IO, i)) {
+ if (sbd_disconnect_io(hp, i) == 0) {
+ SBD_DEVICE_TRANSITION(sbp, SBD_COMP_IO, i,
+ SBD_STATE_EMPTY);
+ SBD_DEV_CLR_PRESENT(sbp, SBD_COMP_IO, i);
+ }
+ }
+
+ /*
+ * Once all the components on a board have been disconnect
+ * the board's state can transition to disconnected and
+ * we can allow the deprobe to take place.
+ */
+ if (SBD_DEVS_PRESENT(sbp) == 0) {
+ SBD_BOARD_TRANSITION(sbp, SBD_STATE_OCCUPIED);
+ sbp->sb_rstate = SBD_STAT_DISCONNECTED;
+ sbp->sb_ostate = SBD_STAT_UNCONFIGURED;
+ (void) drv_getparm(TIME, (void *)&sbp->sb_time);
+ SBD_INJECT_ERR(SBD_DISCONNECT_BOARD_PSEUDO_ERR, hp->h_err, EIO,
+ ESBD_INTERNAL, NULL);
+ return (0);
+ } else {
+ cmn_err(CE_WARN, "%s: could not disconnect devices on board %d",
+ f, sbp->sb_num);
+ return (-1);
+ }
+}
+
+static void
+sbd_test_board(sbd_handle_t *hp)
+{
+ sbd_board_t *sbp;
+ sbdp_handle_t *hdp;
+
+ sbp = SBDH2BD(hp->h_sbd);
+
+ PR_ALL("sbd_test_board: board %d\n", sbp->sb_num);
+
+
+ hdp = sbd_get_sbdp_handle(sbp, hp);
+
+ if (sbdp_test_board(hdp, &hp->h_opts) != 0) {
+ sbderror_t *ep = SBD_HD2ERR(hp);
+
+ SBD_GET_PERR(hdp->h_err, ep);
+ }
+
+ SBD_INJECT_ERR(SBD_TEST_BOARD_PSEUDO_ERR, hp->h_err, EIO,
+ ESBD_INTERNAL, NULL);
+
+ sbd_release_sbdp_handle(hdp);
+}
+
+static void
+sbd_assign_board(sbd_handle_t *hp)
+{
+ sbd_board_t *sbp;
+ sbdp_handle_t *hdp;
+
+ sbp = SBDH2BD(hp->h_sbd);
+
+ PR_ALL("sbd_assign_board: board %d\n", sbp->sb_num);
+
+ hdp = sbd_get_sbdp_handle(sbp, hp);
+
+ if (sbdp_assign_board(hdp) != 0) {
+ sbderror_t *ep = SBD_HD2ERR(hp);
+
+ SBD_GET_PERR(hdp->h_err, ep);
+ }
+
+ SBD_INJECT_ERR(SBD_ASSIGN_BOARD_PSEUDO_ERR, hp->h_err, EIO,
+ ESBD_INTERNAL, NULL);
+
+ sbd_release_sbdp_handle(hdp);
+}
+
+static void
+sbd_unassign_board(sbd_handle_t *hp)
+{
+ sbd_board_t *sbp;
+ sbdp_handle_t *hdp;
+
+ sbp = SBDH2BD(hp->h_sbd);
+
+ PR_ALL("sbd_unassign_board: board %d\n", sbp->sb_num);
+
+ hdp = sbd_get_sbdp_handle(sbp, hp);
+
+ if (sbdp_unassign_board(hdp) != 0) {
+ sbderror_t *ep = SBD_HD2ERR(hp);
+
+ SBD_GET_PERR(hdp->h_err, ep);
+ }
+
+ SBD_INJECT_ERR(SBD_ASSIGN_BOARD_PSEUDO_ERR, hp->h_err, EIO,
+ ESBD_INTERNAL, NULL);
+
+ sbd_release_sbdp_handle(hdp);
+}
+
+static void
+sbd_poweron_board(sbd_handle_t *hp)
+{
+ sbd_board_t *sbp;
+ sbdp_handle_t *hdp;
+
+ sbp = SBDH2BD(hp->h_sbd);
+
+ PR_ALL("sbd_poweron_board: %d\n", sbp->sb_num);
+
+ hdp = sbd_get_sbdp_handle(sbp, hp);
+
+ if (sbdp_poweron_board(hdp) != 0) {
+ sbderror_t *ep = SBD_HD2ERR(hp);
+
+ SBD_GET_PERR(hdp->h_err, ep);
+ }
+
+ SBD_INJECT_ERR(SBD_POWERON_BOARD_PSEUDO_ERR, hp->h_err, EIO,
+ ESBD_INTERNAL, NULL);
+
+ sbd_release_sbdp_handle(hdp);
+}
+
+static void
+sbd_poweroff_board(sbd_handle_t *hp)
+{
+ sbd_board_t *sbp;
+ sbdp_handle_t *hdp;
+
+ sbp = SBDH2BD(hp->h_sbd);
+
+ PR_ALL("sbd_poweroff_board: %d\n", sbp->sb_num);
+
+ hdp = sbd_get_sbdp_handle(sbp, hp);
+
+ if (sbdp_poweroff_board(hdp) != 0) {
+ sbderror_t *ep = SBD_HD2ERR(hp);
+
+ SBD_GET_PERR(hdp->h_err, ep);
+ }
+
+ SBD_INJECT_ERR(SBD_POWEROFF_BOARD_PSEUDO_ERR, hp->h_err, EIO,
+ ESBD_INTERNAL, NULL);
+
+ sbd_release_sbdp_handle(hdp);
+}
+
+
+/*
+ * Return a list of the dip's of devices that are
+ * either present and attached, or present only but
+ * not yet attached for the given board.
+ */
+sbd_devlist_t *
+sbd_get_devlist(sbd_handle_t *hp, sbd_board_t *sbp, sbd_comp_type_t nodetype,
+ int max_units, uint_t uset, int *count, int present_only)
+{
+ int i, ix;
+ sbd_devlist_t *ret_devlist;
+ dev_info_t **devlist;
+ sbdp_handle_t *hdp;
+
+ *count = 0;
+ ret_devlist = GETSTRUCT(sbd_devlist_t, max_units);
+ devlist = sbp->sb_devlist[NIX(nodetype)];
+ /*
+ * Turn into binary value since we're going
+ * to be using XOR for a comparison.
+ * if (present_only) then
+ * dev must be PRESENT, but NOT ATTACHED.
+ * else
+ * dev must be PRESENT AND ATTACHED.
+ * endif
+ */
+ if (present_only)
+ present_only = 1;
+
+ hdp = sbd_get_sbdp_handle(sbp, hp);
+
+ for (i = ix = 0; (i < max_units) && uset; i++) {
+ int ut, is_present, is_attached;
+ dev_info_t *dip;
+ sbderror_t *ep = SBD_HD2ERR(hp);
+ int nunits, distance, j;
+
+ /*
+ * For CMPs, we would like to perform DR operation on
+ * all the cores before moving onto the next chip.
+ * Therefore, when constructing the devlist, we process
+ * all the cores together.
+ */
+ if (nodetype == SBD_COMP_CPU) {
+ /*
+ * Number of units to process in the inner loop
+ */
+ nunits = MAX_CORES_PER_CMP;
+ /*
+ * The distance between the units in the
+ * board's sb_devlist structure.
+ */
+ distance = MAX_CMP_UNITS_PER_BOARD;
+ } else {
+ nunits = 1;
+ distance = 0;
+ }
+
+ for (j = 0; j < nunits; j++) {
+ if ((dip = devlist[i + j * distance]) == NULL)
+ continue;
+
+ ut = sbdp_get_unit_num(hdp, dip);
+
+ if (ut == -1) {
+ SBD_GET_PERR(hdp->h_err, ep);
+ PR_ALL("sbd_get_devlist bad unit %d"
+ " code %d errno %d",
+ i, ep->e_code, ep->e_errno);
+ }
+
+ if ((uset & (1 << ut)) == 0)
+ continue;
+ uset &= ~(1 << ut);
+ is_present = SBD_DEV_IS_PRESENT(sbp, nodetype, ut) ?
+ 1 : 0;
+ is_attached = SBD_DEV_IS_ATTACHED(sbp, nodetype, ut) ?
+ 1 : 0;
+
+ if (is_present && (present_only ^ is_attached)) {
+ ret_devlist[ix].dv_dip = dip;
+ sbd_init_err(&ret_devlist[ix].dv_error);
+ ix++;
+ }
+ }
+ }
+ sbd_release_sbdp_handle(hdp);
+
+ if ((*count = ix) == 0) {
+ FREESTRUCT(ret_devlist, sbd_devlist_t, max_units);
+ ret_devlist = NULL;
+ }
+
+ return (ret_devlist);
+}
+
+static sbd_devlist_t *
+sbd_get_attach_devlist(sbd_handle_t *hp, int32_t *devnump, int32_t pass)
+{
+ sbd_board_t *sbp;
+ uint_t uset;
+ sbd_devset_t devset;
+ sbd_devlist_t *attach_devlist;
+ static int next_pass = 1;
+ static fn_t f = "sbd_get_attach_devlist";
+
+ PR_ALL("%s (pass = %d)...\n", f, pass);
+
+ sbp = SBDH2BD(hp->h_sbd);
+ devset = HD2MACHHD(hp)->sh_devset;
+
+ *devnump = 0;
+ attach_devlist = NULL;
+
+ /*
+ * We switch on next_pass for the cases where a board
+ * does not contain a particular type of component.
+ * In these situations we don't want to return NULL
+ * prematurely. We need to check other devices and
+ * we don't want to check the same type multiple times.
+ * For example, if there were no cpus, then on pass 1
+ * we would drop through and return the memory nodes.
+ * However, on pass 2 we would switch back to the memory
+ * nodes thereby returning them twice! Using next_pass
+ * forces us down to the end (or next item).
+ */
+ if (pass == 1)
+ next_pass = 1;
+
+ switch (next_pass) {
+ case 1:
+ if (DEVSET_IN_SET(devset, SBD_COMP_CPU, DEVSET_ANYUNIT)) {
+ uset = DEVSET_GET_UNITSET(devset, SBD_COMP_CPU);
+
+ attach_devlist = sbd_get_devlist(hp, sbp, SBD_COMP_CPU,
+ MAX_CPU_UNITS_PER_BOARD,
+ uset, devnump, 1);
+
+ DEVSET_DEL(devset, SBD_COMP_CPU, DEVSET_ANYUNIT);
+ if (!devset || attach_devlist) {
+ next_pass = 2;
+ return (attach_devlist);
+ }
+ /*
+ * If the caller is interested in the entire
+ * board, but there aren't any cpus, then just
+ * fall through to check for the next component.
+ */
+ }
+ /*FALLTHROUGH*/
+
+ case 2:
+ if (DEVSET_IN_SET(devset, SBD_COMP_MEM, DEVSET_ANYUNIT)) {
+ uset = DEVSET_GET_UNITSET(devset, SBD_COMP_MEM);
+
+ attach_devlist = sbd_get_devlist(hp, sbp, SBD_COMP_MEM,
+ MAX_MEM_UNITS_PER_BOARD,
+ uset, devnump, 1);
+
+ DEVSET_DEL(devset, SBD_COMP_MEM, DEVSET_ANYUNIT);
+ if (!devset || attach_devlist) {
+ next_pass = 3;
+ return (attach_devlist);
+ }
+ /*
+ * If the caller is interested in the entire
+ * board, but there isn't any memory, then
+ * just fall through to next component.
+ */
+ }
+ /*FALLTHROUGH*/
+
+
+ case 3:
+ next_pass = -1;
+ if (DEVSET_IN_SET(devset, SBD_COMP_IO, DEVSET_ANYUNIT)) {
+ uset = DEVSET_GET_UNITSET(devset, SBD_COMP_IO);
+
+ attach_devlist = sbd_get_devlist(hp, sbp, SBD_COMP_IO,
+ MAX_IO_UNITS_PER_BOARD,
+ uset, devnump, 1);
+
+ DEVSET_DEL(devset, SBD_COMP_IO, DEVSET_ANYUNIT);
+ if (!devset || attach_devlist) {
+ next_pass = 4;
+ return (attach_devlist);
+ }
+ }
+ /*FALLTHROUGH*/
+
+ default:
+ *devnump = 0;
+ return (NULL);
+ }
+ /*NOTREACHED*/
+}
+
+static int
+sbd_pre_attach_devlist(sbd_handle_t *hp, sbd_devlist_t *devlist,
+ int32_t devnum)
+{
+ int max_units = 0, rv = 0;
+ sbd_comp_type_t nodetype;
+ static fn_t f = "sbd_pre_attach_devlist";
+
+ /*
+ * In this driver, all entries in a devlist[] are
+ * of the same nodetype.
+ */
+ nodetype = sbd_get_devtype(hp, devlist->dv_dip);
+
+ PR_ALL("%s (nt = %s(%d), num = %d)...\n",
+ f, sbd_ct_str[(int)nodetype], (int)nodetype, devnum);
+
+ switch (nodetype) {
+
+ case SBD_COMP_MEM:
+ max_units = MAX_MEM_UNITS_PER_BOARD;
+ rv = sbd_pre_attach_mem(hp, devlist, devnum);
+ break;
+
+ case SBD_COMP_CPU:
+ max_units = MAX_CPU_UNITS_PER_BOARD;
+ rv = sbd_pre_attach_cpu(hp, devlist, devnum);
+ break;
+
+ case SBD_COMP_IO:
+ max_units = MAX_IO_UNITS_PER_BOARD;
+ break;
+
+ default:
+ rv = -1;
+ break;
+ }
+
+ if (rv && max_units) {
+ int i;
+ /*
+ * Need to clean up devlist
+ * if pre-op is going to fail.
+ */
+ for (i = 0; i < max_units; i++) {
+ if (SBD_GET_ERRSTR(&devlist[i].dv_error)) {
+ SBD_FREE_ERR(&devlist[i].dv_error);
+ } else {
+ break;
+ }
+ }
+ FREESTRUCT(devlist, sbd_devlist_t, max_units);
+ }
+
+ /*
+ * If an error occurred, return "continue"
+ * indication so that we can continue attaching
+ * as much as possible.
+ */
+ return (rv ? -1 : 0);
+}
+
+static int
+sbd_post_attach_devlist(sbd_handle_t *hp, sbd_devlist_t *devlist,
+ int32_t devnum)
+{
+ int i, max_units = 0, rv = 0;
+ sbd_devset_t devs_unattached, devs_present;
+ sbd_comp_type_t nodetype;
+ sbd_board_t *sbp = SBDH2BD(hp->h_sbd);
+ sbdp_handle_t *hdp;
+ static fn_t f = "sbd_post_attach_devlist";
+
+ sbp = SBDH2BD(hp->h_sbd);
+ nodetype = sbd_get_devtype(hp, devlist->dv_dip);
+
+ PR_ALL("%s (nt = %s(%d), num = %d)...\n",
+ f, sbd_ct_str[(int)nodetype], (int)nodetype, devnum);
+
+ hdp = sbd_get_sbdp_handle(sbp, hp);
+
+ /*
+ * Need to free up devlist[] created earlier in
+ * sbd_get_attach_devlist().
+ */
+ switch (nodetype) {
+ case SBD_COMP_CPU:
+ max_units = MAX_CPU_UNITS_PER_BOARD;
+ rv = sbd_post_attach_cpu(hp, devlist, devnum);
+ break;
+
+
+ case SBD_COMP_MEM:
+ max_units = MAX_MEM_UNITS_PER_BOARD;
+
+ rv = sbd_post_attach_mem(hp, devlist, devnum);
+ break;
+
+ case SBD_COMP_IO:
+ max_units = MAX_IO_UNITS_PER_BOARD;
+ break;
+
+ default:
+ rv = -1;
+ break;
+ }
+
+
+ for (i = 0; i < devnum; i++) {
+ int unit;
+ dev_info_t *dip;
+ sbderror_t *ep;
+
+ ep = &devlist[i].dv_error;
+
+ if (sbd_set_err_in_hdl(hp, ep) == 0)
+ continue;
+
+ dip = devlist[i].dv_dip;
+ nodetype = sbd_get_devtype(hp, dip);
+ unit = sbdp_get_unit_num(hdp, dip);
+
+ if (unit == -1) {
+ SBD_GET_PERR(hdp->h_err, ep);
+ continue;
+ }
+
+ unit = sbd_check_unit_attached(sbp, dip, unit, nodetype, ep);
+
+ if (unit == -1) {
+ PR_ALL("%s: ERROR (nt=%s, b=%d, u=%d) not attached\n",
+ f, sbd_ct_str[(int)nodetype], sbp->sb_num, i);
+ continue;
+ }
+
+ SBD_DEV_SET_ATTACHED(sbp, nodetype, unit);
+ SBD_DEVICE_TRANSITION(sbp, nodetype, unit,
+ SBD_STATE_CONFIGURED);
+ }
+ sbd_release_sbdp_handle(hdp);
+
+ if (rv) {
+ PR_ALL("%s: errno %d, ecode %d during attach\n",
+ f, SBD_GET_ERRNO(SBD_HD2ERR(hp)),
+ SBD_GET_ERR(HD2MACHERR(hp)));
+ }
+
+ devs_present = SBD_DEVS_PRESENT(sbp);
+ devs_unattached = SBD_DEVS_UNATTACHED(sbp);
+
+ switch (SBD_BOARD_STATE(sbp)) {
+ case SBD_STATE_CONNECTED:
+ case SBD_STATE_UNCONFIGURED:
+ ASSERT(devs_present);
+
+ if (devs_unattached == 0) {
+ /*
+ * All devices finally attached.
+ */
+ SBD_BOARD_TRANSITION(sbp, SBD_STATE_CONFIGURED);
+ sbp->sb_rstate = SBD_STAT_CONNECTED;
+ sbp->sb_ostate = SBD_STAT_CONFIGURED;
+ } else if (devs_present != devs_unattached) {
+ /*
+ * Only some devices are fully attached.
+ */
+ SBD_BOARD_TRANSITION(sbp, SBD_STATE_PARTIAL);
+ sbp->sb_rstate = SBD_STAT_CONNECTED;
+ sbp->sb_ostate = SBD_STAT_UNCONFIGURED;
+ }
+ (void) drv_getparm(TIME, (void *)&sbp->sb_time);
+ break;
+
+ case SBD_STATE_PARTIAL:
+ ASSERT(devs_present);
+ /*
+ * All devices finally attached.
+ */
+ if (devs_unattached == 0) {
+ SBD_BOARD_TRANSITION(sbp, SBD_STATE_CONFIGURED);
+ sbp->sb_rstate = SBD_STAT_CONNECTED;
+ sbp->sb_ostate = SBD_STAT_CONFIGURED;
+ (void) drv_getparm(TIME, (void *)&sbp->sb_time);
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ if (max_units && devlist) {
+ int i;
+
+ for (i = 0; i < max_units; i++) {
+ if (SBD_GET_ERRSTR(&devlist[i].dv_error)) {
+ SBD_FREE_ERR(&devlist[i].dv_error);
+ } else {
+ break;
+ }
+ }
+ FREESTRUCT(devlist, sbd_devlist_t, max_units);
+ }
+
+ /*
+ * Our policy is to attach all components that are
+ * possible, thus we always return "success" on the
+ * pre and post operations.
+ */
+ return (0);
+}
+
+/*
+ * We only need to "release" cpu and memory devices.
+ */
+static sbd_devlist_t *
+sbd_get_release_devlist(sbd_handle_t *hp, int32_t *devnump, int32_t pass)
+{
+ sbd_board_t *sbp;
+ uint_t uset;
+ sbd_devset_t devset;
+ sbd_devlist_t *release_devlist;
+ static int next_pass = 1;
+ static fn_t f = "sbd_get_release_devlist";
+
+ PR_ALL("%s (pass = %d)...\n", f, pass);
+
+ sbp = SBDH2BD(hp->h_sbd);
+ devset = HD2MACHHD(hp)->sh_devset;
+
+ *devnump = 0;
+ release_devlist = NULL;
+
+ /*
+ * We switch on next_pass for the cases where a board
+ * does not contain a particular type of component.
+ * In these situations we don't want to return NULL
+ * prematurely. We need to check other devices and
+ * we don't want to check the same type multiple times.
+ * For example, if there were no cpus, then on pass 1
+ * we would drop through and return the memory nodes.
+ * However, on pass 2 we would switch back to the memory
+ * nodes thereby returning them twice! Using next_pass
+ * forces us down to the end (or next item).
+ */
+ if (pass == 1)
+ next_pass = 1;
+
+ switch (next_pass) {
+ case 1:
+ if (DEVSET_IN_SET(devset, SBD_COMP_MEM, DEVSET_ANYUNIT)) {
+ uset = DEVSET_GET_UNITSET(devset, SBD_COMP_MEM);
+
+ release_devlist = sbd_get_devlist(hp, sbp,
+ SBD_COMP_MEM,
+ MAX_MEM_UNITS_PER_BOARD,
+ uset, devnump, 0);
+
+ DEVSET_DEL(devset, SBD_COMP_MEM, DEVSET_ANYUNIT);
+ if (!devset || release_devlist) {
+ next_pass = 2;
+ return (release_devlist);
+ }
+ /*
+ * If the caller is interested in the entire
+ * board, but there isn't any memory, then
+ * just fall through to next component.
+ */
+ }
+ /*FALLTHROUGH*/
+
+
+ case 2:
+ if (DEVSET_IN_SET(devset, SBD_COMP_CPU, DEVSET_ANYUNIT)) {
+ uset = DEVSET_GET_UNITSET(devset, SBD_COMP_CPU);
+
+ release_devlist = sbd_get_devlist(hp, sbp,
+ SBD_COMP_CPU,
+ MAX_CPU_UNITS_PER_BOARD,
+ uset, devnump, 0);
+
+ DEVSET_DEL(devset, SBD_COMP_CPU, DEVSET_ANYUNIT);
+ if (!devset || release_devlist) {
+ next_pass = 3;
+ return (release_devlist);
+ }
+ /*
+ * If the caller is interested in the entire
+ * board, but there aren't any cpus, then just
+ * fall through to check for the next component.
+ */
+ }
+ /*FALLTHROUGH*/
+
+
+ case 3:
+ next_pass = -1;
+ if (DEVSET_IN_SET(devset, SBD_COMP_IO, DEVSET_ANYUNIT)) {
+ uset = DEVSET_GET_UNITSET(devset, SBD_COMP_IO);
+
+ release_devlist = sbd_get_devlist(hp, sbp,
+ SBD_COMP_IO,
+ MAX_IO_UNITS_PER_BOARD,
+ uset, devnump, 0);
+
+ DEVSET_DEL(devset, SBD_COMP_IO, DEVSET_ANYUNIT);
+ if (!devset || release_devlist) {
+ next_pass = 4;
+ return (release_devlist);
+ }
+ }
+ /*FALLTHROUGH*/
+
+ default:
+ *devnump = 0;
+ return (NULL);
+ }
+ /*NOTREACHED*/
+}
+
+static int
+sbd_pre_release_devlist(sbd_handle_t *hp, sbd_devlist_t *devlist,
+ int32_t devnum)
+{
+ int max_units = 0, rv = 0;
+ sbd_comp_type_t nodetype;
+ static fn_t f = "sbd_pre_release_devlist";
+
+ nodetype = sbd_get_devtype(hp, devlist->dv_dip);
+
+ PR_ALL("%s (nt = %s(%d), num = %d)...\n",
+ f, sbd_ct_str[(int)nodetype], (int)nodetype, devnum);
+
+ switch (nodetype) {
+ case SBD_COMP_CPU: {
+ int i, mem_present = 0;
+ sbd_board_t *sbp = SBDH2BD(hp->h_sbd);
+ sbd_devset_t devset;
+ sbd_priv_handle_t *shp = HD2MACHHD(hp);
+
+ max_units = MAX_CPU_UNITS_PER_BOARD;
+
+ devset = shp->sh_orig_devset;
+
+ for (i = 0; i < MAX_MEM_UNITS_PER_BOARD; i++) {
+ /*
+ * if client also requested to unconfigure memory
+ * the we allow the operation. Therefore
+ * we need to warranty that memory gets unconfig
+ * before cpus
+ */
+
+ if (DEVSET_IN_SET(devset, SBD_COMP_MEM, i)) {
+ continue;
+ }
+ if (SBD_DEV_IS_ATTACHED(sbp, SBD_COMP_MEM, i)) {
+ mem_present = 1;
+ break;
+ }
+ }
+ if (mem_present) {
+ sbderror_t *ep = SBD_HD2ERR(hp);
+ SBD_SET_ERR(ep, ESBD_MEMONLINE);
+ SBD_SET_ERRSTR(ep, sbp->sb_mempath[i]);
+ rv = -1;
+ } else {
+ rv = sbd_pre_release_cpu(hp, devlist, devnum);
+ }
+
+ break;
+
+ }
+ case SBD_COMP_MEM:
+ max_units = MAX_MEM_UNITS_PER_BOARD;
+ rv = sbd_pre_release_mem(hp, devlist, devnum);
+ break;
+
+
+ case SBD_COMP_IO:
+ max_units = MAX_IO_UNITS_PER_BOARD;
+ rv = sbd_pre_release_io(hp, devlist, devnum);
+ break;
+
+ default:
+ rv = -1;
+ break;
+ }
+
+ if (rv && max_units) {
+ int i;
+
+ /*
+ * the individual pre_release component routines should
+ * have set the error in the handle. No need to set it
+ * here
+ *
+ * Need to clean up dynamically allocated devlist
+ * if pre-op is going to fail.
+ */
+ for (i = 0; i < max_units; i++) {
+ if (SBD_GET_ERRSTR(&devlist[i].dv_error)) {
+ SBD_FREE_ERR(&devlist[i].dv_error);
+ } else {
+ break;
+ }
+ }
+ FREESTRUCT(devlist, sbd_devlist_t, max_units);
+ }
+
+ return (rv ? -1 : 0);
+}
+
+static int
+sbd_post_release_devlist(sbd_handle_t *hp, sbd_devlist_t *devlist,
+ int32_t devnum)
+{
+ int i, max_units = 0;
+ sbd_comp_type_t nodetype;
+ sbd_board_t *sbp = SBDH2BD(hp->h_sbd);
+ sbdp_handle_t *hdp;
+ sbd_error_t *spe;
+ static fn_t f = "sbd_post_release_devlist";
+
+ nodetype = sbd_get_devtype(hp, devlist->dv_dip);
+ ASSERT(nodetype >= SBD_COMP_CPU && nodetype <= SBD_COMP_IO);
+
+ PR_ALL("%s (nt = %s(%d), num = %d)...\n",
+ f, sbd_ct_str[(int)nodetype], (int)nodetype, devnum);
+
+ /*
+ * Need to free up devlist[] created earlier in
+ * sbd_get_release_devlist().
+ */
+ switch (nodetype) {
+ case SBD_COMP_CPU:
+ max_units = MAX_CPU_UNITS_PER_BOARD;
+ break;
+
+ case SBD_COMP_MEM:
+ max_units = MAX_MEM_UNITS_PER_BOARD;
+ break;
+
+ case SBD_COMP_IO:
+ /*
+ * Need to check if specific I/O is referenced and
+ * fail post-op.
+ */
+
+ if (sbd_check_io_refs(hp, devlist, devnum) > 0) {
+ PR_IO("%s: error - I/O devices ref'd\n", f);
+ }
+
+ max_units = MAX_IO_UNITS_PER_BOARD;
+ break;
+
+ default:
+ {
+ cmn_err(CE_WARN, "%s: invalid nodetype (%d)",
+ f, (int)nodetype);
+ SBD_SET_ERR(HD2MACHERR(hp), ESBD_INVAL);
+ }
+ break;
+ }
+ hdp = sbd_get_sbdp_handle(sbp, hp);
+ spe = hdp->h_err;
+
+ for (i = 0; i < devnum; i++) {
+ int unit;
+ sbderror_t *ep;
+
+ ep = &devlist[i].dv_error;
+
+ if (sbd_set_err_in_hdl(hp, ep) == 0) {
+ continue;
+ }
+
+ unit = sbdp_get_unit_num(hdp, devlist[i].dv_dip);
+ if (unit == -1) {
+ SBD_GET_PERR(hdp->h_err, SBD_HD2ERR(hp));
+ PR_ALL("%s bad unit num: %d code %d",
+ f, unit, spe->e_code);
+ continue;
+ }
+ }
+ sbd_release_sbdp_handle(hdp);
+
+ if (SBD_GET_ERRNO(SBD_HD2ERR(hp))) {
+ PR_ALL("%s: errno %d, ecode %d during release\n",
+ f, SBD_GET_ERRNO(SBD_HD2ERR(hp)),
+ SBD_GET_ERR(SBD_HD2ERR(hp)));
+ }
+
+ if (max_units && devlist) {
+ int i;
+
+ for (i = 0; i < max_units; i++) {
+ if (SBD_GET_ERRSTR(&devlist[i].dv_error)) {
+ SBD_FREE_ERR(&devlist[i].dv_error);
+ } else {
+ break;
+ }
+ }
+ FREESTRUCT(devlist, sbd_devlist_t, max_units);
+ }
+
+ return (SBD_GET_ERRNO(SBD_HD2ERR(hp)) ? -1 : 0);
+}
+
+static void
+sbd_release_dev_done(sbd_board_t *sbp, sbd_comp_type_t nodetype, int unit)
+{
+ SBD_DEV_SET_UNREFERENCED(sbp, nodetype, unit);
+ SBD_DEVICE_TRANSITION(sbp, nodetype, unit, SBD_STATE_UNREFERENCED);
+}
+
+static void
+sbd_release_done(sbd_handle_t *hp, sbd_comp_type_t nodetype, dev_info_t *dip)
+{
+ int unit;
+ sbd_board_t *sbp = SBDH2BD(hp->h_sbd);
+ sbderror_t *ep;
+ static fn_t f = "sbd_release_done";
+ sbdp_handle_t *hdp;
+
+ PR_ALL("%s...\n", f);
+
+ hdp = sbd_get_sbdp_handle(sbp, hp);
+ ep = SBD_HD2ERR(hp);
+
+ if ((unit = sbdp_get_unit_num(hdp, dip)) < 0) {
+ cmn_err(CE_WARN,
+ "sbd:%s: unable to get unit for dip (0x%p)",
+ f, (void *)dip);
+ SBD_GET_PERR(hdp->h_err, ep);
+ sbd_release_sbdp_handle(hdp);
+ return;
+ }
+ sbd_release_sbdp_handle(hdp);
+
+ /*
+ * Transfer the device which just completed its release
+ * to the UNREFERENCED state.
+ */
+ switch (nodetype) {
+
+ case SBD_COMP_MEM:
+ sbd_release_mem_done((void *)hp, unit);
+ break;
+
+ default:
+ sbd_release_dev_done(sbp, nodetype, unit);
+ break;
+ }
+
+ /*
+ * If the entire board was released and all components
+ * unreferenced then transfer it to the UNREFERENCED state.
+ */
+ if (SBD_DEVS_RELEASED(sbp) == SBD_DEVS_UNREFERENCED(sbp)) {
+ SBD_BOARD_TRANSITION(sbp, SBD_STATE_UNREFERENCED);
+ (void) drv_getparm(TIME, (void *)&sbp->sb_time);
+ }
+}
+
+static sbd_devlist_t *
+sbd_get_detach_devlist(sbd_handle_t *hp, int32_t *devnump, int32_t pass)
+{
+ sbd_board_t *sbp;
+ uint_t uset;
+ sbd_devset_t devset;
+ sbd_devlist_t *detach_devlist;
+ static int next_pass = 1;
+ static fn_t f = "sbd_get_detach_devlist";
+
+ PR_ALL("%s (pass = %d)...\n", f, pass);
+
+ sbp = SBDH2BD(hp->h_sbd);
+ devset = HD2MACHHD(hp)->sh_devset;
+
+ *devnump = 0;
+ detach_devlist = NULL;
+
+ /*
+ * We switch on next_pass for the cases where a board
+ * does not contain a particular type of component.
+ * In these situations we don't want to return NULL
+ * prematurely. We need to check other devices and
+ * we don't want to check the same type multiple times.
+ * For example, if there were no cpus, then on pass 1
+ * we would drop through and return the memory nodes.
+ * However, on pass 2 we would switch back to the memory
+ * nodes thereby returning them twice! Using next_pass
+ * forces us down to the end (or next item).
+ */
+ if (pass == 1)
+ next_pass = 1;
+
+ switch (next_pass) {
+ case 1:
+ if (DEVSET_IN_SET(devset, SBD_COMP_MEM, DEVSET_ANYUNIT)) {
+ uset = DEVSET_GET_UNITSET(devset, SBD_COMP_MEM);
+
+ detach_devlist = sbd_get_devlist(hp, sbp,
+ SBD_COMP_MEM,
+ MAX_MEM_UNITS_PER_BOARD,
+ uset, devnump, 0);
+
+ DEVSET_DEL(devset, SBD_COMP_MEM, DEVSET_ANYUNIT);
+ if (!devset || detach_devlist) {
+ next_pass = 2;
+ return (detach_devlist);
+ }
+ /*
+ * If the caller is interested in the entire
+ * board, but there isn't any memory, then
+ * just fall through to next component.
+ */
+ }
+ /*FALLTHROUGH*/
+
+ case 2:
+ if (DEVSET_IN_SET(devset, SBD_COMP_CPU, DEVSET_ANYUNIT)) {
+ uset = DEVSET_GET_UNITSET(devset, SBD_COMP_CPU);
+
+ detach_devlist = sbd_get_devlist(hp, sbp,
+ SBD_COMP_CPU,
+ MAX_CPU_UNITS_PER_BOARD,
+ uset, devnump, 0);
+
+ DEVSET_DEL(devset, SBD_COMP_CPU, DEVSET_ANYUNIT);
+ if (!devset || detach_devlist) {
+ next_pass = 2;
+ return (detach_devlist);
+ }
+ /*
+ * If the caller is interested in the entire
+ * board, but there aren't any cpus, then just
+ * fall through to check for the next component.
+ */
+ }
+ /*FALLTHROUGH*/
+
+ case 3:
+ next_pass = -1;
+ if (DEVSET_IN_SET(devset, SBD_COMP_IO, DEVSET_ANYUNIT)) {
+ uset = DEVSET_GET_UNITSET(devset, SBD_COMP_IO);
+
+ detach_devlist = sbd_get_devlist(hp, sbp,
+ SBD_COMP_IO,
+ MAX_IO_UNITS_PER_BOARD,
+ uset, devnump, 0);
+
+ DEVSET_DEL(devset, SBD_COMP_IO, DEVSET_ANYUNIT);
+ if (!devset || detach_devlist) {
+ next_pass = 4;
+ return (detach_devlist);
+ }
+ }
+ /*FALLTHROUGH*/
+
+ default:
+ *devnump = 0;
+ return (NULL);
+ }
+ /*NOTREACHED*/
+}
+
+static int
+sbd_pre_detach_devlist(sbd_handle_t *hp, sbd_devlist_t *devlist,
+ int32_t devnum)
+{
+ int rv = 0;
+ sbd_comp_type_t nodetype;
+ static fn_t f = "sbd_pre_detach_devlist";
+
+ nodetype = sbd_get_devtype(hp, devlist->dv_dip);
+
+ PR_ALL("%s (nt = %s(%d), num = %d)...\n",
+ f, sbd_ct_str[(int)nodetype], (int)nodetype, devnum);
+
+ switch (nodetype) {
+ case SBD_COMP_CPU:
+ rv = sbd_pre_detach_cpu(hp, devlist, devnum);
+ break;
+
+ case SBD_COMP_MEM:
+ rv = sbd_pre_detach_mem(hp, devlist, devnum);
+ break;
+
+ case SBD_COMP_IO:
+ rv = sbd_pre_detach_io(hp, devlist, devnum);
+ break;
+
+ default:
+ rv = -1;
+ break;
+ }
+
+ /*
+ * We want to continue attempting to detach
+ * other components.
+ */
+ return (rv);
+}
+
+static int
+sbd_post_detach_devlist(sbd_handle_t *hp, sbd_devlist_t *devlist,
+ int32_t devnum)
+{
+ int i, max_units = 0, rv = 0;
+ sbd_comp_type_t nodetype;
+ sbd_board_t *sbp;
+ sbd_istate_t bstate;
+ static fn_t f = "sbd_post_detach_devlist";
+ sbdp_handle_t *hdp;
+
+ sbp = SBDH2BD(hp->h_sbd);
+ nodetype = sbd_get_devtype(hp, devlist->dv_dip);
+
+ hdp = sbd_get_sbdp_handle(sbp, hp);
+
+ PR_ALL("%s (nt = %s(%d), num = %d)...\n",
+ f, sbd_ct_str[(int)nodetype], (int)nodetype, devnum);
+
+ /*
+ * Need to free up devlist[] created earlier in
+ * sbd_get_detach_devlist().
+ */
+ switch (nodetype) {
+ case SBD_COMP_CPU:
+ max_units = MAX_CPU_UNITS_PER_BOARD;
+ rv = sbd_post_detach_cpu(hp, devlist, devnum);
+ break;
+
+ case SBD_COMP_MEM:
+ max_units = MAX_MEM_UNITS_PER_BOARD;
+ rv = sbd_post_detach_mem(hp, devlist, devnum);
+ break;
+
+ case SBD_COMP_IO:
+ max_units = MAX_IO_UNITS_PER_BOARD;
+ rv = sbd_post_detach_io(hp, devlist, devnum);
+ break;
+
+ default:
+ rv = -1;
+ break;
+ }
+
+
+ for (i = 0; i < devnum; i++) {
+ int unit;
+ sbderror_t *ep;
+ dev_info_t *dip;
+
+ ep = &devlist[i].dv_error;
+
+ if (sbd_set_err_in_hdl(hp, ep) == 0)
+ continue;
+
+ dip = devlist[i].dv_dip;
+ unit = sbdp_get_unit_num(hdp, dip);
+ if (unit == -1) {
+ if (hp->h_flags & SBD_IOCTL_FLAG_FORCE)
+ continue;
+ else {
+ SBD_GET_PERR(hdp->h_err, ep);
+ break;
+ }
+ }
+ nodetype = sbd_get_devtype(hp, dip);
+
+ if (sbd_check_unit_attached(sbp, dip, unit, nodetype,
+ ep) >= 0) {
+ /*
+ * Device is still attached probably due
+ * to an error. Need to keep track of it.
+ */
+ PR_ALL("%s: ERROR (nt=%s, b=%d, u=%d) not detached\n",
+ f, sbd_ct_str[(int)nodetype], sbp->sb_num,
+ unit);
+ continue;
+ }
+
+ SBD_DEV_CLR_ATTACHED(sbp, nodetype, unit);
+ SBD_DEV_CLR_RELEASED(sbp, nodetype, unit);
+ SBD_DEV_CLR_UNREFERENCED(sbp, nodetype, unit);
+ SBD_DEVICE_TRANSITION(sbp, nodetype, unit,
+ SBD_STATE_UNCONFIGURED);
+ }
+ sbd_release_sbdp_handle(hdp);
+
+ bstate = SBD_BOARD_STATE(sbp);
+ if (bstate != SBD_STATE_UNCONFIGURED) {
+ if (SBD_DEVS_PRESENT(sbp) == SBD_DEVS_UNATTACHED(sbp)) {
+ /*
+ * All devices are finally detached.
+ */
+ SBD_BOARD_TRANSITION(sbp, SBD_STATE_UNCONFIGURED);
+ } else if ((SBD_BOARD_STATE(sbp) != SBD_STATE_PARTIAL) &&
+ SBD_DEVS_ATTACHED(sbp)) {
+ /*
+ * Some devices remain attached.
+ */
+ SBD_BOARD_TRANSITION(sbp, SBD_STATE_PARTIAL);
+ }
+ }
+
+ if (rv) {
+ PR_ALL("%s: errno %d, ecode %d during detach\n",
+ f, SBD_GET_ERRNO(SBD_HD2ERR(hp)),
+ SBD_GET_ERR(HD2MACHERR(hp)));
+ }
+
+ if (max_units && devlist) {
+ int i;
+
+ for (i = 0; i < max_units; i++) {
+ if (SBD_GET_ERRSTR(&devlist[i].dv_error)) {
+ SBD_FREE_ERR(&devlist[i].dv_error);
+ } else {
+ break;
+ }
+ }
+ FREESTRUCT(devlist, sbd_devlist_t, max_units);
+ }
+
+ return (SBD_GET_ERRNO(SBD_HD2ERR(hp)) ? -1 : 0);
+}
+
+/*
+ * Return the unit number of the respective dip if
+ * it's found to be attached.
+ */
+static int
+sbd_check_unit_attached(sbd_board_t *sbp, dev_info_t *dip, int unit,
+ sbd_comp_type_t nodetype, sbderror_t *ep)
+{
+ int rv = -1;
+ processorid_t cpuid;
+ uint64_t basepa, endpa;
+ struct memlist *ml;
+ extern struct memlist *phys_install;
+ sbdp_handle_t *hdp;
+ sbd_handle_t *hp = MACHBD2HD(sbp);
+ static fn_t f = "sbd_check_unit_attached";
+
+ hdp = sbd_get_sbdp_handle(sbp, hp);
+
+ switch (nodetype) {
+
+ case SBD_COMP_CPU:
+ cpuid = sbdp_get_cpuid(hdp, dip);
+ if (cpuid < 0) {
+ break;
+ }
+ mutex_enter(&cpu_lock);
+ if (cpu_get(cpuid) != NULL)
+ rv = unit;
+ mutex_exit(&cpu_lock);
+ break;
+
+ case SBD_COMP_MEM:
+ if (sbdphw_get_base_physaddr(hdp, dip, &basepa)) {
+ break;
+ }
+ if (sbdp_get_mem_alignment(hdp, dip, &endpa)) {
+ cmn_err(CE_WARN, "%s sbdp_get_mem_alignment fail", f);
+ break;
+ }
+
+ basepa &= ~(endpa - 1);
+ endpa += basepa;
+ /*
+ * Check if base address is in phys_install.
+ */
+ memlist_read_lock();
+ for (ml = phys_install; ml; ml = ml->next)
+ if ((endpa <= ml->address) ||
+ (basepa >= (ml->address + ml->size)))
+ continue;
+ else
+ break;
+ memlist_read_unlock();
+ if (ml != NULL)
+ rv = unit;
+ break;
+
+ case SBD_COMP_IO:
+ {
+ dev_info_t *tdip, *pdip;
+
+ tdip = dip;
+
+ /*
+ * ddi_walk_devs() requires that topdip's parent be held.
+ */
+ pdip = ddi_get_parent(sbp->sb_topdip);
+ if (pdip) {
+ ndi_hold_devi(pdip);
+ ndi_devi_enter(pdip, &rv);
+ }
+ ddi_walk_devs(sbp->sb_topdip, sbd_check_io_attached,
+ (void *)&tdip);
+ if (pdip) {
+ ndi_devi_exit(pdip, rv);
+ ndi_rele_devi(pdip);
+ }
+
+ if (tdip == NULL)
+ rv = unit;
+ else
+ rv = -1;
+ break;
+ }
+
+ default:
+ PR_ALL("%s: unexpected nodetype(%d) for dip 0x%p\n",
+ f, nodetype, (void *)dip);
+ rv = -1;
+ break;
+ }
+
+ /*
+ * Save the error that sbdp sent us and report it
+ */
+ if (rv == -1)
+ SBD_GET_PERR(hdp->h_err, ep);
+
+ sbd_release_sbdp_handle(hdp);
+
+ return (rv);
+}
+
+/*
+ * Return memhandle, if in fact, this memunit is the owner of
+ * a scheduled memory delete.
+ */
+int
+sbd_get_memhandle(sbd_handle_t *hp, dev_info_t *dip, memhandle_t *mhp)
+{
+ sbd_board_t *sbp = SBDH2BD(hp->h_sbd);
+ sbd_mem_unit_t *mp;
+ sbdp_handle_t *hdp;
+ int unit;
+ static fn_t f = "sbd_get_memhandle";
+
+ PR_MEM("%s...\n", f);
+
+ hdp = sbd_get_sbdp_handle(sbp, hp);
+
+ unit = sbdp_get_unit_num(hdp, dip);
+ if (unit == -1) {
+ SBD_GET_PERR(hdp->h_err, SBD_HD2ERR(hp));
+ sbd_release_sbdp_handle(hdp);
+ return (-1);
+ }
+ sbd_release_sbdp_handle(hdp);
+
+ mp = SBD_GET_BOARD_MEMUNIT(sbp, unit);
+
+ if (mp->sbm_flags & SBD_MFLAG_RELOWNER) {
+ *mhp = mp->sbm_memhandle;
+ return (0);
+ } else {
+ SBD_SET_ERR(SBD_HD2ERR(hp), ESBD_INTERNAL);
+ SBD_SET_ERRSTR(SBD_HD2ERR(hp), sbp->sb_mempath[unit]);
+ return (-1);
+ }
+ /*NOTREACHED*/
+}
+
+
+static int
+sbd_cpu_cnt(sbd_handle_t *hp, sbd_devset_t devset)
+{
+ int c, cix;
+ sbd_board_t *sbp;
+
+ sbp = SBDH2BD(hp->h_sbd);
+
+ /*
+ * Only look for requested devices that are actually present.
+ */
+ devset &= SBD_DEVS_PRESENT(sbp);
+
+ for (c = cix = 0; c < MAX_CMP_UNITS_PER_BOARD; c++) {
+ /*
+ * Index for core 1 , if exists.
+ * With the current implementation it is
+ * MAX_CMP_UNITS_PER_BOARD off from core 0.
+ * The calculation will need to change if
+ * the assumption is no longer true.
+ */
+ int c1 = c + MAX_CMP_UNITS_PER_BOARD;
+
+ if (DEVSET_IN_SET(devset, SBD_COMP_CMP, c) == 0) {
+ continue;
+ }
+
+ /*
+ * Check to see if the dip(s) exist for this chip
+ */
+ if ((sbp->sb_devlist[NIX(SBD_COMP_CMP)][c] == NULL) &&
+ (sbp->sb_devlist[NIX(SBD_COMP_CMP)][c1] == NULL))
+ continue;
+
+ cix++;
+ }
+
+ return (cix);
+}
+
+static int
+sbd_mem_cnt(sbd_handle_t *hp, sbd_devset_t devset)
+{
+ int i, ix;
+ sbd_board_t *sbp = SBDH2BD(hp->h_sbd);
+
+ /*
+ * Only look for requested devices that are actually present.
+ */
+ devset &= SBD_DEVS_PRESENT(sbp);
+
+ for (i = ix = 0; i < MAX_MEM_UNITS_PER_BOARD; i++) {
+ dev_info_t *dip;
+
+ if (DEVSET_IN_SET(devset, SBD_COMP_MEM, i) == 0) {
+ continue;
+ }
+
+ dip = sbp->sb_devlist[NIX(SBD_COMP_MEM)][i];
+ if (dip == NULL)
+ continue;
+
+ ix++;
+ }
+
+ return (ix);
+}
+
+/*
+ * NOTE: This routine is only partially smart about multiple
+ * mem-units. Need to make mem-status structure smart
+ * about them also.
+ */
+static int
+sbd_mem_status(sbd_handle_t *hp, sbd_devset_t devset, sbd_dev_stat_t *dsp)
+{
+ int m, mix, rv;
+ memdelstat_t mdst;
+ memquery_t mq;
+ sbd_board_t *sbp;
+ sbd_mem_unit_t *mp;
+ sbd_mem_stat_t *msp;
+ extern int kcage_on;
+ int i;
+ static fn_t f = "sbd_mem_status";
+
+ sbp = SBDH2BD(hp->h_sbd);
+
+ /*
+ * Check the present devset and access the dip with
+ * status lock held to protect agains a concurrent
+ * unconfigure or disconnect thread.
+ */
+ mutex_enter(&sbp->sb_slock);
+
+ /*
+ * Only look for requested devices that are actually present.
+ */
+ devset &= SBD_DEVS_PRESENT(sbp);
+
+ for (m = mix = 0; m < MAX_MEM_UNITS_PER_BOARD; m++) {
+ dev_info_t *dip;
+
+
+ if (DEVSET_IN_SET(devset, SBD_COMP_MEM, m) == 0)
+ continue;
+
+ /*
+ * Check to make sure the memory unit is in a state
+ * where its fully initialized.
+ */
+ if (SBD_DEVICE_STATE(sbp, SBD_COMP_MEM, m) == SBD_STATE_EMPTY)
+ continue;
+
+ dip = sbp->sb_devlist[NIX(SBD_COMP_MEM)][m];
+ if (dip == NULL)
+ continue;
+
+ mp = SBD_GET_BOARD_MEMUNIT(sbp, m);
+
+ msp = &dsp->d_mem;
+
+ bzero((caddr_t)msp, sizeof (*msp));
+ msp->ms_type = SBD_COMP_MEM;
+
+ /*
+ * The plugin expects -1 for the mem unit
+ */
+ msp->ms_cm.c_id.c_unit = -1;
+
+ /*
+ * Get the memory name from what sbdp gave us
+ */
+ for (i = 0; SBD_COMP(i) != SBD_COMP_UNKNOWN; i++) {
+ if (SBD_COMP(i) == SBD_COMP_MEM) {
+ (void) strcpy(msp->ms_name, SBD_DEVNAME(i));
+ }
+ }
+ msp->ms_cm.c_cond = mp->sbm_cm.sbdev_cond;
+ msp->ms_cm.c_busy = mp->sbm_cm.sbdev_busy;
+ msp->ms_cm.c_time = mp->sbm_cm.sbdev_time;
+
+ /* XXX revisit this after memory conversion */
+ msp->ms_ostate = ostate_cvt(SBD_DEVICE_STATE(
+ sbp, SBD_COMP_MEM, m));
+
+ msp->ms_basepfn = mp->sbm_basepfn;
+ msp->ms_pageslost = mp->sbm_pageslost;
+ msp->ms_cage_enabled = kcage_on;
+ msp->ms_interleave = mp->sbm_interleave;
+
+ if (mp->sbm_flags & SBD_MFLAG_RELOWNER)
+ rv = kphysm_del_status(mp->sbm_memhandle, &mdst);
+ else
+ rv = KPHYSM_EHANDLE; /* force 'if' to fail */
+
+ if (rv == KPHYSM_OK) {
+ msp->ms_totpages += mdst.phys_pages;
+
+ /*
+ * Any pages above managed is "free",
+ * i.e. it's collected.
+ */
+ msp->ms_detpages += (uint_t)(mdst.collected +
+ mdst.phys_pages -
+ mdst.managed);
+ } else {
+ msp->ms_totpages += (uint_t)mp->sbm_npages;
+
+ /*
+ * If we're UNREFERENCED or UNCONFIGURED,
+ * then the number of detached pages is
+ * however many pages are on the board.
+ * I.e. detached = not in use by OS.
+ */
+ switch (msp->ms_cm.c_ostate) {
+ /*
+ * changed to use cfgadm states
+ *
+ * was:
+ * case SFDR_STATE_UNREFERENCED:
+ * case SFDR_STATE_UNCONFIGURED:
+ */
+ case SBD_STAT_UNCONFIGURED:
+ msp->ms_detpages = msp->ms_totpages;
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ rv = kphysm_del_span_query(mp->sbm_basepfn,
+ mp->sbm_npages, &mq);
+ if (rv == KPHYSM_OK) {
+ msp->ms_managed_pages = mq.managed;
+ msp->ms_noreloc_pages = mq.nonrelocatable;
+ msp->ms_noreloc_first = mq.first_nonrelocatable;
+ msp->ms_noreloc_last = mq.last_nonrelocatable;
+ msp->ms_cm.c_sflags = 0;
+ if (mq.nonrelocatable) {
+ SBD_SET_SUSPEND(SBD_CMD_UNCONFIGURE,
+ dsp->ds_suspend);
+ }
+ } else {
+ PR_MEM("%s: kphysm_del_span_query() = %d\n", f, rv);
+ }
+
+ mix++;
+ dsp++;
+ }
+
+ mutex_exit(&sbp->sb_slock);
+
+ return (mix);
+}
+
+static void
+sbd_cancel(sbd_handle_t *hp)
+{
+ int i;
+ sbd_devset_t devset;
+ sbd_board_t *sbp = SBDH2BD(hp->h_sbd);
+ static fn_t f = "sbd_cancel";
+ int rv;
+
+ PR_ALL("%s...\n", f);
+
+ /*
+ * Only devices which have been "released" are
+ * subject to cancellation.
+ */
+ devset = HD2MACHHD(hp)->sh_devset & SBD_DEVS_UNREFERENCED(sbp);
+
+ /*
+ * Nothing to do for CPUs or IO other than change back
+ * their state.
+ */
+ for (i = 0; i < MAX_CPU_UNITS_PER_BOARD; i++) {
+ if (!DEVSET_IN_SET(devset, SBD_COMP_CPU, i))
+ continue;
+ if (sbd_cancel_cpu(hp, i) != SBD_CPUERR_FATAL) {
+ SBD_DEVICE_TRANSITION(sbp, SBD_COMP_CPU, i,
+ SBD_STATE_CONFIGURED);
+ } else {
+ SBD_DEVICE_TRANSITION(sbp, SBD_COMP_CPU, i,
+ SBD_STATE_FATAL);
+ }
+ }
+
+ for (i = 0; i < MAX_IO_UNITS_PER_BOARD; i++) {
+ if (!DEVSET_IN_SET(devset, SBD_COMP_IO, i))
+ continue;
+ SBD_DEVICE_TRANSITION(sbp, SBD_COMP_IO, i,
+ SBD_STATE_CONFIGURED);
+ }
+
+ for (i = 0; i < MAX_MEM_UNITS_PER_BOARD; i++) {
+ if (!DEVSET_IN_SET(devset, SBD_COMP_MEM, i))
+ continue;
+ if ((rv = sbd_cancel_mem(hp, i)) == 0) {
+ SBD_DEVICE_TRANSITION(sbp, SBD_COMP_MEM, i,
+ SBD_STATE_CONFIGURED);
+ } else if (rv == -1) {
+ SBD_DEVICE_TRANSITION(sbp, SBD_COMP_MEM, i,
+ SBD_STATE_FATAL);
+ }
+ }
+
+ PR_ALL("%s: unreleasing devset (0x%x)\n", f, (uint_t)devset);
+
+ SBD_DEVS_CANCEL(sbp, devset);
+
+ if (SBD_DEVS_UNREFERENCED(sbp) == 0) {
+ sbd_istate_t new_state;
+ /*
+ * If the board no longer has any released devices
+ * than transfer it back to the CONFIG/PARTIAL state.
+ */
+ if (SBD_DEVS_ATTACHED(sbp) == SBD_DEVS_PRESENT(sbp))
+ new_state = SBD_STATE_CONFIGURED;
+ else
+ new_state = SBD_STATE_PARTIAL;
+ if (SBD_BOARD_STATE(sbp) != new_state) {
+ SBD_BOARD_TRANSITION(sbp, new_state);
+ }
+ sbp->sb_ostate = SBD_STAT_CONFIGURED;
+ (void) drv_getparm(TIME, (void *)&sbp->sb_time);
+ }
+}
+
+static void
+sbd_get_ncm(sbd_handle_t *hp)
+{
+ sbd_devset_t devset;
+ sbd_priv_handle_t *shp = HD2MACHHD(hp);
+ sbd_cmd_t *cmdp = (sbd_cmd_t *)hp->h_iap;
+ int error;
+
+ /* pre_op restricted the devices to those selected by the ioctl */
+ devset = shp->sh_devset;
+
+ cmdp->cmd_getncm.g_ncm = sbd_cpu_cnt(hp, devset)
+ + sbd_io_cnt(hp, devset) + sbd_mem_cnt(hp, devset);
+
+ error = sbd_copyout_ioarg(hp->h_mode, hp->h_cmd, cmdp,
+ (sbd_ioctl_arg_t *)shp->sh_arg);
+
+ if (error != 0)
+ SBD_SET_ERRNO(SBD_HD2ERR(hp), error);
+}
+
+static void
+sbd_status(sbd_handle_t *hp)
+{
+ int nstat, mode, ncm, sz, cksz;
+ sbd_priv_handle_t *shp = HD2MACHHD(hp);
+ sbd_devset_t devset;
+ sbd_board_t *sbp = SBDH2BD(hp->h_sbd);
+ sbd_stat_t *dstatp;
+ sbd_cmd_t *cmdp = (sbd_cmd_t *)hp->h_iap;
+ sbdp_handle_t *hdp;
+ sbd_dev_stat_t *devstatp;
+
+#ifdef _MULTI_DATAMODEL
+ int sz32;
+ sbd_stat32_t *dstat32p;
+#endif /* _MULTI_DATAMODEL */
+
+ static fn_t f = "sbd_status";
+
+ mode = hp->h_mode;
+ devset = shp->sh_devset;
+
+ devset &= SBD_DEVS_PRESENT(sbp);
+
+ if (cmdp->cmd_cm.c_id.c_type == SBD_COMP_NONE) {
+ if (cmdp->cmd_cm.c_flags & SBD_FLAG_ALLCMP) {
+ /*
+ * Get the number of components "ncm" on the board.
+ * Calculate size of buffer required to store one
+ * sbd_stat_t structure plus ncm-1 sbd_dev_stat_t
+ * structures. Note that sbd_stat_t already contains
+ * one sbd_dev_stat_t, so only an additional ncm-1
+ * sbd_dev_stat_t structures need to be accounted for
+ * in the calculation when more than one component
+ * is present.
+ */
+ ncm = sbd_cpu_cnt(hp, devset) + sbd_io_cnt(hp, devset) +
+ sbd_mem_cnt(hp, devset);
+
+ } else {
+ /*
+ * In the case of c_type == SBD_COMP_NONE, and
+ * SBD_FLAG_ALLCMP not specified, only the board
+ * info is to be returned, no components.
+ */
+ ncm = 0;
+ devset = 0;
+ }
+ } else {
+ /* Confirm that only one component is selected. */
+ ncm = sbd_cpu_cnt(hp, devset) + sbd_io_cnt(hp, devset) +
+ sbd_mem_cnt(hp, devset);
+ if (ncm != 1) {
+ PR_ALL("%s: expected ncm of 1, got %d, devset 0x%x\n",
+ f, ncm, devset);
+ SBD_SET_ERRNO(SBD_HD2ERR(hp), EINVAL);
+ return;
+ }
+ }
+
+ sz = sizeof (sbd_stat_t);
+ if (ncm > 1)
+ sz += sizeof (sbd_dev_stat_t) * (ncm - 1);
+
+ cksz = sz;
+
+ /*
+ * s_nbytes describes the size of the preallocated user
+ * buffer into which the application is executing to
+ * receive the sbd_stat_t and sbd_dev_stat_t structures.
+ * This buffer must be at least the required (sz) size.
+ */
+
+#ifdef _MULTI_DATAMODEL
+
+ /*
+ * More buffer space is required for the 64bit to 32bit
+ * conversion of data structures.
+ */
+ if (ddi_model_convert_from(mode & FMODELS) == DDI_MODEL_ILP32) {
+ sz32 = sizeof (sbd_stat32_t);
+ if (ncm > 1)
+ sz32 += sizeof (sbd_dev_stat32_t) * (ncm - 1);
+ cksz = sz32;
+ } else
+ sz32 = 0;
+#endif
+
+ if ((int)cmdp->cmd_stat.s_nbytes < cksz) {
+ PR_ALL("%s: ncm=%d s_nbytes = 0x%x\n", f, ncm,
+ cmdp->cmd_stat.s_nbytes);
+ PR_ALL("%s: expected size of 0x%x\n", f, cksz);
+ SBD_SET_ERRNO(SBD_HD2ERR(hp), EINVAL);
+ return;
+ }
+
+ dstatp = kmem_zalloc(sz, KM_SLEEP);
+ devstatp = &dstatp->s_stat[0];
+
+#ifdef _MULTI_DATAMODEL
+ if (sz32 != 0)
+ dstat32p = kmem_zalloc(sz32, KM_SLEEP);
+#endif
+
+ /*
+ * if connected or better, provide cached status if available,
+ * otherwise call sbdp for status
+ */
+ mutex_enter(&sbp->sb_flags_mutex);
+ switch (sbp->sb_state) {
+
+ case SBD_STATE_CONNECTED:
+ case SBD_STATE_PARTIAL:
+ case SBD_STATE_CONFIGURED:
+ if (sbp->sb_flags & SBD_BOARD_STATUS_CACHED) {
+ bcopy(&sbp->sb_stat, dstatp, sizeof (sbd_stat_t));
+ dstatp->s_rstate = rstate_cvt(sbp->sb_state);
+ dstatp->s_ostate = ostate_cvt(sbp->sb_state);
+ dstatp->s_busy = sbp->sb_busy;
+ dstatp->s_time = sbp->sb_time;
+ dstatp->s_cond = sbp->sb_cond;
+ break;
+ }
+ /*FALLTHROUGH*/
+
+ default:
+ sbp->sb_flags &= ~SBD_BOARD_STATUS_CACHED;
+ dstatp->s_board = sbp->sb_num;
+ dstatp->s_ostate = ostate_cvt(sbp->sb_state);
+ dstatp->s_time = sbp->sb_time;
+
+ hdp = sbd_get_sbdp_handle(sbp, hp);
+
+ if (sbdp_get_board_status(hdp, dstatp) != 0) {
+ SBD_GET_PERR(hdp->h_err, SBD_HD2ERR(hp));
+ sbd_release_sbdp_handle(hdp);
+#ifdef _MULTI_DATAMODEL
+ if (sz32 != 0)
+ kmem_free(dstat32p, sz32);
+#endif
+ kmem_free(dstatp, sz);
+ mutex_exit(&sbp->sb_flags_mutex);
+ return;
+ }
+ /*
+ * Do not cache status if the busy flag has
+ * been set by the call to sbdp_get_board_status().
+ */
+ if (!dstatp->s_busy) {
+ /* Can get board busy flag now */
+ dstatp->s_busy = sbp->sb_busy;
+ sbp->sb_cond = (sbd_cond_t)dstatp->s_cond;
+ bcopy(dstatp, &sbp->sb_stat,
+ sizeof (sbd_stat_t));
+ sbp->sb_flags |= SBD_BOARD_STATUS_CACHED;
+ }
+ sbd_release_sbdp_handle(hdp);
+ break;
+ }
+ mutex_exit(&sbp->sb_flags_mutex);
+
+ if (DEVSET_IN_SET(devset, SBD_COMP_CPU, DEVSET_ANYUNIT))
+ if ((nstat = sbd_cpu_flags(hp, devset, devstatp)) > 0) {
+ dstatp->s_nstat += nstat;
+ devstatp += nstat;
+ }
+
+ if (DEVSET_IN_SET(devset, SBD_COMP_MEM, DEVSET_ANYUNIT))
+ if ((nstat = sbd_mem_status(hp, devset, devstatp)) > 0) {
+ dstatp->s_nstat += nstat;
+ devstatp += nstat;
+ }
+
+ if (DEVSET_IN_SET(devset, SBD_COMP_IO, DEVSET_ANYUNIT))
+ if ((nstat = sbd_io_status(hp, devset, devstatp)) > 0) {
+ dstatp->s_nstat += nstat;
+ devstatp += nstat;
+ }
+
+ /* paranoia: detect buffer overrun */
+ if ((caddr_t)devstatp > ((caddr_t)dstatp) + sz) {
+ PR_ALL("%s: buffer overrun\n", f);
+#ifdef _MULTI_DATAMODEL
+ if (sz32 != 0)
+ kmem_free(dstat32p, sz32);
+#endif
+ kmem_free(dstatp, sz);
+ SBD_SET_ERRNO(SBD_HD2ERR(hp), EINVAL);
+ return;
+ }
+
+/* if necessary, move data into intermediate device status buffer */
+#ifdef _MULTI_DATAMODEL
+ if (ddi_model_convert_from(mode & FMODELS) == DDI_MODEL_ILP32) {
+ int i, j;
+
+ ASSERT(sz32 != 0);
+ /* paranoia: detect buffer overrun */
+ if ((caddr_t)&dstat32p->s_stat[dstatp->s_nstat] >
+ ((caddr_t)dstat32p) + sz32) {
+ cmn_err(CE_WARN,
+ "sbd:%s: buffer32 overrun", f);
+#ifdef _MULTI_DATAMODEL
+ if (sz32 != 0)
+ kmem_free(dstat32p, sz32);
+#endif
+ kmem_free(dstatp, sz);
+ SBD_SET_ERRNO(SBD_HD2ERR(hp), EINVAL);
+ return;
+ }
+
+ /*
+ * initialize 32 bit sbd board status structure
+ */
+ dstat32p->s_board = (int32_t)dstatp->s_board;
+ dstat32p->s_nstat = (int32_t)dstatp->s_nstat;
+ dstat32p->s_rstate = dstatp->s_rstate;
+ dstat32p->s_ostate = dstatp->s_ostate;
+ dstat32p->s_cond = dstatp->s_cond;
+ dstat32p->s_busy = dstatp->s_busy;
+ dstat32p->s_time = dstatp->s_time;
+ dstat32p->s_assigned = dstatp->s_assigned;
+ dstat32p->s_power = dstatp->s_power;
+ dstat32p->s_platopts = (int32_t)dstatp->s_platopts;
+ (void) strcpy(dstat32p->s_type, dstatp->s_type);
+
+ for (i = 0; i < dstatp->s_nstat; i++) {
+ sbd_dev_stat_t *dsp = &dstatp->s_stat[i];
+ sbd_dev_stat32_t *ds32p = &dstat32p->s_stat[i];
+
+ /*
+ * copy common data for the device
+ */
+ ds32p->d_cm.ci_type = (int32_t)dsp->d_cm.ci_type;
+ ds32p->d_cm.ci_unit = (int32_t)dsp->d_cm.ci_unit;
+ ds32p->d_cm.c_ostate = (int32_t)dsp->d_cm.c_ostate;
+ ds32p->d_cm.c_cond = (int32_t)dsp->d_cm.c_cond;
+ ds32p->d_cm.c_busy = (int32_t)dsp->d_cm.c_busy;
+ ds32p->d_cm.c_time = (time32_t)dsp->d_cm.c_time;
+ ds32p->d_cm.c_sflags = (int32_t)dsp->d_cm.c_sflags;
+ (void) strcpy(ds32p->d_cm.ci_name, dsp->d_cm.ci_name);
+
+ /* copy type specific data for the device */
+ switch (dsp->d_cm.ci_type) {
+
+ case SBD_COMP_CPU:
+ ds32p->d_cpu.cs_isbootproc =
+ (int32_t)dsp->d_cpu.cs_isbootproc;
+ ds32p->d_cpu.cs_cpuid =
+ (int32_t)dsp->d_cpu.cs_cpuid;
+ ds32p->d_cpu.cs_speed =
+ (int32_t)dsp->d_cpu.cs_speed;
+ ds32p->d_cpu.cs_ecache =
+ (int32_t)dsp->d_cpu.cs_ecache;
+ break;
+
+ case SBD_COMP_MEM:
+ ds32p->d_mem.ms_type =
+ (int32_t)dsp->d_mem.ms_type;
+ ds32p->d_mem.ms_ostate =
+ (int32_t)dsp->d_mem.ms_ostate;
+ ds32p->d_mem.ms_cond =
+ (int32_t)dsp->d_mem.ms_cond;
+ ds32p->d_mem.ms_interleave =
+ (uint32_t)dsp->d_mem.ms_interleave;
+ ds32p->d_mem.ms_basepfn =
+ (uint32_t)dsp->d_mem.ms_basepfn;
+ ds32p->d_mem.ms_totpages =
+ (uint32_t)dsp->d_mem.ms_totpages;
+ ds32p->d_mem.ms_detpages =
+ (uint32_t)dsp->d_mem.ms_detpages;
+ ds32p->d_mem.ms_pageslost =
+ (int32_t)dsp->d_mem.ms_pageslost;
+ ds32p->d_mem.ms_managed_pages =
+ (int32_t)dsp->d_mem.ms_managed_pages;
+ ds32p->d_mem.ms_noreloc_pages =
+ (int32_t)dsp->d_mem.ms_noreloc_pages;
+ ds32p->d_mem.ms_noreloc_first =
+ (int32_t)dsp->d_mem.ms_noreloc_first;
+ ds32p->d_mem.ms_noreloc_last =
+ (int32_t)dsp->d_mem.ms_noreloc_last;
+ ds32p->d_mem.ms_cage_enabled =
+ (int32_t)dsp->d_mem.ms_cage_enabled;
+ ds32p->d_mem.ms_peer_is_target =
+ (int32_t)dsp->d_mem.ms_peer_is_target;
+ (void) strcpy(ds32p->d_mem.ms_peer_ap_id,
+ dsp->d_mem.ms_peer_ap_id);
+ break;
+
+
+ case SBD_COMP_IO:
+
+ ds32p->d_io.is_type =
+ (int32_t)dsp->d_io.is_type;
+ ds32p->d_io.is_unsafe_count =
+ (int32_t)dsp->d_io.is_unsafe_count;
+ ds32p->d_io.is_referenced =
+ (int32_t)dsp->d_io.is_referenced;
+ for (j = 0; j < SBD_MAX_UNSAFE; j++)
+ ds32p->d_io.is_unsafe_list[j] =
+ (int32_t)
+ ds32p->d_io.is_unsafe_list[j];
+ bcopy(dsp->d_io.is_pathname,
+ ds32p->d_io.is_pathname, MAXPATHLEN);
+ break;
+
+ case SBD_COMP_CMP:
+ /* copy sbd_cmp_stat_t structure members */
+ bcopy(&dsp->d_cmp.ps_cpuid[0],
+ &ds32p->d_cmp.ps_cpuid[0],
+ sizeof (ds32p->d_cmp.ps_cpuid));
+ ds32p->d_cmp.ps_ncores =
+ (int32_t)dsp->d_cmp.ps_ncores;
+ ds32p->d_cmp.ps_speed =
+ (int32_t)dsp->d_cmp.ps_speed;
+ ds32p->d_cmp.ps_ecache =
+ (int32_t)dsp->d_cmp.ps_ecache;
+ break;
+
+ default:
+ cmn_err(CE_WARN,
+ "sbd:%s: unknown dev type (%d)", f,
+ (int)dsp->d_cm.c_id.c_type);
+ break;
+ }
+ }
+
+ if (ddi_copyout((void *)dstat32p,
+ cmdp->cmd_stat.s_statp, sz32, mode) != 0) {
+ cmn_err(CE_WARN,
+ "sbd:%s: failed to copyout status "
+ "for board %d", f, sbp->sb_num);
+ SBD_SET_ERRNO(SBD_HD2ERR(hp), EFAULT);
+ }
+ } else
+#endif /* _MULTI_DATAMODEL */
+ if (ddi_copyout((void *)dstatp, cmdp->cmd_stat.s_statp,
+ sz, mode) != 0) {
+ cmn_err(CE_WARN,
+ "sbd:%s: failed to copyout status for board %d",
+ f, sbp->sb_num);
+ SBD_SET_ERRNO(SBD_HD2ERR(hp), EFAULT);
+ }
+
+#ifdef _MULTI_DATAMODEL
+ if (sz32 != 0)
+ kmem_free(dstat32p, sz32);
+#endif
+ kmem_free(dstatp, sz);
+}
+
+/*
+ * Called at driver load time to determine the state and condition
+ * of an existing board in the system.
+ */
+static void
+sbd_board_discovery(sbd_board_t *sbp)
+{
+ int i;
+ dev_info_t *dip;
+ sbd_devset_t devs_lost, devs_attached = 0;
+ extern kmutex_t cpu_lock;
+ sbdp_handle_t *hdp;
+ static fn_t f = "sbd_board_discovery";
+ sbderror_t error, *ep;
+ sbd_handle_t *hp = MACHBD2HD(sbp);
+
+ if (SBD_DEVS_PRESENT(sbp) == 0) {
+ PR_ALL("%s: board %d has no devices present\n",
+ f, sbp->sb_num);
+ return;
+ }
+
+ ep = &error;
+ bzero(ep, sizeof (sbderror_t));
+
+ /*
+ * Check for existence of cpus.
+ */
+
+ hdp = sbd_get_sbdp_handle(sbp, hp);
+
+ for (i = 0; i < MAX_CPU_UNITS_PER_BOARD; i++) {
+ processorid_t cpuid;
+
+ if (!SBD_DEV_IS_PRESENT(sbp, SBD_COMP_CPU, i))
+ continue;
+
+ dip = sbp->sb_devlist[NIX(SBD_COMP_CPU)][i];
+
+ if (dip != NULL) {
+ cpuid = sbdp_get_cpuid(hdp, dip);
+
+ if (cpuid < 0) {
+ SBD_GET_PERR(hdp->h_err,
+ ep);
+ continue;
+ }
+
+ mutex_enter(&cpu_lock); /* needed to call cpu_get() */
+ if (cpu_get(cpuid)) {
+ SBD_DEV_SET_ATTACHED(sbp, SBD_COMP_CPU, i);
+ DEVSET_ADD(devs_attached, SBD_COMP_CPU, i);
+ PR_ALL("%s: board %d, cpuid %d - attached\n",
+ f, sbp->sb_num, cpuid);
+ }
+ mutex_exit(&cpu_lock);
+ sbd_init_cpu_unit(sbp, i);
+ }
+ }
+
+ /*
+ * Check for existence of memory.
+ */
+ for (i = 0; i < MAX_MEM_UNITS_PER_BOARD; i++) {
+ uint64_t basepa, endpa;
+ struct memlist *ml;
+ extern struct memlist *phys_install;
+
+ if (!SBD_DEV_IS_PRESENT(sbp, SBD_COMP_MEM, i))
+ continue;
+
+ dip = sbp->sb_devlist[NIX(SBD_COMP_MEM)][i];
+ if (dip == NULL)
+ continue;
+
+ if (sbdphw_get_base_physaddr(hdp, dip, &basepa)) {
+ /* omit phantom memory controllers on I/O boards */
+ if (SBD_DEV_IS_PRESENT(sbp, SBD_COMP_MEM, i)) {
+ ASSERT(sbp->sb_ndev != 0);
+ SBD_DEV_CLR_PRESENT(sbp, SBD_COMP_MEM, i);
+ sbp->sb_ndev--;
+ }
+ sbp->sb_devlist[NIX(SBD_COMP_MEM)][i] = NULL;
+ continue;
+ }
+
+ /*
+ * basepa may not be on a alignment boundary, make it so.
+ */
+ if (sbdp_get_mem_alignment(hdp, dip, &endpa)) {
+ cmn_err(CE_WARN, "%s sbdp_get_mem_alignment fail", f);
+ continue;
+ }
+
+ basepa &= ~(endpa - 1);
+ endpa += basepa;
+
+ /*
+ * Check if base address is in phys_install.
+ */
+ memlist_read_lock();
+ for (ml = phys_install; ml; ml = ml->next)
+ if ((endpa <= ml->address) ||
+ (basepa >= (ml->address + ml->size)))
+ continue;
+ else
+ break;
+ memlist_read_unlock();
+
+ if (ml) {
+ SBD_DEV_SET_ATTACHED(sbp, SBD_COMP_MEM, i);
+ DEVSET_ADD(devs_attached, SBD_COMP_MEM, i);
+ PR_ALL("%s: board %d, mem-unit %d - attached\n",
+ f, sbp->sb_num, i);
+ }
+ sbd_init_mem_unit(sbp, i, ep);
+ }
+ sbd_release_sbdp_handle(hdp);
+
+ /*
+ * If so far we have found an error, we just log it but continue
+ */
+ if (SBD_GET_ERRNO(ep) != 0)
+ cmn_err(CE_WARN, "%s errno has occurred: errno %d", f,
+ SBD_GET_ERRNO(ep));
+
+ /*
+ * Check for i/o state.
+ */
+ for (i = 0; i < MAX_IO_UNITS_PER_BOARD; i++) {
+
+ if (!SBD_DEV_IS_PRESENT(sbp, SBD_COMP_IO, i))
+ continue;
+
+ dip = sbp->sb_devlist[NIX(SBD_COMP_IO)][i];
+ if (dip == NULL)
+ continue;
+
+ ASSERT(e_ddi_branch_held(dip));
+
+ /*
+ * XXX Is the devstate check needed ?
+ */
+ if (i_ddi_node_state(dip) >= DS_ATTACHED ||
+ ddi_get_devstate(dip) == DDI_DEVSTATE_UP) {
+
+ /*
+ * Found it!
+ */
+ SBD_DEV_SET_ATTACHED(sbp, SBD_COMP_IO, i);
+ DEVSET_ADD(devs_attached, SBD_COMP_IO, i);
+ PR_ALL("%s: board %d, io-unit %d - attached\n",
+ f, sbp->sb_num, i);
+ }
+ sbd_init_io_unit(sbp, i);
+ }
+
+ SBD_DEVS_CONFIGURE(sbp, devs_attached);
+ if (devs_attached && ((devs_lost = SBD_DEVS_UNATTACHED(sbp)) != 0)) {
+ int ut;
+ /*
+ * A prior comment stated that a partially configured
+ * board was not permitted. The Serengeti architecture
+ * makes this possible, so the SB_DEVS_DISCONNECT
+ * at the end of this block has been removed.
+ */
+
+ PR_ALL("%s: some devices not configured (0x%x)...\n",
+ f, devs_lost);
+
+ for (ut = 0; ut < MAX_CPU_UNITS_PER_BOARD; ut++)
+ if (DEVSET_IN_SET(devs_lost, SBD_COMP_CPU, ut)) {
+ SBD_DEVICE_TRANSITION(sbp, SBD_COMP_CPU,
+ ut, SBD_STATE_UNCONFIGURED);
+ }
+
+ for (ut = 0; ut < MAX_MEM_UNITS_PER_BOARD; ut++)
+ if (DEVSET_IN_SET(devs_lost, SBD_COMP_MEM, ut)) {
+ SBD_DEVICE_TRANSITION(sbp, SBD_COMP_MEM,
+ ut, SBD_STATE_UNCONFIGURED);
+ }
+
+ for (ut = 0; ut < MAX_IO_UNITS_PER_BOARD; ut++)
+ if (DEVSET_IN_SET(devs_lost, SBD_COMP_IO, ut)) {
+ SBD_DEVICE_TRANSITION(sbp, SBD_COMP_IO,
+ ut, SBD_STATE_UNCONFIGURED);
+ }
+ }
+}
+
+static int
+hold_rele_branch(dev_info_t *rdip, void *arg)
+{
+ walk_tree_t *wp = (walk_tree_t *)arg;
+
+ ASSERT(wp && (wp->hold == 0 || wp->hold == 1));
+
+ switch (get_node_type(wp->sbp, rdip, NULL)) {
+ case SBD_COMP_CMP:
+ case SBD_COMP_MEM:
+ case SBD_COMP_IO:
+ break;
+ case SBD_COMP_CPU:
+
+ /*
+ * All CPU nodes under CMP nodes should have
+ * gotten pruned when the CMP node was first
+ * encountered.
+ */
+ ASSERT(!sbd_is_cmp_child(rdip));
+
+ break;
+
+ case SBD_COMP_UNKNOWN:
+ /* Not of interest to us */
+ return (DDI_WALK_CONTINUE);
+ default:
+ ASSERT(0);
+ return (DDI_WALK_PRUNECHILD);
+ }
+
+ if (wp->hold) {
+ ASSERT(!e_ddi_branch_held(rdip));
+ e_ddi_branch_hold(rdip);
+ } else {
+ ASSERT(e_ddi_branch_held(rdip));
+ e_ddi_branch_rele(rdip);
+ }
+
+ return (DDI_WALK_PRUNECHILD);
+}
+
+static void
+sbd_board_init(sbd_board_t *sbp, sbd_softstate_t *softsp,
+ int bd, dev_info_t *top_dip, int wnode)
+{
+ int i;
+ dev_info_t *pdip;
+ int circ;
+ walk_tree_t walk = {0};
+
+ mutex_init(&sbp->sb_mutex, NULL, MUTEX_DRIVER, NULL);
+ mutex_init(&sbp->sb_flags_mutex, NULL, MUTEX_DRIVER, NULL);
+ mutex_init(&sbp->sb_slock, NULL, MUTEX_DRIVER, NULL);
+
+ sbp->sb_ref = 0;
+ sbp->sb_num = bd;
+ sbp->sb_time = gethrestime_sec();
+ /*
+ * For serengeti, top_dip doesn't need to be held because
+ * sbp i.e. sbd_board_t will be destroyed in sbd_teardown_instance()
+ * before top_dip detaches. For Daktari, top_dip is the
+ * root node which never has to be held.
+ */
+ sbp->sb_topdip = top_dip;
+ sbp->sb_cpuid = -1;
+ sbp->sb_softsp = (void *) softsp;
+ sbp->sb_cond = SBD_COND_UNKNOWN;
+ sbp->sb_wnode = wnode;
+ sbp->sb_memaccess_ok = 1;
+
+ ASSERT(MAX_IO_UNITS_PER_BOARD <= SBD_MAX_UNITS_PER_BOARD);
+ ASSERT(MAX_CPU_UNITS_PER_BOARD <= SBD_MAX_UNITS_PER_BOARD);
+ ASSERT(MAX_MEM_UNITS_PER_BOARD <= SBD_MAX_UNITS_PER_BOARD);
+
+ /*
+ * Allocate the devlist for cpus.
+ */
+ sbp->sb_devlist[NIX(SBD_COMP_CPU)] = GETSTRUCT(dev_info_t *,
+ MAX_CPU_UNITS_PER_BOARD);
+
+ /*
+ * Allocate the devlist for mem.
+ */
+ sbp->sb_devlist[NIX(SBD_COMP_MEM)] = GETSTRUCT(dev_info_t *,
+ MAX_MEM_UNITS_PER_BOARD);
+
+ /*
+ * Allocate the devlist for io.
+ */
+ sbp->sb_devlist[NIX(SBD_COMP_IO)] = GETSTRUCT(dev_info_t *,
+ MAX_IO_UNITS_PER_BOARD);
+
+
+ sbp->sb_dev[NIX(SBD_COMP_CPU)] = GETSTRUCT(sbd_dev_unit_t,
+ MAX_CPU_UNITS_PER_BOARD);
+
+ sbp->sb_dev[NIX(SBD_COMP_MEM)] = GETSTRUCT(sbd_dev_unit_t,
+ MAX_MEM_UNITS_PER_BOARD);
+
+ sbp->sb_dev[NIX(SBD_COMP_IO)] = GETSTRUCT(sbd_dev_unit_t,
+ MAX_IO_UNITS_PER_BOARD);
+
+ for (i = 0; i < MAX_CPU_UNITS_PER_BOARD; i++) {
+ sbp->sb_cpupath[i] = kmem_zalloc(MAXPATHLEN, KM_SLEEP);
+ }
+
+ for (i = 0; i < MAX_MEM_UNITS_PER_BOARD; i++) {
+ sbp->sb_mempath[i] = kmem_zalloc(MAXPATHLEN, KM_SLEEP);
+ }
+
+ for (i = 0; i < MAX_IO_UNITS_PER_BOARD; i++) {
+ sbp->sb_iopath[i] = kmem_zalloc(MAXPATHLEN, KM_SLEEP);
+ }
+
+ /*
+ * Walk the device tree, find all top dips on this board and
+ * hold the branches rooted at them
+ */
+ ASSERT(sbp->sb_topdip);
+ pdip = ddi_get_parent(sbp->sb_topdip);
+ if (pdip)
+ ndi_devi_enter(pdip, &circ);
+ walk.sbp = sbp;
+ walk.hold = 1;
+ ddi_walk_devs(sbp->sb_topdip, hold_rele_branch, (void *)&walk);
+ if (pdip)
+ ndi_devi_exit(pdip, circ);
+
+ /*
+ * Initialize the devlists
+ */
+ if (sbd_init_devlists(sbp) == 0) {
+ SBD_BOARD_TRANSITION(sbp, SBD_STATE_EMPTY);
+ } else {
+ /*
+ * Couldn't have made it down here without
+ * having found at least one device.
+ */
+ ASSERT(SBD_DEVS_PRESENT(sbp) != 0);
+ /*
+ * Check the state of any possible devices on the
+ * board.
+ */
+ sbd_board_discovery(sbp);
+
+ if (SBD_DEVS_UNATTACHED(sbp) == 0) {
+ /*
+ * The board has no unattached devices, therefore
+ * by reason of insanity it must be configured!
+ */
+ SBD_BOARD_TRANSITION(sbp, SBD_STATE_CONFIGURED);
+ sbp->sb_cond = SBD_COND_OK;
+ } else if (SBD_DEVS_ATTACHED(sbp)) {
+ SBD_BOARD_TRANSITION(sbp, SBD_STATE_PARTIAL);
+ } else {
+ SBD_BOARD_TRANSITION(sbp, SBD_STATE_CONNECTED);
+ }
+ }
+}
+
+static void
+sbd_board_destroy(sbd_board_t *sbp)
+{
+ int i;
+ dev_info_t *pdip;
+ int circ;
+ walk_tree_t walk = {0};
+
+ SBD_BOARD_TRANSITION(sbp, SBD_STATE_EMPTY);
+
+#ifdef DEBUG
+ for (i = 0; i < MAX_MEM_UNITS_PER_BOARD; i++) {
+ sbd_mem_unit_t *mp;
+
+ mp = SBD_GET_BOARD_MEMUNIT(sbp, i);
+ ASSERT(mp->sbm_mlist == NULL);
+ }
+#endif /* DEBUG */
+
+ /*
+ * Free up MEM unit structs.
+ */
+ FREESTRUCT(sbp->sb_dev[NIX(SBD_COMP_MEM)],
+ sbd_dev_unit_t, MAX_MEM_UNITS_PER_BOARD);
+ sbp->sb_dev[NIX(SBD_COMP_MEM)] = NULL;
+
+ /*
+ * Free up CPU unit structs.
+ */
+ FREESTRUCT(sbp->sb_dev[NIX(SBD_COMP_CPU)],
+ sbd_dev_unit_t, MAX_CPU_UNITS_PER_BOARD);
+ sbp->sb_dev[NIX(SBD_COMP_CPU)] = NULL;
+
+ /*
+ * Free up IO unit structs.
+ */
+ FREESTRUCT(sbp->sb_dev[NIX(SBD_COMP_IO)],
+ sbd_dev_unit_t, MAX_IO_UNITS_PER_BOARD);
+ sbp->sb_dev[NIX(SBD_COMP_IO)] = NULL;
+
+ /*
+ * free up CPU devlists.
+ */
+
+ for (i = 0; i < MAX_CPU_UNITS_PER_BOARD; i++) {
+ kmem_free((caddr_t)sbp->sb_cpupath[i], MAXPATHLEN);
+ }
+ FREESTRUCT(sbp->sb_devlist[NIX(SBD_COMP_CPU)], dev_info_t *,
+ MAX_CPU_UNITS_PER_BOARD);
+ sbp->sb_devlist[NIX(SBD_COMP_CPU)] = NULL;
+
+ /*
+ * free up MEM devlists.
+ */
+ for (i = 0; i < MAX_MEM_UNITS_PER_BOARD; i++) {
+ kmem_free((caddr_t)sbp->sb_mempath[i], MAXPATHLEN);
+ }
+ FREESTRUCT(sbp->sb_devlist[NIX(SBD_COMP_MEM)], dev_info_t *,
+ MAX_MEM_UNITS_PER_BOARD);
+ sbp->sb_devlist[NIX(SBD_COMP_MEM)] = NULL;
+
+ /*
+ * free up IO devlists.
+ */
+ for (i = 0; i < MAX_IO_UNITS_PER_BOARD; i++) {
+ kmem_free((caddr_t)sbp->sb_iopath[i], MAXPATHLEN);
+ }
+ FREESTRUCT(sbp->sb_devlist[NIX(SBD_COMP_IO)], dev_info_t *,
+ MAX_IO_UNITS_PER_BOARD);
+ sbp->sb_devlist[NIX(SBD_COMP_IO)] = NULL;
+
+ /*
+ * Release all branches held earlier
+ */
+ ASSERT(sbp->sb_topdip);
+ pdip = ddi_get_parent(sbp->sb_topdip);
+ if (pdip)
+ ndi_devi_enter(pdip, &circ);
+ walk.sbp = sbp;
+ walk.hold = 0;
+ ddi_walk_devs(sbp->sb_topdip, hold_rele_branch, (void *)&walk);
+ if (pdip)
+ ndi_devi_exit(pdip, circ);
+
+ mutex_destroy(&sbp->sb_slock);
+ mutex_destroy(&sbp->sb_flags_mutex);
+ mutex_destroy(&sbp->sb_mutex);
+}
+
+sbd_comp_type_t
+sbd_cm_type(char *name)
+{
+ sbd_comp_type_t type = SBD_COMP_UNKNOWN;
+ int i;
+
+ /* look up type in table */
+ for (i = 0; SBD_COMP(i) != SBD_COMP_UNKNOWN; i++) {
+ if (strcmp(name, SBD_OTYPE(i)) == 0) {
+ type = SBD_COMP(i);
+ break;
+ }
+ }
+
+ return (type);
+}
+
+/*
+ * There are certain cases where obp marks components as failed
+ * If the status is ok the node won't have any status property. It
+ * is only there if the status is other than ok.
+ *
+ * The translation is as follows:
+ * If there is no status prop, the the cond is SBD_COND_OK
+ * If we find a status prop but can't get to it then cond is SBD_COND_UNKNOWN
+ * if we find a stat and it is failed the cond is SBD_COND_FAILED
+ * If the stat is disabled, the cond is SBD_COND_UNUSABLE
+ * Otherwise we return con as SBD_COND_OK
+ */
+sbd_cond_t
+sbd_get_comp_cond(dev_info_t *dip)
+{
+ int len;
+ char *status_buf;
+ static const char *status = "status";
+ static const char *failed = "fail";
+ static const char *disabled = "disabled";
+
+ if (dip == NULL) {
+ PR_BYP("dip is NULL\n");
+ return (SBD_COND_UNKNOWN);
+ }
+
+ if (ddi_getproplen(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
+ (char *)status, &len) != DDI_PROP_SUCCESS) {
+ PR_CPU("status in sbd is ok\n");
+ return (SBD_COND_OK);
+ }
+
+ status_buf = kmem_zalloc(sizeof (char) * OBP_MAXPROPNAME, KM_SLEEP);
+ if (ddi_getlongprop_buf(DDI_DEV_T_NONE, dip, DDI_PROP_DONTPASS,
+ (char *)status, status_buf, &len) != DDI_PROP_SUCCESS) {
+ PR_CPU("status in sbd is unknown\n");
+ return (SBD_COND_UNKNOWN);
+ }
+
+ if (strncmp(status_buf, failed, strlen(failed)) == 0) {
+ PR_CPU("status in sbd is failed\n");
+ kmem_free(status_buf, sizeof (char) * OBP_MAXPROPNAME);
+ return (SBD_COND_FAILED);
+ }
+
+ if (strcmp(status_buf, disabled) == 0) {
+ PR_CPU("status in sbd is unusable\n");
+ kmem_free(status_buf, sizeof (char) * OBP_MAXPROPNAME);
+ return (SBD_COND_UNUSABLE);
+ }
+
+ kmem_free(status_buf, sizeof (char) * OBP_MAXPROPNAME);
+ return (SBD_COND_OK);
+}
+
+#ifdef SBD_DEBUG_ERRS
+
+/* function to simulate errors throughout the sbd code */
+void
+sbd_inject_err(int error, sbderror_t *ep, int Errno, int ecode,
+ char *rsc)
+{
+ static fn_t f = "sbd_inject_err";
+
+ if (sbd_err_debug == 0)
+ return;
+
+ if (ep == NULL) {
+ cmn_err(CE_WARN, "%s ep is NULL", f);
+ return;
+ }
+
+ if (SBD_GET_ERRNO(ep) != 0) {
+ cmn_err(CE_WARN, "%s errno already set to %d", f,
+ SBD_GET_ERRNO(ep));
+ return;
+ }
+
+ if (SBD_GET_ERR(ep) != 0) {
+ cmn_err(CE_WARN, "%s code already set to %d", f,
+ SBD_GET_ERR(ep));
+ return;
+ }
+
+ if ((sbd_err_debug & (1 << error)) != 0) {
+ ep->e_errno = Errno;
+ ep->e_code = ecode;
+
+ if (rsc != NULL)
+ bcopy((caddr_t)rsc,
+ (caddr_t)ep->e_rsc,
+ sizeof (ep->e_rsc));
+
+ if (Errno != 0)
+ PR_ERR_ERRNO("%s set errno to %d", f, ep->e_errno);
+
+ if (ecode != 0)
+ PR_ERR_ECODE("%s set ecode to %d", f, ep->e_code);
+
+ if (rsc != NULL)
+ PR_ERR_RSC("%s set rsc to %s", f, ep->e_rsc);
+ }
+}
+#endif