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, Version 1.0 only 6 * (the "License"). You may not use this file except in compliance 7 * with the License. 8 * 9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 * or http://www.opensolaris.org/os/licensing. 11 * See the License for the specific language governing permissions 12 * and limitations under the License. 13 * 14 * When distributing Covered Code, include this CDDL HEADER in each 15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 * If applicable, add the following below this CDDL HEADER, with the 17 * fields enclosed by brackets "[]" replaced with your own identifying 18 * information: Portions Copyright [yyyy] [name of copyright owner] 19 * 20 * CDDL HEADER END 21 */ 22 /* 23 * Copyright 2000-2003 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 <stdio.h> 30 #include <stdlib.h> 31 #include <string.h> 32 #include <unistd.h> 33 #include <sys/systeminfo.h> 34 #include <sys/utsname.h> 35 #include <sys/openpromio.h> 36 #include <libdevinfo.h> 37 38 #include "pdevinfo.h" 39 #include "pdevinfo_sun4u.h" 40 #include "display.h" 41 #include "display_sun4u.h" 42 #include "libprtdiag.h" 43 44 /* 45 * This file contains the functions that are to be called when 46 * a platform wants to use libdevinfo for it's device information 47 * instead of OBP. This will allow prtdiag to support hot-plug 48 * events on platforms whose OBP doesn't get updated to reflect 49 * the hot-plug changes to the system. 50 */ 51 52 int do_devinfo(int syserrlog, char *pgname, int log_flag, 53 int prt_flag); 54 static void dump_di_node(Prom_node *pnode, di_node_t di_node); 55 static Prom_node *walk_di_tree(Sys_tree *tree, Prom_node *root, 56 di_node_t di_node); 57 static int match_compatible_name(char *, int, char *); 58 59 /* 60 * Global variables 61 */ 62 di_prom_handle_t ph; /* Handle for using di_prom interface */ 63 extern char *progname; 64 65 66 67 /* 68 * Used instead of the walk() function when a platform wants to 69 * walk libdevinfo's device tree instead of walking OBP's 70 * device tree. 71 */ 72 static Prom_node* 73 walk_di_tree(Sys_tree *tree, Prom_node *root, di_node_t di_node) 74 { 75 di_node_t curnode; 76 Prom_node *pnode; 77 char *name, *type, *model, *compatible_array; 78 int board_node = 0; 79 int *int_val; 80 int portid; 81 int is_schizo = 0, n_names; 82 83 /* allocate a node for this level */ 84 if ((pnode = (Prom_node *) malloc(sizeof (struct prom_node))) == 85 NULL) { 86 perror("malloc"); 87 exit(2); 88 } 89 90 /* assign parent Prom_node */ 91 pnode->parent = root; 92 pnode->sibling = NULL; 93 pnode->child = NULL; 94 95 /* read properties for this node */ 96 dump_di_node(pnode, di_node); 97 98 name = get_node_name(pnode); 99 type = get_node_type(pnode); 100 if (type == NULL) 101 type = ""; 102 model = (char *)get_prop_val(find_prop(pnode, "model")); 103 if (model == NULL) 104 model = ""; 105 106 /* 107 * For identifying Schizo nodes we need to check if the 108 * compatible property contains the string 'pci108e,8001'. 109 * This property contains an array of strings so we need 110 * search all strings. 111 */ 112 if ((n_names = di_compatible_names(di_node, &compatible_array)) > 0) { 113 if (match_compatible_name(compatible_array, n_names, 114 "pci108e,8001")) 115 is_schizo = 1; 116 } 117 118 if (int_val = (int *)get_prop_val(find_prop(pnode, "portid"))) 119 portid = *int_val; 120 else if ((strcmp(type, "cpu") == 0) && 121 (int_val = (int *)get_prop_val(find_prop(pnode->parent, "portid")))) 122 portid = *int_val; 123 else 124 portid = -1; 125 126 #ifdef DEBUG 127 if (name != NULL) 128 printf("name=%s\n", name); 129 if (type != NULL) 130 printf("type=%s\n", type); 131 if (model != NULL) 132 printf("model=%s\n", model); 133 printf("portid=%d\n", portid); 134 #endif 135 136 if (name != NULL) { 137 if (has_board_num(pnode)) { 138 add_node(tree, pnode); 139 board_node = 1; 140 D_PRINTF("\n---\nnodename = %s [ %s ] \n", 141 di_node_name(di_node), di_devfs_path(di_node)); 142 D_PRINTF("ADDED BOARD name=%s type=%s model=%s " 143 "portid =%d\n", name, type, model, portid); 144 } else if ((strcmp(name, FFB_NAME) == 0) || 145 (strcmp(type, "cpu") == 0) || 146 147 ((strcmp(type, "memory-controller") == 0) && 148 (strcmp(name, "ac") != 0)) || 149 150 ((strcmp(name, "pci") == 0) && 151 (strcmp(model, "SUNW,psycho") == 0)) || 152 153 ((strcmp(name, "pci") == 0) && 154 (strcmp(model, "SUNW,sabre") == 0)) || 155 156 ((strcmp(name, "pci") == 0) && (is_schizo)) || 157 158 (strcmp(name, "counter-timer") == 0) || 159 (strcmp(name, "sbus") == 0)) { 160 add_node(tree, pnode); 161 board_node = 1; 162 D_PRINTF("\n---\nnodename = %s [ %s ] \n", 163 di_node_name(di_node), di_devfs_path(di_node)); 164 D_PRINTF("ADDED BOARD name=%s type=%s model=%s\n", 165 name, type, model); 166 } 167 } else { 168 D_PRINTF("node not added: type=%s portid =%d\n", type, portid); 169 } 170 171 if (curnode = di_child_node(di_node)) { 172 pnode->child = walk_di_tree(tree, pnode, curnode); 173 } 174 175 if (curnode = di_sibling_node(di_node)) { 176 if (board_node) { 177 return (walk_di_tree(tree, root, curnode)); 178 } else { 179 pnode->sibling = walk_di_tree(tree, root, curnode); 180 } 181 } 182 183 /* 184 * This check is needed in case the "board node" occurs at the 185 * end of the sibling chain as opposed to the middle or front. 186 */ 187 if (board_node) 188 return (NULL); 189 190 return (pnode); 191 } 192 193 /* 194 * Dump all the devinfo properties and then the obp properties for 195 * the specified devinfo node into the Prom_node structure. 196 */ 197 static void 198 dump_di_node(Prom_node *pnode, di_node_t di_node) 199 { 200 Prop *prop = NULL; /* tail of properties list */ 201 202 Prop *temp; /* newly allocated property */ 203 di_prop_t di_prop; 204 di_prom_prop_t p_prop; 205 int retval = 0; 206 int i; 207 208 /* clear out pointers in pnode */ 209 pnode->props = NULL; 210 211 D_PRINTF("\n\n ------- Dumping devinfo properties for node ------\n"); 212 213 /* 214 * get all the devinfo properties first 215 */ 216 for (di_prop = di_prop_next(di_node, DI_PROP_NIL); 217 di_prop != DI_PROP_NIL; 218 di_prop = di_prop_next(di_node, di_prop)) { 219 220 char *di_name; 221 void *di_data; 222 int di_ptype; 223 224 di_name = di_prop_name(di_prop); 225 if (di_name == (char *)NULL) 226 continue; 227 228 di_ptype = di_prop_type(di_prop); 229 D_PRINTF("DEVINFO Properties %s: ", di_name); 230 231 switch (di_ptype) { 232 int *int_val; 233 char *char_val; 234 case DI_PROP_TYPE_INT: 235 retval = di_prop_lookup_ints(DDI_DEV_T_ANY, 236 di_node, di_name, (int **)&di_data); 237 if (retval > 0) { 238 int_val = (int *)di_data; 239 D_PRINTF("0x%x\n", *int_val); 240 } 241 break; 242 case DI_PROP_TYPE_STRING: 243 retval = di_prop_lookup_strings(DDI_DEV_T_ANY, 244 di_node, di_name, (char **)&di_data); 245 if (retval > 0) { 246 char_val = (char *)di_data; 247 D_PRINTF("%s\n", char_val); 248 } 249 break; 250 case DI_PROP_TYPE_BYTE: 251 retval = di_prop_lookup_bytes(DDI_DEV_T_ANY, 252 di_node, di_name, (uchar_t **)&di_data); 253 if (retval > 0) { 254 char_val = (char *)di_data; 255 D_PRINTF("%s\n", char_val); 256 } 257 break; 258 case DI_PROP_TYPE_UNKNOWN: 259 retval = di_prop_lookup_bytes(DDI_DEV_T_ANY, 260 di_node, di_name, (uchar_t **)&di_data); 261 if (retval > 0) { 262 char_val = (char *)di_data; 263 D_PRINTF("%s\n", char_val); 264 } 265 break; 266 case DI_PROP_TYPE_BOOLEAN: 267 di_data = NULL; 268 retval = 1; 269 break; 270 default: 271 D_PRINTF(" Skipping property\n"); 272 retval = -1; 273 } 274 275 if (retval <= 0) 276 continue; 277 278 /* allocate space for the property */ 279 if ((temp = (Prop *) malloc(sizeof (Prop))) == NULL) { 280 perror("malloc"); 281 exit(1); 282 } 283 284 /* 285 * Given that we're using libdevinfo rather than OBP, 286 * the chances are that future accesses to di_name and 287 * di_data will be via temp->name.val_ptr and 288 * temp->value.val_ptr respectively. However, this may 289 * not be the case, so we have to suitably fill in 290 * temp->name.opp and temp->value.opp. 291 * 292 * di_name is char * and non-NULL if we've made it to 293 * here, so we can simply point 294 * temp->name.opp.oprom_array to temp->name.val_ptr. 295 * 296 * di_data could be NULL, char * or int * at this point. 297 * If it's non-NULL, a 1st char of '\0' indicates int *. 298 * We thus set temp->value.opp.oprom_node[] (although 299 * interest in any element other than 0 is rare, all 300 * elements must be set to ensure compatibility with 301 * OBP), and holds_array is set to 0. 302 * 303 * If di_data is NULL, or the 1st char is not '\0', we set 304 * temp->value.opp.oprom_array. If di_ptype is 305 * DI_PROP_TYPE_BOOLEAN, holds_array is set to 0, else it 306 * is set to 1. 307 */ 308 temp->name.val_ptr = (void *)di_name; 309 temp->name.opp.oprom_array = temp->name.val_ptr; 310 temp->name.opp.holds_array = 1; 311 312 temp->value.val_ptr = (void *)di_data; 313 if ((di_data != NULL) && (*((char *)di_data) == '\0')) { 314 for (i = 0; i < OPROM_NODE_SIZE; i++) 315 temp->value.opp.oprom_node[i] = *((int *)di_data+i); 316 317 temp->value.opp.holds_array = 0; 318 } else { 319 temp->value.opp.oprom_array = temp->value.val_ptr; 320 if (di_ptype == DI_PROP_TYPE_BOOLEAN) 321 temp->value.opp.holds_array = 0; 322 else 323 temp->value.opp.holds_array = 1; 324 } 325 326 temp->size = retval; 327 328 /* everything worked so link the property list */ 329 if (pnode->props == NULL) 330 pnode->props = temp; 331 else if (prop != NULL) 332 prop->next = temp; 333 prop = temp; 334 prop->next = NULL; 335 } 336 337 /* 338 * Then get all the OBP properties. 339 */ 340 for (p_prop = di_prom_prop_next(ph, di_node, DI_PROM_PROP_NIL); 341 p_prop != DI_PROM_PROP_NIL; 342 p_prop = di_prom_prop_next(ph, di_node, p_prop)) { 343 344 char *p_name; 345 unsigned char *p_data; 346 347 p_name = di_prom_prop_name(p_prop); 348 if (p_name == (char *)NULL) 349 retval = -1; 350 else 351 retval = di_prom_prop_data(p_prop, &p_data); 352 353 if (retval <= 0) 354 continue; 355 356 /* allocate space for the property */ 357 if ((temp = (Prop *) malloc(sizeof (Prop))) == NULL) { 358 perror("malloc"); 359 exit(1); 360 } 361 362 /* 363 * As above, p_name is char * and non-NULL if we've made 364 * it to here, so we can simply point 365 * temp->name.opp.oprom_array to temp->name.val_ptr. 366 * 367 * p_data could be NULL, a character or a number at this 368 * point. If it's non-NULL, a 1st char of '\0' indicates a 369 * number, so we set temp->value.opp.oprom_node[] (again 370 * setting every element to ensure OBP compatibility). 371 * These assignments create a lint error, hence the LINTED 372 * comment. 373 * 374 * If p_data is NULL, or the 1st char is not '\0', we set 375 * temp->value.opp.oprom_array. 376 */ 377 temp->name.val_ptr = (void *)p_name; 378 temp->name.opp.oprom_array = temp->name.val_ptr; 379 temp->name.opp.holds_array = 1; 380 381 temp->value.val_ptr = (void *)p_data; 382 if ((p_data != NULL) && (*p_data == '\0')) { 383 for (i = 0; i < OPROM_NODE_SIZE; i++) 384 /* LINTED */ 385 temp->value.opp.oprom_node[i] = *((int *)p_data+i); 386 387 temp->value.opp.holds_array = 0; 388 } else { 389 temp->value.opp.oprom_array = temp->value.val_ptr; 390 temp->value.opp.holds_array = 1; 391 } 392 393 temp->size = retval; 394 395 /* everything worked so link the property list */ 396 if (pnode->props == NULL) { 397 pnode->props = temp; 398 } else if (prop != NULL) { 399 prop->next = temp; 400 } 401 prop = temp; 402 prop->next = NULL; 403 } 404 } 405 406 /* 407 * Used in place of do_prominfo() when a platform wants to use 408 * libdevinfo for getting the device tree instead of OBP. 409 */ 410 int 411 do_devinfo(int syserrlog, char *pgname, int log_flag, int prt_flag) 412 { 413 Sys_tree sys_tree; /* system information */ 414 Prom_node *root_node; /* root node of OBP device tree */ 415 di_node_t di_root_node; /* root of the devinfo tree */ 416 struct system_kstat_data sys_kstat; /* kstats for non-OBP data */ 417 int retval = -1; 418 419 /* set the global flags */ 420 progname = pgname; 421 logging = log_flag; 422 print_flag = prt_flag; 423 424 /* set the the system tree fields */ 425 sys_tree.sys_mem = NULL; 426 sys_tree.boards = NULL; 427 sys_tree.bd_list = NULL; 428 sys_tree.board_cnt = 0; 429 430 /* 431 * create a snapshot of the kernel device tree 432 * and return a handle to it. 433 */ 434 if ((di_root_node = di_init("/", DINFOCPYALL)) == DI_NODE_NIL) { 435 exit(_error("di_init() failed")); 436 } 437 438 /* 439 * create a handle to the PROM device tree. 440 */ 441 if ((ph = di_prom_init()) == NULL) { 442 exit(_error("di_prom_init() failed")); 443 } 444 445 /* 446 * walk the devinfo tree and build up a list of all 447 * nodes and properties. 448 */ 449 root_node = walk_di_tree(&sys_tree, NULL, di_root_node); 450 451 /* resolve the board types now */ 452 resolve_board_types(&sys_tree); 453 454 read_sun4u_kstats(&sys_tree, &sys_kstat); 455 retval = display(&sys_tree, root_node, &sys_kstat, syserrlog); 456 457 di_fini(di_root_node); 458 di_prom_fini(ph); 459 return (retval); 460 } 461 462 /* 463 * check to see if the name shows up in the compatible array 464 */ 465 static int 466 match_compatible_name(char *compatible_array, int n_names, char *name) 467 { 468 int i, ret = 0; 469 470 /* parse the compatible list */ 471 for (i = 0; i < n_names; i++) { 472 if (strcmp(compatible_array, name) == 0) { 473 ret = 1; 474 break; 475 } 476 compatible_array += strlen(compatible_array) + 1; 477 } 478 return (ret); 479 } 480