xref: /onnv-gate/usr/src/uts/sun4u/serengeti/io/sbdp_dr.c (revision 11311:639e7bc0b42f)
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