1: /* This file contains the "DOS file as disk" driver.      Author: Kees J. Bot
   2:  * It uses MS-DOS calls to read or write a large DOS            4 May 1998
   3:  * file that is used as a Minix disk.  Minix will have to
   4:  * be started under DOS for this to work, of course.
   5:  *
   6:  * The file contains two entry points:
   7:  *
   8:  *       dosfile_task:  main entry when system is brought up
   9:  *       dosfile_stop:  called on halt or reboot to close DOS files
  10:  */
  11: 
  12: #include "kernel.h"
  13: #include "driver.h"
  14: #include "drvlib.h"
  15: #include <stdlib.h>
  16: #include <ibm/int86.h>
  17: 
  18: #if ENABLE_DOSFILE
  19: 
  20: /* Limit byte count to something DOS will find reasonable. */
  21: #define MAX_COUNT       MIN(DMA_BUF_SIZE, 16384)
  22: 
  23: /* Parameters for the disk drive. */
  24: #define MAX_DRIVES         4    /* this driver supports this many "drives" */
  25: #define NR_DEVICES      (MAX_DRIVES * DEV_PER_DRIVE)
  26: 
  27: /* Variables. */
  28: PRIVATE struct drive {          /* main drive struct, one entry per drive */
  29:   unsigned long curpos;         /* current position within the "disk" */
  30:   unsigned clustersize;         /* cluster size of the drive the file is on */
  31:   unsigned open_ct;             /* in-use count */
  32:   int dfd;                      /* DOS file descriptor if open_ct > 0 */
  33:   char rw;                      /* opened read-write? */
  34:   struct device part[DEV_PER_DRIVE];    /* disks and partitions */
  35: } drive[MAX_DRIVES], *d_dr;
  36: 
  37: PRIVATE int d_drive;                    /* selected drive */
  38: PRIVATE struct device *d_dv;            /* device's base and size */
  39: 
  40: FORWARD _PROTOTYPE( struct device *d_prepare, (int device) );
  41: FORWARD _PROTOTYPE( char *d_name, (void) );
  42: FORWARD _PROTOTYPE( int d_transfer, (int proc_nr, int opcode, off_t position,
  43:                                         iovec_t *iov, unsigned nr_req) );
  44: FORWARD _PROTOTYPE( int d_do_open, (struct driver *dp, message *m_ptr) );
  45: FORWARD _PROTOTYPE( int d_do_close, (struct driver *dp, message *m_ptr) );
  46: FORWARD _PROTOTYPE( void dosclose, (int handle) );
  47: FORWARD _PROTOTYPE( char *doserror, (unsigned err) );
  48: FORWARD _PROTOTYPE( void d_geometry, (struct partition *entry));
  49: 
  50: 
  51: /* Entry points to this driver. */
  52: PRIVATE struct driver d_dtab = {
  53:   d_name,       /* current device's name */
  54:   d_do_open,    /* open or mount request, initialize device */
  55:   d_do_close,   /* release device */
  56:   do_diocntl,   /* get or set a partition's geometry */
  57:   d_prepare,    /* prepare for I/O on a given minor device */
  58:   d_transfer,   /* do the I/O */
  59:   nop_cleanup,  /* no cleanup needed */
  60:   d_geometry    /* tell the geometry of the disk */
  61: };
  62: 
  63: 
  64: /*===========================================================================*
  65:  *                              dosfile_task                                 *
  66:  *===========================================================================*/
  67: PUBLIC void dosfile_task()
  68: {
  69:   driver_task(&d_dtab);
  70: }
  71: 
  72: 
  73: /*===========================================================================*
  74:  *                              d_prepare                                    *
  75:  *===========================================================================*/
  76: PRIVATE struct device *d_prepare(device)
  77: int device;
  78: {
  79: /* Prepare for I/O on a device. */
  80: 
  81:   if (device < NR_DEVICES) {                     /* d0, d0p[0-3], d1, ... */
  82:         d_drive = device / DEV_PER_DRIVE;       /* save drive number */
  83:         d_dr = &drive[d_drive];
  84:         d_dv = &d_dr->part[device % DEV_PER_DRIVE];
  85:   } else {
  86:         return(NIL_DEV);
  87:   }
  88:   return(d_drive < MAX_DRIVES ? d_dv : NIL_DEV);
  89: }
  90: 
  91: 
  92: /*===========================================================================*
  93:  *                              d_name                                       *
  94:  *===========================================================================*/
  95: PRIVATE char *d_name()
  96: {
  97: /* Return a name for the current device. */
  98:   static char name[] = "dosfile-d0";
  99: 
 100:   name[9] = '0' + d_drive;
 101:   return name;
 102: }
 103: 
 104: 
 105: /*===========================================================================*
 106:  *                              d_transfer                                   *
 107:  *===========================================================================*/
 108: PRIVATE int d_transfer(proc_nr, opcode, position, iov, nr_req)
 109: int proc_nr;                    /* process doing the request */
 110: int opcode;                     /* DEV_GATHER or DEV_SCATTER */
 111: off_t position;                 /* offset on device to read or write */
 112: iovec_t *iov;                   /* pointer to read or write request vector */
 113: unsigned nr_req;                /* length of request vector */
 114: {
 115:   struct drive *dr = d_dr;
 116:   iovec_t *iop, *iov_end = iov + nr_req;
 117:   unsigned nbytes, count, chunk;
 118:   unsigned long pos;
 119:   unsigned long dv_size = cv64ul(d_dv->dv_size);
 120:   phys_bytes user_base = proc_vir2phys(proc_addr(proc_nr), 0);
 121: 
 122:   while (nr_req > 0) {
 123:         /* How many bytes to transfer? */
 124:         nbytes = 0;
 125:         for (iop = iov; iop < iov_end; iop++) {
 126:                 if (nbytes + iop->iov_size > MAX_COUNT) {
 127:                         /* Don't do half a segment if you can avoid it. */
 128:                         if (nbytes == 0) nbytes = MAX_COUNT;
 129:                         break;
 130:                 }
 131:                 nbytes += iop->iov_size;
 132:         }
 133: 
 134:         /* Which block on disk and how close to EOF? */
 135:         pos = position;
 136:         if (pos >= dv_size) return(OK);          /* At EOF */
 137:         if (pos + nbytes > dv_size) nbytes = dv_size - pos;
 138:         pos = cv64ul(d_dv->dv_base) + pos;
 139: 
 140:         if (opcode == DEV_SCATTER) {
 141:                 /* Copy from user space to the DMA buffer. */
 142:                 count = 0;
 143:                 for (iop = iov; count < nbytes; iop++) {
 144:                         chunk = iov->iov_size;
 145:                         if (count + chunk > nbytes) chunk = nbytes - count;
 146:                         phys_copy(user_base + iop->iov_addr, tmp_phys + count,
 147:                                                         (phys_bytes) chunk);
 148:                         count += chunk;
 149:                 }
 150:         }
 151: 
 152:         /* Need to seek to a new position? */
 153:         if (pos != dr->curpos) {
 154:                 reg86.b.intno = 0x21;
 155:                 reg86.w.ax = 0x4200;    /* LSEEK absolute */
 156:                 reg86.w.bx = dr->dfd;
 157:                 reg86.w.dx = pos & 0xFFFF;
 158:                 reg86.w.cx = pos >> 16;
 159:                 level0(int86);
 160:                 if (reg86.w.f & 0x0001) {        /* Seek failed */
 161:                         printf("%s: seek to %lu failed: %s\n",
 162:                                 d_name(), pos, doserror(reg86.w.ax));
 163:                         dr->curpos = -1;
 164:                         return(EIO);
 165:                 }
 166:                 dr->curpos = pos;
 167:         }
 168: 
 169:         /* Do the transfer using a DOS read or write call */
 170:         reg86.b.intno = 0x21;
 171:         reg86.b.ah = opcode == DEV_SCATTER ? 0x40 : 0x3F;
 172:         reg86.w.bx = dr->dfd;
 173:         reg86.w.cx = nbytes;
 174:         reg86.w.dx = tmp_phys % HCLICK_SIZE;
 175:         reg86.w.ds = tmp_phys / HCLICK_SIZE;
 176:         level0(int86);
 177:         if ((reg86.w.f & 0x0001) || reg86.w.ax != nbytes) {
 178:                 /* Read or write error or unexpected EOF */
 179:                 dr->curpos = -1;
 180:                 return(EIO);
 181:         }
 182:         dr->curpos += nbytes;
 183: 
 184:         if (opcode == DEV_GATHER) {
 185:                 /* Copy from the DMA buffer to user space. */
 186:                 count = 0;
 187:                 for (iop = iov; count < nbytes; iop++) {
 188:                         chunk = iov->iov_size;
 189:                         if (count + chunk > nbytes) chunk = nbytes - count;
 190:                         phys_copy(tmp_phys + count, user_base + iop->iov_addr,
 191:                                                         (phys_bytes) chunk);
 192:                         count += chunk;
 193:                 }
 194:         }
 195: 
 196:         /* Book the bytes successfully transferred. */
 197:         position += nbytes;
 198:         for (;;) {
 199:                 if (nbytes < iov->iov_size) {
 200:                         /* Not done with this one yet. */
 201:                         iov->iov_addr += nbytes;
 202:                         iov->iov_size -= nbytes;
 203:                         break;
 204:                 }
 205:                 nbytes -= iov->iov_size;
 206:                 iov->iov_addr += iov->iov_size;
 207:                 iov->iov_size = 0;
 208:                 if (nbytes == 0) {
 209:                         /* The rest is optional, so we return to give FS a
 210:                          * chance to think it over.
 211:                          */
 212:                         return(OK);
 213:                 }
 214:                 iov++;
 215:                 nr_req--;
 216:         }
 217:   }
 218:   return(OK);
 219: }
 220: 
 221: 
 222: /*============================================================================*
 223:  *                              d_do_open                                     *
 224:  *============================================================================*/
 225: PRIVATE int d_do_open(dp, m_ptr)
 226: struct driver *dp;
 227: message *m_ptr;
 228: {
 229: /* Device open: Open the DOS file and read the partition table. */
 230:   char *file;
 231:   struct drive *dr;
 232: 
 233:   if (d_prepare(m_ptr->DEVICE) == NIL_DEV) return(ENXIO);
 234:   dr = d_dr;
 235: 
 236:   if (dr->open_ct == 0) {
 237:         if ((file = getenv(d_name())) == NULL) {
 238:                 printf("%s: boot environent variable '%s' not set\n",
 239:                         d_name(), d_name());
 240:                 return(EIO);
 241:         }
 242: 
 243:         /* Check if DOS 3.0 or better present. */
 244:         reg86.b.intno = 0x21;
 245:         reg86.w.ax = 0x3000;    /* GET DOS VERSION */
 246:         level0(int86);
 247:         if (reg86.b.al < 3) {
 248:                 printf("%s: No DOS 3.0+ running\n", d_name());
 249:                 return(EIO);
 250:         }
 251: 
 252:         /* Open the DOS file. */
 253:         dr->rw = 1;
 254:         reg86.b.intno = 0x21;
 255:         reg86.w.ax = 0x3D22;    /* OPEN read-write & deny write */
 256:         reg86.w.dx = vir2phys(file) % HCLICK_SIZE;
 257:         reg86.w.ds = vir2phys(file) / HCLICK_SIZE;
 258:         level0(int86);
 259:         if ((reg86.w.f & 0x0001) && reg86.w.ax == 5) {
 260:                 /* Open failed (access denied), try read-only */
 261:                 dr->rw = 0;
 262:                 reg86.b.intno = 0x21;
 263:                 reg86.w.ax = 0x3D40;    /* OPEN read-only */
 264:                 reg86.w.dx = vir2phys(file) % HCLICK_SIZE;
 265:                 reg86.w.ds = vir2phys(file) / HCLICK_SIZE;
 266:                 level0(int86);
 267:         }
 268:         if (reg86.w.f & 0x0001) {
 269:                 printf("%s: can't open '%s': %s\n",
 270:                         d_name(), file, doserror(reg86.w.ax));
 271:                 return(EIO);
 272:         }
 273:         dr->dfd = reg86.w.ax;            /* File handle */
 274: 
 275:         reg86.b.intno = 0x21;
 276:         reg86.w.ax = 0x4202;    /* LSEEK to end */
 277:         reg86.w.bx = dr->dfd;
 278:         reg86.w.dx = 0;
 279:         reg86.w.cx = 0;
 280:         level0(int86);
 281:         if (reg86.w.f & 0x0001) {        /* Seek failed */
 282:                 printf("%s: can't determine size of '%s': %s\n",
 283:                         d_name(), file, doserror(reg86.w.ax));
 284:                 dosclose(dr->dfd);
 285:                 return(EIO);
 286:         }
 287:         dr->curpos = ((u32_t) reg86.w.dx << 16) | reg86.w.ax;
 288:         if (cv64ul(dr->part[0].dv_size) != dr->curpos) {
 289:                 printf("%s: using '%s', %lu bytes%s\n",
 290:                         d_name(), file, dr->curpos,
 291:                         dr->rw ? "" : " (read-only)");
 292:                 dr->part[0].dv_size = cvul64(dr->curpos);
 293:         }
 294: 
 295:         reg86.b.intno = 0x21;
 296:         reg86.b.ah = 0x1C;      /* GET ALLOCATION INFO */
 297:         reg86.b.dl = 0x00;      /* default drive or ... */
 298:         if ((unsigned) ((file[0] & 0xDF) - 'A') < 26 && file[1] == ':') {
 299:                 reg86.b.dl = (file[0] & 0xDF) - '@';     /* ... this drive */
 300:         }
 301:         level0(int86);
 302:         if (reg86.b.al == 0 || reg86.b.al > 63 ||
 303:                         (file[0] == '/' && file[1] == '/')) {
 304:                 /* Weird return value or a network path, 32 will do. */
 305:                 reg86.b.al = 32;
 306:         }
 307:         dr->clustersize = reg86.b.al << SECTOR_SHIFT;
 308: 
 309:         /* Partition the "disk". */
 310:         partition(&d_dtab, d_drive * DEV_PER_DRIVE, P_PRIMARY);
 311:   }
 312: 
 313:   /* Record one more user. */
 314:   dr->open_ct++;
 315: 
 316:   if (!dr->rw && (m_ptr->COUNT & W_BIT)) {
 317:         /* Read-only "disk" can't be opened for writing. */
 318:         if (--dr->open_ct == 0) dosclose(dr->dfd);
 319:         return(EACCES);
 320:   }
 321:   return(OK);
 322: }
 323: 
 324: 
 325: /*============================================================================*
 326:  *                              d_do_close                                    *
 327:  *============================================================================*/
 328: PRIVATE int d_do_close(dp, m_ptr)
 329: struct driver *dp;
 330: message *m_ptr;
 331: {
 332: /* Device close: Release a device. */
 333: 
 334:   if (d_prepare(m_ptr->DEVICE) == NIL_DEV) return(ENXIO);
 335:   if (--d_dr->open_ct == 0) dosclose(d_dr->dfd);
 336:   return(OK);
 337: }
 338: 
 339: 
 340: /*============================================================================*
 341:  *                              dosfile_stop                                  *
 342:  *============================================================================*/
 343: PUBLIC void dosfile_stop()
 344: {
 345: /* Prepare for halt or reboot, i.e. make sure all is closed. */
 346: 
 347:   struct drive *dr;
 348: 
 349:   for (dr = drive; dr < drive + MAX_DRIVES; dr++) {
 350:         if (dr->open_ct > 0) dosclose(dr->dfd);
 351:   }
 352: }
 353: 
 354: 
 355: /*============================================================================*
 356:  *                              dosclose                                      *
 357:  *============================================================================*/
 358: PRIVATE void dosclose(handle)
 359: int handle;                             /* file handle */
 360: {
 361: /* Close a DOS file. */
 362:   reg86.b.intno = 0x21;
 363:   reg86.b.ah = 0x3E;    /* CLOSE */
 364:   reg86.w.bx = handle;
 365:   level0(int86);
 366: }
 367: 
 368: 
 369: /*============================================================================*
 370:  *                              doserror                                      *
 371:  *============================================================================*/
 372: PRIVATE char *doserror(err)
 373: unsigned err;
 374: {
 375: /* Translate some DOS error codes to text. */
 376:   static struct errlist {
 377:         int     err;
 378:         char    *what;
 379:   } errlist[] = {
 380:         {  0, "No error" },
 381:         {  1, "Function number invalid" },
 382:         {  2, "File not found" },
 383:         {  3, "Path not found" },
 384:         {  4, "Too many open files" },
 385:         {  5, "Access denied" },
 386:         {  6, "Invalid handle" },
 387:         { 12, "Access code invalid" },
 388:   };
 389:   struct errlist *ep;
 390:   static char unknown[]= "Error 65535";
 391:   char *p;
 392: 
 393:   for (ep = errlist; ep < errlist + sizeof(errlist)/sizeof(errlist[0]); ep++) {
 394:         if (ep->err == err) return ep->what;
 395:   }
 396:   p = unknown + sizeof(unknown) - 1;
 397:   do *--p = '0' + (err % 10); while ((err /= 10) > 0);
 398:   strcpy(unknown + 6, p);
 399:   return unknown;
 400: }
 401: 
 402: 
 403: /*============================================================================*
 404:  *                              d_geometry                                    *
 405:  *============================================================================*/
 406: PRIVATE void d_geometry(entry)
 407: struct partition *entry;
 408: {
 409:   /* The number of sectors per track is chosen to match the cluster size
 410:    * to make it easy for people to place partitions on cluster boundaries.
 411:    */
 412:   entry->cylinders = cv64ul(d_dr->part[0].dv_size) / d_dr->clustersize / 64;
 413:   entry->heads = 64;
 414:   entry->sectors = d_dr->clustersize >> SECTOR_SHIFT;
 415: }
 416: #endif /* ENABLE_DOSFILE */