1190 lines
26 KiB
Plaintext
1190 lines
26 KiB
Plaintext
; -*- 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
|