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