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