11708Sstevel /*
21708Sstevel * CDDL HEADER START
31708Sstevel *
41708Sstevel * The contents of this file are subject to the terms of the
51708Sstevel * Common Development and Distribution License (the "License").
61708Sstevel * You may not use this file except in compliance with the License.
71708Sstevel *
81708Sstevel * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
91708Sstevel * or http://www.opensolaris.org/os/licensing.
101708Sstevel * See the License for the specific language governing permissions
111708Sstevel * and limitations under the License.
121708Sstevel *
131708Sstevel * When distributing Covered Code, include this CDDL HEADER in each
141708Sstevel * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
151708Sstevel * If applicable, add the following below this CDDL HEADER, with the
161708Sstevel * fields enclosed by brackets "[]" replaced with your own identifying
171708Sstevel * information: Portions Copyright [yyyy] [name of copyright owner]
181708Sstevel *
191708Sstevel * CDDL HEADER END
201708Sstevel */
211708Sstevel
221708Sstevel /*
23*11311SSurya.Prakki@Sun.COM * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
241708Sstevel * Use is subject to license terms.
251708Sstevel */
261708Sstevel
271708Sstevel #include <sys/types.h>
281708Sstevel #include <sys/cmn_err.h>
291708Sstevel #include <sys/conf.h>
301708Sstevel #include <sys/autoconf.h>
311708Sstevel #include <sys/systm.h>
321708Sstevel #include <sys/modctl.h>
331708Sstevel #include <sys/ddi.h>
341708Sstevel #include <sys/sunddi.h>
351708Sstevel #include <sys/sunndi.h>
361708Sstevel #include <sys/ndi_impldefs.h>
371708Sstevel #include <sys/ddi_impldefs.h>
381708Sstevel #include <sys/promif.h>
391708Sstevel #include <sys/stat.h>
401708Sstevel #include <sys/kmem.h>
411708Sstevel #include <sys/promif.h>
421708Sstevel #include <sys/conf.h>
431708Sstevel #include <sys/obpdefs.h>
441708Sstevel #include <sys/sgsbbc_mailbox.h>
451708Sstevel #include <sys/cpuvar.h>
461708Sstevel #include <vm/seg_kmem.h>
471708Sstevel #include <sys/prom_plat.h>
481708Sstevel #include <sys/machsystm.h>
491708Sstevel #include <sys/cheetahregs.h>
501708Sstevel
511708Sstevel #include <sys/sbd_ioctl.h>
521708Sstevel #include <sys/sbd.h>
531708Sstevel #include <sys/sbdp_priv.h>
541708Sstevel
551708Sstevel static int sbdp_detach_nodes(attach_pkt_t *);
561708Sstevel static void
sbdp_walk_prom_tree_worker(pnode_t node,int (* f)(pnode_t,void *,uint_t),void * arg)571708Sstevel sbdp_walk_prom_tree_worker(
581708Sstevel pnode_t node,
591708Sstevel int(*f)(pnode_t, void *, uint_t),
601708Sstevel void *arg)
611708Sstevel {
621708Sstevel /*
631708Sstevel * Ignore return value from callback. Return value from callback
641708Sstevel * does NOT indicate subsequent walk behavior.
651708Sstevel */
661708Sstevel (void) (*f)(node, arg, 0);
671708Sstevel
681708Sstevel if (node != OBP_NONODE) {
691708Sstevel sbdp_walk_prom_tree_worker(prom_childnode(node), f, arg);
701708Sstevel sbdp_walk_prom_tree_worker(prom_nextnode(node), f, arg);
711708Sstevel }
721708Sstevel }
731708Sstevel
741708Sstevel struct sbdp_walk_prom_tree_args {
751708Sstevel pnode_t node;
761708Sstevel int (*f)(pnode_t, void *, uint_t);
771708Sstevel void *arg;
781708Sstevel };
791708Sstevel
801708Sstevel /*ARGSUSED*/
811708Sstevel static int
sbdp_walk_prom_tree_start(void * arg,int has_changed)821708Sstevel sbdp_walk_prom_tree_start(void *arg, int has_changed)
831708Sstevel {
841708Sstevel struct sbdp_walk_prom_tree_args *argbp = arg;
851708Sstevel
861708Sstevel sbdp_walk_prom_tree_worker(argbp->node, argbp->f, argbp->arg);
871708Sstevel return (0);
881708Sstevel }
891708Sstevel
901708Sstevel void
sbdp_walk_prom_tree(pnode_t node,int (* f)(pnode_t,void *,uint_t),void * arg)911708Sstevel sbdp_walk_prom_tree(pnode_t node, int(*f)(pnode_t, void *, uint_t), void *arg)
921708Sstevel {
931708Sstevel struct sbdp_walk_prom_tree_args arg_block;
941708Sstevel
951708Sstevel arg_block.node = node;
961708Sstevel arg_block.f = f;
971708Sstevel arg_block.arg = arg;
98*11311SSurya.Prakki@Sun.COM (void) prom_tree_access(sbdp_walk_prom_tree_start, &arg_block, NULL);
991708Sstevel }
1001708Sstevel
1011708Sstevel static void
sbdp_attach_branch(dev_info_t * pdip,pnode_t node,void * arg)1021708Sstevel sbdp_attach_branch(dev_info_t *pdip, pnode_t node, void *arg)
1031708Sstevel {
1041708Sstevel attach_pkt_t *apktp = (attach_pkt_t *)arg;
1051708Sstevel pnode_t child;
1061708Sstevel dev_info_t *dip = NULL;
1071708Sstevel static int err = 0;
1081708Sstevel static int len = 0;
1091708Sstevel char name[OBP_MAXDRVNAME];
1101708Sstevel #if OBP_MAXDRVNAME == OBP_MAXPROPNAME
1111708Sstevel #define buf name
1121708Sstevel #else
1131708Sstevel char buf[OBP_MAXPROPNAME];
1141708Sstevel #endif
1151708Sstevel static fn_t f = "sbdp_attach_branch";
1161708Sstevel
1171708Sstevel SBDP_DBG_FUNC("%s\n", f);
1181708Sstevel
1191708Sstevel if (node == OBP_NONODE)
1201708Sstevel return;
1211708Sstevel
1221708Sstevel /*
1231708Sstevel * Get the status for this node
1241708Sstevel * If it has failed we imitate boot by not creating a node
1251708Sstevel * in solaris. We just warn the user
1261708Sstevel */
1271708Sstevel if (check_status(node, buf, pdip) != DDI_SUCCESS) {
1281708Sstevel SBDP_DBG_STATE("status failed skipping this node\n");
1291708Sstevel return;
1301708Sstevel }
1311708Sstevel
1321708Sstevel len = prom_getproplen(node, OBP_REG);
1331708Sstevel if (len <= 0) {
1341708Sstevel return;
1351708Sstevel }
1361708Sstevel
1371708Sstevel (void) prom_getprop(node, OBP_NAME, (caddr_t)name);
1381708Sstevel err = ndi_devi_alloc(pdip, name, node, &dip);
1391708Sstevel if (err != NDI_SUCCESS) {
1401708Sstevel return;
1411708Sstevel }
1421708Sstevel SBDP_DBG_STATE("attaching %s\n", name);
1431708Sstevel err = ndi_devi_online(dip, NDI_DEVI_BIND);
1441708Sstevel if (err != NDI_SUCCESS) {
145*11311SSurya.Prakki@Sun.COM (void) ndi_devi_free(dip);
1461708Sstevel return;
1471708Sstevel }
1481708Sstevel child = prom_childnode(node);
1491708Sstevel if (child != OBP_NONODE) {
1501708Sstevel for (; child != OBP_NONODE;
1511708Sstevel child = prom_nextnode(child)) {
1521708Sstevel sbdp_attach_branch(dip, child, (void *)apktp);
1531708Sstevel }
1541708Sstevel }
1551708Sstevel #undef buf
1561708Sstevel }
1571708Sstevel
1581708Sstevel static int
sbdp_find_ssm_dip(dev_info_t * dip,void * arg)1591708Sstevel sbdp_find_ssm_dip(dev_info_t *dip, void *arg)
1601708Sstevel {
1611708Sstevel attach_pkt_t *apktp;
1621708Sstevel int node;
1631708Sstevel static fn_t f = "sbdp_find_ssm_dip";
1641708Sstevel
1651708Sstevel SBDP_DBG_FUNC("%s\n", f);
1661708Sstevel
1671708Sstevel apktp = (attach_pkt_t *)arg;
1681708Sstevel
1691708Sstevel if (apktp == NULL) {
1701708Sstevel SBDP_DBG_STATE("error on the argument\n");
1711708Sstevel return (DDI_WALK_CONTINUE);
1721708Sstevel }
1731708Sstevel
1741708Sstevel if ((node = ddi_getprop(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
1751708Sstevel "nodeid", -1)) == -1)
1761708Sstevel return (DDI_WALK_CONTINUE);
1771708Sstevel
1781708Sstevel if (node == apktp->node) {
1791708Sstevel ndi_hold_devi(dip);
1801708Sstevel apktp->top_node = dip;
1811708Sstevel return (DDI_WALK_TERMINATE);
1821708Sstevel }
1831708Sstevel return (DDI_WALK_CONTINUE);
1841708Sstevel }
1851708Sstevel
1861708Sstevel /*ARGSUSED*/
1871708Sstevel int
sbdp_select_top_nodes(pnode_t node,void * arg,uint_t flags)1881708Sstevel sbdp_select_top_nodes(pnode_t node, void *arg, uint_t flags)
1891708Sstevel {
1901708Sstevel int board, bd;
1911708Sstevel attach_pkt_t *apktp = (attach_pkt_t *)arg;
1921708Sstevel char devtype[OBP_MAXDRVNAME];
1931708Sstevel char devname[OBP_MAXDRVNAME];
1941708Sstevel int i;
1951708Sstevel sbd_devattr_t *sbdp_top_nodes;
1961708Sstevel int wnode;
1971708Sstevel static fn_t f = "sbdp_select_top_nodes";
1981708Sstevel
1991708Sstevel SBDP_DBG_FUNC("%s\n", f);
2001708Sstevel
2011708Sstevel if (apktp == NULL) {
2021708Sstevel SBDP_DBG_STATE("error on the argument\n");
2031708Sstevel return (DDI_FAILURE);
2041708Sstevel }
2051708Sstevel
2061708Sstevel board = apktp->board;
2071708Sstevel sbdp_top_nodes = sbdp_get_devattr();
2081708Sstevel
2091708Sstevel if (sbdp_get_bd_and_wnode_num(node, &bd, &wnode) < 0)
2101708Sstevel return (DDI_FAILURE);
2111708Sstevel
2121708Sstevel if (bd != board)
2131708Sstevel return (DDI_FAILURE);
2141708Sstevel
2151708Sstevel SBDP_DBG_MISC("%s: board is %d\n", f, bd);
2161708Sstevel
2171708Sstevel (void) prom_getprop(node, OBP_DEVICETYPE, (caddr_t)devtype);
2181708Sstevel (void) prom_getprop(node, OBP_NAME, (caddr_t)devname);
2191708Sstevel
2201708Sstevel if (strcmp(devname, "cmp") == 0) {
2211708Sstevel apktp->nodes[apktp->num_of_nodes] = node;
2221708Sstevel apktp->num_of_nodes++;
2231708Sstevel
2241708Sstevel /* We want this node */
2251708Sstevel return (DDI_SUCCESS);
2261708Sstevel }
2271708Sstevel
2281708Sstevel for (i = 0; sbdp_top_nodes[i].s_obp_type != NULL; i++) {
2291708Sstevel if (strcmp(devtype, sbdp_top_nodes[i].s_obp_type) == 0) {
2301708Sstevel if (strcmp(devtype, "cpu") == 0) {
2311708Sstevel int cpuid;
2321708Sstevel int impl;
2331708Sstevel
2341708Sstevel /*
2351708Sstevel * Check the status of the cpu
2361708Sstevel * If it is failed ignore it
2371708Sstevel */
2381708Sstevel if (sbdp_get_comp_status(node) != SBD_COND_OK)
2391708Sstevel return (DDI_FAILURE);
2401708Sstevel
2411708Sstevel if (prom_getprop(node, "cpuid",
2421708Sstevel (caddr_t)&cpuid) == -1) {
2431708Sstevel
2441708Sstevel if (prom_getprop(node, "portid",
2451708Sstevel (caddr_t)&cpuid) == -1) {
2461708Sstevel
2471708Sstevel return (DDI_WALK_TERMINATE);
2481708Sstevel }
2491708Sstevel }
2501708Sstevel
2511708Sstevel if (sbdp_set_cpu_present(wnode, bd,
2521708Sstevel SG_CPUID_TO_CPU_UNIT(cpuid)) == -1)
2531708Sstevel return (DDI_WALK_TERMINATE);
2541708Sstevel
2551708Sstevel (void) prom_getprop(node, "implementation#",
256*11311SSurya.Prakki@Sun.COM (caddr_t)&impl);
2571708Sstevel /*
2581708Sstevel * If it is a CPU under CMP, don't save
2591708Sstevel * the node as we will be saving the CMP
2601708Sstevel * node.
2611708Sstevel */
2621708Sstevel if (CPU_IMPL_IS_CMP(impl))
2631708Sstevel return (DDI_FAILURE);
2641708Sstevel }
2651708Sstevel
2661708Sstevel /*
2671708Sstevel * Check to make sure we haven't run out of bounds
2681708Sstevel */
2691708Sstevel if (apktp->num_of_nodes >= SBDP_MAX_NODES)
2701708Sstevel return (DDI_FAILURE);
2711708Sstevel
2721708Sstevel /* Save node */
2731708Sstevel apktp->nodes[apktp->num_of_nodes] = node;
2741708Sstevel apktp->num_of_nodes++;
2751708Sstevel
2761708Sstevel /* We want this node */
2771708Sstevel return (DDI_SUCCESS);
2781708Sstevel }
2791708Sstevel }
2801708Sstevel
2811708Sstevel return (DDI_FAILURE);
2821708Sstevel }
2831708Sstevel
2841708Sstevel void
sbdp_attach_bd(int node,int board)2851708Sstevel sbdp_attach_bd(int node, int board)
2861708Sstevel {
2871708Sstevel devi_branch_t b = {0};
2881708Sstevel attach_pkt_t apkt, *apktp = &apkt;
2891708Sstevel static fn_t f = "sbdp_attach_bd";
2901708Sstevel
2911708Sstevel SBDP_DBG_FUNC("%s\n", f);
2921708Sstevel
2931708Sstevel apktp->node = node;
2941708Sstevel apktp->board = board;
2951708Sstevel apktp->num_of_nodes = 0;
2961708Sstevel apktp->flags = 0;
2971708Sstevel
2981708Sstevel apktp->top_node = NULL;
2991708Sstevel
3001708Sstevel /*
3011708Sstevel * Root node doesn't have to be held for ddi_walk_devs()
3021708Sstevel */
3031708Sstevel ddi_walk_devs(ddi_root_node(), sbdp_find_ssm_dip, (void *) apktp);
3041708Sstevel
3051708Sstevel if (apktp->top_node == NULL) {
3061708Sstevel SBDP_DBG_STATE("BAD Serengeti\n");
3071708Sstevel return;
3081708Sstevel }
3091708Sstevel
3101708Sstevel b.arg = (void *)apktp;
3111708Sstevel b.type = DEVI_BRANCH_PROM;
3121708Sstevel b.create.prom_branch_select = sbdp_select_top_nodes;
3131708Sstevel b.devi_branch_callback = NULL;
3141708Sstevel
3151708Sstevel (void) e_ddi_branch_create(apktp->top_node, &b, NULL, 0);
3161708Sstevel
3171708Sstevel /*
3181708Sstevel * Release hold acquired in sbdp_find_ssm_dip()
3191708Sstevel */
3201708Sstevel ndi_rele_devi(apktp->top_node);
3211708Sstevel
3221708Sstevel sbdp_cpu_in_reset(node, board, SBDP_ALL_CPUS, 1);
3231708Sstevel }
3241708Sstevel
3251708Sstevel int
sbdp_detach_bd(int node,int board,sbd_error_t * sep)3261708Sstevel sbdp_detach_bd(int node, int board, sbd_error_t *sep)
3271708Sstevel {
3281708Sstevel int rv;
3291708Sstevel attach_pkt_t apkt, *apktp = &apkt;
3301708Sstevel static fn_t f = "sbdp_detach_bd";
3311708Sstevel
3321708Sstevel SBDP_DBG_FUNC("%s\n", f);
3331708Sstevel
3341708Sstevel apktp->node = node;
3351708Sstevel apktp->board = board;
3361708Sstevel apktp->num_of_nodes = 0;
3371708Sstevel apktp->error = 0;
3381708Sstevel apktp->errstr = NULL;
3391708Sstevel sbdp_walk_prom_tree(prom_rootnode(), sbdp_select_top_nodes,
3401708Sstevel (void *) apktp);
3411708Sstevel
3421708Sstevel if (rv = sbdp_detach_nodes(apktp)) {
3431708Sstevel sbdp_set_err(sep, ESBD_IO, NULL);
3441708Sstevel return (rv);
3451708Sstevel }
3461708Sstevel
3471708Sstevel sbdp_cpu_in_reset(node, board, SBDP_ALL_CPUS, 1);
3481708Sstevel /*
3491708Sstevel * Clean up this board struct
3501708Sstevel */
3511708Sstevel sbdp_cleanup_bd(node, board);
3521708Sstevel
3531708Sstevel return (0);
3541708Sstevel }
3551708Sstevel
3561708Sstevel static int
sbdp_detach_nodes(attach_pkt_t * apktp)3571708Sstevel sbdp_detach_nodes(attach_pkt_t *apktp)
3581708Sstevel {
3591708Sstevel dev_info_t **dip;
3601708Sstevel dev_info_t **dev_list;
3611708Sstevel int dev_list_len = 0;
3621708Sstevel int i, rv = 0;
3631708Sstevel
3641708Sstevel dev_list = kmem_zalloc(sizeof (dev_info_t *) * SBDP_MAX_NODES,
3651708Sstevel KM_SLEEP);
3661708Sstevel
3671708Sstevel for (i = 0, dip = dev_list; i < apktp->num_of_nodes; i++) {
3681708Sstevel *dip = e_ddi_nodeid_to_dip(apktp->nodes[i]);
3691708Sstevel if (*dip != NULL) {
3701708Sstevel /*
3711708Sstevel * The branch rooted at dip should already be held,
3721708Sstevel * so release hold acquired in e_ddi_nodeid_to_dip()
3731708Sstevel */
3741708Sstevel ddi_release_devi(*dip);
3751708Sstevel dip++;
3761708Sstevel ++dev_list_len;
3771708Sstevel }
3781708Sstevel }
3791708Sstevel
3801708Sstevel for (i = dev_list_len, dip = &dev_list[i - 1]; i > 0; i--, dip--) {
3811708Sstevel dev_info_t *fdip = NULL;
3821708Sstevel
3831708Sstevel ASSERT(e_ddi_branch_held(*dip));
3841708Sstevel rv = e_ddi_branch_destroy(*dip, &fdip, 0);
3851708Sstevel if (rv) {
3861708Sstevel char *path = kmem_alloc(MAXPATHLEN, KM_SLEEP);
3871708Sstevel
3881708Sstevel /*
3891708Sstevel * If non-NULL, fdip is held and must be released.
3901708Sstevel */
3911708Sstevel if (fdip != NULL) {
3921708Sstevel (void) ddi_pathname(fdip, path);
3931708Sstevel ddi_release_devi(fdip);
3941708Sstevel } else {
3951708Sstevel (void) ddi_pathname(*dip, path);
3961708Sstevel }
3971708Sstevel
3981708Sstevel cmn_err(CE_WARN, "failed to remove node %s (%p): %d",
3991708Sstevel path, fdip ? (void *)fdip : (void *)*dip, rv);
4001708Sstevel
4011708Sstevel kmem_free(path, MAXPATHLEN);
4021708Sstevel
4031708Sstevel apktp->error = apktp->error ? apktp->error : rv;
4041708Sstevel break;
4051708Sstevel }
4061708Sstevel }
4071708Sstevel
4081708Sstevel kmem_free(dev_list, sizeof (dev_info_t *) * SBDP_MAX_NODES);
4091708Sstevel
4101708Sstevel return (rv);
4111708Sstevel }
412