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 2005 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 <string.h> 31 #include <stdlib.h> 32 #include <unistd.h> 33 #include <sys/param.h> 34 #include <errno.h> 35 #include <sys/types.h> 36 #include <dirent.h> 37 #include <libdevinfo.h> 38 #include <fcntl.h> 39 #include <sys/types.h> 40 #include <sys/stat.h> 41 #include <sys/pci.h> 42 #include <sys/biosdisk.h> 43 44 45 /* 46 * structure used for searching device tree for a node matching 47 * pci bus/dev/fn 48 */ 49 typedef struct pcibdf { 50 int busnum; 51 int devnum; 52 int funcnum; 53 di_node_t di_node; 54 } pcibdf_t; 55 56 /* 57 * structure used for searching device tree for a node matching 58 * USB serial number. 59 */ 60 typedef struct { 61 uint64_t serialno; 62 di_node_t node; 63 } usbser_t; 64 65 /* 66 * structure for holding the mapping info 67 */ 68 typedef struct { 69 int disklist_index; /* index to disk_list of the mapped path */ 70 int matchcount; /* number of matches per this device number */ 71 } mapinfo_t; 72 73 #define DEVFS_PREFIX "/devices" 74 #define DISKS_LIST_INCR 20 /* increment for resizing disk_list */ 75 76 #define BIOSPROPNAME_TMPL "biosdev-0x%x" 77 #define BIOSPROPNAME_TMPL_LEN 13 78 #define BIOSDEV_NUM 8 79 #define STARTING_DRVNUM 0x80 80 81 /* 82 * array to hold mappings. Element at index X corresponds to BIOS device 83 * number 0x80 + X 84 */ 85 static mapinfo_t mapinfo[BIOSDEV_NUM]; 86 87 /* 88 * Cache copy of kernel device tree snapshot root handle, includes devices 89 * that are detached 90 */ 91 static di_node_t root_node = DI_NODE_NIL; 92 93 /* 94 * kernel device tree snapshot with currently attached devices. Detached 95 * devices are not included. 96 */ 97 static di_node_t root_allnode = DI_NODE_NIL; 98 99 /* 100 * handle to retrieve prom properties 101 */ 102 103 static di_prom_handle_t prom_hdl = DI_PROM_HANDLE_NIL; 104 105 static char **disk_list = NULL; /* array of physical device pathnames */ 106 static int disk_list_len = 0; /* length of disk_list */ 107 static int disk_list_valid = 0; /* number of valid entries in disk_list */ 108 109 static int debug = 0; /* used for enabling debug output */ 110 111 112 /* Local function prototypes */ 113 static void new_disk_list_entry(di_node_t node); 114 static int find_disks_callback(di_node_t node, di_minor_t minor, void *arg); 115 static void build_disk_list(); 116 static void free_disks(); 117 static int matchpcibdf(di_node_t node, void *arg); 118 static int matchusbserial(di_node_t node, void *arg); 119 static di_node_t search_tree_forpcibdf(int bus, int dev, int fn); 120 static int match_edd(biosdev_data_t *bd); 121 static int match_first_block(biosdev_data_t *bd); 122 static void cleanup_and_exit(int); 123 static int find_path_index_in_disk_list(char *path); 124 125 126 static void 127 new_disk_list_entry(di_node_t node) 128 { 129 size_t newsize; 130 char **newlist; 131 int newlen; 132 char *devfspath; 133 134 if (disk_list_valid >= disk_list_len) { 135 /* valid should never really be larger than len */ 136 /* if they are equal we need to init or realloc */ 137 newlen = disk_list_len + DISKS_LIST_INCR; 138 newsize = newlen * sizeof (*disk_list); 139 140 newlist = (char **)realloc(disk_list, newsize); 141 if (newlist == NULL) { 142 (void) printf("realloc failed to resize disk table\n"); 143 cleanup_and_exit(1); 144 } 145 disk_list = newlist; 146 disk_list_len = newlen; 147 } 148 149 devfspath = di_devfs_path(node); 150 disk_list[disk_list_valid] = devfspath; 151 if (debug) 152 (void) printf("adding %s\n", devfspath); 153 disk_list_valid++; 154 } 155 156 /* ARGSUSED */ 157 static int 158 find_disks_callback(di_node_t node, di_minor_t minor, void *arg) 159 { 160 char *minortype; 161 162 if (di_minor_spectype(minor) == S_IFCHR) { 163 minortype = di_minor_nodetype(minor); 164 165 /* exclude CD's */ 166 if (strncmp(minortype, DDI_NT_CD, sizeof (DDI_NT_CD) - 1) != 0) 167 /* only take p0 raw device */ 168 if (strcmp(di_minor_name(minor), "q,raw") == 0) 169 new_disk_list_entry(node); 170 } 171 return (DI_WALK_CONTINUE); 172 } 173 174 static void 175 build_disk_list() 176 { 177 int ret; 178 ret = di_walk_minor(root_node, DDI_NT_BLOCK, 0, NULL, 179 find_disks_callback); 180 if (ret != 0) { 181 (void) fprintf(stderr, "di_walk_minor failed errno %d\n", 182 errno); 183 cleanup_and_exit(1); 184 } 185 } 186 187 static void 188 free_disks() 189 { 190 int i; 191 192 if (disk_list) { 193 for (i = 0; i < disk_list_valid; i++) 194 di_devfs_path_free(disk_list[i]); 195 196 free(disk_list); 197 } 198 } 199 200 static int 201 matchpcibdf(di_node_t node, void *arg) 202 { 203 pcibdf_t *pbp; 204 int len; 205 uint32_t regval; 206 uint32_t busnum, funcnum, devicenum; 207 char *devtype; 208 uint32_t *regbuf = NULL; 209 di_node_t parentnode; 210 211 pbp = (pcibdf_t *)arg; 212 213 parentnode = di_parent_node(node); 214 215 len = di_prop_lookup_strings(DDI_DEV_T_ANY, parentnode, 216 "device_type", (char **)&devtype); 217 218 if ((len <= 0) || (strcmp(devtype, "pci") != 0)) 219 return (DI_WALK_CONTINUE); 220 221 len = di_prop_lookup_ints(DDI_DEV_T_ANY, node, "reg", 222 (int **)®buf); 223 224 if (len <= 0) { 225 /* Try PROM property */ 226 len = di_prom_prop_lookup_ints(prom_hdl, node, "reg", 227 (int **)®buf); 228 } 229 230 231 if (len > 0) { 232 regval = regbuf[0]; 233 234 busnum = PCI_REG_BUS_G(regval); 235 devicenum = PCI_REG_DEV_G(regval); 236 funcnum = PCI_REG_FUNC_G(regval); 237 238 if ((busnum == pbp->busnum) && 239 (devicenum == pbp->devnum) && 240 (funcnum == pbp->funcnum)) { 241 /* found it */ 242 pbp->di_node = node; 243 return (DI_WALK_TERMINATE); 244 } 245 } 246 247 return (DI_WALK_CONTINUE); 248 } 249 250 static di_node_t 251 search_tree_forpcibdf(int bus, int dev, int fn) 252 { 253 pcibdf_t pb; 254 pb.busnum = bus; 255 pb.devnum = dev; 256 pb.funcnum = fn; 257 pb.di_node = DI_NODE_NIL; 258 259 (void) di_walk_node(root_node, DI_WALK_CLDFIRST, &pb, matchpcibdf); 260 return (pb.di_node); 261 262 } 263 264 static int 265 matchusbserial(di_node_t node, void *arg) 266 { 267 int len; 268 char *serialp; 269 usbser_t *usbsp; 270 271 usbsp = (usbser_t *)arg; 272 273 len = di_prop_lookup_bytes(DDI_DEV_T_ANY, node, "usb-serialno", 274 (uchar_t **)&serialp); 275 276 if ((len > 0) && (strcmp((char *)&usbsp->serialno, serialp) == 0)) { 277 usbsp->node = node; 278 return (DI_WALK_TERMINATE); 279 } 280 return (DI_WALK_CONTINUE); 281 } 282 283 static di_node_t 284 match_usb(di_node_t node, uint64_t serialno) 285 { 286 287 usbser_t usbs; 288 289 usbs.serialno = serialno; 290 usbs.node = DI_NODE_NIL; 291 292 (void) di_walk_node(node, DI_WALK_CLDFIRST, &usbs, matchusbserial); 293 return (usbs.node); 294 } 295 296 297 static int 298 find_path_index_in_disk_list(char *path) 299 { 300 int i; 301 for (i = 0; i < disk_list_valid; i++) 302 if (strcmp(disk_list[i], path) == 0) { 303 return (i); 304 } 305 return (-1); 306 } 307 308 309 /* 310 * Construct a physical device pathname from EDD and verify the 311 * path exists. Return the index of in disk_list for the mapped 312 * path on success, -1 on failure. 313 */ 314 static int 315 match_edd(biosdev_data_t *bdata) 316 { 317 di_node_t node; 318 char *devfspath; 319 fn48_t *bd; 320 int index; 321 char path[MAXPATHLEN]; 322 323 if (!bdata->edd_valid) { 324 if (debug) 325 (void) printf("edd not valid\n"); 326 return (-1); 327 } 328 329 bd = &bdata->fn48_dev_params; 330 331 if (bd->magic != 0xBEDD || bd->pathinfo_len == 0) { 332 /* EDD extensions for devicepath not present */ 333 if (debug) 334 (void) printf("magic not valid %x pathinfolen %d\n", 335 bd->magic, bd->pathinfo_len); 336 return (-1); 337 } 338 339 /* we handle only PCI scsi, ata or sata for now */ 340 if (strncmp(bd->bustype, "PCI", 3) != 0) { 341 if (debug) 342 (void) printf("was not pci %s\n", bd->bustype); 343 return (-1); 344 } 345 if (debug) 346 (void) printf("match_edd bdf %d %d %d\n", 347 bd->interfacepath.pci.bus, 348 bd->interfacepath.pci.device, 349 bd->interfacepath.pci.function); 350 351 /* look into devinfo tree and find a node with matching pci b/d/f */ 352 node = search_tree_forpcibdf(bd->interfacepath.pci.bus, 353 bd->interfacepath.pci.device, 354 bd->interfacepath.pci.function); 355 356 if (node == DI_NODE_NIL) { 357 if (debug) 358 (void) printf(" could not find a node in tree " 359 "matching bdf\n"); 360 return (-1); 361 } 362 363 /* construct a path */ 364 devfspath = di_devfs_path(node); 365 366 if (debug) 367 (void) printf("interface type %s\n", bd->interface_type); 368 369 if (strncmp(bd->interface_type, "SCSI", 4) == 0) { 370 371 /* at this time sd doesnot support luns greater than uchar_t */ 372 (void) snprintf(path, MAXPATHLEN, "%s/sd@%d,%d", devfspath, 373 bd->devicepath.scsi.target, bd->devicepath.scsi.lun_lo); 374 375 } else if (strncmp(bd->interface_type, "ATAPI", 5) == 0) { 376 377 (void) snprintf(path, MAXPATHLEN, "%s/ide@%d/sd@%d,0", 378 devfspath, bd->interfacepath.pci.channel, 379 bd->devicepath.ata.chan); 380 381 } else if (strncmp(bd->interface_type, "ATA", 3) == 0) { 382 383 (void) snprintf(path, MAXPATHLEN, "%s/ide@%d/cmdk@%d,0", 384 devfspath, bd->interfacepath.pci.channel, 385 bd->devicepath.ata.chan); 386 387 } else if (strncmp(bd->interface_type, "SATA", 4) == 0) { 388 389 (void) snprintf(path, MAXPATHLEN, "%s/ide@%d/cmdk@%d,0", 390 devfspath, bd->interfacepath.pci.channel, 391 bd->devicepath.ata.chan); 392 393 } else if (strncmp(bd->interface_type, "USB", 3) == 0) { 394 (void) printf("USB\n"); 395 node = match_usb(node, bd->devicepath.usb.usb_serial_id); 396 if (node != DI_NODE_NIL) { 397 if (debug) 398 (void) printf("usb path %s\n", 399 di_devfs_path(node)); 400 (void) snprintf(path, MAXPATHLEN, "%s", devfspath); 401 } else 402 return (-1); 403 } else { 404 if (debug) 405 (void) printf("sorry not supported interface %s\n", 406 bd->interface_type); 407 return (-1); 408 } 409 410 index = find_path_index_in_disk_list(path); 411 if (index >= 0) { 412 return (index); 413 } 414 415 return (-1); 416 } 417 418 /* 419 * For each disk in list of disks, compare the first block with the 420 * one from bdd. On the first match, return the index of path in 421 * disk_list. If none matched return -1. 422 */ 423 static int 424 match_first_block(biosdev_data_t *bd) 425 { 426 427 char diskpath[MAXPATHLEN]; 428 int fd; 429 char buf[512]; 430 ssize_t num_read; 431 int i; 432 433 if (!bd->first_block_valid) 434 return (-1); 435 436 for (i = 0; i < disk_list_valid; i++) { 437 (void) snprintf(diskpath, MAXPATHLEN, "%s/%s:q,raw", 438 DEVFS_PREFIX, disk_list[i]); 439 fd = open(diskpath, O_RDONLY); 440 if (fd < 0) { 441 (void) fprintf(stderr, "opening %s failed errno %d\n", 442 diskpath, errno); 443 continue; 444 } 445 num_read = read(fd, buf, 512); 446 if (num_read != 512) { 447 (void) printf("read only %d bytes from %s\n", num_read, 448 diskpath); 449 continue; 450 } 451 452 if (memcmp(buf, bd->first_block, 512) == 0) { 453 /* found it */ 454 return (i); 455 } 456 } 457 return (-1); 458 } 459 460 461 static void 462 cleanup_and_exit(int exitcode) 463 { 464 465 free_disks(); 466 467 if (root_node != DI_NODE_NIL) 468 di_fini(root_node); 469 470 if (root_allnode != DI_NODE_NIL) 471 di_fini(root_allnode); 472 473 if (prom_hdl != DI_PROM_HANDLE_NIL) 474 di_prom_fini(prom_hdl); 475 exit(exitcode); 476 477 } 478 479 480 int 481 main(int argc, char *argv[]) 482 { 483 biosdev_data_t *biosdata; 484 int len, i, c, j; 485 int matchedindex = -1; 486 char biospropname[BIOSPROPNAME_TMPL_LEN]; 487 int totalmatches = 0; 488 489 490 while ((c = getopt(argc, argv, "d")) != -1) { 491 switch (c) { 492 case 'd': 493 debug = 1; 494 break; 495 default: 496 (void) printf("unknown option %c\n", c); 497 exit(1); 498 } 499 } 500 501 if ((prom_hdl = di_prom_init()) == DI_PROM_HANDLE_NIL) { 502 (void) fprintf(stderr, "di_prom_init failed\n"); 503 cleanup_and_exit(1); 504 } 505 506 if ((root_node = di_init("/", DINFOCACHE)) == DI_NODE_NIL) { 507 (void) fprintf(stderr, "di_init failed\n"); 508 cleanup_and_exit(1); 509 } 510 511 if ((root_allnode = di_init("/", DINFOCPYALL)) == DI_NODE_NIL) { 512 (void) fprintf(stderr, "di_init failed\n"); 513 cleanup_and_exit(1); 514 } 515 516 (void) memset(mapinfo, 0, sizeof (mapinfo)); 517 518 /* get a list of all disks in the system */ 519 build_disk_list(); 520 521 /* for each property try to match with a disk */ 522 for (i = 0; i < BIOSDEV_NUM; i++) { 523 524 (void) snprintf((char *)biospropname, BIOSPROPNAME_TMPL_LEN, 525 BIOSPROPNAME_TMPL, i + STARTING_DRVNUM); 526 527 len = di_prop_lookup_bytes(DDI_DEV_T_ANY, root_allnode, 528 biospropname, (uchar_t **)&biosdata); 529 530 if (len <= 0) { 531 continue; 532 } 533 534 if (debug) 535 (void) printf("matching %s\n", biospropname); 536 537 matchedindex = match_edd(biosdata); 538 539 if (matchedindex == -1) { 540 matchedindex = match_first_block(biosdata); 541 if (debug && matchedindex != -1) 542 (void) printf("matched first block\n"); 543 } else if (debug) 544 (void) printf("matched thru edd\n"); 545 546 if (matchedindex != -1) { 547 mapinfo[i].disklist_index = matchedindex; 548 mapinfo[i].matchcount++; 549 if (debug) 550 (void) printf("0x%x %s\n", i + STARTING_DRVNUM, 551 disk_list[matchedindex]); 552 for (j = 0; j < i; j++) { 553 if (mapinfo[j].matchcount > 0 && 554 mapinfo[j].disklist_index == matchedindex) { 555 mapinfo[j].matchcount++; 556 mapinfo[i].matchcount++; 557 } 558 } 559 560 } else if (debug) 561 (void) fprintf(stderr, "Could not match %s\n", 562 biospropname); 563 } 564 565 for (i = 0; i < BIOSDEV_NUM; i++) { 566 if (mapinfo[i].matchcount == 1) { 567 (void) printf("0x%x %s\n", i + STARTING_DRVNUM, 568 disk_list[mapinfo[i].disklist_index]); 569 totalmatches++; 570 } else if (debug && mapinfo[i].matchcount > 1) { 571 (void) printf("0x%x %s matchcount %d\n", 572 i + STARTING_DRVNUM, 573 disk_list[mapinfo[i].disklist_index], 574 mapinfo[i].matchcount); 575 } 576 } 577 578 if (totalmatches == 0) { 579 (void) fprintf(stderr, "biosdev: Could not match any!!\n"); 580 cleanup_and_exit(1); 581 } 582 583 cleanup_and_exit(0); 584 /* NOTREACHED */ 585 return (0); 586 } 587