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