blob: 91040badce00e2cda8d8b831bde696c50ed36e91 [file] [log] [blame]
; -*- fundamental -*- (asm-mode sucks)
; ****************************************************************************
;
; memdisk.inc
;
; A program to emulate an INT 13h disk BIOS from a "disk" in extended
; memory.
;
; Copyright 2001-2009 H. Peter Anvin - All Rights Reserved
; Copyright 2009 Intel Corporation; author: H. Peter Anvin
; Portions copyright 2009 Shao Miller [El Torito code, mBFT, safe hook]
;
; This program is free software; you can redistribute it and/or modify
; it under the terms of the GNU General Public License as published by
; the Free Software Foundation, Inc., 53 Temple Place Ste 330,
; Boston MA 02111-1307, USA; either version 2 of the License, or
; (at your option) any later version; incorporated herein by reference.
;
; ****************************************************************************
%include "../version.gen"
; %define DEBUG_TRACERS ; Uncomment to get debugging tracers
%ifdef DEBUG_TRACERS
%macro TRACER 1
call debug_tracer
db %1
%endmacro
%macro WRITEHEX2 0-1 al
%ifnidni %1,al
push ax
mov al,%1
call writehex2
pop ax
%else
call writehex2
%endif
%endmacro
%macro WRITEHEX4 0-1 ax
%ifnidni %1,ax
push ax
mov ax,%1
call writehex4
pop ax
%else
call writehex4
%endif
%endmacro
%macro WRITEHEX8 0-1 eax
%ifnidni %1,eax
push eax
mov eax,%1
call writehex8
pop eax
%else
call writehex8
%endif
%endmacro
%else ; DEBUG_TRACERS
%macro TRACER 1
%endmacro
%macro WRITEHEX2 0-1
%endmacro
%macro WRITEHEX4 0-1
%endmacro
%macro WRITEHEX8 0-1
%endmacro
%endif ; DEBUG_TRACERS
; Flags we test our configuration against
%define CONFIG_READONLY 0x01
%define CONFIG_RAW 0x02
%define CONFIG_SAFEINT 0x04
%define CONFIG_BIGRAW 0x08 ; MUST be 8!
org 0h
%define SECTORSIZE (1 << SECTORSIZE_LG2)
; Parameter registers definition; this is the definition
; of the stack frame.
%define P_DS word [bp+34]
%define P_ES word [bp+32]
%define P_EAX dword [bp+28]
%define P_HAX word [bp+30]
%define P_AX word [bp+28]
%define P_AL byte [bp+28]
%define P_AH byte [bp+29]
%define P_ECX dword [bp+24]
%define P_HCX word [bp+26]
%define P_CX word [bp+24]
%define P_CL byte [bp+24]
%define P_CH byte [bp+25]
%define P_EDX dword [bp+20]
%define P_HDX word [bp+22]
%define P_DX word [bp+20]
%define P_DL byte [bp+20]
%define P_DH byte [bp+21]
%define P_EBX dword [bp+16]
%define P_HBX word [bp+18]
%define P_HBXL byte [bp+18]
%define P_BX word [bp+16]
%define P_BL byte [bp+16]
%define P_BH byte [bp+17]
%define P_EBP dword [bp+8]
%define P_BP word [bp+8]
%define P_ESI dword [bp+4]
%define P_SI word [bp+4]
%define P_EDI dword [bp]
%define P_DI word [bp]
section .text
; These pointers are used by the installer and
; must be first in the binary
Pointers: dw Int13Start
dw Int15Start
dw MemDisk_Info ; Portions are patched by installer
dw TotalSize
dw IretPtr
IretPtr equ Int13Start.iret
Int13Start:
jmp strict near .SafeHookEnd ; 3-byte jump
db '$INT13SF' ; Signature for "safe hook"
db 'MEMDISK ' ; Vendor ID
dd 0 ; SEG:OFF of previous INT 13h hook
; Must be filled in by installer
dd 0 ; "Safe hook" flags
; ---- "Safe hook" structure ends here ---
; This next field should be guaranteed at this position after the
; "safe hook" structure. This allows for a MEMDISK OS driver to
; immediately find out the particular parameters using the mBFT
; and MDI structures. This binary will have the offset to the mBFT
; in this field to begin with, so the installer knows where the mBFT
; is. This is akin to the "Pointers" section above. The installer
; will refill this field with the physical address of the mBFT for
; future consumers, such as OS drivers.
dd mBFT ; Offset from hook to the mBFT
.SafeHookEnd:
cmp word [cs:Recursive],0
jne recursive
; Swap stack
mov [cs:Stack],esp
mov [cs:Stack+4],ss
mov [cs:SavedAX],ax
mov ax,cs
mov ss,ax
mov sp,[cs:MyStack]
%if ELTORITO
cmp word [cs:SavedAX],4a00h ; El Torito function?
jae our_drive ; We grab it
%endif
; See if DL points to our class of device (FD, HD)
push dx
push dx
xor dl,[cs:DriveNo]
pop dx
js .nomatch ; If SF=0, we have a class match here
; 0x00 the sign bit for FD
; 0x80 the sign bit for HD
jz our_drive ; If ZF=1, we have an exact match
cmp dl,[cs:DriveNo]
jb .nomatch ; Drive < Our drive
cmp dl,[cs:DriveShiftLimit]
jae .nomatch ; Drive > The maximum drive
; number that we will shift for.
; This leaves any higher-up BIOS
; drives alone, such as an optical
; disc drive 0xA0 or 0xE0
dec dl ; Drive > Our drive, adjust drive #
.nomatch:
TRACER '!'
WRITEHEX2 dl
TRACER ','
mov ax,[cs:SavedAX]
WRITEHEX4
inc word [cs:Recursive]
pushf
call far [cs:OldInt13]
pushf
dec word [cs:Recursive]
push bp
mov bp,sp
cmp byte [cs:SavedAX+1],08h ; Get drive params function?
je .norestoredl ; DL = number of drives
cmp byte [cs:SavedAX+1],15h ; Get disk type function?
jne .restoredl
test byte [bp+4],80h ; Hard disk?
jnz .norestoredl ; CX:DX = size of device
.restoredl:
mov dl,[bp+4]
.norestoredl:
push ax
push ebx
push ds
mov ax,[bp+2] ; Flags
lds ebx,[cs:Stack]
mov [bx+4],al ; Arithmetic flags
pop ds
pop ebx
pop ax
pop bp
lss esp,[cs:Stack]
.iret: iret
recursive:
TRACER '@'
jmp_oldint13:
jmp far [cs:OldInt13]
our_drive:
; Set up standard entry frame
push ds
push es
mov ds,ax
mov es,ax
mov ax,[SavedAX]
pushad
mov bp,sp ; Point BP to the entry stack frame
TRACER 'F'
WRITEHEX4
; Note: AX == P_AX here
cmp ah,Int13FuncsCnt-1
ja Invalid_jump
%if ELTORITO
mov al,[CD_PKT.type] ; Check if we are in
cmp al,0 ; El Torito no emulation mode
ja .emulation ; No. We support the function
cmp ah,3fh ; Yes. We must not support functions
jbe Invalid_jump ; 0 through 3Fh. Check and decide
.emulation:
%endif
xor al,al ; AL = 0 is standard entry condition
mov di,ax
shr di,7 ; Convert AH to an offset in DI
call [Int13Funcs+di]
Done: ; Standard routine for return
mov P_AX,ax
DoneWeird:
TRACER 'D'
xor bx,bx
mov es,bx
mov bx,[StatusPtr]
mov [es:bx],ah ; Save status
and ah,ah
lds ebx,[Stack]
; This sets the low byte (the arithmetic flags) of the
; FLAGS on stack to either 00h (no flags) or 01h (CF)
; depending on if AH was zero or not.
setnz [bx+4] ; Set CF iff error
popad
pop es
pop ds
lss esp,[cs:Stack]
iret
Reset:
; Reset affects multiple drives, so we need to pass it on
TRACER 'R'
xor ax,ax ; Bottom of memory
mov es,ax
test dl,dl ; Always pass it on if we are
; resetting HD
js .hard_disk ; Bit 7 set
; Some BIOSes get very unhappy if we pass a reset floppy
; command to them and don't actually have any floppies.
; This is a bug, but we have to deal with it nontheless.
; Therefore, if we are the *ONLY* floppy drive, and the
; user didn't request HD reset, then just drop the command.
; BIOS equipment byte, top two bits + 1 == total # of floppies
test byte [es:0x410],0C0h
jz success
jmp .pass_on ; ... otherwise pass it to the BIOS
.hard_disk:
; ... same thing for hard disks, sigh ...
cmp byte [es:0x475],1 ; BIOS variable for number of hard
; disks
jbe success
.pass_on:
pop ax ; Drop return address
popad ; Restore all registers
pop es
pop ds
lss esp,[cs:Stack] ; Restore the stack
and dl,80h ; Clear all but the type bit
jmp jmp_oldint13
Invalid:
pop dx ; Drop return address
Invalid_jump:
TRACER 'I'
mov ah,01h ; Unsupported function
jmp short Done
GetDriveType:
test byte [DriveNo],80h
mov bl,02h ; Type 02h = floppy with changeline
jz .floppy
; Hard disks only! DO NOT set CX:DX for floppies...
; it apparently causes Win98SE DOS to go into an loop
; resetting the drive over and over. Sigh.
inc bx ; Type = 03h
mov dx,[DiskSize] ; Return the disk size in sectors
mov P_DX,dx
mov cx,[DiskSize+2]
mov P_CX,cx
.floppy:
mov P_AH,bl ; 02h floppy, 03h hard disk
pop ax ; Drop return address
xor ax,ax ; Success...
jmp DoneWeird ; But don't stick it into P_AX
GetStatus:
xor ax,ax
mov es,ax
mov bx,[StatusPtr]
mov ah,[bx] ; Copy last status
ret
ReadMult:
TRACER 'm'
Read:
TRACER 'R'
call setup_regs
do_copy:
TRACER '<'
call bcopy
TRACER '>'
movzx ax,P_AL ; AH = 0, AL = transfer count
ret
WriteMult:
TRACER 'M'
Write:
TRACER 'W'
test byte [ConfigFlags],CONFIG_READONLY
jnz .readonly
call setup_regs
xchg esi,edi ; Opposite direction of a Read!
jmp short do_copy
.readonly: mov ah,03h ; Write protected medium
ret
; Verify integrity; just bounds-check
Seek:
Verify:
call setup_regs ; Returns error if appropriate
; And fall through to success
CheckIfReady: ; These are always-successful noop functions
Recalibrate:
InitWithParms:
DetectChange:
EDDDetectChange:
EDDLock:
SetMode:
success:
xor ax,ax ; Always successful
ret
GetParms:
TRACER 'G'
mov dl,[DriveCnt] ; Cached data
mov P_DL,dl
test byte [DriveNo],80h
jnz .hd
mov P_DI,DPT
mov P_ES,cs
mov bl,[DriveType]
mov P_BL,bl
.hd:
mov ax,[Cylinders]
dec ax ; We report the highest #, not the count
xchg al,ah
shl al,6
or al,[Sectors]
mov P_CX,ax
mov ax,[Heads]
dec ax
mov P_DH,al
;
; Is this MEMDISK installation check?
;
cmp P_HAX,'ME'
jne .notic
cmp P_HCX,'MD'
jne .notic
cmp P_HDX,'IS'
jne .notic
cmp P_HBX,'K?'
jne .notic
; MEMDISK installation check...
mov P_HAX,'!M'
mov P_HCX,'EM'
mov P_HDX,'DI'
mov P_HBX,'SK'
mov P_ES,cs
mov P_DI,MemDisk_Info
.notic:
xor ax,ax
ret
;
; EDD functions -- only if enabled
;
%if EDD
EDDPresence:
TRACER 'E'
TRACER 'c'
cmp P_BX,55AAh
jne Invalid
mov P_BX,0AA55h ; EDD signature
mov P_AX,03000h ; EDD 3.0
mov P_CX,0007h ; Bit 0 - Fixed disk access subset
; Bit 1 - Locking and ejecting subset
; Bit 2 - EDD subset
pop ax ; Drop return address
xor ax,ax ; Success
jmp DoneWeird ; Success, but AH != 0, sigh...
EDDRead:
TRACER 'E'
TRACER 'r'
call edd_setup_regs
call bcopy
xor ax,ax
ret
EDDWrite:
TRACER 'E'
TRACER 'w'
call edd_setup_regs
xchg esi,edi ; Opposite direction of a Read!
call bcopy
xor ax,ax
ret
EDDVerify:
EDDSeek:
call edd_setup_regs ; Just bounds checking
xor ax,ax
ret
EDDGetParms:
TRACER 'E'
TRACER 'p'
mov es,P_DS
mov di,P_SI
mov si,EDD_DPT
lodsw ; Length of our DPT
mov cx,[es:di]
cmp cx,26 ; Minimum size
jb .overrun
cmp cx,ax
jb .oksize
mov cx,ax
.oksize:
mov ax,cx
stosw
dec cx
dec cx
rep movsb
xor ax,ax
ret
.overrun:
mov ax,0100h
ret
%endif ; EDD
; Set up registers as for a "Read", and compares against disk
; size.
; WARNING: This fails immediately, even if we can transfer some
; sectors. This isn't really the correct behaviour.
setup_regs:
; Convert a CHS address in P_CX/P_DH into an LBA in eax
; CH = cyl[7:0]
; CL[0:5] = sector (1-based) CL[7:6] = cyl[9:8]
; DH = head
movzx ecx,P_CX
movzx ebx,cl ; Sector number
and bl,3Fh
dec ebx ; Sector number is 1-based
cmp bx,[Sectors]
jae .overrun
movzx edi,P_DH ; Head number
movzx eax,word [Heads]
cmp di,ax
jae .overrun
shr cl,6
xchg cl,ch ; Now (E)CX <- cylinder number
mul ecx ; eax <- Heads*cyl# (edx <- 0)
add eax,edi
mul dword [Sectors]
add eax,ebx
; Now eax = LBA, edx = 0
;
; setup_regs continues...
;
; Note: edi[31:16] and ecx[31:16] = 0 already
mov di,P_BX ; Get linear address of target buffer
mov cx,P_ES
shl ecx,4
add edi,ecx ; EDI = address to fetch to
movzx ecx,P_AL ; Sector count
mov esi,eax
add eax,ecx ; LBA of final sector + 1
shl esi,SECTORSIZE_LG2 ; LBA -> byte offset
add esi,[DiskBuf] ; Get address in high memory
cmp eax,[DiskSize] ; Check the high mark against limit
ja .overrun
shl ecx,SECTORSIZE_LG2-2 ; Convert count to dwords
ret
.overrun: pop ax ; Drop setup_regs return address
mov ax,0200h ; Missing address mark
ret ; Return to Done
; Set up registers as for an EDD Read, and compares against disk size.
%if EDD
edd_setup_regs:
push es
mov si,P_SI ; DS:SI -> DAPA
mov es,P_DS
mov dx,[es:si]
cmp dx,16
jb .baddapa
cmp dword [es:si+4],-1
je .linear_address
movzx ebx,word [es:si+4] ; Offset
movzx edi,word [es:si+6] ; Segment
shl edi,4
add ebx,edi
jmp .got_address
.linear_address:
cmp dx,24 ; Must be large enough to hold
; linear address
jb .baddapa
cmp dword [es:si+20],0 ; > 4 GB addresses not supported
mov ax,0900h ; "Data boundary error" - bogus, but
; no really better code available
jne .error
mov ebx,[es:si+16]
.got_address:
cmp dword [es:si+12],0 ; LBA too large?
jne .overrun
movzx ecx, word [es:si+2] ; Sectors to transfer
mov esi,[es:si+8] ; Starting sector
mov eax,esi
add eax,ecx
jc .overrun
cmp eax,[DiskSize]
ja .overrun
shl ecx,SECTORSIZE_LG2-2 ; Convert to dwords
shl esi,SECTORSIZE_LG2 ; Convert to an offset
add esi,[DiskBuf]
mov edi,ebx
pop es
ret
.baddapa:
mov ax,0100h ; Invalid command
pop es
pop ax ; Drop setup_regs return address
ret
.overrun:
mov ax,0200h ; "Address mark not found" =
; LBA beyond end of disk
.error:
and word [es:si+2],0 ; No sectors transferred
pop es
pop ax
ret
EDDEject:
mov ax,0B200h ; Volume Not Removable
ret
%if ELTORITO
ElToritoTerminate:
TRACER 'T'
mov ax,[cs:SavedAX]
cmp al,1 ; We only support query, not terminate
jne ElToritoErr ; Fail
cmp dl,7fh ; Terminate all?
je .doit
cmp dl,[cs:DriveNo] ; Terminate our drive?
je .doit
jmp ElToritoErr ; Fail
.doit: mov es,P_DS ; Caller's DS:SI pointed to packet
mov di,P_SI ; We'll use ES:DI
mov si,CD_PKT.size ; First byte is packet size
xor cx,0 ; Empty our count
;mov cl,[ds:si] ; We'll copy that many bytes
mov cl,13h
rep movsb ; Copy until CX is zero
mov ax,0 ; Success
ret
ElToritoEmulate:
ElToritoBoot:
ElToritoCatalog:
ElToritoErr:
TRACER '!'
mov ax,100h ; Invalid parameter
ret
%endif ; ELTORITO
%endif ; EDD
;
; INT 15h intercept routines
;
int15_e820:
cmp edx,534D4150h ; "SMAP"
jne oldint15
cmp ecx,20 ; Need 20 bytes
jb err86
push ds
push cs
pop ds
push edx ; "SMAP"
and ebx,ebx
jne .renew
mov ebx,E820Table
.renew:
add bx,12 ; Advance to next
mov eax,[bx-4] ; Type
and eax,eax ; Null type?
jz .renew ; If so advance to next
mov [es:di+16],eax
mov eax,[bx-12] ; Start addr (low)
mov edx,[bx-8] ; Start addr (high)
mov [es:di],eax
mov [es:di+4],edx
mov eax,[bx] ; End addr (low)
mov edx,[bx+4] ; End addr (high)
sub eax,[bx-12] ; Derive the length
sbb edx,[bx-8]
mov [es:di+8],eax ; Length (low)
mov [es:di+12],edx ; Length (high)
cmp dword [bx+8],-1 ; Type of next = end?
jne .notdone
xor ebx,ebx ; Done with table
.notdone:
pop eax ; "SMAP"
mov edx,eax ; Some systems expect eax = edx = SMAP
mov ecx,20 ; Bytes loaded
pop ds
int15_success:
mov byte [bp+6], 02h ; Clear CF
pop bp
iret
err86:
mov byte [bp+6], 03h ; Set CF
mov ah,86h
pop bp
iret
Int15Start:
push bp
mov bp,sp
cmp ax,0E820h
je near int15_e820
cmp ax,0E801h
je int15_e801
cmp ax,0E881h
je int15_e881
cmp ah,88h
je int15_88
oldint15: pop bp
jmp far [cs:OldInt15]
int15_e801: ; Get mem size for > 64 MB config
mov ax,[cs:Mem1MB]
mov cx,ax
mov bx,[cs:Mem16MB]
mov dx,bx
jmp short int15_success
int15_e881: ; Get mem size for > 64 MB config
; 32-bit code
mov eax,[cs:Mem1MB]
mov ecx,eax
mov ebx,[cs:Mem16MB]
mov edx,ebx
jmp short int15_success
int15_88: ; Get extended mem size
mov ax,[cs:MemInt1588]
jmp short int15_success
;
; Routine to copy in/out of high memory
; esi = linear source address
; edi = linear target address
; ecx = 32-bit word count
;
; Assumes cs = ds = es
;
bcopy:
push eax
push ebx
push edx
push ebp
mov bx, real_int15_stub
test byte [ConfigFlags], CONFIG_RAW|CONFIG_SAFEINT
jz .anymode ; Always do the real INT 15h
smsw ax ; Unprivileged!
test al,01h
jnz .protmode ; Protmode -> do real INT 15h
.realmode:
; Raw or Safeint mode, and we're in real mode...
test byte [ConfigFlags], CONFIG_SAFEINT
jnz .fakeint15
.raw:
TRACER 'r'
; We're in real mode, do it outselves
pushfd ; <A>
push ds ; <B>
push es ; <C>
cli
cld
xor ebx,ebx
mov bx,cs
shl ebx,4
lea edx,[Shaker+ebx]
mov [Shaker+2],edx
; Test to see if A20 is enabled or not
xor ax,ax
mov ds,ax
dec ax
mov es,ax
mov ax,[0]
mov bx,ax
xor bx,[es:10h]
not ax
mov [0],ax
mov dx,ax
xor dx,[es:10h]
not ax
mov [0],ax
or dx,bx
push dx ; <D> Save A20 status
jnz .skip_a20e
mov ax,2401h ; Enable A20
int 15h
.skip_a20e:
mov dl,[ConfigFlags]
and dx,CONFIG_BIGRAW
add dx,8
; DX = 16 for BIGRAW, 8 for RAW
; 8 is selector for a 64K flat segment,
; 16 is selector for a 4GB flat segment.
lgdt [cs:Shaker]
mov eax,cr0
or al,01h
mov cr0,eax
mov bx,16 ; Large flat segment
mov ds,bx
mov es,bx
a32 rep movsd
; DX has the appropriate value to put in
; the registers on return
mov ds,dx
mov es,dx
and al,~01h
mov cr0,eax
pop dx ; <D> A20 status
pop es ; <C>
pop ds ; <B>
and dx,dx
jnz .skip_a20d
mov ax,2400h ; Disable A20
int 15h
.skip_a20d:
popfd ; <A>
jmp .done
.fakeint15:
; We're in real mode with CONFIG_SAFEINT, invoke the
; original INT 15h vector. We used to test for the
; INT 15h vector being unchanged here, but that is
; *us*; however, the test was wrong for years (always
; negative) so instead of fixing the test do what we
; tested and don't bother probing.
mov bx, fake_int15_stub
.protmode:
TRACER 'p'
.anymode:
.copy_loop:
push esi
push edi
push ecx
cmp ecx,4000h
jna .safe_size
mov ecx,4000h
.safe_size:
push ecx ; Transfer size this cycle
mov eax, esi
mov [Mover_src1], si
shr eax, 16
mov [Mover_src1+2], al
mov [Mover_src2], ah
mov eax, edi
mov [Mover_dst1], di
shr eax, 16
mov [Mover_dst1+2], al
mov [Mover_dst2], ah
mov si,Mover
mov ah, 87h
shl cx,1 ; Convert to 16-bit words
call bx ; INT 15h stub
pop eax ; Transfer size this cycle
pop ecx
pop edi
pop esi
jc .error
lea esi,[esi+4*eax]
lea edi,[edi+4*eax]
sub ecx, eax
jnz .copy_loop
; CF = 0
.error:
.done:
pop ebp
pop edx
pop ebx
pop eax
ret
real_int15_stub:
int 15h
cli ; Some BIOSes enable interrupts on INT 15h
ret
fake_int15_stub:
pushf
call far [OldInt15]
cli
ret
%ifdef DEBUG_TRACERS
debug_tracer: pushad
pushfd
mov bp,sp
mov bx,[bp+9*4]
mov al,[cs:bx]
inc word [bp+9*4]
mov ah,0Eh
mov bx,7
int 10h
popfd
popad
ret
writehex2: pushad
pushfd
mov cx,2
ror eax,4
jmp writehex_common
writehex4: pushad
pushfd
mov cx,4
ror eax,12
jmp writehex_common
writehex8: pushad
pushfd
mov cx,8
ror eax,28
writehex_common:
.loop: push cx
push eax
and al,0Fh
cmp al,10
jb .isdec
add al,'a'-'0'-10
.isdec: add al,'0'
mov ah,0Eh
mov bx,7
int 10h
pop eax
rol eax,4
pop cx
loop .loop
popfd
popad
ret
%endif
section .data align=16
alignb 2
Int13Funcs dw Reset ; 00h - RESET
dw GetStatus ; 01h - GET STATUS
dw Read ; 02h - READ
dw Write ; 03h - WRITE
dw Verify ; 04h - VERIFY
dw Invalid ; 05h - FORMAT TRACK
dw Invalid ; 06h - FORMAT TRACK AND SET BAD FLAGS
dw Invalid ; 07h - FORMAT DRIVE AT TRACK
dw GetParms ; 08h - GET PARAMETERS
dw InitWithParms ; 09h - INITIALIZE CONTROLLER WITH
; DRIVE PARAMETERS
dw Invalid ; 0Ah
dw Invalid ; 0Bh
dw Seek ; 0Ch - SEEK TO CYLINDER
dw Reset ; 0Dh - RESET HARD DISKS
dw Invalid ; 0Eh
dw Invalid ; 0Fh
dw CheckIfReady ; 10h - CHECK IF READY
dw Recalibrate ; 11h - RECALIBRATE
dw Invalid ; 12h
dw Invalid ; 13h
dw Invalid ; 14h
dw GetDriveType ; 15h - GET DRIVE TYPE
dw DetectChange ; 16h - DETECT DRIVE CHANGE
%if EDD
dw Invalid ; 17h
dw Invalid ; 18h
dw Invalid ; 19h
dw Invalid ; 1Ah
dw Invalid ; 1Bh
dw Invalid ; 1Ch
dw Invalid ; 1Dh
dw Invalid ; 1Eh
dw Invalid ; 1Fh
dw Invalid ; 20h
dw ReadMult ; 21h - READ MULTIPLE
dw WriteMult ; 22h - WRITE MULTIPLE
dw SetMode ; 23h - SET CONTROLLER FEATURES
dw SetMode ; 24h - SET MULTIPLE MODE
dw Invalid ; 25h - IDENTIFY DRIVE
dw Invalid ; 26h
dw Invalid ; 27h
dw Invalid ; 28h
dw Invalid ; 29h
dw Invalid ; 2Ah
dw Invalid ; 2Bh
dw Invalid ; 2Ch
dw Invalid ; 2Dh
dw Invalid ; 2Eh
dw Invalid ; 2Fh
dw Invalid ; 30h
dw Invalid ; 31h
dw Invalid ; 32h
dw Invalid ; 33h
dw Invalid ; 34h
dw Invalid ; 35h
dw Invalid ; 36h
dw Invalid ; 37h
dw Invalid ; 38h
dw Invalid ; 39h
dw Invalid ; 3Ah
dw Invalid ; 3Bh
dw Invalid ; 3Ch
dw Invalid ; 3Dh
dw Invalid ; 3Eh
dw Invalid ; 3Fh
dw Invalid ; 40h
dw EDDPresence ; 41h - EDD PRESENCE DETECT
dw EDDRead ; 42h - EDD READ
dw EDDWrite ; 43h - EDD WRITE
dw EDDVerify ; 44h - EDD VERIFY
dw EDDLock ; 45h - EDD LOCK/UNLOCK MEDIA
dw EDDEject ; 46h - EDD EJECT
dw EDDSeek ; 47h - EDD SEEK
dw EDDGetParms ; 48h - EDD GET PARAMETERS
dw EDDDetectChange ; 49h - EDD MEDIA CHANGE STATUS
%if ELTORITO ; EDD El Torito Functions
; ELTORITO _must_ also have EDD
dw ElToritoEmulate ; 4Ah - Initiate Disk Emulation
dw ElToritoTerminate ; 4Bh - Terminate Disk Emulation
dw ElToritoBoot ; 4Ch - Initiate Disk Emu. and Reboot
dw ElToritoCatalog ; 4Dh - Return Boot Catalog
%endif ; ELTORITO
%endif ; EDD
Int13FuncsEnd equ $
Int13FuncsCnt equ (Int13FuncsEnd-Int13Funcs) >> 1
alignb 8, db 0
Shaker dw ShakerEnd-$-1 ; Descriptor table limit
dd 0 ; Pointer to self
dw 0
Shaker_RMDS: dd 0x0000ffff ; 64K data segment
dd 0x00009300
Shaker_DS: dd 0x0000ffff ; 4GB data segment
dd 0x008f9300
ShakerEnd equ $
alignb 8, db 0
Mover dd 0, 0, 0, 0 ; Must be zero
dw 0ffffh ; 64 K segment size
Mover_src1: db 0, 0, 0 ; Low 24 bits of source addy
db 93h ; Access rights
db 00h ; Extended access rights
Mover_src2: db 0 ; High 8 bits of source addy
dw 0ffffh ; 64 K segment size
Mover_dst1: db 0, 0, 0 ; Low 24 bits of target addy
db 93h ; Access rights
db 00h ; Extended access rights
Mover_dst2: db 0 ; High 8 bits of source addy
Mover_dummy2: dd 0, 0, 0, 0 ; More space for the BIOS
alignb 16, db 0
mBFT:
; Fields common to all ACPI tables
dd ' ' ; ACPI signature ("mBFT")
; This is filled-in by the installer
; to avoid an accidentally valid mBFT
dd mBFT_Len ; ACPI table length
db 1 ; ACPI revision
db 0 ; ACPI table checksum
db 'MEMDSK' ; ACPI OEM ID
db 'Syslinux' ; ACPI OEM table ID
dd 0 ; ACPI OEM revision
dd 0 ; ACPI ASL compiler vendor ID
dd 0 ; ACPI ASL compiler revision
; The next field is mBFT-specific and filled-in by the installer
dd 0 ; "Safe hook" physical address
; Note that the above ends on a DWORD boundary.
; The MDI has always started at such a boundary.
; Portions of the MDI are patched by the installer
MemDisk_Info equ $ ; Pointed to by installation check
MDI_Bytes dw MDI_Len ; Total bytes in MDI structure
MDI_Version db VERSION_MINOR, VERSION_MAJOR ; MEMDISK version
DiskBuf dd 0 ; Linear address of high memory disk
DiskSize dd 0 ; Size of disk in blocks
CommandLine dw 0, 0 ; Far pointer to saved command line
OldInt13 dd 0 ; INT 13h in chain
OldInt15 dd 0 ; INT 15h in chain
OldDosMem dw 0 ; Old position of DOS mem end
BootLoaderID db 0 ; Boot loader ID from header
db 0 ; pad
DPT_ptr dw 0 ; If nonzero, pointer to DPT
; Original DPT pointer follows
MDI_Len equ $-MemDisk_Info
mBFT_Len equ $-mBFT ; mBFT includes the MDI
; ---- MDI structure ends here ---
DriveShiftLimit db 0ffh ; Installer will probe for
; a range of contiguous drives.
; Any BIOS drives above this region
; shall not be impacted by our
; shifting behaviour
db 0 ; pad to a DWORD
dw 0 ; pad to a QWORD
MemInt1588 dw 0 ; 1MB-65MB memory amount (1K)
Cylinders dw 0 ; Cylinder count
Heads dw 0 ; Head count
Sectors dd 0 ; Sector count (zero-extended)
Mem1MB dd 0 ; 1MB-16MB memory amount (1K)
Mem16MB dd 0 ; 16MB-4G memory amount (64K)
DriveNo db 0 ; Our drive number
DriveType db 0 ; Our drive type (floppies)
DriveCnt db 0 ; Drive count (from the BIOS)
ConfigFlags db 0 ; Bit 0 - readonly
MyStack dw 0 ; Offset of stack
StatusPtr dw 0 ; Where to save status (zeroseg ptr)
DPT times 16 db 0 ; BIOS parameter table pointer (floppies)
OldInt1E dd 0 ; Previous INT 1E pointer (DPT)
%if EDD
EDD_DPT:
.length dw 30
.info dw 0029h
; Bit 0 - DMA boundaries handled transparently
; Bit 3 - Device supports write verify
; Bit 5 - Media is lockable
.cylinders dd 0 ; Filled in by installer
.heads dd 0 ; Filled in by installer
.sectors dd 0 ; Filled in by installer
.totalsize dd 0, 0 ; Filled in by installer
.bytespersec dw SECTORSIZE
.eddtable dw -1, -1 ; Invalid DPTE pointer
.dpikey dw 0BEDDh ; Device Path Info magic
.dpilen db 2ch ; DPI len
.res1 db 0 ; Reserved
.res2 dw 0 ; Reserved
.bustype dd 'MEM ' ; Host bus type (4 bytes, space padded)
.inttype dd 'MEMORY ' ; Interface type (8 bytes, spc. padded)
.intpath dd 0, 0 ; Interface path
.devpath dd 0, 0, 0, 0 ; Device path
.res3 db 0 ; Reserved
.chksum db 0 ; DPI checksum
%if ELTORITO
; El Torito CD Specification Packet - mostly filled in by installer
CD_PKT:
.size db 13h ; Packet size (19 bytes)
.type db 0 ; Boot media type (flags)
.driveno db 0E0h ; INT 13h drive number
.controller db 0 ; Controller index
.start dd 0 ; Starting LBA of image
.devno dw 0 ; Device number
.user_buf dw 0 ; User buffer segment
.load_seg dw 0 ; Load segment
.sect_count dw 0 ; Emulated sectors to load
.geom1 db 0 ; Cylinders bits 0 thru 7
.geom2 db 0 ; Sects/track 0 thru 5, cyls 8, 9
.geom3 db 0 ; Heads
%endif ; ELTORITO
%endif ; EDD
; End patch area
alignb 4, db 0
Stack dd 0 ; Saved SS:ESP on invocation
dw 0
SavedAX dw 0 ; AX saved on invocation
Recursive dw 0 ; Recursion counter
alignb 4, db 0 ; We *MUST* end on a dword boundary
E820Table equ $ ; The installer loads the E820 table here
TotalSize equ $ ; End pointer