windows-nt/Source/XPSP1/NT/base/boot/bootcode/mbr/i386/x86mboot.asm
2020-09-26 16:20:57 +08:00

433 lines
15 KiB
NASM

page ,132
;
; Microsoft Confidential
; Copyright (C) Microsoft Corporation 1983-1997
; All Rights Reserved.
;
;
; This is the standard boot record that will be shipped on all hard disks.
; It contains:
;
; 1. Code to load (and give control to) the active boot record for 1 of 4
; possible operating systems.
;
; 2. A partition table at the end of the boot record, followed by the
; required signature.
;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
VOLBOOT_ORG EQU 7c00h
SIZE_SECTOR EQU 512
;
; Partition table entry structure and other related stuff
;
Part_Active EQU 0
Part_StartHead EQU 1
Part_StartSector EQU 2
Part_StartCylinder EQU 3
Part_Type EQU 4
Part_EndHead EQU 5
Part_EndSector EQU 6
Part_EndCylinder EQU 7
Part_AbsoluteSector EQU 8
Part_AbsoluteSectorH EQU 10
Part_SectorCount EQU 12
Part_SectorCountH EQU 14
SIZE_PART_TAB_ENT EQU 16
NUM_PART_TAB_ENTS EQU 4
PART_TAB_OFF EQU (SIZE_SECTOR - 2 - (SIZE_PART_TAB_ENT * NUM_PART_TAB_ENTS))
MBR_NT_OFFSET EQU (PART_TAB_OFF - 6)
MBR_MSG_TABLE_OFFSET EQU (PART_TAB_OFF - 9)
;
; Space we use for temp storage, beyond the end of
; the active partition table entry, and thus safe.
;
UsingBackup EQU SIZE_PART_TAB_ENT
;
; Partition types
;
PART_IFS EQU 07h
PART_FAT32 EQU 0bh
PART_FAT32_XINT13 EQU 0ch
PART_XINT13 EQU 0eh
;
; Filesystem and pbr stuff
;
BOOTSECTRAILSIGH EQU 0aa55h
FAT32_BACKUP EQU 6
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
RELOCATION_ORG EQU 600h
READ_RETRY_CNT EQU 5
_data segment public
assume cs:_data,ds:nothing,es:nothing,ss:nothing
org 100h
start100 label near
org RELOCATION_ORG
; Move ourselves so that we can load the partition boot record from
; the active partition at 0:VOLBOOT_ORG without trashing our own code.
;
; WARNING: We are not executing at 0:RELOCATION_ORG until the far
; immediate jump, below. This basically means beware of OFFSETs of
; any labels until we get there. Until then, we're still executing at
; 0:VOLBOOT_ORG.
reloc_delta = 1Bh
start:
xor ax,ax
mov ss,ax
mov sp,VOLBOOT_ORG ; new stack at 0:7c00
sti
push ax
pop es
assume es:_data
push ax
pop ds
assume ds:_data
cld
mov si,VOLBOOT_ORG + reloc_delta
mov di,RELOCATION_ORG + reloc_delta
push ax
push di
mov cx,SIZE_SECTOR - reloc_delta
rep movsb ; relocate to 0:0600
retf
; We are now RELOCATED and are now executing at 0:RELOCATION_ORG.
;
; Find the active partition. Once we find it, we also make sure that the
; remaining partitions are INACTIVE, too.
.errnz reloc_delta NE $-start ; make sure reloc_delta is correct
mov bp,offset tab ; partition table
mov cl,NUM_PART_TAB_ENTS ; number of table entries (CH==0)
active_loop:
cmp [bp].Part_Active,ch ; is this the active entry?
jl check_inactive ; yes
jne display_bad ; no, it must be "00" or "8x"
add bp,SIZE_PART_TAB_ENT ; yes, go to next entry
loop active_loop
int 18h ; no active entries - go to ROM BASIC
; Now make sure the remaining partitions are INACTIVE.
check_inactive:
mov si,bp ; si = active entry
inactive_loop:
add si,SIZE_PART_TAB_ENT ; point si to next entry
dec cx ; # entries left
jz StartLoad ; all entries look ok, start load
cmp [si],ch ; all remaining entries should be zero
je inactive_loop ; this one is ok
display_bad:
mov al,byte ptr [m1] ; partition table is bad
display_msg:
;
; Al is the offset-256 of the message text. Adjust and
; stick in si so lodsb below will work.
;
.ERRNZ RELOCATION_ORG MOD 256
mov ah,(RELOCATION_ORG / 256) + 1
mov si,ax
display_msg1:
lodsb ; get a message character
@@: cmp al,0 ; end of string?
je @b ; yes, loop infinitely
mov bx,7
mov ah,14
int 10h ; and display it
jmp display_msg1 ; do the entire message
;
; Now attempt to read the sector.
;
; We store a data byte indicating whether this is the backup
; sector, at the byte just beyond the end of the partition table entry.
; We know there's nothing there we care about any more, so this
; is harmless.
;
; BP is the address of the active partition entry.
; AX and CX are currently zero.
;
StartLoad:
mov byte ptr [bp].UsingBackup,cl ; not backup sector
call ReadSector
jnc CheckPbr
trybackup:
inc byte ptr [bp].UsingBackup
;
; Switch to backup sector for NTFS and FAT32.
;
if 0
;
; (tedm) for NTFS this code is actually worthless since other code,
; such as ntldr and the filesystem itself won't all do the right thing.
; For example the filesystem won't recognize the volume if sector 0
; can be read but is bad.
;
cmp byte ptr [bp].Part_Type,PART_IFS
jne tryfat32
;
; We assume here that type 7 is NTFS.
; Back up to end of partition by using the end CHS and subtracting 1
; from the sector #. There is no check for the case where we underflow
; and wrap a head -- it is assumed that partitions are at least head-aligned.
; There is also no check for the case where the start sector and sector count
; are both maxdword and so adding them overflows.
;
mov al,[bp].Part_EndHead ; make start head same as end head
mov [bp].Part_StartHead,al
mov ax,[bp].Part_EndSector ; ax = end sector and cylinder
dec al ; start sector = end sector minus 1
mov [bp].Part_StartSector,ax
mov ax,[bp].Part_SectorCount
mov dx,[bp].Part_SectorCountH
add [bp].Part_AbsoluteSector,ax
adc [bp].Part_AbsoluteSectorH,dx
sub word ptr [bp].Part_AbsoluteSector,1
sbb word ptr [bp].Part_AbsoluteSectorH,0
jmp short RestartLoad
endif
tryfat32:
cmp byte ptr [bp].Part_Type,PART_FAT32
je fat32backup
cmp byte ptr [bp].Part_Type,PART_FAT32_XINT13
je fat32backup
mov al,byte ptr [m2] ; unknown fs, so no backup sector
jne display_msg
fat32backup:
;
; There is no check for the case where adding to the sector value
; in the start CHS overflows and wraps to the next head. It is assumed
; that partitions are at least head-aligned and that hard drives have
; at least FAT32_BACKUP+1 sectors per track.
;
add byte ptr [bp].Part_StartSector,FAT32_BACKUP
add word ptr [bp].Part_AbsoluteSector,FAT32_BACKUP
adc word ptr [bp].Part_AbsoluteSectorH,0
RestartLoad:
call ReadSector
jnc CheckPbr
mov al,byte ptr [m2] ; can't load, we're done.
jmp short display_msg
CheckPbr:
cmp word ptr ds:[VOLBOOT_ORG + SIZE_SECTOR - 2],BOOTSECTRAILSIGH
je done
;
; Not a valid filesystem boot sector. Switch to backup if this
; isn't already the backup.
;
cmp byte ptr [bp].UsingBackup,0
je trybackup
mov al,byte ptr [m3]
jmp short display_msg
;
; Jump to PBR. We pass a pointer to the table entry that booted us
; in bp. If we used the backup boot sector, then that table entry
; will have been altered, but neither NTFS not FAT32 use the pointer
; in BP, and no other fs type will have been loaded via the backup
; boot sector, so this isn't an issue.
;
done:
mov di,sp ; DI -> start of PBR
push ds ; prepare for RETF, which is
push di ; smaller than JMP 0:VOLBOOT_ORG
mov si,bp ; pass boot partition table entry address
retf ; start executing PBR
ReadSector proc near
mov di,5 ; retry count
;
; Calculate the maximum sector # that can be addressed via
; conventional int13. Note that the max # of heads is 256
; and the maximum # of sectors per track is 63. Thus the maximum
; # of sectors per cylinder is something less than a 16-bit quantity.
;
mov dl,[bp].Part_Active ;
mov ah,8 ; get disk params
int 13h
jc nonxint13 ; strange case, fall back to std int13
mov al,cl
and al,3fh ; al = # of sectors per track
cbw ; ax = # of sectors per track (ah=0)
mov bl,dh ; bl = max head # (0-based)
mov bh,ah ; bh = 0
inc bx ; bx = # of heads
mul bx ; ax = sectors per cylinder, dx = 0
mov dx,cx ; dx = cylinder/sector in int13 format
xchg dl,dh ; dl = low 8 bits of cylinder
mov cl,6
shr dh,cl ; dx = max cylinder # (0-based)
inc dx ; dx = # cylinders
mul dx ; dx:ax = # sectors visible via int13
;
; If the sector # we're reading is less than the than number of
; addressable sectors, use conventional int 13
;
cmp [bp].Part_AbsoluteSectorH,dx
ja xint13
jb nonxint13
cmp [bp].Part_AbsoluteSector,ax
jae xint13
nonxint13:
mov ax,201h
mov bx,VOLBOOT_ORG ; es:bx = read address (0:7c00)
mov cx,[bp].Part_StartSector
mov dx,[bp].Part_Active
int 13h
jnc endread
dec di ; retry? (does not affect carry)
jz endread ; carry set for return
xor ah,ah ; ah = 0 (reset disk system)
mov dl,[bp].Part_Active ; dl = int13 unit #
int 13h
jmp short nonxint13
xint13:
;
; We want to avoid calling xint13 unless we know it's available
; since we don't trust all BIOSes to not hang if we attempt an xint13 read.
; If xint13 isn't supported we won't be able to boot, but at least
; we'll give an error message instead of just a black screen.
;
mov dl,[bp].Part_Active ; unit #
.286
pusha
mov bx,055AAh ; signature
mov ah,41h ; perform X13 interrogation
int 13h ;
jc endread1 ; call failed
cmp bx,0AA55h ; did our signature get flipped?
jne endread1 ; no
test cl,1 ; is there X13 support?
jz endread1 ; no
popa
doxint13:
pusha ; save regs
push 0 ; push dword of 0
push 0 ; (high 32 bits of sector #)
push [bp].Part_AbsoluteSectorH
push [bp].Part_AbsoluteSector ; low 32 bits of sector #
push 0
push VOLBOOT_ORG ; transfer address
push 1 ; 1 sector
push 16 ; packet size and reserved byte
mov ah,42h ; extended read
mov si,sp ; ds:si points to parameter packet
int 13h ; dl already set from above
popa ; pop param packet off stack
popa ; get real registers back
jnc endread
dec di ; retry? (does not affect carry)
jz endread ; carry set for return
xor ah,ah ; ah = 0 (reset disk system)
mov dl,[bp].Part_Active ; dl = int13 unit #
int 13h
jmp short doxint13
endread1:
popa
stc ; this is the error case
.8086
endread:
ret
ReadSector endp
;
; Message table.
;
; We put English messages here as a placeholder only, so that in case
; anyone uses bootmbr.h without patching new messages in, things will
; still be correct (in English, but at least functional).
;
_m1: db "Invalid partition table",0
_m2: db "Error loading operating system",0
_m3: db "Missing operating system",0
;
; 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 MBR_MSG_TABLE_OFFSET
org RELOCATION_ORG + MBR_MSG_TABLE_OFFSET
m1: db (OFFSET _m1 - RELOCATION_ORG) - 256
m2: db (OFFSET _m2 - RELOCATION_ORG) - 256
m3: db (OFFSET _m3 - RELOCATION_ORG) - 256
.errnz ($ - start) NE MBR_NT_OFFSET
dd 0 ; NT disk administrator signature
dw 0
.errnz ($ - start) GT PART_TAB_OFF
org RELOCATION_ORG + PART_TAB_OFF
tab: ;partition table
dw 0,0 ;partition 1 begin
dw 0,0 ;partition 1 end
dw 0,0 ;partition 1 relative sector (low, high parts)
dw 0,0 ;partition 1 # of sectors (low, high parts)
dw 0,0 ;partition 2 begin
dw 0,0 ;partition 2 end
dw 0,0 ;partition 2 relative sector
dw 0,0 ;partition 2 # of sectors
dw 0,0 ;partition 3 begin
dw 0,0 ;partition 3 end
dw 0,0 ;partition 3 relative sector
dw 0,0 ;partition 3 # of sectors
dw 0,0 ;partition 4 begin
dw 0,0 ;partition 4 end
dw 0,0 ;partition 4 relative sector
dw 0,0 ;partition 4 # of sectors
.errnz ($ - tab) NE (SIZE_PART_TAB_ENT * NUM_PART_TAB_ENTS)
.errnz ($ - start) NE (SIZE_SECTOR - 2)
signa dw BOOTSECTRAILSIGH ;signature
.errnz ($ - start) NE SIZE_SECTOR
_data ends
end start100