root/arch/i386/stand/libsa/biosdev.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. biosdreset
  2. bios_getdiskinfo
  3. CHS_rw
  4. EDD_rw
  5. biosd_io
  6. bios_getdisklabel
  7. biosopen
  8. biosdisk_err
  9. biosdisk_errno
  10. biosstrategy
  11. biosclose
  12. biosioctl

    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 }

/* [<][>][^][v][top][bottom][index][help] */