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