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 2008 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 #include "mdescplugin.h" 28 29 static di_prom_handle_t ph = DI_PROM_HANDLE_NIL; 30 31 typedef struct cpu_lookup { 32 di_node_t di_node; 33 picl_nodehdl_t nodeh; 34 int result; 35 } cpu_lookup_t; 36 37 extern int add_cpu_prop(picl_nodehdl_t node, void *args); 38 extern md_t *mdesc_devinit(void); 39 40 /* 41 * This function is identical to the one in the picldevtree plugin. 42 * Unfortunately we can't just reuse that code. 43 */ 44 int 45 add_string_list_prop(picl_nodehdl_t nodeh, char *name, char *strlist, 46 unsigned int nrows) 47 { 48 ptree_propinfo_t propinfo; 49 picl_prophdl_t proph; 50 picl_prophdl_t tblh; 51 int err; 52 unsigned int i; 53 unsigned int j; 54 picl_prophdl_t *proprow; 55 int len; 56 57 #define NCOLS_IN_STRING_TABLE 1 58 59 err = ptree_init_propinfo(&propinfo, PTREE_PROPINFO_VERSION, 60 PICL_PTYPE_TABLE, PICL_READ, sizeof (picl_prophdl_t), name, 61 NULL, NULL); 62 if (err != PICL_SUCCESS) 63 return (err); 64 65 err = ptree_create_table(&tblh); 66 if (err != PICL_SUCCESS) 67 return (err); 68 69 err = ptree_create_and_add_prop(nodeh, &propinfo, &tblh, &proph); 70 if (err != PICL_SUCCESS) 71 return (err); 72 73 proprow = alloca(sizeof (picl_prophdl_t) * nrows); 74 if (proprow == NULL) { 75 (void) ptree_destroy_prop(proph); 76 return (PICL_FAILURE); 77 } 78 79 for (j = 0; j < nrows; ++j) { 80 len = strlen(strlist) + 1; 81 err = ptree_init_propinfo(&propinfo, PTREE_PROPINFO_VERSION, 82 PICL_PTYPE_CHARSTRING, PICL_READ, len, name, 83 NULL, NULL); 84 if (err != PICL_SUCCESS) 85 break; 86 err = ptree_create_prop(&propinfo, strlist, &proprow[j]); 87 if (err != PICL_SUCCESS) 88 break; 89 strlist += len; 90 err = ptree_add_row_to_table(tblh, NCOLS_IN_STRING_TABLE, 91 &proprow[j]); 92 if (err != PICL_SUCCESS) 93 break; 94 } 95 96 if (err != PICL_SUCCESS) { 97 for (i = 0; i < j; ++i) 98 (void) ptree_destroy_prop(proprow[i]); 99 (void) ptree_delete_prop(proph); 100 (void) ptree_destroy_prop(proph); 101 return (err); 102 } 103 104 return (PICL_SUCCESS); 105 } 106 107 /* 108 * This function is identical to the one in the picldevtree plugin. 109 * Unfortunately we can't just reuse that code. 110 */ 111 static void 112 add_devinfo_props(picl_nodehdl_t nodeh, di_node_t di_node) 113 { 114 int instance; 115 char *di_val; 116 di_prop_t di_prop; 117 int di_ptype; 118 ptree_propinfo_t propinfo; 119 120 instance = di_instance(di_node); 121 (void) ptree_init_propinfo(&propinfo, PTREE_PROPINFO_VERSION, 122 PICL_PTYPE_INT, PICL_READ, sizeof (instance), PICL_PROP_INSTANCE, 123 NULL, NULL); 124 (void) ptree_create_and_add_prop(nodeh, &propinfo, &instance, NULL); 125 126 di_val = di_bus_addr(di_node); 127 if (di_val) { 128 (void) ptree_init_propinfo(&propinfo, PTREE_PROPINFO_VERSION, 129 PICL_PTYPE_CHARSTRING, PICL_READ, strlen(di_val) + 1, 130 PICL_PROP_BUS_ADDR, NULL, NULL); 131 (void) ptree_create_and_add_prop(nodeh, &propinfo, di_val, 132 NULL); 133 } 134 135 di_val = di_binding_name(di_node); 136 if (di_val) { 137 (void) ptree_init_propinfo(&propinfo, PTREE_PROPINFO_VERSION, 138 PICL_PTYPE_CHARSTRING, PICL_READ, strlen(di_val) + 1, 139 PICL_PROP_BINDING_NAME, NULL, NULL); 140 (void) ptree_create_and_add_prop(nodeh, &propinfo, di_val, 141 NULL); 142 } 143 144 di_val = di_driver_name(di_node); 145 if (di_val) { 146 (void) ptree_init_propinfo(&propinfo, PTREE_PROPINFO_VERSION, 147 PICL_PTYPE_CHARSTRING, PICL_READ, strlen(di_val) + 1, 148 PICL_PROP_DRIVER_NAME, NULL, NULL); 149 (void) ptree_create_and_add_prop(nodeh, &propinfo, di_val, 150 NULL); 151 } 152 153 di_val = di_devfs_path(di_node); 154 if (di_val) { 155 (void) ptree_init_propinfo(&propinfo, PTREE_PROPINFO_VERSION, 156 PICL_PTYPE_CHARSTRING, PICL_READ, strlen(di_val) + 1, 157 PICL_PROP_DEVFS_PATH, NULL, NULL); 158 (void) ptree_create_and_add_prop(nodeh, &propinfo, di_val, 159 NULL); 160 di_devfs_path_free(di_val); 161 } 162 163 for (di_prop = di_prop_next(di_node, DI_PROP_NIL); 164 di_prop != DI_PROP_NIL; 165 di_prop = di_prop_next(di_node, di_prop)) { 166 167 di_val = di_prop_name(di_prop); 168 di_ptype = di_prop_type(di_prop); 169 switch (di_ptype) { 170 case DI_PROP_TYPE_BOOLEAN: 171 (void) ptree_init_propinfo(&propinfo, 172 PTREE_PROPINFO_VERSION, PICL_PTYPE_VOID, 173 PICL_READ, (size_t)0, di_val, NULL, NULL); 174 (void) ptree_create_and_add_prop(nodeh, &propinfo, 175 NULL, NULL); 176 break; 177 case DI_PROP_TYPE_INT: { 178 int *idata; 179 int len; 180 181 len = di_prop_ints(di_prop, &idata); 182 if (len < 0) 183 /* Recieved error, so ignore prop */ 184 break; 185 186 if (len == 1) 187 (void) ptree_init_propinfo(&propinfo, 188 PTREE_PROPINFO_VERSION, PICL_PTYPE_INT, 189 PICL_READ, len * sizeof (int), di_val, 190 NULL, NULL); 191 else 192 (void) ptree_init_propinfo(&propinfo, 193 PTREE_PROPINFO_VERSION, 194 PICL_PTYPE_BYTEARRAY, PICL_READ, 195 len * sizeof (int), di_val, 196 NULL, NULL); 197 198 (void) ptree_create_and_add_prop(nodeh, &propinfo, 199 idata, NULL); 200 } 201 break; 202 case DI_PROP_TYPE_STRING: { 203 char *sdata; 204 int len; 205 206 len = di_prop_strings(di_prop, &sdata); 207 if (len < 0) 208 break; 209 210 if (len == 1) { 211 (void) ptree_init_propinfo(&propinfo, 212 PTREE_PROPINFO_VERSION, 213 PICL_PTYPE_CHARSTRING, PICL_READ, 214 strlen(sdata) + 1, di_val, 215 NULL, NULL); 216 (void) ptree_create_and_add_prop(nodeh, 217 &propinfo, sdata, NULL); 218 } else { 219 (void) add_string_list_prop(nodeh, di_val, 220 sdata, len); 221 } 222 } 223 break; 224 case DI_PROP_TYPE_BYTE: { 225 int len; 226 unsigned char *bdata; 227 228 len = di_prop_bytes(di_prop, &bdata); 229 if (len < 0) 230 break; 231 (void) ptree_init_propinfo(&propinfo, 232 PTREE_PROPINFO_VERSION, PICL_PTYPE_BYTEARRAY, 233 PICL_READ, len, di_val, NULL, NULL); 234 (void) ptree_create_and_add_prop(nodeh, &propinfo, 235 bdata, NULL); 236 } 237 break; 238 case DI_PROP_TYPE_UNKNOWN: 239 break; 240 case DI_PROP_TYPE_UNDEF_IT: 241 break; 242 default: 243 break; 244 } 245 } 246 } 247 248 /* 249 * add OBP_REG property to picl cpu node if it's not already there. 250 */ 251 static void 252 add_reg_prop(picl_nodehdl_t pn, di_node_t dn) 253 { 254 int reg_prop[SUN4V_CPU_REGSIZE]; 255 int status; 256 int dlen; 257 int *pdata; 258 ptree_propinfo_t propinfo; 259 260 status = ptree_get_propval_by_name(pn, OBP_REG, reg_prop, 261 sizeof (reg_prop)); 262 if (status == PICL_SUCCESS) { 263 return; 264 } 265 dlen = di_prom_prop_lookup_ints(ph, dn, OBP_REG, &pdata); 266 if (dlen < 0) { 267 return; 268 } 269 status = ptree_init_propinfo(&propinfo, PTREE_PROPINFO_VERSION, 270 PICL_PTYPE_BYTEARRAY, PICL_READ, dlen * sizeof (int), OBP_REG, 271 NULL, NULL); 272 if (status != PICL_SUCCESS) { 273 return; 274 } 275 (void) ptree_create_and_add_prop(pn, &propinfo, pdata, NULL); 276 } 277 278 /* 279 * Create a picl node of type cpu and fill it. 280 * properties are filled from both the device tree and the 281 * Machine description. 282 */ 283 static int 284 construct_cpu_node(picl_nodehdl_t plath, di_node_t dn) 285 { 286 int err; 287 char *nodename; 288 picl_nodehdl_t anodeh; 289 290 nodename = di_node_name(dn); /* PICL_PROP_NAME */ 291 292 err = ptree_create_and_add_node(plath, nodename, PICL_CLASS_CPU, 293 &anodeh); 294 if (err != PICL_SUCCESS) 295 return (err); 296 297 add_devinfo_props(anodeh, dn); 298 add_reg_prop(anodeh, dn); 299 (void) add_cpu_prop(anodeh, NULL); 300 301 return (err); 302 } 303 304 /* 305 * Given a devinfo node find its reg property. 306 */ 307 static int 308 get_reg_prop(di_node_t dn, int **pdata) 309 { 310 int dret = 0; 311 312 dret = di_prop_lookup_ints(DDI_DEV_T_ANY, dn, OBP_REG, pdata); 313 if (dret > 0) 314 return (dret); 315 316 if (!ph) 317 return (0); 318 dret = di_prom_prop_lookup_ints(ph, dn, OBP_REG, pdata); 319 return (dret < 0? 0 : dret); 320 } 321 322 /* 323 * Given a devinfo cpu node find its cpuid property. 324 */ 325 int 326 get_cpuid(di_node_t di_node) 327 { 328 int len; 329 int *idata; 330 int dcpuid = -1; 331 332 len = get_reg_prop(di_node, &idata); 333 334 if (len != SUN4V_CPU_REGSIZE) 335 return (dcpuid); 336 if (len == SUN4V_CPU_REGSIZE) 337 dcpuid = CFGHDL_TO_CPUID(idata[0]); 338 339 return (dcpuid); 340 } 341 342 int 343 find_cpu(di_node_t node, int cpuid) 344 { 345 int dcpuid; 346 di_node_t cnode; 347 char *nodename; 348 349 for (cnode = di_child_node(node); cnode != DI_NODE_NIL; 350 cnode = di_sibling_node(cnode)) { 351 nodename = di_node_name(cnode); 352 if (nodename == NULL) 353 continue; 354 if (strcmp(nodename, OBP_CPU) == 0) { 355 dcpuid = get_cpuid(cnode); 356 if (dcpuid == cpuid) { 357 return (1); 358 } 359 } 360 } 361 return (0); 362 } 363 364 /* 365 * Callback to the ptree walk function during remove_cpus. 366 * As a part of the args receives a picl nodeh, searches 367 * the device tree for a cpu whose cpuid matches the picl cpu node. 368 * Sets arg struct's result to 1 if it failed to match and terminates 369 * the walk. 370 */ 371 static int 372 remove_cpu_candidate(picl_nodehdl_t nodeh, void *c_args) 373 { 374 di_node_t di_node; 375 cpu_lookup_t *cpu_arg; 376 int err; 377 int pcpuid; 378 int reg_prop[SUN4V_CPU_REGSIZE]; 379 380 if (c_args == NULL) 381 return (PICL_INVALIDARG); 382 383 cpu_arg = c_args; 384 di_node = cpu_arg->di_node; 385 386 err = ptree_get_propval_by_name(nodeh, OBP_REG, reg_prop, 387 sizeof (reg_prop)); 388 389 if (err != PICL_SUCCESS) { 390 return (PICL_WALK_CONTINUE); 391 } 392 393 pcpuid = CFGHDL_TO_CPUID(reg_prop[0]); 394 395 if (!find_cpu(di_node, pcpuid)) { 396 cpu_arg->result = 1; 397 cpu_arg->nodeh = nodeh; 398 return (PICL_WALK_TERMINATE); 399 } 400 401 cpu_arg->result = 0; 402 return (PICL_WALK_CONTINUE); 403 } 404 405 /* 406 * Given the start node of the device tree. 407 * find all cpus in the picl tree that don't have 408 * device tree counterparts and remove them. 409 */ 410 static void 411 remove_cpus(di_node_t di_start) 412 { 413 int err; 414 picl_nodehdl_t plath; 415 cpu_lookup_t cpu_arg; 416 417 err = ptree_get_node_by_path(PLATFORM_PATH, &plath); 418 if (err != PICL_SUCCESS) 419 return; 420 421 do { 422 cpu_arg.di_node = di_start; 423 cpu_arg.nodeh = 0; 424 cpu_arg.result = 0; 425 426 if (ptree_walk_tree_by_class(plath, 427 PICL_CLASS_CPU, &cpu_arg, remove_cpu_candidate) 428 != PICL_SUCCESS) 429 return; 430 431 if (cpu_arg.result == 1) { 432 err = ptree_delete_node(cpu_arg.nodeh); 433 if (err == PICL_SUCCESS) 434 ptree_destroy_node(cpu_arg.nodeh); 435 } 436 } while (cpu_arg.result); 437 } 438 439 /* 440 * Callback to the ptree walk function during add_cpus. 441 * As a part of the args receives a cpu di_node, compares 442 * each picl cpu node's cpuid to the device tree node's cpuid. 443 * Sets arg struct's result to 1 on a match. 444 */ 445 static int 446 cpu_exists(picl_nodehdl_t nodeh, void *c_args) 447 { 448 di_node_t di_node; 449 cpu_lookup_t *cpu_arg; 450 int err; 451 int dcpuid, pcpuid; 452 int reg_prop[4]; 453 454 if (c_args == NULL) 455 return (PICL_INVALIDARG); 456 457 cpu_arg = c_args; 458 di_node = cpu_arg->di_node; 459 dcpuid = get_cpuid(di_node); 460 461 err = ptree_get_propval_by_name(nodeh, OBP_REG, reg_prop, 462 sizeof (reg_prop)); 463 464 if (err != PICL_SUCCESS) 465 return (PICL_WALK_CONTINUE); 466 467 pcpuid = CFGHDL_TO_CPUID(reg_prop[0]); 468 469 if (dcpuid == pcpuid) { 470 cpu_arg->result = 1; 471 return (PICL_WALK_TERMINATE); 472 } 473 474 cpu_arg->result = 0; 475 return (PICL_WALK_CONTINUE); 476 } 477 478 /* 479 * Given the root node of the device tree. 480 * compare it to the picl tree and add to it cpus 481 * that are new. 482 */ 483 static void 484 add_cpus(di_node_t di_node) 485 { 486 int err; 487 di_node_t cnode; 488 picl_nodehdl_t plath; 489 cpu_lookup_t cpu_arg; 490 char *nodename; 491 492 err = ptree_get_node_by_path(PLATFORM_PATH, &plath); 493 if (err != PICL_SUCCESS) 494 return; 495 496 for (cnode = di_child_node(di_node); cnode != DI_NODE_NIL; 497 cnode = di_sibling_node(cnode)) { 498 nodename = di_node_name(cnode); 499 if (nodename == NULL) 500 continue; 501 if (strcmp(nodename, OBP_CPU) == 0) { 502 cpu_arg.di_node = cnode; 503 504 if (ptree_walk_tree_by_class(plath, 505 PICL_CLASS_CPU, &cpu_arg, cpu_exists) 506 != PICL_SUCCESS) 507 return; 508 509 if (cpu_arg.result == 0) 510 /* 511 * Didn't find a matching cpu, add it. 512 */ 513 (void) construct_cpu_node(plath, 514 cnode); 515 } 516 } 517 } 518 519 /* 520 * Handle DR events. Only supports cpu add and remove. 521 */ 522 int 523 update_devices(char *dev, int op) 524 { 525 di_node_t di_root; 526 527 if ((di_root = di_init("/", DINFOCPYALL)) == DI_NODE_NIL) 528 return (PICL_FAILURE); 529 530 if ((ph = di_prom_init()) == NULL) 531 return (PICL_FAILURE); 532 533 if (op == DEV_ADD) { 534 if (strcmp(dev, OBP_CPU) == 0) 535 add_cpus(di_root); 536 } 537 538 if (op == DEV_REMOVE) { 539 if (strcmp(dev, OBP_CPU) == 0) 540 remove_cpus(di_root); 541 } 542 543 di_fini(di_root); 544 di_prom_fini(ph); 545 return (PICL_SUCCESS); 546 } 547