verbose printing and sanity checking functions.
[minix.git] / drivers / bios_wini / bios_wini.c
blob97c6c016e2c42270dd3805f05785ee92b1e2053c
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.
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.
13 * The file contains one entry point:
15 * bios_winchester_task: main entry when system is brought up
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.
23 #include "../drivers.h"
24 #include "../libdriver/driver.h"
25 #include "../libdriver/drvlib.h"
26 #include <minix/sysutil.h>
27 #include <minix/safecopies.h>
28 #include <minix/keymap.h>
29 #include <sys/ioc_disk.h>
30 #include <ibm/int86.h>
31 #include <assert.h>
33 #define ME "BIOS_WINI"
35 /* Error codes */
36 #define ERR (-1) /* general error */
38 /* Parameters for the disk drive. */
39 #define MAX_DRIVES 8 /* this driver supports 8 drives (d0 - d7)*/
40 #define MAX_SECS 255 /* bios can transfer this many sectors */
41 #define NR_MINORS (MAX_DRIVES * DEV_PER_DRIVE)
42 #define SUB_PER_DRIVE (NR_PARTITIONS * NR_PARTITIONS)
43 #define NR_SUBDEVS (MAX_DRIVES * SUB_PER_DRIVE)
45 PRIVATE int pc_at = 1; /* What about PC XTs? */
47 /* Variables. */
48 PRIVATE struct wini { /* main drive struct, one entry per drive */
49 unsigned cylinders; /* number of cylinders */
50 unsigned heads; /* number of heads */
51 unsigned sectors; /* number of sectors per track */
52 unsigned open_ct; /* in-use count */
53 int drive_id; /* Drive ID at BIOS level */
54 int present; /* Valid drive */
55 int int13ext; /* IBM/MS INT 13 extensions supported? */
56 struct device part[DEV_PER_DRIVE]; /* disks and partitions */
57 struct device subpart[SUB_PER_DRIVE]; /* subpartitions */
58 } wini[MAX_DRIVES], *w_wn;
60 PRIVATE int w_drive; /* selected drive */
61 PRIVATE struct device *w_dv; /* device's base and size */
62 PRIVATE char *bios_buf_v;
63 PRIVATE phys_bytes bios_buf_phys;
64 PRIVATE int remap_first = 0; /* Remap drives for CD HD emulation */
65 #define BIOSBUF 16384
66 PRIVATE cp_grant_id_t my_bios_grant_id;
68 _PROTOTYPE(int main, (void) );
69 FORWARD _PROTOTYPE( struct device *w_prepare, (int device) );
70 FORWARD _PROTOTYPE( char *w_name, (void) );
71 FORWARD _PROTOTYPE( int w_transfer, (int proc_nr, int opcode, u64_t position,
72 iovec_t *iov, unsigned nr_req, int safe) );
73 FORWARD _PROTOTYPE( int w_do_open, (struct driver *dp, message *m_ptr) );
74 FORWARD _PROTOTYPE( int w_do_close, (struct driver *dp, message *m_ptr) );
75 FORWARD _PROTOTYPE( void w_init, (void) );
76 FORWARD _PROTOTYPE( void w_geometry, (struct partition *entry));
77 FORWARD _PROTOTYPE( int w_other, (struct driver *dp, message *m_ptr, int) );
79 /* Entry points to this driver. */
80 PRIVATE struct driver w_dtab = {
81 w_name, /* current device's name */
82 w_do_open, /* open or mount request, initialize device */
83 w_do_close, /* release device */
84 do_diocntl, /* get or set a partition's geometry */
85 w_prepare, /* prepare for I/O on a given minor device */
86 w_transfer, /* do the I/O */
87 nop_cleanup, /* no cleanup needed */
88 w_geometry, /* tell the geometry of the disk */
89 nop_signal, /* no cleanup needed on shutdown */
90 nop_alarm, /* ignore leftover alarms */
91 nop_cancel, /* ignore CANCELs */
92 nop_select, /* ignore selects */
93 w_other, /* catch-all for unrecognized commands and ioctls */
94 NULL /* leftover hardware interrupts */
97 /*===========================================================================*
98 * bios_winchester_task *
99 *===========================================================================*/
100 PUBLIC int main()
102 long v;
104 v= 0;
105 env_parse("bios_remap_first", "d", 0, &v, 0, 1);
106 remap_first= v;
108 /* Set special disk parameters then call the generic main loop. */
109 driver_task(&w_dtab);
110 return(OK);
113 /*===========================================================================*
114 * w_prepare *
115 *===========================================================================*/
116 PRIVATE struct device *w_prepare(device)
117 int device;
119 /* Prepare for I/O on a device. */
121 if (device < NR_MINORS) { /* d0, d0p[0-3], d1, ... */
122 w_drive = device / DEV_PER_DRIVE; /* save drive number */
123 w_wn = &wini[w_drive];
124 w_dv = &w_wn->part[device % DEV_PER_DRIVE];
125 } else
126 if ((unsigned) (device -= MINOR_d0p0s0) < NR_SUBDEVS) {/*d[0-7]p[0-3]s[0-3]*/
127 w_drive = device / SUB_PER_DRIVE;
128 w_wn = &wini[w_drive];
129 w_dv = &w_wn->subpart[device % SUB_PER_DRIVE];
130 } else {
131 return(NIL_DEV);
133 if (w_drive >= MAX_DRIVES || !w_wn->present)
134 return NIL_DEV;
135 return(w_dv);
138 /*===========================================================================*
139 * w_name *
140 *===========================================================================*/
141 PRIVATE char *w_name()
143 /* Return a name for the current device. */
144 static char name[] = "bios-d0";
146 name[6] = '0' + w_drive;
147 return name;
150 /*===========================================================================*
151 * w_transfer *
152 *===========================================================================*/
153 PRIVATE int w_transfer(proc_nr, opcode, pos64, iov, nr_req, safe)
154 int proc_nr; /* process doing the request */
155 int opcode; /* DEV_GATHER or DEV_SCATTER */
156 u64_t pos64; /* offset on device to read or write */
157 iovec_t *iov; /* pointer to read or write request vector */
158 unsigned nr_req; /* length of request vector */
159 int safe; /* use safecopies? */
161 struct wini *wn = w_wn;
162 iovec_t *iop, *iov_end = iov + nr_req;
163 int r, errors;
164 unsigned nbytes, count, chunk;
165 unsigned long block;
166 vir_bytes i13e_rw_off, rem_buf_size;
167 size_t vir_offset = 0;
168 unsigned secspcyl = wn->heads * wn->sectors;
169 struct int13ext_rw {
170 u8_t len;
171 u8_t res1;
172 u16_t count;
173 u16_t addr[2];
174 u32_t block[2];
175 } *i13e_rw;
176 struct reg86u reg86;
177 u32_t lopos;
179 lopos= ex64lo(pos64);
181 /* Check disk address. */
182 if ((lopos & SECTOR_MASK) != 0) return(EINVAL);
184 errors = 0;
186 i13e_rw_off= BIOSBUF-sizeof(*i13e_rw);
187 rem_buf_size= (i13e_rw_off & ~SECTOR_MASK);
188 i13e_rw = (struct int13ext_rw *) (bios_buf_v + i13e_rw_off);
189 assert(rem_buf_size != 0);
191 while (nr_req > 0) {
192 /* How many bytes to transfer? */
193 nbytes = 0;
194 for (iop = iov; iop < iov_end; iop++) {
195 if (nbytes + iop->iov_size > rem_buf_size) {
196 /* Don't do half a segment if you can avoid it. */
197 if (nbytes == 0) nbytes = rem_buf_size;
198 break;
200 nbytes += iop->iov_size;
202 if ((nbytes & SECTOR_MASK) != 0) return(EINVAL);
204 /* Which block on disk and how close to EOF? */
205 if (cmp64(pos64, w_dv->dv_size) >= 0) return(OK); /* At EOF */
206 if (cmp64(add64u(pos64, nbytes), w_dv->dv_size) > 0) {
207 u64_t n;
208 n = sub64(w_dv->dv_size, pos64);
209 assert(ex64hi(n) == 0);
210 nbytes = ex64lo(n);
212 block = div64u(add64(w_dv->dv_base, pos64), SECTOR_SIZE);
214 /* Degrade to per-sector mode if there were errors. */
215 if (errors > 0) nbytes = SECTOR_SIZE;
217 if (opcode == DEV_SCATTER_S) {
218 /* Copy from user space to the DMA buffer. */
219 count = 0;
220 for (iop = iov; count < nbytes; iop++) {
221 chunk = iov->iov_size;
222 if (count + chunk > nbytes) chunk = nbytes - count;
223 assert(chunk <= rem_buf_size);
225 if(safe) {
226 r=sys_safecopyfrom(proc_nr,
227 (cp_grant_id_t) iop->iov_addr,
228 0, (vir_bytes) (bios_buf_v+count),
229 chunk, D);
230 if (r != OK)
231 panic(ME, "copy failed", r);
232 } else {
233 if(proc_nr != SELF) {
234 panic(ME, "unsafe outside self", r);
236 memcpy(bios_buf_v+count,
237 (char *) iop->iov_addr, chunk);
239 count += chunk;
243 /* Do the transfer */
244 if (wn->int13ext) {
245 i13e_rw->len = 0x10;
246 i13e_rw->res1 = 0;
247 i13e_rw->count = nbytes >> SECTOR_SHIFT;
248 i13e_rw->addr[0] = bios_buf_phys % HCLICK_SIZE;
249 i13e_rw->addr[1] = bios_buf_phys / HCLICK_SIZE;
250 i13e_rw->block[0] = block;
251 i13e_rw->block[1] = 0;
253 /* Set up an extended read or write BIOS call. */
254 reg86.u.b.intno = 0x13;
255 reg86.u.w.ax = opcode == DEV_SCATTER_S ? 0x4300 : 0x4200;
256 reg86.u.b.dl = wn->drive_id;
257 reg86.u.w.si = (bios_buf_phys + i13e_rw_off) % HCLICK_SIZE;
258 reg86.u.w.ds = (bios_buf_phys + i13e_rw_off) / HCLICK_SIZE;
259 } else {
260 /* Set up an ordinary read or write BIOS call. */
261 unsigned cylinder = block / secspcyl;
262 unsigned sector = (block % wn->sectors) + 1;
263 unsigned head = (block % secspcyl) / wn->sectors;
265 reg86.u.b.intno = 0x13;
266 reg86.u.b.ah = opcode == DEV_SCATTER_S ? 0x03 : 0x02;
267 reg86.u.b.al = nbytes >> SECTOR_SHIFT;
268 reg86.u.w.bx = bios_buf_phys % HCLICK_SIZE;
269 reg86.u.w.es = bios_buf_phys / HCLICK_SIZE;
270 reg86.u.b.ch = cylinder & 0xFF;
271 reg86.u.b.cl = sector | ((cylinder & 0x300) >> 2);
272 reg86.u.b.dh = head;
273 reg86.u.b.dl = wn->drive_id;
276 r= sys_int86(&reg86);
277 if (r != OK)
278 panic(ME, "BIOS call failed", r);
280 if (reg86.u.w.f & 0x0001) {
281 /* An error occurred, try again sector by sector unless */
282 if (++errors == 2) return(EIO);
283 continue;
286 if (opcode == DEV_GATHER_S) {
287 /* Copy from the DMA buffer to user space. */
288 count = 0;
289 for (iop = iov; count < nbytes; iop++) {
290 chunk = iov->iov_size;
291 if (count + chunk > nbytes) chunk = nbytes - count;
292 assert(chunk <= rem_buf_size);
294 if(safe) {
295 r=sys_safecopyto(proc_nr, iop->iov_addr,
296 0, (vir_bytes) (bios_buf_v+count), chunk, D);
298 if (r != OK)
299 panic(ME, "sys_vircopy failed", r);
300 } else {
301 if (proc_nr != SELF)
302 panic(ME, "unsafe without self", NO_NUM);
303 memcpy((char *) iop->iov_addr,
304 bios_buf_v+count, chunk);
306 count += chunk;
310 /* Book the bytes successfully transferred. */
311 pos64 = add64ul(pos64, nbytes);
312 for (;;) {
313 if (nbytes < iov->iov_size) {
314 /* Not done with this one yet. */
315 vir_offset += nbytes;
316 iov->iov_size -= nbytes;
317 break;
319 nbytes -= iov->iov_size;
320 vir_offset = 0;
321 iov->iov_size = 0;
322 if (nbytes == 0) {
323 /* The rest is optional, so we return to give FS a
324 * chance to think it over.
326 return(OK);
328 iov++;
329 nr_req--;
332 return(OK);
335 /*============================================================================*
336 * w_do_open *
337 *============================================================================*/
338 PRIVATE int w_do_open(dp, m_ptr)
339 struct driver *dp;
340 message *m_ptr;
342 /* Device open: Initialize the controller and read the partition table. */
344 static int init_done = FALSE;
346 if (!init_done) { w_init(); init_done = TRUE; }
348 if (w_prepare(m_ptr->DEVICE) == NIL_DEV) return(ENXIO);
350 if (w_wn->open_ct++ == 0) {
351 /* Partition the disk. */
352 partition(&w_dtab, w_drive * DEV_PER_DRIVE, P_PRIMARY, 0);
354 return(OK);
357 /*============================================================================*
358 * w_do_close *
359 *============================================================================*/
360 PRIVATE int w_do_close(dp, m_ptr)
361 struct driver *dp;
362 message *m_ptr;
364 /* Device close: Release a device. */
366 if (w_prepare(m_ptr->DEVICE) == NIL_DEV) return(ENXIO);
367 w_wn->open_ct--;
368 return(OK);
371 /*===========================================================================*
372 * w_init *
373 *===========================================================================*/
374 PRIVATE void w_init()
376 /* This routine is called at startup to initialize the drive parameters. */
378 int r, drive, drive_id, nr_drives;
379 struct wini *wn;
380 unsigned long capacity;
381 struct int13ext_params {
382 u16_t len;
383 u16_t flags;
384 u32_t cylinders;
385 u32_t heads;
386 u32_t sectors;
387 u32_t capacity[2];
388 u16_t bts_per_sec;
389 u16_t config[2];
390 } *i13e_par;
391 struct reg86u reg86;
393 /* Ask the system task for a suitable buffer */
394 if(!(bios_buf_v = alloc_contig(BIOSBUF, AC_LOWER1M, &bios_buf_phys))) {
395 panic(ME, "allocating bios buffer failed", r);
398 if (bios_buf_phys+BIOSBUF > 0x100000)
399 panic(ME, "bad BIOS buffer, phys", bios_buf_phys);
400 #if 0
401 printf("bios_wini: got buffer size %d, virtual 0x%x, phys 0x%x\n",
402 BIOSBUF, bios_buf_v, bios_buf_phys);
403 #endif
405 i13e_par = (struct int13ext_params *) bios_buf_v;
407 /* Get the geometry of the drives */
408 for (drive = 0; drive < MAX_DRIVES; drive++) {
409 if (remap_first)
411 if (drive == 7)
412 drive_id= 0x80;
413 else
414 drive_id= 0x80 + drive + 1;
416 else
417 drive_id= 0x80 + drive;
419 (void) w_prepare(drive * DEV_PER_DRIVE);
420 wn = w_wn;
421 wn->drive_id= drive_id;
423 reg86.u.b.intno = 0x13;
424 reg86.u.b.ah = 0x08; /* Get drive parameters. */
425 reg86.u.b.dl = drive_id;
426 r= sys_int86(&reg86);
427 if (r != OK)
428 panic(ME, "BIOS call failed", r);
430 nr_drives = !(reg86.u.w.f & 0x0001) ? reg86.u.b.dl : drive;
431 if (drive_id >= 0x80 + nr_drives) continue;
432 wn->present= 1;
434 wn->heads = reg86.u.b.dh + 1;
435 wn->sectors = reg86.u.b.cl & 0x3F;
436 wn->cylinders = (reg86.u.b.ch | ((reg86.u.b.cl & 0xC0) << 2)) + 1;
438 capacity = (unsigned long) wn->cylinders * wn->heads * wn->sectors;
440 reg86.u.b.intno = 0x13;
441 reg86.u.b.ah = 0x41; /* INT 13 Extensions - Installation check */
442 reg86.u.w.bx = 0x55AA;
443 reg86.u.b.dl = drive_id;
445 if (pc_at) {
446 r= sys_int86(&reg86);
447 if (r != OK)
448 panic(ME, "BIOS call failed", r);
451 if (!(reg86.u.w.f & 0x0001) && reg86.u.w.bx == 0xAA55
452 && (reg86.u.w.cx & 0x0001)) {
453 /* INT 13 Extensions available. */
454 i13e_par->len = 0x001E; /* Input size of parameter packet */
455 reg86.u.b.intno = 0x13;
456 reg86.u.b.ah = 0x48; /* Ext. Get drive parameters. */
457 reg86.u.b.dl = drive_id;
458 reg86.u.w.si = bios_buf_phys % HCLICK_SIZE;
459 reg86.u.w.ds = bios_buf_phys / HCLICK_SIZE;
461 r= sys_int86(&reg86);
462 if (r != OK)
463 panic(ME, "BIOS call failed", r);
465 if (!(reg86.u.w.f & 0x0001)) {
466 wn->int13ext = 1; /* Extensions can be used. */
467 capacity = i13e_par->capacity[0];
468 if (i13e_par->capacity[1] != 0) capacity = 0xFFFFFFFF;
472 if (wn->int13ext) {
473 printf("%s: %lu sectors\n", w_name(), capacity);
474 } else {
475 printf("%s: %d cylinders, %d heads, %d sectors per track\n",
476 w_name(), wn->cylinders, wn->heads, wn->sectors);
478 wn->part[0].dv_size = mul64u(capacity, SECTOR_SIZE);
482 /*============================================================================*
483 * w_geometry *
484 *============================================================================*/
485 PRIVATE void w_geometry(entry)
486 struct partition *entry;
488 entry->cylinders = w_wn->cylinders;
489 entry->heads = w_wn->heads;
490 entry->sectors = w_wn->sectors;
493 /*============================================================================*
494 * w_other *
495 *============================================================================*/
496 PRIVATE int w_other(dr, m, safe)
497 struct driver *dr;
498 message *m;
499 int safe;
501 int r, timeout, prev;
503 if (m->m_type != DEV_IOCTL_S )
504 return EINVAL;
506 if (m->REQUEST == DIOCOPENCT) {
507 int count;
508 if (w_prepare(m->DEVICE) == NIL_DEV) return ENXIO;
509 count = w_wn->open_ct;
510 if(safe) {
511 r=sys_safecopyto(m->IO_ENDPT, (vir_bytes)m->ADDRESS,
512 0, (vir_bytes)&count, sizeof(count), D);
513 } else {
514 r=sys_datacopy(SELF, (vir_bytes)&count,
515 m->IO_ENDPT, (vir_bytes)m->ADDRESS, sizeof(count));
518 if(r != OK)
519 return r;
520 return OK;
523 return EINVAL;