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