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