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
sbdp_walk_prom_tree_worker(pnode_t node,int (* f)(pnode_t,void *,uint_t),void * arg)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
sbdp_walk_prom_tree_start(void * arg,int has_changed)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
sbdp_walk_prom_tree(pnode_t node,int (* f)(pnode_t,void *,uint_t),void * arg)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
sbdp_attach_branch(dev_info_t * pdip,pnode_t node,void * arg)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
sbdp_find_ssm_dip(dev_info_t * dip,void * arg)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
sbdp_select_top_nodes(pnode_t node,void * arg,uint_t flags)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#",
256*07d06da5SSurya Prakki 				    (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
sbdp_attach_bd(int node,int board)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
sbdp_detach_bd(int node,int board,sbd_error_t * sep)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
sbdp_detach_nodes(attach_pkt_t * apktp)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