source: trunk/minix/drivers/bios_wini/bios_wini.c@ 12

Last change on this file since 12 was 9, checked in by Mattia Monga, 14 years ago

Minix 3.1.2a

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