mirror of
https://github.com/CloverHackyColor/CloverBootloader.git
synced 2024-12-10 14:23:31 +01:00
7c0aa811ec
Signed-off-by: Sergey Isakov <isakov-sl@bk.ru>
667 lines
15 KiB
ArmAsm
667 lines
15 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@
|
|
;
|
|
; Partition Boot Loader: boot1f32
|
|
;
|
|
; This program is designed to reside in sector 0 of a FAT32 partition.
|
|
; It expects that the MBR has left the drive number in DL
|
|
; and a pointer to the partition entry in SI.
|
|
;
|
|
; This version requires a BIOS with EBIOS (LBA) support.
|
|
;
|
|
; This code is written for the NASM assembler.
|
|
; nasm boot1f32.s -o boot1f32
|
|
;
|
|
; dd if=origbs of=newbs skip=3 seek=3 bs=1 count=87 conv=notrunc
|
|
;
|
|
|
|
;
|
|
; This version of boot1f32 tries to find a stage2 boot file in the root folder.
|
|
;
|
|
; Written by mackerintel on 2009-01-26
|
|
;
|
|
|
|
;
|
|
; Set to 1 to enable obscure debug messages.
|
|
;
|
|
DEBUG EQU 0
|
|
|
|
;
|
|
; Set to 1 to enable unused code.
|
|
;
|
|
UNUSED EQU 0
|
|
|
|
;
|
|
; Set to 1 to enable verbose mode.
|
|
;
|
|
VERBOSE EQU 0
|
|
|
|
;
|
|
; Set to 1 to make this stage 1 loader expecting arguments in SI and DL registers.
|
|
;
|
|
USESIDL EQU 1
|
|
|
|
;
|
|
; Various constants.
|
|
;
|
|
NULL EQU 0
|
|
CR EQU 0x0D
|
|
LF EQU 0x0A
|
|
|
|
maxSectorCount EQU 64 ; maximum sector count for readSectors
|
|
kSectorBytes EQU 512 ; sector size in bytes
|
|
kBootSignature EQU 0xAA55 ; boot sector signature
|
|
|
|
kBoot1StackAddress EQU 0xFFF0 ; boot1 stack pointer
|
|
kBoot1LoadAddr EQU 0x7C00 ; boot1 load address
|
|
kBoot1RelocAddr EQU 0xE000 ; boot1 relocated address
|
|
|
|
kBoot2Sectors EQU (480 * 1024 - 512) / kSectorBytes ; max size of 'boot' file in sectors
|
|
kBoot2Segment EQU 0x2000 ; boot2 load segment
|
|
kBoot2Address EQU kSectorBytes ; boot2 load address
|
|
|
|
FATBUF EQU 0x7000 ; Just place for one sectors
|
|
DIRBUFSEG EQU 0x1000 ; Cluster sizes >64KB aren't supported
|
|
|
|
;
|
|
; 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
|
|
|
|
struc direntry
|
|
.nameext resb 11
|
|
.attr resb 1
|
|
.nused1 resb 8
|
|
.highclus resw 1
|
|
.nused2 resb 4
|
|
.lowclus resw 1
|
|
.size resd 1
|
|
endstruc
|
|
|
|
|
|
;
|
|
; Macros.
|
|
;
|
|
%macro jmpabs 1
|
|
push WORD %1
|
|
ret
|
|
%endmacro
|
|
|
|
%macro DebugCharMacro 1
|
|
pushad
|
|
mov al, %1
|
|
call print_char
|
|
call getc
|
|
popad
|
|
%endmacro
|
|
|
|
%macro PrintCharMacro 1
|
|
pushad
|
|
mov al, %1
|
|
call print_char
|
|
popad
|
|
%endmacro
|
|
|
|
%macro PutCharMacro 1
|
|
call print_char
|
|
%endmacro
|
|
|
|
%macro PrintHexMacro 1
|
|
call print_hex
|
|
%endmacro
|
|
|
|
%macro PrintString 1
|
|
mov si, %1
|
|
call print_string
|
|
%endmacro
|
|
|
|
%macro LogString 1
|
|
mov di, %1
|
|
call log_string
|
|
%endmacro
|
|
|
|
%if DEBUG
|
|
%define DebugChar(x) DebugCharMacro x
|
|
%define PrintChar(x) PrintCharMacro x
|
|
%define PutChar(x) PutCharMacro
|
|
%define PrintHex(x) PrintHexMacro x
|
|
%else
|
|
%define DebugChar(x)
|
|
%define PrintChar(x)
|
|
%define PutChar(x)
|
|
%define PrintHex(x)
|
|
%endif
|
|
|
|
;--------------------------------------------------------------------------
|
|
; Start of text segment.
|
|
|
|
SEGMENT .text
|
|
|
|
ORG kBoot1LoadAddr
|
|
|
|
jmp start
|
|
times 3-($-$$) nop
|
|
|
|
gOEMName times 8 db 0 ;OEMNAME
|
|
gBPS dw 0
|
|
gSPC db 0
|
|
gReservedSectors dw 0
|
|
gNumFats db 0
|
|
gCrap1 times 11 db 0
|
|
gPartLBA dd 0
|
|
gPartSize dd 0
|
|
gSectPerFat dd 0
|
|
gCrap2 times 4 db 0
|
|
gRootCluster dd 0
|
|
gCrap3 times 16 db 0
|
|
|
|
gBIOSDriveNumber db 0
|
|
gExtInfo times 25 db 0
|
|
gFileName db "BOOT " ; Used as a magic string in boot0
|
|
|
|
;--------------------------------------------------------------------------
|
|
; Boot code is loaded at 0:7C00h.
|
|
;
|
|
start:
|
|
;
|
|
; set up the stack to grow down from kBoot1StackSegment:kBoot1StackAddress.
|
|
; Interrupts should be off while the stack is being manipulated.
|
|
;
|
|
cli ; interrupts off
|
|
xor eax, eax ; zero ax
|
|
mov ss, ax ; ss <- 0
|
|
mov sp, kBoot1StackAddress ; sp <- top of stack
|
|
sti ; reenable interrupts
|
|
|
|
mov ds, ax ; ds <- 0
|
|
mov es, ax ; es <- 0
|
|
|
|
;
|
|
; Initializing global variables.
|
|
;
|
|
mov ax, word [gReservedSectors]
|
|
%if USESIDL
|
|
add eax, [si + part.lba]
|
|
%else
|
|
add eax, [gPartLBA]
|
|
%endif
|
|
mov [gPartLBA], eax ; save the current FAT LBA offset
|
|
%if USESIDL
|
|
mov [gBIOSDriveNumber], dl ; save BIOS drive number
|
|
%endif
|
|
xor eax,eax
|
|
mov al, [gNumFats]
|
|
mul dword [gSectPerFat]
|
|
mov [gSectPerFat], eax
|
|
|
|
;--------------------------------------------------------------------------
|
|
; Find stage2 boot file in a FAT32 Volume's root folder.
|
|
;
|
|
|
|
findRootBoot:
|
|
|
|
setBootFile:
|
|
mov cx, 2000 ; loop counter = max 2000 miliseconds in total
|
|
.loop:
|
|
mov ah, 0x01 ; int 0x16, Func 0x01 - get keyboard status/preview key
|
|
int 0x16
|
|
jz .wait ; no keypress - wait and loop again
|
|
xor ah, ah ; read the char from buffer to spend it
|
|
int 0x16
|
|
; have a key - ASCII is in al - put it to file name /boot<pressed key>
|
|
mov BYTE [gFileName + 4], al
|
|
jmp SHORT .bootFileSet ; try to boot
|
|
|
|
.wait:
|
|
; waith for 1 ms: int 0x15, Func 0x86 (wait for cx:dx microseconds)
|
|
push cx ; save loop counter
|
|
xor cx, cx
|
|
mov dx, 1000
|
|
mov ah, 0x86
|
|
int 0x15
|
|
pop cx ; restore loop counter
|
|
|
|
loop .loop
|
|
; no keypress so far
|
|
; change filename to /boot by putting space as 5th char
|
|
; and try to load
|
|
; mov BYTE [gFileName + 4], ' '
|
|
.bootFileSet:
|
|
|
|
|
|
%if VERBOSE
|
|
LogString(init_str)
|
|
%endif
|
|
|
|
mov eax, [gRootCluster]
|
|
|
|
nextdirclus:
|
|
mov edx, DIRBUFSEG<<4
|
|
call readCluster
|
|
jc error
|
|
xor si, si
|
|
mov bl, [gSPC]
|
|
shl bx, 9
|
|
add bx, si
|
|
|
|
nextdirent:
|
|
mov di, gFileName
|
|
push ds
|
|
push DIRBUFSEG
|
|
pop ds
|
|
mov cl, [si]
|
|
test cl, cl
|
|
jz dserror
|
|
mov cx, 11
|
|
repe cmpsb
|
|
jz direntfound
|
|
|
|
falsealert:
|
|
pop ds
|
|
add cl, 21
|
|
add si, cx
|
|
cmp si, bx
|
|
jz nextdirclus
|
|
jmp nextdirent
|
|
|
|
direntfound:
|
|
; test byte [ds:si+direntry.attr-11], 0x18
|
|
lodsb
|
|
test al, 0x18
|
|
jnz falsealert
|
|
push WORD [si + direntry.highclus - 12]
|
|
push WORD [si + direntry.lowclus - 12]
|
|
pop eax
|
|
pop ds
|
|
mov edx, (kBoot2Segment << 4) + kBoot2Address
|
|
|
|
cont_read:
|
|
push edx
|
|
call readCluster
|
|
pop edx
|
|
pushf
|
|
xor ebx,ebx
|
|
mov bl, [gSPC]
|
|
shl ebx, 9
|
|
add edx, ebx
|
|
popf
|
|
jnc cont_read
|
|
|
|
boot2:
|
|
|
|
%if DEBUG
|
|
DebugChar ('!')
|
|
%endif
|
|
|
|
%if UNUSED
|
|
;
|
|
; Waiting for a key press.
|
|
;
|
|
|
|
mov ah, 0
|
|
int 0x16
|
|
%endif
|
|
|
|
mov dl, [gBIOSDriveNumber] ; load BIOS drive number
|
|
jmp kBoot2Segment:kBoot2Address
|
|
|
|
dserror:
|
|
pop ds
|
|
|
|
error:
|
|
|
|
%if VERBOSE
|
|
LogString(error_str)
|
|
%endif
|
|
|
|
hang:
|
|
hlt
|
|
jmp hang
|
|
|
|
; readCluster - Reads cluster EAX to (EDX), updates EAX to next cluster
|
|
readCluster:
|
|
cmp eax, 0x0ffffff8
|
|
jb do_read
|
|
stc
|
|
ret
|
|
|
|
do_read:
|
|
push eax
|
|
xor ecx,ecx
|
|
dec eax
|
|
dec eax
|
|
mov cl, [gSPC]
|
|
push edx
|
|
mul ecx
|
|
pop edx
|
|
add eax, [gSectPerFat]
|
|
mov ecx, eax
|
|
xor ah,ah
|
|
mov al, [gSPC]
|
|
call readSectors
|
|
jc clusend
|
|
pop ecx
|
|
push cx
|
|
shr ecx, 7
|
|
xor ax, ax
|
|
inc ax
|
|
mov edx, FATBUF
|
|
call readSectors
|
|
jc clusend
|
|
pop si
|
|
and si, 0x7f
|
|
shl si, 2
|
|
mov eax, [FATBUF + si]
|
|
and eax, 0x0fffffff
|
|
clc
|
|
ret
|
|
|
|
clusend:
|
|
pop eax
|
|
ret
|
|
|
|
;--------------------------------------------------------------------------
|
|
; readSectors - Reads more than 127 sectors using LBA addressing.
|
|
;
|
|
; Arguments:
|
|
; AX = number of 512-byte sectors to read (valid from 1-1280).
|
|
; EDX = pointer to where the sectors should be stored.
|
|
; ECX = sector offset in partition
|
|
;
|
|
; Returns:
|
|
; CF = 0 success
|
|
; 1 error
|
|
;
|
|
readSectors:
|
|
pushad
|
|
mov bx, ax
|
|
|
|
.loop:
|
|
xor eax, eax ; EAX = 0
|
|
mov al, bl ; assume we reached the last block.
|
|
cmp bx, maxSectorCount ; check if we really reached the last block
|
|
jb .readBlock ; yes, BX < MaxSectorCount
|
|
mov al, maxSectorCount ; no, read MaxSectorCount
|
|
|
|
.readBlock:
|
|
call readLBA
|
|
sub bx, ax ; decrease remaning sectors with the read amount
|
|
jz .exit ; exit if no more sectors left to be loaded
|
|
add ecx, eax ; adjust LBA sector offset
|
|
shl ax, 9 ; convert sectors to bytes
|
|
add edx, eax ; adjust target memory location
|
|
jmp .loop ; read remaining sectors
|
|
|
|
.exit:
|
|
popad
|
|
ret
|
|
|
|
;--------------------------------------------------------------------------
|
|
; readLBA - Read sectors from a partition using LBA addressing.
|
|
;
|
|
; Arguments:
|
|
; AL = number of 512-byte sectors to read (valid from 1-127).
|
|
; EDX = pointer to where the sectors should be stored.
|
|
; ECX = sector offset in partition
|
|
; [bios_drive_number] = drive number (0x80 + unit number)
|
|
;
|
|
; Returns:
|
|
; CF = 0 success
|
|
; 1 error
|
|
;
|
|
readLBA:
|
|
pushad ; save all registers
|
|
push es ; save ES
|
|
mov bp, sp ; save current SP
|
|
|
|
;
|
|
; Convert EDX to segment:offset model and set ES:BX
|
|
;
|
|
; Some BIOSes do not like offset to be negative while reading
|
|
; from hard drives. This usually leads to "boot1: error" when trying
|
|
; to boot from hard drive, while booting normally from USB flash.
|
|
; The routines, responsible for this are apparently different.
|
|
; Thus we split linear address slightly differently for these
|
|
; capricious BIOSes to make sure offset is always positive.
|
|
;
|
|
|
|
mov bx, dx ; save offset to BX
|
|
and bh, 0x0f ; keep low 12 bits
|
|
shr edx, 4 ; adjust linear address to segment base
|
|
xor dl, dl ; mask low 8 bits
|
|
mov es, dx ; save segment to ES
|
|
|
|
;
|
|
; 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.
|
|
|
|
add ecx, [gPartLBA] ; 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
|
|
|
|
push WORD 16 ; offset 0-1, packet size
|
|
|
|
;
|
|
; INT13 Func 42 - Extended Read Sectors
|
|
;
|
|
; Arguments:
|
|
; AH = 0x42
|
|
; [bios_drive_number] = drive number (0x80 + unit number)
|
|
; 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 dl, [gBIOSDriveNumber] ; load BIOS drive number
|
|
mov si, sp
|
|
mov ah, 0x42
|
|
int 0x13
|
|
|
|
jc error
|
|
|
|
;
|
|
; 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
|
|
pop es ; restore ES
|
|
popad
|
|
ret
|
|
|
|
%if VERBOSE
|
|
|
|
;--------------------------------------------------------------------------
|
|
; Write a string with 'boot1: ' prefix to the console.
|
|
;
|
|
; Arguments:
|
|
; ES:DI pointer to a NULL terminated string.
|
|
;
|
|
; Clobber list:
|
|
; DI
|
|
;
|
|
log_string:
|
|
pushad
|
|
|
|
push di
|
|
mov si, log_title_str
|
|
call print_string
|
|
|
|
pop si
|
|
call print_string
|
|
|
|
popad
|
|
|
|
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)
|
|
|
|
.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 .loop
|
|
|
|
.exit:
|
|
ret
|
|
|
|
%endif ; VERBOSE
|
|
|
|
%if DEBUG
|
|
|
|
;--------------------------------------------------------------------------
|
|
; 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
|
|
|
|
%if UNUSED
|
|
mov al, 10 ; carriage return
|
|
call print_char
|
|
mov al, 13
|
|
call print_char
|
|
%endif ; UNUSED
|
|
|
|
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
|
|
|
|
;--------------------------------------------------------------------------
|
|
; getc - wait for a key press
|
|
;
|
|
getc:
|
|
pushad
|
|
mov ah, 0
|
|
int 0x16
|
|
popad
|
|
ret
|
|
|
|
;--------------------------------------------------------------------------
|
|
; Write a ASCII character to the console.
|
|
;
|
|
; Arguments:
|
|
; AL = ASCII character.
|
|
;
|
|
print_char:
|
|
pushad
|
|
mov bx, 1 ; BH=0, BL=1 (blue)
|
|
mov ah, 0x0e ; bios INT 10, Function 0xE
|
|
int 0x10 ; display byte in tty mode
|
|
popad
|
|
ret
|
|
|
|
%endif ; DEBUG
|
|
|
|
;--------------------------------------------------------------------------
|
|
; Static data.
|
|
;
|
|
|
|
%if VERBOSE
|
|
log_title_str db CR, LF, 'b1f: ', NULL
|
|
init_str db 'init', NULL
|
|
error_str db 'error', NULL
|
|
%endif
|
|
|
|
;--------------------------------------------------------------------------
|
|
; Pad the rest of the 512 byte sized sector 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.
|
|
|
|
pad_table_and_sig:
|
|
times 510-($-$$) db 0
|
|
dw kBootSignature
|
|
|
|
ABSOLUTE kBoot1LoadAddr + kSectorBytes
|
|
|
|
; END
|