1 ; -*- fundamental -*- (asm-mode sucks)
2 ; ****************************************************************************
6 ; A program to emulate an INT 13h disk BIOS from a "disk" in extended
9 ; Copyright 2001-2008 H. Peter Anvin - All Rights Reserved
11 ; This program is free software; you can redistribute it and/or modify
12 ; it under the terms of the GNU General Public License as published by
13 ; the Free Software Foundation, Inc., 53 Temple Place Ste 330,
14 ; Boston MA 02111-1307, USA; either version 2 of the License, or
15 ; (at your option) any later version; incorporated herein by reference.
17 ; ****************************************************************************
20 %include "../version.gen"
23 ; %define DEBUG_TRACERS ; Uncomment to get debugging tracers
43 %endif
; DEBUG_TRACERS
45 %define CONFIG_READONLY
0x01
46 %define CONFIG_RAW
0x02
47 %define CONFIG_SAFEINT
0x04
48 %define CONFIG_BIGRAW
0x08 ; MUST be 8!
52 %define SECTORSIZE_LG2
9 ; log2(sector size)
53 %define SECTORSIZE
(1 << SECTORSIZE_LG2
)
55 ; Parameter registers definition; this is the definition
57 %define P_DS
word [bp+34]
58 %define P_ES
word [bp+32]
59 %define P_EAX
dword [bp+28]
60 %define P_HAX
word [bp+30]
61 %define P_AX
word [bp+28]
62 %define P_AL
byte [bp+28]
63 %define P_AH
byte [bp+29]
64 %define P_ECX
dword [bp+24]
65 %define P_HCX
word [bp+26]
66 %define P_CX
word [bp+24]
67 %define P_CL
byte [bp+24]
68 %define P_CH
byte [bp+25]
69 %define P_EDX
dword [bp+20]
70 %define P_HDX
word [bp+22]
71 %define P_DX
word [bp+20]
72 %define P_DL
byte [bp+20]
73 %define P_DH
byte [bp+21]
74 %define P_EBX
dword [bp+16]
75 %define P_HBX
word [bp+18]
76 %define P_HBXL
byte [bp+18]
77 %define P_BX
word [bp+16]
78 %define P_BL
byte [bp+16]
79 %define P_BH
byte [bp+17]
80 %define P_EBP
dword [bp+8]
81 %define P_BP
word [bp+8]
82 %define P_ESI
dword [bp+4]
83 %define P_SI
word [bp+4]
84 %define P_EDI
dword [bp]
85 %define P_DI
word [bp]
88 ; These pointers are used by the installer and
89 ; must be first in the binary
90 Pointers: dw Int13Start
96 IretPtr
equ Int13Start.
iret
107 ; See if DL points to our class of device (FD, HD)
112 js .nomatch
; If SF=0, we have a class match here
113 jz .our_drive
; If ZF=1, we have an exact match
115 jb .nomatch
; Drive < Our drive
116 dec dl ; Drive > Our drive, adjust drive #
120 call far [cs:OldInt13
]
124 cmp byte [cs:SavedAX
+1],08h
126 cmp byte [cs:SavedAX
+1],15h
128 test byte [bp+4],80h ; Hard disk?
136 mov ax,[bp+2] ; Flags
138 mov [bx+4],al ; Arithmetric flags
147 ; Set up standard entry frame
154 mov bp,sp ; Point BP to the entry stack frame
156 ; Note: AH == P_AH here
157 cmp ah,[Int13MaxFunc
]
159 xor al,al ; AL = 0 is standard entry condition
161 shr di,7 ; Convert AH to an offset in DI
164 Done: ; Standard routine for return
171 mov [es:bx],ah ; Save status
175 ; This sets the low byte (the arithmetric flags) of the
176 ; FLAGS on stack to either 00h (no flags) or 01h (CF)
177 ; depending on if AH was zero or not.
178 setnz [bx+4] ; Set CF iff error
186 ; Reset affects multiple drives, so we need to pass it on
188 xor ax,ax ; Bottom of memory
190 test dl,dl ; Always pass it on if we are resetting HD
191 js .hard_disk
; Bit 7 set
192 ; Some BIOSes get very unhappy if we pass a reset floppy
193 ; command to them and don't actually have any floppies.
194 ; This is a bug, but we have to deal with it nontheless.
195 ; Therefore, if we are the *ONLY* floppy drive, and the
196 ; user didn't request HD reset, then just drop the command.
197 ; BIOS equipment byte, top two bits + 1 == total # of floppies
198 test byte [es:0x410],0C0h
200 jmp .pass_on
; ... otherwise pass it to the BIOS
202 ; ... same thing for hard disks, sigh ...
203 cmp byte [es:0x475],1 ; BIOS variable for number of hard disks
207 pop ax ; Drop return address
208 popad ; Restore all registers
211 lss esp,[cs:Stack
] ; Restore the stack
212 and dl,80h ; Clear all but the type bit
213 jmp far [cs:OldInt13
]
217 pop dx ; Drop return address
220 mov ah,01h ; Unsupported function
224 test byte [DriveNo
],80h
225 mov bl,02h ; Type 02h = floppy with changeline
229 mov dx,[DiskSize
] ; Return the disk size in sectors
234 mov P_AH
,bl ; 02h floppy, 03h hard disk
235 pop ax ; Drop return address
236 xor ax,ax ; Success...
237 jmp DoneWeird
; But don't stick it into P_AX
243 mov ah,[bx] ; Copy last status
255 movzx ax,P_AL
; AH = 0, AL = transfer count
262 test byte [ConfigFlags
],CONFIG_READONLY
265 xchg esi,edi ; Opposite direction of a Read!
267 .
readonly: mov ah,03h ; Write protected medium
270 ; Verify integrity; just bounds-check
273 call setup_regs
; Returns error if appropriate
274 ; And fall through to success
276 CheckIfReady: ; These are always-successful noop functions
284 xor ax,ax ; Always successful
289 mov dl,[DriveCnt
] ; Cached data
291 test byte [DriveNo
],80h
299 dec ax ; We report the highest #, not the count
309 ; Is this MEMDISK installation check?
320 ; MEMDISK installation check...
326 mov P_DI
,MemDisk_Info
332 ; EDD functions -- only if enabled
341 mov P_BX
,0AA55h
; EDD signature
342 mov P_AX
,03000h ; EDD 3.0
343 mov P_CX
,0003h ; Bit 0 - Fixed disk access subset
344 ; Bit 1 - Locking and ejecting subset
345 pop ax ; Drop return address
347 jmp DoneWeird
; Success, but AH != 0, sigh...
370 call edd_setup_regs
; Just bounds checking
382 lodsw ; Length of our DPT
384 cmp cx,26 ; Minimum size
406 ; Set up registers as for a "Read", and compares against disk size.
407 ; WARNING: This fails immediately, even if we can transfer some
408 ; sectors. This isn't really the correct behaviour.
411 ; Convert a CHS address in P_CX/P_DH into an LBA in eax
413 ; CL[0:5] = sector (1-based) CL[7:6] = cyl[9:8]
416 movzx ebx,cl ; Sector number
418 dec ebx ; Sector number is 1-based
421 movzx edi,P_DH
; Head number
422 movzx eax,word [Heads
]
426 xchg cl,ch ; Now (E)CX <- cylinder number
427 mul ecx ; eax <- Heads*cyl# (edx <- 0)
431 ; Now eax = LBA, edx = 0
434 ; setup_regs continues...
436 ; Note: edi[31:16] and ecx[31:16] = 0 already
437 mov di,P_BX
; Get linear address of target buffer
440 add edi,ecx ; EDI = address to fetch to
441 movzx ecx,P_AL
; Sector count
443 add eax,ecx ; LBA of final sector + 1
444 shl esi,SECTORSIZE_LG2
; LBA -> byte offset
445 add esi,[DiskBuf
] ; Get address in high memory
446 cmp eax,[DiskSize
] ; Check the high mark against limit
448 shl ecx,SECTORSIZE_LG2
-2 ; Convert count to dwords
451 .
overrun: pop ax ; Drop setup_regs return address
452 mov ax,0200h ; Missing address mark
455 ; Set up registers as for an EDD Read, and compares against disk size.
459 mov si,P_SI
; DS:SI -> DAPA
466 cmp dword [es:si+4],-1
469 movzx ebx,word [es:si+4] ; Offset
470 movzx edi,word [es:si+6] ; Segment
476 cmp dx,24 ; Must be large enough to hold
480 cmp dword [es:si+20],0 ; > 4 GB addresses not supported
481 mov ax,0900h ; "Data boundary error" - bogus, but
482 ; no really better code available
488 cmp dword [es:si+12],0 ; LBA too large?
491 movzx ecx, word [es:si+2] ; Sectors to transfer
492 mov esi,[es:si+8] ; Starting sector
499 shl ecx,SECTORSIZE_LG2
-2 ; Convert to dwords
500 shl esi,SECTORSIZE_LG2
; Convert to an offset
507 mov ax,0100h ; Invalid command
509 pop ax ; Drop setup_regs return address
513 mov ax,0200h ; "Address mark not found" =
514 ; LBA beyond end of disk
516 and word [es:si+2],0 ; No sectors transferred
522 mov ax,0B200h ; Volume Not Removable
529 ; INT 15h intercept routines
532 cmp edx,534D4150h
; "SMAP"
534 cmp ecx,20 ; Need 20 bytes
543 add bx,12 ; Advance to next
544 mov eax,[bx-4] ; Type
545 and eax,eax ; Null type?
546 jz .renew
; If so advance to next
548 mov eax,[bx-12] ; Start addr (low)
550 mov ecx,[bx-8] ; Start addr (high)
552 mov eax,[bx] ; End addr (low)
553 mov ecx,[bx+4] ; End addr (high)
554 sub eax,[bx-12] ; Derive the length
556 mov [es:di+8],eax ; Length (low)
557 mov [es:di+12],ecx ; Length (high)
558 cmp dword [bx+8],-1 ; Type of next = end?
560 xor ebx,ebx ; Done with table
564 mov ecx,20 ; Bytes loaded
566 mov byte [bp+6], 02h ; Clear CF
571 mov byte [bp+6], 03h ; Set CF
588 jmp far [cs:OldInt15
]
595 jmp short int15_success
602 jmp short int15_success
605 mov ax,[cs:MemInt1588
]
606 jmp short int15_success
609 ; Routine to copy in/out of high memory
610 ; esi = linear source address
611 ; edi = linear target address
612 ; ecx = 32-bit word count
614 ; Assumes cs = ds = es
622 mov bx, real_int15_stub
624 test byte [ConfigFlags
], CONFIG_RAW|CONFIG_SAFEINT
627 smsw ax ; Unprivileged!
632 test byte [ConfigFlags
], CONFIG_RAW
635 ; We're in real mode with CONFIG_SAFEINT, invoke INT 15h
636 ; directly if the vector is unchanged, otherwise invoke
637 ; the *old* INT 15h vector.
643 cmp word [4*0x15], Int15Start
647 cmp word [4*0x15+2], ax
651 jmp .anymode
; INT 15h unchanged, safe to execute
653 .
changed: ; INT 15h modified, execute *old* INT 15h
655 mov bx, fake_int15_stub
660 ; We're in real mode, do it outselves
675 ; Test to see if A20 is enabled or not
692 push dx ; <D> Save A20 status
695 mov ax,2401h ; Enable A20
701 ; DX = 16 for BIGRAW, 8 for RAW
702 ; 8 is selector for a 64K flat segment,
703 ; 16 is selector for a 4GB flat segment.
710 mov bx,16 ; Large flat segment
716 ; DX has the appropriate value to put in
717 ; the registers on return
724 pop dx ; <D> A20 status
730 mov ax,2400h ; Disable A20
748 push ecx ; Transfer size this cycle
752 mov [Mover_src1
+2], al
757 mov [Mover_dst1
+2], al
761 shl cx,1 ; Convert to 16-bit words
762 call bx ; INT 15h stub
763 pop eax ; Transfer size this cycle
783 cli ; Some BIOSes enable interrupts on INT 15h
809 Int13Funcs
dw Reset
; 00h - RESET
810 dw GetStatus
; 01h - GET STATUS
812 dw Write
; 03h - WRITE
813 dw Verify
; 04h - VERIFY
814 dw Invalid
; 05h - FORMAT TRACK
815 dw Invalid
; 06h - FORMAT TRACK AND SET BAD FLAGS
816 dw Invalid
; 07h - FORMAT DRIVE AT TRACK
817 dw GetParms
; 08h - GET PARAMETERS
818 dw InitWithParms
; 09h - INITIALIZE CONTROLLER WITH DRIVE PARAMETERS
821 dw Seek
; 0Ch - SEEK TO CYLINDER
822 dw Reset
; 0Dh - RESET HARD DISKS
825 dw CheckIfReady
; 10h - CHECK IF READY
826 dw Recalibrate
; 11h - RECALIBRATE
830 dw GetDriveType
; 15h - GET DRIVE TYPE
831 dw DetectChange
; 16h - DETECT DRIVE CHANGE
843 dw ReadMult
; 21h - READ MULTIPLE
844 dw WriteMult
; 22h - WRITE MULTIPLE
845 dw SetMode
; 23h - SET CONTROLLER FEATURES
846 dw SetMode
; 24h - SET MULTIPLE MODE
847 dw Invalid
; 25h - IDENTIFY DRIVE
875 dw EDDPresence
; 41h - EDD PRESENCE DETECT
876 dw EDDRead
; 42h - EDD READ
877 dw EDDWrite
; 43h - EDD WRITE
878 dw EDDVerify
; 44h - EDD VERIFY
879 dw EDDLock
; 45h - EDD LOCK/UNLOCK MEDIA
880 dw EDDEject
; 46h - EDD EJECT
881 dw EDDSeek
; 47h - EDD SEEK
882 dw EDDGetParms
; 48h - EDD GET PARAMETERS
883 dw EDDDetectChange
; 49h - EDD MEDIA CHANGE STATUS
887 Int13FuncsCnt
equ (Int13FuncsEnd
-Int13Funcs
) >> 1
891 Shaker
dw ShakerEnd
-$
892 dd 0 ; Pointer to self
895 Shaker_RMDS: dd 0x0000ffff ; 64K data segment
898 Shaker_DS: dd 0x0000ffff ; 4GB data segment
905 Mover
dd 0, 0, 0, 0 ; Must be zero
906 dw 0ffffh
; 64 K segment size
907 Mover_src1: db 0, 0, 0 ; Low 24 bits of source addy
908 db 93h ; Access rights
909 db 00h ; Extended access rights
910 Mover_src2: db 0 ; High 8 bits of source addy
911 dw 0ffffh
; 64 K segment size
912 Mover_dst1: db 0, 0, 0 ; Low 24 bits of target addy
913 db 93h ; Access rights
914 db 00h ; Extended access rights
915 Mover_dst2: db 0 ; High 8 bits of source addy
916 Mover_dummy2: dd 0, 0, 0, 0 ; More space for the BIOS
919 MemDisk_Info
equ $
; Pointed to by installation check
920 MDI_Bytes
dw 27 ; Total bytes in MDI structure
921 MDI_Version
db VER_MINOR
, VER_MAJOR
; MEMDISK version
923 PatchArea
equ $
; This gets filled in by the installer
925 DiskBuf
dd 0 ; Linear address of high memory disk
926 DiskSize
dd 0 ; Size of disk in blocks
927 CommandLine
dw 0, 0 ; Far pointer to saved command line
929 OldInt13
dd 0 ; INT 13h in chain
930 OldInt15
dd 0 ; INT 15h in chain
932 OldDosMem
dw 0 ; Old position of DOS mem end
933 BootLoaderID
db 0 ; Boot loader ID from header
934 ; ---- MDI structure ends here ---
935 Int13MaxFunc
db Int13FuncsCnt
-1 ; Max INT 13h function (to disable EDD)
938 MemInt1588
dw 0 ; 1MB-65MB memory amount (1K)
940 Cylinders
dw 0 ; Cylinder count
941 Heads
dw 0 ; Head count
942 Sectors
dd 0 ; Sector count (zero-extended)
944 Mem1MB
dd 0 ; 1MB-16MB memory amount (1K)
945 Mem16MB
dd 0 ; 16MB-4G memory amount (64K)
947 DriveNo
db 0 ; Our drive number
948 DriveType
db 0 ; Our drive type (floppies)
949 DriveCnt
db 0 ; Drive count (from the BIOS)
951 ConfigFlags
db 0 ; Bit 0 - readonly
953 MyStack
dw 0 ; Offset of stack
954 StatusPtr
dw 0 ; Where to save status (zeroseg ptr)
956 DPT times
16 db 0 ; BIOS parameter table pointer (floppies)
961 ; Bit 0 - DMA boundaries handled transparently
962 ; Bit 3 - Device supports write verify
963 ; Bit 5 - Media is lockable
964 .cylinders
dd 0 ; Filled in by installer
965 .heads
dd 0 ; Filled in by installer
966 .sectors
dd 0 ; Filled in by installer
967 .totalsize
dd 0, 0 ; Filled in by installer
968 .bytespersec
dw SECTORSIZE
969 .eddtable
dw -1, -1 ; Invalid DPTE pointer
974 Stack
dd 0 ; Saved SS:ESP on invocation
976 SavedAX
dw 0 ; AX saved on invocation
978 alignb
4, db 0 ; We *MUST* end on a dword boundary
980 E820Table
equ $
; The installer loads the E820 table here
981 TotalSize
equ $
; End pointer