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