Darwin weird or my fault?
Posted: Tue Jan 27, 2009 10:52 pm
Hello, everyone. It's me again.
I was just wondering if there was anything weird about the way Darwin boots partitions. I'm booting with Darwin because it'll boot both Vista and Leopard with minimal fuss. It also brings up an entry with my OS's test partition, but whenever I boot from it my bootloader locks up and dies. I know it at least partially works, because the exact same bootloader will run off of a USB flash stick (which includes a basic partition table with a default MBR) and I wasn't sure whether Darwin does something different that Vista's boot sector can handle but mine can't, or if Darwin's completely normal and I screwed up somewhere. Due to the fact that Darwin (and Leopard) technically shouldn't be on this computer, I'm tending towards Darwin being weird, but it wouldn't surprise me in any way if it was my fault...
Here's a dump of my latest boot sector code, since it's changed a bit since my other thread...
I was just wondering if there was anything weird about the way Darwin boots partitions. I'm booting with Darwin because it'll boot both Vista and Leopard with minimal fuss. It also brings up an entry with my OS's test partition, but whenever I boot from it my bootloader locks up and dies. I know it at least partially works, because the exact same bootloader will run off of a USB flash stick (which includes a basic partition table with a default MBR) and I wasn't sure whether Darwin does something different that Vista's boot sector can handle but mine can't, or if Darwin's completely normal and I screwed up somewhere. Due to the fact that Darwin (and Leopard) technically shouldn't be on this computer, I'm tending towards Darwin being weird, but it wouldn't surprise me in any way if it was my fault...
Here's a dump of my latest boot sector code, since it's changed a bit since my other thread...
Code: Select all
;; Stage 1 bootloader for Firebird O.S
;; FAT32 Partition version
;; Version 2.2
org 0x7C00
bits 16
BS_jmpBoot:
jmp start
nop
;; FAT fields, labeled here for convenience
BS_OEMName:
dd 16843009
dd 16843009
BPB_BytsPerSec:
dw 257
BPB_SecPerClus:
db 1
BPB_ResvdSecCnt:
dw 257
BPB_NumFATs:
db 1
BPB_RootEntCnt:
dw 257
BPB_TotSec16:
dw 257
BPB_Media:
db 1
BPB_FATSz16:
dw 257
BPB_SecPerTrk:
dw 257
BPB_NumHeads:
dw 257
BPB_HiddSec:
dd 16843009
BPB_TotSec32:
dd 16843009
BPB_FATSz32:
dd 16843009
BPB_ExtFlags:
;; Bits 0-3 = Active FAT, 7 = !FAT mirroring
dw 257
BPB_FSVer:
dw 257
BPB_RootClus:
dd 16843009
BPB_FSInfo:
dw 257
BPB_BkBootSec:
dw 257
BPB_Reserved:
dd 16843009
dd 16843009
dd 16843009
BS_DrvNum:
db 1
BS_Reseved1:
db 1
BS_BootSig:
db 1
BS_VolID:
dd 16843009
BS_VolLab:
dd 16843009
dd 16843009
dw 257
db 1
BS_FilSysType:
dd 16843009
dd 16843009
start:
;; save DS so we can get the value at ds:si later
push ds
pop fs
;; Set CS, DS, ES, & SS to a known value
;; I used eax because I want to have the upper word cleared later
xor eax, eax
mov ds, ax
mov es, ax
jmp 0:loadCS
loadCS:
;; set up the stack ( a temporary 512-byte stack )
mov ss, ax
mov sp, 0x8000
mov bp, sp
;; LBA packet
mov [LBAindex+4], eax
mov al, 16
mov [LBApacket], ax
;; Save the boot drive
;; BootDrive = dl
push dx
;; check if we're on a partition
push si
xor al, al
call readDiskToBP
;; Now to compare.
;; If the sector we just loaded == this code,
;; we're on a flat disk. Else, let's assume a partition
mov si, 0x7C00
mov di, bp
mov cx, 512
repe cmpsb
pop si
;; get the partition start from the MBR
mov ecx, [fs:si+8]
jne .partitioned
;; if we're here, it's not actually partitioned,
;; so clear the 'offset'
xor ecx, ecx
.partitioned:
;; calculate the first data sector
;; FirstDataSector = BPB_NumFATs * BPB_FATSz32 + BPB_ResvdSecCnt + partitionStart
mov al, [BPB_NumFATs]
mul dword [BPB_FATSz32]
movzx ebx, word [BPB_ResvdSecCnt]
add eax, ebx
add eax, ecx
push eax
;; now let's get the location of the first FAT
;; FATsector = BPB_ResvdSecCnt + partitionStart
movzx eax, word [BPB_ResvdSecCnt]
add eax, ecx
push eax
;; BytsPerCluster = BPB_BytsPerSec * BPB_SecPerClus
mov ax, [BPB_BytsPerSec]
mul word [BPB_SecPerClus]
push eax
;; FATClusterMask = 0x0FFFFFFF
mov eax, 0x0FFFFFFF
push eax
;; FATEoFMask = 2nd 'cluster' value & (bitwise) FATClusterMask
;; load the FAT
mov eax, [FATsector]
call readDiskToBP
;; Get the second cluster's value
mov eax, [bp+4]
and eax, [FATClusterMask]
push eax
;; CurrentCluster = BPB_RootClus
mov eax, [BPB_RootClus]
push eax
;; Fortunately, clusters are relative to FirstDataSector
;; Reserve the LBA packet,
;; (even though we've already been using it)
sub sp, byte 16
mov di, bp
;; Stack is now as follows:
BootDrive equ BP- 2
FirstDataSector equ BP- 6
FATsector equ BP-10
BytsPerCluster equ BP-14
FATClusterMask equ BP-18
FATEoFMask equ BP-22
CurrentCluster equ BP-26
LBAindex equ BP-36
LBAseg equ BP-38
LBAaddr equ BP-40
LBAcount equ BP-42
LBApacket equ BP-44
LBApacketAddr equ 0x8000-44
;; Load the first root directory cluster
call readCluster
;; parse first cluster to see if it has what we want
nextDirCluster:
;; Set bx to the end of the cluster
mov bx, [BytsPerCluster]
add bx, bp
;; ax = 0x8000 - sizeof(FAT_DIR_entry)
;; this simplifies the upcoming loop a bit
lea ax, [bp-32]
findloop:
;; move to next entry
add ax, 32
;; check if we're at the end of the cluster
cmp ax, bx
;; if so, handle it
jz notFound
;; I got 99 problems, bein' found is one.
;; If you're havin' directory problems,
;; I feel bad for you, son.
;; (Too much?)
;; else let's check the entry
mov si, ax
mov di, fileName
mov cx, 11
;; compare names
repe cmpsb
;; if not the same, try next entry
jnz findloop
;; file found!
;; +9 and +15 because SI is already 11 bytes into the entry
mov ax, [si+9]
sal eax, 16
mov ax, [si+15]
;; eax = cluster of file
mov [CurrentCluster], eax
mov di, bp
loadFileLoop:
;; if we're already at the EoF, this won't read anything
call readCluster
;; it will, however, set the carry flag to indicate that we're at the EoF
;; so if( !cf )
;; keep loading clusters
jnc loadFileLoop
;; else
;; jump to the second stage
jmp bp
notFound:
;; try reading the next cluster
call readCluster
jnc nextDirCluster
;; if this was the last one
;; show an error
mov si, noFile
jmp putStr
;; A few useful routines
readDiskToBP:
mov cx, sp
mov sp, LBApacketAddr
push cx
mov bx, bp
mov cl, 1
call readDiskLBA
pop sp
ret
;; readCluster :: ES:DI = dest
readCluster:
;; Check if we're already at the EoF
mov eax, [CurrentCluster]
and eax, [FATClusterMask]
cmp eax, [FATEoFMask]
;; if so, bail
jz eofLoad
;; Get the first sector of the cluster
;; Sector = (cluster - 2) * BPB_SecPerClus + FirstDataSector
sub eax, 2
movzx ebx, byte [BPB_SecPerClus]
mul ebx
add eax, [FirstDataSector]
;; eax = first sector of cluster
;; fetch the cluster and put it where the user wanted
mov bx, di
mov cl, [BPB_SecPerClus]
call readDiskLBA
;; increment the destination pointer
add di, [BytsPerCluster]
;; we've trashed eax, so let's get it back
;; so we can store the next cluster
mov eax, [CurrentCluster]
;; now, let's get the number of
;; cluster pointers in a sector
movzx ebx, word [BPB_BytsPerSec]
shr bx, 2
;; now that we have that, let's figure
;; out the offset, in sectors, of the
;; current cluster pointer
xor edx, edx
div eax, ebx
;; now that we have those, let's save the clusterpointer index
push dx
;; add in the location of the first FAT sector
;; to get the location on disk
add eax, [FATsector]
;; figure out where to put it
mov bx, 0x7C00
sub bx, [BPB_BytsPerSec]
mov si, bx
mov cl, 1
call readDiskLBA
pop dx
;; dx = index of desired clusterpointer
;; let's figure out the memory offset
;; for the cluster pointer
sal dx, 2
add si, dx
;; load the next cluster
mov eax, [si]
;; and set it to the new current cluster
mov [CurrentCluster], eax
;; clear the EoF flag and return
clc
ret
eofLoad:
;; set the EoF flag and return
stc
ret
readDiskLBA:
push si
;; Set the LBA packet parameters
mov [LBAindex], eax
mov [LBAcount], cl
mov [LBAseg], es
mov [LBAaddr], bx
;; set the error count to 0
xor al, al
push ax
.tryLoop:
mov si, LBApacketAddr
mov ah, 0x42
mov dl, [BootDrive]
int 13h
jc .readError
pop ax
pop si
ret
.readError:
;; whoops! there was an error reading the drive!
;; Let's check and see if we've already tried too many times
;; grab the value
pop ax
inc al
push ax
;; It just so happens that the number of times we want to loop
;; also can be used as the bit mask for comparing. How useful!
and al, 0x04
jz .tryLoop
freeze:
;; Too many failures, display an error and freeze
mov si, readFail
putStr:
;; ah = useTeletype
mov ah, 0x0E
;; bh = use page 0, bl = useless in Bochs
xor bx, bx
.loopPut:
;; lodsb == mov al, si \ inc si
;; too bad it doesn't do "or al, al" also
lodsb
;; check if al == 0
or al, al
;; if so, we're done
.here
jz .here
;; otherwise, let's put it
int 10h
;; and go again for the next character
jmp .loopPut
;; Some basic data
readFail:
db "Drive read failed!", 0
noFile:
db "No FBOOT.SYS!", 0
fileName:
db "FBOOT SYS"
;; this is here in case I make changes
times 510-($-$$) db 0
;; Standard end of boot sector
db 0x55, 0xAA