1 ; -*- fundamental -*- (asm-mode sucks)
2 ; ****************************************************************************
6 ; A program to emulate an INT 13h disk BIOS from a "disk" in extended
9 ; Copyright (C) 2001-2007 H. Peter Anvin
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
105 ; See if DL points to our class of device (FD, HD)
110 js .nomatch
; If SF=0, we have a class match here
111 jz .our_drive
; If ZF=1, we have an exact match
113 jb .nomatch
; Drive < Our drive
114 dec dl ; Drive > Our drive, adjust drive #
118 call far [cs:OldInt13
]
122 cmp byte [cs:SavedAX
+1],08h
124 cmp byte [cs:SavedAX
+1],15h
126 test byte [bp+4],80h ; Hard disk?
134 mov ax,[bp+2] ; Flags
136 mov [bx+4],al ; Arithmetric flags
145 ; Set up standard entry frame
152 mov bp,sp ; Point BP to the entry stack frame
154 ; Note: AH == P_AH here
155 cmp ah,[Int13MaxFunc
]
157 xor al,al ; AL = 0 is standard entry condition
159 shr di,7 ; Convert AH to an offset in DI
162 Done: ; Standard routine for return
169 mov [es:bx],ah ; Save status
173 ; This sets the low byte (the arithmetric flags) of the
174 ; FLAGS on stack to either 00h (no flags) or 01h (CF)
175 ; depending on if AH was zero or not.
176 setnz [bx+4] ; Set CF iff error
184 ; Reset affects multiple drives, so we need to pass it on
186 xor ax,ax ; Bottom of memory
188 test dl,dl ; Always pass it on if we are resetting HD
189 js .hard_disk
; Bit 7 set
190 ; Some BIOSes get very unhappy if we pass a reset floppy
191 ; command to them and don't actually have any floppies.
192 ; This is a bug, but we have to deal with it nontheless.
193 ; Therefore, if we are the *ONLY* floppy drive, and the
194 ; user didn't request HD reset, then just drop the command.
195 ; BIOS equipment byte, top two bits + 1 == total # of floppies
196 test byte [es:0x410],0C0h
198 jmp .pass_on
; ... otherwise pass it to the BIOS
200 ; ... same thing for hard disks, sigh ...
201 cmp byte [es:0x475],1 ; BIOS variable for number of hard disks
205 pop ax ; Drop return address
206 popad ; Restore all registers
209 lss esp,[cs:Stack
] ; Restore the stack
210 and dl,80h ; Clear all but the type bit
211 jmp far [cs:OldInt13
]
215 pop dx ; Drop return address
218 mov ah,01h ; Unsupported function
222 test byte [DriveNo
],80h
223 mov bl,02h ; Type 02h = floppy with changeline
227 mov dx,[DiskSize
] ; Return the disk size in sectors
232 mov P_AH
,bl ; 02h floppy, 03h hard disk
233 pop ax ; Drop return address
234 xor ax,ax ; Success...
235 jmp DoneWeird
; But don't stick it into P_AX
241 mov ah,[bx] ; Copy last status
253 movzx ax,P_AL
; AH = 0, AL = transfer count
260 test byte [ConfigFlags
],CONFIG_READONLY
263 xchg esi,edi ; Opposite direction of a Read!
265 .
readonly: mov ah,03h ; Write protected medium
268 ; Verify integrity; just bounds-check
271 call setup_regs
; Returns error if appropriate
272 ; And fall through to success
274 CheckIfReady: ; These are always-successful noop functions
280 xor ax,ax ; Always successful
285 mov dl,[DriveCnt
] ; Cached data
287 test byte [DriveNo
],80h
295 dec ax ; We report the highest #, not the count
305 ; Is this MEMDISK installation check?
316 ; MEMDISK installation check...
322 mov P_DI
,MemDisk_Info
328 ; EDD functions -- only if enabled
337 mov P_BX
,0AA55h
; EDD signature
338 mov P_AX
,02100h ; EDD 1.1
339 mov P_CX
,0001h ; Fixed disk access subset
340 pop ax ; Drop return address
342 jmp DoneWeird
; Success, but AH != 0, sigh...
365 call edd_setup_regs
; Just bounds checking
375 mov cx,30 ; Length of our DPT
387 ; This should be done by the installer...
402 ; Set up registers as for a "Read", and compares against disk size.
403 ; WARNING: This fails immediately, even if we can transfer some
404 ; sectors. This isn't really the correct behaviour.
407 ; Convert a CHS address in P_CX/P_DH into an LBA in eax
409 ; CL[0:5] = sector (1-based) CL[7:6] = cyl[9:8]
412 movzx ebx,cl ; Sector number
414 dec ebx ; Sector number is 1-based
417 movzx edi,P_DH
; Head number
418 movzx eax,word [Heads
]
422 xchg cl,ch ; Now (E)CX <- cylinder number
423 mul ecx ; eax <- Heads*cyl# (edx <- 0)
427 ; Now eax = LBA, edx = 0
430 ; setup_regs continues...
432 ; Note: edi[31:16] and ecx[31:16] = 0 already
433 mov di,P_BX
; Get linear address of target buffer
436 add edi,ecx ; EDI = address to fetch to
437 movzx ecx,P_AL
; Sector count
439 add eax,ecx ; LBA of final sector + 1
440 shl esi,SECTORSIZE_LG2
; LBA -> byte offset
441 add esi,[DiskBuf
] ; Get address in high memory
442 cmp eax,[DiskSize
] ; Check the high mark against limit
444 shl ecx,SECTORSIZE_LG2
-2 ; Convert count to dwords
447 .
overrun: pop ax ; Drop setup_regs return address
448 mov ax,0200h ; Missing address mark
451 ; Set up registers as for an EDD Read, and compares against disk size.
455 mov si,P_SI
; DS:SI -> DAPA
462 cmp dword [es:si+4],-1
465 movzx esi,word [es:si+4] ; Offset
466 movzx edi,word [es:si+6] ; Segment
473 cmp dx,24 ; Must be large enough to hold linear address
476 cmp dword [es:si+20],0 ; > 4 GB addresses not supported
482 cmp dword [es:si+12],0 ; LBA too large?
485 movzx ecx, word [es:si+2] ; Sectors to transfer
486 mov edi,[es:si+8] ; Starting sector
493 shl ecx,SECTORSIZE_LG2
-2 ; Convert to dwords
494 shl edi,SECTORSIZE_LG2
; Convert to an offset
500 mov ax,0100h ; Invalid command
502 pop ax ; Drop setup_regs return address
506 and word [es:si+2],0 ; No sectors transferred
516 ; INT 15h intercept routines
519 cmp edx,534D4150h
; "SMAP"
521 cmp ecx,20 ; Need 20 bytes
530 add bx,12 ; Advance to next
531 mov eax,[bx-4] ; Type
532 and eax,eax ; Null type?
533 jz .renew
; If so advance to next
535 mov eax,[bx-12] ; Start addr (low)
537 mov ecx,[bx-8] ; Start addr (high)
539 mov eax,[bx] ; End addr (low)
540 mov ecx,[bx+4] ; End addr (high)
541 sub eax,[bx-12] ; Derive the length
543 mov [es:di+8],eax ; Length (low)
544 mov [es:di+12],ecx ; Length (high)
545 cmp dword [bx+8],-1 ; Type of next = end?
547 xor ebx,ebx ; Done with table
551 mov ecx,20 ; Bytes loaded
553 mov byte [bp+6], 02h ; Clear CF
558 mov byte [bp+6], 03h ; Set CF
575 jmp far [cs:OldInt15
]
582 jmp short int15_success
589 jmp short int15_success
592 mov ax,[cs:MemInt1588
]
593 jmp short int15_success
596 ; Routine to copy in/out of high memory
597 ; esi = linear source address
598 ; edi = linear target address
599 ; ecx = 32-bit word count
601 ; Assumes cs = ds = es
609 mov bx, real_int15_stub
611 test byte [ConfigFlags
], CONFIG_RAW|CONFIG_SAFEINT
614 smsw ax ; Unprivileged!
619 test byte [ConfigFlags
], CONFIG_RAW
622 ; We're in real mode with CONFIG_SAFEINT, invoke INT 15h
623 ; directly if the vector is unchanged, otherwise invoke
624 ; the *old* INT 15h vector.
630 cmp word [4*0x15], Int15Start
634 cmp word [4*0x15+2], ax
638 jmp .anymode
; INT 15h unchanged, safe to execute
640 .
changed: ; INT 15h modified, execute *old* INT 15h
642 mov bx, fake_int15_stub
647 ; We're in real mode, do it outselves
662 ; Test to see if A20 is enabled or not
679 push dx ; <D> Save A20 status
682 mov ax,2401h ; Enable A20
688 ; DX = 16 for BIGRAW, 8 for RAW
689 ; 8 is selector for a 64K flat segment,
690 ; 16 is selector for a 4GB flat segment.
697 mov bx,16 ; Large flat segment
703 ; DX has the appropriate value to put in
704 ; the registers on return
711 pop dx ; <D> A20 status
717 mov ax,2400h ; Disable A20
735 push ecx ; Transfer size this cycle
739 mov [Mover_src1
+2], al
744 mov [Mover_dst1
+2], al
748 shl cx,1 ; Convert to 16-bit words
749 call bx ; INT 15h stub
750 pop eax ; Transfer size this cycle
770 cli ; Some BIOSes enable interrupts on INT 15h
796 Int13Funcs
dw Reset
; 00h - RESET
797 dw GetStatus
; 01h - GET STATUS
799 dw Write
; 03h - WRITE
800 dw Verify
; 04h - VERIFY
801 dw Invalid
; 05h - FORMAT TRACK
802 dw Invalid
; 06h - FORMAT TRACK AND SET BAD FLAGS
803 dw Invalid
; 07h - FORMAT DRIVE AT TRACK
804 dw GetParms
; 08h - GET PARAMETERS
805 dw InitWithParms
; 09h - INITIALIZE CONTROLLER WITH DRIVE PARAMETERS
808 dw Seek
; 0Ch - SEEK TO CYLINDER
809 dw Reset
; 0Dh - RESET HARD DISKS
812 dw CheckIfReady
; 10h - CHECK IF READY
813 dw Recalibrate
; 11h - RECALIBRATE
817 dw GetDriveType
; 15h - GET DRIVE TYPE
818 dw DetectChange
; 16h - DETECT DRIVE CHANGE
830 dw ReadMult
; 21h - READ MULTIPLE
831 dw WriteMult
; 22h - WRITE MULTIPLE
832 dw SetMode
; 23h - SET CONTROLLER FEATURES
833 dw SetMode
; 24h - SET MULTIPLE MODE
834 dw Invalid
; 25h - IDENTIFY DRIVE
862 dw EDDPresence
; 41h - EDD PRESENCE DETECT
863 dw EDDRead
; 42h - EDD READ
864 dw EDDWrite
; 43h - EDD WRITE
865 dw EDDVerify
; 44h - EDD VERIFY
866 dw Invalid
; 45h - EDD LOCK/UNLOCK MEDIA
867 dw Invalid
; 46h - EDD EJECT
868 dw EDDSeek
; 47h - EDD SEEK
869 dw EDDGetParms
; 48h - EDD GET PARAMETERS
873 Int13FuncsCnt
equ (Int13FuncsEnd
-Int13Funcs
) >> 1
878 ; Bit 0 - DMA boundaries handled transparently
879 ; Bit 3 - Device supports write verify
881 .cylinders
dd 0 ; No physical geometry
882 .heads
dd 0 ; No physical geometry
883 .sectors
dd 0 ; No physical geometry
884 .totalsize
dd 0, 0 ; Total number of sectors
885 .bytespersec
dw SECTORSIZE
886 .eddtable
dw -1, -1 ; Invalid EDD table
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
906 Mover
dd 0, 0, 0, 0 ; Must be zero
907 dw 0ffffh
; 64 K segment size
908 Mover_src1: db 0, 0, 0 ; Low 24 bits of source addy
909 db 93h ; Access rights
910 db 00h ; Extended access rights
911 Mover_src2: db 0 ; High 8 bits of source addy
912 dw 0ffffh
; 64 K segment size
913 Mover_dst1: db 0, 0, 0 ; Low 24 bits of target addy
914 db 93h ; Access rights
915 db 00h ; Extended access rights
916 Mover_dst2: db 0 ; High 8 bits of source addy
917 Mover_dummy2: dd 0, 0, 0, 0 ; More space for the BIOS
920 MemDisk_Info
equ $
; Pointed to by installation check
921 MDI_Bytes
dw 27 ; Total bytes in MDI structure
922 MDI_Version
db VER_MINOR
, VER_MAJOR
; MEMDISK version
924 PatchArea
equ $
; This gets filled in by the installer
926 DiskBuf
dd 0 ; Linear address of high memory disk
927 DiskSize
dd 0 ; Size of disk in blocks
928 CommandLine
dw 0, 0 ; Far pointer to saved command line
930 OldInt13
dd 0 ; INT 13h in chain
931 OldInt15
dd 0 ; INT 15h in chain
933 OldDosMem
dw 0 ; Old position of DOS mem end
934 BootLoaderID
db 0 ; Boot loader ID from header
935 ; ---- MDI structure ends here ---
936 Int13MaxFunc
db Int13FuncsCnt
-1 ; Max INT 13h function (to disable EDD)
939 MemInt1588
dw 0 ; 1MB-65MB memory amount (1K)
941 Cylinders
dw 0 ; Cylinder count
942 Heads
dw 0 ; Head count
943 Sectors
dd 0 ; Sector count (zero-extended)
945 Mem1MB
dd 0 ; 1MB-16MB memory amount (1K)
946 Mem16MB
dd 0 ; 16MB-4G memory amount (64K)
948 DriveNo
db 0 ; Our drive number
949 DriveType
db 0 ; Our drive type (floppies)
950 DriveCnt
db 0 ; Drive count (from the BIOS)
952 ConfigFlags
db 0 ; Bit 0 - readonly
954 MyStack
dw 0 ; Offset of stack
955 StatusPtr
dw 0 ; Where to save status (zeroseg ptr)
957 DPT times
16 db 0 ; BIOS parameter table pointer (floppies)
961 Stack
dd 0 ; Saved SS:ESP on invocation
963 SavedAX
dw 0 ; AX saved on invocation
965 alignb
4, db 0 ; We *MUST* end on a dword boundary
967 E820Table
equ $
; The installer loads the E820 table here
968 TotalSize
equ $
; End pointer