xref: /openbsd/sys/arch/amd64/stand/libsa/biosdev.c (revision fb9d9d2c)
1 /*	$OpenBSD: biosdev.c,v 1.15 2011/03/17 12:53:44 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 "disk.h"
39 #include "libsa.h"
40 #include "biosdev.h"
41 
42 static const char *biosdisk_err(u_int);
43 static int biosdisk_errno(u_int);
44 
45 int CHS_rw (int, int, int, int, int, int, void *);
46 static int EDD_rw (int, int, u_int32_t, u_int32_t, void *);
47 
48 static u_int findopenbsd(bios_diskinfo_t *, const char **);
49 
50 extern int debug;
51 int bios_bootdev;
52 int bios_cddev = -1;		/* Set by srt0 if coming from CD */
53 
54 #if 0
55 struct biosdisk {
56 	bios_diskinfo_t *bios_info;
57 	dev_t	bsddev;
58 	struct disklabel disklabel;
59 };
60 #endif
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
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
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
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
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 	/* Some (most?) BIOSen get confused by i/o above 2 ^ 28 - 1 sector. */
220 	if ((daddr + nblk) > BOOTBIOS_MAXSEC)
221 		return (1); /* Invalid function/parameter. */
222 
223 	/* Zero out reserved stuff */
224 	cb.edd_res1 = 0;
225 	cb.edd_res2 = 0;
226 
227 	/* Fill in parameters */
228 	cb.edd_len = sizeof(cb);
229 	cb.edd_nblk = nblk;
230 	cb.edd_seg = ((u_int32_t)buf >> 4) & 0xffff;
231 	cb.edd_off = (u_int32_t)buf & 0xf;
232 	cb.edd_daddr = daddr;
233 
234 	/* if offset/segment are zero, punt */
235 	if (!cb.edd_seg && !cb.edd_off)
236 		return 1;
237 
238 	/* Call extended read/write (with disk packet) */
239 	BIOS_regs.biosr_ds = (u_int32_t)&cb >> 4;
240 	__asm __volatile (DOINT(0x13) "; setc %b0" : "=a" (rv)
241 	    : "0" ((rw == F_READ)? 0x4200: 0x4300),
242 	      "d" (dev), "S" ((int) (&cb) & 0xf) : "%ecx", "cc");
243 	return ((rv & 0xff)? rv >> 8 : 0);
244 }
245 
246 /*
247  * Read given sector, handling retry/errors/etc.
248  */
249 int
250 biosd_io(int rw, bios_diskinfo_t *bd, u_int off, int nsect, void *buf)
251 {
252 	int dev = bd->bios_number;
253 	int j, error;
254 	void *bb;
255 	int bbsize = nsect * DEV_BSIZE;
256 
257 	if (bd->flags & BDI_EL_TORITO) {	/* It's a CD device */
258 		dev &= 0xff;			/* Mask out this flag bit */
259 
260 		/*
261 		 * sys/lib/libsa/cd9600.c converts 2,048-byte CD sectors
262 		 * to DEV_BSIZE blocks before calling the device strategy
263 		 * routine.  However, the El Torito spec says that the
264 		 * BIOS will work in 2,048-byte sectors.  So shift back.
265 		 */
266 		off >>= (ISO_DEFAULT_BLOCK_SHIFT - DEV_BSHIFT);
267 		nsect >>= (ISO_DEFAULT_BLOCK_SHIFT - DEV_BSHIFT);
268 	}
269 
270 	/*
271 	 * Use a bounce buffer to not cross 64k DMA boundary, and to
272 	 * not access 1 MB or above.
273 	 */
274 	if (((((u_int32_t)buf) & ~0xffff) !=
275 	    (((u_int32_t)buf + bbsize) & ~0xffff)) ||
276 	    (((u_int32_t)buf) >= 0x100000)) {
277 		/*
278 		 * XXX we believe that all the io is buffered
279 		 * by fs routines, so no big reads anyway
280 		 */
281 		bb = alloca(bbsize);
282 		if (rw != F_READ)
283 			bcopy(buf, bb, bbsize);
284 	} else
285 		bb = buf;
286 
287 	/* Try to do operation up to 5 times */
288 	for (error = 1, j = 5; j-- && error; ) {
289 		/* CHS or LBA access? */
290 		if (bd->bios_edd != -1) {
291 			error = EDD_rw(rw, dev, off, nsect, bb);
292 		} else {
293 			int cyl, head, sect;
294 			size_t i, n;
295 			char *p = bb;
296 
297 			/* Handle track boundaries */
298 			for (error = i = 0; error == 0 && i < nsect;
299 			    i += n, off += n, p += n * DEV_BSIZE) {
300 
301 				btochs(off, cyl, head, sect, bd->bios_heads,
302 				    bd->bios_sectors);
303 
304 				if ((sect + (nsect - i)) >= bd->bios_sectors)
305 					n = bd->bios_sectors - sect;
306 				else
307 					n = nsect - i;
308 
309 				error = CHS_rw(rw, dev, cyl, head, sect, n, p);
310 
311 				/* ECC corrected */
312 				if (error == 0x11)
313 					error = 0;
314 			}
315 		}
316 		switch (error) {
317 		case 0x00:	/* No errors */
318 		case 0x11:	/* ECC corrected */
319 			error = 0;
320 			break;
321 
322 		default:	/* All other errors */
323 #ifdef BIOS_DEBUG
324 			if (debug)
325 				printf("\nBIOS error 0x%x (%s)\n",
326 				    error, biosdisk_err(error));
327 #endif
328 			biosdreset(dev);
329 			break;
330 		}
331 	}
332 
333 	if (bb != buf && rw == F_READ)
334 		bcopy(bb, buf, bbsize);
335 
336 #ifdef BIOS_DEBUG
337 	if (debug) {
338 		if (error != 0)
339 			printf("=0x%x(%s)", error, biosdisk_err(error));
340 		putchar('\n');
341 	}
342 #endif
343 
344 	return error;
345 }
346 
347 /*
348  * Try to read the bsd label on the given BIOS device
349  */
350 static u_int
351 findopenbsd(bios_diskinfo_t *bd, const char **err)
352 {
353 	struct dos_mbr mbr;
354 	struct dos_partition *dp;
355 	u_int mbroff = DOSBBSECTOR;
356 	u_int mbr_eoff = DOSBBSECTOR;	/* Offset of MBR extended partition. */
357 	int error, i, maxebr = DOS_MAXEBR, nextebr;
358 
359 again:
360 	if (!maxebr--) {
361 		*err = "too many extended partitions";
362 		return (-1);
363 	}
364 
365 	/* Read MBR */
366 	bzero(&mbr, sizeof(mbr));
367 	error = biosd_io(F_READ, bd, mbroff, 1, &mbr);
368 	if (error) {
369 		*err = biosdisk_err(error);
370 		return (-1);
371 	}
372 
373 	/* check mbr signature */
374 	if (mbr.dmbr_sign != DOSMBR_SIGNATURE) {
375 		*err = "bad MBR signature\n";
376 		return (-1);
377 	}
378 
379 	/* Search for OpenBSD partition */
380 	nextebr = 0;
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 			return (dp->dp_start + mbroff);
397 		}
398 
399 		/*
400 		 * Record location of next ebr if and only if this is the first
401 		 * extended partition in this boot record!
402 		 */
403 		if (!nextebr && (dp->dp_typ == DOSPTYP_EXTEND ||
404 		    dp->dp_typ == DOSPTYP_EXTENDL)) {
405 			nextebr = dp->dp_start + mbr_eoff;
406 			if (nextebr < dp->dp_start)
407 				nextebr = (u_int)-1;
408 			if (mbr_eoff == DOSBBSECTOR)
409 				mbr_eoff = dp->dp_start;
410 		}
411 	}
412 
413 	if (nextebr && nextebr != (u_int)-1) {
414 		mbroff = nextebr;
415 		goto again;
416 	}
417 
418 	return (-1);
419 }
420 
421 const char *
422 bios_getdisklabel(bios_diskinfo_t *bd, struct disklabel *label)
423 {
424 	u_int start = 0;
425 	char *buf;
426 	const char *err = NULL;
427 	int error;
428 
429 	/* Sanity check */
430 	if (bd->bios_edd == -1 &&
431 	    (bd->bios_heads == 0 || bd->bios_sectors == 0))
432 		return "failed to read disklabel";
433 
434 	/* MBR is a harddisk thing */
435 	if (bd->bios_number & 0x80) {
436 		start = findopenbsd(bd, &err);
437 		if (start == (u_int)-1) {
438 			if (err != NULL)
439 				return (err);
440  			return "no OpenBSD partition\n";
441 		}
442 	}
443 	start = LABELSECTOR + start;
444 
445 	/* Load BSD disklabel */
446 	buf = alloca(DEV_BSIZE);
447 #ifdef BIOS_DEBUG
448 	if (debug)
449 		printf("loading disklabel @ %u\n", start);
450 #endif
451 	/* read disklabel */
452 	error = biosd_io(F_READ, bd, start, 1, buf);
453 
454 	if (error)
455 		return "failed to read disklabel";
456 
457 	/* Fill in disklabel */
458 	return (getdisklabel(buf, label));
459 }
460 
461 int
462 biosopen(struct open_file *f, ...)
463 {
464 	va_list ap;
465 	register char	*cp, **file;
466 	dev_t maj, unit, part;
467 	struct diskinfo *dip;
468 	int biosdev;
469 
470 	va_start(ap, f);
471 	cp = *(file = va_arg(ap, char **));
472 	va_end(ap);
473 
474 #ifdef BIOS_DEBUG
475 	if (debug)
476 		printf("%s\n", cp);
477 #endif
478 
479 	f->f_devdata = NULL;
480 	/* search for device specification */
481 	cp += 2;
482 	if (cp[2] != ':') {
483 		if (cp[3] != ':')
484 			return ENOENT;
485 		else
486 			cp++;
487 	}
488 
489 	for (maj = 0; maj < nbdevs && strncmp(*file, bdevs[maj], cp - *file); )
490 	    maj++;
491 	if (maj >= nbdevs) {
492 		printf("Unknown device: ");
493 		for (cp = *file; *cp != ':'; cp++)
494 			putchar(*cp);
495 		putchar('\n');
496 		return EADAPT;
497 	}
498 
499 	/* get unit */
500 	if ('0' <= *cp && *cp <= '9')
501 		unit = *cp++ - '0';
502 	else {
503 		printf("Bad unit number\n");
504 		return EUNIT;
505 	}
506 	/* get partition */
507 	if ('a' <= *cp && *cp <= 'p')
508 		part = *cp++ - 'a';
509 	else {
510 		printf("Bad partition id\n");
511 		return EPART;
512 	}
513 
514 	cp++;	/* skip ':' */
515 	if (*cp != 0)
516 		*file = cp;
517 	else
518 		f->f_flags |= F_RAW;
519 
520 	biosdev = unit;
521 	switch (maj) {
522 	case 0:  /* wd */
523 	case 4:  /* sd */
524 	case 17: /* hd */
525 		biosdev |= 0x80;
526 		break;
527 	case 2:  /* fd */
528 		break;
529 	case 6:  /* cd */
530 		biosdev = bios_bootdev & 0xff;
531 		break;
532 	default:
533 		return ENXIO;
534 	}
535 
536 	/* Find device */
537 	bootdev_dip = dip = dklookup(biosdev);
538 
539 	/* Fix up bootdev */
540 	{ dev_t bsd_dev;
541 		bsd_dev = dip->bios_info.bsd_dev;
542 		dip->bsddev = MAKEBOOTDEV(B_TYPE(bsd_dev), B_ADAPTOR(bsd_dev),
543 		    B_CONTROLLER(bsd_dev), unit, part);
544 		dip->bootdev = MAKEBOOTDEV(B_TYPE(bsd_dev), B_ADAPTOR(bsd_dev),
545 		    B_CONTROLLER(bsd_dev), B_UNIT(bsd_dev), part);
546 	}
547 
548 #if 0
549 	dip->bios_info.bsd_dev = dip->bootdev;
550 	bootdev = dip->bootdev;
551 #endif
552 
553 #ifdef BIOS_DEBUG
554 	if (debug) {
555 		printf("BIOS geometry: heads=%u, s/t=%u; EDD=%d\n",
556 		    dip->bios_info.bios_heads, dip->bios_info.bios_sectors,
557 		    dip->bios_info.bios_edd);
558 	}
559 #endif
560 
561 	/* Try for disklabel again (might be removable media) */
562 	if (dip->bios_info.flags & BDI_BADLABEL) {
563 		const char *st = bios_getdisklabel(&dip->bios_info,
564 		    &dip->disklabel);
565 #ifdef BIOS_DEBUG
566 		if (debug && st)
567 			printf("%s\n", st);
568 #endif
569 		if (!st) {
570 			dip->bios_info.flags &= ~BDI_BADLABEL;
571 			dip->bios_info.flags |= BDI_GOODLABEL;
572 		} else
573 			return ERDLAB;
574 	}
575 
576 	f->f_devdata = dip;
577 
578 	return 0;
579 }
580 
581 const u_char bidos_errs[] =
582 /* ignored	"\x00" "successful completion\0" */
583 		"\x01" "invalid function/parameter\0"
584 		"\x02" "address mark not found\0"
585 		"\x03" "write-protected\0"
586 		"\x04" "sector not found\0"
587 		"\x05" "reset failed\0"
588 		"\x06" "disk changed\0"
589 		"\x07" "drive parameter activity failed\0"
590 		"\x08" "DMA overrun\0"
591 		"\x09" "data boundary error\0"
592 		"\x0A" "bad sector detected\0"
593 		"\x0B" "bad track detected\0"
594 		"\x0C" "invalid media\0"
595 		"\x0E" "control data address mark detected\0"
596 		"\x0F" "DMA arbitration level out of range\0"
597 		"\x10" "uncorrectable CRC or ECC error on read\0"
598 /* ignored	"\x11" "data ECC corrected\0" */
599 		"\x20" "controller failure\0"
600 		"\x31" "no media in drive\0"
601 		"\x32" "incorrect drive type in CMOS\0"
602 		"\x40" "seek failed\0"
603 		"\x80" "operation timed out\0"
604 		"\xAA" "drive not ready\0"
605 		"\xB0" "volume not locked in drive\0"
606 		"\xB1" "volume locked in drive\0"
607 		"\xB2" "volume not removable\0"
608 		"\xB3" "volume in use\0"
609 		"\xB4" "lock count exceeded\0"
610 		"\xB5" "valid eject request failed\0"
611 		"\xBB" "undefined error\0"
612 		"\xCC" "write fault\0"
613 		"\xE0" "status register error\0"
614 		"\xFF" "sense operation failed\0"
615 		"\x00" "\0";
616 
617 static const char *
618 biosdisk_err(u_int error)
619 {
620 	register const u_char *p = bidos_errs;
621 
622 	while (*p && *p != error)
623 		while (*p++);
624 
625 	return ++p;
626 }
627 
628 const struct biosdisk_errors {
629 	u_char error;
630 	u_char errno;
631 } tab[] = {
632 	{ 0x01, EINVAL },
633 	{ 0x03, EROFS },
634 	{ 0x08, EINVAL },
635 	{ 0x09, EINVAL },
636 	{ 0x0A, EBSE },
637 	{ 0x0B, EBSE },
638 	{ 0x0C, ENXIO },
639 	{ 0x0D, EINVAL },
640 	{ 0x10, EECC },
641 	{ 0x20, EHER },
642 	{ 0x31, ENXIO },
643 	{ 0x32, ENXIO },
644 	{ 0x00, EIO }
645 };
646 
647 static int
648 biosdisk_errno(u_int error)
649 {
650 	register const struct biosdisk_errors *p;
651 
652 	if (error == 0)
653 		return 0;
654 
655 	for (p = tab; p->error && p->error != error; p++);
656 
657 	return p->errno;
658 }
659 
660 int
661 biosstrategy(void *devdata, int rw, daddr32_t blk, size_t size, void *buf,
662     size_t *rsize)
663 {
664 	struct diskinfo *dip = (struct diskinfo *)devdata;
665 	bios_diskinfo_t *bd = &dip->bios_info;
666 	u_int8_t error = 0;
667 	size_t nsect;
668 
669 	nsect = (size + DEV_BSIZE-1) / DEV_BSIZE;
670 	if (rsize != NULL)
671 		blk += dip->disklabel.
672 			d_partitions[B_PARTITION(dip->bsddev)].p_offset;
673 
674 	/* Read all, sub-functions handle track boundaries */
675 	if (blk < 0)
676 		error = EINVAL;
677 	else
678 		error = biosd_io(rw, bd, blk, nsect, buf);
679 
680 #ifdef BIOS_DEBUG
681 	if (debug) {
682 		if (error != 0)
683 			printf("=0x%x(%s)", error, biosdisk_err(error));
684 		putchar('\n');
685 	}
686 #endif
687 
688 	if (rsize != NULL)
689 		*rsize = nsect * DEV_BSIZE;
690 
691 	return (biosdisk_errno(error));
692 }
693 
694 int
695 biosclose(struct open_file *f)
696 {
697 	f->f_devdata = NULL;
698 
699 	return 0;
700 }
701 
702 int
703 biosioctl(struct open_file *f, u_long cmd, void *data)
704 {
705 	return 0;
706 }
707