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