bios_wini.c

Go to the documentation of this file.
00001 /* This file contains the "device dependent" part of a hard disk driver that
00002  * uses the ROM BIOS.  It makes a call and just waits for the transfer to
00003  * happen.  It is not interrupt driven and thus will (*) have poor performance.
00004  * The advantage is that it should work on virtually any PC, XT, 386, PS/2
00005  * or clone.  The demo disk uses this driver.  It is suggested that all
00006  * MINIX users try the other drivers, and use this one only as a last resort,
00007  * if all else fails.
00008  *
00009  * (*) The performance is within 10% of the AT driver for reads on any disk
00010  *     and writes on a 2:1 interleaved disk, it will be DMA_BUF_SIZE bytes
00011  *     per revolution for a minimum of 60 kb/s for writes to 1:1 disks.
00012  *
00013  * The file contains one entry point:
00014  *
00015  *       bios_winchester_task:  main entry when system is brought up
00016  *
00017  *
00018  * Changes:
00019  *      30 Apr 1992 by Kees J. Bot: device dependent/independent split.
00020  *      14 May 2000 by Kees J. Bot: d-d/i rewrite.
00021  */
00022 
00023 #include "../drivers.h"
00024 #include "../libdriver/driver.h"
00025 #include "../libdriver/drvlib.h"
00026 #include <minix/sysutil.h>
00027 #include <minix/keymap.h>
00028 #include <sys/ioc_disk.h>
00029 #include <ibm/int86.h>
00030 #include <assert.h>
00031 
00032 #define ME "BIOS_WINI"
00033 
00034 /* Error codes */
00035 #define ERR              (-1)   /* general error */
00036 
00037 /* Parameters for the disk drive. */
00038 #define MAX_DRIVES         8    /* this driver supports 8 drives (d0 - d7)*/
00039 #define MAX_SECS         255    /* bios can transfer this many sectors */
00040 #define NR_MINORS      (MAX_DRIVES * DEV_PER_DRIVE)
00041 #define SUB_PER_DRIVE   (NR_PARTITIONS * NR_PARTITIONS)
00042 #define NR_SUBDEVS      (MAX_DRIVES * SUB_PER_DRIVE)
00043 
00044 PRIVATE int pc_at = 1;  /* What about PC XTs? */
00045 
00046 /* Variables. */
00047 PRIVATE struct wini {           /* main drive struct, one entry per drive */
00048   unsigned cylinders;           /* number of cylinders */
00049   unsigned heads;               /* number of heads */
00050   unsigned sectors;             /* number of sectors per track */
00051   unsigned open_ct;             /* in-use count */
00052   int drive_id;                 /* Drive ID at BIOS level */
00053   int present;                  /* Valid drive */
00054   int int13ext;                 /* IBM/MS INT 13 extensions supported? */
00055   struct device part[DEV_PER_DRIVE];    /* disks and partitions */
00056   struct device subpart[SUB_PER_DRIVE]; /* subpartitions */
00057 } wini[MAX_DRIVES], *w_wn;
00058 
00059 PRIVATE int w_drive;                    /* selected drive */
00060 PRIVATE struct device *w_dv;            /* device's base and size */
00061 PRIVATE vir_bytes bios_buf_vir, bios_buf_size;
00062 PRIVATE phys_bytes bios_buf_phys;
00063 PRIVATE int remap_first = 0;            /* Remap drives for CD HD emulation */
00064 
00065 _PROTOTYPE(int main, (void) );
00066 FORWARD _PROTOTYPE( struct device *w_prepare, (int device) );
00067 FORWARD _PROTOTYPE( char *w_name, (void) );
00068 FORWARD _PROTOTYPE( int w_transfer, (int proc_nr, int opcode, off_t position,
00069                                         iovec_t *iov, unsigned nr_req) );
00070 FORWARD _PROTOTYPE( int w_do_open, (struct driver *dp, message *m_ptr) );
00071 FORWARD _PROTOTYPE( int w_do_close, (struct driver *dp, message *m_ptr) );
00072 FORWARD _PROTOTYPE( void w_init, (void) );
00073 FORWARD _PROTOTYPE( void w_geometry, (struct partition *entry));
00074 FORWARD _PROTOTYPE( int w_other, (struct driver *dp, message *m_ptr)    );
00075 
00076 /* Entry points to this driver. */
00077 PRIVATE struct driver w_dtab = {
00078   w_name,       /* current device's name */
00079   w_do_open,    /* open or mount request, initialize device */
00080   w_do_close,   /* release device */
00081   do_diocntl,   /* get or set a partition's geometry */
00082   w_prepare,    /* prepare for I/O on a given minor device */
00083   w_transfer,   /* do the I/O */
00084   nop_cleanup,  /* no cleanup needed */
00085   w_geometry,   /* tell the geometry of the disk */
00086   nop_signal,           /* no cleanup needed on shutdown */
00087   nop_alarm,            /* ignore leftover alarms */
00088   nop_cancel,           /* ignore CANCELs */
00089   nop_select,           /* ignore selects */
00090   w_other,              /* catch-all for unrecognized commands and ioctls */
00091   NULL                  /* leftover hardware interrupts */
00092 };
00093 
00094 /*===========================================================================*
00095  *                              bios_winchester_task                         *
00096  *===========================================================================*/
00097 PUBLIC int main()
00098 {
00099   long v;
00100   struct sigaction sa;
00101  
00102   sa.sa_handler = SIG_MESS;
00103   sigemptyset(&sa.sa_mask);
00104   sa.sa_flags = 0;
00105   if (sigaction(SIGTERM,&sa,NULL)<0) panic("BIOS","sigaction failed", errno);
00106 
00107   v= 0;
00108   env_parse("bios_remap_first", "d", 0, &v, 0, 1);
00109   remap_first= v;
00110 
00111 /* Set special disk parameters then call the generic main loop. */
00112   driver_task(&w_dtab);
00113   return(OK);
00114 }
00115 
00116 /*===========================================================================*
00117  *                              w_prepare                                    *
00118  *===========================================================================*/
00119 PRIVATE struct device *w_prepare(device)
00120 int device;
00121 {
00122 /* Prepare for I/O on a device. */
00123 
00124   if (device < NR_MINORS) {                     /* d0, d0p[0-3], d1, ... */
00125         w_drive = device / DEV_PER_DRIVE;       /* save drive number */
00126         w_wn = &wini[w_drive];
00127         w_dv = &w_wn->part[device % DEV_PER_DRIVE];
00128   } else
00129   if ((unsigned) (device -= MINOR_d0p0s0) < NR_SUBDEVS) {/*d[0-7]p[0-3]s[0-3]*/
00130         w_drive = device / SUB_PER_DRIVE;
00131         w_wn = &wini[w_drive];
00132         w_dv = &w_wn->subpart[device % SUB_PER_DRIVE];
00133   } else {
00134         return(NIL_DEV);
00135   }
00136   if (w_drive >= MAX_DRIVES || !w_wn->present)
00137         return NIL_DEV;
00138   return(w_dv);
00139 }
00140 
00141 /*===========================================================================*
00142  *                              w_name                                       *
00143  *===========================================================================*/
00144 PRIVATE char *w_name()
00145 {
00146 /* Return a name for the current device. */
00147   static char name[] = "bios-d0";
00148 
00149   name[6] = '0' + w_drive;
00150   return name;
00151 }
00152 
00153 /*===========================================================================*
00154  *                              w_transfer                                   *
00155  *===========================================================================*/
00156 PRIVATE int w_transfer(proc_nr, opcode, position, iov, nr_req)
00157 int proc_nr;                    /* process doing the request */
00158 int opcode;                     /* DEV_GATHER or DEV_SCATTER */
00159 off_t position;                 /* offset on device to read or write */
00160 iovec_t *iov;                   /* pointer to read or write request vector */
00161 unsigned nr_req;                /* length of request vector */
00162 {
00163   struct wini *wn = w_wn;
00164   iovec_t *iop, *iov_end = iov + nr_req;
00165   int r, errors;
00166   unsigned nbytes, count, chunk;
00167   unsigned long block;
00168   vir_bytes i13e_rw_off, rem_buf_size;
00169   unsigned long dv_size = cv64ul(w_dv->dv_size);
00170   unsigned secspcyl = wn->heads * wn->sectors;
00171   struct int13ext_rw {
00172         u8_t    len;
00173         u8_t    res1;
00174         u16_t   count;
00175         u16_t   addr[2];
00176         u32_t   block[2];
00177   } i13e_rw;
00178   struct reg86u reg86;
00179 
00180   /* Check disk address. */
00181   if ((position & SECTOR_MASK) != 0) return(EINVAL);
00182 
00183   errors = 0;
00184 
00185   i13e_rw_off= bios_buf_size-sizeof(i13e_rw);
00186   rem_buf_size= (i13e_rw_off & ~SECTOR_MASK);
00187   assert(rem_buf_size != 0);
00188 
00189   while (nr_req > 0) {
00190         /* How many bytes to transfer? */
00191         nbytes = 0;
00192         for (iop = iov; iop < iov_end; iop++) {
00193                 if (nbytes + iop->iov_size > rem_buf_size) {
00194                         /* Don't do half a segment if you can avoid it. */
00195                         if (nbytes == 0) nbytes = rem_buf_size;
00196                         break;
00197                 }
00198                 nbytes += iop->iov_size;
00199         }
00200         if ((nbytes & SECTOR_MASK) != 0) return(EINVAL);
00201 
00202         /* Which block on disk and how close to EOF? */
00203         if (position >= dv_size) return(OK);            /* At EOF */
00204         if (position + nbytes > dv_size) nbytes = dv_size - position;
00205         block = div64u(add64ul(w_dv->dv_base, position), SECTOR_SIZE);
00206 
00207         /* Degrade to per-sector mode if there were errors. */
00208         if (errors > 0) nbytes = SECTOR_SIZE;
00209 
00210         if (opcode == DEV_SCATTER) {
00211                 /* Copy from user space to the DMA buffer. */
00212                 count = 0;
00213                 for (iop = iov; count < nbytes; iop++) {
00214                         chunk = iov->iov_size;
00215                         if (count + chunk > nbytes) chunk = nbytes - count;
00216                         assert(chunk <= rem_buf_size);
00217                         r= sys_vircopy(proc_nr, D, iop->iov_addr,
00218                                 SYSTEM, D, bios_buf_vir+count, 
00219                                 chunk);
00220                         if (r != OK)
00221                                 panic(ME, "sys_vircopy failed", r);
00222                         count += chunk;
00223                 }
00224         }
00225 
00226         /* Do the transfer */
00227         if (wn->int13ext) {
00228                 i13e_rw.len = 0x10;
00229                 i13e_rw.res1 = 0;
00230                 i13e_rw.count = nbytes >> SECTOR_SHIFT;
00231                 i13e_rw.addr[0] = bios_buf_phys % HCLICK_SIZE;
00232                 i13e_rw.addr[1] = bios_buf_phys / HCLICK_SIZE;
00233                 i13e_rw.block[0] = block;
00234                 i13e_rw.block[1] = 0;
00235                 r= sys_vircopy(SELF, D, (vir_bytes)&i13e_rw,
00236                         SYSTEM, D, (bios_buf_vir+i13e_rw_off), 
00237                         sizeof(i13e_rw));
00238                 if (r != OK)
00239                         panic(ME, "sys_vircopy failed", r);
00240 
00241                 /* Set up an extended read or write BIOS call. */
00242                 reg86.u.b.intno = 0x13;
00243                 reg86.u.w.ax = opcode == DEV_SCATTER ? 0x4300 : 0x4200;
00244                 reg86.u.b.dl = wn->drive_id;
00245                 reg86.u.w.si = (bios_buf_phys + i13e_rw_off) % HCLICK_SIZE;
00246                 reg86.u.w.ds = (bios_buf_phys + i13e_rw_off) / HCLICK_SIZE;
00247         } else {
00248                 /* Set up an ordinary read or write BIOS call. */
00249                 unsigned cylinder = block / secspcyl;
00250                 unsigned sector = (block % wn->sectors) + 1;
00251                 unsigned head = (block % secspcyl) / wn->sectors;
00252 
00253                 reg86.u.b.intno = 0x13;
00254                 reg86.u.b.ah = opcode == DEV_SCATTER ? 0x03 : 0x02;
00255                 reg86.u.b.al = nbytes >> SECTOR_SHIFT;
00256                 reg86.u.w.bx = bios_buf_phys % HCLICK_SIZE;
00257                 reg86.u.w.es = bios_buf_phys / HCLICK_SIZE;
00258                 reg86.u.b.ch = cylinder & 0xFF;
00259                 reg86.u.b.cl = sector | ((cylinder & 0x300) >> 2);
00260                 reg86.u.b.dh = head;
00261                 reg86.u.b.dl = wn->drive_id;
00262         }
00263 
00264         r= sys_int86(&reg86);
00265         if (r != OK)
00266                 panic(ME, "BIOS call failed", r);
00267 
00268         if (reg86.u.w.f & 0x0001) {
00269                 /* An error occurred, try again sector by sector unless */
00270                 if (++errors == 2) return(EIO);
00271                 continue;
00272         }
00273 
00274         if (opcode == DEV_GATHER) {
00275                 /* Copy from the DMA buffer to user space. */
00276                 count = 0;
00277                 for (iop = iov; count < nbytes; iop++) {
00278                         chunk = iov->iov_size;
00279                         if (count + chunk > nbytes) chunk = nbytes - count;
00280                         assert(chunk <= rem_buf_size);
00281                         r= sys_vircopy(SYSTEM, D, bios_buf_vir+count, 
00282                                 proc_nr, D, iop->iov_addr,
00283                                 chunk);
00284                         if (r != OK)
00285                                 panic(ME, "sys_vircopy failed", r);
00286                         count += chunk;
00287                 }
00288         }
00289 
00290         /* Book the bytes successfully transferred. */
00291         position += nbytes;
00292         for (;;) {
00293                 if (nbytes < iov->iov_size) {
00294                         /* Not done with this one yet. */
00295                         iov->iov_addr += nbytes;
00296                         iov->iov_size -= nbytes;
00297                         break;
00298                 }
00299                 nbytes -= iov->iov_size;
00300                 iov->iov_addr += iov->iov_size;
00301                 iov->iov_size = 0;
00302                 if (nbytes == 0) {
00303                         /* The rest is optional, so we return to give FS a
00304                          * chance to think it over.
00305                          */
00306                         return(OK);
00307                 }
00308                 iov++;
00309                 nr_req--;
00310         }
00311   }
00312   return(OK);
00313 }
00314 
00315 /*============================================================================*
00316  *                              w_do_open                                     *
00317  *============================================================================*/
00318 PRIVATE int w_do_open(dp, m_ptr)
00319 struct driver *dp;
00320 message *m_ptr;
00321 {
00322 /* Device open: Initialize the controller and read the partition table. */
00323 
00324   static int init_done = FALSE;
00325 
00326   if (!init_done) { w_init(); init_done = TRUE; }
00327 
00328   if (w_prepare(m_ptr->DEVICE) == NIL_DEV) return(ENXIO);
00329 
00330   if (w_wn->open_ct++ == 0) {
00331         /* Partition the disk. */
00332         partition(&w_dtab, w_drive * DEV_PER_DRIVE, P_PRIMARY, 0);
00333   }
00334   return(OK);
00335 }
00336 
00337 /*============================================================================*
00338  *                              w_do_close                                    *
00339  *============================================================================*/
00340 PRIVATE int w_do_close(dp, m_ptr)
00341 struct driver *dp;
00342 message *m_ptr;
00343 {
00344 /* Device close: Release a device. */
00345 
00346   if (w_prepare(m_ptr->DEVICE) == NIL_DEV) return(ENXIO);
00347   w_wn->open_ct--;
00348   return(OK);
00349 }
00350 
00351 /*===========================================================================*
00352  *                              w_init                                       *
00353  *===========================================================================*/
00354 PRIVATE void w_init()
00355 {
00356 /* This routine is called at startup to initialize the drive parameters. */
00357 
00358   int r, drive, drive_id, nr_drives;
00359   struct wini *wn;
00360   unsigned long capacity;
00361   struct int13ext_params {
00362         u16_t   len;
00363         u16_t   flags;
00364         u32_t   cylinders;
00365         u32_t   heads;
00366         u32_t   sectors;
00367         u32_t   capacity[2];
00368         u16_t   bts_per_sec;
00369         u16_t   config[2];
00370   } i13e_par;
00371   struct reg86u reg86;
00372 
00373   /* Ask the system task for a suitable buffer */
00374   r= sys_getbiosbuffer(&bios_buf_vir, &bios_buf_size);
00375   if (r != OK)
00376         panic(ME, "sys_getbiosbuffer failed", r);
00377   r= sys_umap(SYSTEM, D, (vir_bytes)bios_buf_vir, (phys_bytes)bios_buf_size,
00378         &bios_buf_phys);
00379   if (r != OK)
00380         panic(ME, "sys_umap failed", r);
00381   if (bios_buf_phys+bios_buf_size > 0x100000)
00382         panic(ME, "bad BIOS buffer, phys", bios_buf_phys);
00383 #if 0
00384   printf("bios_wini: got buffer size %d, virtual 0x%x, phys 0x%x\n",
00385                 bios_buf_size, bios_buf_vir, bios_buf_phys);
00386 #endif
00387 
00388   /* Get the geometry of the drives */
00389   for (drive = 0; drive < MAX_DRIVES; drive++) {
00390         if (remap_first)
00391         {
00392                 if (drive == 7)
00393                         drive_id= 0x80;
00394                 else
00395                         drive_id= 0x80 + drive + 1;
00396         }
00397         else
00398                 drive_id= 0x80 + drive;
00399 
00400         (void) w_prepare(drive * DEV_PER_DRIVE);
00401         wn = w_wn;
00402         wn->drive_id= drive_id;
00403 
00404         reg86.u.b.intno = 0x13;
00405         reg86.u.b.ah = 0x08;    /* Get drive parameters. */
00406         reg86.u.b.dl = drive_id;
00407         r= sys_int86(&reg86);
00408         if (r != OK)
00409                 panic(ME, "BIOS call failed", r);
00410 
00411         nr_drives = !(reg86.u.w.f & 0x0001) ? reg86.u.b.dl : drive;
00412         if (drive_id >= 0x80 + nr_drives) continue;
00413         wn->present= 1;
00414 
00415         wn->heads = reg86.u.b.dh + 1;
00416         wn->sectors = reg86.u.b.cl & 0x3F;
00417         wn->cylinders = (reg86.u.b.ch | ((reg86.u.b.cl & 0xC0) << 2)) + 1;
00418 
00419         capacity = (unsigned long) wn->cylinders * wn->heads * wn->sectors;
00420 
00421         reg86.u.b.intno = 0x13;
00422         reg86.u.b.ah = 0x41;    /* INT 13 Extensions - Installation check */
00423         reg86.u.w.bx = 0x55AA;
00424         reg86.u.b.dl = drive_id;
00425 
00426         if (pc_at) {
00427                 r= sys_int86(&reg86);
00428                 if (r != OK)
00429                         panic(ME, "BIOS call failed", r);
00430         }
00431 
00432         if (!(reg86.u.w.f & 0x0001) && reg86.u.w.bx == 0xAA55
00433                                 && (reg86.u.w.cx & 0x0001)) {
00434                 /* INT 13 Extensions available. */
00435                 i13e_par.len = 0x001E;  /* Input size of parameter packet */
00436                 r= sys_vircopy(SELF, D, (vir_bytes)&i13e_par,
00437                         SYSTEM, D, bios_buf_vir, 
00438                         sizeof(i13e_par));
00439                 if (r != OK)
00440                         panic(ME, "sys_vircopy failed\n", r);
00441                 reg86.u.b.intno = 0x13;
00442                 reg86.u.b.ah = 0x48;    /* Ext. Get drive parameters. */
00443                 reg86.u.b.dl = drive_id;
00444                 reg86.u.w.si = bios_buf_phys % HCLICK_SIZE;
00445                 reg86.u.w.ds = bios_buf_phys / HCLICK_SIZE;
00446 
00447                 r= sys_int86(&reg86);
00448                 if (r != OK)
00449                         panic(ME, "BIOS call failed", r);
00450 
00451                 r= sys_vircopy(SYSTEM, D, bios_buf_vir,
00452                          SELF, D, (vir_bytes)&i13e_par,
00453                         sizeof(i13e_par));
00454                 if (r != OK)
00455                         panic(ME, "sys_vircopy failed\n", r);
00456 
00457                 if (!(reg86.u.w.f & 0x0001)) {
00458                         wn->int13ext = 1;       /* Extensions can be used. */
00459                         capacity = i13e_par.capacity[0];
00460                         if (i13e_par.capacity[1] != 0) capacity = 0xFFFFFFFF;
00461                 }
00462         }
00463 
00464         if (wn->int13ext) {
00465                 printf("%s: %lu sectors\n", w_name(), capacity);
00466         } else {
00467                 printf("%s: %d cylinders, %d heads, %d sectors per track\n",
00468                         w_name(), wn->cylinders, wn->heads, wn->sectors);
00469         }
00470         wn->part[0].dv_size = mul64u(capacity, SECTOR_SIZE);
00471   }
00472 }
00473 
00474 /*============================================================================*
00475  *                              w_geometry                                    *
00476  *============================================================================*/
00477 PRIVATE void w_geometry(entry)
00478 struct partition *entry;
00479 {
00480   entry->cylinders = w_wn->cylinders;
00481   entry->heads = w_wn->heads;
00482   entry->sectors = w_wn->sectors;
00483 }
00484 
00485 /*============================================================================*
00486  *                              w_other                               *
00487  *============================================================================*/
00488 PRIVATE int w_other(dr, m)
00489 struct driver *dr;
00490 message *m;
00491 {
00492         int r, timeout, prev;
00493 
00494         if (m->m_type != DEV_IOCTL ) {
00495                 return EINVAL;
00496         }
00497 
00498         if (m->REQUEST == DIOCOPENCT) {
00499                 int count;
00500                 if (w_prepare(m->DEVICE) == NIL_DEV) return ENXIO;
00501                 count = w_wn->open_ct;
00502                 if ((r=sys_datacopy(SELF, (vir_bytes)&count,
00503                         m->IO_ENDPT, (vir_bytes)m->ADDRESS, sizeof(count))) != OK)
00504                         return r;
00505                 return OK;
00506         }
00507 
00508         return EINVAL;
00509 }
00510 

Generated on Fri Apr 14 22:57:15 2006 for minix by  doxygen 1.4.6