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