1: /* This file contains the "device dependent" part of a hard disk driver that
   2:  * uses the ROM BIOS.  It makes a call and just waits for the transfer to
   3:  * happen.  It is not interrupt driven and thus will (*) have poor performance.
   4:  * The advantage is that it should work on virtually any PC, XT, 386, PS/2
   5:  * or clone.  The demo disk uses this driver.  It is suggested that all
   6:  * MINIX users try the other drivers, and use this one only as a last resort,
   7:  * if all else fails.
   8:  *
   9:  * (*) The performance is within 10% of the AT driver for reads on any disk
  10:  *     and writes on a 2:1 interleaved disk, it will be DMA_BUF_SIZE bytes
  11:  *     per revolution for a minimum of 60 kb/s for writes to 1:1 disks.
  12:  *
  13:  * The file contains one entry point:
  14:  *
  15:  *       bios_winchester_task:  main entry when system is brought up
  16:  *
  17:  *
  18:  * Changes:
  19:  *      30 Apr 1992 by Kees J. Bot: device dependent/independent split.
  20:  *      14 May 2000 by Kees J. Bot: d-d/i rewrite.
  21:  */
  22: 
  23: #include "kernel.h"
  24: #include "driver.h"
  25: #include "drvlib.h"
  26: #include <ibm/int86.h>
  27: 
  28: #if ENABLE_BIOS_WINI
  29: 
  30: /* Error codes */
  31: #define ERR              (-1)   /* general error */
  32: 
  33: /* Parameters for the disk drive. */
  34: #define MAX_DRIVES         4    /* this driver supports 4 drives (d0 - d3)*/
  35: #define MAX_SECS         255    /* bios can transfer this many sectors */
  36: #define NR_DEVICES      (MAX_DRIVES * DEV_PER_DRIVE)
  37: #define SUB_PER_DRIVE   (NR_PARTITIONS * NR_PARTITIONS)
  38: #define NR_SUBDEVS      (MAX_DRIVES * SUB_PER_DRIVE)
  39: 
  40: /* Variables. */
  41: PRIVATE struct wini {           /* main drive struct, one entry per drive */
  42:   unsigned cylinders;           /* number of cylinders */
  43:   unsigned heads;               /* number of heads */
  44:   unsigned sectors;             /* number of sectors per track */
  45:   unsigned open_ct;             /* in-use count */
  46:   int int13ext;                 /* IBM/MS INT 13 extensions supported? */
  47:   struct device part[DEV_PER_DRIVE];    /* disks and partitions */
  48:   struct device subpart[SUB_PER_DRIVE]; /* subpartitions */
  49: } wini[MAX_DRIVES], *w_wn;
  50: 
  51: PRIVATE int nr_drives = MAX_DRIVES;     /* Number of drives */
  52: PRIVATE int w_drive;                    /* selected drive */
  53: PRIVATE struct device *w_dv;            /* device's base and size */
  54: 
  55: FORWARD _PROTOTYPE( struct device *w_prepare, (int device) );
  56: FORWARD _PROTOTYPE( char *w_name, (void) );
  57: FORWARD _PROTOTYPE( int w_transfer, (int proc_nr, int opcode, off_t position,
  58:                                         iovec_t *iov, unsigned nr_req) );
  59: FORWARD _PROTOTYPE( int w_do_open, (struct driver *dp, message *m_ptr) );
  60: FORWARD _PROTOTYPE( int w_do_close, (struct driver *dp, message *m_ptr) );
  61: FORWARD _PROTOTYPE( void w_init, (void) );
  62: FORWARD _PROTOTYPE( void w_geometry, (struct partition *entry));
  63: 
  64: 
  65: /* Entry points to this driver. */
  66: PRIVATE struct driver w_dtab = {
  67:   w_name,       /* current device's name */
  68:   w_do_open,    /* open or mount request, initialize device */
  69:   w_do_close,   /* release device */
  70:   do_diocntl,   /* get or set a partition's geometry */
  71:   w_prepare,    /* prepare for I/O on a given minor device */
  72:   w_transfer,   /* do the I/O */
  73:   nop_cleanup,  /* no cleanup needed */
  74:   w_geometry    /* tell the geometry of the disk */
  75: };
  76: 
  77: 
  78: /*===========================================================================*
  79:  *                              bios_winchester_task                         *
  80:  *===========================================================================*/
  81: PUBLIC void bios_winchester_task()
  82: {
  83:   driver_task(&w_dtab);
  84: }
  85: 
  86: 
  87: /*===========================================================================*
  88:  *                              w_prepare                                    *
  89:  *===========================================================================*/
  90: PRIVATE struct device *w_prepare(device)
  91: int device;
  92: {
  93: /* Prepare for I/O on a device. */
  94: 
  95:   if (device < NR_DEVICES) {                     /* d0, d0p[0-3], d1, ... */
  96:         w_drive = device / DEV_PER_DRIVE;       /* save drive number */
  97:         w_wn = &wini[w_drive];
  98:         w_dv = &w_wn->part[device % DEV_PER_DRIVE];
  99:   } else
 100:   if ((unsigned) (device -= MINOR_d0p0s0) < NR_SUBDEVS) {/*d[0-7]p[0-3]s[0-3]*/
 101:         w_drive = device / SUB_PER_DRIVE;
 102:         w_wn = &wini[w_drive];
 103:         w_dv = &w_wn->subpart[device % SUB_PER_DRIVE];
 104:   } else {
 105:         return(NIL_DEV);
 106:   }
 107:   return(w_drive < nr_drives ? w_dv : NIL_DEV);
 108: }
 109: 
 110: 
 111: /*===========================================================================*
 112:  *                              w_name                                       *
 113:  *===========================================================================*/
 114: PRIVATE char *w_name()
 115: {
 116: /* Return a name for the current device. */
 117:   static char name[] = "bios-d0";
 118: 
 119:   name[6] = '0' + w_drive;
 120:   return name;
 121: }
 122: 
 123: 
 124: /*===========================================================================*
 125:  *                              w_transfer                                   *
 126:  *===========================================================================*/
 127: PRIVATE int w_transfer(proc_nr, opcode, position, iov, nr_req)
 128: int proc_nr;                    /* process doing the request */
 129: int opcode;                     /* DEV_GATHER or DEV_SCATTER */
 130: off_t position;                 /* offset on device to read or write */
 131: iovec_t *iov;                   /* pointer to read or write request vector */
 132: unsigned nr_req;                /* length of request vector */
 133: {
 134:   struct wini *wn = w_wn;
 135:   iovec_t *iop, *iov_end = iov + nr_req;
 136:   int errors;
 137:   unsigned nbytes, count, chunk;
 138:   unsigned long block;
 139:   unsigned long dv_size = cv64ul(w_dv->dv_size);
 140:   unsigned secspcyl = wn->heads * wn->sectors;
 141:   phys_bytes user_base = proc_vir2phys(proc_addr(proc_nr), 0);
 142:   static struct int13ext_rw {
 143:         u8_t    len;
 144:         u8_t    res1;
 145:         u16_t   count;
 146:         u16_t   addr[2];
 147:         u32_t   block[2];
 148:   } i13e_rw;
 149: 
 150:   /* Check disk address. */
 151:   if ((position & SECTOR_MASK) != 0) return(EINVAL);
 152: 
 153:   errors = 0;
 154: 
 155:   while (nr_req > 0) {
 156:         /* How many bytes to transfer? */
 157:         nbytes = 0;
 158:         for (iop = iov; iop < iov_end; iop++) {
 159:                 if (nbytes + iop->iov_size > DMA_BUF_SIZE) {
 160:                         /* Don't do half a segment if you can avoid it. */
 161:                         if (nbytes == 0) nbytes = DMA_BUF_SIZE;
 162:                         break;
 163:                 }
 164:                 nbytes += iop->iov_size;
 165:         }
 166:         if ((nbytes & SECTOR_MASK) != 0) return(EINVAL);
 167: 
 168:         /* Which block on disk and how close to EOF? */
 169:         if (position >= dv_size) return(OK);             /* At EOF */
 170:         if (position + nbytes > dv_size) nbytes = dv_size - position;
 171:         block = div64u(add64ul(w_dv->dv_base, position), SECTOR_SIZE);
 172: 
 173:         /* Degrade to per-sector mode if there were errors. */
 174:         if (errors > 0) nbytes = SECTOR_SIZE;
 175: 
 176:         if (opcode == DEV_SCATTER) {
 177:                 /* Copy from user space to the DMA buffer. */
 178:                 count = 0;
 179:                 for (iop = iov; count < nbytes; iop++) {
 180:                         chunk = iov->iov_size;
 181:                         if (count + chunk > nbytes) chunk = nbytes - count;
 182:                         phys_copy(user_base + iop->iov_addr, tmp_phys + count,
 183:                                                         (phys_bytes) chunk);
 184:                         count += chunk;
 185:                 }
 186:         }
 187: 
 188:         /* Do the transfer */
 189:         if (wn->int13ext) {
 190:                 /* Set up an extended read or write BIOS call. */
 191:                 reg86.b.intno = 0x13;
 192:                 reg86.w.ax = opcode == DEV_SCATTER ? 0x4300 : 0x4200;
 193:                 reg86.b.dl = 0x80 + w_drive;
 194:                 reg86.w.si = vir2phys(&i13e_rw) % HCLICK_SIZE;
 195:                 reg86.w.ds = vir2phys(&i13e_rw) / HCLICK_SIZE;
 196:                 i13e_rw.len = 0x10;
 197:                 i13e_rw.res1 = 0;
 198:                 i13e_rw.count = nbytes >> SECTOR_SHIFT;
 199:                 i13e_rw.addr[0] = tmp_phys % HCLICK_SIZE;
 200:                 i13e_rw.addr[1] = tmp_phys / HCLICK_SIZE;
 201:                 i13e_rw.block[0] = block;
 202:                 i13e_rw.block[1] = 0;
 203:         } else {
 204:                 /* Set up an ordinary read or write BIOS call. */
 205:                 unsigned cylinder = block / secspcyl;
 206:                 unsigned sector = (block % wn->sectors) + 1;
 207:                 unsigned head = (block % secspcyl) / wn->sectors;
 208: 
 209:                 reg86.b.intno = 0x13;
 210:                 reg86.b.ah = opcode == DEV_SCATTER ? 0x03 : 0x02;
 211:                 reg86.b.al = nbytes >> SECTOR_SHIFT;
 212:                 reg86.w.bx = tmp_phys % HCLICK_SIZE;
 213:                 reg86.w.es = tmp_phys / HCLICK_SIZE;
 214:                 reg86.b.ch = cylinder & 0xFF;
 215:                 reg86.b.cl = sector | ((cylinder & 0x300) >> 2);
 216:                 reg86.b.dh = head;
 217:                 reg86.b.dl = 0x80 + w_drive;
 218:         }
 219:         level0(int86);
 220:         if (reg86.w.f & 0x0001) {
 221:                 /* An error occurred, try again sector by sector unless */
 222:                 if (++errors == 2) return(EIO);
 223:                 continue;
 224:         }
 225: 
 226:         if (opcode == DEV_GATHER) {
 227:                 /* Copy from the DMA buffer to user space. */
 228:                 count = 0;
 229:                 for (iop = iov; count < nbytes; iop++) {
 230:                         chunk = iov->iov_size;
 231:                         if (count + chunk > nbytes) chunk = nbytes - count;
 232:                         phys_copy(tmp_phys + count, user_base + iop->iov_addr,
 233:                                                         (phys_bytes) chunk);
 234:                         count += chunk;
 235:                 }
 236:         }
 237: 
 238:         /* Book the bytes successfully transferred. */
 239:         position += nbytes;
 240:         for (;;) {
 241:                 if (nbytes < iov->iov_size) {
 242:                         /* Not done with this one yet. */
 243:                         iov->iov_addr += nbytes;
 244:                         iov->iov_size -= nbytes;
 245:                         break;
 246:                 }
 247:                 nbytes -= iov->iov_size;
 248:                 iov->iov_addr += iov->iov_size;
 249:                 iov->iov_size = 0;
 250:                 if (nbytes == 0) {
 251:                         /* The rest is optional, so we return to give FS a
 252:                          * chance to think it over.
 253:                          */
 254:                         return(OK);
 255:                 }
 256:                 iov++;
 257:                 nr_req--;
 258:         }
 259:   }
 260:   return(OK);
 261: }
 262: 
 263: 
 264: /*============================================================================*
 265:  *                              w_do_open                                     *
 266:  *============================================================================*/
 267: PRIVATE int w_do_open(dp, m_ptr)
 268: struct driver *dp;
 269: message *m_ptr;
 270: {
 271: /* Device open: Initialize the controller and read the partition table. */
 272: 
 273:   static int init_done = FALSE;
 274: 
 275:   if (!init_done) { w_init(); init_done = TRUE; }
 276: 
 277:   if (w_prepare(m_ptr->DEVICE) == NIL_DEV) return(ENXIO);
 278: 
 279:   if (w_wn->open_ct++ == 0) {
 280:         /* Partition the disk. */
 281:         partition(&w_dtab, w_drive * DEV_PER_DRIVE, P_PRIMARY);
 282:   }
 283:   return(OK);
 284: }
 285: 
 286: 
 287: /*============================================================================*
 288:  *                              w_do_close                                    *
 289:  *============================================================================*/
 290: PRIVATE int w_do_close(dp, m_ptr)
 291: struct driver *dp;
 292: message *m_ptr;
 293: {
 294: /* Device close: Release a device. */
 295: 
 296:   if (w_prepare(m_ptr->DEVICE) == NIL_DEV) return(ENXIO);
 297:   w_wn->open_ct--;
 298:   return(OK);
 299: }
 300: 
 301: 
 302: /*===========================================================================*
 303:  *                              w_init                                       *
 304:  *===========================================================================*/
 305: PRIVATE void w_init()
 306: {
 307: /* This routine is called at startup to initialize the drive parameters. */
 308: 
 309:   int drive;
 310:   struct wini *wn;
 311:   unsigned long capacity;
 312:   static struct int13ext_params {
 313:         u16_t   len;
 314:         u16_t   flags;
 315:         u32_t   cylinders;
 316:         u32_t   heads;
 317:         u32_t   sectors;
 318:         u32_t   capacity[2];
 319:         u16_t   bts_per_sec;
 320:         u16_t   config[2];
 321:   } i13e_par;
 322: 
 323:   /* Get the geometry of the drives */
 324:   for (drive = 0; drive < nr_drives; drive++) {
 325:         (void) w_prepare(drive * DEV_PER_DRIVE);
 326:         wn = w_wn;
 327:         reg86.b.intno = 0x13;
 328:         reg86.b.ah = 0x08;      /* Get drive parameters. */
 329:         reg86.b.dl = 0x80 + drive;
 330:         level0(int86);
 331:         nr_drives = !(reg86.w.f & 0x0001) ? reg86.b.dl : drive;
 332:         if (nr_drives > MAX_DRIVES) nr_drives = MAX_DRIVES;
 333:         if (drive >= nr_drives) break;
 334: 
 335:         wn->heads = reg86.b.dh + 1;
 336:         wn->sectors = reg86.b.cl & 0x3F;
 337:         wn->cylinders = (reg86.b.ch | ((reg86.b.cl & 0xC0) << 2)) + 1;
 338: 
 339:         capacity = (unsigned long) wn->cylinders * wn->heads * wn->sectors;
 340: 
 341:         reg86.b.intno = 0x13;
 342:         reg86.b.ah = 0x41;      /* INT 13 Extensions - Installation check */
 343:         reg86.w.bx = 0x55AA;
 344:         reg86.b.dl = 0x80 + drive;
 345:         if (pc_at) level0(int86);
 346: 
 347:         if (!(reg86.w.f & 0x0001) && reg86.w.bx == 0xAA55
 348:                                 && (reg86.w.cx & 0x0001)) {
 349:                 /* INT 13 Extensions available. */
 350:                 reg86.b.intno = 0x13;
 351:                 reg86.b.ah = 0x48;      /* Ext. Get drive parameters. */
 352:                 reg86.b.dl = 0x80 + drive;
 353:                 reg86.w.si = vir2phys(&i13e_par) % HCLICK_SIZE;
 354:                 reg86.w.ds = vir2phys(&i13e_par) / HCLICK_SIZE;
 355:                 i13e_par.len = 0x001E;  /* Input size of parameter packet */
 356:                 level0(int86);
 357:                 if (!(reg86.w.f & 0x0001)) {
 358:                         wn->int13ext = 1;        /* Extensions can be used. */
 359:                         capacity = i13e_par.capacity[0];
 360:                         if (i13e_par.capacity[1] != 0) capacity = 0xFFFFFFFF;
 361:                 }
 362:         }
 363: 
 364:         if (wn->int13ext) {
 365:                 printf("%s: %lu sectors\n", w_name(), capacity);
 366:         } else {
 367:                 printf("%s: %d cylinders, %d heads, %d sectors per track\n",
 368:                         w_name(), wn->cylinders, wn->heads, wn->sectors);
 369:         }
 370:         wn->part[0].dv_size = mul64u(capacity, SECTOR_SIZE);
 371:   }
 372: }
 373: 
 374: 
 375: /*============================================================================*
 376:  *                              w_geometry                                    *
 377:  *============================================================================*/
 378: PRIVATE void w_geometry(entry)
 379: struct partition *entry;
 380: {
 381:   entry->cylinders = w_wn->cylinders;
 382:   entry->heads = w_wn->heads;
 383:   entry->sectors = w_wn->sectors;
 384: }
 385: #endif /* ENABLE_BIOS_WINI */