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 * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
24 */
25
26 #include <stdio.h>
27 #include <string.h>
28 #include <stdlib.h>
29 #include <unistd.h>
30 #include <sys/param.h>
31 #include <errno.h>
32 #include <sys/types.h>
33 #include <dirent.h>
34 #include <libdevinfo.h>
35 #include <fcntl.h>
36 #include <sys/types.h>
37 #include <sys/stat.h>
38 #include <sys/pci.h>
39 #include <sys/biosdisk.h>
40
41
42 /*
43 * structure used for searching device tree for a node matching
44 * pci bus/dev/fn
45 */
46 typedef struct pcibdf {
47 int busnum;
48 int devnum;
49 int funcnum;
50 di_node_t di_node;
51 } pcibdf_t;
52
53 /*
54 * structure used for searching device tree for a node matching
55 * USB serial number.
56 */
57 typedef struct {
58 uint64_t serialno;
59 di_node_t node;
60 } usbser_t;
61
62 /*
63 * structure for holding the mapping info
64 */
65 typedef struct {
66 int disklist_index; /* index to disk_list of the mapped path */
67 int matchcount; /* number of matches per this device number */
68 } mapinfo_t;
69
70 #define DEVFS_PREFIX "/devices"
71 #define DISKS_LIST_INCR 20 /* increment for resizing disk_list */
72
73 #define BIOSPROPNAME_TMPL "biosdev-0x%x"
74 #define BIOSPROPNAME_TMPL_LEN 13
75 #define BIOSDEV_NUM 8
76 #define STARTING_DRVNUM 0x80
77
78 /*
79 * array to hold mappings. Element at index X corresponds to BIOS device
80 * number 0x80 + X
81 */
82 static mapinfo_t mapinfo[BIOSDEV_NUM];
83
84 /*
85 * Cache copy of kernel device tree snapshot root handle, includes devices
86 * that are detached
87 */
88 static di_node_t root_node = DI_NODE_NIL;
89
90 /*
91 * kernel device tree snapshot with currently attached devices. Detached
92 * devices are not included.
93 */
94 static di_node_t root_allnode = DI_NODE_NIL;
95
96 /*
97 * handle to retrieve prom properties
98 */
99
100 static di_prom_handle_t prom_hdl = DI_PROM_HANDLE_NIL;
101
102 static char **disk_list = NULL; /* array of physical device pathnames */
103 static int disk_list_len = 0; /* length of disk_list */
104 static int disk_list_valid = 0; /* number of valid entries in disk_list */
105
106 static int debug = 0; /* used for enabling debug output */
107
108
109 /* Local function prototypes */
110 static void new_disk_list_entry(di_node_t node);
111 static int i_disktype(di_node_t node, di_minor_t minor, void *arg);
112 static void build_disk_list();
113 static int search_disklist_match_path(char *path);
114 static void free_disks();
115 static void cleanup_and_exit(int);
116
117 static int match_edd(biosdev_data_t *bd);
118 static int match_first_block(biosdev_data_t *bd);
119
120 static di_node_t search_tree_match_pcibdf(di_node_t node, int bus, int dev,
121 int fn);
122 static int i_match_pcibdf(di_node_t node, void *arg);
123
124 static di_node_t search_tree_match_usbserialno(di_node_t node,
125 uint64_t serialno);
126 static int i_match_usbserialno(di_node_t node, void *arg);
127
128 static di_node_t search_children_match_busaddr(di_node_t node,
129 char *matchbusaddr);
130
131
132
133 static void
new_disk_list_entry(di_node_t node)134 new_disk_list_entry(di_node_t node)
135 {
136 size_t newsize;
137 char **newlist;
138 int newlen;
139 char *devfspath;
140
141 if (disk_list_valid >= disk_list_len) {
142 /* valid should never really be larger than len */
143 /* if they are equal we need to init or realloc */
144 newlen = disk_list_len + DISKS_LIST_INCR;
145 newsize = newlen * sizeof (*disk_list);
146
147 newlist = (char **)realloc(disk_list, newsize);
148 if (newlist == NULL) {
149 (void) printf("realloc failed to resize disk table\n");
150 cleanup_and_exit(1);
151 }
152 disk_list = newlist;
153 disk_list_len = newlen;
154 }
155
156 devfspath = di_devfs_path(node);
157 disk_list[disk_list_valid] = devfspath;
158 if (debug)
159 (void) printf("adding %s\n", devfspath);
160 disk_list_valid++;
161 }
162
163 /* ARGSUSED */
164 static int
i_disktype(di_node_t node,di_minor_t minor,void * arg)165 i_disktype(di_node_t node, di_minor_t minor, void *arg)
166 {
167 char *minortype;
168
169 if (di_minor_spectype(minor) == S_IFCHR) {
170 minortype = di_minor_nodetype(minor);
171
172 /* exclude CD's */
173 if (strncmp(minortype, DDI_NT_CD, sizeof (DDI_NT_CD) - 1) != 0)
174 /* only take p0 raw device */
175 if (strcmp(di_minor_name(minor), "q,raw") == 0)
176 new_disk_list_entry(node);
177 }
178 return (DI_WALK_CONTINUE);
179 }
180
181 static void
build_disk_list()182 build_disk_list()
183 {
184 int ret;
185 ret = di_walk_minor(root_node, DDI_NT_BLOCK, 0, NULL,
186 i_disktype);
187 if (ret != 0) {
188 (void) fprintf(stderr, "di_walk_minor failed errno %d\n",
189 errno);
190 cleanup_and_exit(1);
191 }
192 }
193
194 static void
free_disks()195 free_disks()
196 {
197 int i;
198
199 if (disk_list) {
200 for (i = 0; i < disk_list_valid; i++)
201 di_devfs_path_free(disk_list[i]);
202
203 free(disk_list);
204 }
205 }
206
207 static int
i_match_pcibdf(di_node_t node,void * arg)208 i_match_pcibdf(di_node_t node, void *arg)
209 {
210 pcibdf_t *pbp;
211 int len;
212 uint32_t regval;
213 uint32_t busnum, funcnum, devicenum;
214 char *devtype;
215 uint32_t *regbuf = NULL;
216 di_node_t parentnode;
217
218 pbp = (pcibdf_t *)arg;
219
220 parentnode = di_parent_node(node);
221
222 len = di_prop_lookup_strings(DDI_DEV_T_ANY, parentnode,
223 "device_type", (char **)&devtype);
224
225 if ((len <= 0) ||
226 ((strcmp(devtype, "pci") != 0) && (strcmp(devtype, "pciex") != 0)))
227 return (DI_WALK_CONTINUE);
228
229 len = di_prop_lookup_ints(DDI_DEV_T_ANY, node, "reg",
230 (int **)®buf);
231
232 if (len <= 0) {
233 /* Try PROM property */
234 len = di_prom_prop_lookup_ints(prom_hdl, node, "reg",
235 (int **)®buf);
236 }
237
238
239 if (len > 0) {
240 regval = regbuf[0];
241
242 busnum = PCI_REG_BUS_G(regval);
243 devicenum = PCI_REG_DEV_G(regval);
244 funcnum = PCI_REG_FUNC_G(regval);
245
246 if ((busnum == pbp->busnum) &&
247 (devicenum == pbp->devnum) &&
248 (funcnum == pbp->funcnum)) {
249 /* found it */
250 pbp->di_node = node;
251 return (DI_WALK_TERMINATE);
252 }
253 }
254
255 return (DI_WALK_CONTINUE);
256 }
257
258 static di_node_t
search_tree_match_pcibdf(di_node_t node,int bus,int dev,int fn)259 search_tree_match_pcibdf(di_node_t node, int bus, int dev, int fn)
260 {
261 pcibdf_t pb;
262 pb.busnum = bus;
263 pb.devnum = dev;
264 pb.funcnum = fn;
265 pb.di_node = DI_NODE_NIL;
266
267 (void) di_walk_node(node, DI_WALK_CLDFIRST, &pb, i_match_pcibdf);
268 return (pb.di_node);
269
270 }
271
272 static int
i_match_usbserialno(di_node_t node,void * arg)273 i_match_usbserialno(di_node_t node, void *arg)
274 {
275 int len;
276 char *serialp;
277 usbser_t *usbsp;
278
279 usbsp = (usbser_t *)arg;
280
281 len = di_prop_lookup_bytes(DDI_DEV_T_ANY, node, "usb-serialno",
282 (uchar_t **)&serialp);
283
284 if ((len > 0) && (strncmp((char *)&usbsp->serialno, serialp,
285 sizeof (uint64_t)) == 0)) {
286 usbsp->node = node;
287 return (DI_WALK_TERMINATE);
288 }
289 return (DI_WALK_CONTINUE);
290 }
291
292 static di_node_t
search_tree_match_usbserialno(di_node_t node,uint64_t serialno)293 search_tree_match_usbserialno(di_node_t node, uint64_t serialno)
294 {
295
296 usbser_t usbs;
297
298 usbs.serialno = serialno;
299 usbs.node = DI_NODE_NIL;
300
301 (void) di_walk_node(node, DI_WALK_CLDFIRST, &usbs, i_match_usbserialno);
302 return (usbs.node);
303 }
304
305 /*
306 * returns the index to the disklist to the disk with matching path
307 */
308 static int
search_disklist_match_path(char * path)309 search_disklist_match_path(char *path)
310 {
311 int i;
312 for (i = 0; i < disk_list_valid; i++)
313 if (strcmp(disk_list[i], path) == 0) {
314 return (i);
315 }
316 return (-1);
317 }
318
319 /*
320 * Find first child of 'node' whose unit address is 'matchbusaddr'
321 */
322 static di_node_t
search_children_match_busaddr(di_node_t node,char * matchbusaddr)323 search_children_match_busaddr(di_node_t node, char *matchbusaddr)
324 {
325 di_node_t cnode;
326 char *busaddr;
327 di_path_t pi = DI_PATH_NIL;
328
329 if (matchbusaddr == NULL)
330 return (DI_NODE_NIL);
331
332 while ((pi = di_path_phci_next_path(node, pi)) != DI_PATH_NIL) {
333 busaddr = di_path_bus_addr(pi);
334 if (busaddr == NULL)
335 continue;
336 if (strncmp(busaddr, matchbusaddr, MAXNAMELEN) == 0)
337 return (di_path_client_node(pi));
338 }
339
340 for (cnode = di_child_node(node); cnode != DI_NODE_NIL;
341 cnode = di_sibling_node(cnode)) {
342 busaddr = di_bus_addr(cnode);
343 if (busaddr == NULL)
344 continue;
345 if (strncmp(busaddr, matchbusaddr, MAXNAMELEN) == 0)
346 return (cnode);
347 }
348
349 return (DI_NODE_NIL);
350 }
351
352 /*
353 * Construct a physical device pathname from EDD and verify the
354 * path exists. Return the index of in disk_list for the mapped
355 * path on success, -1 on failure.
356 */
357 static int
match_edd(biosdev_data_t * bdata)358 match_edd(biosdev_data_t *bdata)
359 {
360 di_node_t node, cnode = DI_NODE_NIL;
361 char *devfspath = NULL;
362 fn48_t *bd;
363 int index;
364 char busaddrbuf[MAXNAMELEN];
365
366 if (!bdata->edd_valid) {
367 if (debug)
368 (void) printf("edd not valid\n");
369 return (-1);
370 }
371
372 bd = &bdata->fn48_dev_params;
373
374 if (bd->magic != 0xBEDD || bd->pathinfo_len == 0) {
375 /* EDD extensions for devicepath not present */
376 if (debug)
377 (void) printf("magic not valid %x pathinfolen %d\n",
378 bd->magic, bd->pathinfo_len);
379 return (-1);
380 }
381
382 /* we handle only PCI scsi, ata or sata for now */
383 if (strncmp(bd->bustype, "PCI", 3) != 0) {
384 if (debug)
385 (void) printf("was not pci %s\n", bd->bustype);
386 return (-1);
387 }
388 if (debug)
389 (void) printf("match_edd bdf %d %d %d\n",
390 bd->interfacepath.pci.bus,
391 bd->interfacepath.pci.device,
392 bd->interfacepath.pci.function);
393
394 /* look into devinfo tree and find a node with matching pci b/d/f */
395 node = search_tree_match_pcibdf(root_node, bd->interfacepath.pci.bus,
396 bd->interfacepath.pci.device, bd->interfacepath.pci.function);
397
398 if (node == DI_NODE_NIL) {
399 if (debug)
400 (void) printf(" could not find a node in tree "
401 "matching bdf\n");
402 return (-1);
403 }
404
405 if (debug) {
406 int i;
407 (void) printf("interface type ");
408 for (i = 0; i < 8; i++)
409 (void) printf("%c", bd->interface_type[i]);
410 (void) printf(" pci channel %x target %x\n",
411 bd->interfacepath.pci.channel,
412 bd->devicepath.scsi.target);
413 }
414
415 if (strncmp(bd->interface_type, "SCSI", 4) == 0) {
416
417 (void) snprintf(busaddrbuf, MAXNAMELEN, "%x,%x",
418 bd->devicepath.scsi.target, bd->devicepath.scsi.lun_lo);
419
420 cnode = search_children_match_busaddr(node, busaddrbuf);
421
422 } else if ((strncmp(bd->interface_type, "ATAPI", 5) == 0) ||
423 (strncmp(bd->interface_type, "ATA", 3) == 0) ||
424 (strncmp(bd->interface_type, "SATA", 4) == 0)) {
425
426 if (strncmp(di_node_name(node), "pci-ide", 7) == 0) {
427 /*
428 * Legacy using pci-ide
429 * the child should be ide@<x>, where x is
430 * the channel number
431 */
432 (void) snprintf(busaddrbuf, MAXNAMELEN, "%d",
433 bd->interfacepath.pci.channel);
434
435 if ((cnode = search_children_match_busaddr(node,
436 busaddrbuf)) != DI_NODE_NIL) {
437
438 (void) snprintf(busaddrbuf, MAXNAMELEN, "%x,0",
439 bd->devicepath.ata.chan);
440 cnode = search_children_match_busaddr(cnode,
441 busaddrbuf);
442
443 if (cnode == DI_NODE_NIL)
444 if (debug)
445 (void) printf("Interface %s "
446 "using pci-ide no "
447 "grandchild at %s\n",
448 bd->interface_type,
449 busaddrbuf);
450 } else {
451 if (debug)
452 (void) printf("Interface %s using "
453 "pci-ide, with no child at %s\n",
454 bd->interface_type, busaddrbuf);
455 }
456 } else {
457 if (strncmp(bd->interface_type, "SATA", 4) == 0) {
458 /*
459 * The current EDD (EDD-2) spec does not
460 * address port number. This is work in
461 * progress.
462 * Interprete the first field of device path
463 * as port number. Needs to be revisited
464 * with port multiplier support.
465 */
466 (void) snprintf(busaddrbuf, MAXNAMELEN, "%x,0",
467 bd->devicepath.ata.chan);
468
469 cnode = search_children_match_busaddr(node,
470 busaddrbuf);
471 } else {
472 if (debug)
473 (void) printf("Interface %s, not using"
474 " pci-ide\n", bd->interface_type);
475 }
476 }
477
478 } else if (strncmp(bd->interface_type, "USB", 3) == 0) {
479 cnode = search_tree_match_usbserialno(node,
480 bd->devicepath.usb.usb_serial_id);
481 } else {
482 if (debug)
483 (void) printf("sorry not supported interface %s\n",
484 bd->interface_type);
485 }
486
487 if (cnode != DI_NODE_NIL) {
488 devfspath = di_devfs_path(cnode);
489 index = search_disklist_match_path(devfspath);
490 di_devfs_path_free(devfspath);
491 if (index >= 0)
492 return (index);
493 }
494
495 return (-1);
496 }
497
498 /*
499 * For each disk in list of disks, compare the first block with the
500 * one from bdd. On the first match, return the index of path in
501 * disk_list. If none matched return -1.
502 */
503 static int
match_first_block(biosdev_data_t * bd)504 match_first_block(biosdev_data_t *bd)
505 {
506
507 char diskpath[MAXPATHLEN];
508 int fd;
509 char buf[512];
510 ssize_t num_read;
511 int i;
512
513 if (!bd->first_block_valid)
514 return (-1);
515
516 for (i = 0; i < disk_list_valid; i++) {
517 (void) snprintf(diskpath, MAXPATHLEN, "%s/%s:q,raw",
518 DEVFS_PREFIX, disk_list[i]);
519 fd = open(diskpath, O_RDONLY);
520 if (fd < 0) {
521 (void) fprintf(stderr, "opening %s failed errno %d\n",
522 diskpath, errno);
523 continue;
524 }
525 num_read = read(fd, buf, 512);
526 if (num_read != 512) {
527 (void) printf("read only %d bytes from %s\n", num_read,
528 diskpath);
529 continue;
530 }
531
532 if (memcmp(buf, bd->first_block, 512) == 0) {
533 /* found it */
534 return (i);
535 }
536 }
537 return (-1);
538 }
539
540
541 static void
cleanup_and_exit(int exitcode)542 cleanup_and_exit(int exitcode)
543 {
544
545 free_disks();
546
547 if (root_node != DI_NODE_NIL)
548 di_fini(root_node);
549
550 if (root_allnode != DI_NODE_NIL)
551 di_fini(root_allnode);
552
553 if (prom_hdl != DI_PROM_HANDLE_NIL)
554 di_prom_fini(prom_hdl);
555 exit(exitcode);
556
557 }
558
559
560 int
main(int argc,char * argv[])561 main(int argc, char *argv[])
562 {
563 biosdev_data_t *biosdata;
564 int i, c, j;
565 int matchedindex = -1;
566 char biospropname[BIOSPROPNAME_TMPL_LEN];
567 int totalmatches = 0;
568 biosdev_data_t *biosdataarray[BIOSDEV_NUM];
569
570
571 while ((c = getopt(argc, argv, "d")) != -1) {
572 switch (c) {
573 case 'd':
574 debug = 1;
575 break;
576 default:
577 (void) printf("unknown option %c\n", c);
578 exit(1);
579 }
580 }
581
582 if ((prom_hdl = di_prom_init()) == DI_PROM_HANDLE_NIL) {
583 (void) fprintf(stderr, "di_prom_init failed\n");
584 cleanup_and_exit(1);
585 }
586
587 if ((root_node = di_init("/", DINFOCACHE)) == DI_NODE_NIL) {
588 (void) fprintf(stderr, "di_init failed\n");
589 cleanup_and_exit(1);
590 }
591
592 if ((root_allnode = di_init("/", DINFOCPYALL)) == DI_NODE_NIL) {
593 (void) fprintf(stderr, "di_init failed\n");
594 cleanup_and_exit(1);
595 }
596
597 (void) memset(mapinfo, 0, sizeof (mapinfo));
598
599 /* get a list of all disks in the system */
600 build_disk_list();
601
602 /* Get property values that were created at boot up time */
603 for (i = 0; i < BIOSDEV_NUM; i++) {
604
605 (void) snprintf((char *)biospropname, BIOSPROPNAME_TMPL_LEN,
606 BIOSPROPNAME_TMPL, i + STARTING_DRVNUM);
607 if (di_prop_lookup_bytes(DDI_DEV_T_ANY, root_allnode,
608 biospropname, (uchar_t **)&biosdataarray[i]) <= 0)
609 biosdataarray[i] = NULL;
610 }
611
612 /* Try to match based on device/interface path info from BIOS */
613 for (i = 0; i < BIOSDEV_NUM; i++) {
614
615 if ((biosdata = biosdataarray[i]) == NULL)
616 continue;
617 if (debug)
618 (void) printf("matching edd 0x%x\n",
619 i + STARTING_DRVNUM);
620
621 matchedindex = match_edd(biosdata);
622
623 if (matchedindex != -1) {
624 if (debug) {
625 (void) printf("matched by edd\n");
626 (void) printf("0x%x %s\n", i + STARTING_DRVNUM,
627 disk_list[matchedindex]);
628 }
629
630 mapinfo[i].disklist_index = matchedindex;
631 mapinfo[i].matchcount++;
632
633 for (j = 0; j < i; j++) {
634 if (mapinfo[j].matchcount > 0 &&
635 mapinfo[j].disklist_index == matchedindex) {
636 mapinfo[j].matchcount++;
637 mapinfo[i].matchcount++;
638 }
639 }
640
641 } else
642 if (debug)
643 (void) printf("No matches by edd\n");
644 }
645
646 /*
647 * Go through the list and ignore any found matches that are dups.
648 * This is to workaround issues with BIOSes that do not implement
649 * providing interface/device path info correctly.
650 */
651
652 for (i = 0; i < BIOSDEV_NUM; i++) {
653 if (mapinfo[i].matchcount > 1) {
654 if (debug)
655 (void) printf("Ignoring dup match_edd\n(count "
656 "%d): 0x%x %s\n", mapinfo[i].matchcount,
657 i + STARTING_DRVNUM,
658 disk_list[mapinfo[i].disklist_index]);
659
660 mapinfo[i].matchcount = 0;
661 mapinfo[i].disklist_index = 0;
662 }
663 }
664
665
666 /*
667 * For each bios dev number that we do not have exactly one match
668 * already, try to match based on first block
669 */
670 for (i = 0; i < BIOSDEV_NUM; i++) {
671 if (mapinfo[i].matchcount == 1)
672 continue;
673
674 if ((biosdata = biosdataarray[i]) == NULL)
675 continue;
676
677 if (debug)
678 (void) printf("matching first block 0x%x\n",
679 i + STARTING_DRVNUM);
680
681 matchedindex = match_first_block(biosdata);
682 if (matchedindex != -1) {
683 if (debug) {
684 (void) printf("matched by first block\n");
685 (void) printf("0x%x %s\n", i + STARTING_DRVNUM,
686 disk_list[matchedindex]);
687 }
688
689 mapinfo[i].disklist_index = matchedindex;
690 mapinfo[i].matchcount++;
691
692 for (j = 0; j < i; j++) {
693 if (mapinfo[j].matchcount > 0 &&
694 mapinfo[j].disklist_index == matchedindex) {
695 mapinfo[j].matchcount++;
696 mapinfo[i].matchcount++;
697 }
698 }
699 } else
700 if (debug) {
701 (void) printf(" No matches by first block\n");
702 (void) fprintf(stderr, "Could not match 0x%x\n",
703 i + STARTING_DRVNUM);
704 }
705 }
706
707
708 for (i = 0; i < BIOSDEV_NUM; i++) {
709 if (mapinfo[i].matchcount == 1) {
710 (void) printf("0x%x %s\n", i + STARTING_DRVNUM,
711 disk_list[mapinfo[i].disklist_index]);
712 totalmatches++;
713 } else if (debug && mapinfo[i].matchcount > 1) {
714 (void) printf("0x%x %s matchcount %d\n",
715 i + STARTING_DRVNUM,
716 disk_list[mapinfo[i].disklist_index],
717 mapinfo[i].matchcount);
718 }
719 }
720
721 if (totalmatches == 0) {
722 (void) fprintf(stderr, "biosdev: Could not match any!!\n");
723 cleanup_and_exit(1);
724 }
725
726 cleanup_and_exit(0);
727 /* NOTREACHED */
728 return (0);
729 }
730