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 #include <stdio.h> 28 #include <stdlib.h> 29 #include <string.h> 30 #include <unistd.h> 31 #include <sys/systeminfo.h> 32 #include <sys/utsname.h> 33 #include <sys/openpromio.h> 34 #include <libdevinfo.h> 35 36 #include "pdevinfo.h" 37 #include "pdevinfo_sun4u.h" 38 #include "display.h" 39 #include "display_sun4u.h" 40 #include "libprtdiag.h" 41 42 /* 43 * This file contains the functions that are to be called when 44 * a platform wants to use libdevinfo for it's device information 45 * instead of OBP. This will allow prtdiag to support hot-plug 46 * events on platforms whose OBP doesn't get updated to reflect 47 * the hot-plug changes to the system. 48 */ 49 50 int do_devinfo(int syserrlog, char *pgname, int log_flag, 51 int prt_flag); 52 static void dump_di_node(Prom_node *pnode, di_node_t di_node); 53 static Prom_node *walk_di_tree(Sys_tree *tree, Prom_node *root, 54 di_node_t di_node); 55 static int match_compatible_name(char *, int, char *); 56 57 /* 58 * Global variables 59 */ 60 di_prom_handle_t ph; /* Handle for using di_prom interface */ 61 extern char *progname; 62 63 64 65 /* 66 * Used instead of the walk() function when a platform wants to 67 * walk libdevinfo's device tree instead of walking OBP's 68 * device tree. 69 */ 70 static Prom_node* 71 walk_di_tree(Sys_tree *tree, Prom_node *root, di_node_t di_node) 72 { 73 di_node_t curnode; 74 Prom_node *pnode; 75 char *name, *type, *model, *compatible_array; 76 int board_node = 0; 77 int *int_val; 78 int is_schizo = 0, n_names; 79 #ifdef DEBUG 80 int portid; 81 #endif 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 #ifdef DEBUG 119 if (int_val = (int *)get_prop_val(find_prop(pnode, "portid"))) 120 portid = *int_val; 121 else if ((strcmp(type, "cpu") == 0) && 122 (int_val = (int *)get_prop_val(find_prop(pnode->parent, "portid")))) 123 portid = *int_val; 124 else 125 portid = -1; 126 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 case DI_PROP_TYPE_INT: 233 retval = di_prop_lookup_ints(DDI_DEV_T_ANY, 234 di_node, di_name, (int **)&di_data); 235 if (retval > 0) { 236 D_PRINTF("0x%x\n", *(int *)di_data); 237 } 238 break; 239 case DI_PROP_TYPE_STRING: 240 retval = di_prop_lookup_strings(DDI_DEV_T_ANY, 241 di_node, di_name, (char **)&di_data); 242 if (retval > 0) { 243 D_PRINTF("%s\n", (char *)di_data); 244 } 245 break; 246 case DI_PROP_TYPE_BYTE: 247 retval = di_prop_lookup_bytes(DDI_DEV_T_ANY, 248 di_node, di_name, (uchar_t **)&di_data); 249 if (retval > 0) { 250 D_PRINTF("%s\n", (char *)di_data); 251 } 252 break; 253 case DI_PROP_TYPE_UNKNOWN: 254 retval = di_prop_lookup_bytes(DDI_DEV_T_ANY, 255 di_node, di_name, (uchar_t **)&di_data); 256 if (retval > 0) { 257 D_PRINTF("%s\n", (char *)di_data); 258 } 259 break; 260 case DI_PROP_TYPE_BOOLEAN: 261 di_data = NULL; 262 retval = 1; 263 break; 264 default: 265 D_PRINTF(" Skipping property\n"); 266 retval = -1; 267 } 268 269 if (retval <= 0) 270 continue; 271 272 /* allocate space for the property */ 273 if ((temp = (Prop *) malloc(sizeof (Prop))) == NULL) { 274 perror("malloc"); 275 exit(1); 276 } 277 278 /* 279 * Given that we're using libdevinfo rather than OBP, 280 * the chances are that future accesses to di_name and 281 * di_data will be via temp->name.val_ptr and 282 * temp->value.val_ptr respectively. However, this may 283 * not be the case, so we have to suitably fill in 284 * temp->name.opp and temp->value.opp. 285 * 286 * di_name is char * and non-NULL if we've made it to 287 * here, so we can simply point 288 * temp->name.opp.oprom_array to temp->name.val_ptr. 289 * 290 * di_data could be NULL, char * or int * at this point. 291 * If it's non-NULL, a 1st char of '\0' indicates int *. 292 * We thus set temp->value.opp.oprom_node[] (although 293 * interest in any element other than 0 is rare, all 294 * elements must be set to ensure compatibility with 295 * OBP), and holds_array is set to 0. 296 * 297 * If di_data is NULL, or the 1st char is not '\0', we set 298 * temp->value.opp.oprom_array. If di_ptype is 299 * DI_PROP_TYPE_BOOLEAN, holds_array is set to 0, else it 300 * is set to 1. 301 */ 302 temp->name.val_ptr = (void *)di_name; 303 temp->name.opp.oprom_array = temp->name.val_ptr; 304 temp->name.opp.holds_array = 1; 305 306 temp->value.val_ptr = (void *)di_data; 307 if ((di_data != NULL) && (*((char *)di_data) == '\0')) { 308 for (i = 0; i < OPROM_NODE_SIZE; i++) 309 temp->value.opp.oprom_node[i] = 310 *((int *)di_data+i); 311 312 temp->value.opp.holds_array = 0; 313 } else { 314 temp->value.opp.oprom_array = temp->value.val_ptr; 315 if (di_ptype == DI_PROP_TYPE_BOOLEAN) 316 temp->value.opp.holds_array = 0; 317 else 318 temp->value.opp.holds_array = 1; 319 } 320 321 temp->size = retval; 322 323 /* everything worked so link the property list */ 324 if (pnode->props == NULL) 325 pnode->props = temp; 326 else if (prop != NULL) 327 prop->next = temp; 328 prop = temp; 329 prop->next = NULL; 330 } 331 332 /* 333 * Then get all the OBP properties. 334 */ 335 for (p_prop = di_prom_prop_next(ph, di_node, DI_PROM_PROP_NIL); 336 p_prop != DI_PROM_PROP_NIL; 337 p_prop = di_prom_prop_next(ph, di_node, p_prop)) { 338 339 char *p_name; 340 unsigned char *p_data; 341 342 p_name = di_prom_prop_name(p_prop); 343 if (p_name == (char *)NULL) 344 retval = -1; 345 else 346 retval = di_prom_prop_data(p_prop, &p_data); 347 348 if (retval <= 0) 349 continue; 350 351 /* allocate space for the property */ 352 if ((temp = (Prop *) malloc(sizeof (Prop))) == NULL) { 353 perror("malloc"); 354 exit(1); 355 } 356 357 /* 358 * As above, p_name is char * and non-NULL if we've made 359 * it to here, so we can simply point 360 * temp->name.opp.oprom_array to temp->name.val_ptr. 361 * 362 * p_data could be NULL, a character or a number at this 363 * point. If it's non-NULL, a 1st char of '\0' indicates a 364 * number, so we set temp->value.opp.oprom_node[] (again 365 * setting every element to ensure OBP compatibility). 366 * These assignments create a lint error, hence the LINTED 367 * comment. 368 * 369 * If p_data is NULL, or the 1st char is not '\0', we set 370 * temp->value.opp.oprom_array. 371 */ 372 temp->name.val_ptr = (void *)p_name; 373 temp->name.opp.oprom_array = temp->name.val_ptr; 374 temp->name.opp.holds_array = 1; 375 376 temp->value.val_ptr = (void *)p_data; 377 if ((p_data != NULL) && (*p_data == '\0')) { 378 for (i = 0; i < OPROM_NODE_SIZE; i++) 379 temp->value.opp.oprom_node[i] = 380 *((int *)p_data+i); 381 382 temp->value.opp.holds_array = 0; 383 } else { 384 temp->value.opp.oprom_array = temp->value.val_ptr; 385 temp->value.opp.holds_array = 1; 386 } 387 388 temp->size = retval; 389 390 /* everything worked so link the property list */ 391 if (pnode->props == NULL) { 392 pnode->props = temp; 393 } else if (prop != NULL) { 394 prop->next = temp; 395 } 396 prop = temp; 397 prop->next = NULL; 398 } 399 } 400 401 /* 402 * Used in place of do_prominfo() when a platform wants to use 403 * libdevinfo for getting the device tree instead of OBP. 404 */ 405 int 406 do_devinfo(int syserrlog, char *pgname, int log_flag, int prt_flag) 407 { 408 Sys_tree sys_tree; /* system information */ 409 Prom_node *root_node; /* root node of OBP device tree */ 410 di_node_t di_root_node; /* root of the devinfo tree */ 411 struct system_kstat_data sys_kstat; /* kstats for non-OBP data */ 412 int retval = -1; 413 414 /* set the global flags */ 415 progname = pgname; 416 logging = log_flag; 417 print_flag = prt_flag; 418 419 /* set the the system tree fields */ 420 sys_tree.sys_mem = NULL; 421 sys_tree.boards = NULL; 422 sys_tree.bd_list = NULL; 423 sys_tree.board_cnt = 0; 424 425 /* 426 * create a snapshot of the kernel device tree 427 * and return a handle to it. 428 */ 429 if ((di_root_node = di_init("/", DINFOCPYALL)) == DI_NODE_NIL) { 430 exit(_error("di_init() failed")); 431 } 432 433 /* 434 * create a handle to the PROM device tree. 435 */ 436 if ((ph = di_prom_init()) == NULL) { 437 exit(_error("di_prom_init() failed")); 438 } 439 440 /* 441 * walk the devinfo tree and build up a list of all 442 * nodes and properties. 443 */ 444 root_node = walk_di_tree(&sys_tree, NULL, di_root_node); 445 446 /* resolve the board types now */ 447 resolve_board_types(&sys_tree); 448 449 read_sun4u_kstats(&sys_tree, &sys_kstat); 450 retval = display(&sys_tree, root_node, &sys_kstat, syserrlog); 451 452 di_fini(di_root_node); 453 di_prom_fini(ph); 454 return (retval); 455 } 456 457 /* 458 * check to see if the name shows up in the compatible array 459 */ 460 static int 461 match_compatible_name(char *compatible_array, int n_names, char *name) 462 { 463 int i, ret = 0; 464 465 /* parse the compatible list */ 466 for (i = 0; i < n_names; i++) { 467 if (strcmp(compatible_array, name) == 0) { 468 ret = 1; 469 break; 470 } 471 compatible_array += strlen(compatible_array) + 1; 472 } 473 return (ret); 474 } 475