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