2 * Copyright (c) 1992, 1993, 1996
3 * Berkeley Software Design, Inc. All rights reserved.
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * 3. All advertising materials mentioning features or use of this software
14 * must display the following acknowledgement:
15 * This product includes software developed by Berkeley Software
18 * THIS SOFTWARE IS PROVIDED BY Berkeley Software Design, Inc. ``AS IS'' AND
19 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21 * ARE DISCLAIMED. IN NO EVENT SHALL Berkeley Software Design, Inc. BE LIABLE
22 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30 * BSDI int13.c,v 2.3 1996/04/08 19:32:43 bostic Exp
32 * $FreeBSD: src/usr.bin/doscmd/int13.c,v 1.4.2.2 2002/04/25 11:04:51 tg Exp $
33 * $DragonFly: src/usr.bin/doscmd/int13.c,v 1.2 2003/06/17 04:29:26 dillon Exp $
36 #include <sys/ioctl.h>
37 #include <sys/types.h>
43 #define FDCHANGED _IOR('F', 64, int)
45 #define INT13_ERR_NONE 0x00
46 #define INT13_ERR_BAD_COMMAND 0x01
47 #define INT13_ERR_BAD_ADDRESS_MARK 0x02
48 #define INT13_ERR_WRITE_PROTECT 0x03
49 #define INT13_ERR_SECTOR_ID_BAD 0x04
50 #define INT13_ERR_RESET_FAILURE 0x05
51 #define INT13_ERR_CLL_ACTIVE 0x06
52 #define INT13_ERR_ACT_FAILED 0x07
53 #define INT13_ERR_DMA_OVERRUN 0x08
54 #define INT13_ERR_DMA_BOUNDARY 0x09
55 #define INT13_ERR_BAD_TRACK_FLAG 0x0B
56 #define INT13_ERR_MEDIA_TYP_UNKNOWN 0x0C
57 #define INT13_ERR_CRC 0x10
58 #define INT13_ERR_CORRECTED 0x11
59 #define INT13_ERR_CTRLR_FAILURE 0x20
60 #define INT13_ERR_SEEK 0x40
61 #define INT13_ERR_TIME_OUT 0x80
62 #define INT13_ERR_NOT_READY 0xAA
63 #define INT13_ERR_UNDEFINED 0xBB
64 #define INT13_ERR_SENSE_OPERATION 0xFF
90 char *list
[4]; /* Up to 4 devices allowed */
94 int changed
:1; /* Set if we change format */
97 #define hd_status (*(u_char *)0x474)
98 #define fd_status (*(u_char *)0x441)
101 disize(struct diskinfo
*di
)
103 return(di
->sectors
* di
->cylinders
* di
->sides
);
107 cylsize(struct diskinfo
*di
)
109 return(di
->sectors
* di
->sides
);
112 static u_long ftable
= 0xF1000; /* Floppy table */
113 static u_long htable
= 0xF1020; /* Hard disk table */
115 static struct diskinfo diskinfo
[26];
117 static struct diskinfo floppyinfo
[] = {
118 {0, 9, 40, 1, 512, -1, NULL
, 0, NULL
, 0,
119 {NULL
, NULL
, NULL
, NULL
}, 0, 0, 0, 0}, /* Probably not correct */
120 {1, 9, 40, 2, 512, -1, NULL
, 0, NULL
, 0,
121 {NULL
, NULL
, NULL
, NULL
}, 0, 0, 0, 0},
122 {2, 9, 80, 2, 512, -1, NULL
, 0, NULL
, 0,
123 {NULL
, NULL
, NULL
, NULL
}, 0, 0, 0, 0},
124 {3, 15, 80, 2, 512, -1, NULL
, 0, NULL
, 0,
125 {NULL
, NULL
, NULL
, NULL
}, 0, 0, 0, 0},
126 {4, 18, 80, 2, 512, -1, NULL
, 0, NULL
, 0,
127 {NULL
, NULL
, NULL
, NULL
}, 0, 0, 0, 0},
128 {6, 36, 80, 2, 512, -1, NULL
, 0, NULL
, 0,
129 {NULL
, NULL
, NULL
, NULL
}, 0, 0, 0, 0},
130 {-1, 0, 0, 0, 0, 0, NULL
, 0, NULL
, 0,
131 {NULL
, NULL
, NULL
, NULL
}, 0, 0, 0, 0}
134 static struct diskinfo
*
139 if (drive
>= 2 && drive
< 0x80) {
147 if (drive
> 25 || diskinfo
[drive
].path
== 0) {
150 di
= &diskinfo
[drive
];
152 if (di
->removeable
) {
154 if (!(di
->path
= di
->list
[di
->multi
]))
155 di
->path
= di
->list
[di
->multi
= 0];
157 if ((di
->fd
= open(di
->path
, di
->read_only
? O_RDONLY
158 : O_RDWR
|O_FSYNC
)) < 0 &&
159 (di
->read_only
= 1) &&
160 (di
->fd
= open(di
->path
, O_RDONLY
)) < 0) {
163 di
->fd
= squirrel_fd(di
->fd
);
182 make_readonly(int drive
)
184 if (drive
< 0 || drive
>= 26)
186 diskinfo
[drive
].read_only
= 1;
190 init_hdisk(int drive
, int cyl
, int head
, int tracksize
, char *file
, char *fake_ptab
)
196 for (drive
= 2; drive
< 26; ++drive
) {
197 if (diskinfo
[drive
].path
== 0)
202 fprintf(stderr
, "Only floppies may be assigned to A: or B:\n");
207 fprintf(stderr
, "Too many disk drives (only 24 allowed)\n");
211 di
= &diskinfo
[drive
];
214 fprintf(stderr
, "Drive %c: already assigned to %s\n", drntol(drive
),
219 di
->sectors
= tracksize
;
231 if ((fd
= open(fake_ptab
, 0)) < 0) {
235 di
->sector0
= malloc(512);
237 perror("malloc in init_hdisk");
241 read(fd
, di
->sector0
, 512);
244 ptab
= (PTAB
*)(di
->sector0
+ 0x01BE);
246 for (fd
= 0; fd
< 4; ++fd
) {
247 if (*(u_short
*)(di
->sector0
+ 0x1FE) == 0xAA55 &&
248 ptab
[fd
].numSectors
== (u_long
)(head
* tracksize
* cyl
) &&
249 (ptab
[fd
].systemID
== 1 || ptab
[fd
].systemID
== 4 ||
250 ptab
[fd
].systemID
== 6))
255 memcpy(ptab
, ptab
+ fd
, sizeof(PTAB
));
256 memset(ptab
+ 1, 0, sizeof(PTAB
) * 3);
257 di
->offset
= ptab
[0].relSector
;
258 di
->cylinders
+= di
->offset
/ cylsize(di
);
260 memset(ptab
, 0, sizeof(PTAB
) * 4);
263 ptab
->beginSector
= 1; /* this is 1 based */
266 ptab
->endHead
= head
- 1;
267 ptab
->endSector
= tracksize
; /* this is 1 based */
268 ptab
->endCyl
= cyl
& 0xff;
269 ptab
->endSector
|= (cyl
& 0x300) >> 2;
271 ptab
->relSector
= head
* tracksize
;
272 ptab
->numSectors
= head
* tracksize
* cyl
;
274 *(u_short
*)(di
->sector0
+ 0x1FE) = 0xAA55;
284 if ((clusters
= buf
[0x0D]) == 0) {
285 if (disize(di
) <= 128 * 2048)
287 else if (disize(di
) <= 256 * 2048)
289 else if (disize(di
) <= 8 * 1024 * 2048)
291 else if (disize(di
) <= 16 * 1024 * 2048)
296 if ((disize(di
) / clusters
) <= 4096) {
297 ptab
->systemID
= 0x01;
299 ptab
->systemID
= 0x04;
302 di
->cylinders
+= 1; /* Extra cylinder for partition table, etc. */
304 ptab
->bootIndicator
= 0x80;
309 di
->path
= strdup(file
);
311 di
->location
= ((table
& 0xf0000) << 12) | (table
& 0xffff);
314 ivec
[0x41] = di
->location
;
315 } else if (drive
== 1) {
316 ivec
[0x46] = di
->location
;
319 table
= htable
+ (drive
- 2) * 0x10;
320 *(u_short
*)(table
+0x00) = di
->cylinders
-1; /* Cylinders */
321 *(u_char
*)(table
+0x02) = di
->sides
; /* Heads */
322 *(u_short
*)(table
+0x03) = 0; /* 0 */
323 *(u_short
*)(table
+0x05) = 0xffff; /* write pre-comp */
324 *(u_char
*)(table
+0x07) = 0; /* ECC Burst length */
325 *(u_char
*)(table
+0x08) = 0; /* Control Byte */
326 *(u_char
*)(table
+0x09) = 0; /* standard timeout */
327 *(u_char
*)(table
+0x0a) = 0; /* formatting timeout */
328 *(u_char
*)(table
+0x0b) = 0; /* timeout for checking drive */
329 *(u_short
*)(table
+0x0c) = di
->cylinders
-1; /* landing zone */
330 *(u_char
*)(table
+0x0e) = di
->sectors
; /* sectors/track */
331 *(u_char
*)(table
+0x0f) = 0;
333 if ((drive
- 1) >= ndisks
)
345 case 1024: return(3);
347 fprintf(stderr
, "Invalid sector size: %d\n", size
);
350 /* keep `gcc -Wall' happy */
355 init_floppy(int drive
, int type
, char *file
)
357 struct diskinfo
*di
= floppyinfo
;
361 while (di
->type
>= 0 && di
->type
!= type
&& disize(di
)/2 != type
)
365 fprintf(stderr
, "Invalid floppy type: %d\n", type
);
370 if (diskinfo
[0].path
== 0) {
372 } else if (diskinfo
[1].path
== 0) {
375 fprintf(stderr
, "Too many floppy drives (only 2 allowed)\n");
380 fprintf(stderr
, "Floppies must be either drive A: or B:\n");
384 if (drive
>= nfloppies
)
385 nfloppies
= drive
+ 1;
387 if (diskinfo
[drive
].path
== 0) {
388 diskinfo
[drive
] = *di
;
391 di
= &diskinfo
[drive
];
393 if (stat(file
, &sb
) < 0) {
394 fprintf(stderr
, "Drive %c: Could not stat %s\n", drntol(drive
), file
);
398 if (drive
< 2 && (S_ISCHR(sb
.st_mode
) || S_ISBLK(sb
.st_mode
))) {
399 if (di
->path
&& !di
->removeable
) {
400 fprintf(stderr
, "Drive %c: is not removeable and hence can only have one assignment\n", drntol(drive
));
404 } else if (di
->removeable
) {
405 fprintf(stderr
, "Drive %c: already assigned to %s\n", drntol(drive
),
410 if (di
->removeable
) {
412 if (di
->multi
== 4) {
413 fprintf(stderr
, "Drive %c: already assigned 4 devices\n",
418 di
->path
= di
->list
[di
->multi
++] = strdup(file
);
421 fprintf(stderr
, "Drive %c: already assigned to %s\n",
422 drntol(drive
), di
->path
);
426 di
->path
= strdup(file
);
429 di
->location
= ((table
& 0xf0000) << 12) | (table
& 0xffff);
433 ivec
[0x1e] = ((ftable
& 0xf0000) << 12) | (ftable
& 0xffff);
435 table
= ftable
+ drive
* 0x0a;
437 *(u_char
*)(table
+0x00) = 0xdf; /* First Specify Byte */
438 *(u_char
*)(table
+0x01) = 0x02; /* Second Specify Byte */
439 *(u_char
*)(table
+0x02) = 0x25; /* Timer ticks to wait 'til motor OFF */
440 *(u_char
*)(table
+0x03) = bps(di
->secsize
); /* Number of bytes/sector */
441 *(u_char
*)(table
+0x04) = di
->sectors
; /* Number of sectors/track */
442 *(u_char
*)(table
+0x05) = 0x1b; /* Gap length, in bytes */
443 *(u_char
*)(table
+0x06) = 0xff; /* Data length, in bytes */
444 *(u_char
*)(table
+0x07) = 0x6c; /* Gap length for format */
445 *(u_char
*)(table
+0x09) = 0xf6; /* Fill byte for formatting */
446 *(u_char
*)(table
+0x09) = 0x0f; /* Head settle time, in milliseconds */
447 *(u_char
*)(table
+0x0a) = 0x08; /* Motor startup time, in 1/8 seconds */
454 return(i
< nfloppies
? diskinfo
[i
].type
: 0);
457 #define seterror(err) { \
467 trynext(struct diskinfo
*di
)
473 if (di
->multi
++ >= 4)
476 if (di
->list
[di
->multi
] && (di
= getdisk(di
- diskinfo
))) {
484 diread(struct diskinfo
*di
, regcontext_t
*REGS
,
485 off_t start
, char *addr
, int sectors
)
489 int drive
= di
- diskinfo
;
498 res
= lseek(di
->fd
, start
* di
->secsize
, 0);
500 if (res
< 0 && di
->removeable
&& trynext(di
))
504 seterror(INT13_ERR_SEEK
);
508 res
= read(di
->fd
, addr
, sectors
* di
->secsize
);
510 if (res
< 0 && di
->removeable
&& trynext(di
))
513 if (di
->removeable
) {
515 seterror(INT13_ERR_NOT_READY
);
518 return(res
/ di
->secsize
);
522 * reads always work, if if they don't.
523 * Just pretend any byte not read was actually a 0
526 memset(addr
, 0, sectors
* di
->secsize
);
527 else if (res
< sectors
* di
->secsize
)
528 memset(addr
+ res
, 0, sectors
* di
->secsize
- res
);
534 diwrite(struct diskinfo
*di
, regcontext_t
*REGS
,
535 off_t start
, char *addr
, int sectors
)
538 int drive
= di
- diskinfo
;
547 res
= lseek(di
->fd
, start
* di
->secsize
, 0);
549 if (res
< 0 && di
->removeable
&& trynext(di
))
553 seterror(INT13_ERR_SEEK
);
557 res
= write(di
->fd
, addr
, sectors
* di
->secsize
);
559 if (res
< 0 && di
->removeable
&& trynext(di
))
562 if (di
->removeable
) {
564 seterror(INT13_ERR_NOT_READY
);
567 } else if (res
< 0) {
568 seterror(INT13_ERR_WRITE_PROTECT
);
571 return(res
/ di
->secsize
);
575 int13(regcontext_t
*REGS
)
602 case 0x00: /* Reset */
604 case 0x01: /* Read disk status */
612 case 0x02: /* Read */
614 addr
= (char *)MAKEPTR(R_ES
, R_BX
);
617 R_AL
= 0; /* Start out with nothing read */
620 cyl
= R_CH
| ((R_CL
& 0xc0) << 2);
621 sector
= (R_CL
& 0x3f) - 1;
627 if ((di
= getdisk(drive
)) == 0) {
628 debug(D_DISK
, "Bad drive: %02x (%d : %d : %d)\n",
629 drive
, cyl
, side
, sector
);
630 seterror(INT13_ERR_BAD_COMMAND
);
633 start
= cyl
* di
->sectors
* di
->sides
+
637 if (start
>= disize(di
)) {
638 debug(D_DISK
, "Read past end of disk\n");
639 seterror(INT13_ERR_SEEK
);
642 if (sectors
+ start
>= disize(di
)) {
643 sectors
= disize(di
) - start
;
647 if (start
< di
->offset
) {
650 memcpy(addr
, di
->sector0
, di
->secsize
);
654 memset(addr
, 0, sectors
* di
->secsize
);
660 debug(D_DISK
, "%02x: Read %2d sectors from %qd to %04x:%04x\n",
661 drive
, sectors
, start
, R_ES
, R_BX
);
663 if ((did
= diread(di
, REGS
, start
, addr
, sectors
)) >= 0)
671 case 0x03: /* Write */
673 addr
= (char *)MAKEPTR(R_ES
, R_BX
);
676 R_AL
= 0; /* Start out with nothing written */
679 cyl
= R_CH
| ((R_CL
& 0xc0) << 2);
680 sector
= (R_CL
& 0x3f) - 1;
686 if ((di
= getdisk(drive
)) == 0) {
687 debug(D_DISK
, "Bad drive: %d (%d : %d : %d)\n",
688 drive
, cyl
, side
, sector
);
689 seterror(INT13_ERR_BAD_COMMAND
);
693 debug(D_DISK
, "%02x: Attempt to write readonly disk\n", drive
);
694 seterror(INT13_ERR_WRITE_PROTECT
);
697 start
= cyl
* di
->sectors
* di
->sides
+
701 if (start
>= disize(di
)) {
702 debug(D_DISK
, "Write past end of disk\n");
703 seterror(INT13_ERR_SEEK
);
707 if (sectors
+ start
>= disize(di
))
708 sectors
= disize(di
) - start
;
711 if (start
< di
->offset
) {
719 debug(D_DISK
, "%02x: Write %2d sectors from %qd to %04x:%04x\n",
720 drive
, sectors
, start
, R_ES
, R_BX
);
722 if ((did
= diwrite(di
, REGS
, start
, addr
, sectors
)) >= 0)
730 case 0x04: /* Verify */
736 cyl
= R_CH
| ((R_CL
& 0xc0) << 2);
737 sector
= (R_CL
& 0x3f) - 1;
743 if ((di
= getdisk(drive
)) == 0) {
744 debug(D_DISK
, "Bad drive: %d (%d : %d : %d)\n",
745 drive
, cyl
, side
, sector
);
746 seterror(INT13_ERR_BAD_COMMAND
);
749 start
= cyl
* di
->sectors
* di
->sides
+
753 if (start
>= disize(di
)) {
754 debug(D_DISK
, "Verify past end of disk\n");
755 seterror(INT13_ERR_SEEK
);
759 if (sectors
+ start
>= disize(di
))
760 sectors
= disize(di
) - start
;
763 if (start
< di
->offset
)
769 debug(D_DISK
, "Verify %2d sectors from %qd\n", sectors
, start
);
770 if (lseek(di
->fd
, start
* di
->secsize
, 0) < 0) {
771 debug(D_DISK
, "Seek error\n");
772 seterror(INT13_ERR_SEEK
);
775 while (sectors
> 0) {
777 if (read(di
->fd
, buf
, di
->secsize
) != di
->secsize
) {
778 debug(D_DISK
, "Verify error\n");
790 case 0x05: /* Format track */
791 seterror(INT13_ERR_BAD_COMMAND
);
794 case 0x08: /* Status */
797 if ((di
= getdisk(drive
)) == 0) {
798 debug(D_DISK
, "Bad drive: %d\n", drive
);
799 seterror(INT13_ERR_BAD_COMMAND
);
805 if ((drive
& 0x80) == 0)
806 PUTVEC(R_ES
, R_DI
, di
->location
);
808 R_CL
= di
->sectors
| ((di
->cylinders
>> 2) & 0xc0);
809 R_CH
= di
->cylinders
& 0xff;
810 R_DL
= (drive
& 0x80) ? ndisks
: nfloppies
;
811 R_DH
= di
->sides
- 1;
812 debug(D_DISK
, "%02x: Status requested: sec %d cyl %d side %d drive %d\n",
813 drive
, R_CL
, R_CH
, R_DH
, R_DL
);
820 case 0x0c: /* Move read/write head */
821 case 0x0d: /* Reset */
824 case 0x10: /* check for disk ready */
825 R_AH
= 0; /* always open for business */
829 if ((di
= getdisk(drive
)) == 0) {
836 start
= di
->sectors
* di
->cylinders
* di
->sides
;
841 R_AH
= 1; /* Non-changeable disk */
845 case 0x16: /* Media change */
847 if ((di
= getdisk(drive
)) && di
->changed
) {
853 case 0x17: /* Determine floppy disk format */
854 seterror(INT13_ERR_BAD_COMMAND
);
857 case 0x18: /* Determine disk format */
858 if ((di
= getdisk(drive
)) == 0) {
863 /* XXX incomplete? */
867 unknown_int2(0x13, R_AH
, REGS
);
877 vec
= insert_softint_trampoline();
879 register_callback(vec
, int13
, "int 13");
881 vec
= insert_null_trampoline();