/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (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 2010 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* * safari system board DR module. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* start sbd includes */ #include #include #include #include #include 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" }; 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=%lx\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); (void) 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)(uintptr_t)scmd32.cmd_cm.c_opts; if (cmd == SBD_CMD_PASSTHRU) { PR_BYP("passthru copyin: iap=%p, sz=%ld", (void *)iap, sizeof (sbd_cmd32_t)); PR_BYP("passthru copyin: c_opts=%x, 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)(uintptr_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->ml_next) if ((endpa <= ml->ml_address) || (basepa >= (ml->ml_address + ml->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->ml_next) if ((endpa <= ml->ml_address) || (basepa >= (ml->ml_address + ml->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_devi_attached(dip) || 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 retired, return FAILED */ if (DEVI(dip)->devi_flags & DEVI_RETIRED) { PR_CPU("dip is retired\n"); return (SBD_COND_FAILED); } 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_ANY, 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