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