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