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 */