Windows NT 4.0 source code leak
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

584 lines
20 KiB

;++
;
;Copyright (c) 1995 Compaq Computer Corporation
;
;Module Name:
;
; etfsboot.asm
;
;Abstract:
;
; The ROM in the IBM PC starts the boot process by performing a hardware
; initialization and a verification of all external devices. If an El
; Torito CD-ROM with no-emulation support is detected, it will then load
; the "image" pointed to in the Boot Catalog. This "image" is placed at
; the physical address specified in the Boot Catalog (which should be 07C00h).
;
; The code in this "image" is responsible for locating NTLDR, loading the
; first sector of NTLDR into memory at 2000:0000, and branching to it.
;
; There are only two errors possible during execution of this code.
; 1 - NTLDR does not exist
; 2 - BIOS read error
;
; In both cases, a short message is printed, and the user is prompted to
; reboot the system.
;
;
;Author:
;
; Steve Collins (stevec) 25-Oct-1995
;
;Environment:
;
; Image has been loaded at 7C0:0000 by BIOS.
; Real mode
; ISO 9660 El Torito no-emulation CD-ROM Boot support
; DL = El Torito drive number we booted from
;
;Revision History:
;
;--
page ,132
title boot - NTLDR ETFS loader
name etfsboot
BootSeg segment at 07c0h
BootSeg ends
DirSeg segment at 1000h
DirSeg ends
NtLdrSeg segment at 2000h
NtLdrSeg ends
BootCode segment ;would like to use BootSeg here, but LINK flips its lid
ASSUME CS:BootCode,DS:NOTHING,ES:NOTHING,SS:NOTHING
public ETFSBOOT
ETFSBOOT proc far
xor ax,ax ; Setup the stack to a known good spot
mov ss,ax ; Stack is set to 0000:7c00, which is just below this code
mov sp,7c00h
mov ax,BootSeg ; Set DS to our code/data segment (07C0h)
mov ds,ax
assume DS:BootCode
;
; Save the Drive Number for later use
mov DriveNum,dl
;
; The system is now prepared for us to begin reading. First, we need to
; read in the Primary Volume Descriptor so we can locate the root directory
;
.286
push 01h ; Word 0 (low word) of Transfer size = 1 block (2048 bytes)
push 0h ; Word 1 (high word) of Transfer size = 0
push DirSeg ; Segment of Transfer buffer = DirSeg
push 010h ; Word 0 (low word) of Starting absolute block number = 10h
push 0h ; Word 1 of Starting absolute block number = 0
.8086
call ExtRead
add sp,10 ; Clean 5 arguments off the stack
;
; Determine the root directory location LBN -> ExtentLoc1:ExtentLoc0
; determine the root directory data length in bytes -> ExtentLen1:ExtentLen0
;
mov ax,DirSeg ; ES is set to segment used for storing PVD and directories
mov es,ax
ASSUME ES:DirSeg
mov ax,es:[09eh] ; 32-bit LBN of extent at offset 158 in Primary Volume Descriptor
mov ExtentLoc0,ax ; store low word
mov ax,es:[0a0h]
mov ExtentLoc1,ax ; store high word
mov ax,es:[0a6h] ; 32-bit Root directory data length in bytes at offset 166 in Primary Volume Descriptor
mov ExtentLen0,ax ; store low word
mov ax,es:[0a8h]
mov ExtentLen1,ax ; store high word
;
; Now read in the root directory
;
.286
push DirSeg ; Segment used for transfer = DirSeg
.8086
call ReadExtent
add sp,2 ; Clean 1 argument off the stack
;
; Scan for the presence of the I386 directory
; ES points to directory segment
;
mov EntryToFind, offset I386DIRNAME
mov EntryLen,4
mov IsDir,1
call ScanForEntry
;
; We found the I386 directory entry, so now get its extent location (offset -31 from filename ID)
; ES:[BX] still points to the directory record for the I386 directory
;
call GetExtentInfo
;
; Now read in the I386 directory
;
.286
push DirSeg ; Segment used for transfer = DirSeg
.8086
call ReadExtent
add sp,2 ; Clean 1 argument off the stack
;
; Scan for the presence of SETUPLDR.BIN
; ES points to directory segment
;
mov ax,DirSeg
mov es,ax
mov EntryToFind, offset LOADERNAME
mov EntryLen,12
mov IsDir,0
call ScanForEntry
;
; We found the loader entry, so now get its extent location (offset -31 from filename ID)
; ES:[BX] still points to the directory record for the LOADER
;
call GetExtentInfo
;
; Now, go read the file
;
.286
push NtLdrSeg ; Segment used for transfer = NtLdrSeg
.8086
call ReadExtent
add sp,2 ; Clean 1 argument off the stack
;
; NTLDR requires:
; DL = INT 13 drive number we booted from
;
mov dl, DriveNum ; DL = CD drive number - this isn't really necessary since DirveNum is already in dl
xor ax,ax
.386
push NtLdrSeg
push ax
retf ; "return" to NTLDR.
ETFSBOOT endp
;
; ScanForEntry - Scan for an entry in a directory
;
; Entry:
; ES:0 points to the beginning of the directory to search
; Directory length in bytes is in ExtentLen1 and Extend_Len_0
;
; Exit:
; ES:BX points to record containing entry if match is found
; Otherwise, we jump to error routine
;
ScanForEntry proc near
mov cx,ExtentLen0 ; CX = length of root directory in bytes (low word only)
cld ; Work up for string compares
xor bx,bx
xor dx,dx
ScanLoop:
mov si, EntryToFind
mov dl,byte ptr es:[bx] ; directory record length -> DL
cmp dl,0
jz Skip00 ; if the "record length" assume it is "system use" and skip it
mov ax,bx
add ax,021h ; file identifier is at offset 21h in directory record
mov di,ax ; ES:DI now points to file identifier
push cx
xor cx,cx
mov cl,EntryLen ; compare bytes
repe cmpsb
pop cx
jz ScanEnd ; do we have a match?
CheckCountUnderFlow:
; If CX is about to underflow or be 0 we need to reset CX, ES and BX if ExtentLen1 is non-0
cmp dx,cx
jae ResetCount0
sub cx,dx ; update CX to contain number of bytes left in directory
cmp ScanIncCount, 1
je ScanAdd1ToCount
AdjustScanPtr: ; Adjust ES:BX to point to next record
add dx,bx
mov bx,dx
and bx,0fh
push cx
mov cl,4
shr dx,cl
pop cx
mov ax,es
add ax,dx
mov es,ax
jmp ScanLoop
Skip00:
mov dx,1 ; Skip past this byte
jmp CheckCountUnderFlow
ScanAdd1ToCount:
inc cx
mov ScanIncCount,0
jmp AdjustScanPtr
S0:
mov ScanIncCount,1 ; We'll need to increment Count next time we get a chance
jmp SetNewCount
ResetCount0:
cmp ExtentLen1,0 ; Do we still have at least 64K bytes left to scan?
je BootErr$bnf ; We overran the end of the directory - corrupt/invalid directory
sub ExtentLen1,1
add bx,dx ; Adjust ES:BX to point to next record - we cross seg boundary here
push bx
push cx
mov cl,4
shr bx,cl
pop cx
mov ax,es
add ax,bx
mov es,ax
pop bx
and bx,0fh
sub dx,cx ; Get overflow amount
je S0 ; If we ended right on the boundary we need to make special adjustments
dec dx
SetNewCount:
mov ax,0ffffh
sub ax,dx ; and subtract it from 10000h
mov cx,ax ; - this is the new count
jmp ScanLoop
ScanEnd:
cmp IsDir,1
je CheckDir
test byte ptr es:[bx][25],2 ; Is this a file?
jnz CheckCountUnderFlow ; No - go to next record
jmp CheckLen
CheckDir:
test byte ptr es:[bx][25],2 ; Is this a directory?
jz CheckCountUnderFlow ; No - go to next record
CheckLen:
mov al,EntryLen
cmp byte ptr es:[bx][32],al ; Is the identifier length correct?
jnz CheckCountUnderFlow ; No - go to next record
ret
ScanForEntry endp
;
; BootErr - print error message and hang the system.
;
BootErr proc
BootErr$bnf:
MOV SI,OFFSET MSG_NO_NTLDR
jmp short BootErr2
BootErr$mof:
MOV SI,OFFSET MSG_MEM_OVERFLOW
jmp short BootErr2
BootErr2:
call BootErrPrint
MOV SI,OFFSET MSG_REBOOT_ERROR
call BootErrPrint
sti
jmp $ ;Wait forever
BootErrPrint:
LODSB ; Get next character
or al,al
jz BEdone
MOV AH,14 ; Write teletype
MOV BX,7 ; Attribute
INT 10H ; Print it
jmp BootErrPrint
BEdone:
ret
BootErr endp
;
; ExtRead - Do an INT 13h extended read
; NOTE: I force the offset of the Transfer buffer address to be 0
; I force the high 2 words of the Starting absolute block number to be 0
; - This allows for a max 4 GB medium - a safe assumption for now
;
; Entry:
; Arg1 - word 0 (low word) of Number of 2048-byte blocks to transfer
; Arg2 - word 1 (high word) of Number of 2048-byte blocks to transfer
; Arg3 - segment of Transfer buffer address
; Arg4 - word 0 (low word) of Starting absolute block number
; Arg5 - word 1 of Starting absolute block number
;
; Exit
; The following are modified:
; Count0
; Count1
; Dest
; Source0
; Source1
; PartialRead
; NumBlocks
; Disk Address Packet [DiskAddPack]
;
ExtRead proc near
push bp ; set up stack frame so we can get args
mov bp,sp
push bx ; Save registers used during this routine
push si
push dx
push ax
mov bx,offset DiskAddPack ; Use BX as base to index into Disk Address Packet
; Set up constant fields
mov [bx][0],byte ptr 010h ; Offset 0: Packet size = 16 bytes
mov [bx][1],byte ptr 0h ; Offset 1: Reserved (must be 0)
mov [bx][3],byte ptr 0h ; Offset 3: Reserved (must be 0)
mov [bx][4],word ptr 0h ; Offset 4: Offset of Transfer buffer address (force 0)
mov [bx][12],word ptr 0h ; Offset 12: Word 2 of Starting absolute block number (force 0)
mov [bx][14],word ptr 0h ; Offset 14: Word 3 (high word) of Starting absolute block number (force 0)
;
; Initialize loop variables
;
mov ax,[bp][12] ; set COUNT to number of blocks to transfer
mov Count0,ax
mov ax,[bp][10]
mov Count1,ax
mov ax,[bp][8] ; set DEST to destination segment
mov Dest,ax
mov ax,[bp][6] ; set SOURCE to source lbn
mov Source0,ax
mov ax,[bp][4]
mov Source1,ax
ExtReadLoop:
;
; First check if COUNT <= 32
;
cmp Count1,word ptr 0h ; Is upper word 0?
jne SetupPartialRead ; No - we're trying to read at least 64K blocks (128 MB)
cmp Count0,word ptr 20h ; Is lower word greater than 32?
jg SetupPartialRead ; Yes - only read in 32-block increments
mov PartialRead,0 ; Clear flag to indicate we are doing a full read
mov ax,Count0 ; NUMBLOCKS = COUNT
mov NumBlocks,al ; Since Count0 < 32 we're OK just using low byte
jmp DoExtRead ; Do read
SetupPartialRead:
;
; Since COUNT > 32,
; Set flag indicating we are only doing a partial read
;
mov PartialRead,1
mov NumBlocks,20h ; NUMBYTES = 32
DoExtRead:
;
; Perform Extended Read
;
mov al,NumBlocks ; Offset 2: Number of 2048-byte blocks to transfer
mov [bx][2],al
mov ax,Dest ; Offset 6: Segment of Transfer buffer address
mov [bx][6],ax
mov ax,Source0 ; Offset 8: Word 0 (low word) of Starting absolute block number
mov [bx][8],ax
mov ax,Source1 ; Offset 10: Word 1 of Starting absolute block number
mov [bx][10],ax
mov si,offset DiskAddPack ; Disk Address Packet in DS:SI
mov ah,042h ; Function = Extended Read
mov dl,DriveNum ; CD-ROM drive number
int 13h
;
; Determine if we are done reading
;
cmp PartialRead,1 ; Did we just do a partial read?
jne ExtReadDone ; No - we're done
ReadjustValues:
;
; We're not done reading yet, so
; COUNT = COUNT - 32
;
sub Count0,020h ; Subtract low-order words
sbb Count1,0h ; Subtract high-order words
;
; Just read 32 blocks and have more to read
; Increment DEST to next 64K segment (this equates to adding 1000h to the segment)
;
add Dest,1000h
jc BootErr$mof ; Error if we overflowed
;
; SOURCE = SOURCE + 32 blocks
;
add Source0,word ptr 020h ; Add low order words
adc Source1,word ptr 0h ; Add high order words
; NOTE - I don't account for overflow - probably OK now since we already account for 4 GB medium
;
; jump back to top of loop to do another read
;
jmp ExtReadLoop
ExtReadDone:
pop ax ; Restore registers used during this routine
pop dx
pop si
pop bx
mov sp,bp ; restore BP and SP
pop bp
ret
ExtRead endp
;
; ReadExtent - Read in an extent
;
; Arg1 - segment to transfer extent to
;
; Entry:
; ExtentLen0 = word 0 (low word) of extent length in bytes
; ExtentLen1 = word 1 (high word) of extent length in bytes
; ExtentLoc0 = word 0 (low word) of starting absolute block number of extent
; ExtentLoc1 = word 1 of starting absolute block number of extent
;
; Exit:
; ExtRead exit mods
;
ReadExtent proc near
push bp ; set up stack frame so we can get args
mov bp,sp
push cx ; Save registers used during this routine
push bx
push ax
mov cl,11 ; Convert length in bytes to 2048-byte blocks
mov bx,ExtentLen1 ; Directory length = BX:AX
mov ax,ExtentLen0
.386
shrd ax,bx,cl ; Shift AX, filling with BX
.8086
shr bx,cl ; BX:AX = number of blocks (rounded down)
test ExtentLen0,07ffh ; If any of the low-order 11 bits are set we need to round up
jz ReadExtentNoRoundUp
add ax,1 ; We need to round up by incrementing AX, and
adc bx,0 ; adding the carry to BX
ReadExtentNoRoundUp:
push ax ; Word 0 (low word) of Transfer size = AX
push bx ; Word 1 (high word) of Transfer size = BX
.286
push [bp][4] ; Segment used to transfer extent
.8086
push ExtentLoc0 ; Word 0 (low word) of Starting absolute block number
push ExtentLoc1 ; Word 1 of Starting absolute block number
call ExtRead
add sp,10 ; Clean 5 arguments off the stack
pop ax ; Restore registers used during this routine
pop bx
pop cx
mov sp,bp ; restore BP and SP
pop bp
ret
ReadExtent endp
;
; GetExtentInfo - Get extent location
;
; Entry:
; ES:BX points to record
; Exit:
; Location -> ExtentLoc1 and ExtentLoc0
; Length -> ExtentLen1 and ExtentLen0
;
GetExtentInfo proc near
push ax ; Save registers used during this routine
mov ax,es:[bx][2] ; 32-bit LBN of extent
mov ExtentLoc0,ax ; store low word
mov ax,es:[bx][4]
mov ExtentLoc1,ax ; store high word
mov ax,es:[bx][10] ; 32-bit file length in bytes
mov ExtentLen0,ax ; store low word
mov ax,es:[bx][12]
mov ExtentLen1,ax ; store high word
pop ax ; Restore registers used during this routine
ret
GetExtentInfo endp
include etfsboot.inc ; message text
DiskAddPack db 16 dup (?) ; Disk Address Packet
PartialRead db 0 ; Boolean indicating whether or not we are doing a partial read
LOADERNAME db "SETUPLDR.BIN"
I386DIRNAME db "I386"
DriveNum db (?) ; Drive number used for INT 13h extended reads
ExtentLoc0 dw (?) ; Loader LBN - low word
ExtentLoc1 dw (?) ; Loader LBN - high word
ExtentLen0 dw (?) ; Loader Length - low word
ExtentLen1 dw (?) ; Loader Length - high word
Count0 dw (?) ; Read Count - low word
Count1 dw (?) ; Read Count - high word
Dest dw (?) ; Read Destination segment
Source0 dw (?) ; Read Source - word 0 (low word)
Source1 dw (?) ; Read Source - word 1
NumBlocks db (?) ; Number of blocks to Read
EntryToFind dw (?) ; Offset of string trying to match in ScanForEntry
EntryLen db (?) ; Length in bytes of entry to match in ScanForEntry
IsDir db (?) ; Boolean indicating whether or not entry to match in ScanForEntry is a directory
ScanIncCount db 0 ; Boolean indicating if we need to add 1 to Count after adjustment in ScanForEntry
.errnz ($-ETFSBOOT) GT 2046 ; FATAL PROBLEM: boot sector is too large
org 2046
db 55h,0aah
BootSectorEnd label dword
BootCode ends
END ETFSBOOT