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 2006 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 static 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 * Create a picl node of type cpu and fill it. 252 * properties are filled from both the device tree and the 253 * Machine description. 254 */ 255 static int 256 construct_cpu_node(picl_nodehdl_t plath, di_node_t dn) 257 { 258 int err; 259 char *nodename; 260 picl_nodehdl_t anodeh; 261 262 nodename = di_node_name(dn); /* PICL_PROP_NAME */ 263 264 err = ptree_create_and_add_node(plath, nodename, PICL_CLASS_CPU, 265 &anodeh); 266 if (err != PICL_SUCCESS) 267 return (err); 268 269 add_devinfo_props(anodeh, dn); 270 (void) add_cpu_prop(anodeh, NULL); 271 272 return (err); 273 } 274 275 /* 276 * Given a devinfo node find its reg property. 277 */ 278 static int 279 get_reg_prop(di_node_t dn, int **pdata) 280 { 281 int dret = 0; 282 283 dret = di_prop_lookup_ints(DDI_DEV_T_ANY, dn, OBP_REG, pdata); 284 if (dret > 0) 285 return (dret); 286 287 if (!ph) 288 return (0); 289 dret = di_prom_prop_lookup_ints(ph, dn, OBP_REG, pdata); 290 return (dret < 0? 0 : dret); 291 } 292 293 /* 294 * Given a devinfo cpu node find its cpuid property. 295 */ 296 int 297 get_cpuid(di_node_t di_node) 298 { 299 int len; 300 int *idata; 301 int dcpuid = -1; 302 303 len = get_reg_prop(di_node, &idata); 304 305 if (len != SUN4V_CPU_REGSIZE) 306 return (dcpuid); 307 if (len == SUN4V_CPU_REGSIZE) 308 dcpuid = CFGHDL_TO_CPUID(idata[0]); 309 310 return (dcpuid); 311 } 312 313 int 314 find_cpu(di_node_t node, int cpuid) 315 { 316 int dcpuid; 317 di_node_t cnode; 318 char *nodename; 319 320 for (cnode = di_child_node(node); cnode != DI_NODE_NIL; 321 cnode = di_sibling_node(cnode)) { 322 nodename = di_node_name(cnode); 323 if (nodename == NULL) 324 continue; 325 if (strcmp(nodename, OBP_CPU) == 0) { 326 dcpuid = get_cpuid(cnode); 327 if (dcpuid == cpuid) { 328 return (1); 329 } 330 } 331 } 332 return (0); 333 } 334 335 /* 336 * Callback to the ptree walk function during remove_cpus. 337 * As a part of the args receives a picl nodeh, searches 338 * the device tree for a cpu whose cpuid matches the picl cpu node. 339 * Sets arg struct's result to 1 if it failed to match and terminates 340 * the walk. 341 */ 342 static int 343 remove_cpu_candidate(picl_nodehdl_t nodeh, void *c_args) 344 { 345 di_node_t di_node; 346 cpu_lookup_t *cpu_arg; 347 int err; 348 int pcpuid; 349 int reg_prop[SUN4V_CPU_REGSIZE]; 350 351 if (c_args == NULL) 352 return (PICL_INVALIDARG); 353 354 cpu_arg = c_args; 355 di_node = cpu_arg->di_node; 356 357 err = ptree_get_propval_by_name(nodeh, OBP_REG, reg_prop, 358 sizeof (reg_prop)); 359 360 if (err != PICL_SUCCESS) { 361 return (PICL_WALK_CONTINUE); 362 } 363 364 pcpuid = CFGHDL_TO_CPUID(reg_prop[0]); 365 366 if (!find_cpu(di_node, pcpuid)) { 367 cpu_arg->result = 1; 368 cpu_arg->nodeh = nodeh; 369 return (PICL_WALK_TERMINATE); 370 } 371 372 cpu_arg->result = 0; 373 return (PICL_WALK_CONTINUE); 374 } 375 376 /* 377 * Given the start node of the device tree. 378 * find all cpus in the picl tree that don't have 379 * device tree counterparts and remove them. 380 */ 381 static void 382 remove_cpus(di_node_t di_start) 383 { 384 int err; 385 picl_nodehdl_t plath; 386 cpu_lookup_t cpu_arg; 387 388 err = ptree_get_node_by_path(PLATFORM_PATH, &plath); 389 if (err != PICL_SUCCESS) 390 return; 391 392 do { 393 cpu_arg.di_node = di_start; 394 cpu_arg.nodeh = 0; 395 cpu_arg.result = 0; 396 397 if (ptree_walk_tree_by_class(plath, 398 PICL_CLASS_CPU, &cpu_arg, remove_cpu_candidate) 399 != PICL_SUCCESS) 400 return; 401 402 if (cpu_arg.result == 1) { 403 err = ptree_delete_node(cpu_arg.nodeh); 404 if (err == PICL_SUCCESS) 405 ptree_destroy_node(cpu_arg.nodeh); 406 } 407 } while (cpu_arg.result); 408 } 409 410 /* 411 * Callback to the ptree walk function during add_cpus. 412 * As a part of the args receives a cpu di_node, compares 413 * each picl cpu node's cpuid to the device tree node's cpuid. 414 * Sets arg struct's result to 1 on a match. 415 */ 416 static int 417 cpu_exists(picl_nodehdl_t nodeh, void *c_args) 418 { 419 di_node_t di_node; 420 cpu_lookup_t *cpu_arg; 421 int err; 422 int dcpuid, pcpuid; 423 int reg_prop[4]; 424 425 if (c_args == NULL) 426 return (PICL_INVALIDARG); 427 428 cpu_arg = c_args; 429 di_node = cpu_arg->di_node; 430 dcpuid = get_cpuid(di_node); 431 432 err = ptree_get_propval_by_name(nodeh, OBP_REG, reg_prop, 433 sizeof (reg_prop)); 434 435 if (err != PICL_SUCCESS) 436 return (PICL_WALK_CONTINUE); 437 438 pcpuid = CFGHDL_TO_CPUID(reg_prop[0]); 439 440 if (dcpuid == pcpuid) { 441 cpu_arg->result = 1; 442 return (PICL_WALK_TERMINATE); 443 } 444 445 cpu_arg->result = 0; 446 return (PICL_WALK_CONTINUE); 447 } 448 449 /* 450 * Given the root node of the device tree. 451 * compare it to the picl tree and add to it cpus 452 * that are new. 453 */ 454 static void 455 add_cpus(di_node_t di_node) 456 { 457 int err; 458 di_node_t cnode; 459 picl_nodehdl_t plath; 460 cpu_lookup_t cpu_arg; 461 char *nodename; 462 463 err = ptree_get_node_by_path(PLATFORM_PATH, &plath); 464 if (err != PICL_SUCCESS) 465 return; 466 467 for (cnode = di_child_node(di_node); cnode != DI_NODE_NIL; 468 cnode = di_sibling_node(cnode)) { 469 nodename = di_node_name(cnode); 470 if (nodename == NULL) 471 continue; 472 if (strcmp(nodename, OBP_CPU) == 0) { 473 cpu_arg.di_node = cnode; 474 475 if (ptree_walk_tree_by_class(plath, 476 PICL_CLASS_CPU, &cpu_arg, cpu_exists) 477 != PICL_SUCCESS) 478 return; 479 480 if (cpu_arg.result == 0) 481 /* 482 * Didn't find a matching cpu, add it. 483 */ 484 (void) construct_cpu_node(plath, 485 cnode); 486 } 487 } 488 } 489 490 /* 491 * Handle DR events. Only supports cpu add and remove. 492 */ 493 int 494 update_devices(char *dev, int op) 495 { 496 di_node_t di_root; 497 498 if ((di_root = di_init("/", DINFOCPYALL)) == DI_NODE_NIL) 499 return (PICL_FAILURE); 500 501 if ((ph = di_prom_init()) == NULL) 502 return (PICL_FAILURE); 503 504 if (op == DEV_ADD) { 505 if (strcmp(dev, OBP_CPU) == 0) 506 add_cpus(di_root); 507 } 508 509 if (op == DEV_REMOVE) { 510 if (strcmp(dev, OBP_CPU) == 0) 511 remove_cpus(di_root); 512 } 513 514 di_fini(di_root); 515 di_prom_fini(ph); 516 return (PICL_SUCCESS); 517 } 518