mirror of
https://github.com/reactos/reactos.git
synced 2025-08-05 20:53:02 +00:00
584 lines
14 KiB
ArmAsm
584 lines
14 KiB
ArmAsm
![]() |
/*
|
||
|
* PROJECT: FreeLoader
|
||
|
* LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later)
|
||
|
* PURPOSE: EXTFS second stage loader
|
||
|
* COPYRIGHT: Copyright 2024-2025 Daniel Victor <ilauncherdeveloper@gmail.com>
|
||
|
*/
|
||
|
|
||
|
#define INCLUDED_ASM
|
||
|
#include "ext.S"
|
||
|
|
||
|
ExtLdrEntryPoint:
|
||
|
// Clear segment registers
|
||
|
xor eax, eax
|
||
|
mov ds, ax
|
||
|
mov es, ax
|
||
|
|
||
|
// Read root directory Inode
|
||
|
mov al, EXT_ROOT_INODE
|
||
|
call ExtReadInode
|
||
|
|
||
|
// Set file base address and search for freeldr
|
||
|
mov dword ptr BP_REL(ExtFileAddress), FREELDR_BASE
|
||
|
call ExtSetFileSegment
|
||
|
|
||
|
// Swap ES with DS segment and clear SI
|
||
|
call SwapESWithDS
|
||
|
xor si, si
|
||
|
|
||
|
// Set the directory entries size limit
|
||
|
mov eax, EXT_INODE_DATA_SIZE_LIMIT
|
||
|
|
||
|
// Get directory entries size
|
||
|
mov edx, dword ptr BP_REL(ExtFileSize)
|
||
|
|
||
|
// Set the buffer size with limit
|
||
|
cmp edx, eax
|
||
|
jbe .FLoop2
|
||
|
mov edx, eax
|
||
|
.FLoop2:
|
||
|
// Load directory Inode
|
||
|
mov eax, dword ptr ds:[si]
|
||
|
|
||
|
// If the buffer reached the end then abort the search
|
||
|
cmp si, dx
|
||
|
jae FreeLdrPrintFileNotFound
|
||
|
|
||
|
// Save SI
|
||
|
push si
|
||
|
|
||
|
// If the Inode is zero then skip this entry
|
||
|
test eax, eax
|
||
|
jz .FSkip2
|
||
|
|
||
|
mov di, offset FreeLdrFileName // Load target filename address
|
||
|
mov cx, offset FreeLdrFileNameEnd - FreeLdrFileName // Length of filename to compare
|
||
|
cmp byte ptr ds:[si + EXT_DIRECTORY_ENTRY_NAME_LENGTH_OFFSET], cl // Compare if both names have the same length
|
||
|
jnz .FSkip2 // Skip this entry if yes
|
||
|
add si, EXT_DIRECTORY_ENTRY_NAME_OFFSET // Move to filename in entry
|
||
|
repe cmpsb // Compare filenames
|
||
|
pop si // Restore SI
|
||
|
jz LoadFreeLdr // Found matching file
|
||
|
push si // Save SI
|
||
|
|
||
|
.FSkip2:
|
||
|
// Restore SI
|
||
|
pop si
|
||
|
|
||
|
// Move to next directory entry
|
||
|
add si, word ptr ds:[si + EXT_DIRECTORY_ENTRY_SIZE_OFFSET]
|
||
|
|
||
|
jmp .FLoop2
|
||
|
|
||
|
ExtFileIndirectPointer:
|
||
|
.long 0
|
||
|
ExtFileIndirectPointerDouble:
|
||
|
.long 0
|
||
|
|
||
|
SwapESWithDS:
|
||
|
// Swap ES and DS
|
||
|
push es
|
||
|
push ds
|
||
|
pop es
|
||
|
pop ds
|
||
|
ret
|
||
|
|
||
|
ExtSetFileSegment:
|
||
|
mov ebx, dword ptr BP_REL(ExtFileAddress) // Get the EXT file address
|
||
|
shr ebx, 4 // Shift four bits to the right to get segment
|
||
|
jmp .ExtSkip
|
||
|
ExtSetInodeSegment:
|
||
|
mov bx, EXT_INODE_ADDRESS / 16 // Get the EXT inode address
|
||
|
jmp .ExtSkip
|
||
|
ExtSetBlockSegment:
|
||
|
mov bx, EXT_BLOCK_ADDRESS / 16 // Get the EXT block address
|
||
|
jmp .ExtSkip
|
||
|
ExtSetBlock2Segment:
|
||
|
mov bx, EXT_BLOCK2_ADDRESS / 16 // Get the EXT second block address
|
||
|
jmp .ExtSkip
|
||
|
ExtSetBlock3Segment:
|
||
|
mov bx, EXT_BLOCK3_ADDRESS / 16 // Get the EXT third block address
|
||
|
jmp .ExtSkip
|
||
|
ExtSetBlock4Segment:
|
||
|
mov bx, EXT_BLOCK4_ADDRESS / 16 // Get the EXT fourth block address
|
||
|
jmp .ExtSkip
|
||
|
ExtSetBlock5Segment:
|
||
|
mov bx, EXT_BLOCK5_ADDRESS / 16 // Get the EXT fifth block address
|
||
|
jmp .ExtSkip
|
||
|
ExtSetBlock6Segment:
|
||
|
mov bx, EXT_BLOCK6_ADDRESS / 16 // Get the EXT sixth block address
|
||
|
jmp .ExtSkip
|
||
|
ExtSetBlock7Segment:
|
||
|
mov bx, EXT_BLOCK7_ADDRESS / 16 // Get the EXT seventh block address
|
||
|
.ExtSkip:
|
||
|
mov es, bx // Set ES
|
||
|
xor bx, bx // Clear BX
|
||
|
ret
|
||
|
|
||
|
ExtUpdateFileSize:
|
||
|
mov eax, dword ptr BP_REL(ExtBlockSizeInBytes)
|
||
|
|
||
|
ExtAdjustFileSize:
|
||
|
// Update the file size
|
||
|
sub dword ptr BP_REL(ExtFileSizeState), eax
|
||
|
add dword ptr BP_REL(ExtFileAddress), eax
|
||
|
ret
|
||
|
|
||
|
ExtInodeDetectExtentsFlag:
|
||
|
push eax
|
||
|
mov eax, es:[si + EXT_INODE_FLAGS_OFFSET]
|
||
|
test eax, EXT_INODE_FLAG_EXTENTS
|
||
|
pop eax
|
||
|
ret
|
||
|
|
||
|
ExtFileReadBlocks:
|
||
|
// If the block count is zero then do nothing
|
||
|
test bx, bx
|
||
|
jz .FRDone
|
||
|
|
||
|
// Read the block
|
||
|
call ExtFileReadBlock
|
||
|
|
||
|
// Go to the next block and decrement the block count
|
||
|
inc eax
|
||
|
dec bx
|
||
|
|
||
|
// Loop until all blocks are read
|
||
|
jmp ExtFileReadBlocks
|
||
|
.FRDone:
|
||
|
ret
|
||
|
|
||
|
ExtSafeReadBlock:
|
||
|
pushad
|
||
|
|
||
|
// If the block is zero then just clear the block
|
||
|
test eax, eax
|
||
|
jz .NullBlk
|
||
|
|
||
|
// Read the block safely
|
||
|
call ExtReadBlock
|
||
|
jmp .RBDone
|
||
|
.NullBlk:
|
||
|
// If the block is 0 then just clear the block
|
||
|
mov di, bx
|
||
|
xor ax, ax
|
||
|
mov cx, word ptr BP_REL(ExtBlockSizeInBytes)
|
||
|
rep stosb
|
||
|
.RBDone:
|
||
|
popad
|
||
|
ret
|
||
|
|
||
|
// Reads a block from the EXT file
|
||
|
// EAX has the block to read
|
||
|
// ExtFileAddress has the address to write to
|
||
|
ExtFileReadBlock:
|
||
|
push es
|
||
|
pushad
|
||
|
|
||
|
// Set the EXT file segment
|
||
|
call ExtSetFileSegment
|
||
|
|
||
|
// Read the block safely
|
||
|
call ExtSafeReadBlock
|
||
|
|
||
|
// Update the file size
|
||
|
call ExtUpdateFileSize
|
||
|
|
||
|
// Exit
|
||
|
popad
|
||
|
pop es
|
||
|
ret
|
||
|
|
||
|
ExtReadEntireFileDone:
|
||
|
push eax // Save EAX register
|
||
|
mov eax, dword ptr BP_REL(ExtBlockSizeInBytes) // Load block size in bytes into EAX
|
||
|
cmp dword ptr BP_REL(ExtFileSizeState), eax // Compare file size state with block size
|
||
|
pop eax // Restore EAX register
|
||
|
ret // Return from procedure
|
||
|
|
||
|
// Loops over the block pointers and reads the blocks
|
||
|
// until the file size state is zero
|
||
|
ExtReadEntireFileLoop:
|
||
|
call ExtReadEntireFileDone
|
||
|
jb .RDone
|
||
|
|
||
|
mov eax, dword ptr es:[si]
|
||
|
|
||
|
// Read the block safely
|
||
|
call ExtFileReadBlock
|
||
|
|
||
|
add si, EXT_POINTER_SIZE
|
||
|
loop ExtReadEntireFileLoop
|
||
|
.RDone:
|
||
|
ret
|
||
|
|
||
|
// Reads the double indirect block data
|
||
|
// This function is used to read the blocks that are not contiguous
|
||
|
// to the blocks that are pointed by the indirect block
|
||
|
ExtReadEntireFileIndirectDouble:
|
||
|
push es
|
||
|
pushad
|
||
|
|
||
|
// Check if there is no more blocks to read
|
||
|
call ExtReadEntireFileDone
|
||
|
jb .RDone4
|
||
|
|
||
|
// Reset block offset
|
||
|
xor si, si
|
||
|
|
||
|
// Load the double indirect block address
|
||
|
mov eax, dword ptr BP_REL(ExtFileIndirectPointerDouble)
|
||
|
call ExtSetBlock2Segment
|
||
|
call ExtSafeReadBlock
|
||
|
|
||
|
// Load the number of pointers per block
|
||
|
mov ecx, dword ptr BP_REL(ExtPointersPerBlock)
|
||
|
.RLoop4:
|
||
|
// Check if there is no more blocks to read
|
||
|
call ExtReadEntireFileDone
|
||
|
jb .RDone4
|
||
|
|
||
|
// Get the block address
|
||
|
mov eax, dword ptr es:[si]
|
||
|
|
||
|
// Read the indirect block
|
||
|
call ExtReadEntireFileIndirect
|
||
|
|
||
|
// Increment block offset
|
||
|
add si, EXT_POINTER_SIZE
|
||
|
|
||
|
// Loop until all blocks are read
|
||
|
loop .RLoop4
|
||
|
.RDone4:
|
||
|
popad
|
||
|
pop es
|
||
|
ret
|
||
|
|
||
|
// Reads the indirect block data
|
||
|
ExtReadEntireFileIndirect:
|
||
|
push es
|
||
|
pushad
|
||
|
|
||
|
// Check if there is no more blocks to read
|
||
|
call ExtReadEntireFileDone
|
||
|
jb .RDone3
|
||
|
|
||
|
// Reset block offset
|
||
|
xor si, si
|
||
|
|
||
|
// Set the block segment
|
||
|
call ExtSetBlockSegment
|
||
|
|
||
|
// Read the block safely
|
||
|
call ExtSafeReadBlock
|
||
|
|
||
|
// Read the blocks
|
||
|
mov ecx, dword ptr BP_REL(ExtPointersPerBlock)
|
||
|
call ExtReadEntireFileLoop
|
||
|
|
||
|
.RDone3:
|
||
|
popad
|
||
|
pop es
|
||
|
ret
|
||
|
|
||
|
// Reads the direct block data
|
||
|
ExtReadEntireFileDirect:
|
||
|
push es
|
||
|
pushad
|
||
|
|
||
|
// If there is no more blocks to read then abort it
|
||
|
call ExtReadEntireFileDone
|
||
|
jb .RDone2
|
||
|
|
||
|
// Move to block pointer in Inode
|
||
|
add si, EXT_INODE_BLOCK_POINTER_OFFSET
|
||
|
xor ecx, ecx
|
||
|
mov cl, EXT_INODE_BLOCKS
|
||
|
|
||
|
// Set the correct segment
|
||
|
call ExtSetInodeSegment
|
||
|
|
||
|
// Read the blocks
|
||
|
call ExtReadEntireFileLoop
|
||
|
|
||
|
.RDone2:
|
||
|
popad
|
||
|
pop es
|
||
|
ret
|
||
|
|
||
|
ExtExtentsLevelSegmentFunctionTable:
|
||
|
.word ExtSetBlockSegment
|
||
|
.word ExtSetBlock2Segment
|
||
|
.word ExtSetBlock3Segment
|
||
|
.word ExtSetBlock4Segment
|
||
|
.word ExtSetBlock5Segment
|
||
|
.word ExtSetBlock6Segment
|
||
|
|
||
|
ExtExtentsLevelFunctionTable:
|
||
|
.word ExtReadEntireFileExtentsLevel0
|
||
|
.word ExtReadEntireFileExtentsLevelX
|
||
|
.word ExtReadEntireFileExtentsLevelX
|
||
|
.word ExtReadEntireFileExtentsLevelX
|
||
|
.word ExtReadEntireFileExtentsLevelX
|
||
|
.word ExtReadEntireFileExtentsLevelX
|
||
|
|
||
|
ExtReadEntireFileExtentsLevelX:
|
||
|
push es
|
||
|
pushad
|
||
|
|
||
|
// Load extent header entries
|
||
|
mov cx, word ptr es:[si + EXT_EXTENT_HEADER_ENTRIES_OFFSET]
|
||
|
.ExtentsLoopX:
|
||
|
// Check if there is no more blocks to read
|
||
|
call ExtReadEntireFileDone
|
||
|
jb .ExtentsDoneX
|
||
|
|
||
|
// Go to next extent
|
||
|
add si, EXT_EXTENT_SIZE
|
||
|
|
||
|
// Save current state
|
||
|
push es
|
||
|
pushad
|
||
|
|
||
|
// Read the extent block
|
||
|
mov eax, dword ptr es:[si + EXT_EXTENT_INDEX_LEAF_OFFSET]
|
||
|
|
||
|
// Set block segment and read block safely
|
||
|
call ExtSetBlock7Segment
|
||
|
call ExtSafeReadBlock
|
||
|
|
||
|
// Reset SI for calculations
|
||
|
xor si, si
|
||
|
|
||
|
// Calculate function table offset
|
||
|
mov bp, word ptr es:[si + EXT_EXTENT_HEADER_DEPTH_OFFSET]
|
||
|
shl bp, 1
|
||
|
|
||
|
// Call the segment and level functions
|
||
|
call word ptr cs:ExtExtentsLevelSegmentFunctionTable[bp]
|
||
|
call ExtSafeReadBlock
|
||
|
call word ptr cs:ExtExtentsLevelFunctionTable[bp]
|
||
|
|
||
|
// Restore state and continue loop
|
||
|
popad
|
||
|
pop es
|
||
|
|
||
|
loop .ExtentsLoopX
|
||
|
.ExtentsDoneX:
|
||
|
popad
|
||
|
pop es
|
||
|
ret
|
||
|
|
||
|
ExtReadEntireFileExtentsLevel0:
|
||
|
push es
|
||
|
pushad
|
||
|
|
||
|
// Load extent header entries
|
||
|
mov cx, word ptr es:[si + EXT_EXTENT_HEADER_ENTRIES_OFFSET]
|
||
|
|
||
|
.ExtentsLoop0:
|
||
|
// Check if there is no more blocks to read
|
||
|
call ExtReadEntireFileDone
|
||
|
jb .ExtentsDone0
|
||
|
|
||
|
// Go to next extent
|
||
|
add si, EXT_EXTENT_SIZE
|
||
|
|
||
|
// Load extent length
|
||
|
mov bx, word ptr es:[si + EXT_EXTENT_LENGTH_OFFSET]
|
||
|
and ebx, HEX(FFFF)
|
||
|
|
||
|
// Check if extent is sparse
|
||
|
cmp bx, EXT_EXTENT_MAX_LENGTH
|
||
|
jbe .NotSparse
|
||
|
|
||
|
// Adjust sparse extent length
|
||
|
sub bx, EXT_EXTENT_MAX_LENGTH
|
||
|
|
||
|
push es
|
||
|
.SparseLoop:
|
||
|
// Check if the sparse blocks reached the end
|
||
|
test bx, bx
|
||
|
jz .SparseDone
|
||
|
|
||
|
xor eax, eax
|
||
|
call ExtFileReadBlock
|
||
|
|
||
|
// Decrement the block count
|
||
|
dec bx
|
||
|
jmp .SparseLoop
|
||
|
.SparseDone:
|
||
|
pop es
|
||
|
jmp .ExtentsSkip0
|
||
|
|
||
|
.NotSparse:
|
||
|
// Read blocks from extent start
|
||
|
mov eax, dword ptr es:[si + EXT_EXTENT_START_OFFSET]
|
||
|
call ExtFileReadBlocks
|
||
|
|
||
|
.ExtentsSkip0:
|
||
|
loop .ExtentsLoop0
|
||
|
.ExtentsDone0:
|
||
|
popad
|
||
|
pop es
|
||
|
ret
|
||
|
|
||
|
ExtReadEntireFileExtents:
|
||
|
push es
|
||
|
pushad
|
||
|
|
||
|
// Add the extent header offset
|
||
|
add si, EXT_INODE_BLOCK_POINTER_OFFSET
|
||
|
|
||
|
// Load extent header depth
|
||
|
mov ax, word ptr es:[si + EXT_EXTENT_HEADER_DEPTH_OFFSET]
|
||
|
|
||
|
// Check if the file has more than the maximum allowed levels
|
||
|
cmp ax, EXT_EXTENT_MAX_LEVEL
|
||
|
ja FreeLdrPrintFileBig
|
||
|
|
||
|
// If the extent header depth is zero then this is a level 0 extent
|
||
|
test ax, ax
|
||
|
jz .Level0
|
||
|
|
||
|
// Call the recursive function
|
||
|
.LevelX:
|
||
|
call ExtReadEntireFileExtentsLevelX
|
||
|
|
||
|
// Jump to the end of the function
|
||
|
jmp .FEDone
|
||
|
|
||
|
// Level 0 extent
|
||
|
.Level0:
|
||
|
// Call the level 0 extent function
|
||
|
call ExtReadEntireFileExtentsLevel0
|
||
|
|
||
|
// End of the function
|
||
|
.FEDone:
|
||
|
popad
|
||
|
pop es
|
||
|
ret
|
||
|
|
||
|
// Reads the entire file in EXTFS
|
||
|
ExtReadEntireFile:
|
||
|
// Set the correct Inode offset
|
||
|
mov si, word ptr BP_REL(ExtReadInodeIndexOffset)
|
||
|
|
||
|
// Set file size
|
||
|
mov ecx, dword ptr es:[si + EXT_INODE_SIZE_OFFSET]
|
||
|
mov dword ptr BP_REL(ExtFileSize), ecx
|
||
|
|
||
|
// If file size is zero then abort it
|
||
|
test ecx, ecx
|
||
|
jz FreeLdrPrintFileZero
|
||
|
|
||
|
// Set rounded up file size
|
||
|
add ecx, dword ptr BP_REL(ExtBlockSizeInBytes)
|
||
|
dec ecx
|
||
|
mov dword ptr BP_REL(ExtFileSizeState), ecx
|
||
|
|
||
|
// Use extents method if necessary
|
||
|
call ExtInodeDetectExtentsFlag
|
||
|
jz .NoExtents
|
||
|
|
||
|
// Call the extents method
|
||
|
call ExtReadEntireFileExtents
|
||
|
ret
|
||
|
|
||
|
.NoExtents:
|
||
|
// Set Indirect pointer
|
||
|
mov eax, dword ptr es:[si + (EXT_INODE_BLOCK_POINTER_OFFSET + (EXT_INODE_BLOCKS * EXT_POINTER_SIZE))]
|
||
|
mov dword ptr BP_REL(ExtFileIndirectPointer), eax
|
||
|
|
||
|
// Set Double indirect pointer
|
||
|
mov eax, dword ptr es:[si + (EXT_INODE_BLOCK_POINTER_OFFSET + (EXT_INODE_BLOCKS * EXT_POINTER_SIZE) + EXT_POINTER_SIZE)]
|
||
|
mov dword ptr BP_REL(ExtFileIndirectPointerDouble), eax
|
||
|
|
||
|
call ExtReadEntireFileDirect // Read the direct blocks
|
||
|
|
||
|
mov eax, dword ptr BP_REL(ExtFileIndirectPointer) // Load the simple indirect pointer
|
||
|
call ExtReadEntireFileIndirect // Read the simple indirect blocks
|
||
|
|
||
|
call ExtReadEntireFileIndirectDouble // Read the double indirect blocks
|
||
|
ret
|
||
|
|
||
|
LoadFreeLdr:
|
||
|
// Swap ES and DS
|
||
|
call SwapESWithDS
|
||
|
|
||
|
// Show output loading status
|
||
|
push si
|
||
|
mov si, offset msgLoadingFreeLdr
|
||
|
call PutChars
|
||
|
pop si
|
||
|
|
||
|
// Load Inode at directory entry
|
||
|
mov eax, dword ptr es:[si]
|
||
|
call ExtReadInode
|
||
|
mov si, word ptr BP_REL(ExtReadInodeIndexOffset)
|
||
|
|
||
|
// Get Inode type
|
||
|
mov ax, word ptr es:[si]
|
||
|
and ax, EXT_INODE_TYPE_MASK
|
||
|
|
||
|
// If it's not a regular file then abort it
|
||
|
cmp ax, EXT_INODE_TYPE_REGULAR
|
||
|
jnz FreeLdrPrintRegFileError
|
||
|
|
||
|
// Load Freeldr at FREELDR_BASE
|
||
|
mov dword ptr BP_REL(ExtFileAddress), FREELDR_BASE
|
||
|
call ExtReadEntireFile
|
||
|
|
||
|
// Restore the boot drive and partition
|
||
|
mov dl, byte ptr BP_REL(BootDrive)
|
||
|
mov dh, byte ptr BP_REL(BootPartition)
|
||
|
|
||
|
// Transfer execution to the bootloader
|
||
|
ljmp16 FREELDR_BASE / 16, 0
|
||
|
|
||
|
FreeLdrPrintFileBig:
|
||
|
// Make DS correct, display it and reboot
|
||
|
call SwapESWithDS
|
||
|
mov si, offset msgFreeLdrBig
|
||
|
jmp DisplayItAndReboot
|
||
|
|
||
|
FreeLdrPrintFileZero:
|
||
|
// Make DS correct, display it and reboot
|
||
|
call SwapESWithDS
|
||
|
mov si, offset msgFreeLdrZero
|
||
|
jmp DisplayItAndReboot
|
||
|
|
||
|
FreeLdrPrintFileNotFound:
|
||
|
// Make DS correct, display it and reboot
|
||
|
call SwapESWithDS
|
||
|
mov si, offset msgFreeLdr
|
||
|
jmp DisplayItAndReboot
|
||
|
|
||
|
FreeLdrPrintRegFileError:
|
||
|
// Make DS correct, display it and reboot
|
||
|
call SwapESWithDS
|
||
|
mov si, offset msgFreeLdrNotRegularFile // FreeLdr not found message
|
||
|
jmp DisplayItAndReboot
|
||
|
|
||
|
FreeLdrFileName:
|
||
|
.ascii "freeldr.sys"
|
||
|
FreeLdrFileNameEnd:
|
||
|
|
||
|
msgFreeLdrZero:
|
||
|
.ascii "freeldr.sys size is zero", CR, LF, NUL
|
||
|
msgFreeLdrBig:
|
||
|
.ascii "freeldr.sys has many extent levels", CR, LF, NUL
|
||
|
msgFreeLdr:
|
||
|
.ascii "freeldr.sys not found", CR, LF, NUL
|
||
|
msgFreeLdrNotRegularFile:
|
||
|
.ascii "freeldr.sys is not a regular file", CR, LF, NUL
|
||
|
msgLoadingFreeLdr:
|
||
|
.ascii "Loading FreeLoader...", CR, LF, NUL
|
||
|
|
||
|
.org (EXTLDR_BOOTSECTOR_SIZE - 2)
|
||
|
.word HEX(AA55) // BootSector signature
|
||
|
|
||
|
.endcode16
|
||
|
|
||
|
END
|