1 ; -*- fundamental -*- (asm-mode sucks)
2 ; ****************************************************************************
6 ; A program to emulate an INT 13h disk BIOS from a "disk" in extended
9 ; Copyright 2001-2009 H. Peter Anvin - All Rights Reserved
10 ; Copyright 2009 Intel Corporation; author: H. Peter Anvin
11 ; Portions copyright 2009 Shao Miller [El Torito code, mBFT, safe hook]
13 ; This program is free software; you can redistribute it and/or modify
14 ; it under the terms of the GNU General Public License as published by
15 ; the Free Software Foundation, Inc., 53 Temple Place Ste 330,
16 ; Boston MA 02111-1307, USA; either version 2 of the License, or
17 ; (at your option) any later version; incorporated herein by reference.
19 ; ****************************************************************************
21 %include "../version.gen"
23 ; %define DEBUG_TRACERS ; Uncomment to get debugging tracers
31 %macro WRITEHEX2 0-1 al
41 %macro WRITEHEX4 0-1 ax
51 %macro WRITEHEX8 0-1 eax
73 %endif ; DEBUG_TRACERS
75 ; Flags we test our configuration against
76 %define CONFIG_READONLY 0x01
77 %define CONFIG_RAW 0x02
78 %define CONFIG_SAFEINT 0x04
79 %define CONFIG_BIGRAW 0x08 ; MUST be 8!
83 %define SECTORSIZE (1 << SECTORSIZE_LG2)
85 ; Parameter registers definition; this is the definition
87 %define P_DS word [bp+34]
88 %define P_ES word [bp+32]
89 %define P_EAX dword [bp+28]
90 %define P_HAX word [bp+30]
91 %define P_AX word [bp+28]
92 %define P_AL byte [bp+28]
93 %define P_AH byte [bp+29]
94 %define P_ECX dword [bp+24]
95 %define P_HCX word [bp+26]
96 %define P_CX word [bp+24]
97 %define P_CL byte [bp+24]
98 %define P_CH byte [bp+25]
99 %define P_EDX dword [bp+20]
100 %define P_HDX word [bp+22]
101 %define P_DX word [bp+20]
102 %define P_DL byte [bp+20]
103 %define P_DH byte [bp+21]
104 %define P_EBX dword [bp+16]
105 %define P_HBX word [bp+18]
106 %define P_HBXL byte [bp+18]
107 %define P_BX word [bp+16]
108 %define P_BL byte [bp+16]
109 %define P_BH byte [bp+17]
110 %define P_EBP dword [bp+8]
111 %define P_BP word [bp+8]
112 %define P_ESI dword [bp+4]
113 %define P_SI word [bp+4]
114 %define P_EDI dword [bp]
115 %define P_DI word [bp]
118 ; These pointers are used by the installer and
119 ; must be first in the binary
120 Pointers: dw Int13Start
122 dw MemDisk_Info ; Portions are patched by installer
126 IretPtr equ Int13Start.iret
128 jmp strict near .SafeHookEnd ; 3-byte jump
129 db '$INT13SF' ; Signature for "safe hook"
130 db 'MEMDISK ' ; Vendor ID
131 dd 0 ; SEG:OFF of previous INT 13h hook
132 ; Must be filled in by installer
133 dd 0 ; "Safe hook" flags
134 ; ---- "Safe hook" structure ends here ---
136 ; This next field should be guaranteed at this position after the
137 ; "safe hook" structure. This allows for a MEMDISK OS driver to
138 ; immediately find out the particular parameters using the mBFT
139 ; and MDI structures. This binary will have the offset to the mBFT
140 ; in this field to begin with, so the installer knows where the mBFT
141 ; is. This is akin to the "Pointers" section above. The installer
142 ; will refill this field with the physical address of the mBFT for
143 ; future consumers, such as OS drivers.
144 dd mBFT ; Offset from hook to the mBFT
147 cmp word [cs:Recursive],0
159 cmp word [cs:SavedAX],4a00h ; El Torito function?
160 jae our_drive ; We grab it
162 ; See if DL points to our class of device (FD, HD)
167 js .nomatch ; If SF=0, we have a class match here
168 ; 0x00 the sign bit for FD
169 ; 0x80 the sign bit for HD
170 jz our_drive ; If ZF=1, we have an exact match
172 jb .nomatch ; Drive < Our drive
173 cmp dl,[cs:DriveShiftLimit]
174 jae .nomatch ; Drive > The maximum drive
175 ; number that we will shift for.
176 ; This leaves any higher-up BIOS
177 ; drives alone, such as an optical
178 ; disc drive 0xA0 or 0xE0
179 dec dl ; Drive > Our drive, adjust drive #
186 inc word [cs:Recursive]
188 call far [cs:OldInt13]
190 dec word [cs:Recursive]
193 cmp byte [cs:SavedAX+1],08h ; Get drive params function?
194 je .norestoredl ; DL = number of drives
195 cmp byte [cs:SavedAX+1],15h ; Get disk type function?
197 test byte [bp+4],80h ; Hard disk?
198 jnz .norestoredl ; CX:DX = size of device
205 mov ax,[bp+2] ; Flags
207 mov [bx+4],al ; Arithmetic flags
218 jmp far [cs:OldInt13]
221 ; Set up standard entry frame
228 mov bp,sp ; Point BP to the entry stack frame
231 ; Note: AX == P_AX here
232 cmp ah,Int13FuncsCnt-1
235 mov al,[CD_PKT.type] ; Check if we are in
236 cmp al,0 ; El Torito no emulation mode
237 ja .emulation ; No. We support the function
238 cmp ah,3fh ; Yes. We must not support functions
239 jbe Invalid_jump ; 0 through 3Fh. Check and decide
242 xor al,al ; AL = 0 is standard entry condition
244 shr di,7 ; Convert AH to an offset in DI
247 Done: ; Standard routine for return
254 mov [es:bx],ah ; Save status
258 ; This sets the low byte (the arithmetic flags) of the
259 ; FLAGS on stack to either 00h (no flags) or 01h (CF)
260 ; depending on if AH was zero or not.
261 setnz [bx+4] ; Set CF iff error
269 ; Reset affects multiple drives, so we need to pass it on
271 xor ax,ax ; Bottom of memory
273 test dl,dl ; Always pass it on if we are
275 js .hard_disk ; Bit 7 set
276 ; Some BIOSes get very unhappy if we pass a reset floppy
277 ; command to them and don't actually have any floppies.
278 ; This is a bug, but we have to deal with it nontheless.
279 ; Therefore, if we are the *ONLY* floppy drive, and the
280 ; user didn't request HD reset, then just drop the command.
281 ; BIOS equipment byte, top two bits + 1 == total # of floppies
282 test byte [es:0x410],0C0h
284 jmp .pass_on ; ... otherwise pass it to the BIOS
286 ; ... same thing for hard disks, sigh ...
287 cmp byte [es:0x475],1 ; BIOS variable for number of hard
292 pop ax ; Drop return address
293 popad ; Restore all registers
296 lss esp,[cs:Stack] ; Restore the stack
297 and dl,80h ; Clear all but the type bit
302 pop dx ; Drop return address
305 mov ah,01h ; Unsupported function
309 test byte [DriveNo],80h
310 mov bl,02h ; Type 02h = floppy with changeline
312 ; Hard disks only! DO NOT set CX:DX for floppies...
313 ; it apparently causes Win98SE DOS to go into an loop
314 ; resetting the drive over and over. Sigh.
316 mov dx,[DiskSize] ; Return the disk size in sectors
321 mov P_AH,bl ; 02h floppy, 03h hard disk
322 pop ax ; Drop return address
323 xor ax,ax ; Success...
324 jmp DoneWeird ; But don't stick it into P_AX
330 mov ah,[bx] ; Copy last status
342 movzx ax,P_AL ; AH = 0, AL = transfer count
349 test byte [ConfigFlags],CONFIG_READONLY
352 xchg esi,edi ; Opposite direction of a Read!
354 .readonly: mov ah,03h ; Write protected medium
357 ; Verify integrity; just bounds-check
360 call setup_regs ; Returns error if appropriate
361 ; And fall through to success
363 CheckIfReady: ; These are always-successful noop functions
371 xor ax,ax ; Always successful
376 mov dl,[DriveCnt] ; Cached data
378 test byte [DriveNo],80h
386 dec ax ; We report the highest #, not the count
396 ; Is this MEMDISK installation check?
407 ; MEMDISK installation check...
413 mov P_DI,MemDisk_Info
419 ; EDD functions -- only if enabled
428 mov P_BX,0AA55h ; EDD signature
429 mov P_AX,03000h ; EDD 3.0
430 mov P_CX,0007h ; Bit 0 - Fixed disk access subset
431 ; Bit 1 - Locking and ejecting subset
433 pop ax ; Drop return address
435 jmp DoneWeird ; Success, but AH != 0, sigh...
451 xchg esi,edi ; Opposite direction of a Read!
458 call edd_setup_regs ; Just bounds checking
470 lodsw ; Length of our DPT
472 cmp cx,26 ; Minimum size
494 ; Set up registers as for a "Read", and compares against disk
496 ; WARNING: This fails immediately, even if we can transfer some
497 ; sectors. This isn't really the correct behaviour.
500 ; Convert a CHS address in P_CX/P_DH into an LBA in eax
502 ; CL[0:5] = sector (1-based) CL[7:6] = cyl[9:8]
505 movzx ebx,cl ; Sector number
507 dec ebx ; Sector number is 1-based
510 movzx edi,P_DH ; Head number
511 movzx eax,word [Heads]
515 xchg cl,ch ; Now (E)CX <- cylinder number
516 mul ecx ; eax <- Heads*cyl# (edx <- 0)
520 ; Now eax = LBA, edx = 0
523 ; setup_regs continues...
525 ; Note: edi[31:16] and ecx[31:16] = 0 already
526 mov di,P_BX ; Get linear address of target buffer
529 add edi,ecx ; EDI = address to fetch to
530 movzx ecx,P_AL ; Sector count
532 add eax,ecx ; LBA of final sector + 1
533 shl esi,SECTORSIZE_LG2 ; LBA -> byte offset
534 add esi,[DiskBuf] ; Get address in high memory
535 cmp eax,[DiskSize] ; Check the high mark against limit
537 shl ecx,SECTORSIZE_LG2-2 ; Convert count to dwords
540 .overrun: pop ax ; Drop setup_regs return address
541 mov ax,0200h ; Missing address mark
544 ; Set up registers as for an EDD Read, and compares against disk size.
548 mov si,P_SI ; DS:SI -> DAPA
555 cmp dword [es:si+4],-1
558 movzx ebx,word [es:si+4] ; Offset
559 movzx edi,word [es:si+6] ; Segment
565 cmp dx,24 ; Must be large enough to hold
569 cmp dword [es:si+20],0 ; > 4 GB addresses not supported
570 mov ax,0900h ; "Data boundary error" - bogus, but
571 ; no really better code available
577 cmp dword [es:si+12],0 ; LBA too large?
580 movzx ecx, word [es:si+2] ; Sectors to transfer
581 mov esi,[es:si+8] ; Starting sector
588 shl ecx,SECTORSIZE_LG2-2 ; Convert to dwords
589 shl esi,SECTORSIZE_LG2 ; Convert to an offset
596 mov ax,0100h ; Invalid command
598 pop ax ; Drop setup_regs return address
602 mov ax,0200h ; "Address mark not found" =
603 ; LBA beyond end of disk
605 and word [es:si+2],0 ; No sectors transferred
611 mov ax,0B200h ; Volume Not Removable
617 cmp al,1 ; We only support query, not terminate
618 jne ElToritoErr ; Fail
619 cmp dl,7fh ; Terminate all?
621 cmp dl,[cs:DriveNo] ; Terminate our drive?
623 jmp ElToritoErr ; Fail
624 .doit: mov es,P_DS ; Caller's DS:SI pointed to packet
625 mov di,P_SI ; We'll use ES:DI
626 mov si,CD_PKT.size ; First byte is packet size
627 xor cx,0 ; Empty our count
628 ;mov cl,[ds:si] ; We'll copy that many bytes
630 rep movsb ; Copy until CX is zero
638 mov ax,100h ; Invalid parameter
644 ; INT 15h intercept routines
647 cmp edx,534D4150h ; "SMAP"
649 cmp ecx,20 ; Need 20 bytes
659 add bx,12 ; Advance to next
660 mov eax,[bx-4] ; Type
661 and eax,eax ; Null type?
662 jz .renew ; If so advance to next
664 mov eax,[bx-12] ; Start addr (low)
665 mov edx,[bx-8] ; Start addr (high)
668 mov eax,[bx] ; End addr (low)
669 mov edx,[bx+4] ; End addr (high)
670 sub eax,[bx-12] ; Derive the length
672 mov [es:di+8],eax ; Length (low)
673 mov [es:di+12],edx ; Length (high)
674 cmp dword [bx+8],-1 ; Type of next = end?
676 xor ebx,ebx ; Done with table
679 mov edx,eax ; Some systems expect eax = edx = SMAP
680 mov ecx,20 ; Bytes loaded
683 mov byte [bp+6], 02h ; Clear CF
688 mov byte [bp+6], 03h ; Set CF
705 jmp far [cs:OldInt15]
707 int15_e801: ; Get mem size for > 64 MB config
712 jmp short int15_success
714 int15_e881: ; Get mem size for > 64 MB config
720 jmp short int15_success
722 int15_88: ; Get extended mem size
723 mov ax,[cs:MemInt1588]
724 jmp short int15_success
727 ; Routine to copy in/out of high memory
728 ; esi = linear source address
729 ; edi = linear target address
730 ; ecx = 32-bit word count
732 ; Assumes cs = ds = es
740 mov bx, real_int15_stub
742 test byte [ConfigFlags], CONFIG_RAW|CONFIG_SAFEINT
743 jz .anymode ; Always do the real INT 15h
745 smsw ax ; Unprivileged!
747 jnz .protmode ; Protmode -> do real INT 15h
750 ; Raw or Safeint mode, and we're in real mode...
752 test byte [ConfigFlags], CONFIG_SAFEINT
757 ; We're in real mode, do it outselves
772 ; Test to see if A20 is enabled or not
789 push dx ; <D> Save A20 status
792 mov ax,2401h ; Enable A20
798 ; DX = 16 for BIGRAW, 8 for RAW
799 ; 8 is selector for a 64K flat segment,
800 ; 16 is selector for a 4GB flat segment.
807 mov bx,16 ; Large flat segment
813 ; DX has the appropriate value to put in
814 ; the registers on return
821 pop dx ; <D> A20 status
827 mov ax,2400h ; Disable A20
834 ; We're in real mode with CONFIG_SAFEINT, invoke the
835 ; original INT 15h vector. We used to test for the
836 ; INT 15h vector being unchanged here, but that is
837 ; *us*; however, the test was wrong for years (always
838 ; negative) so instead of fixing the test do what we
839 ; tested and don't bother probing.
840 mov bx, fake_int15_stub
854 push ecx ; Transfer size this cycle
858 mov [Mover_src1+2], al
863 mov [Mover_dst1+2], al
867 shl cx,1 ; Convert to 16-bit words
868 call bx ; INT 15h stub
869 pop eax ; Transfer size this cycle
889 cli ; Some BIOSes enable interrupts on INT 15h
946 section .data align=16
948 Int13Funcs dw Reset ; 00h - RESET
949 dw GetStatus ; 01h - GET STATUS
951 dw Write ; 03h - WRITE
952 dw Verify ; 04h - VERIFY
953 dw Invalid ; 05h - FORMAT TRACK
954 dw Invalid ; 06h - FORMAT TRACK AND SET BAD FLAGS
955 dw Invalid ; 07h - FORMAT DRIVE AT TRACK
956 dw GetParms ; 08h - GET PARAMETERS
957 dw InitWithParms ; 09h - INITIALIZE CONTROLLER WITH
961 dw Seek ; 0Ch - SEEK TO CYLINDER
962 dw Reset ; 0Dh - RESET HARD DISKS
965 dw CheckIfReady ; 10h - CHECK IF READY
966 dw Recalibrate ; 11h - RECALIBRATE
970 dw GetDriveType ; 15h - GET DRIVE TYPE
971 dw DetectChange ; 16h - DETECT DRIVE CHANGE
983 dw ReadMult ; 21h - READ MULTIPLE
984 dw WriteMult ; 22h - WRITE MULTIPLE
985 dw SetMode ; 23h - SET CONTROLLER FEATURES
986 dw SetMode ; 24h - SET MULTIPLE MODE
987 dw Invalid ; 25h - IDENTIFY DRIVE
1015 dw EDDPresence ; 41h - EDD PRESENCE DETECT
1016 dw EDDRead ; 42h - EDD READ
1017 dw EDDWrite ; 43h - EDD WRITE
1018 dw EDDVerify ; 44h - EDD VERIFY
1019 dw EDDLock ; 45h - EDD LOCK/UNLOCK MEDIA
1020 dw EDDEject ; 46h - EDD EJECT
1021 dw EDDSeek ; 47h - EDD SEEK
1022 dw EDDGetParms ; 48h - EDD GET PARAMETERS
1023 dw EDDDetectChange ; 49h - EDD MEDIA CHANGE STATUS
1024 %if ELTORITO ; EDD El Torito Functions
1025 ; ELTORITO _must_ also have EDD
1026 dw ElToritoEmulate ; 4Ah - Initiate Disk Emulation
1027 dw ElToritoTerminate ; 4Bh - Terminate Disk Emulation
1028 dw ElToritoBoot ; 4Ch - Initiate Disk Emu. and Reboot
1029 dw ElToritoCatalog ; 4Dh - Return Boot Catalog
1034 Int13FuncsCnt equ (Int13FuncsEnd-Int13Funcs) >> 1
1038 Shaker dw ShakerEnd-$-1 ; Descriptor table limit
1039 dd 0 ; Pointer to self
1042 Shaker_RMDS: dd 0x0000ffff ; 64K data segment
1045 Shaker_DS: dd 0x0000ffff ; 4GB data segment
1052 Mover dd 0, 0, 0, 0 ; Must be zero
1053 dw 0ffffh ; 64 K segment size
1054 Mover_src1: db 0, 0, 0 ; Low 24 bits of source addy
1055 db 93h ; Access rights
1056 db 00h ; Extended access rights
1057 Mover_src2: db 0 ; High 8 bits of source addy
1058 dw 0ffffh ; 64 K segment size
1059 Mover_dst1: db 0, 0, 0 ; Low 24 bits of target addy
1060 db 93h ; Access rights
1061 db 00h ; Extended access rights
1062 Mover_dst2: db 0 ; High 8 bits of source addy
1063 Mover_dummy2: dd 0, 0, 0, 0 ; More space for the BIOS
1067 ; Fields common to all ACPI tables
1068 dd ' ' ; ACPI signature ("mBFT")
1069 ; This is filled-in by the installer
1070 ; to avoid an accidentally valid mBFT
1071 dd mBFT_Len ; ACPI table length
1072 db 1 ; ACPI revision
1073 db 0 ; ACPI table checksum
1074 db 'MEMDSK' ; ACPI OEM ID
1075 db 'Syslinux' ; ACPI OEM table ID
1076 dd 0 ; ACPI OEM revision
1077 dd 0 ; ACPI ASL compiler vendor ID
1078 dd 0 ; ACPI ASL compiler revision
1079 ; The next field is mBFT-specific and filled-in by the installer
1080 dd 0 ; "Safe hook" physical address
1082 ; Note that the above ends on a DWORD boundary.
1083 ; The MDI has always started at such a boundary.
1084 ; Portions of the MDI are patched by the installer
1085 MemDisk_Info equ $ ; Pointed to by installation check
1086 MDI_Bytes dw MDI_Len ; Total bytes in MDI structure
1087 MDI_Version db VERSION_MINOR, VERSION_MAJOR ; MEMDISK version
1089 DiskBuf dd 0 ; Linear address of high memory disk
1090 DiskSize dd 0 ; Size of disk in blocks
1091 CommandLine dw 0, 0 ; Far pointer to saved command line
1093 OldInt13 dd 0 ; INT 13h in chain
1094 OldInt15 dd 0 ; INT 15h in chain
1096 OldDosMem dw 0 ; Old position of DOS mem end
1097 BootLoaderID db 0 ; Boot loader ID from header
1100 DPT_ptr dw 0 ; If nonzero, pointer to DPT
1101 ; Original DPT pointer follows
1103 MDI_Len equ $-MemDisk_Info
1104 mBFT_Len equ $-mBFT ; mBFT includes the MDI
1106 ; ---- MDI structure ends here ---
1107 DriveShiftLimit db 0ffh ; Installer will probe for
1108 ; a range of contiguous drives.
1109 ; Any BIOS drives above this region
1110 ; shall not be impacted by our
1111 ; shifting behaviour
1112 db 0 ; pad to a DWORD
1113 dw 0 ; pad to a QWORD
1114 MemInt1588 dw 0 ; 1MB-65MB memory amount (1K)
1116 Cylinders dw 0 ; Cylinder count
1117 Heads dw 0 ; Head count
1118 Sectors dd 0 ; Sector count (zero-extended)
1120 Mem1MB dd 0 ; 1MB-16MB memory amount (1K)
1121 Mem16MB dd 0 ; 16MB-4G memory amount (64K)
1123 DriveNo db 0 ; Our drive number
1124 DriveType db 0 ; Our drive type (floppies)
1125 DriveCnt db 0 ; Drive count (from the BIOS)
1127 ConfigFlags db 0 ; Bit 0 - readonly
1129 MyStack dw 0 ; Offset of stack
1130 StatusPtr dw 0 ; Where to save status (zeroseg ptr)
1132 DPT times 16 db 0 ; BIOS parameter table pointer (floppies)
1133 OldInt1E dd 0 ; Previous INT 1E pointer (DPT)
1139 ; Bit 0 - DMA boundaries handled transparently
1140 ; Bit 3 - Device supports write verify
1141 ; Bit 5 - Media is lockable
1142 .cylinders dd 0 ; Filled in by installer
1143 .heads dd 0 ; Filled in by installer
1144 .sectors dd 0 ; Filled in by installer
1145 .totalsize dd 0, 0 ; Filled in by installer
1146 .bytespersec dw SECTORSIZE
1147 .eddtable dw -1, -1 ; Invalid DPTE pointer
1148 .dpikey dw 0BEDDh ; Device Path Info magic
1149 .dpilen db 2ch ; DPI len
1150 .res1 db 0 ; Reserved
1151 .res2 dw 0 ; Reserved
1152 .bustype dd 'MEM ' ; Host bus type (4 bytes, space padded)
1153 .inttype dd 'MEMORY ' ; Interface type (8 bytes, spc. padded)
1154 .intpath dd 0, 0 ; Interface path
1155 .devpath dd 0, 0, 0, 0 ; Device path
1156 .res3 db 0 ; Reserved
1157 .chksum db 0 ; DPI checksum
1160 ; El Torito CD Specification Packet - mostly filled in by installer
1162 .size db 13h ; Packet size (19 bytes)
1163 .type db 0 ; Boot media type (flags)
1164 .driveno db 0E0h ; INT 13h drive number
1165 .controller db 0 ; Controller index
1166 .start dd 0 ; Starting LBA of image
1167 .devno dw 0 ; Device number
1168 .user_buf dw 0 ; User buffer segment
1169 .load_seg dw 0 ; Load segment
1170 .sect_count dw 0 ; Emulated sectors to load
1171 .geom1 db 0 ; Cylinders bits 0 thru 7
1172 .geom2 db 0 ; Sects/track 0 thru 5, cyls 8, 9
1181 Stack dd 0 ; Saved SS:ESP on invocation
1183 SavedAX dw 0 ; AX saved on invocation
1184 Recursive dw 0 ; Recursion counter
1186 alignb 4, db 0 ; We *MUST* end on a dword boundary
1188 E820Table equ $ ; The installer loads the E820 table here
1189 TotalSize equ $ ; End pointer