; - if not found, itarates over drives again and searches for active partition and␊ |
; loads from it␊ |
;␊ |
; dmazar: 19/7/2011␊ |
; Searching for bootable partition works in 3 passes now:␊ |
;␊ |
; - Pass1:␊ |
; - for the boot drive only:␊ |
; - searches MBR partition table for an active HSF+ bootable partition and boots it␊ |
; - if not found and disk is actually GPT, then searches for the first HFS+ bootable␊ |
; partition (or EFI with boot1f32) in the GPT array and boots it␊ |
; - if still not found, then continues with Pass2␊ |
;␊ |
; - Pass2:␊ |
; - iterates over all drives and for each drive:␊ |
; - searches MBR partition table for the first HSF+ bootable partition and boots it␊ |
; - if not found and disk is actually GPT, then searches for the first HFS+ bootable␊ |
; partition (or EFI with boot1f32) in the GPT array and boots it␊ |
; - if still not found, then continues with the next drive␊ |
; - if all drives are searched and nothing found, then continues with Pass3␊ |
␊ |
; - Pass3:␊ |
; - iterates over all drives and for each drive:␊ |
; - searches MBR partition table for the first active bootable partition and boots it␊ |
; - if not found and disk is actually GPT, then searches for the first HFS+ bootable␊ |
; partition (or EFI with boot1f32) in the GPT and boots it␊ |
; - if still not found, then continues with the next drive␊ |
; - if all drives are searched and nothing found, finishes with "boot0: error"␊ |
;␊ |
; Bootable partition above means a partition with the boot sector signature (0xAA55)␊ |
; at the end of the partition boot sector.␊ |
; Booting partition means loading partition boot sector and passing control to partition␊ |
; boot loader (for example boot1h).␊ |
; Drives are searched in the order defined in the BIOS. Drive which is selected as the boot drive␊ |
; is searched first.␊ |
;␊ |
; If compiled with DEBUG=1 gives debug output:␊ |
; P - starting new pass␊ |
; D - starting disk scanning: MBR and then GPT␊ |
; p - checking MBR partition entry␊ |
; t - testing MBR partition␊ |
; l - MBR or GPT partition satisfies conditions - loading partition boot sector␊ |
; G - found GPT␊ |
; + - stage 1 booter loaded, press a key to continue␊ |
; E - error␊ |
;␊ |
␊ |
;␊ |
; Set to 1 to enable obscure debug messages.␊ |
;␊ |
DEBUG␉␉␉␉EQU CONFIG_BOOT0_DEBUG␊ |
;DEBUG␉␉␉␉EQU CONFIG_BOOT0_DEBUG␊ |
DEBUG␉␉␉␉EQU 0␊ |
NOT_USED␉␉␉EQU 0␉␉␉␉; exclude print_hex - no space for it ␊ |
␊ |
;␊ |
; Set to 1 to enable verbose mode␊ |
; Verbose - write boot0 messages␊ |
; No space for verbose and debug in the same time␊ |
;␊ |
VERBOSE␉␉␉␉EQU CONFIG_BOOT0_VERBOSE␊ |
;VERBOSE␉␉␉␉EQU CONFIG_BOOT0_VERBOSE␊ |
%if DEBUG␊ |
VERBOSE␉␉␉␉EQU 0␊ |
%else␊ |
VERBOSE␉␉␉␉EQU 1␊ |
%endif␊ |
␊ |
;␊ |
; Various constants.␊ |
|
kDriveNumber␉␉EQU 0x80␊ |
%endif␊ |
␊ |
kPass1␉␉␉␉EQU 3␉␉␉␉; Pass1 ␊ |
kPass2␉␉␉␉EQU 2␉␉␉␉; Pass2 ␊ |
kPass3␉␉␉␉EQU 1␉␉␉␉; Pass3 ␊ |
␊ |
;␊ |
; Format of fdisk partition entry.␊ |
;␊ |
|
call print_char␊ |
%endmacro␊ |
␊ |
%macro LogString 1␊ |
%macro DebugPauseMacro 0␊ |
call getc␊ |
%endmacro␊ |
␊ |
%macro LogStringMacro 1␊ |
mov di, %1␊ |
call log_string␊ |
%endmacro␊ |
␊ |
%if DEBUG␊ |
%define DebugChar(x) DebugCharMacro x␊ |
%define DebugPause DebugPauseMacro␊ |
%else␊ |
%define DebugChar(x)␊ |
%define DebugPause␊ |
%endif␊ |
␊ |
%if VERBOSE␊ |
%define LogString(x) LogStringMacro x␊ |
%else␊ |
%define LogString(x)␊ |
%endif␊ |
␊ |
;--------------------------------------------------------------------------␊ |
; Start of text segment.␊ |
␊ |
|
;␊ |
start_reloc:␊ |
␊ |
␉push␉dx␉␉␉␉␉␉; save dl (boot drive) for second pass.␊ |
␉␉␉␉␉␉␉␉␉; will stay on stack if booter loaded in first pass.␊ |
␉␉␉␉␉␉␉␉␉; this should not be a problem␊ |
mov␉␉bh, 1␉␉␉␉␉; BH = 1. two pass scanning (active or hfs partition).␊ |
␉␉␉␉␉␉␉␉␉; actuall use of it (scanning) is in find_boot␊ |
␉;␊ |
␉; BH is pass counter␊ |
␉; Pass1 BH=3, Pass2 BH=2, Pass3 BH=1␊ |
␉;␊ |
mov␉␉bh, kPass1␉␉␉␉; BH = 3. Pass1␊ |
␊ |
scan_drives:␊ |
pass_loop:␊ |
␊ |
␉DebugChar('>')␊ |
␉DebugChar('P')␉␉␉␉␉; starting new pass␊ |
␉push␉dx␉␉␉␉␉␉; save dl (boot drive) for next pass␊ |
␉␊ |
␉␊ |
.scan_drive:␊ |
␊ |
%if DEBUG␊ |
mov al, dl␊ |
call print_hex␊ |
%endif␊ |
␊ |
;␊ |
; Since this code may not always reside in the MBR, always start by␊ |
; loading the MBR to kMBRBuffer and LBA1 to kGPTBuffer.␊ |
|
mov bx, kMBRBuffer␉␉␉; MBR load address␊ |
call load␊ |
␉pop␉␉bx␉␉␉␉␉␉; restore BH␊ |
jc .mbr_load_error␉␉␉; MBR load error - normally because we scanned all drives␊ |
jc .next_pass␉␉␉␉; MBR load error - normally because we scanned all drives␊ |
␊ |
␉DebugChar('D')␉␉␉␉␉; starting disk scanning␊ |
␉␊ |
;␊ |
; Look for the booter partition in the MBR partition table,␊ |
; which is at offset kMBRPartTable.␊ |
|
mov si, kMBRPartTable␉␉; pointer to partition table␊ |
call find_boot␉␉␉␉; will not return on success␊ |
␉␊ |
␉; if returns - booter partition not found␊ |
␉; if returns - booter partition is not found␊ |
␉␊ |
␉; skip scanning of all drives in Pass1␊ |
␉cmp␉␉bh, kPass1␊ |
␉je␉␉.next_pass␊ |
␉␊ |
␉; try next drive␊ |
␉; if next drive does not exists - will break on above MBR load error␊ |
␉; if next drive does not exists - will break on the MBR load error above␊ |
␉inc␉␉dl␊ |
␉jmp␉␉scan_drives␊ |
␉jmp␉␉short .scan_drive␊ |
␉␊ |
␊ |
.mbr_load_error:␊ |
␉; all drives scanned - see if we need to run second pass␊ |
.next_pass:␊ |
␉; all drives scanned - move to next pass␊ |
␉pop␉␉dx␉␉␉␉␉␉; restore orig boot drive␊ |
␉dec␉␉bh␉␉␉␉␉␉; decrement scan pass counter␊ |
␉jz␉␉scan_drives␉␉␉␉; if zero - run seccond pass␊ |
␉jnz␉␉pass_loop␉␉␉␉; if not zero - exec next pass␊ |
␉␊ |
␉; we ran two passes - nothing found - error␊ |
␉; we ran all passes - nothing found - error␊ |
␉␊ |
error:␊ |
DebugChar('E')␊ |
DebugPause␊ |
LogString(boot_error_str)␊ |
␊ |
hang:␊ |
hlt␊ |
jmp hang␊ |
jmp short hang␊ |
␊ |
␊ |
;--------------------------------------------------------------------------␊ |
|
; Arguments:␊ |
; DL = drive number (0x80 + unit number)␊ |
; SI = pointer to fdisk partition table.␊ |
; BH = pass counter (1=first pass, 0=second pass)␊ |
; BH = pass counter␊ |
;␊ |
; Clobber list:␊ |
; EAX, BX, EBP␊ |
|
␊ |
.loop:␊ |
␊ |
;␊ |
; First scan through the partition table looking for the active␊ |
; partition.␊ |
;␊ |
%if DEBUG␊ |
mov al, [si + part.type] ␉ ; print partition type␊ |
call print_hex␊ |
%endif␊ |
␊ |
␉DebugChar('p')␉␉␉␉␉␉␉␉␉; checking partition entry␊ |
mov␉ eax, [si + part.lba]␉␉␉␉␉; save starting LBA of current ␊ |
mov␉ [my_lba], eax␉␉␉␉␉␉␉; MBR partition entry for read_lba function␊ |
cmp BYTE [si + part.type], 0␉␉␉␉; unused partition?␊ |
|
mov␉ bl, 1␉␉␉␉␉␉␉␉␉; Assume we can deal with GPT but try to scan␊ |
␉␉␉␉␉ ␉␉␉␉␉␉␉; later if not found any other bootable partitions.␊ |
␊ |
␉;␊ |
␉; The following code between .testPass and .tryToBoot performs checking for 3 passes: ␊ |
␉; Pass1 (BH=3) if (partition is HFS+ and active) then { DH=1; call loadBootSector} ␊ |
␉; Pass2 (BH=2) if (partition is HFS+) then { DH=1; call loadBootSector}␊ |
␉; Pass3 (BH=1) if (partition is active) then { DH=0; call loadBootSector}␊ |
␉;␊ |
␉; BH is Pass counter␊ |
␉; DH is argument to loadBootSector␊ |
␉; = 0 - skip HFS+ partition signature check␊ |
␉; = 1 - check for HFS+ partition signature␊ |
␉; ␊ |
␉; Code may be harder to read because I tried to optimized it for minimum size.␊ |
␉;␊ |
␉␉␉␉␉␉␉␉␉␉␉␉␉␊ |
.testPass:␊ |
cmp␉ bh, 1␊ |
jne␉ .Pass2␊ |
␉DebugChar('t')␉␉␉␉␉␉␉␉␉; testing partition␊ |
xor␉␉dh, dh ␉␉␉␉␉; DH=0 This will be used in Pass3 (partition is active, not HFS+).␊ |
␉␉␉␉␉␉␉␉␉␉␉␉␊ |
cmp␉ bh, kPass3␉␉␉␉␉␉␉␉; If this is Pass3 (BH=1)␊ |
je␉ .checkActive␉␉␉␉␉␉␉; check for active flag only.␊ |
␊ |
.Pass1:␊ |
cmp␉ BYTE [si + part.type], kPartTypeHFS␉␉; In pass 1 we're going to find a HFS+ partition␊ |
; equipped with boot1h in its boot record␊ |
; regardless if it's active or not.␊ |
.checkHFS:␊ |
␉␉␉␉␉␉␉␉␉␉␉␉␉; We are in Pass1 (BH=3) or Pass2 (BH=2).␊ |
␉inc␉␉dh␉ ␉␉␉␉␉; DH=1␊ |
cmp␉ BYTE [si + part.type], kPartTypeHFS␉␉; Check for a HFS+ partition.␊ |
jne .continue␊ |
mov␉␉dh, 1 ␉␉␉␉␉; Argument for loadBootSector to check HFS+ partition signature.␊ |
␊ |
jmp .tryToBoot␊ |
cmp␉ bh, kPass2␉␉␉␉␉␉␉␉; It's HFS+. That's enough checking for Pass2,␊ |
je␉ .tryToBoot␉␉␉␉␉␉␉␉; so try to boot (with DH=1)␊ |
␉␉␉␉␉␉␉␉␉␉␉␉; Pass1 needs active flag check also ...␊ |
␊ |
.Pass2: ␊ |
cmp BYTE [si + part.bootid], kPartActive␉; In pass 2 we are walking on the standard path␊ |
; by trying to hop on the active partition.␊ |
.checkActive:␊ |
␉␉␉␉␉␉␉␉␉␉␉␉␉; We are in Pass1 or Pass3␊ |
cmp BYTE [si + part.bootid], kPartActive␉; Check if partition is Active␊ |
jne .continue␊ |
xor␉␉dh, dh ␉␉␉␉␉; Argument for loadBootSector to skip HFS+ partition␊ |
; signature check.␊ |
␊ |
DebugChar('*')␊ |
␊ |
;␊ |
; Found boot partition, read boot sector to memory.␊ |
|
;␊ |
initBootLoader: ␊ |
␊ |
DebugChar('J')␊ |
DebugChar('+')␊ |
DebugPause␊ |
␊ |
%if VERBOSE␊ |
LogString(done_str)␊ |
%endif␊ |
␊ |
jmp kBoot0LoadAddr␊ |
␊ |
|
jne␉ .exit␉␉␉␉␉␉␉␉; not found. Giving up.␊ |
cmp␉ DWORD [di + 4], kGPTSignatureHigh ; looking for 'PART'␊ |
jne␉ .exit␉␉␉␉␉␉␉␉; not found. Giving up indeed.␊ |
␊ |
DebugChar('G')␉␉␉␉␉␉␉␉; found GPT␊ |
mov␉ si, di␊ |
␊ |
␊ |
;␊ |
; Loading GUID Partition Table Array␊ |
;␊ |
|
call load␉␉␉␉␉; read GPT Array␊ |
pop␉ si␉␉␉␉␉␉; SI = address of GPT Array␊ |
pop␉ bx␉␉␉␉␉␉; BX = size of GUID Partition Array entry␊ |
jc␉ error␊ |
;jc error␊ |
␉jc .exit␉␉␉␉␉; dmazar's change to continue disk scanning if encountering invalid LBA.␊ |
␊ |
;␊ |
; Walk through GUID Partition Table Array␊ |
|
; otherwise skip to next partition.␊ |
;␊ |
␊ |
%if VERBOSE␊ |
LogString(gpt_str)␊ |
%endif␊ |
␊ |
.gpt_loop:␊ |
␊ |
|
loadBootSector:␊ |
pusha␊ |
␊ |
DebugChar('l')␉␉␉␉␉␉␉␉␉; loading partition boot sector␊ |
␊ |
mov al, 3␊ |
mov bx, kBoot0LoadAddr␊ |
call load␊ |
jc error␊ |
;jc error␊ |
or dl, dl ; to set flag Z=0␉; dmazar's change to continue disk scanning if encountering invalid LBA.␊ |
jc .exit␉␉␉␉␉; dmazar's change to continue disk scanning if encountering invalid LBA.␊ |
␊ |
␉or␉␉dh, dh␊ |
␉jz␉␉.checkBootSignature␊ |
␉␊ |
.checkHFSSignature:␊ |
␊ |
%if VERBOSE␊ |
;LogString(test_str)␉␉␉; dmazar: removed to get space␊ |
%endif␊ |
␊ |
␉;␊ |
␉; Looking for HFSPlus ('H+') or HFSPlus case-sensitive ('HX') signature.␊ |
|
pop cx␊ |
ret␊ |
␊ |
␊ |
;--------------------------------------------------------------------------␊ |
; read_lba - Read sectors from a partition using LBA addressing.␊ |
;␊ |
|
; It pushes 2 bytes with a smaller opcode than if WORD was used␊ |
push BYTE 16 ; offset 0-1, packet size␊ |
␊ |
DebugChar('<')␊ |
%if DEBUG␊ |
mov eax, ecx␊ |
call print_hex␊ |
%endif␊ |
␊ |
;␊ |
; INT13 Func 42 - Extended Read Sectors␊ |
;␊ |
|
␊ |
jnc .exit␊ |
␊ |
DebugChar('R') ; indicate INT13/F42 error␊ |
␊ |
;␊ |
; Issue a disk reset on error.␊ |
; Should this be changed to Func 0xD to skip the diskette controller␊ |
|
popad␊ |
ret␊ |
␊ |
␊ |
␊ |
%if VERBOSE␊ |
␊ |
;--------------------------------------------------------------------------␊ |
; Write a string with 'boot0: ' prefix to the console.␊ |
;␊ |
|
.exit:␊ |
ret␊ |
␊ |
%endif ;VERBOSE␊ |
␊ |
␊ |
%if DEBUG␊ |
␊ |
;--------------------------------------------------------------------------␊ |
|
popa␊ |
ret␊ |
␊ |
getc:␊ |
pusha␊ |
mov ah, 0␊ |
int 0x16␊ |
popa␊ |
ret␊ |
%endif ;DEBUG␊ |
␉␊ |
%if NOT_USED␊ |
␊ |
;--------------------------------------------------------------------------␊ |
; Write the 4-byte value to the console in hex.␊ |
|
call print_char␊ |
ret␊ |
␊ |
getc:␊ |
pusha␊ |
mov ah, 0␊ |
int 0x16␊ |
popa␊ |
ret␊ |
%endif ;DEBUG␊ |
␉␊ |
%endif ; NOT_USED␊ |
␊ |
␊ |
␊ |
%if VERBOSE␊ |
␊ |
;--------------------------------------------------------------------------␊ |
; NULL terminated strings.␊ |
;␊ |
log_title_str␉␉db 10, 13, 'boot0: ', 0␊ |
log_title_str␉␉db 10, 13, 'boot0:', 0␊ |
boot_error_str ␉db 'error', 0␊ |
␊ |
%if VERBOSE␊ |
gpt_str␉␉␉db 'GPT', 0␊ |
;test_str␉␉db 'test', 0␊ |
done_str␉␉db 'done', 0␊ |
␊ |
%endif␊ |
␊ |
;--------------------------------------------------------------------------␊ |