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 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 <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) || 219 ((strcmp(devtype, "pci") != 0) && (strcmp(devtype, "pciex") != 0))) 220 return (DI_WALK_CONTINUE); 221 222 len = di_prop_lookup_ints(DDI_DEV_T_ANY, node, "reg", 223 (int **)®buf); 224 225 if (len <= 0) { 226 /* Try PROM property */ 227 len = di_prom_prop_lookup_ints(prom_hdl, node, "reg", 228 (int **)®buf); 229 } 230 231 232 if (len > 0) { 233 regval = regbuf[0]; 234 235 busnum = PCI_REG_BUS_G(regval); 236 devicenum = PCI_REG_DEV_G(regval); 237 funcnum = PCI_REG_FUNC_G(regval); 238 239 if ((busnum == pbp->busnum) && 240 (devicenum == pbp->devnum) && 241 (funcnum == pbp->funcnum)) { 242 /* found it */ 243 pbp->di_node = node; 244 return (DI_WALK_TERMINATE); 245 } 246 } 247 248 return (DI_WALK_CONTINUE); 249 } 250 251 static di_node_t 252 search_tree_forpcibdf(int bus, int dev, int fn) 253 { 254 pcibdf_t pb; 255 pb.busnum = bus; 256 pb.devnum = dev; 257 pb.funcnum = fn; 258 pb.di_node = DI_NODE_NIL; 259 260 (void) di_walk_node(root_node, DI_WALK_CLDFIRST, &pb, matchpcibdf); 261 return (pb.di_node); 262 263 } 264 265 static int 266 matchusbserial(di_node_t node, void *arg) 267 { 268 int len; 269 char *serialp; 270 usbser_t *usbsp; 271 272 usbsp = (usbser_t *)arg; 273 274 len = di_prop_lookup_bytes(DDI_DEV_T_ANY, node, "usb-serialno", 275 (uchar_t **)&serialp); 276 277 if ((len > 0) && (strcmp((char *)&usbsp->serialno, serialp) == 0)) { 278 usbsp->node = node; 279 return (DI_WALK_TERMINATE); 280 } 281 return (DI_WALK_CONTINUE); 282 } 283 284 static di_node_t 285 match_usb(di_node_t node, uint64_t serialno) 286 { 287 288 usbser_t usbs; 289 290 usbs.serialno = serialno; 291 usbs.node = DI_NODE_NIL; 292 293 (void) di_walk_node(node, DI_WALK_CLDFIRST, &usbs, matchusbserial); 294 return (usbs.node); 295 } 296 297 298 static int 299 find_path_index_in_disk_list(char *path) 300 { 301 int i; 302 for (i = 0; i < disk_list_valid; i++) 303 if (strcmp(disk_list[i], path) == 0) { 304 return (i); 305 } 306 return (-1); 307 } 308 309 310 /* 311 * Construct a physical device pathname from EDD and verify the 312 * path exists. Return the index of in disk_list for the mapped 313 * path on success, -1 on failure. 314 */ 315 static int 316 match_edd(biosdev_data_t *bdata) 317 { 318 di_node_t node; 319 char *devfspath; 320 fn48_t *bd; 321 int index; 322 char path[MAXPATHLEN]; 323 324 if (!bdata->edd_valid) { 325 if (debug) 326 (void) printf("edd not valid\n"); 327 return (-1); 328 } 329 330 bd = &bdata->fn48_dev_params; 331 332 if (bd->magic != 0xBEDD || bd->pathinfo_len == 0) { 333 /* EDD extensions for devicepath not present */ 334 if (debug) 335 (void) printf("magic not valid %x pathinfolen %d\n", 336 bd->magic, bd->pathinfo_len); 337 return (-1); 338 } 339 340 /* we handle only PCI scsi, ata or sata for now */ 341 if (strncmp(bd->bustype, "PCI", 3) != 0) { 342 if (debug) 343 (void) printf("was not pci %s\n", bd->bustype); 344 return (-1); 345 } 346 if (debug) 347 (void) printf("match_edd bdf %d %d %d\n", 348 bd->interfacepath.pci.bus, 349 bd->interfacepath.pci.device, 350 bd->interfacepath.pci.function); 351 352 /* look into devinfo tree and find a node with matching pci b/d/f */ 353 node = search_tree_forpcibdf(bd->interfacepath.pci.bus, 354 bd->interfacepath.pci.device, 355 bd->interfacepath.pci.function); 356 357 if (node == DI_NODE_NIL) { 358 if (debug) 359 (void) printf(" could not find a node in tree " 360 "matching bdf\n"); 361 return (-1); 362 } 363 364 /* construct a path */ 365 devfspath = di_devfs_path(node); 366 367 if (debug) 368 (void) printf("interface type %s\n", bd->interface_type); 369 370 if (strncmp(bd->interface_type, "SCSI", 4) == 0) { 371 372 /* at this time sd doesnot support luns greater than uchar_t */ 373 (void) snprintf(path, MAXPATHLEN, "%s/sd@%x,%x", devfspath, 374 bd->devicepath.scsi.target, bd->devicepath.scsi.lun_lo); 375 376 } else if (strncmp(bd->interface_type, "ATAPI", 5) == 0) { 377 378 (void) snprintf(path, MAXPATHLEN, "%s/ide@%d/sd@%x,0", 379 devfspath, bd->interfacepath.pci.channel, 380 bd->devicepath.ata.chan); 381 382 } else if (strncmp(bd->interface_type, "ATA", 3) == 0) { 383 384 (void) snprintf(path, MAXPATHLEN, "%s/ide@%d/cmdk@%x,0", 385 devfspath, bd->interfacepath.pci.channel, 386 bd->devicepath.ata.chan); 387 388 } else if (strncmp(bd->interface_type, "SATA", 4) == 0) { 389 390 (void) snprintf(path, MAXPATHLEN, "%s/ide@%d/cmdk@%x,0", 391 devfspath, bd->interfacepath.pci.channel, 392 bd->devicepath.ata.chan); 393 394 } else if (strncmp(bd->interface_type, "USB", 3) == 0) { 395 (void) printf("USB\n"); 396 node = match_usb(node, bd->devicepath.usb.usb_serial_id); 397 if (node != DI_NODE_NIL) { 398 if (debug) 399 (void) printf("usb path %s\n", 400 di_devfs_path(node)); 401 (void) snprintf(path, MAXPATHLEN, "%s", devfspath); 402 } else 403 return (-1); 404 } else { 405 if (debug) 406 (void) printf("sorry not supported interface %s\n", 407 bd->interface_type); 408 return (-1); 409 } 410 411 index = find_path_index_in_disk_list(path); 412 if (index >= 0) { 413 return (index); 414 } 415 416 return (-1); 417 } 418 419 /* 420 * For each disk in list of disks, compare the first block with the 421 * one from bdd. On the first match, return the index of path in 422 * disk_list. If none matched return -1. 423 */ 424 static int 425 match_first_block(biosdev_data_t *bd) 426 { 427 428 char diskpath[MAXPATHLEN]; 429 int fd; 430 char buf[512]; 431 ssize_t num_read; 432 int i; 433 434 if (!bd->first_block_valid) 435 return (-1); 436 437 for (i = 0; i < disk_list_valid; i++) { 438 (void) snprintf(diskpath, MAXPATHLEN, "%s/%s:q,raw", 439 DEVFS_PREFIX, disk_list[i]); 440 fd = open(diskpath, O_RDONLY); 441 if (fd < 0) { 442 (void) fprintf(stderr, "opening %s failed errno %d\n", 443 diskpath, errno); 444 continue; 445 } 446 num_read = read(fd, buf, 512); 447 if (num_read != 512) { 448 (void) printf("read only %d bytes from %s\n", num_read, 449 diskpath); 450 continue; 451 } 452 453 if (memcmp(buf, bd->first_block, 512) == 0) { 454 /* found it */ 455 return (i); 456 } 457 } 458 return (-1); 459 } 460 461 462 static void 463 cleanup_and_exit(int exitcode) 464 { 465 466 free_disks(); 467 468 if (root_node != DI_NODE_NIL) 469 di_fini(root_node); 470 471 if (root_allnode != DI_NODE_NIL) 472 di_fini(root_allnode); 473 474 if (prom_hdl != DI_PROM_HANDLE_NIL) 475 di_prom_fini(prom_hdl); 476 exit(exitcode); 477 478 } 479 480 481 int 482 main(int argc, char *argv[]) 483 { 484 biosdev_data_t *biosdata; 485 int len, i, c, j; 486 int matchedindex = -1; 487 char biospropname[BIOSPROPNAME_TMPL_LEN]; 488 int totalmatches = 0; 489 490 491 while ((c = getopt(argc, argv, "d")) != -1) { 492 switch (c) { 493 case 'd': 494 debug = 1; 495 break; 496 default: 497 (void) printf("unknown option %c\n", c); 498 exit(1); 499 } 500 } 501 502 if ((prom_hdl = di_prom_init()) == DI_PROM_HANDLE_NIL) { 503 (void) fprintf(stderr, "di_prom_init failed\n"); 504 cleanup_and_exit(1); 505 } 506 507 if ((root_node = di_init("/", DINFOCACHE)) == DI_NODE_NIL) { 508 (void) fprintf(stderr, "di_init failed\n"); 509 cleanup_and_exit(1); 510 } 511 512 if ((root_allnode = di_init("/", DINFOCPYALL)) == DI_NODE_NIL) { 513 (void) fprintf(stderr, "di_init failed\n"); 514 cleanup_and_exit(1); 515 } 516 517 (void) memset(mapinfo, 0, sizeof (mapinfo)); 518 519 /* get a list of all disks in the system */ 520 build_disk_list(); 521 522 /* for each property try to match with a disk */ 523 for (i = 0; i < BIOSDEV_NUM; i++) { 524 525 (void) snprintf((char *)biospropname, BIOSPROPNAME_TMPL_LEN, 526 BIOSPROPNAME_TMPL, i + STARTING_DRVNUM); 527 528 len = di_prop_lookup_bytes(DDI_DEV_T_ANY, root_allnode, 529 biospropname, (uchar_t **)&biosdata); 530 531 if (len <= 0) { 532 continue; 533 } 534 535 if (debug) 536 (void) printf("matching %s\n", biospropname); 537 538 matchedindex = match_edd(biosdata); 539 540 if (matchedindex == -1) { 541 matchedindex = match_first_block(biosdata); 542 if (debug && matchedindex != -1) 543 (void) printf("matched first block\n"); 544 } else if (debug) 545 (void) printf("matched thru edd\n"); 546 547 if (matchedindex != -1) { 548 mapinfo[i].disklist_index = matchedindex; 549 mapinfo[i].matchcount++; 550 if (debug) 551 (void) printf("0x%x %s\n", i + STARTING_DRVNUM, 552 disk_list[matchedindex]); 553 for (j = 0; j < i; j++) { 554 if (mapinfo[j].matchcount > 0 && 555 mapinfo[j].disklist_index == matchedindex) { 556 mapinfo[j].matchcount++; 557 mapinfo[i].matchcount++; 558 } 559 } 560 561 } else if (debug) 562 (void) fprintf(stderr, "Could not match %s\n", 563 biospropname); 564 } 565 566 for (i = 0; i < BIOSDEV_NUM; i++) { 567 if (mapinfo[i].matchcount == 1) { 568 (void) printf("0x%x %s\n", i + STARTING_DRVNUM, 569 disk_list[mapinfo[i].disklist_index]); 570 totalmatches++; 571 } else if (debug && mapinfo[i].matchcount > 1) { 572 (void) printf("0x%x %s matchcount %d\n", 573 i + STARTING_DRVNUM, 574 disk_list[mapinfo[i].disklist_index], 575 mapinfo[i].matchcount); 576 } 577 } 578 579 if (totalmatches == 0) { 580 (void) fprintf(stderr, "biosdev: Could not match any!!\n"); 581 cleanup_and_exit(1); 582 } 583 584 cleanup_and_exit(0); 585 /* NOTREACHED */ 586 return (0); 587 } 588