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.
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.
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]
22 * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
26 #pragma ident "%Z%%M% %I% %E% SMI"
32 #include <sys/param.h>
34 #include <sys/types.h>
36 #include <libdevinfo.h>
38 #include <sys/types.h>
41 #include <sys/biosdisk.h>
45 * structure used for searching device tree for a node matching
48 typedef struct pcibdf
{
56 * structure used for searching device tree for a node matching
65 * structure for holding the mapping info
68 int disklist_index
; /* index to disk_list of the mapped path */
69 int matchcount
; /* number of matches per this device number */
72 #define DEVFS_PREFIX "/devices"
73 #define DISKS_LIST_INCR 20 /* increment for resizing disk_list */
75 #define BIOSPROPNAME_TMPL "biosdev-0x%x"
76 #define BIOSPROPNAME_TMPL_LEN 13
78 #define STARTING_DRVNUM 0x80
81 * array to hold mappings. Element at index X corresponds to BIOS device
84 static mapinfo_t mapinfo
[BIOSDEV_NUM
];
87 * Cache copy of kernel device tree snapshot root handle, includes devices
90 static di_node_t root_node
= DI_NODE_NIL
;
93 * kernel device tree snapshot with currently attached devices. Detached
94 * devices are not included.
96 static di_node_t root_allnode
= DI_NODE_NIL
;
99 * handle to retrieve prom properties
102 static di_prom_handle_t prom_hdl
= DI_PROM_HANDLE_NIL
;
104 static char **disk_list
= NULL
; /* array of physical device pathnames */
105 static int disk_list_len
= 0; /* length of disk_list */
106 static int disk_list_valid
= 0; /* number of valid entries in disk_list */
108 static int debug
= 0; /* used for enabling debug output */
111 /* Local function prototypes */
112 static void new_disk_list_entry(di_node_t node
);
113 static int i_disktype(di_node_t node
, di_minor_t minor
, void *arg
);
114 static void build_disk_list();
115 static int search_disklist_match_path(char *path
);
116 static void free_disks();
117 static void cleanup_and_exit(int);
119 static int match_edd(biosdev_data_t
*bd
);
120 static int match_first_block(biosdev_data_t
*bd
);
122 static di_node_t
search_tree_match_pcibdf(di_node_t node
, int bus
, int dev
,
124 static int i_match_pcibdf(di_node_t node
, void *arg
);
126 static di_node_t
search_tree_match_usbserialno(di_node_t node
,
128 static int i_match_usbserialno(di_node_t node
, void *arg
);
130 static di_node_t
search_children_match_busaddr(di_node_t node
,
136 new_disk_list_entry(di_node_t node
)
143 if (disk_list_valid
>= disk_list_len
) {
144 /* valid should never really be larger than len */
145 /* if they are equal we need to init or realloc */
146 newlen
= disk_list_len
+ DISKS_LIST_INCR
;
147 newsize
= newlen
* sizeof (*disk_list
);
149 newlist
= (char **)realloc(disk_list
, newsize
);
150 if (newlist
== NULL
) {
151 (void) printf("realloc failed to resize disk table\n");
155 disk_list_len
= newlen
;
158 devfspath
= di_devfs_path(node
);
159 disk_list
[disk_list_valid
] = devfspath
;
161 (void) printf("adding %s\n", devfspath
);
167 i_disktype(di_node_t node
, di_minor_t minor
, void *arg
)
171 if (di_minor_spectype(minor
) == S_IFCHR
) {
172 minortype
= di_minor_nodetype(minor
);
175 if (strncmp(minortype
, DDI_NT_CD
, sizeof (DDI_NT_CD
) - 1) != 0)
176 /* only take p0 raw device */
177 if (strcmp(di_minor_name(minor
), "q,raw") == 0)
178 new_disk_list_entry(node
);
180 return (DI_WALK_CONTINUE
);
187 ret
= di_walk_minor(root_node
, DDI_NT_BLOCK
, 0, NULL
,
190 (void) fprintf(stderr
, "di_walk_minor failed errno %d\n",
202 for (i
= 0; i
< disk_list_valid
; i
++)
203 di_devfs_path_free(disk_list
[i
]);
210 i_match_pcibdf(di_node_t node
, void *arg
)
215 uint32_t busnum
, funcnum
, devicenum
;
217 uint32_t *regbuf
= NULL
;
218 di_node_t parentnode
;
220 pbp
= (pcibdf_t
*)arg
;
222 parentnode
= di_parent_node(node
);
224 len
= di_prop_lookup_strings(DDI_DEV_T_ANY
, parentnode
,
225 "device_type", (char **)&devtype
);
228 ((strcmp(devtype
, "pci") != 0) && (strcmp(devtype
, "pciex") != 0)))
229 return (DI_WALK_CONTINUE
);
231 len
= di_prop_lookup_ints(DDI_DEV_T_ANY
, node
, "reg",
235 /* Try PROM property */
236 len
= di_prom_prop_lookup_ints(prom_hdl
, node
, "reg",
244 busnum
= PCI_REG_BUS_G(regval
);
245 devicenum
= PCI_REG_DEV_G(regval
);
246 funcnum
= PCI_REG_FUNC_G(regval
);
248 if ((busnum
== pbp
->busnum
) &&
249 (devicenum
== pbp
->devnum
) &&
250 (funcnum
== pbp
->funcnum
)) {
253 return (DI_WALK_TERMINATE
);
257 return (DI_WALK_CONTINUE
);
261 search_tree_match_pcibdf(di_node_t node
, int bus
, int dev
, int fn
)
267 pb
.di_node
= DI_NODE_NIL
;
269 (void) di_walk_node(node
, DI_WALK_CLDFIRST
, &pb
, i_match_pcibdf
);
275 i_match_usbserialno(di_node_t node
, void *arg
)
281 usbsp
= (usbser_t
*)arg
;
283 len
= di_prop_lookup_bytes(DDI_DEV_T_ANY
, node
, "usb-serialno",
284 (uchar_t
**)&serialp
);
286 if ((len
> 0) && (strncmp((char *)&usbsp
->serialno
, serialp
,
287 sizeof (uint64_t)) == 0)) {
289 return (DI_WALK_TERMINATE
);
291 return (DI_WALK_CONTINUE
);
295 search_tree_match_usbserialno(di_node_t node
, uint64_t serialno
)
300 usbs
.serialno
= serialno
;
301 usbs
.node
= DI_NODE_NIL
;
303 (void) di_walk_node(node
, DI_WALK_CLDFIRST
, &usbs
, i_match_usbserialno
);
308 * returns the index to the disklist to the disk with matching path
311 search_disklist_match_path(char *path
)
314 for (i
= 0; i
< disk_list_valid
; i
++)
315 if (strcmp(disk_list
[i
], path
) == 0) {
322 * Find first child of 'node' whose unit address is 'matchbusaddr'
325 search_children_match_busaddr(di_node_t node
, char *matchbusaddr
)
329 di_path_t pi
= DI_PATH_NIL
;
331 if (matchbusaddr
== NULL
)
332 return (DI_NODE_NIL
);
334 while ((pi
= di_path_phci_next_path(node
, pi
)) != DI_PATH_NIL
) {
335 busaddr
= di_path_bus_addr(pi
);
338 if (strncmp(busaddr
, matchbusaddr
, MAXNAMELEN
) == 0)
339 return (di_path_client_node(pi
));
342 for (cnode
= di_child_node(node
); cnode
!= DI_NODE_NIL
;
343 cnode
= di_sibling_node(cnode
)) {
344 busaddr
= di_bus_addr(cnode
);
347 if (strncmp(busaddr
, matchbusaddr
, MAXNAMELEN
) == 0)
351 return (DI_NODE_NIL
);
355 * Construct a physical device pathname from EDD and verify the
356 * path exists. Return the index of in disk_list for the mapped
357 * path on success, -1 on failure.
360 match_edd(biosdev_data_t
*bdata
)
362 di_node_t node
, cnode
= DI_NODE_NIL
;
363 char *devfspath
= NULL
;
366 char busaddrbuf
[MAXNAMELEN
];
368 if (!bdata
->edd_valid
) {
370 (void) printf("edd not valid\n");
374 bd
= &bdata
->fn48_dev_params
;
376 if (bd
->magic
!= 0xBEDD || bd
->pathinfo_len
== 0) {
377 /* EDD extensions for devicepath not present */
379 (void) printf("magic not valid %x pathinfolen %d\n",
380 bd
->magic
, bd
->pathinfo_len
);
384 /* we handle only PCI scsi, ata or sata for now */
385 if (strncmp(bd
->bustype
, "PCI", 3) != 0) {
387 (void) printf("was not pci %s\n", bd
->bustype
);
391 (void) printf("match_edd bdf %d %d %d\n",
392 bd
->interfacepath
.pci
.bus
,
393 bd
->interfacepath
.pci
.device
,
394 bd
->interfacepath
.pci
.function
);
396 /* look into devinfo tree and find a node with matching pci b/d/f */
397 node
= search_tree_match_pcibdf(root_node
, bd
->interfacepath
.pci
.bus
,
398 bd
->interfacepath
.pci
.device
, bd
->interfacepath
.pci
.function
);
400 if (node
== DI_NODE_NIL
) {
402 (void) printf(" could not find a node in tree "
409 (void) printf("interface type ");
410 for (i
= 0; i
< 8; i
++)
411 (void) printf("%c", bd
->interface_type
[i
]);
412 (void) printf(" pci channel %x target %x\n",
413 bd
->interfacepath
.pci
.channel
,
414 bd
->devicepath
.scsi
.target
);
417 if (strncmp(bd
->interface_type
, "SCSI", 4) == 0) {
419 (void) snprintf(busaddrbuf
, MAXNAMELEN
, "%x,%x",
420 bd
->devicepath
.scsi
.target
, bd
->devicepath
.scsi
.lun_lo
);
422 cnode
= search_children_match_busaddr(node
, busaddrbuf
);
424 } else if ((strncmp(bd
->interface_type
, "ATAPI", 5) == 0) ||
425 (strncmp(bd
->interface_type
, "ATA", 3) == 0) ||
426 (strncmp(bd
->interface_type
, "SATA", 4) == 0)) {
428 if (strncmp(di_node_name(node
), "pci-ide", 7) == 0) {
430 * Legacy using pci-ide
431 * the child should be ide@<x>, where x is
434 (void) snprintf(busaddrbuf
, MAXNAMELEN
, "%d",
435 bd
->interfacepath
.pci
.channel
);
437 if ((cnode
= search_children_match_busaddr(node
,
438 busaddrbuf
)) != DI_NODE_NIL
) {
440 (void) snprintf(busaddrbuf
, MAXNAMELEN
, "%x,0",
441 bd
->devicepath
.ata
.chan
);
442 cnode
= search_children_match_busaddr(cnode
,
445 if (cnode
== DI_NODE_NIL
)
447 (void) printf("Interface %s "
449 "grandchild at %s\n",
454 (void) printf("Interface %s using "
455 "pci-ide, with no child at %s\n",
456 bd
->interface_type
, busaddrbuf
);
459 if (strncmp(bd
->interface_type
, "SATA", 4) == 0) {
461 * The current EDD (EDD-2) spec does not
462 * address port number. This is work in
464 * Interprete the first field of device path
465 * as port number. Needs to be revisited
466 * with port multiplier support.
468 (void) snprintf(busaddrbuf
, MAXNAMELEN
, "%x,0",
469 bd
->devicepath
.ata
.chan
);
471 cnode
= search_children_match_busaddr(node
,
475 (void) printf("Interface %s, not using"
476 " pci-ide\n", bd
->interface_type
);
480 } else if (strncmp(bd
->interface_type
, "USB", 3) == 0) {
481 cnode
= search_tree_match_usbserialno(node
,
482 bd
->devicepath
.usb
.usb_serial_id
);
485 (void) printf("sorry not supported interface %s\n",
489 if (cnode
!= DI_NODE_NIL
) {
490 devfspath
= di_devfs_path(cnode
);
491 index
= search_disklist_match_path(devfspath
);
492 di_devfs_path_free(devfspath
);
501 * For each disk in list of disks, compare the first block with the
502 * one from bdd. On the first match, return the index of path in
503 * disk_list. If none matched return -1.
506 match_first_block(biosdev_data_t
*bd
)
509 char diskpath
[MAXPATHLEN
];
515 if (!bd
->first_block_valid
)
518 for (i
= 0; i
< disk_list_valid
; i
++) {
519 (void) snprintf(diskpath
, MAXPATHLEN
, "%s/%s:q,raw",
520 DEVFS_PREFIX
, disk_list
[i
]);
521 fd
= open(diskpath
, O_RDONLY
);
523 (void) fprintf(stderr
, "opening %s failed errno %d\n",
527 num_read
= read(fd
, buf
, 512);
528 if (num_read
!= 512) {
529 (void) printf("read only %d bytes from %s\n", num_read
,
534 if (memcmp(buf
, bd
->first_block
, 512) == 0) {
544 cleanup_and_exit(int exitcode
)
549 if (root_node
!= DI_NODE_NIL
)
552 if (root_allnode
!= DI_NODE_NIL
)
553 di_fini(root_allnode
);
555 if (prom_hdl
!= DI_PROM_HANDLE_NIL
)
556 di_prom_fini(prom_hdl
);
563 main(int argc
, char *argv
[])
565 biosdev_data_t
*biosdata
;
567 int matchedindex
= -1;
568 char biospropname
[BIOSPROPNAME_TMPL_LEN
];
569 int totalmatches
= 0;
570 biosdev_data_t
*biosdataarray
[BIOSDEV_NUM
];
573 while ((c
= getopt(argc
, argv
, "d")) != -1) {
579 (void) printf("unknown option %c\n", c
);
584 if ((prom_hdl
= di_prom_init()) == DI_PROM_HANDLE_NIL
) {
585 (void) fprintf(stderr
, "di_prom_init failed\n");
589 if ((root_node
= di_init("/", DINFOCACHE
)) == DI_NODE_NIL
) {
590 (void) fprintf(stderr
, "di_init failed\n");
594 if ((root_allnode
= di_init("/", DINFOCPYALL
)) == DI_NODE_NIL
) {
595 (void) fprintf(stderr
, "di_init failed\n");
599 (void) memset(mapinfo
, 0, sizeof (mapinfo
));
601 /* get a list of all disks in the system */
604 /* Get property values that were created at boot up time */
605 for (i
= 0; i
< BIOSDEV_NUM
; i
++) {
607 (void) snprintf((char *)biospropname
, BIOSPROPNAME_TMPL_LEN
,
608 BIOSPROPNAME_TMPL
, i
+ STARTING_DRVNUM
);
609 if (di_prop_lookup_bytes(DDI_DEV_T_ANY
, root_allnode
,
610 biospropname
, (uchar_t
**)&biosdataarray
[i
]) <= 0)
611 biosdataarray
[i
] = NULL
;
614 /* Try to match based on device/interface path info from BIOS */
615 for (i
= 0; i
< BIOSDEV_NUM
; i
++) {
617 if ((biosdata
= biosdataarray
[i
]) == NULL
)
620 (void) printf("matching edd 0x%x\n",
621 i
+ STARTING_DRVNUM
);
623 matchedindex
= match_edd(biosdata
);
625 if (matchedindex
!= -1) {
627 (void) printf("matched by edd\n");
628 (void) printf("0x%x %s\n", i
+ STARTING_DRVNUM
,
629 disk_list
[matchedindex
]);
632 mapinfo
[i
].disklist_index
= matchedindex
;
633 mapinfo
[i
].matchcount
++;
635 for (j
= 0; j
< i
; j
++) {
636 if (mapinfo
[j
].matchcount
> 0 &&
637 mapinfo
[j
].disklist_index
== matchedindex
) {
638 mapinfo
[j
].matchcount
++;
639 mapinfo
[i
].matchcount
++;
645 (void) printf("No matches by edd\n");
649 * Go through the list and ignore any found matches that are dups.
650 * This is to workaround issues with BIOSes that do not implement
651 * providing interface/device path info correctly.
654 for (i
= 0; i
< BIOSDEV_NUM
; i
++) {
655 if (mapinfo
[i
].matchcount
> 1) {
657 (void) printf("Ignoring dup match_edd\n(count "
658 "%d): 0x%x %s\n", mapinfo
[i
].matchcount
,
660 disk_list
[mapinfo
[i
].disklist_index
]);
662 mapinfo
[i
].matchcount
= 0;
663 mapinfo
[i
].disklist_index
= 0;
669 * For each bios dev number that we do not have exactly one match
670 * already, try to match based on first block
672 for (i
= 0; i
< BIOSDEV_NUM
; i
++) {
673 if (mapinfo
[i
].matchcount
== 1)
676 if ((biosdata
= biosdataarray
[i
]) == NULL
)
680 (void) printf("matching first block 0x%x\n",
681 i
+ STARTING_DRVNUM
);
683 matchedindex
= match_first_block(biosdata
);
684 if (matchedindex
!= -1) {
686 (void) printf("matched by first block\n");
687 (void) printf("0x%x %s\n", i
+ STARTING_DRVNUM
,
688 disk_list
[matchedindex
]);
691 mapinfo
[i
].disklist_index
= matchedindex
;
692 mapinfo
[i
].matchcount
++;
694 for (j
= 0; j
< i
; j
++) {
695 if (mapinfo
[j
].matchcount
> 0 &&
696 mapinfo
[j
].disklist_index
== matchedindex
) {
697 mapinfo
[j
].matchcount
++;
698 mapinfo
[i
].matchcount
++;
703 (void) printf(" No matches by first block\n");
704 (void) fprintf(stderr
, "Could not match 0x%x\n",
705 i
+ STARTING_DRVNUM
);
710 for (i
= 0; i
< BIOSDEV_NUM
; i
++) {
711 if (mapinfo
[i
].matchcount
== 1) {
712 (void) printf("0x%x %s\n", i
+ STARTING_DRVNUM
,
713 disk_list
[mapinfo
[i
].disklist_index
]);
715 } else if (debug
&& mapinfo
[i
].matchcount
> 1) {
716 (void) printf("0x%x %s matchcount %d\n",
718 disk_list
[mapinfo
[i
].disklist_index
],
719 mapinfo
[i
].matchcount
);
723 if (totalmatches
== 0) {
724 (void) fprintf(stderr
, "biosdev: Could not match any!!\n");