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