xref: /openbsd/sys/arch/i386/stand/libsa/biosdev.c (revision a8401cd3)
1 /*	$OpenBSD: biosdev.c,v 1.88 2013/12/28 02:40:41 jsing Exp $	*/
2 
3 /*
4  * Copyright (c) 1996 Michael Shalayeff
5  * Copyright (c) 2003 Tobias Weingartner
6  * All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
19  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
21  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27  * SUCH DAMAGE.
28  *
29  */
30 
31 #include <sys/param.h>
32 #include <sys/reboot.h>
33 #include <sys/disklabel.h>
34 #include <isofs/cd9660/iso.h>
35 #include <lib/libsa/saerrno.h>
36 #include <machine/biosvar.h>
37 #include <machine/tss.h>
38 
39 #include "biosdev.h"
40 #include "debug.h"
41 #include "disk.h"
42 #include "libsa.h"
43 
44 #ifdef SOFTRAID
45 #include <dev/softraidvar.h>
46 #include "softraid.h"
47 #endif
48 
49 static const char *biosdisk_err(u_int);
50 static int biosdisk_errno(u_int);
51 
52 int CHS_rw (int, int, int, int, int, int, void *);
53 static int EDD_rw (int, int, u_int32_t, u_int32_t, void *);
54 
55 static u_int findopenbsd(bios_diskinfo_t *, const char **);
56 
57 extern int debug;
58 int bios_bootdev;
59 int bios_cddev = -1;		/* Set by srt0 if coming from CD */
60 
61 struct EDD_CB {
62 	u_int8_t  edd_len;	/* size of packet */
63 	u_int8_t  edd_res1;	/* reserved */
64 	u_int8_t  edd_nblk;	/* # of blocks to transfer */
65 	u_int8_t  edd_res2;	/* reserved */
66 	u_int16_t edd_off;	/* address of buffer (offset) */
67 	u_int16_t edd_seg;	/* address of buffer (segment) */
68 	u_int64_t edd_daddr;	/* starting block */
69 };
70 
71 /*
72  * reset disk system
73  */
74 static int
75 biosdreset(int dev)
76 {
77 	int rv;
78 
79 	__asm __volatile (DOINT(0x13) "; setc %b0" : "=a" (rv)
80 	    : "0" (0), "d" (dev) : "%ecx", "cc");
81 
82 	return ((rv & 0xff)? rv >> 8 : 0);
83 }
84 
85 /*
86  * Fill out a bios_diskinfo_t for this device.
87  * Return 0 if all ok.
88  * Return 1 if not ok.
89  */
90 int
91 bios_getdiskinfo(int dev, bios_diskinfo_t *pdi)
92 {
93 	u_int rv;
94 
95 	/* Just reset, don't check return code */
96 	rv = biosdreset(dev);
97 
98 #ifdef BIOS_DEBUG
99 	if (debug)
100 		printf("getinfo: try #8, 0x%x, %p\n", dev, pdi);
101 #endif
102 	__asm __volatile (DOINT(0x13) "\n\t"
103 	    "setc %b0; movzbl %h1, %1\n\t"
104 	    "movzbl %%cl, %3; andb $0x3f, %b3\n\t"
105 	    "xchgb %%cl, %%ch; rolb $2, %%ch"
106 	    : "=a" (rv), "=d" (pdi->bios_heads),
107 	      "=c" (pdi->bios_cylinders),
108 	      "=b" (pdi->bios_sectors)
109 	    : "0" (0x0800), "1" (dev) : "cc");
110 
111 #ifdef BIOS_DEBUG
112 	if (debug) {
113 		printf("getinfo: got #8\n");
114 		printf("disk 0x%x: %d,%d,%d\n", dev, pdi->bios_cylinders,
115 		    pdi->bios_heads, pdi->bios_sectors);
116 	}
117 #endif
118 	if (rv & 0xff)
119 		return 1;
120 
121 	/* Fix up info */
122 	pdi->bios_number = dev;
123 	pdi->bios_heads++;
124 	pdi->bios_cylinders &= 0x3ff;
125 	pdi->bios_cylinders++;
126 
127 	/* NOTE:
128 	 * This currently hangs/reboots some machines
129 	 * The IBM ThinkPad 750ED for one.
130 	 *
131 	 * Funny that an IBM/MS extension would not be
132 	 * implemented by an IBM system...
133 	 *
134 	 * Future hangs (when reported) can be "fixed"
135 	 * with getSYSCONFaddr() and an exceptions list.
136 	 */
137 	if (dev & 0x80 && (dev == 0x80 || dev == 0x81 || dev == bios_bootdev)) {
138 		int bm;
139 
140 #ifdef BIOS_DEBUG
141 		if (debug)
142 			printf("getinfo: try #41, 0x%x\n", dev);
143 #endif
144 		/* EDD support check */
145 		__asm __volatile(DOINT(0x13) "; setc %b0"
146 			 : "=a" (rv), "=c" (bm)
147 			 : "0" (0x4100), "b" (0x55aa), "d" (dev) : "cc");
148 		if (!(rv & 0xff) && (BIOS_regs.biosr_bx & 0xffff) == 0xaa55)
149 			pdi->bios_edd = (bm & 0xffff) | ((rv & 0xff) << 16);
150 		else
151 			pdi->bios_edd = -1;
152 
153 #ifdef BIOS_DEBUG
154 		if (debug) {
155 			printf("getinfo: got #41\n");
156 			printf("disk 0x%x: 0x%x\n", dev, bm);
157 		}
158 #endif
159 		/*
160 		 * If extended disk access functions are not supported
161 		 * there is not much point on doing EDD.
162 		 */
163 		if (!(pdi->bios_edd & EXT_BM_EDA))
164 			pdi->bios_edd = -1;
165 	} else
166 		pdi->bios_edd = -1;
167 
168 	/* Skip sanity check for CHS options in EDD mode. */
169 	if (pdi->bios_edd != -1)
170 		return 0;
171 
172 	/* Sanity check */
173 	if (!pdi->bios_cylinders || !pdi->bios_heads || !pdi->bios_sectors)
174 		return 1;
175 
176 	/* CD-ROMs sometimes return heads == 1 */
177 	if (pdi->bios_heads < 2)
178 		return 1;
179 
180 	return 0;
181 }
182 
183 /*
184  * Read/Write a block from given place using the BIOS.
185  */
186 int
187 CHS_rw(int rw, int dev, int cyl, int head, int sect, int nsect, void *buf)
188 {
189 	int rv;
190 
191 	rw = rw == F_READ ? 2 : 3;
192 	BIOS_regs.biosr_es = (u_int32_t)buf >> 4;
193 	__asm __volatile ("movb %b7, %h1\n\t"
194 	    "movb %b6, %%dh\n\t"
195 	    "andl $0xf, %4\n\t"
196 	    /* cylinder; the highest 2 bits of cyl is in %cl */
197 	    "xchgb %%ch, %%cl\n\t"
198 	    "rorb  $2, %%cl\n\t"
199 	    "orb %b5, %%cl\n\t"
200 	    "inc %%cx\n\t"
201 	    DOINT(0x13) "\n\t"
202 	    "setc %b0"
203 	    : "=a" (rv)
204 	    : "0" (nsect), "d" (dev), "c" (cyl),
205 	      "b" (buf), "m" (sect), "m" (head),
206 	      "m" (rw)
207 	    : "cc", "memory");
208 
209 	return ((rv & 0xff)? rv >> 8 : 0);
210 }
211 
212 static __inline int
213 EDD_rw(int rw, int dev, u_int32_t daddr, u_int32_t nblk, void *buf)
214 {
215 	int rv;
216 	volatile static struct EDD_CB cb;
217 
218 	/* Zero out reserved stuff */
219 	cb.edd_res1 = 0;
220 	cb.edd_res2 = 0;
221 
222 	/* Fill in parameters */
223 	cb.edd_len = sizeof(cb);
224 	cb.edd_nblk = nblk;
225 	cb.edd_seg = ((u_int32_t)buf >> 4) & 0xffff;
226 	cb.edd_off = (u_int32_t)buf & 0xf;
227 	cb.edd_daddr = daddr;
228 
229 	/* if offset/segment are zero, punt */
230 	if (!cb.edd_seg && !cb.edd_off)
231 		return 1;
232 
233 	/* Call extended read/write (with disk packet) */
234 	BIOS_regs.biosr_ds = (u_int32_t)&cb >> 4;
235 	__asm __volatile (DOINT(0x13) "; setc %b0" : "=a" (rv)
236 	    : "0" ((rw == F_READ)? 0x4200: 0x4300),
237 	      "d" (dev), "S" ((int) (&cb) & 0xf) : "%ecx", "cc");
238 	return ((rv & 0xff)? rv >> 8 : 0);
239 }
240 
241 /*
242  * Read given sector, handling retry/errors/etc.
243  */
244 int
245 biosd_io(int rw, bios_diskinfo_t *bd, u_int off, int nsect, void *buf)
246 {
247 	int dev = bd->bios_number;
248 	int j, error;
249 	void *bb;
250 	int bbsize = nsect * DEV_BSIZE;
251 
252 	if (bd->flags & BDI_EL_TORITO) {	/* It's a CD device */
253 		dev &= 0xff;			/* Mask out this flag bit */
254 
255 		/*
256 		 * sys/lib/libsa/cd9600.c converts 2,048-byte CD sectors
257 		 * to DEV_BSIZE blocks before calling the device strategy
258 		 * routine.  However, the El Torito spec says that the
259 		 * BIOS will work in 2,048-byte sectors.  So shift back.
260 		 */
261 		off /= (ISO_DEFAULT_BLOCK_SIZE / DEV_BSIZE);
262 		nsect /= (ISO_DEFAULT_BLOCK_SIZE / DEV_BSIZE);
263 	}
264 
265 	/*
266 	 * Use a bounce buffer to not cross 64k DMA boundary, and to
267 	 * not access 1 MB or above.
268 	 */
269 	if (((((u_int32_t)buf) & ~0xffff) !=
270 	    (((u_int32_t)buf + bbsize) & ~0xffff)) ||
271 	    (((u_int32_t)buf) >= 0x100000)) {
272 		/*
273 		 * XXX we believe that all the io is buffered
274 		 * by fs routines, so no big reads anyway
275 		 */
276 		bb = alloca(bbsize);
277 		if (rw != F_READ)
278 			bcopy(buf, bb, bbsize);
279 	} else
280 		bb = buf;
281 
282 	/* Try to do operation up to 5 times */
283 	for (error = 1, j = 5; j-- && error; ) {
284 		/* CHS or LBA access? */
285 		if (bd->bios_edd != -1) {
286 			error = EDD_rw(rw, dev, off, nsect, bb);
287 		} else {
288 			int cyl, head, sect;
289 			size_t i, n;
290 			char *p = bb;
291 
292 			/* Handle track boundaries */
293 			for (error = i = 0; error == 0 && i < nsect;
294 			    i += n, off += n, p += n * DEV_BSIZE) {
295 
296 				btochs(off, cyl, head, sect, bd->bios_heads,
297 				    bd->bios_sectors);
298 
299 				if ((sect + (nsect - i)) >= bd->bios_sectors)
300 					n = bd->bios_sectors - sect;
301 				else
302 					n = nsect - i;
303 
304 				error = CHS_rw(rw, dev, cyl, head, sect, n, p);
305 
306 				/* ECC corrected */
307 				if (error == 0x11)
308 					error = 0;
309 			}
310 		}
311 		switch (error) {
312 		case 0x00:	/* No errors */
313 		case 0x11:	/* ECC corrected */
314 			error = 0;
315 			break;
316 
317 		default:	/* All other errors */
318 #ifdef BIOS_DEBUG
319 			if (debug)
320 				printf("\nBIOS error 0x%x (%s)\n",
321 				    error, biosdisk_err(error));
322 #endif
323 			biosdreset(dev);
324 			break;
325 		}
326 	}
327 
328 	if (bb != buf && rw == F_READ)
329 		bcopy(bb, buf, bbsize);
330 
331 #ifdef BIOS_DEBUG
332 	if (debug) {
333 		if (error != 0)
334 			printf("=0x%x(%s)", error, biosdisk_err(error));
335 		putchar('\n');
336 	}
337 #endif
338 
339 	return error;
340 }
341 
342 /*
343  * Try to read the bsd label on the given BIOS device.
344  */
345 static u_int
346 findopenbsd(bios_diskinfo_t *bd, const char **err)
347 {
348 	struct dos_mbr mbr;
349 	struct dos_partition *dp;
350 	u_int mbroff = DOSBBSECTOR;
351 	u_int mbr_eoff = DOSBBSECTOR;	/* Offset of MBR extended partition. */
352 	int error, i, maxebr = DOS_MAXEBR, nextebr;
353 
354 again:
355 	if (!maxebr--) {
356 		*err = "too many extended partitions";
357 		return (-1);
358 	}
359 
360 	/* Read MBR */
361 	bzero(&mbr, sizeof(mbr));
362 	error = biosd_io(F_READ, bd, mbroff, 1, &mbr);
363 	if (error) {
364 		*err = biosdisk_err(error);
365 		return (-1);
366 	}
367 
368 	/* check mbr signature */
369 	if (mbr.dmbr_sign != DOSMBR_SIGNATURE) {
370 		*err = "bad MBR signature\n";
371 		return (-1);
372 	}
373 
374 	/* Search for OpenBSD partition */
375 	nextebr = 0;
376 	for (i = 0; i < NDOSPART; i++) {
377 		dp = &mbr.dmbr_parts[i];
378 		if (!dp->dp_size)
379 			continue;
380 #ifdef BIOS_DEBUG
381 		if (debug)
382 			printf("found partition %u: "
383 			    "type %u (0x%x) offset %u (0x%x)\n",
384 			    (int)(dp - mbr.dmbr_parts),
385 			    dp->dp_typ, dp->dp_typ,
386 			    dp->dp_start, dp->dp_start);
387 #endif
388 		if (dp->dp_typ == DOSPTYP_OPENBSD) {
389 			if (dp->dp_start > (dp->dp_start + mbroff))
390 				continue;
391 			return (dp->dp_start + mbroff);
392 		}
393 
394 		/*
395 		 * Record location of next ebr if and only if this is the first
396 		 * extended partition in this boot record!
397 		 */
398 		if (!nextebr && (dp->dp_typ == DOSPTYP_EXTEND ||
399 		    dp->dp_typ == DOSPTYP_EXTENDL)) {
400 			nextebr = dp->dp_start + mbr_eoff;
401 			if (nextebr < dp->dp_start)
402 				nextebr = (u_int)-1;
403 			if (mbr_eoff == DOSBBSECTOR)
404 				mbr_eoff = dp->dp_start;
405 		}
406 	}
407 
408 	if (nextebr && nextebr != (u_int)-1) {
409 		mbroff = nextebr;
410 		goto again;
411 	}
412 
413 	return (-1);
414 }
415 
416 const char *
417 bios_getdisklabel(bios_diskinfo_t *bd, struct disklabel *label)
418 {
419 	u_int start = 0;
420 	char *buf;
421 	const char *err = NULL;
422 	int error;
423 
424 	/* Sanity check */
425 	if (bd->bios_edd == -1 &&
426 	    (bd->bios_heads == 0 || bd->bios_sectors == 0))
427 		return "failed to read disklabel";
428 
429 	/* MBR is a harddisk thing */
430 	if (bd->bios_number & 0x80) {
431 		start = findopenbsd(bd, &err);
432 		if (start == (u_int)-1) {
433 			if (err != NULL)
434 				return (err);
435  			return "no OpenBSD partition\n";
436 		}
437 	}
438 	start = LABELSECTOR + start;
439 
440 	/* Load BSD disklabel */
441 	buf = alloca(DEV_BSIZE);
442 #ifdef BIOS_DEBUG
443 	if (debug)
444 		printf("loading disklabel @ %u\n", start);
445 #endif
446 	/* read disklabel */
447 	error = biosd_io(F_READ, bd, start, 1, buf);
448 
449 	if (error)
450 		return "failed to read disklabel";
451 
452 	/* Fill in disklabel */
453 	return (getdisklabel(buf, label));
454 }
455 
456 int
457 biosopen(struct open_file *f, ...)
458 {
459 #ifdef SOFTRAID
460 	struct sr_boot_volume *bv;
461 #endif
462 	register char *cp, **file;
463 	dev_t maj, unit, part;
464 	struct diskinfo *dip;
465 	int biosdev, devlen;
466 	const char *st;
467 	va_list ap;
468 	char *dev;
469 
470 	va_start(ap, f);
471 	cp = *(file = va_arg(ap, char **));
472 	va_end(ap);
473 
474 #ifdef BIOS_DEBUG
475 	if (debug)
476 		printf("%s\n", cp);
477 #endif
478 
479 	f->f_devdata = NULL;
480 
481 	/* Search for device specification. */
482 	dev = cp;
483 	if (cp[4] == ':')
484 		devlen = 2;
485 	else if (cp[5] == ':')
486 		devlen = 3;
487 	else
488 		return ENOENT;
489 	cp += devlen;
490 
491 	/* Get unit. */
492 	if ('0' <= *cp && *cp <= '9')
493 		unit = *cp++ - '0';
494 	else {
495 		printf("Bad unit number\n");
496 		return EUNIT;
497 	}
498 
499 	/* Get partition. */
500 	if ('a' <= *cp && *cp <= 'p')
501 		part = *cp++ - 'a';
502 	else {
503 		printf("Bad partition\n");
504 		return EPART;
505 	}
506 
507 	/* Get filename. */
508 	cp++;	/* skip ':' */
509 	if (*cp != 0)
510 		*file = cp;
511 	else
512 		f->f_flags |= F_RAW;
513 
514 #ifdef SOFTRAID
515 	/* Intercept softraid disks. */
516 	if (strncmp("sr", dev, 2) == 0) {
517 
518 		/* Create a fake diskinfo for this softraid volume. */
519 		SLIST_FOREACH(bv, &sr_volumes, sbv_link)
520 			if (bv->sbv_unit == unit)
521 				break;
522 		if (bv == NULL) {
523 			printf("Unknown device: sr%d\n", unit);
524 			return EADAPT;
525 		}
526 
527 		if (bv->sbv_level == 'C' && bv->sbv_keys == NULL)
528 			if (sr_crypto_decrypt_keys(bv) != 0)
529 				return EPERM;
530 
531 		if (bv->sbv_diskinfo == NULL) {
532 			dip = alloc(sizeof(struct diskinfo));
533 			bzero(dip, sizeof(*dip));
534 			bv->sbv_diskinfo = dip;
535 			dip->sr_vol = bv;
536 			dip->bios_info.flags |= BDI_BADLABEL;
537 		}
538 
539 		dip = bv->sbv_diskinfo;
540 
541 		if (dip->bios_info.flags & BDI_BADLABEL) {
542 			/* Attempt to read disklabel. */
543 			bv->sbv_part = 'c';
544 			if (sr_getdisklabel(bv, &dip->disklabel))
545 				return ERDLAB;
546 			dip->bios_info.flags &= ~BDI_BADLABEL;
547 		}
548 
549 		bv->sbv_part = part + 'a';
550 
551 		bootdev_dip = dip;
552 		f->f_devdata = dip;
553 
554 		return 0;
555 	}
556 #endif
557 
558 	for (maj = 0; maj < nbdevs &&
559 	    strncmp(dev, bdevs[maj], devlen); maj++);
560 	if (maj >= nbdevs) {
561 		printf("Unknown device: ");
562 		for (cp = *file; *cp != ':'; cp++)
563 			putchar(*cp);
564 		putchar('\n');
565 		return EADAPT;
566 	}
567 
568 	biosdev = unit;
569 	switch (maj) {
570 	case 0:  /* wd */
571 	case 4:  /* sd */
572 	case 17: /* hd */
573 		biosdev |= 0x80;
574 		break;
575 	case 2:  /* fd */
576 		break;
577 	case 6:  /* cd */
578 		biosdev = bios_bootdev & 0xff;
579 		break;
580 	default:
581 		return ENXIO;
582 	}
583 
584 	/* Find device */
585 	bootdev_dip = dip = dklookup(biosdev);
586 
587 	/* Fix up bootdev */
588 	{ dev_t bsd_dev;
589 		bsd_dev = dip->bios_info.bsd_dev;
590 		dip->bsddev = MAKEBOOTDEV(B_TYPE(bsd_dev), B_ADAPTOR(bsd_dev),
591 		    B_CONTROLLER(bsd_dev), unit, part);
592 		dip->bootdev = MAKEBOOTDEV(B_TYPE(bsd_dev), B_ADAPTOR(bsd_dev),
593 		    B_CONTROLLER(bsd_dev), B_UNIT(bsd_dev), part);
594 	}
595 
596 #if 0
597 	dip->bios_info.bsd_dev = dip->bootdev;
598 	bootdev = dip->bootdev;
599 #endif
600 
601 #ifdef BIOS_DEBUG
602 	if (debug) {
603 		printf("BIOS geometry: heads=%u, s/t=%u; EDD=%d\n",
604 		    dip->bios_info.bios_heads, dip->bios_info.bios_sectors,
605 		    dip->bios_info.bios_edd);
606 	}
607 #endif
608 
609 	/* Try for disklabel again (might be removable media) */
610 	if (dip->bios_info.flags & BDI_BADLABEL) {
611 		st = bios_getdisklabel(&dip->bios_info, &dip->disklabel);
612 #ifdef BIOS_DEBUG
613 		if (debug && st)
614 			printf("%s\n", st);
615 #endif
616 		if (!st) {
617 			dip->bios_info.flags &= ~BDI_BADLABEL;
618 			dip->bios_info.flags |= BDI_GOODLABEL;
619 		} else
620 			return ERDLAB;
621 	}
622 
623 	f->f_devdata = dip;
624 
625 	return 0;
626 }
627 
628 const u_char bidos_errs[] =
629 /* ignored	"\x00" "successful completion\0" */
630 		"\x01" "invalid function/parameter\0"
631 		"\x02" "address mark not found\0"
632 		"\x03" "write-protected\0"
633 		"\x04" "sector not found\0"
634 		"\x05" "reset failed\0"
635 		"\x06" "disk changed\0"
636 		"\x07" "drive parameter activity failed\0"
637 		"\x08" "DMA overrun\0"
638 		"\x09" "data boundary error\0"
639 		"\x0A" "bad sector detected\0"
640 		"\x0B" "bad track detected\0"
641 		"\x0C" "invalid media\0"
642 		"\x0E" "control data address mark detected\0"
643 		"\x0F" "DMA arbitration level out of range\0"
644 		"\x10" "uncorrectable CRC or ECC error on read\0"
645 /* ignored	"\x11" "data ECC corrected\0" */
646 		"\x20" "controller failure\0"
647 		"\x31" "no media in drive\0"
648 		"\x32" "incorrect drive type in CMOS\0"
649 		"\x40" "seek failed\0"
650 		"\x80" "operation timed out\0"
651 		"\xAA" "drive not ready\0"
652 		"\xB0" "volume not locked in drive\0"
653 		"\xB1" "volume locked in drive\0"
654 		"\xB2" "volume not removable\0"
655 		"\xB3" "volume in use\0"
656 		"\xB4" "lock count exceeded\0"
657 		"\xB5" "valid eject request failed\0"
658 		"\xBB" "undefined error\0"
659 		"\xCC" "write fault\0"
660 		"\xE0" "status register error\0"
661 		"\xFF" "sense operation failed\0"
662 		"\x00" "\0";
663 
664 static const char *
665 biosdisk_err(u_int error)
666 {
667 	register const u_char *p = bidos_errs;
668 
669 	while (*p && *p != error)
670 		while (*p++);
671 
672 	return ++p;
673 }
674 
675 const struct biosdisk_errors {
676 	u_char error;
677 	u_char errno;
678 } tab[] = {
679 	{ 0x01, EINVAL },
680 	{ 0x03, EROFS },
681 	{ 0x08, EINVAL },
682 	{ 0x09, EINVAL },
683 	{ 0x0A, EBSE },
684 	{ 0x0B, EBSE },
685 	{ 0x0C, ENXIO },
686 	{ 0x0D, EINVAL },
687 	{ 0x10, EECC },
688 	{ 0x20, EHER },
689 	{ 0x31, ENXIO },
690 	{ 0x32, ENXIO },
691 	{ 0x00, EIO }
692 };
693 
694 static int
695 biosdisk_errno(u_int error)
696 {
697 	register const struct biosdisk_errors *p;
698 
699 	if (error == 0)
700 		return 0;
701 
702 	for (p = tab; p->error && p->error != error; p++);
703 
704 	return p->errno;
705 }
706 
707 int
708 biosstrategy(void *devdata, int rw, daddr32_t blk, size_t size, void *buf,
709     size_t *rsize)
710 {
711 	struct diskinfo *dip = (struct diskinfo *)devdata;
712 	bios_diskinfo_t *bd = &dip->bios_info;
713 	u_int8_t error = 0;
714 	size_t nsect;
715 
716 #ifdef SOFTRAID
717 	/* Intercept strategy for softraid volumes. */
718 	if (dip->sr_vol)
719 		return sr_strategy(dip->sr_vol, rw, blk, size, buf, rsize);
720 #endif
721 
722 	nsect = (size + DEV_BSIZE - 1) / DEV_BSIZE;
723 	blk += dip->disklabel.d_partitions[B_PARTITION(dip->bsddev)].p_offset;
724 
725 	/* Read all, sub-functions handle track boundaries */
726 	if (blk < 0)
727 		error = EINVAL;
728 	else
729 		error = biosd_io(rw, bd, blk, nsect, buf);
730 
731 #ifdef BIOS_DEBUG
732 	if (debug) {
733 		if (error != 0)
734 			printf("=0x%x(%s)", error, biosdisk_err(error));
735 		putchar('\n');
736 	}
737 #endif
738 
739 	if (rsize != NULL)
740 		*rsize = nsect * DEV_BSIZE;
741 
742 	return (biosdisk_errno(error));
743 }
744 
745 int
746 biosclose(struct open_file *f)
747 {
748 	f->f_devdata = NULL;
749 
750 	return 0;
751 }
752 
753 int
754 biosioctl(struct open_file *f, u_long cmd, void *data)
755 {
756 	return 0;
757 }
758