mirror of
https://github.com/CloverHackyColor/CloverBootloader.git
synced 2024-12-26 16:47:40 +01:00
906 lines
26 KiB
ArmAsm
906 lines
26 KiB
ArmAsm
|
; Copyright (c) 1999-2003 Apple Computer, Inc. All rights reserved.
|
|||
|
;
|
|||
|
; @APPLE_LICENSE_HEADER_START@
|
|||
|
;
|
|||
|
; Portions Copyright (c) 1999-2003 Apple Computer, Inc. All Rights
|
|||
|
; Reserved. This file contains Original Code and/or Modifications of
|
|||
|
; Original Code as defined in and that are subject to the Apple Public
|
|||
|
; Source License Version 2.0 (the "License"). You may not use this file
|
|||
|
; except in compliance with the License. Please obtain a copy of the
|
|||
|
; License at http://www.apple.com/publicsource and read it before using
|
|||
|
; this file.
|
|||
|
;
|
|||
|
; The Original Code and all software distributed under the License are
|
|||
|
; distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER
|
|||
|
; EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
|
|||
|
; INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
|
|||
|
; FITNESS FOR A PARTICULAR PURPOSE OR NON- INFRINGEMENT. Please see the
|
|||
|
; License for the specific language governing rights and limitations
|
|||
|
; under the License.
|
|||
|
;
|
|||
|
; @APPLE_LICENSE_HEADER_END@
|
|||
|
;
|
|||
|
; Boot Loader: boot0
|
|||
|
;
|
|||
|
; A small boot sector program written in x86 assembly whose only
|
|||
|
; responsibility is to locate the active partition, load the
|
|||
|
; partition booter into memory, and jump to the booter's entry point.
|
|||
|
; It leaves the boot drive in DL and a pointer to the partition entry in SI.
|
|||
|
;
|
|||
|
; This boot loader must be placed in the Master Boot Record.
|
|||
|
;
|
|||
|
; In order to coexist with a fdisk partition table (64 bytes), and
|
|||
|
; leave room for a two byte signature (0xAA55) in the end, boot0 is
|
|||
|
; restricted to 446 bytes (512 - 64 - 2). If boot0 did not have to
|
|||
|
; live in the MBR, then we would have 510 bytes to work with.
|
|||
|
;
|
|||
|
; boot0 is always loaded by the BIOS or another booter to 0:7C00h.
|
|||
|
;
|
|||
|
; This code is written for the NASM assembler.
|
|||
|
; nasm boot0.s -o boot0
|
|||
|
|
|||
|
;
|
|||
|
; This version of boot0 implements hybrid GUID/MBR partition scheme support
|
|||
|
;
|
|||
|
; Written by Tam<61>s Kos<6F>rszky on 2008-03-10
|
|||
|
;
|
|||
|
; Turbo added EFI System Partition boot support
|
|||
|
;
|
|||
|
; Added KillerJK's switchPass2 modifications
|
|||
|
;
|
|||
|
; dmazar: 10/7/2011 added scanning of all BIOS accessible drives:
|
|||
|
; - iterates over all drives and searches for HSF bootable partition (with boot1h)
|
|||
|
; and loads from it
|
|||
|
; - 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 0
|
|||
|
NOT_USED EQU 0 ; exclude print_hex - no space for it
|
|||
|
|
|||
|
;
|
|||
|
; Verbose - write boot0 messages
|
|||
|
; No space for verbose and debug in the same time
|
|||
|
;
|
|||
|
;VERBOSE EQU CONFIG_BOOT0_VERBOSE
|
|||
|
%if DEBUG
|
|||
|
VERBOSE EQU 0
|
|||
|
%else
|
|||
|
VERBOSE EQU 1
|
|||
|
%endif
|
|||
|
|
|||
|
;
|
|||
|
; Various constants.
|
|||
|
;
|
|||
|
kBoot0Segment EQU 0x0000
|
|||
|
kBoot0Stack EQU 0xFFF0 ; boot0 stack pointer
|
|||
|
kBoot0LoadAddr EQU 0x7C00 ; boot0 load address
|
|||
|
kBoot0RelocAddr EQU 0xE000 ; boot0 relocated address
|
|||
|
|
|||
|
kMBRBuffer EQU 0x1000 ; MBR buffer address
|
|||
|
kLBA1Buffer EQU 0x1200 ; LBA1 - GPT Partition Table Header buffer address
|
|||
|
kGPTABuffer EQU 0x1400 ; GUID Partition Entry Array buffer address
|
|||
|
|
|||
|
kPartTableOffset EQU 0x1be
|
|||
|
kMBRPartTable EQU kMBRBuffer + kPartTableOffset
|
|||
|
|
|||
|
kSectorBytes EQU 512 ; sector size in bytes
|
|||
|
kBootSignature EQU 0xAA55 ; boot sector signature
|
|||
|
kHFSPSignature EQU 'H+' ; HFS+ volume signature
|
|||
|
kHFSPCaseSignature EQU 'HX' ; HFS+ volume case-sensitive signature
|
|||
|
kFAT32BootCodeOffset EQU 0x5a ; offset of boot code in FAT32 boot sector
|
|||
|
kBoot1FAT32Magic EQU 'BO' ; Magic string to detect our boot1f32 code
|
|||
|
|
|||
|
|
|||
|
kGPTSignatureLow EQU 'EFI ' ; GUID Partition Table Header Signature
|
|||
|
kGPTSignatureHigh EQU 'PART'
|
|||
|
kGUIDLastDwordOffs EQU 12 ; last 4 byte offset of a GUID
|
|||
|
|
|||
|
kPartCount EQU 4 ; number of paritions per table
|
|||
|
kPartTypeHFS EQU 0xaf ; HFS+ Filesystem type
|
|||
|
kPartTypePMBR EQU 0xee ; On all GUID Partition Table disks a Protective MBR (PMBR)
|
|||
|
; in LBA 0 (that is, the first block) precedes the
|
|||
|
; GUID Partition Table Header to maintain compatibility
|
|||
|
; with existing tools that do not understand GPT partition structures.
|
|||
|
; The Protective MBR has the same format as a legacy MBR
|
|||
|
; and contains one partition entry with an OSType set to 0xEE
|
|||
|
; reserving the entire space used on the disk by the GPT partitions,
|
|||
|
; including all headers.
|
|||
|
|
|||
|
kPartActive EQU 0x80 ; active flag enabled
|
|||
|
kPartInactive EQU 0x00 ; active flag disabled
|
|||
|
kHFSGUID EQU 0x48465300 ; first 4 bytes of Apple HFS Partition Type GUID.
|
|||
|
kAppleGUID EQU 0xACEC4365 ; last 4 bytes of Apple type GUIDs.
|
|||
|
kEFISystemGUID EQU 0x3BC93EC9 ; last 4 bytes of EFI System Partition Type GUID:
|
|||
|
; C12A7328-F81F-11D2-BA4B-00A0C93EC93B
|
|||
|
|
|||
|
%ifdef FLOPPY
|
|||
|
kDriveNumber EQU 0x00
|
|||
|
%else
|
|||
|
kDriveNumber EQU 0x80
|
|||
|
%endif
|
|||
|
|
|||
|
kPass1 EQU 3 ; Pass1
|
|||
|
kPass2 EQU 2 ; Pass2
|
|||
|
kPass3 EQU 1 ; Pass3
|
|||
|
|
|||
|
;
|
|||
|
; Format of fdisk partition entry.
|
|||
|
;
|
|||
|
; The symbol 'part_size' is automatically defined as an `EQU'
|
|||
|
; giving the size of the structure.
|
|||
|
;
|
|||
|
struc part
|
|||
|
.bootid resb 1 ; bootable or not
|
|||
|
.head resb 1 ; starting head, sector, cylinder
|
|||
|
.sect resb 1 ;
|
|||
|
.cyl resb 1 ;
|
|||
|
.type resb 1 ; partition type
|
|||
|
.endhead resb 1 ; ending head, sector, cylinder
|
|||
|
.endsect resb 1 ;
|
|||
|
.endcyl resb 1 ;
|
|||
|
.lba resd 1 ; starting lba
|
|||
|
.sectors resd 1 ; size in sectors
|
|||
|
endstruc
|
|||
|
|
|||
|
;
|
|||
|
; Format of GPT Partition Table Header
|
|||
|
;
|
|||
|
struc gpth
|
|||
|
.Signature resb 8
|
|||
|
.Revision resb 4
|
|||
|
.HeaderSize resb 4
|
|||
|
.HeaderCRC32 resb 4
|
|||
|
.Reserved resb 4
|
|||
|
.MyLBA resb 8
|
|||
|
.AlternateLBA resb 8
|
|||
|
.FirstUsableLBA resb 8
|
|||
|
.LastUsableLBA resb 8
|
|||
|
.DiskGUID resb 16
|
|||
|
.PartitionEntryLBA resb 8
|
|||
|
.NumberOfPartitionEntries resb 4
|
|||
|
.SizeOfPartitionEntry resb 4
|
|||
|
.PartitionEntryArrayCRC32 resb 4
|
|||
|
endstruc
|
|||
|
|
|||
|
;
|
|||
|
; Format of GUID Partition Entry Array
|
|||
|
;
|
|||
|
struc gpta
|
|||
|
.PartitionTypeGUID resb 16
|
|||
|
.UniquePartitionGUID resb 16
|
|||
|
.StartingLBA resb 8
|
|||
|
.EndingLBA resb 8
|
|||
|
.Attributes resb 8
|
|||
|
.PartitionName resb 72
|
|||
|
endstruc
|
|||
|
|
|||
|
;
|
|||
|
; Macros.
|
|||
|
;
|
|||
|
%macro DebugCharMacro 1
|
|||
|
mov al, %1
|
|||
|
call print_char
|
|||
|
%endmacro
|
|||
|
|
|||
|
%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.
|
|||
|
|
|||
|
SEGMENT .text
|
|||
|
|
|||
|
ORG kBoot0RelocAddr
|
|||
|
|
|||
|
;--------------------------------------------------------------------------
|
|||
|
; Boot code is loaded at 0:7C00h.
|
|||
|
;
|
|||
|
start:
|
|||
|
;
|
|||
|
; Set up the stack to grow down from kBoot0Segment:kBoot0Stack.
|
|||
|
; Interrupts should be off while the stack is being manipulated.
|
|||
|
;
|
|||
|
cli ; interrupts off
|
|||
|
xor ax, ax ; zero ax
|
|||
|
mov ss, ax ; ss <- 0
|
|||
|
mov sp, kBoot0Stack ; sp <- top of stack
|
|||
|
sti ; reenable interrupts
|
|||
|
|
|||
|
mov es, ax ; es <- 0
|
|||
|
mov ds, ax ; ds <- 0
|
|||
|
|
|||
|
;
|
|||
|
; Relocate boot0 code.
|
|||
|
;
|
|||
|
mov si, kBoot0LoadAddr ; si <- source
|
|||
|
mov di, kBoot0RelocAddr ; di <- destination
|
|||
|
cld ; auto-increment SI and/or DI registers
|
|||
|
mov cx, kSectorBytes/2 ; copy 256 words
|
|||
|
repnz movsw ; repeat string move (word) operation
|
|||
|
|
|||
|
;
|
|||
|
; Code relocated, jump to start_reloc in relocated location.
|
|||
|
;
|
|||
|
jmp kBoot0Segment:start_reloc
|
|||
|
|
|||
|
;--------------------------------------------------------------------------
|
|||
|
; Start execution from the relocated location.
|
|||
|
;
|
|||
|
start_reloc:
|
|||
|
|
|||
|
;
|
|||
|
; BH is pass counter
|
|||
|
; Pass1 BH=3, Pass2 BH=2, Pass3 BH=1
|
|||
|
;
|
|||
|
mov bh, kPass1 ; BH = 3. Pass1
|
|||
|
|
|||
|
pass_loop:
|
|||
|
|
|||
|
DebugChar('P') ; starting new pass
|
|||
|
push dx ; save dl (boot drive) for next pass
|
|||
|
|
|||
|
|
|||
|
.scan_drive:
|
|||
|
|
|||
|
;
|
|||
|
; Since this code may not always reside in the MBR, always start by
|
|||
|
; loading the MBR to kMBRBuffer and LBA1 to kGPTBuffer.
|
|||
|
;
|
|||
|
|
|||
|
push bx ; save BH (scan pass counter)
|
|||
|
xor eax, eax
|
|||
|
mov [my_lba], eax ; store LBA sector 0 for read_lba function
|
|||
|
mov al, 2 ; load two sectors: MBR and LBA1
|
|||
|
mov bx, kMBRBuffer ; MBR load address
|
|||
|
call load
|
|||
|
pop bx ; restore BH
|
|||
|
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 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 the MBR load error above
|
|||
|
inc dl
|
|||
|
jmp short .scan_drive
|
|||
|
|
|||
|
|
|||
|
.next_pass:
|
|||
|
; all drives scanned - move to next pass
|
|||
|
pop dx ; restore orig boot drive
|
|||
|
dec bh ; decrement scan pass counter
|
|||
|
jnz pass_loop ; if not zero - exec next pass
|
|||
|
|
|||
|
; we ran all passes - nothing found - error
|
|||
|
|
|||
|
error:
|
|||
|
DebugChar('E')
|
|||
|
DebugPause
|
|||
|
LogString(boot_error_str)
|
|||
|
|
|||
|
hang:
|
|||
|
hlt
|
|||
|
jmp short hang
|
|||
|
|
|||
|
|
|||
|
;--------------------------------------------------------------------------
|
|||
|
; Find the active (boot) partition and load the booter from the partition.
|
|||
|
;
|
|||
|
; Arguments:
|
|||
|
; DL = drive number (0x80 + unit number)
|
|||
|
; SI = pointer to fdisk partition table.
|
|||
|
; BH = pass counter
|
|||
|
;
|
|||
|
; Clobber list:
|
|||
|
; EAX, BX, EBP
|
|||
|
;
|
|||
|
find_boot:
|
|||
|
|
|||
|
;
|
|||
|
; Check for boot block signature 0xAA55 following the 4 partition
|
|||
|
; entries.
|
|||
|
;
|
|||
|
cmp WORD [si + part_size * kPartCount], kBootSignature
|
|||
|
jne .exit ; boot signature not found.
|
|||
|
|
|||
|
xor bl, bl ; BL will be set to 1 later in case of
|
|||
|
; Protective MBR has been found
|
|||
|
|
|||
|
.start_scan:
|
|||
|
mov cx, kPartCount ; number of partition entries per table
|
|||
|
|
|||
|
.loop:
|
|||
|
|
|||
|
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?
|
|||
|
je .continue ; skip to next entry
|
|||
|
cmp BYTE [si + part.type], kPartTypePMBR ; check for Protective MBR
|
|||
|
jne .testPass
|
|||
|
|
|||
|
mov BYTE [si + part.bootid], kPartInactive ; found Protective MBR
|
|||
|
; clear active flag to make sure this protective
|
|||
|
; partition won't be used as a bootable 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:
|
|||
|
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.
|
|||
|
|
|||
|
.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
|
|||
|
|
|||
|
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 ...
|
|||
|
|
|||
|
.checkActive:
|
|||
|
; We are in Pass1 or Pass3
|
|||
|
cmp BYTE [si + part.bootid], kPartActive ; Check if partition is Active
|
|||
|
jne .continue
|
|||
|
|
|||
|
|
|||
|
;
|
|||
|
; Found boot partition, read boot sector to memory.
|
|||
|
;
|
|||
|
|
|||
|
.tryToBoot:
|
|||
|
|
|||
|
call loadBootSector
|
|||
|
jne .continue
|
|||
|
jmp SHORT initBootLoader
|
|||
|
|
|||
|
.continue:
|
|||
|
add si, BYTE part_size ; advance SI to next partition entry
|
|||
|
loop .loop ; loop through all partition entries
|
|||
|
|
|||
|
;
|
|||
|
; Scanned all partitions but not found any with active flag enabled
|
|||
|
; Anyway if we found a protective MBR before we still have a chance
|
|||
|
; for a possible GPT Header at LBA 1
|
|||
|
;
|
|||
|
dec bl
|
|||
|
jnz .exit ; didn't find Protective MBR before
|
|||
|
call checkGPT
|
|||
|
|
|||
|
.exit:
|
|||
|
ret ; Giving up.
|
|||
|
|
|||
|
|
|||
|
;
|
|||
|
; Jump to partition booter. The drive number is already in register DL.
|
|||
|
; SI is pointing to the modified partition entry.
|
|||
|
;
|
|||
|
initBootLoader:
|
|||
|
|
|||
|
DebugChar('+')
|
|||
|
DebugPause
|
|||
|
|
|||
|
LogString(done_str)
|
|||
|
|
|||
|
jmp kBoot0LoadAddr
|
|||
|
|
|||
|
|
|||
|
;
|
|||
|
; Found Protective MBR Partition Type: 0xEE
|
|||
|
; Check for 'EFI PART' string at the beginning
|
|||
|
; of LBA1 for possible GPT Table Header
|
|||
|
;
|
|||
|
checkGPT:
|
|||
|
push bx
|
|||
|
|
|||
|
mov di, kLBA1Buffer ; address of GUID Partition Table Header
|
|||
|
cmp DWORD [di], kGPTSignatureLow ; looking for 'EFI '
|
|||
|
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
|
|||
|
;
|
|||
|
mov eax, [si + gpth.PartitionEntryLBA] ; starting LBA of GPT Array
|
|||
|
mov [my_lba], eax ; save starting LBA for read_lba function
|
|||
|
mov cx, [si + gpth.NumberOfPartitionEntries] ; number of GUID Partition Array entries
|
|||
|
mov bx, [si + gpth.SizeOfPartitionEntry] ; size of GUID Partition Array entry
|
|||
|
push bx ; push size of GUID Partition entry
|
|||
|
|
|||
|
;
|
|||
|
; Calculating number of sectors we need to read for loading a GPT Array
|
|||
|
;
|
|||
|
; push dx ; preserve DX (DL = BIOS drive unit number)
|
|||
|
; mov ax, cx ; AX * BX = number of entries * size of one entry
|
|||
|
; mul bx ; AX = total byte size of GPT Array
|
|||
|
; pop dx ; restore DX
|
|||
|
; shr ax, 9 ; convert to sectors
|
|||
|
|
|||
|
;
|
|||
|
; ... or:
|
|||
|
; Current GPT Arrays uses 128 partition entries each 128 bytes long
|
|||
|
; 128 entries * 128 bytes long GPT Array entries / 512 bytes per sector = 32 sectors
|
|||
|
;
|
|||
|
mov al, 32 ; maximum sector size of GPT Array (hardcoded method)
|
|||
|
|
|||
|
mov bx, kGPTABuffer
|
|||
|
push bx ; push address of GPT 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 .exit ; dmazar's change to continue disk scanning if encountering invalid LBA.
|
|||
|
|
|||
|
;
|
|||
|
; Walk through GUID Partition Table Array
|
|||
|
; and load boot record from first available HFS+ partition.
|
|||
|
;
|
|||
|
; If it has boot signature (0xAA55) then jump to it
|
|||
|
; otherwise skip to next partition.
|
|||
|
;
|
|||
|
|
|||
|
LogString(gpt_str)
|
|||
|
|
|||
|
.gpt_loop:
|
|||
|
|
|||
|
mov eax, [si + gpta.PartitionTypeGUID + kGUIDLastDwordOffs]
|
|||
|
|
|||
|
cmp eax, kAppleGUID ; check current GUID Partition for Apple's GUID type
|
|||
|
je .gpt_ok
|
|||
|
|
|||
|
;
|
|||
|
; Turbo - also try EFI System Partition
|
|||
|
;
|
|||
|
|
|||
|
cmp eax, kEFISystemGUID ; check current GUID Partition for EFI System Partition GUID type
|
|||
|
jne .gpt_continue
|
|||
|
|
|||
|
.gpt_ok:
|
|||
|
;
|
|||
|
; Found HFS Partition
|
|||
|
;
|
|||
|
|
|||
|
mov eax, [si + gpta.StartingLBA] ; load boot sector from StartingLBA
|
|||
|
mov [my_lba], eax
|
|||
|
mov dh, 1 ; Argument for loadBootSector to check HFS+ partition signature.
|
|||
|
call loadBootSector
|
|||
|
jne .gpt_continue ; no boot loader signature
|
|||
|
|
|||
|
mov si, kMBRPartTable ; fake the current GUID Partition
|
|||
|
mov [si + part.lba], eax ; as MBR style partition for boot1h
|
|||
|
mov BYTE [si + part.type], kPartTypeHFS ; with HFS+ filesystem type (0xAF)
|
|||
|
jmp SHORT initBootLoader
|
|||
|
|
|||
|
.gpt_continue:
|
|||
|
|
|||
|
add si, bx ; advance SI to next partition entry
|
|||
|
loop .gpt_loop ; loop through all partition entries
|
|||
|
|
|||
|
.exit:
|
|||
|
pop bx
|
|||
|
ret ; no more GUID partitions. Giving up.
|
|||
|
|
|||
|
|
|||
|
;--------------------------------------------------------------------------
|
|||
|
; loadBootSector - Load boot sector
|
|||
|
;
|
|||
|
; Arguments:
|
|||
|
; DL = drive number (0x80 + unit number)
|
|||
|
; DH = 0 skip HFS+ partition signature checking
|
|||
|
; 1 enable HFS+ partition signature checking
|
|||
|
; [my_lba] = starting LBA.
|
|||
|
;
|
|||
|
; Returns:
|
|||
|
; ZF = 0 if boot sector hasn't kBootSignature
|
|||
|
; 1 if boot sector has kBootSignature
|
|||
|
;
|
|||
|
loadBootSector:
|
|||
|
pusha
|
|||
|
|
|||
|
DebugChar('l') ; loading partition boot sector
|
|||
|
|
|||
|
mov al, 3
|
|||
|
mov bx, kBoot0LoadAddr
|
|||
|
call load
|
|||
|
;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:
|
|||
|
|
|||
|
;LogString(test_str) ; dmazar: removed to get space
|
|||
|
|
|||
|
;
|
|||
|
; Looking for HFSPlus ('H+') or HFSPlus case-sensitive ('HX') signature.
|
|||
|
;
|
|||
|
mov ax, [kBoot0LoadAddr + 2 * kSectorBytes]
|
|||
|
cmp ax, kHFSPSignature ; 'H+'
|
|||
|
je .checkBootSignature
|
|||
|
cmp ax, kHFSPCaseSignature ; 'HX'
|
|||
|
je .checkBootSignature
|
|||
|
|
|||
|
;
|
|||
|
; Looking for boot1f32 magic string.
|
|||
|
;
|
|||
|
mov ax, [kBoot0LoadAddr + kFAT32BootCodeOffset]
|
|||
|
cmp ax, kBoot1FAT32Magic
|
|||
|
jne .exit
|
|||
|
|
|||
|
.checkBootSignature:
|
|||
|
;
|
|||
|
; Check for boot block signature 0xAA55
|
|||
|
;
|
|||
|
mov di, bx
|
|||
|
cmp WORD [di + kSectorBytes - 2], kBootSignature
|
|||
|
|
|||
|
.exit:
|
|||
|
|
|||
|
popa
|
|||
|
|
|||
|
ret
|
|||
|
|
|||
|
|
|||
|
;--------------------------------------------------------------------------
|
|||
|
; load - Load one or more sectors from a partition.
|
|||
|
;
|
|||
|
; Arguments:
|
|||
|
; AL = number of 512-byte sectors to read.
|
|||
|
; ES:BX = pointer to where the sectors should be stored.
|
|||
|
; DL = drive number (0x80 + unit number)
|
|||
|
; [my_lba] = starting LBA.
|
|||
|
;
|
|||
|
; Returns:
|
|||
|
; CF = 0 success
|
|||
|
; 1 error
|
|||
|
;
|
|||
|
load:
|
|||
|
push cx
|
|||
|
|
|||
|
.ebios:
|
|||
|
mov cx, 5 ; load retry count
|
|||
|
.ebios_loop:
|
|||
|
call read_lba ; use INT13/F42
|
|||
|
jnc .exit
|
|||
|
loop .ebios_loop
|
|||
|
|
|||
|
.exit:
|
|||
|
pop cx
|
|||
|
ret
|
|||
|
|
|||
|
;--------------------------------------------------------------------------
|
|||
|
; read_lba - Read sectors from a partition using LBA addressing.
|
|||
|
;
|
|||
|
; Arguments:
|
|||
|
; AL = number of 512-byte sectors to read (valid from 1-127).
|
|||
|
; ES:BX = pointer to where the sectors should be stored.
|
|||
|
; DL = drive number (0x80 + unit number)
|
|||
|
; [my_lba] = starting LBA.
|
|||
|
;
|
|||
|
; Returns:
|
|||
|
; CF = 0 success
|
|||
|
; 1 error
|
|||
|
;
|
|||
|
read_lba:
|
|||
|
pushad ; save all registers
|
|||
|
mov bp, sp ; save current SP
|
|||
|
|
|||
|
;
|
|||
|
; Create the Disk Address Packet structure for the
|
|||
|
; INT13/F42 (Extended Read Sectors) on the stack.
|
|||
|
;
|
|||
|
|
|||
|
; push DWORD 0 ; offset 12, upper 32-bit LBA
|
|||
|
push ds ; For sake of saving memory,
|
|||
|
push ds ; push DS register, which is 0.
|
|||
|
mov ecx, [my_lba] ; offset 8, lower 32-bit LBA
|
|||
|
push ecx
|
|||
|
push es ; offset 6, memory segment
|
|||
|
push bx ; offset 4, memory offset
|
|||
|
xor ah, ah ; offset 3, must be 0
|
|||
|
push ax ; offset 2, number of sectors
|
|||
|
|
|||
|
; It pushes 2 bytes with a smaller opcode than if WORD was used
|
|||
|
push BYTE 16 ; offset 0-1, packet size
|
|||
|
|
|||
|
;
|
|||
|
; INT13 Func 42 - Extended Read Sectors
|
|||
|
;
|
|||
|
; Arguments:
|
|||
|
; AH = 0x42
|
|||
|
; DL = drive number (80h + drive unit)
|
|||
|
; DS:SI = pointer to Disk Address Packet
|
|||
|
;
|
|||
|
; Returns:
|
|||
|
; AH = return status (sucess is 0)
|
|||
|
; carry = 0 success
|
|||
|
; 1 error
|
|||
|
;
|
|||
|
; Packet offset 2 indicates the number of sectors read
|
|||
|
; successfully.
|
|||
|
;
|
|||
|
mov si, sp
|
|||
|
mov ah, 0x42
|
|||
|
int 0x13
|
|||
|
|
|||
|
jnc .exit
|
|||
|
|
|||
|
;
|
|||
|
; Issue a disk reset on error.
|
|||
|
; Should this be changed to Func 0xD to skip the diskette controller
|
|||
|
; reset?
|
|||
|
;
|
|||
|
xor ax, ax ; Func 0
|
|||
|
int 0x13 ; INT 13
|
|||
|
stc ; set carry to indicate error
|
|||
|
|
|||
|
.exit:
|
|||
|
mov sp, bp ; restore SP
|
|||
|
popad
|
|||
|
ret
|
|||
|
|
|||
|
|
|||
|
%if VERBOSE
|
|||
|
|
|||
|
;--------------------------------------------------------------------------
|
|||
|
; Write a string with 'boot0: ' prefix to the console.
|
|||
|
;
|
|||
|
; Arguments:
|
|||
|
; ES:DI pointer to a NULL terminated string.
|
|||
|
;
|
|||
|
; Clobber list:
|
|||
|
; DI
|
|||
|
;
|
|||
|
log_string:
|
|||
|
pusha
|
|||
|
|
|||
|
push di
|
|||
|
mov si, log_title_str
|
|||
|
call print_string
|
|||
|
|
|||
|
pop si
|
|||
|
call print_string
|
|||
|
|
|||
|
popa
|
|||
|
|
|||
|
ret
|
|||
|
|
|||
|
|
|||
|
;--------------------------------------------------------------------------
|
|||
|
; Write a string to the console.
|
|||
|
;
|
|||
|
; Arguments:
|
|||
|
; DS:SI pointer to a NULL terminated string.
|
|||
|
;
|
|||
|
; Clobber list:
|
|||
|
; AX, BX, SI
|
|||
|
;
|
|||
|
print_string:
|
|||
|
mov bx, 1 ; BH=0, BL=1 (blue)
|
|||
|
cld ; increment SI after each lodsb call
|
|||
|
.loop:
|
|||
|
lodsb ; load a byte from DS:SI into AL
|
|||
|
cmp al, 0 ; Is it a NULL?
|
|||
|
je .exit ; yes, all done
|
|||
|
mov ah, 0xE ; INT10 Func 0xE
|
|||
|
int 0x10 ; display byte in tty mode
|
|||
|
jmp short .loop
|
|||
|
.exit:
|
|||
|
ret
|
|||
|
|
|||
|
%endif ;VERBOSE
|
|||
|
|
|||
|
|
|||
|
%if DEBUG
|
|||
|
|
|||
|
;--------------------------------------------------------------------------
|
|||
|
; Write a ASCII character to the console.
|
|||
|
;
|
|||
|
; Arguments:
|
|||
|
; AL = ASCII character.
|
|||
|
;
|
|||
|
print_char:
|
|||
|
pusha
|
|||
|
mov bx, 1 ; BH=0, BL=1 (blue)
|
|||
|
mov ah, 0x0e ; bios INT 10, Function 0xE
|
|||
|
int 0x10 ; display byte in tty mode
|
|||
|
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.
|
|||
|
;
|
|||
|
; Arguments:
|
|||
|
; EAX = Value to be displayed in hex.
|
|||
|
;
|
|||
|
print_hex:
|
|||
|
pushad
|
|||
|
mov cx, WORD 4
|
|||
|
bswap eax
|
|||
|
.loop:
|
|||
|
push ax
|
|||
|
ror al, 4
|
|||
|
call print_nibble ; display upper nibble
|
|||
|
pop ax
|
|||
|
call print_nibble ; display lower nibble
|
|||
|
ror eax, 8
|
|||
|
loop .loop
|
|||
|
|
|||
|
mov al, 10 ; carriage return
|
|||
|
call print_char
|
|||
|
mov al, 13
|
|||
|
call print_char
|
|||
|
|
|||
|
popad
|
|||
|
ret
|
|||
|
|
|||
|
print_nibble:
|
|||
|
and al, 0x0f
|
|||
|
add al, '0'
|
|||
|
cmp al, '9'
|
|||
|
jna .print_ascii
|
|||
|
add al, 'A' - '9' - 1
|
|||
|
.print_ascii:
|
|||
|
call print_char
|
|||
|
ret
|
|||
|
|
|||
|
%endif ; NOT_USED
|
|||
|
|
|||
|
|
|||
|
|
|||
|
%if VERBOSE
|
|||
|
|
|||
|
;--------------------------------------------------------------------------
|
|||
|
; NULL terminated strings.
|
|||
|
;
|
|||
|
log_title_str db 10, 13, 'boot0:', 0
|
|||
|
boot_error_str db 'error', 0
|
|||
|
|
|||
|
gpt_str db 'GPT', 0
|
|||
|
;test_str db 'test', 0
|
|||
|
done_str db 'done', 0
|
|||
|
|
|||
|
%endif
|
|||
|
|
|||
|
;--------------------------------------------------------------------------
|
|||
|
; Pad the rest of the 512 byte sized booter with zeroes. The last
|
|||
|
; two bytes is the mandatory boot sector signature.
|
|||
|
;
|
|||
|
; If the booter code becomes too large, then nasm will complain
|
|||
|
; that the 'times' argument is negative.
|
|||
|
|
|||
|
;
|
|||
|
; According to EFI specification, maximum boot code size is 440 bytes
|
|||
|
;
|
|||
|
|
|||
|
pad_boot:
|
|||
|
times 440-($-$$) db 0
|
|||
|
|
|||
|
pad_table_and_sig:
|
|||
|
times 510-($-$$) db 0
|
|||
|
dw kBootSignature
|
|||
|
|
|||
|
|
|||
|
ABSOLUTE 0xE400
|
|||
|
|
|||
|
;
|
|||
|
; In memory variables.
|
|||
|
;
|
|||
|
my_lba resd 1 ; Starting LBA for read_lba function
|
|||
|
|
|||
|
; END
|