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,
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
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>
33 #define ME "BIOS_WINI"
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? */
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 */
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 *===========================================================================*/
105 env_parse("bios_remap_first", "d", 0, &v
, 0, 1);
108 /* Set special disk parameters then call the generic main loop. */
109 driver_task(&w_dtab
);
113 /*===========================================================================*
115 *===========================================================================*/
116 PRIVATE
struct device
*w_prepare(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
];
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
];
133 if (w_drive
>= MAX_DRIVES
|| !w_wn
->present
)
138 /*===========================================================================*
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
;
150 /*===========================================================================*
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
;
164 unsigned nbytes
, count
, chunk
;
166 vir_bytes i13e_rw_off
, rem_buf_size
;
167 size_t vir_offset
= 0;
168 unsigned secspcyl
= wn
->heads
* wn
->sectors
;
179 lopos
= ex64lo(pos64
);
181 /* Check disk address. */
182 if ((lopos
& SECTOR_MASK
) != 0) return(EINVAL
);
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);
192 /* How many bytes to transfer? */
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
;
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) {
208 n
= sub64(w_dv
->dv_size
, pos64
);
209 assert(ex64hi(n
) == 0);
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. */
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
);
226 r
=sys_safecopyfrom(proc_nr
,
227 (cp_grant_id_t
) iop
->iov_addr
,
228 0, (vir_bytes
) (bios_buf_v
+count
),
231 panic(ME
, "copy failed", r
);
233 if(proc_nr
!= SELF
) {
234 panic(ME
, "unsafe outside self", r
);
236 memcpy(bios_buf_v
+count
,
237 (char *) iop
->iov_addr
, chunk
);
243 /* Do the transfer */
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
;
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);
273 reg86
.u
.b
.dl
= wn
->drive_id
;
276 r
= sys_int86(®86
);
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
);
286 if (opcode
== DEV_GATHER_S
) {
287 /* Copy from the DMA buffer to user space. */
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
);
295 r
=sys_safecopyto(proc_nr
, iop
->iov_addr
,
296 0, (vir_bytes
) (bios_buf_v
+count
), chunk
, D
);
299 panic(ME
, "sys_vircopy failed", r
);
302 panic(ME
, "unsafe without self", NO_NUM
);
303 memcpy((char *) iop
->iov_addr
,
304 bios_buf_v
+count
, chunk
);
310 /* Book the bytes successfully transferred. */
311 pos64
= add64ul(pos64
, nbytes
);
313 if (nbytes
< iov
->iov_size
) {
314 /* Not done with this one yet. */
315 vir_offset
+= nbytes
;
316 iov
->iov_size
-= nbytes
;
319 nbytes
-= iov
->iov_size
;
323 /* The rest is optional, so we return to give FS a
324 * chance to think it over.
335 /*============================================================================*
337 *============================================================================*/
338 PRIVATE
int w_do_open(dp
, 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);
357 /*============================================================================*
359 *============================================================================*/
360 PRIVATE
int w_do_close(dp
, m_ptr
)
364 /* Device close: Release a device. */
366 if (w_prepare(m_ptr
->DEVICE
) == NIL_DEV
) return(ENXIO
);
371 /*===========================================================================*
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
;
380 unsigned long capacity
;
381 struct int13ext_params
{
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
);
401 printf("bios_wini: got buffer size %d, virtual 0x%x, phys 0x%x\n",
402 BIOSBUF
, bios_buf_v
, bios_buf_phys
);
405 i13e_par
= (struct int13ext_params
*) bios_buf_v
;
407 /* Get the geometry of the drives */
408 for (drive
= 0; drive
< MAX_DRIVES
; drive
++) {
414 drive_id
= 0x80 + drive
+ 1;
417 drive_id
= 0x80 + drive
;
419 (void) w_prepare(drive
* DEV_PER_DRIVE
);
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(®86
);
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;
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
;
446 r
= sys_int86(®86
);
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(®86
);
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;
473 printf("%s: %lu sectors\n", w_name(), capacity
);
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 /*============================================================================*
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 /*============================================================================*
495 *============================================================================*/
496 PRIVATE
int w_other(dr
, m
, safe
)
501 int r
, timeout
, prev
;
503 if (m
->m_type
!= DEV_IOCTL_S
)
506 if (m
->REQUEST
== DIOCOPENCT
) {
508 if (w_prepare(m
->DEVICE
) == NIL_DEV
) return ENXIO
;
509 count
= w_wn
->open_ct
;
511 r
=sys_safecopyto(m
->IO_ENDPT
, (vir_bytes
)m
->ADDRESS
,
512 0, (vir_bytes
)&count
, sizeof(count
), D
);
514 r
=sys_datacopy(SELF
, (vir_bytes
)&count
,
515 m
->IO_ENDPT
, (vir_bytes
)m
->ADDRESS
, sizeof(count
));