xref: /illumos-gate/usr/src/cmd/biosdev/biosdev.c (revision 012e6ce759c490003aed29439cc47d3d73a99ad3)
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
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
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
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
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
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 **)&regbuf);
231 
232 	if (len <= 0) {
233 		/* Try PROM property */
234 		len = di_prom_prop_lookup_ints(prom_hdl, node, "reg",
235 		    (int **)&regbuf);
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
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
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
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
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
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
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
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
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
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