xref: /openbsd/sys/arch/i386/stand/libsa/biosdev.c (revision be4b99eb)
1 /*	$OpenBSD: biosdev.c,v 1.95 2016/09/18 15:13:10 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 		bb = bb1 = alloc(bbsize * 2);
275 		if ((((u_int32_t)bb) & ~0xffff) !=
276 		    (((u_int32_t)bb + bbsize - 1) & ~0xffff))
277 			bb = (void *)(((u_int32_t)bb + bbsize - 1) & ~0xffff);
278 		if (rw != F_READ)
279 			bcopy(buf, bb, bbsize);
280 	} else
281 		bb = buf;
282 
283 	/* Try to do operation up to 5 times */
284 	for (error = 1, j = 5; j-- && error; ) {
285 		/* CHS or LBA access? */
286 		if (bd->bios_edd != -1) {
287 			error = EDD_rw(rw, dev, off, nsect, bb);
288 		} else {
289 			int cyl, head, sect;
290 			size_t i, n;
291 			char *p = bb;
292 
293 			/* Handle track boundaries */
294 			for (error = i = 0; error == 0 && i < nsect;
295 			    i += n, off += n, p += n * DEV_BSIZE) {
296 
297 				btochs(off, cyl, head, sect, bd->bios_heads,
298 				    bd->bios_sectors);
299 
300 				if ((sect + (nsect - i)) >= bd->bios_sectors)
301 					n = bd->bios_sectors - sect;
302 				else
303 					n = nsect - i;
304 
305 				error = CHS_rw(rw, dev, cyl, head, sect, n, p);
306 
307 				/* ECC corrected */
308 				if (error == 0x11)
309 					error = 0;
310 			}
311 		}
312 		switch (error) {
313 		case 0x00:	/* No errors */
314 		case 0x11:	/* ECC corrected */
315 			error = 0;
316 			break;
317 
318 		default:	/* All other errors */
319 #ifdef BIOS_DEBUG
320 			if (debug)
321 				printf("\nBIOS error 0x%x (%s)\n",
322 				    error, biosdisk_err(error));
323 #endif
324 			biosdreset(dev);
325 			break;
326 		}
327 	}
328 
329 	if (bb != buf && rw == F_READ)
330 		bcopy(bb, buf, bbsize);
331 	free(bb1, bbsize * 2);
332 
333 #ifdef BIOS_DEBUG
334 	if (debug) {
335 		if (error != 0)
336 			printf("=0x%x(%s)", error, biosdisk_err(error));
337 		putchar('\n');
338 	}
339 #endif
340 
341 	return error;
342 }
343 
344 int
345 biosd_diskio(int rw, struct diskinfo *dip, u_int off, int nsect, void *buf)
346 {
347 	return biosd_io(rw, &dip->bios_info, off, nsect, buf);
348 }
349 /*
350  * Try to read the bsd label on the given BIOS device.
351  */
352 static u_int
353 findopenbsd(bios_diskinfo_t *bd, const char **err)
354 {
355 	struct dos_mbr mbr;
356 	struct dos_partition *dp;
357 	u_int mbroff = DOSBBSECTOR;
358 	u_int mbr_eoff = DOSBBSECTOR;	/* Offset of MBR extended partition. */
359 	int error, i, maxebr = DOS_MAXEBR, nextebr;
360 
361 again:
362 	if (!maxebr--) {
363 		*err = "too many extended partitions";
364 		return (-1);
365 	}
366 
367 	/* Read MBR */
368 	bzero(&mbr, sizeof(mbr));
369 	error = biosd_io(F_READ, bd, mbroff, 1, &mbr);
370 	if (error) {
371 		*err = biosdisk_err(error);
372 		return (-1);
373 	}
374 
375 	/* check mbr signature */
376 	if (mbr.dmbr_sign != DOSMBR_SIGNATURE) {
377 		*err = "bad MBR signature\n";
378 		return (-1);
379 	}
380 
381 	/* Search for OpenBSD partition */
382 	nextebr = 0;
383 	for (i = 0; i < NDOSPART; i++) {
384 		dp = &mbr.dmbr_parts[i];
385 		if (!dp->dp_size)
386 			continue;
387 #ifdef BIOS_DEBUG
388 		if (debug)
389 			printf("found partition %u: "
390 			    "type %u (0x%x) offset %u (0x%x)\n",
391 			    (int)(dp - mbr.dmbr_parts),
392 			    dp->dp_typ, dp->dp_typ,
393 			    dp->dp_start, dp->dp_start);
394 #endif
395 		if (dp->dp_typ == DOSPTYP_OPENBSD) {
396 			if (dp->dp_start > (dp->dp_start + mbroff))
397 				continue;
398 			return (dp->dp_start + mbroff);
399 		}
400 
401 		/*
402 		 * Record location of next ebr if and only if this is the first
403 		 * extended partition in this boot record!
404 		 */
405 		if (!nextebr && (dp->dp_typ == DOSPTYP_EXTEND ||
406 		    dp->dp_typ == DOSPTYP_EXTENDL)) {
407 			nextebr = dp->dp_start + mbr_eoff;
408 			if (nextebr < dp->dp_start)
409 				nextebr = (u_int)-1;
410 			if (mbr_eoff == DOSBBSECTOR)
411 				mbr_eoff = dp->dp_start;
412 		}
413 	}
414 
415 	if (nextebr && nextebr != (u_int)-1) {
416 		mbroff = nextebr;
417 		goto again;
418 	}
419 
420 	return (-1);
421 }
422 
423 const char *
424 bios_getdisklabel(bios_diskinfo_t *bd, struct disklabel *label)
425 {
426 	u_int start = 0;
427 	char buf[DEV_BSIZE];
428 	const char *err = NULL;
429 	int error;
430 
431 	/* Sanity check */
432 	if (bd->bios_edd == -1 &&
433 	    (bd->bios_heads == 0 || bd->bios_sectors == 0))
434 		return "failed to read disklabel";
435 
436 	/* MBR is a harddisk thing */
437 	if (bd->bios_number & 0x80) {
438 		start = findopenbsd(bd, &err);
439 		if (start == (u_int)-1) {
440 			if (err != NULL)
441 				return (err);
442  			return "no OpenBSD partition\n";
443 		}
444 	}
445 
446 	/* Load BSD disklabel */
447 #ifdef BIOS_DEBUG
448 	if (debug)
449 		printf("loading disklabel @ %u\n", start + DOS_LABELSECTOR);
450 #endif
451 	/* read disklabel */
452 	error = biosd_io(F_READ, bd, start + DOS_LABELSECTOR, 1, buf);
453 
454 	if (error)
455 		return "failed to read disklabel";
456 
457 	/* Fill in disklabel */
458 	return (getdisklabel(buf, label));
459 }
460 
461 int
462 biosopen(struct open_file *f, ...)
463 {
464 #ifdef SOFTRAID
465 	struct sr_boot_volume *bv;
466 #endif
467 	register char *cp, **file;
468 	dev_t maj, unit, part;
469 	struct diskinfo *dip;
470 	int biosdev, devlen;
471 	const char *st;
472 	va_list ap;
473 	char *dev;
474 
475 	va_start(ap, f);
476 	cp = *(file = va_arg(ap, char **));
477 	va_end(ap);
478 
479 #ifdef BIOS_DEBUG
480 	if (debug)
481 		printf("%s\n", cp);
482 #endif
483 
484 	f->f_devdata = NULL;
485 
486 	/* Search for device specification. */
487 	dev = cp;
488 	if (cp[4] == ':')
489 		devlen = 2;
490 	else if (cp[5] == ':')
491 		devlen = 3;
492 	else
493 		return ENOENT;
494 	cp += devlen;
495 
496 	/* Get unit. */
497 	if ('0' <= *cp && *cp <= '9')
498 		unit = *cp++ - '0';
499 	else {
500 		printf("Bad unit number\n");
501 		return EUNIT;
502 	}
503 
504 	/* Get partition. */
505 	if ('a' <= *cp && *cp <= 'p')
506 		part = *cp++ - 'a';
507 	else {
508 		printf("Bad partition\n");
509 		return EPART;
510 	}
511 
512 	/* Get filename. */
513 	cp++;	/* skip ':' */
514 	if (*cp != 0)
515 		*file = cp;
516 	else
517 		f->f_flags |= F_RAW;
518 
519 #ifdef SOFTRAID
520 	/* Intercept softraid disks. */
521 	if (strncmp("sr", dev, 2) == 0) {
522 
523 		/* Create a fake diskinfo for this softraid volume. */
524 		SLIST_FOREACH(bv, &sr_volumes, sbv_link)
525 			if (bv->sbv_unit == unit)
526 				break;
527 		if (bv == NULL) {
528 			printf("Unknown device: sr%d\n", unit);
529 			return EADAPT;
530 		}
531 
532 		if (bv->sbv_level == 'C' && bv->sbv_keys == NULL)
533 			if (sr_crypto_decrypt_keys(bv) != 0)
534 				return EPERM;
535 
536 		if (bv->sbv_diskinfo == NULL) {
537 			dip = alloc(sizeof(struct diskinfo));
538 			bzero(dip, sizeof(*dip));
539 			bv->sbv_diskinfo = dip;
540 			dip->sr_vol = bv;
541 			dip->bios_info.flags |= BDI_BADLABEL;
542 		}
543 
544 		dip = bv->sbv_diskinfo;
545 
546 		if (dip->bios_info.flags & BDI_BADLABEL) {
547 			/* Attempt to read disklabel. */
548 			bv->sbv_part = 'c';
549 			if (sr_getdisklabel(bv, &dip->disklabel))
550 				return ERDLAB;
551 			dip->bios_info.flags &= ~BDI_BADLABEL;
552 		}
553 
554 		bv->sbv_part = part + 'a';
555 
556 		bootdev_dip = dip;
557 		f->f_devdata = dip;
558 
559 		return 0;
560 	}
561 #endif
562 
563 	for (maj = 0; maj < nbdevs &&
564 	    strncmp(dev, bdevs[maj], devlen); maj++);
565 	if (maj >= nbdevs) {
566 		printf("Unknown device: ");
567 		for (cp = *file; *cp != ':'; cp++)
568 			putchar(*cp);
569 		putchar('\n');
570 		return EADAPT;
571 	}
572 
573 	biosdev = unit;
574 	switch (maj) {
575 	case 0:  /* wd */
576 	case 4:  /* sd */
577 	case 17: /* hd */
578 		biosdev |= 0x80;
579 		break;
580 	case 2:  /* fd */
581 		break;
582 	case 6:  /* cd */
583 		biosdev = bios_bootdev & 0xff;
584 		break;
585 	default:
586 		return ENXIO;
587 	}
588 
589 	/* Find device */
590 	bootdev_dip = dip = dklookup(biosdev);
591 
592 	/* Fix up bootdev */
593 	{ dev_t bsd_dev;
594 		bsd_dev = dip->bios_info.bsd_dev;
595 		dip->bsddev = MAKEBOOTDEV(B_TYPE(bsd_dev), B_ADAPTOR(bsd_dev),
596 		    B_CONTROLLER(bsd_dev), unit, part);
597 		dip->bootdev = MAKEBOOTDEV(B_TYPE(bsd_dev), B_ADAPTOR(bsd_dev),
598 		    B_CONTROLLER(bsd_dev), B_UNIT(bsd_dev), part);
599 	}
600 
601 #if 0
602 	dip->bios_info.bsd_dev = dip->bootdev;
603 	bootdev = dip->bootdev;
604 #endif
605 
606 #ifdef BIOS_DEBUG
607 	if (debug) {
608 		printf("BIOS geometry: heads=%u, s/t=%u; EDD=%d\n",
609 		    dip->bios_info.bios_heads, dip->bios_info.bios_sectors,
610 		    dip->bios_info.bios_edd);
611 	}
612 #endif
613 
614 	/* Try for disklabel again (might be removable media) */
615 	if (dip->bios_info.flags & BDI_BADLABEL) {
616 		st = bios_getdisklabel(&dip->bios_info, &dip->disklabel);
617 #ifdef BIOS_DEBUG
618 		if (debug && st)
619 			printf("%s\n", st);
620 #endif
621 		if (!st) {
622 			dip->bios_info.flags &= ~BDI_BADLABEL;
623 			dip->bios_info.flags |= BDI_GOODLABEL;
624 		} else
625 			return ERDLAB;
626 	}
627 
628 	f->f_devdata = dip;
629 
630 	return 0;
631 }
632 
633 const u_char bidos_errs[] =
634 /* ignored	"\x00" "successful completion\0" */
635 		"\x01" "invalid function/parameter\0"
636 		"\x02" "address mark not found\0"
637 		"\x03" "write-protected\0"
638 		"\x04" "sector not found\0"
639 		"\x05" "reset failed\0"
640 		"\x06" "disk changed\0"
641 		"\x07" "drive parameter activity failed\0"
642 		"\x08" "DMA overrun\0"
643 		"\x09" "data boundary error\0"
644 		"\x0A" "bad sector detected\0"
645 		"\x0B" "bad track detected\0"
646 		"\x0C" "invalid media\0"
647 		"\x0E" "control data address mark detected\0"
648 		"\x0F" "DMA arbitration level out of range\0"
649 		"\x10" "uncorrectable CRC or ECC error on read\0"
650 /* ignored	"\x11" "data ECC corrected\0" */
651 		"\x20" "controller failure\0"
652 		"\x31" "no media in drive\0"
653 		"\x32" "incorrect drive type in CMOS\0"
654 		"\x40" "seek failed\0"
655 		"\x80" "operation timed out\0"
656 		"\xAA" "drive not ready\0"
657 		"\xB0" "volume not locked in drive\0"
658 		"\xB1" "volume locked in drive\0"
659 		"\xB2" "volume not removable\0"
660 		"\xB3" "volume in use\0"
661 		"\xB4" "lock count exceeded\0"
662 		"\xB5" "valid eject request failed\0"
663 		"\xBB" "undefined error\0"
664 		"\xCC" "write fault\0"
665 		"\xE0" "status register error\0"
666 		"\xFF" "sense operation failed\0"
667 		"\x00" "\0";
668 
669 static const char *
670 biosdisk_err(u_int error)
671 {
672 	register const u_char *p = bidos_errs;
673 
674 	while (*p && *p != error)
675 		while (*p++);
676 
677 	return ++p;
678 }
679 
680 const struct biosdisk_errors {
681 	u_char error;
682 	u_char errno;
683 } tab[] = {
684 	{ 0x01, EINVAL },
685 	{ 0x03, EROFS },
686 	{ 0x08, EINVAL },
687 	{ 0x09, EINVAL },
688 	{ 0x0A, EBSE },
689 	{ 0x0B, EBSE },
690 	{ 0x0C, ENXIO },
691 	{ 0x0D, EINVAL },
692 	{ 0x10, EECC },
693 	{ 0x20, EHER },
694 	{ 0x31, ENXIO },
695 	{ 0x32, ENXIO },
696 	{ 0x00, EIO }
697 };
698 
699 static int
700 biosdisk_errno(u_int error)
701 {
702 	register const struct biosdisk_errors *p;
703 
704 	if (error == 0)
705 		return 0;
706 
707 	for (p = tab; p->error && p->error != error; p++);
708 
709 	return p->errno;
710 }
711 
712 int
713 biosstrategy(void *devdata, int rw, daddr32_t blk, size_t size, void *buf,
714     size_t *rsize)
715 {
716 	struct diskinfo *dip = (struct diskinfo *)devdata;
717 	bios_diskinfo_t *bd = &dip->bios_info;
718 	u_int8_t error = 0;
719 	size_t nsect;
720 
721 #ifdef SOFTRAID
722 	/* Intercept strategy for softraid volumes. */
723 	if (dip->sr_vol)
724 		return sr_strategy(dip->sr_vol, rw, blk, size, buf, rsize);
725 #endif
726 
727 	nsect = (size + DEV_BSIZE - 1) / DEV_BSIZE;
728 	blk += dip->disklabel.d_partitions[B_PARTITION(dip->bsddev)].p_offset;
729 
730 	/* Read all, sub-functions handle track boundaries */
731 	if (blk < 0)
732 		error = EINVAL;
733 	else
734 		error = biosd_io(rw, bd, blk, nsect, buf);
735 
736 #ifdef BIOS_DEBUG
737 	if (debug) {
738 		if (error != 0)
739 			printf("=0x%x(%s)", error, biosdisk_err(error));
740 		putchar('\n');
741 	}
742 #endif
743 
744 	if (rsize != NULL)
745 		*rsize = nsect * DEV_BSIZE;
746 
747 	return (biosdisk_errno(error));
748 }
749 
750 int
751 biosclose(struct open_file *f)
752 {
753 	f->f_devdata = NULL;
754 
755 	return 0;
756 }
757 
758 int
759 biosioctl(struct open_file *f, u_long cmd, void *data)
760 {
761 	return 0;
762 }
763