Source code of Windows XP (NT5)
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.
 
 
 
 
 
 

651 lines
16 KiB

;++
;
;Copyright (c) 1995 Microsoft Corporation
;
;Module Name:
;
; bioschk.asm
;
;Abstract:
;
; The code in this "image" is responsible for checking if is appropriate
; for us to start setupldr.bin. We consider this appropriate when we pass
; BIOS checkings. Setupldr.bin is binary appended at the end of this image.
;
;Author:
;
; Calin Negreanu (calinn) 16-Dec-1999
;
;Environment:
;
; Real mode
;
; Case 1:
; Complete image has been loaded at 2000:0000 by the boot code
; DL = INT 13h drive number we've booted from
;
; Case 2:
; First 512 bytes of this image has been loaded at 2000:000 by the boot code
; BX = Starting Cluster Number of this image
; DL = INT 13h drive number we've booted from
; DS:SI -> boot media's BPB
; DS:DI -> argument structure
;
;Revision History:
;
;--
page ,132
title boot - BIOS check
name bioschk
.8086
CODE SEGMENT
ASSUME CS:CODE,DS:CODE,SS:NOTHING,ES:NOTHING
ORG 0000H
_BiosChk label byte
BiosChkDestSeg EQU 1000h
SetupLdrDestSeg EQU 2000h
MaxCodeSize EQU 0800h ;number of paragraphs (32k)
MaxSetupLdrSize EQU 4000h ;number of paragraphs (256k)
StackSeg EQU 1000h ;stack goes from here
MAXREAD EQU 10000h
MAXSECTORS EQU MAXREAD/0200h
DoubleWord struc
lsw dw ?
msw dw ?
DoubleWord ends
SHARED struc
ReadClusters dd ? ; function pointer
ReadSectors dd ? ; function pointer
SectorBase dd ? ; starting sector for
; ReadSectors callback
SHARED ends
BPB struc
BytesPerSector dw ?
SectorsPerCluster db ?
ReservedSectors dw ?
Fats db ?
DirectoryEntries dw ?
Sectors dw ?
Media db ?
FatSectors dw ?
SectorsPerTrack dw ?
Heads dw ?
HiddenSectors dd ?
SectorsLong dd ?
BootDriveNumber db ?
BPB ends
JMPFAR MACRO DestOfs,DestSeg
db 0eah
dw OFFSET DestOfs
dw DestSeg
endm
START:
;
; The FAT boot sector only reads in the first 512 bytes of NTLDR. This is
; the module that contains those 512 bytes, so we are now responsible for
; loading the rest of the file. Other filesystems will load the whole file,
; so the default entrypoint branches around the FAT-specific code.
;
jmp RealStart
FatBegin:
.386
;
; If we're here, we've booted off a FAT system and we must load the rest
; of the binary image at 2000:0200 (right behind this sector). The boot
; sector passes us the following:
; BX = Starting Cluster Number of this image
; DL = INT 13h drive number we've booted from
; DS:SI -> boot media's BPB
; DS:DI -> argument structure
;
;
; Save away the boot drive and the starting cluster number
;
push dx
push bx
;
; Blast the FAT into memory at 6000:0000 - 8000:0000
;
.386
push 06000h
.8086
pop es
xor bx,bx ; (es:bx) = 6000:0000
mov cx,ds:[si].ReservedSectors
mov ds:[di].SectorBase.msw,0
mov ds:[di].SectorBase.lsw,cx ; set up Sector Base
mov ax,ds:[si].FatSectors ; (al) = # Sectors to read
cmp ax,080h
jbe FatLt64k
; The FAT is > 64k, so we read the first 64k chunk, then the rest.
; (A 16-bit FAT can't be bigger than 128k)
push cx
mov ax,080h ; (al) = # of sectors to read
call ds:[di].ReadSectors
pop cx ; (cx) = previous SectorBase
.386
push 07000h
.8086
pop es
xor bx,bx ; (es:bx) = 7000:0000
mov ax,ds:[si].FatSectors
sub ax,080h ; (ax) = # Sectors left to read
add cx,080h ; (cx) = SectorBase for next read
mov ds:[di].SectorBase.lsw,cx
adc ds:[di].SectorBase.msw,0 ; set up SectorBase
;
; (al) = # of sectors to read
;
FatLt64k:
call ds:[di].ReadSectors
;
; FAT is in memory, now we restore our starting cluster number
;
pop dx ; (dx) = starting cluster number
xor bx,bx
;
; set up FS and GS for reading the FAT
;
.386
mov ax,6000h
mov fs,ax
mov ax,7000h
mov gs,ax
.8086
;
; set up ES for reading in the rest of us
;
push cs
pop es
mov ah,MAXSECTORS ; (ah) = number of sectors we can read
FatLoop:
;
; (dx) = next cluster to load
;
push dx
mov al,ds:[si].SectorsPerCluster ; (al) = number of contiguous sectors
; found
sub ah,ds:[si].SectorsPerCluster ; can read before 64k
;
; Check to see if we've reached the end of the file
;
cmp dx,0ffffh
jne Fat10
;
; The entire file has been loaded. Throw away the saved next cluster,
; restore the boot drive, and let NTLDR do its thing.
;
pop dx
pop dx
jmp RealStart
Fat10:
mov cx,dx
;
; (dx) = (cx) = last contiguous cluster
; (al) = # of contiguous clusters found
;
call NextFatEntry
;
; (dx) = cluster following last contiguous cluster
;
; Check to see if the next cluster is contiguous. If not, go load the
; contiguous block we've found.
;
inc cx
cmp dx,cx
jne LncLoad
;
; Check to see if we've reached the 64k boundary. If so, go load the
; contiguous block so far. If not, increment the number of contiguous
; sectors and loop again.
;
cmp ah,0
jne Lnc20
mov ah,MAXSECTORS ; (ah) = number of sectors until
jmp short LncLoad
Lnc20:
add al,ds:[si].SectorsPerCluster
sub ah,ds:[si].SectorsPerCluster
jmp short Fat10
LncLoad:
;
; (TOS) = first cluster to load
; (dx) = first cluster of next group to load
; (al) = number of contiguous sectors
;
pop cx
push dx
mov dx,cx
mov cx,10 ; (cx) = retry count
;
; N.B.
; This assumes that we will never have more than 255 contiguous clusters.
; Since that would get broken up into chunks that don't cross the 64k
; boundary, this is ok.
;
; (dx) = first cluster to load
; (al) = number of contiguous sectors
; (TOS) = first cluster of next group to load
; (es:bx) = address where clusters should be loaded
;
FatRetry:
push bx
push ax
push dx
push cx
call [di].ReadClusters
jnc ReadOk
;
; error in the read, reset the drive and try again
;
mov ax,01h
mov al,ds:[si].BootDriveNumber
int 13h
xor ax,ax
mov al,ds:[si].BootDriveNumber
int 13h
;
; pause for a while
;
xor ax,ax
FatPause:
dec ax
jnz FatPause
pop cx
pop dx
pop ax
pop bx
dec cx
jnz FatRetry
;
; we have re-tried ten times, it still doesn't work, so punt.
;
push cs
pop ds
mov si,offset FAT_ERROR
FatErrPrint:
lodsb
or al,al
jz FatErrDone
mov ah,14 ; write teletype
mov bx,7 ; attribute
int 10h ; print it
jmp FatErrPrint
FatErrDone:
jmp $
; BUGBUG this should be replaced by a mechanism to get a pointer
; passed to us in the param block. since the boot sector msg itself
; is properly localized but this one isn't.
FAT_ERROR db 13,10,"Disk I/O error",0dh,0ah,0
ReadOk:
pop cx
pop dx
pop ax
pop bx
pop dx ; (dx) = first cluster of next group
; to load.
.386
;
; Convert # of sectors into # of bytes.
;
mov cl,al
xor ch,ch
shl cx,9
.8086
add bx,cx
jz FatLoopDone
jmp FatLoop
FatLoopDone:
;
; (bx) = 0
; This means we've just ended on a 64k boundary, so we have to
; increment ES to continue reading the file. We are guaranteed to
; always end on a 64k boundary and never cross it, because we
; will reduce the number of contiguous clusters to read
; to ensure that the last cluster read will end on the 64k boundary.
; Since we start reading at 0, and ClusterSize will always be a power
; of two, a cluster will never cross a 64k boundary.
;
mov ax,es
add ax,01000h
mov es,ax
mov ah,MAXSECTORS
jmp FatLoop
;++
;
; NextFatEntry - This procedure returns the next cluster in the FAT chain.
; It will deal with both 12-bit and 16-bit FATs. It assumes
; that the entire FAT has been loaded into memory.
;
; Arguments:
; (dx) = current cluster number
; (fs:0) = start of FAT in memory
; (gs:0) = start of second 64k of FAT in memory
;
; Returns:
; (dx) = next cluster number in FAT chain
; (dx) = 0ffffh if there are no more clusters in the chain
;
;--
NextFatEntry proc near
push bx
;
; Check to see if this is a 12-bit or 16-bit FAT. The biggest FAT we can
; have for a 12-bit FAT is 4080 clusters. This is 6120 bytes, or just under
; 12 sectors.
;
; A 16-bit FAT that's 12 sectors long would only hold 3072 clusters. Thus,
; we compare the number of FAT sectors to 12. If it's greater than 12, we
; have a 16-bit FAT. If it's less than or equal to 12, we have a 12-bit FAT.
;
call IsFat12
jnc Next16Fat
Next12Fat:
mov bx,dx ; (fs:bx) => temporary index
shr dx,1 ; (dx) = offset/2
; (CY) = 1 need to shift
pushf ; = 0 don't need to shift
add bx,dx ; (fs:bx) => next cluster number
.386
mov dx,fs:[bx] ; (dx) = next cluster number
.8086
popf
jc shift ; carry flag tells us whether to
and dx,0fffh ; mask
jmp short N12Tail
shift:
.386
shr dx,4 ; or shift
.8086
N12Tail:
;
; Check for end of file
;
cmp dx,0ff8h ; If we're at the end of the file,
jb NfeDone ; convert to canonical EOF.
mov dx,0ffffh
jmp short NfeDone
Next16Fat:
add dx,dx ; (dx) = offset
jc N16high
mov bx,dx ; (fs:bx) => next cluster number
.386
mov dx,fs:[bx] ; (dx) = next cluster number
.8086
jmp short N16Tail
N16high:
mov bx,dx
.386
mov dx,gs:[bx]
.8086
N16Tail:
cmp dx,0fff8h
jb NfeDone
mov dx,0ffffh ; If we're at the end of the file
; convert to canonical EOF.
NfeDone:
pop bx
ret
NextFatEntry endp
;++
;
; IsFat12 - This function determines whether the BPB describes a 12-bit
; or 16-bit FAT.
;
; Arguments - ds:si supplies pointer to BPB
;
; Returns
; CY set - 12-bit FAT
; CY clear - 16-bit FAT
;
;--
IsFat12 proc near
.386
push eax
push ebx
push ecx
push edx
movzx ecx, ds:[si].Sectors
or cx,cx
jnz if10
mov ecx, ds:[si].SectorsLong
if10:
;
; (ecx) = number of sectors
;
movzx ebx, byte ptr ds:[si].Fats
movzx eax, word ptr ds:[si].FatSectors
mul ebx
sub ecx,eax
;
; (ecx) = (#sectors)-(sectors in FATs)
;
movzx eax, word ptr ds:[si].DirectoryEntries
shl eax, 5
;
; (eax) = #bytes in root dir
;
mov edx,eax
and edx,0ffff0000h
div word ptr ds:[si].BytesPerSector
sub ecx,eax
;
; (ecx) = (#sectors) - (sectors in fat) - (sectors in root dir)
;
movzx eax, word ptr ds:[si].ReservedSectors
sub ecx, eax
mov eax, ecx
movzx ecx, byte ptr ds:[si].SectorsPerCluster
xor edx,edx
div ecx
cmp eax, 4087
jae if20
stc
jmp short if30
if20:
clc
if30:
pop edx
pop ecx
pop ebx
pop eax
ret
.8086
IsFat12 endp
Free EQU 512-($-Start)
if Free lt 0
%out FATAL PROBLEM: FAT-specific startup code is greater than
%out 512 bytes. Fix it!
.err
endif
RealStart:
;
; we are completely done with the boot sector, we can party on it's memory as we like.
; set up the stack
;
mov ax,StackSeg
mov ss,ax
xor sp,sp
mov ax,cs
mov ds,ax
mov es,ax
;
; save setupldr data
;
mov Preserve, dl
; move ourselves from 2000:0000 to 1000:0000, one paragraph at a time.
mov ax, BiosChkDestSeg
mov es, ax
mov dx, MaxCodeSize
cld
Again1:
xor di, di
xor si, si
mov cx, 10h
rep movsb
mov ax, ds
inc ax
mov ds, ax
mov ax, es
inc ax
mov es, ax
dec dx
jnz Again1
mov ax, BiosChkDestSeg
mov ds, ax
mov es, ax
JMPFAR Continue1, BiosChkDestSeg
Continue1:
; insert your BIOS check code here
; for now we will just display a message and wait for the user to press a key
mov si,OFFSET MsgPressKey
call PrintMsg
QueueEmpty:
mov ah, 01h
int 16h
jz QueueEmpty
; instead of a real BIOS check we will check if the user holds down CTRL.
; If yes, we will behave like BIOS check failed
; mov ah,02h
; int 16h
; and al,00000100b
; jz MoveSetupLdr
; at this point, the BIOS check failed. You can add whatever code you want here
; to give a message or to make the computer crash. If you don't do anything, the
; code will jump to 2000:0000 so an infinite loop is going to happen.
; jmp Continue2
MoveSetupLdr:
; move Setupldr code from 2000+MaxCodeSize:0000 to 2000:0000, one paragraph at a time.
push ds
push es
mov ax, SetupLdrDestSeg
mov es, ax
add ax, MaxCodeSize
mov ds, ax
mov dx, MaxSetupLdrSize
cld
Again2:
xor di, di
xor si, si
mov cx, 10h
rep movsb
mov ax, ds
inc ax
mov ds, ax
mov ax, es
inc ax
mov es, ax
dec dx
jnz Again2
pop es
pop ds
Continue2:
mov dl, Preserve
JMPFAR 0,SetupLdrDestSeg
;
;EXPECTS DS:SI - MESSAGE ADDR
;
PrintMsg proc near
push ax
push bx
PrintMsgLoop:
lodsb
cmp al,0
je PrintMsgEnd
mov ah,0eh
mov bx,0007h
int 10h
jmp PrintMsgLoop
PrintMsgEnd:
pop bx
pop ax
ret
PrintMsg endp
Preserve db ?
MsgPressKey db 0dh, 0ah, "Press any key to continue..."
db 0
.errnz ($-_BiosChk) GT (MaxCodeSize*16 - 2) ;FATAL: BiosChk code is too large
org MaxCodeSize*16 - 2
db 55h,0aah
CODE ENDS
END START