1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 
22 /*
23  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #include <sys/types.h>
28 #include <sys/cmn_err.h>
29 #include <sys/conf.h>
30 #include <sys/autoconf.h>
31 #include <sys/systm.h>
32 #include <sys/modctl.h>
33 #include <sys/ddi.h>
34 #include <sys/sunddi.h>
35 #include <sys/sunndi.h>
36 #include <sys/ndi_impldefs.h>
37 #include <sys/ddi_impldefs.h>
38 #include <sys/promif.h>
39 #include <sys/stat.h>
40 #include <sys/kmem.h>
41 #include <sys/promif.h>
42 #include <sys/conf.h>
43 #include <sys/obpdefs.h>
44 #include <sys/sgsbbc_mailbox.h>
45 #include <sys/cpuvar.h>
46 #include <vm/seg_kmem.h>
47 #include <sys/prom_plat.h>
48 #include <sys/machsystm.h>
49 #include <sys/cheetahregs.h>
50 
51 #include <sys/sbd_ioctl.h>
52 #include <sys/sbd.h>
53 #include <sys/sbdp_priv.h>
54 
55 static int sbdp_detach_nodes(attach_pkt_t *);
56 static void
sbdp_walk_prom_tree_worker(pnode_t node,int (* f)(pnode_t,void *,uint_t),void * arg)57 sbdp_walk_prom_tree_worker(
58 	pnode_t node,
59 	int(*f)(pnode_t, void *, uint_t),
60 	void *arg)
61 {
62 	/*
63 	 * Ignore return value from callback. Return value from callback
64 	 * does NOT indicate subsequent walk behavior.
65 	 */
66 	(void) (*f)(node, arg, 0);
67 
68 	if (node != OBP_NONODE) {
69 		sbdp_walk_prom_tree_worker(prom_childnode(node), f, arg);
70 		sbdp_walk_prom_tree_worker(prom_nextnode(node), f, arg);
71 	}
72 }
73 
74 struct sbdp_walk_prom_tree_args {
75 	pnode_t	node;
76 	int	(*f)(pnode_t, void *, uint_t);
77 	void	*arg;
78 };
79 
80 /*ARGSUSED*/
81 static int
sbdp_walk_prom_tree_start(void * arg,int has_changed)82 sbdp_walk_prom_tree_start(void *arg, int has_changed)
83 {
84 	struct sbdp_walk_prom_tree_args *argbp = arg;
85 
86 	sbdp_walk_prom_tree_worker(argbp->node, argbp->f, argbp->arg);
87 	return (0);
88 }
89 
90 void
sbdp_walk_prom_tree(pnode_t node,int (* f)(pnode_t,void *,uint_t),void * arg)91 sbdp_walk_prom_tree(pnode_t node, int(*f)(pnode_t, void *, uint_t), void *arg)
92 {
93 	struct sbdp_walk_prom_tree_args arg_block;
94 
95 	arg_block.node = node;
96 	arg_block.f = f;
97 	arg_block.arg = arg;
98 	(void) prom_tree_access(sbdp_walk_prom_tree_start, &arg_block, NULL);
99 }
100 
101 static void
sbdp_attach_branch(dev_info_t * pdip,pnode_t node,void * arg)102 sbdp_attach_branch(dev_info_t *pdip, pnode_t node, void *arg)
103 {
104 	attach_pkt_t	*apktp = (attach_pkt_t *)arg;
105 	pnode_t		child;
106 	dev_info_t	*dip = NULL;
107 	static int	err = 0;
108 	static int	len = 0;
109 	char		name[OBP_MAXDRVNAME];
110 #if OBP_MAXDRVNAME == OBP_MAXPROPNAME
111 #define	buf	name
112 #else
113 	char		buf[OBP_MAXPROPNAME];
114 #endif
115 	static fn_t	f = "sbdp_attach_branch";
116 
117 	SBDP_DBG_FUNC("%s\n", f);
118 
119 	if (node == OBP_NONODE)
120 		return;
121 
122 	/*
123 	 * Get the status for this node
124 	 * If it has failed we imitate boot by not creating a node
125 	 * in solaris. We just warn the user
126 	 */
127 	if (check_status(node, buf, pdip) != DDI_SUCCESS) {
128 		SBDP_DBG_STATE("status failed skipping this node\n");
129 		return;
130 	}
131 
132 	len = prom_getproplen(node, OBP_REG);
133 	if (len <= 0) {
134 		return;
135 	}
136 
137 	(void) prom_getprop(node, OBP_NAME, (caddr_t)name);
138 	err = ndi_devi_alloc(pdip, name, node, &dip);
139 	if (err != NDI_SUCCESS) {
140 		return;
141 	}
142 	SBDP_DBG_STATE("attaching %s\n", name);
143 	err = ndi_devi_online(dip, NDI_DEVI_BIND);
144 	if (err != NDI_SUCCESS) {
145 		(void) ndi_devi_free(dip);
146 		return;
147 	}
148 	child = prom_childnode(node);
149 	if (child != OBP_NONODE) {
150 		for (; child != OBP_NONODE;
151 		    child = prom_nextnode(child)) {
152 			sbdp_attach_branch(dip, child, (void *)apktp);
153 		}
154 	}
155 #undef buf
156 }
157 
158 static int
sbdp_find_ssm_dip(dev_info_t * dip,void * arg)159 sbdp_find_ssm_dip(dev_info_t *dip, void *arg)
160 {
161 	attach_pkt_t	*apktp;
162 	int		node;
163 	static fn_t	f = "sbdp_find_ssm_dip";
164 
165 	SBDP_DBG_FUNC("%s\n", f);
166 
167 	apktp = (attach_pkt_t *)arg;
168 
169 	if (apktp == NULL) {
170 		SBDP_DBG_STATE("error on the argument\n");
171 		return (DDI_WALK_CONTINUE);
172 	}
173 
174 	if ((node = ddi_getprop(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
175 	    "nodeid", -1)) == -1)
176 		return (DDI_WALK_CONTINUE);
177 
178 	if (node == apktp->node) {
179 		ndi_hold_devi(dip);
180 		apktp->top_node = dip;
181 		return (DDI_WALK_TERMINATE);
182 	}
183 	return (DDI_WALK_CONTINUE);
184 }
185 
186 /*ARGSUSED*/
187 int
sbdp_select_top_nodes(pnode_t node,void * arg,uint_t flags)188 sbdp_select_top_nodes(pnode_t node, void *arg, uint_t flags)
189 {
190 	int		board, bd;
191 	attach_pkt_t    *apktp = (attach_pkt_t *)arg;
192 	char		devtype[OBP_MAXDRVNAME];
193 	char		devname[OBP_MAXDRVNAME];
194 	int		i;
195 	sbd_devattr_t	*sbdp_top_nodes;
196 	int		wnode;
197 	static fn_t	f = "sbdp_select_top_nodes";
198 
199 	SBDP_DBG_FUNC("%s\n", f);
200 
201 	if (apktp == NULL) {
202 		SBDP_DBG_STATE("error on the argument\n");
203 		return (DDI_FAILURE);
204 	}
205 
206 	board = apktp->board;
207 	sbdp_top_nodes = sbdp_get_devattr();
208 
209 	if (sbdp_get_bd_and_wnode_num(node, &bd, &wnode) < 0)
210 		return (DDI_FAILURE);
211 
212 	if (bd != board)
213 		return (DDI_FAILURE);
214 
215 	SBDP_DBG_MISC("%s: board is %d\n", f, bd);
216 
217 	(void) prom_getprop(node, OBP_DEVICETYPE, (caddr_t)devtype);
218 	(void) prom_getprop(node, OBP_NAME, (caddr_t)devname);
219 
220 	if (strcmp(devname, "cmp") == 0) {
221 		apktp->nodes[apktp->num_of_nodes] = node;
222 		apktp->num_of_nodes++;
223 
224 		/* We want this node */
225 		return (DDI_SUCCESS);
226 	}
227 
228 	for (i = 0; sbdp_top_nodes[i].s_obp_type != NULL; i++) {
229 		if (strcmp(devtype, sbdp_top_nodes[i].s_obp_type) == 0) {
230 			if (strcmp(devtype, "cpu") == 0) {
231 				int		cpuid;
232 				int		impl;
233 
234 				/*
235 				 * Check the status of the cpu
236 				 * If it is failed ignore it
237 				 */
238 				if (sbdp_get_comp_status(node) != SBD_COND_OK)
239 					return (DDI_FAILURE);
240 
241 				if (prom_getprop(node, "cpuid",
242 				    (caddr_t)&cpuid) == -1) {
243 
244 					if (prom_getprop(node, "portid",
245 					    (caddr_t)&cpuid) == -1) {
246 
247 						return (DDI_WALK_TERMINATE);
248 					}
249 				}
250 
251 				if (sbdp_set_cpu_present(wnode, bd,
252 				    SG_CPUID_TO_CPU_UNIT(cpuid)) == -1)
253 					return (DDI_WALK_TERMINATE);
254 
255 				(void) prom_getprop(node, "implementation#",
256 				    (caddr_t)&impl);
257 				/*
258 				 * If it is a CPU under CMP, don't save
259 				 * the node as we will be saving the CMP
260 				 * node.
261 				 */
262 				if (CPU_IMPL_IS_CMP(impl))
263 					return (DDI_FAILURE);
264 			}
265 
266 			/*
267 			 * Check to make sure we haven't run out of bounds
268 			 */
269 			if (apktp->num_of_nodes >= SBDP_MAX_NODES)
270 				return (DDI_FAILURE);
271 
272 			/* Save node */
273 			apktp->nodes[apktp->num_of_nodes] = node;
274 			apktp->num_of_nodes++;
275 
276 			/* We want this node */
277 			return (DDI_SUCCESS);
278 		}
279 	}
280 
281 	return (DDI_FAILURE);
282 }
283 
284 void
sbdp_attach_bd(int node,int board)285 sbdp_attach_bd(int node, int board)
286 {
287 	devi_branch_t	b = {0};
288 	attach_pkt_t    apkt, *apktp = &apkt;
289 	static fn_t	f = "sbdp_attach_bd";
290 
291 	SBDP_DBG_FUNC("%s\n", f);
292 
293 	apktp->node = node;
294 	apktp->board = board;
295 	apktp->num_of_nodes = 0;
296 	apktp->flags = 0;
297 
298 	apktp->top_node = NULL;
299 
300 	/*
301 	 * Root node doesn't have to be held for ddi_walk_devs()
302 	 */
303 	ddi_walk_devs(ddi_root_node(), sbdp_find_ssm_dip, (void *) apktp);
304 
305 	if (apktp->top_node == NULL) {
306 		SBDP_DBG_STATE("BAD Serengeti\n");
307 		return;
308 	}
309 
310 	b.arg = (void *)apktp;
311 	b.type = DEVI_BRANCH_PROM;
312 	b.create.prom_branch_select = sbdp_select_top_nodes;
313 	b.devi_branch_callback = NULL;
314 
315 	(void) e_ddi_branch_create(apktp->top_node, &b, NULL, 0);
316 
317 	/*
318 	 * Release hold acquired in sbdp_find_ssm_dip()
319 	 */
320 	ndi_rele_devi(apktp->top_node);
321 
322 	sbdp_cpu_in_reset(node, board, SBDP_ALL_CPUS, 1);
323 }
324 
325 int
sbdp_detach_bd(int node,int board,sbd_error_t * sep)326 sbdp_detach_bd(int node, int board, sbd_error_t *sep)
327 {
328 	int		rv;
329 	attach_pkt_t	apkt, *apktp = &apkt;
330 	static fn_t	f = "sbdp_detach_bd";
331 
332 	SBDP_DBG_FUNC("%s\n", f);
333 
334 	apktp->node = node;
335 	apktp->board = board;
336 	apktp->num_of_nodes = 0;
337 	apktp->error = 0;
338 	apktp->errstr = NULL;
339 	sbdp_walk_prom_tree(prom_rootnode(), sbdp_select_top_nodes,
340 	    (void *) apktp);
341 
342 	if (rv = sbdp_detach_nodes(apktp)) {
343 		sbdp_set_err(sep, ESBD_IO, NULL);
344 		return (rv);
345 	}
346 
347 	sbdp_cpu_in_reset(node, board, SBDP_ALL_CPUS, 1);
348 	/*
349 	 * Clean up this board struct
350 	 */
351 	sbdp_cleanup_bd(node, board);
352 
353 	return (0);
354 }
355 
356 static int
sbdp_detach_nodes(attach_pkt_t * apktp)357 sbdp_detach_nodes(attach_pkt_t *apktp)
358 {
359 	dev_info_t	**dip;
360 	dev_info_t	**dev_list;
361 	int		dev_list_len = 0;
362 	int		i, rv = 0;
363 
364 	dev_list =  kmem_zalloc(sizeof (dev_info_t *) * SBDP_MAX_NODES,
365 	    KM_SLEEP);
366 
367 	for (i = 0, dip = dev_list; i < apktp->num_of_nodes; i++) {
368 		*dip = e_ddi_nodeid_to_dip(apktp->nodes[i]);
369 		if (*dip != NULL) {
370 			/*
371 			 * The branch rooted at dip should already be held,
372 			 * so release hold acquired in e_ddi_nodeid_to_dip()
373 			 */
374 			ddi_release_devi(*dip);
375 			dip++;
376 			++dev_list_len;
377 		}
378 	}
379 
380 	for (i = dev_list_len, dip = &dev_list[i - 1]; i > 0; i--, dip--) {
381 		dev_info_t	*fdip = NULL;
382 
383 		ASSERT(e_ddi_branch_held(*dip));
384 		rv = e_ddi_branch_destroy(*dip, &fdip, 0);
385 		if (rv) {
386 			char *path = kmem_alloc(MAXPATHLEN, KM_SLEEP);
387 
388 			/*
389 			 * If non-NULL, fdip is held and must be released.
390 			 */
391 			if (fdip != NULL) {
392 				(void) ddi_pathname(fdip, path);
393 				ddi_release_devi(fdip);
394 			} else {
395 				(void) ddi_pathname(*dip, path);
396 			}
397 
398 			cmn_err(CE_WARN, "failed to remove node %s (%p): %d",
399 			    path, fdip ? (void *)fdip : (void *)*dip, rv);
400 
401 			kmem_free(path, MAXPATHLEN);
402 
403 			apktp->error = apktp->error ? apktp->error : rv;
404 			break;
405 		}
406 	}
407 
408 	kmem_free(dev_list, sizeof (dev_info_t *) * SBDP_MAX_NODES);
409 
410 	return (rv);
411 }
412