xref: /openbsd/sys/arch/i386/stand/libsa/biosdev.c (revision 36dba039)
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 
50 static const char *biosdisk_err(u_int);
51 static int biosdisk_errno(u_int);
52 
53 int CHS_rw (int, int, int, int, int, int, void *);
54 static int EDD_rw (int, int, u_int32_t, u_int32_t, void *);
55 
56 static int biosd_io(int, bios_diskinfo_t *, u_int, int, void *);
57 static u_int findopenbsd(bios_diskinfo_t *, const char **);
58 
59 extern int debug;
60 int bios_bootdev;
61 int bios_cddev = -1;		/* Set by srt0 if coming from CD */
62 
63 struct 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  */
76 static int
biosdreset(int dev)77 biosdreset(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  */
92 int
bios_getdiskinfo(int dev,bios_diskinfo_t * pdi)93 bios_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  */
188 int
CHS_rw(int rw,int dev,int cyl,int head,int sect,int nsect,void * buf)189 CHS_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 
214 static __inline int
EDD_rw(int rw,int dev,u_int32_t daddr,u_int32_t nblk,void * buf)215 EDD_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  */
246 int
biosd_io(int rw,bios_diskinfo_t * bd,u_int off,int nsect,void * buf)247 biosd_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 
346 int
biosd_diskio(int rw,struct diskinfo * dip,u_int off,int nsect,void * buf)347 biosd_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  */
367 static u_int
findopenbsd(bios_diskinfo_t * bd,const char ** err)368 findopenbsd(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 
376 again:
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 
438 const char *
bios_getdisklabel(bios_diskinfo_t * bd,struct disklabel * label)439 bios_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 
476 int
biosopen(struct open_file * f,...)477 biosopen(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 
652 const 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 
688 static const char *
biosdisk_err(u_int error)689 biosdisk_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 
699 const 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 
718 static int
biosdisk_errno(u_int error)719 biosdisk_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 
732 int
biosstrategy(void * devdata,int rw,daddr_t blk,size_t size,void * buf,size_t * rsize)733 biosstrategy(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 
769 int
biosclose(struct open_file * f)770 biosclose(struct open_file *f)
771 {
772 	f->f_devdata = NULL;
773 
774 	return 0;
775 }
776 
777 int
biosioctl(struct open_file * f,u_long cmd,void * data)778 biosioctl(struct open_file *f, u_long cmd, void *data)
779 {
780 	return 0;
781 }
782