1/*	$OpenBSD: biosdev.c,v 1.102 2024/04/14 03:26:25 jsg 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
50static const char *biosdisk_err(u_int);
51static int biosdisk_errno(u_int);
52
53int CHS_rw (int, int, int, int, int, int, void *);
54static int EDD_rw (int, int, u_int32_t, u_int32_t, void *);
55
56static int biosd_io(int, bios_diskinfo_t *, u_int, int, void *);
57static u_int findopenbsd(bios_diskinfo_t *, const char **);
58
59extern int debug;
60int bios_bootdev;
61int bios_cddev = -1;		/* Set by srt0 if coming from CD */
62
63struct 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 */
76static int
77biosdreset(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 */
92int
93bios_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 */
188int
189CHS_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
214static __inline int
215EDD_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 */
246int
247biosd_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
346int
347biosd_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 */
367static u_int
368findopenbsd(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
376again:
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
438const char *
439bios_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
476int
477biosopen(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		/* We only support read-only softraid. */
538		f->f_flags |= F_NOWRITE;
539
540		/* Create a fake diskinfo for this softraid volume. */
541		SLIST_FOREACH(bv, &sr_volumes, sbv_link)
542			if (bv->sbv_unit == unit)
543				break;
544		if (bv == NULL) {
545			printf("Unknown device: sr%d\n", unit);
546			return EADAPT;
547		}
548
549		if (bv->sbv_level == 'C' && bv->sbv_keys == NULL)
550			if (sr_crypto_unlock_volume(bv) != 0)
551				return EPERM;
552
553		if (bv->sbv_diskinfo == NULL) {
554			dip = alloc(sizeof(struct diskinfo));
555			bzero(dip, sizeof(*dip));
556			dip->strategy = biosstrategy;
557			bv->sbv_diskinfo = dip;
558			dip->sr_vol = bv;
559			dip->bios_info.flags |= BDI_BADLABEL;
560		}
561
562		dip = bv->sbv_diskinfo;
563
564		if (dip->bios_info.flags & BDI_BADLABEL) {
565			/* Attempt to read disklabel. */
566			bv->sbv_part = 'c';
567			if (sr_getdisklabel(bv, &dip->disklabel))
568				return ERDLAB;
569			dip->bios_info.flags &= ~BDI_BADLABEL;
570			check_hibernate(dip);
571		}
572
573		bv->sbv_part = part + 'a';
574
575		bootdev_dip = dip;
576		f->f_devdata = dip;
577
578		return 0;
579	}
580#endif
581
582	for (maj = 0; maj < nbdevs &&
583	    strncmp(dev, bdevs[maj], devlen); maj++);
584	if (maj >= nbdevs) {
585		printf("Unknown device: ");
586		for (cp = *file; *cp != ':'; cp++)
587			putchar(*cp);
588		putchar('\n');
589		return EADAPT;
590	}
591
592	biosdev = unit;
593	switch (maj) {
594	case 0:  /* wd */
595	case 4:  /* sd */
596	case 17: /* hd */
597		biosdev |= 0x80;
598		break;
599	case 2:  /* fd */
600		break;
601	case 6:  /* cd */
602		biosdev = bios_bootdev & 0xff;
603		break;
604	default:
605		return ENXIO;
606	}
607
608	/* Find device */
609	bootdev_dip = dip = dklookup(biosdev);
610
611	/* Fix up bootdev */
612	{ dev_t bsd_dev;
613		bsd_dev = dip->bios_info.bsd_dev;
614		dip->bsddev = MAKEBOOTDEV(B_TYPE(bsd_dev), B_ADAPTOR(bsd_dev),
615		    B_CONTROLLER(bsd_dev), unit, part);
616		dip->bootdev = MAKEBOOTDEV(B_TYPE(bsd_dev), B_ADAPTOR(bsd_dev),
617		    B_CONTROLLER(bsd_dev), B_UNIT(bsd_dev), part);
618	}
619
620#if 0
621	dip->bios_info.bsd_dev = dip->bootdev;
622	bootdev = dip->bootdev;
623#endif
624
625#ifdef BIOS_DEBUG
626	if (debug) {
627		printf("BIOS geometry: heads=%u, s/t=%u; EDD=%d\n",
628		    dip->bios_info.bios_heads, dip->bios_info.bios_sectors,
629		    dip->bios_info.bios_edd);
630	}
631#endif
632
633	/* Try for disklabel again (might be removable media) */
634	if (dip->bios_info.flags & BDI_BADLABEL) {
635		st = bios_getdisklabel(&dip->bios_info, &dip->disklabel);
636#ifdef BIOS_DEBUG
637		if (debug && st)
638			printf("%s\n", st);
639#endif
640		if (!st) {
641			dip->bios_info.flags &= ~BDI_BADLABEL;
642			dip->bios_info.flags |= BDI_GOODLABEL;
643		} else
644			return ERDLAB;
645	}
646
647	f->f_devdata = dip;
648
649	return 0;
650}
651
652const u_char bidos_errs[] =
653/* ignored	"\x00" "successful completion\0" */
654		"\x01" "invalid function/parameter\0"
655		"\x02" "address mark not found\0"
656		"\x03" "write-protected\0"
657		"\x04" "sector not found\0"
658		"\x05" "reset failed\0"
659		"\x06" "disk changed\0"
660		"\x07" "drive parameter activity failed\0"
661		"\x08" "DMA overrun\0"
662		"\x09" "data boundary error\0"
663		"\x0A" "bad sector detected\0"
664		"\x0B" "bad track detected\0"
665		"\x0C" "invalid media\0"
666		"\x0E" "control data address mark detected\0"
667		"\x0F" "DMA arbitration level out of range\0"
668		"\x10" "uncorrectable CRC or ECC error on read\0"
669/* ignored	"\x11" "data ECC corrected\0" */
670		"\x20" "controller failure\0"
671		"\x31" "no media in drive\0"
672		"\x32" "incorrect drive type in CMOS\0"
673		"\x40" "seek failed\0"
674		"\x80" "operation timed out\0"
675		"\xAA" "drive not ready\0"
676		"\xB0" "volume not locked in drive\0"
677		"\xB1" "volume locked in drive\0"
678		"\xB2" "volume not removable\0"
679		"\xB3" "volume in use\0"
680		"\xB4" "lock count exceeded\0"
681		"\xB5" "valid eject request failed\0"
682		"\xBB" "undefined error\0"
683		"\xCC" "write fault\0"
684		"\xE0" "status register error\0"
685		"\xFF" "sense operation failed\0"
686		"\x00" "\0";
687
688static const char *
689biosdisk_err(u_int error)
690{
691	register const u_char *p = bidos_errs;
692
693	while (*p && *p != error)
694		while (*p++);
695
696	return ++p;
697}
698
699const struct biosdisk_errors {
700	u_char error;
701	u_char errno;
702} tab[] = {
703	{ 0x01, EINVAL },
704	{ 0x03, EROFS },
705	{ 0x08, EINVAL },
706	{ 0x09, EINVAL },
707	{ 0x0A, EBSE },
708	{ 0x0B, EBSE },
709	{ 0x0C, ENXIO },
710	{ 0x0D, EINVAL },
711	{ 0x10, EECC },
712	{ 0x20, EHER },
713	{ 0x31, ENXIO },
714	{ 0x32, ENXIO },
715	{ 0x00, EIO }
716};
717
718static int
719biosdisk_errno(u_int error)
720{
721	register const struct biosdisk_errors *p;
722
723	if (error == 0)
724		return 0;
725
726	for (p = tab; p->error && p->error != error; p++)
727		;
728
729	return p->errno;
730}
731
732int
733biosstrategy(void *devdata, int rw, daddr_t blk, size_t size, void *buf,
734    size_t *rsize)
735{
736	struct diskinfo *dip = (struct diskinfo *)devdata;
737	u_int8_t error = 0;
738	size_t nsect;
739
740#ifdef SOFTRAID
741	/* Intercept strategy for softraid volumes. */
742	if (dip->sr_vol)
743		return sr_strategy(dip->sr_vol, rw, blk, size, buf, rsize);
744#endif
745
746	nsect = (size + DEV_BSIZE - 1) / DEV_BSIZE;
747	blk += dip->disklabel.d_partitions[B_PARTITION(dip->bsddev)].p_offset;
748
749	/* Read all, sub-functions handle track boundaries */
750	if (blk < 0)
751		error = EINVAL;
752	else
753		error = biosd_diskio(rw, dip, blk, nsect, buf);
754
755#ifdef BIOS_DEBUG
756	if (debug) {
757		if (error != 0)
758			printf("=0x%x(%s)", error, biosdisk_err(error));
759		putchar('\n');
760	}
761#endif
762
763	if (rsize != NULL)
764		*rsize = nsect * DEV_BSIZE;
765
766	return (biosdisk_errno(error));
767}
768
769int
770biosclose(struct open_file *f)
771{
772	f->f_devdata = NULL;
773
774	return 0;
775}
776
777int
778biosioctl(struct open_file *f, u_long cmd, void *data)
779{
780	return 0;
781}
782