windows-nt/Source/XPSP1/NT/base/boot/bootcode/fat/i386/fatboot.asm

580 lines
21 KiB
NASM
Raw Normal View History

2020-09-26 03:20:57 -05:00
Page ,132
TITLE BOOT SECTOR 1 OF TRACK 0 - BOOT LOADER
;/*
; * Microsoft Confidential
; * Copyright (C) Microsoft Corporation 1991
; * All Rights Reserved.
; */
; Rev 1.0 ChrisP, AaronR and others. 2.0 format boot
;
; Rev 3.0 MarkZ PC/AT enhancements
; 2.50 in label
; Rev 3.1 MarkZ 3.1 in label due to vagaries of SYSing to IBM drive D's
; This resulted in the BPB being off by 1. So we now trust
; 2.0 and 3.1 boot sectors and disbelieve 3.0.
;
; Rev 3.2 LeeAc Modify layout of extended BPB for >32M support
; Move PHYDRV to 3rd byte from end of sector
; so that it won't have to be moved again
; FORMAT and SYS count on PHYDRV being in a known location
;
; Rev 3.3 D.C.L. Changed Sec 9 EOT field from 15 to 18. May 29, 1986.
;
; Rev 3.31 MarkT The COUNT value has a bogus check (JBE????) to determine
; if we've loaded in all the sectors of IBMBIO. This will
; cause too big of a load if the sectors per track is high
; enough, causing either a stack overflow or the boot code
; to be overwritten.
;
; Rev 4.00 J. K. For DOS 4.00 Modified to handle the extended BPB, and
; 32 bit sector number calculation to enable the primary
; partition be started beyond 32 MB boundary.
;
; Rev 7.0 JeffPar Loads WINBOOT.SYS from anywhere in the root of the boot
; drive. WINBOOT.SYS must be an EXE with exactly 1 sector
; of header information, followed by a series of binary
; images imbedded in the EXE, as follows:
;
; 1. WINLOAD module (ORGed at 200h, loaded at 70:200h)
; 2. IO.SYS module
; 3. MSDOS.SYS module
;
; The WINLOAD module should fit within WINLOAD_SIZE sectors,
; which includes 1 sector for the EXE header, so the code
; and data for the WINLOAD bootstrap should fit in 3 sectors.
;
; Rev 7.1 ScottQ removed Winboot.sys stuff, add support for extended int 13
; MSliger bootable partitions implemented by scanning the MBR.
;
; Rev 8.0 ScottQ re-implement winboot.sys dual-boot as JO.SYS dual boot
; MSliger
;
; The ROM in the IBM PC starts the boot process by performing a hardware
; initialization and a verification of all external devices. If all goes
; well, it will then load from the boot drive the sector from track 0, head 0,
; sector 1. This sector is placed at physical address 07C00h. The initial
; registers are presumably set up as follows: CS=DS=ES=SS=0, IP=7C00h, and
; SP=0400h. But all we rely on is the BIOS being loaded at linear 07C00h.
;
; If IO.SYS is not found, an error message is displayed and the user is
; prompted to insert another disk. If there is a disk error during the
; process, a message is displayed and things are halted.
;
; At the beginning of the boot sector, there is a table which describes the
; MSDOS structure of the media. This is equivalent to the BPB with some
; additional information describing the physical layout of the driver (heads,
; tracks, sectors)
;
;==============================================================================
;REALLY OLD REVISION HISTORY which has no meaning but hey its nostalgic
;AN000 - New for DOS Version 4.00 - J.K.
;AC000 - Changed for DOS Version 4.00 - J.K.
;AN00x - PTM number for DOS Version 4.00 - J.K.
;==============================================================================
;AN001; d52 Make the fixed positioned variable "CURHD" to be local. 7/6/87 J.K.
;AN002; d48 Change head settle at boot time. 7/7/87 J.K.
;AN003; P1820 New message SKL file 10/20/87 J.K.
;AN004; D304 New structrue of Boot record for OS2. 11/09/87 J.K.
;AN005; Changed version to 5.0 03/08/90 E.A.
;AN006; Changed to remove MSLOAD in first cluster restriction 04/23/90 J.H.
;==============================================================================
.xlist
include bpb.inc
include bootsec.inc
include dirent.inc
;include version.inc
.list
; ==========================================================================
ORIGIN EQU 7C00H ; Origin of bootstrap LOADER
BIO_SEG EQU 2000H ; Destination segment of ntldr
BIO_OFFSET EQU 0 ; Offset of ntldr
SECTOR_SIZE EQU 512 ; Sector size in bytes
DIR_ENTRY_SIZE EQU SIZE DIR_ENTRY ; Size of directory entry in bytes
DSK_PARMS EQU 1EH*4 ; POINTER TO DRIVE PARMS
SEC9 EQU 522h ; ADDRESS OF NEW DRIVE PARM TABLE
ROM_DISKRD EQU 2
ROM_DISKRDX EQU 42h
; ==========================================================================
;
; This little set of directives establishes the address
; where we'll jump to once the first sector of ntldr has been
; loaded. The first 3 bytes of that sector are used for non-FAT
; filesystems, so we must skip over them.
;
SEGBIOS SEGMENT AT BIO_SEG
ORG 3
NTLOAD LABEL BYTE
SEGBIOS ENDS
; ==========================================================================
; Data storage between temp. stack and start of boot sector
pReadClustersO EQU -16
pReadClustersS EQU -14
pReadSectorsO EQU -12
pReadSectorsS EQU -10
SectorBase EQU -8
DataSecL EQU -4 ; absolute sector # of first sector in data area
DataSecH EQU -2
; ==========================================================================
CODE SEGMENT
ASSUME CS:CODE,DS:NOTHING,ES:NOTHING,SS:NOTHING
ORG ORIGIN
Public $START
$START Label byte
jmp short Main
PartitionType: ;the in-memory copy of this will leave
nop ;the partition type in this NOP's place
;so IO.SYS can tell if it needs x13
PartitionTypeOffset = (offset PartitionType - offset $START)
; ==========================================================================
; Start of BPB area of the boot record
OsName DB "MSWIN"
OsVersion DB "4.1" ; Windows version number
CoreBpb label byte
BytesPerSector DW SECTOR_SIZE ; Size of a physical sector
SecsPerClust DB 8 ; Sectors per allocation unit
ReservedSecs DW 1 ; Number of reserved sectors
NumFats DB 2 ; Number of fats
NumDirEntries DW 512 ; Number of direc entries
TotalSectors DW 4*17*305-1 ; Number of sectors - number of hidden
; sectors (0 when 32 bit sector number)
MediaByte DB 0F8H ; MediaByte byte
NumFatSecs DW 8 ; Number of fat sectors
SecPerTrack DW 17 ; Sectors per track
NumHeads DW 4 ; Number of drive heads
HiddenSecs DD 1 ; Number of hidden sectors
BigTotalSecs DD 0 ; 32 bit version of number of sectors
.errnz ($-BytesPerSector) NE SIZE BPB
BootDrv DB 80h
CurrentHead DB 0h ; Current Head
ExtBootSig DB 41
SerialNum DD 0
VolumeLabel DB 'NO NAME '
FatId DB 'FAT12 '
.errnz ($-$START) NE SIZE BOOTSEC
; =========================================================================
;
; First thing is to reset the stack to a better and more known
; place. The ROM may change, but we'd like to get the stack
; in the correct place.
;
Main:
xor CX,CX
mov SS,CX ;Work in stack just below this routine
mov SP,ORIGIN+pReadClustersO
ASSUME SS:CODE
mov ds,cx
assume DS:CODE
;
; Set up es for sector reads
;
mov ax,BIO_SEG
mov es,ax
ASSUME ES:NOTHING
cld
mov BP,ORIGIN
; The system is now prepared for us to begin reading.
; First we read the master boot record (MBR) if this is a
; harddisk so we can get our partion type. Types E and C
; need extended int13 calls.
cmp [BP].bsDriveNumber,cl ;assert cl == 0 from above
jnl failed ;dont check for MBR on floppies!
mov ax,cx ;read sector zero
cwd
call DoReadOne ;note! assume cx==0 for DoReadOne
jc failed
sub bx,(42h - 12 +4) ;bx comes back point it end of sector,
;we want hidden sector field of first
;partition entry
.386
mov eax,dword ptr HiddenSecs;get the hidden sectors for us
scan:
cmp eax,dword ptr es:[bx] ;is it the same as this partition entry?
.286
mov dl,byte ptr es:[bx-4] ;put the entry's type in dl
jne short dontgotit ;its our partition, go write it down
or dl,2 ;turn C's into E's
mov [bp+2],dl ;put our partition type at byte 2's nop
dontgotit:
add bl,10h ; 8CA - 8DA - 8EA - 8FA - 90A : carry
jnc short scan ; we scan 4 partition entries
failed:
;if we failed, leave nop alone; we
;will use old int13 and so will IO.SYS
xor cx,cx ;later code needs CX to still be zero
.286
; Next, determine logical sector numbers of the start of the
; directory and the start of the data area.
;
DirRead:
mov AL,[BP].bsBPB.BPB_NumberOfFATs ; Determine sector dir starts on (NumFats)
cbw ;
mul [BP].bsBPB.BPB_SectorsPerFAT ; DX:AX (NumFatSecs)
add AX,[BP].bsBPB.BPB_HiddenSectors ; (HiddenSecs)
adc DX,[BP].bsBPB.BPB_HiddenSectorsHigh
add AX,[BP].bsBPB.BPB_ReservedSectors;(ReservedSecs)
adc DX,CX
;
; DX:AX = NumFats * NumFatSecs + ReservedSecs + cSecHid
;
mov SI,[BP].bsBPB.BPB_RootEntries
pusha
mov [BP].DataSecL,AX
mov [BP].DataSecH,DX
mov AX,DIR_ENTRY_SIZE ; bytes per directory entry
mul SI ; convert to bytes in directory (NumDirEntries)
mov BX,[BP].bsBPB.BPB_BytesPerSector; add in sector size
add AX,BX
dec AX ; decrement so that we round up
div BX ; convert to sector number
add [BP].DataSecL,AX ; Start sector # of Data area
adc [BP].DataSecH,CX ;
popa
DirSector:
mov DI,BIO_OFFSET ; address in DI for comparisons
call DoReadOne ;NOTE assumes CX==0!
DiskErrorJump:
jc DiskError ; if errors try to recover
DirEntry:
cmp byte ptr es:[di],ch ; empty directory entry?
je MissingFile ; yes, that's the end
pusha
mov cl,11
mov si,offset BootFilename
repz cmpsb ; see if the same
popa
jz DoLoad ; if so, continue booting
dec SI ; decrement # root entries
jz MissingFile ; hmmm, file doesn't exist in root
next_entry:
add DI,DIR_ENTRY_SIZE
cmp DI,BX ; exhausted this root dir sector yet?
jb DirEntry ; no, check next entry
jmp DirSector ; yes, check next sector
MissingFile:
mov al,byte ptr [MSGOFF_NOSYS] ; point to no system file message
;
; There has been some recoverable error. Display a message
; and wait for a keystroke. Al is the offset - 256 of the
; message within the boot sector. So we first calculate
; the real segment-based offset to the message and stick it in si
; so lodsb will work later.
;
DisplayError:
.ERRNZ ORIGIN MOD 256
mov ah,(ORIGIN / 256) + 1
mov si,ax
DisplayError1:
lodsb ; get next character
cbw ; make 00->0000, FF->FFFF
inc ax ; end of sub-message? (0xFF?)
jz DisplayWait ; if so, get tail
dec ax ; end of message? (0x00?)
jz WaitForKey ; if so, get key
mov AH,14 ; write character & attribute
mov BX,7 ; attribute (white char on black)
int 10h ; print the character
jmp short DisplayError1
DisplayWait:
mov al,byte ptr [MSGOFF_COMMON]
jmp short DisplayError
DiskError:
mov al,byte ptr [MSGOFF_IOERROR]
jmp short DisplayError
WaitForKey:
;we know ax is zero, thats how we got here
int 16h ; get character from keyboard
int 19h ; Continue in loop till good disk
;
; We now load the first sector of ntldr.
;
; All we have to do is multiply the file's starting cluster
; (whose directory entry is at ES:DI-11) by sectors per cluster and
; add that to the disk's starting data sector.
;
; Note that after we're read the sector into 2000:0 the directory sector
; will get blown away. So we need to save the starting cluster number.
;
DoLoad:
mov dx,es:[di].dir_first ; dx = ntldr starting cluster
push dx ; save for later
mov al,1 ; 1 sector
mov bx,BIO_OFFSET ; into 2000:0
call NtldrClusterRead
jc DiskError
; =========================================================================
;
; NTLDR requires the following input conditions:
;
; BX == starting cluster number of NTLDR
; DL == int13 unit number of boot drive
; DS:SI -> BPB
; DS:DI -> arg structure
;
; =========================================================================
pop bx ; starting cluster number
mov dl,[bp].bsDriveNumber
mov si,OFFSET CoreBpb
mov di,sp ; we should be at TOS now
mov [bp].pReadClustersO,OFFSET NtldrClusterRead
mov [bp].pReadSectorsO,OFFSET NtldrSectorRead
mov cx,ds
mov [bp].pReadClustersS,cx
mov [bp].pReadSectorsS,cx
;
; We do something nasty. Ntldr is expecting the readcluster and readsector
; routines to do a far return. Since ntldr is now the only guy who will be
; reading via those routines, we patch the return instruction from a near
; return to a far return.
;
mov byte ptr DoReadExit,0cbh ; retf
jmp FAR PTR NTLOAD ; Crank up NTLDR
;
; Sector read routine required by ntldr
;
; Read al sectors into es:bx starting from sector SectorBase
; (SectorBase is logical sector # from start of partition)
;
NtldrSectorRead label near
.386
movzx cx,al
mov eax,[bp].SectorBase
add eax,dword ptr [bp].bsBPB.BPB_HiddenSectors
mov edx,eax
shr edx,16
.286
jmp short DoReadMore
;
; Cluster read routine required by ntldr
;
; Read al sectors into es:bx starting from cluster dx
;
NtldrClusterRead label near
.386
movzx cx,al ; cx = # of sectors to read
.286
dec dx
dec dx ; adjust for reserved clusters 0 and 1
mov al,[BP].bsBPB.BPB_SectorsPerCluster
xor ah,ah
mul dx ; DX:AX = starting sector number
add ax,[bp].DataSecL ; adjust for FATs, root dir, boot sec.
adc dx,[bp].DataSecH
DoPush:
jmp short DoReadMore
; =========================================================================
;
; Read disk sector(s).
;
; Inputs: DX:AX == logical sector #
; CL == # sectors (CH == 0)
; ES:BX == transfer address
;
; Outputs: DX:AX next logical sector #
; CX == 0 (assuming no errors)
; ES:BX -> byte after last byte of read
; Carry set if error, else clear if success
;
; Preserves: BP, SI, DI
;
; New Notes: This function will now use extended int 13 if
; necessary. The next note is correct for standard int 13
;
; Notes: Reads sectors one at a time in case they straddle a
; track boundary. Performs full 32-bit division on the
; first decomposition (of logical sector into track+sector)
; but not on the second (of track into cylinder+head),
; since (A) we don't have room for it, and (B) the results
; of that division must yield a quotient < 1024 anyway, because
; the CHS-style INT 13h interface can't deal with cylinders
; larger than that.
;
; =========================================================================
switchx13: ;setup for x13 read, and switch to old if not needed
push dx ;
push ax ; block number
push es
push bx ; transfer address
push 1 ; count of one, because we're looping
push 16 ; packet size
xchg AX,CX ; AX -> CX
mov ax,[bp].bsBPB.BPB_SectorsPerTrack ; get sectors/track
xchg ax,si ; save for divides
xchg AX,DX ; DX -> AX
xor DX,DX ; divide 0:AX
div si ; AX = high word of track
xchg AX,CX ; save AX in CX and restore old AX
div si ; CX:AX = track, DX = sector
inc DX ; sector is 1-based
xchg CX,DX ; CX = sector, DX = high word of track
div [BP].bsBPB.BPB_Heads ; AX = cylinder, DX = head
mov DH,DL ; head # < 255
mov CH,AL ; CH = cyl #
ror AH,2 ; move bits 8,9 of AX to bits 14,15
; (the rest has to be zero, since
; cyls cannot be more than 10 bits)
or CL,AH ; CL = sector # plus high bits of cyl #
mov AX,(ROM_DISKRD SHL 8)+1 ; disk read 1 sector
cmp byte ptr [bp].PartitionTypeOffset,0Eh ;set flag to be tested
jne short doio ;use extended calls if E (or C)
mov ah,ROM_DISKRDX ;x13, we're ready to rock
mov si,sp ; DS:SI -> X13 packet
doio:
mov dl,[bp].bsDriveNumber ; DL == drive #
int 13h
popa ; throw away packet on stack (8 words)
popa ; get real registers back
jc DoReadExit ; disk error
inc ax
jnz DoReadNext
inc dx
DoReadNext: ; Adjust buffer address
add BX,[BP].bsBPB.BPB_BytesPerSector
dec cx
jnz DoReadMore
clc
DoReadExit:
ret
DoReadOne:
inc cx ;assumes cx == 0! to set to 1 sector read
DoRead:
mov bx,BIO_OFFSET
DoReadMore:
pusha
.386
db 66h ;push a dword of 0 on the stack
push 0 ;hand coded for 386
.286
jmp switchx13
;
; This string can go anywhere. NT and NT Setup are not picky about it.
;
Public BootFilename
BootFilename db "NTLDR ";
;
; Message table.
;
; We put English messages here as a placeholder only, so that in case
; anyone uses bootfat.h without patching new messages in, things will
; still be correct (in English, but at least functional).
;
include msgstub.inc
;
; Now build a table with the low byte of the offset to each message.
; Code that patches the boot sector messages updates this table.
;
.errnz ($-$START) GT (SECTOR_SIZE-5)
ORG ORIGIN + SECTOR_SIZE - 5
MSGOFF_NOSYS:
db OFFSET (MSG_NOSYS - ORIGIN) - 256
MSGOFF_IOERROR:
db OFFSET (MSG_IOERROR - ORIGIN) - 256
MSGOFF_COMMON:
db OFFSET (MSG_COMMON - ORIGIN) - 256
.errnz ($-$START) NE (SECTOR_SIZE-2)
DB 55h,0AAh ; Boot sector signature
CODE ENDS
END