103831d35Sstevel /* 203831d35Sstevel * CDDL HEADER START 303831d35Sstevel * 403831d35Sstevel * The contents of this file are subject to the terms of the 503831d35Sstevel * Common Development and Distribution License (the "License"). 603831d35Sstevel * You may not use this file except in compliance with the License. 703831d35Sstevel * 803831d35Sstevel * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 903831d35Sstevel * or http://www.opensolaris.org/os/licensing. 1003831d35Sstevel * See the License for the specific language governing permissions 1103831d35Sstevel * and limitations under the License. 1203831d35Sstevel * 1303831d35Sstevel * When distributing Covered Code, include this CDDL HEADER in each 1403831d35Sstevel * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 1503831d35Sstevel * If applicable, add the following below this CDDL HEADER, with the 1603831d35Sstevel * fields enclosed by brackets "[]" replaced with your own identifying 1703831d35Sstevel * information: Portions Copyright [yyyy] [name of copyright owner] 1803831d35Sstevel * 1903831d35Sstevel * CDDL HEADER END 2003831d35Sstevel */ 2103831d35Sstevel 2203831d35Sstevel /* 23*07d06da5SSurya Prakki * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 2403831d35Sstevel * Use is subject to license terms. 2503831d35Sstevel */ 2603831d35Sstevel 2703831d35Sstevel #include <sys/types.h> 2803831d35Sstevel #include <sys/cmn_err.h> 2903831d35Sstevel #include <sys/conf.h> 3003831d35Sstevel #include <sys/autoconf.h> 3103831d35Sstevel #include <sys/systm.h> 3203831d35Sstevel #include <sys/modctl.h> 3303831d35Sstevel #include <sys/ddi.h> 3403831d35Sstevel #include <sys/sunddi.h> 3503831d35Sstevel #include <sys/sunndi.h> 3603831d35Sstevel #include <sys/ndi_impldefs.h> 3703831d35Sstevel #include <sys/ddi_impldefs.h> 3803831d35Sstevel #include <sys/promif.h> 3903831d35Sstevel #include <sys/stat.h> 4003831d35Sstevel #include <sys/kmem.h> 4103831d35Sstevel #include <sys/promif.h> 4203831d35Sstevel #include <sys/conf.h> 4303831d35Sstevel #include <sys/obpdefs.h> 4403831d35Sstevel #include <sys/sgsbbc_mailbox.h> 4503831d35Sstevel #include <sys/cpuvar.h> 4603831d35Sstevel #include <vm/seg_kmem.h> 4703831d35Sstevel #include <sys/prom_plat.h> 4803831d35Sstevel #include <sys/machsystm.h> 4903831d35Sstevel #include <sys/cheetahregs.h> 5003831d35Sstevel 5103831d35Sstevel #include <sys/sbd_ioctl.h> 5203831d35Sstevel #include <sys/sbd.h> 5303831d35Sstevel #include <sys/sbdp_priv.h> 5403831d35Sstevel 5503831d35Sstevel static int sbdp_detach_nodes(attach_pkt_t *); 5603831d35Sstevel static void 5703831d35Sstevel sbdp_walk_prom_tree_worker( 5803831d35Sstevel pnode_t node, 5903831d35Sstevel int(*f)(pnode_t, void *, uint_t), 6003831d35Sstevel void *arg) 6103831d35Sstevel { 6203831d35Sstevel /* 6303831d35Sstevel * Ignore return value from callback. Return value from callback 6403831d35Sstevel * does NOT indicate subsequent walk behavior. 6503831d35Sstevel */ 6603831d35Sstevel (void) (*f)(node, arg, 0); 6703831d35Sstevel 6803831d35Sstevel if (node != OBP_NONODE) { 6903831d35Sstevel sbdp_walk_prom_tree_worker(prom_childnode(node), f, arg); 7003831d35Sstevel sbdp_walk_prom_tree_worker(prom_nextnode(node), f, arg); 7103831d35Sstevel } 7203831d35Sstevel } 7303831d35Sstevel 7403831d35Sstevel struct sbdp_walk_prom_tree_args { 7503831d35Sstevel pnode_t node; 7603831d35Sstevel int (*f)(pnode_t, void *, uint_t); 7703831d35Sstevel void *arg; 7803831d35Sstevel }; 7903831d35Sstevel 8003831d35Sstevel /*ARGSUSED*/ 8103831d35Sstevel static int 8203831d35Sstevel sbdp_walk_prom_tree_start(void *arg, int has_changed) 8303831d35Sstevel { 8403831d35Sstevel struct sbdp_walk_prom_tree_args *argbp = arg; 8503831d35Sstevel 8603831d35Sstevel sbdp_walk_prom_tree_worker(argbp->node, argbp->f, argbp->arg); 8703831d35Sstevel return (0); 8803831d35Sstevel } 8903831d35Sstevel 9003831d35Sstevel void 9103831d35Sstevel sbdp_walk_prom_tree(pnode_t node, int(*f)(pnode_t, void *, uint_t), void *arg) 9203831d35Sstevel { 9303831d35Sstevel struct sbdp_walk_prom_tree_args arg_block; 9403831d35Sstevel 9503831d35Sstevel arg_block.node = node; 9603831d35Sstevel arg_block.f = f; 9703831d35Sstevel arg_block.arg = arg; 98*07d06da5SSurya Prakki (void) prom_tree_access(sbdp_walk_prom_tree_start, &arg_block, NULL); 9903831d35Sstevel } 10003831d35Sstevel 10103831d35Sstevel static void 10203831d35Sstevel sbdp_attach_branch(dev_info_t *pdip, pnode_t node, void *arg) 10303831d35Sstevel { 10403831d35Sstevel attach_pkt_t *apktp = (attach_pkt_t *)arg; 10503831d35Sstevel pnode_t child; 10603831d35Sstevel dev_info_t *dip = NULL; 10703831d35Sstevel static int err = 0; 10803831d35Sstevel static int len = 0; 10903831d35Sstevel char name[OBP_MAXDRVNAME]; 11003831d35Sstevel #if OBP_MAXDRVNAME == OBP_MAXPROPNAME 11103831d35Sstevel #define buf name 11203831d35Sstevel #else 11303831d35Sstevel char buf[OBP_MAXPROPNAME]; 11403831d35Sstevel #endif 11503831d35Sstevel static fn_t f = "sbdp_attach_branch"; 11603831d35Sstevel 11703831d35Sstevel SBDP_DBG_FUNC("%s\n", f); 11803831d35Sstevel 11903831d35Sstevel if (node == OBP_NONODE) 12003831d35Sstevel return; 12103831d35Sstevel 12203831d35Sstevel /* 12303831d35Sstevel * Get the status for this node 12403831d35Sstevel * If it has failed we imitate boot by not creating a node 12503831d35Sstevel * in solaris. We just warn the user 12603831d35Sstevel */ 12703831d35Sstevel if (check_status(node, buf, pdip) != DDI_SUCCESS) { 12803831d35Sstevel SBDP_DBG_STATE("status failed skipping this node\n"); 12903831d35Sstevel return; 13003831d35Sstevel } 13103831d35Sstevel 13203831d35Sstevel len = prom_getproplen(node, OBP_REG); 13303831d35Sstevel if (len <= 0) { 13403831d35Sstevel return; 13503831d35Sstevel } 13603831d35Sstevel 13703831d35Sstevel (void) prom_getprop(node, OBP_NAME, (caddr_t)name); 13803831d35Sstevel err = ndi_devi_alloc(pdip, name, node, &dip); 13903831d35Sstevel if (err != NDI_SUCCESS) { 14003831d35Sstevel return; 14103831d35Sstevel } 14203831d35Sstevel SBDP_DBG_STATE("attaching %s\n", name); 14303831d35Sstevel err = ndi_devi_online(dip, NDI_DEVI_BIND); 14403831d35Sstevel if (err != NDI_SUCCESS) { 145*07d06da5SSurya Prakki (void) ndi_devi_free(dip); 14603831d35Sstevel return; 14703831d35Sstevel } 14803831d35Sstevel child = prom_childnode(node); 14903831d35Sstevel if (child != OBP_NONODE) { 15003831d35Sstevel for (; child != OBP_NONODE; 15103831d35Sstevel child = prom_nextnode(child)) { 15203831d35Sstevel sbdp_attach_branch(dip, child, (void *)apktp); 15303831d35Sstevel } 15403831d35Sstevel } 15503831d35Sstevel #undef buf 15603831d35Sstevel } 15703831d35Sstevel 15803831d35Sstevel static int 15903831d35Sstevel sbdp_find_ssm_dip(dev_info_t *dip, void *arg) 16003831d35Sstevel { 16103831d35Sstevel attach_pkt_t *apktp; 16203831d35Sstevel int node; 16303831d35Sstevel static fn_t f = "sbdp_find_ssm_dip"; 16403831d35Sstevel 16503831d35Sstevel SBDP_DBG_FUNC("%s\n", f); 16603831d35Sstevel 16703831d35Sstevel apktp = (attach_pkt_t *)arg; 16803831d35Sstevel 16903831d35Sstevel if (apktp == NULL) { 17003831d35Sstevel SBDP_DBG_STATE("error on the argument\n"); 17103831d35Sstevel return (DDI_WALK_CONTINUE); 17203831d35Sstevel } 17303831d35Sstevel 17403831d35Sstevel if ((node = ddi_getprop(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, 17503831d35Sstevel "nodeid", -1)) == -1) 17603831d35Sstevel return (DDI_WALK_CONTINUE); 17703831d35Sstevel 17803831d35Sstevel if (node == apktp->node) { 17903831d35Sstevel ndi_hold_devi(dip); 18003831d35Sstevel apktp->top_node = dip; 18103831d35Sstevel return (DDI_WALK_TERMINATE); 18203831d35Sstevel } 18303831d35Sstevel return (DDI_WALK_CONTINUE); 18403831d35Sstevel } 18503831d35Sstevel 18603831d35Sstevel /*ARGSUSED*/ 18703831d35Sstevel int 18803831d35Sstevel sbdp_select_top_nodes(pnode_t node, void *arg, uint_t flags) 18903831d35Sstevel { 19003831d35Sstevel int board, bd; 19103831d35Sstevel attach_pkt_t *apktp = (attach_pkt_t *)arg; 19203831d35Sstevel char devtype[OBP_MAXDRVNAME]; 19303831d35Sstevel char devname[OBP_MAXDRVNAME]; 19403831d35Sstevel int i; 19503831d35Sstevel sbd_devattr_t *sbdp_top_nodes; 19603831d35Sstevel int wnode; 19703831d35Sstevel static fn_t f = "sbdp_select_top_nodes"; 19803831d35Sstevel 19903831d35Sstevel SBDP_DBG_FUNC("%s\n", f); 20003831d35Sstevel 20103831d35Sstevel if (apktp == NULL) { 20203831d35Sstevel SBDP_DBG_STATE("error on the argument\n"); 20303831d35Sstevel return (DDI_FAILURE); 20403831d35Sstevel } 20503831d35Sstevel 20603831d35Sstevel board = apktp->board; 20703831d35Sstevel sbdp_top_nodes = sbdp_get_devattr(); 20803831d35Sstevel 20903831d35Sstevel if (sbdp_get_bd_and_wnode_num(node, &bd, &wnode) < 0) 21003831d35Sstevel return (DDI_FAILURE); 21103831d35Sstevel 21203831d35Sstevel if (bd != board) 21303831d35Sstevel return (DDI_FAILURE); 21403831d35Sstevel 21503831d35Sstevel SBDP_DBG_MISC("%s: board is %d\n", f, bd); 21603831d35Sstevel 21703831d35Sstevel (void) prom_getprop(node, OBP_DEVICETYPE, (caddr_t)devtype); 21803831d35Sstevel (void) prom_getprop(node, OBP_NAME, (caddr_t)devname); 21903831d35Sstevel 22003831d35Sstevel if (strcmp(devname, "cmp") == 0) { 22103831d35Sstevel apktp->nodes[apktp->num_of_nodes] = node; 22203831d35Sstevel apktp->num_of_nodes++; 22303831d35Sstevel 22403831d35Sstevel /* We want this node */ 22503831d35Sstevel return (DDI_SUCCESS); 22603831d35Sstevel } 22703831d35Sstevel 22803831d35Sstevel for (i = 0; sbdp_top_nodes[i].s_obp_type != NULL; i++) { 22903831d35Sstevel if (strcmp(devtype, sbdp_top_nodes[i].s_obp_type) == 0) { 23003831d35Sstevel if (strcmp(devtype, "cpu") == 0) { 23103831d35Sstevel int cpuid; 23203831d35Sstevel int impl; 23303831d35Sstevel 23403831d35Sstevel /* 23503831d35Sstevel * Check the status of the cpu 23603831d35Sstevel * If it is failed ignore it 23703831d35Sstevel */ 23803831d35Sstevel if (sbdp_get_comp_status(node) != SBD_COND_OK) 23903831d35Sstevel return (DDI_FAILURE); 24003831d35Sstevel 24103831d35Sstevel if (prom_getprop(node, "cpuid", 24203831d35Sstevel (caddr_t)&cpuid) == -1) { 24303831d35Sstevel 24403831d35Sstevel if (prom_getprop(node, "portid", 24503831d35Sstevel (caddr_t)&cpuid) == -1) { 24603831d35Sstevel 24703831d35Sstevel return (DDI_WALK_TERMINATE); 24803831d35Sstevel } 24903831d35Sstevel } 25003831d35Sstevel 25103831d35Sstevel if (sbdp_set_cpu_present(wnode, bd, 25203831d35Sstevel SG_CPUID_TO_CPU_UNIT(cpuid)) == -1) 25303831d35Sstevel return (DDI_WALK_TERMINATE); 25403831d35Sstevel 25503831d35Sstevel (void) prom_getprop(node, "implementation#", 25603831d35Sstevel (caddr_t)&impl); 25703831d35Sstevel /* 25803831d35Sstevel * If it is a CPU under CMP, don't save 25903831d35Sstevel * the node as we will be saving the CMP 26003831d35Sstevel * node. 26103831d35Sstevel */ 26203831d35Sstevel if (CPU_IMPL_IS_CMP(impl)) 26303831d35Sstevel return (DDI_FAILURE); 26403831d35Sstevel } 26503831d35Sstevel 26603831d35Sstevel /* 26703831d35Sstevel * Check to make sure we haven't run out of bounds 26803831d35Sstevel */ 26903831d35Sstevel if (apktp->num_of_nodes >= SBDP_MAX_NODES) 27003831d35Sstevel return (DDI_FAILURE); 27103831d35Sstevel 27203831d35Sstevel /* Save node */ 27303831d35Sstevel apktp->nodes[apktp->num_of_nodes] = node; 27403831d35Sstevel apktp->num_of_nodes++; 27503831d35Sstevel 27603831d35Sstevel /* We want this node */ 27703831d35Sstevel return (DDI_SUCCESS); 27803831d35Sstevel } 27903831d35Sstevel } 28003831d35Sstevel 28103831d35Sstevel return (DDI_FAILURE); 28203831d35Sstevel } 28303831d35Sstevel 28403831d35Sstevel void 28503831d35Sstevel sbdp_attach_bd(int node, int board) 28603831d35Sstevel { 28703831d35Sstevel devi_branch_t b = {0}; 28803831d35Sstevel attach_pkt_t apkt, *apktp = &apkt; 28903831d35Sstevel static fn_t f = "sbdp_attach_bd"; 29003831d35Sstevel 29103831d35Sstevel SBDP_DBG_FUNC("%s\n", f); 29203831d35Sstevel 29303831d35Sstevel apktp->node = node; 29403831d35Sstevel apktp->board = board; 29503831d35Sstevel apktp->num_of_nodes = 0; 29603831d35Sstevel apktp->flags = 0; 29703831d35Sstevel 29803831d35Sstevel apktp->top_node = NULL; 29903831d35Sstevel 30003831d35Sstevel /* 30103831d35Sstevel * Root node doesn't have to be held for ddi_walk_devs() 30203831d35Sstevel */ 30303831d35Sstevel ddi_walk_devs(ddi_root_node(), sbdp_find_ssm_dip, (void *) apktp); 30403831d35Sstevel 30503831d35Sstevel if (apktp->top_node == NULL) { 30603831d35Sstevel SBDP_DBG_STATE("BAD Serengeti\n"); 30703831d35Sstevel return; 30803831d35Sstevel } 30903831d35Sstevel 31003831d35Sstevel b.arg = (void *)apktp; 31103831d35Sstevel b.type = DEVI_BRANCH_PROM; 31203831d35Sstevel b.create.prom_branch_select = sbdp_select_top_nodes; 31303831d35Sstevel b.devi_branch_callback = NULL; 31403831d35Sstevel 31503831d35Sstevel (void) e_ddi_branch_create(apktp->top_node, &b, NULL, 0); 31603831d35Sstevel 31703831d35Sstevel /* 31803831d35Sstevel * Release hold acquired in sbdp_find_ssm_dip() 31903831d35Sstevel */ 32003831d35Sstevel ndi_rele_devi(apktp->top_node); 32103831d35Sstevel 32203831d35Sstevel sbdp_cpu_in_reset(node, board, SBDP_ALL_CPUS, 1); 32303831d35Sstevel } 32403831d35Sstevel 32503831d35Sstevel int 32603831d35Sstevel sbdp_detach_bd(int node, int board, sbd_error_t *sep) 32703831d35Sstevel { 32803831d35Sstevel int rv; 32903831d35Sstevel attach_pkt_t apkt, *apktp = &apkt; 33003831d35Sstevel static fn_t f = "sbdp_detach_bd"; 33103831d35Sstevel 33203831d35Sstevel SBDP_DBG_FUNC("%s\n", f); 33303831d35Sstevel 33403831d35Sstevel apktp->node = node; 33503831d35Sstevel apktp->board = board; 33603831d35Sstevel apktp->num_of_nodes = 0; 33703831d35Sstevel apktp->error = 0; 33803831d35Sstevel apktp->errstr = NULL; 33903831d35Sstevel sbdp_walk_prom_tree(prom_rootnode(), sbdp_select_top_nodes, 34003831d35Sstevel (void *) apktp); 34103831d35Sstevel 34203831d35Sstevel if (rv = sbdp_detach_nodes(apktp)) { 34303831d35Sstevel sbdp_set_err(sep, ESBD_IO, NULL); 34403831d35Sstevel return (rv); 34503831d35Sstevel } 34603831d35Sstevel 34703831d35Sstevel sbdp_cpu_in_reset(node, board, SBDP_ALL_CPUS, 1); 34803831d35Sstevel /* 34903831d35Sstevel * Clean up this board struct 35003831d35Sstevel */ 35103831d35Sstevel sbdp_cleanup_bd(node, board); 35203831d35Sstevel 35303831d35Sstevel return (0); 35403831d35Sstevel } 35503831d35Sstevel 35603831d35Sstevel static int 35703831d35Sstevel sbdp_detach_nodes(attach_pkt_t *apktp) 35803831d35Sstevel { 35903831d35Sstevel dev_info_t **dip; 36003831d35Sstevel dev_info_t **dev_list; 36103831d35Sstevel int dev_list_len = 0; 36203831d35Sstevel int i, rv = 0; 36303831d35Sstevel 36403831d35Sstevel dev_list = kmem_zalloc(sizeof (dev_info_t *) * SBDP_MAX_NODES, 36503831d35Sstevel KM_SLEEP); 36603831d35Sstevel 36703831d35Sstevel for (i = 0, dip = dev_list; i < apktp->num_of_nodes; i++) { 36803831d35Sstevel *dip = e_ddi_nodeid_to_dip(apktp->nodes[i]); 36903831d35Sstevel if (*dip != NULL) { 37003831d35Sstevel /* 37103831d35Sstevel * The branch rooted at dip should already be held, 37203831d35Sstevel * so release hold acquired in e_ddi_nodeid_to_dip() 37303831d35Sstevel */ 37403831d35Sstevel ddi_release_devi(*dip); 37503831d35Sstevel dip++; 37603831d35Sstevel ++dev_list_len; 37703831d35Sstevel } 37803831d35Sstevel } 37903831d35Sstevel 38003831d35Sstevel for (i = dev_list_len, dip = &dev_list[i - 1]; i > 0; i--, dip--) { 38103831d35Sstevel dev_info_t *fdip = NULL; 38203831d35Sstevel 38303831d35Sstevel ASSERT(e_ddi_branch_held(*dip)); 38403831d35Sstevel rv = e_ddi_branch_destroy(*dip, &fdip, 0); 38503831d35Sstevel if (rv) { 38603831d35Sstevel char *path = kmem_alloc(MAXPATHLEN, KM_SLEEP); 38703831d35Sstevel 38803831d35Sstevel /* 38903831d35Sstevel * If non-NULL, fdip is held and must be released. 39003831d35Sstevel */ 39103831d35Sstevel if (fdip != NULL) { 39203831d35Sstevel (void) ddi_pathname(fdip, path); 39303831d35Sstevel ddi_release_devi(fdip); 39403831d35Sstevel } else { 39503831d35Sstevel (void) ddi_pathname(*dip, path); 39603831d35Sstevel } 39703831d35Sstevel 39803831d35Sstevel cmn_err(CE_WARN, "failed to remove node %s (%p): %d", 39903831d35Sstevel path, fdip ? (void *)fdip : (void *)*dip, rv); 40003831d35Sstevel 40103831d35Sstevel kmem_free(path, MAXPATHLEN); 40203831d35Sstevel 40303831d35Sstevel apktp->error = apktp->error ? apktp->error : rv; 40403831d35Sstevel break; 40503831d35Sstevel } 40603831d35Sstevel } 40703831d35Sstevel 40803831d35Sstevel kmem_free(dev_list, sizeof (dev_info_t *) * SBDP_MAX_NODES); 40903831d35Sstevel 41003831d35Sstevel return (rv); 41103831d35Sstevel } 412