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