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