mirror of
https://github.com/reactos/reactos.git
synced 2025-04-26 08:30:21 +00:00
726 lines
24 KiB
ArmAsm
726 lines
24 KiB
ArmAsm
/*
|
|
* PROJECT: FreeLoader
|
|
* LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later)
|
|
* PURPOSE: EXTFS volume boot sector
|
|
* COPYRIGHT: Copyright 2002-2003 Brian Palmer <brianp@sginet.com>
|
|
* Copyright 2024-2025 Daniel Victor <ilauncherdeveloper@gmail.com>
|
|
*/
|
|
|
|
#include <asm.inc>
|
|
#include <freeldr/include/arch/pc/x86common.h>
|
|
|
|
// Boot sector constants
|
|
#define BOOTSECTOR_BASE_ADDRESS HEX(7C00)
|
|
#define EXTLDR_BOOTSECTOR_SIZE 1024
|
|
#define EXT_POINTER_SIZE 4
|
|
#define EXT_EXTENT_SIZE 12
|
|
|
|
// Maximum extent values
|
|
#define EXT_EXTENT_MAX_LEVEL 5
|
|
#define EXT_EXTENT_MAX_LENGTH 32768
|
|
|
|
// Group descriptor offsets
|
|
#define EXT_GROUP_DESC_INODE_TABLE_OFFSET 8
|
|
|
|
// Extent offsets
|
|
#define EXT_EXTENT_HEADER_ENTRIES_OFFSET 2
|
|
#define EXT_EXTENT_HEADER_DEPTH_OFFSET 6
|
|
#define EXT_EXTENT_INDEX_LEAF_OFFSET 4
|
|
#define EXT_EXTENT_LENGTH_OFFSET 4
|
|
#define EXT_EXTENT_START_OFFSET 8
|
|
|
|
// Inode offsets
|
|
#define EXT_INODE_SIZE_OFFSET 4
|
|
#define EXT_INODE_FLAGS_OFFSET 32
|
|
#define EXT_INODE_BLOCK_POINTER_OFFSET 40
|
|
|
|
// Directory entry offsets
|
|
#define EXT_DIRECTORY_ENTRY_SIZE_OFFSET 4
|
|
#define EXT_DIRECTORY_ENTRY_NAME_LENGTH_OFFSET 6
|
|
#define EXT_DIRECTORY_ENTRY_NAME_OFFSET 8
|
|
|
|
// Inode flags
|
|
#define EXT_INODE_FLAG_EXTENTS HEX(80000)
|
|
|
|
// Inode blocks constants
|
|
#define EXT_INODE_BLOCKS 12
|
|
#define EXT_INODE_INDIRECT_BLOCKS 3
|
|
|
|
// Root Inode
|
|
#define EXT_ROOT_INODE 2
|
|
|
|
// Inode address
|
|
#define EXT_INODE_ADDRESS HEX(9000)
|
|
|
|
// Data block addresses
|
|
#define EXT_BLOCK_ADDRESS HEX(1000)
|
|
#define EXT_BLOCK2_ADDRESS HEX(2000)
|
|
#define EXT_BLOCK3_ADDRESS HEX(3000)
|
|
#define EXT_BLOCK4_ADDRESS HEX(4000)
|
|
#define EXT_BLOCK5_ADDRESS HEX(5000)
|
|
#define EXT_BLOCK6_ADDRESS HEX(6000)
|
|
#define EXT_BLOCK7_ADDRESS HEX(A000)
|
|
|
|
// Inode types
|
|
#define EXT_INODE_TYPE_MASK HEX(F000)
|
|
#define EXT_INODE_TYPE_REGULAR HEX(8000)
|
|
|
|
// File size limit
|
|
#define EXT_INODE_DATA_SIZE_LIMIT HEX(F000)
|
|
|
|
// Offset of functions addresses that will be used by the extldr.sys 3rd-stage bootsector
|
|
#define ExtReadBlockOffset 2
|
|
#define ExtReadInodeOffset 4
|
|
#define DisplayItAndRebootOffset 6
|
|
#define PutCharsOffset 8
|
|
|
|
// Boot sector stack constants
|
|
#define BOOTSECTOR_STACK_TEMP_VARIABLES 2
|
|
#define BOOTSECTOR_STACK_TEMP_VARIABLES_SIZE (4 * BOOTSECTOR_STACK_TEMP_VARIABLES)
|
|
#define BOOTSECTOR_STACK_OFFSET (8 + BOOTSECTOR_STACK_TEMP_VARIABLES_SIZE)
|
|
#define BOOTSECTOR_STACK_BASE (BOOTSECTOR_BASE_ADDRESS - BOOTSECTOR_STACK_OFFSET)
|
|
#define BP_REL(x) ss:[bp + (x - BOOTSECTOR_BASE_ADDRESS)]
|
|
|
|
// Temporary variables
|
|
#define ExtFileSizeState ((BOOTSECTOR_STACK_BASE + BOOTSECTOR_STACK_TEMP_VARIABLES_SIZE) - 4)
|
|
#define LBASectorsRead (ExtFileSizeState - 4)
|
|
|
|
.code16
|
|
|
|
#ifndef INCLUDED_ASM
|
|
|
|
start:
|
|
jmp short main
|
|
nop
|
|
|
|
// Fields that will be changed by the installer
|
|
BootDrive:
|
|
.byte HEX(FF)
|
|
ExtVolumeStartSector:
|
|
.long 263088 // Start sector of the ext2 volume
|
|
ExtBlockSize:
|
|
.long 2 // Block size in sectors
|
|
ExtBlockSizeInBytes:
|
|
.long 1024 // Block size in bytes
|
|
ExtPointersPerBlock:
|
|
.long 256 // Number of block pointers that can be contained in one block
|
|
ExtGroupDescSize:
|
|
.long 32 // Size of Group Descriptor
|
|
ExtFirstDataBlock:
|
|
.long 2 // First data block (2 for 1024-byte blocks, 1 for bigger sizes)
|
|
ExtInodeSize:
|
|
.long 128 // Size of Inode
|
|
ExtInodesPerGroup:
|
|
.long 2048 // Number of inodes per group
|
|
|
|
// File variables
|
|
ExtFileSize:
|
|
.long 0 // File size in bytes
|
|
ExtFileAddress:
|
|
.long FREELDR_BASE // File address
|
|
ExtFileAddressOld:
|
|
.long FREELDR_BASE // Old file address
|
|
|
|
// Inode variables
|
|
ExtReadInodeGroup:
|
|
.long 0
|
|
ExtReadInodeIndex:
|
|
.long 0
|
|
ExtReadInodeGroupBlock:
|
|
.long 0
|
|
ExtReadInodeIndexBlock:
|
|
.long 0
|
|
ExtReadInodeGroupOffset:
|
|
.word 0
|
|
ExtReadInodeIndexOffset:
|
|
.word 0
|
|
|
|
main:
|
|
xor ax, ax // Setup segment registers
|
|
mov ds, ax // Make DS correct
|
|
mov es, ax // Make ES correct
|
|
mov ss, ax // Make SS correct
|
|
mov bp, BOOTSECTOR_BASE_ADDRESS
|
|
mov sp, bp // Setup a stack
|
|
sub sp, BOOTSECTOR_STACK_OFFSET
|
|
|
|
// Save the function addresses so the helper code knows where to call them
|
|
mov word ptr ss:[bp-ExtReadBlockOffset], offset ExtReadBlock
|
|
mov word ptr ss:[bp-ExtReadInodeOffset], offset ExtReadInode
|
|
mov word ptr ss:[bp-DisplayItAndRebootOffset], offset DisplayItAndReboot
|
|
mov word ptr ss:[bp-PutCharsOffset], offset PutChars
|
|
|
|
mov si, offset BootDrive
|
|
cmp byte ptr [si], HEX(0ff) // If they have specified a boot drive then use it
|
|
jne CheckInt13hExtensions
|
|
|
|
mov byte ptr [si], dl // Save the boot drive
|
|
|
|
// Now check if this computer supports extended reads. This boot sector will not work without it.
|
|
CheckInt13hExtensions:
|
|
mov ah, HEX(41) // AH = 41h
|
|
mov bx, HEX(55aa) // BX = 55AAh
|
|
int HEX(13) // IBM/MS INT 13 Extensions - INSTALLATION CHECK
|
|
jc PrintDiskError // CF set on error (extensions not supported)
|
|
cmp bx, HEX(aa55) // BX = AA55h if installed
|
|
jne PrintDiskError
|
|
test cl, 1 // si = API subset support bitmap
|
|
jz PrintDiskError // Bit 0, extended disk access functions (AH=42h-44h,47h,48h) supported
|
|
|
|
LoadExtraBootCode:
|
|
// First we have to load our extra boot code at
|
|
// sector 1 into memory at [0000:7e00h]
|
|
xor eax, eax
|
|
inc eax
|
|
mov cx, 1
|
|
xor bx, bx
|
|
mov es, bx // Read sector to [0000:7e00h]
|
|
mov bx, HEX(7e00)
|
|
call ReadSectors
|
|
|
|
jmp LoadRootDirectory
|
|
|
|
// Reads logical sectors into ES:[BX]
|
|
// EAX has logical sector number to read
|
|
// CX has number of sectors to read
|
|
ReadSectors:
|
|
push es
|
|
add eax, dword ptr BP_REL(ExtVolumeStartSector) // Add the start of the volume
|
|
// If at all possible we want to use LBA routines because
|
|
// they are optimized to read more than 1 sector per read
|
|
|
|
ReadSectorsLBA:
|
|
pushad // Save logical sector number & sector count
|
|
|
|
cmp cx, 64 // Since the LBA calls only support 0x7F sectors at a time we will limit ourselves to 64
|
|
jbe ReadSectorsSetupDiskAddressPacket // If we are reading less than 65 sectors then just do the read
|
|
mov cx, 64 // Otherwise read only 64 sectors on this loop iteration
|
|
|
|
ReadSectorsSetupDiskAddressPacket:
|
|
movzx ecx, cx
|
|
mov dword ptr BP_REL(LBASectorsRead), ecx
|
|
data32 push 0
|
|
push eax // Put 64-bit logical block address on stack
|
|
push es // Put transfer segment on stack
|
|
push bx // Put transfer offset on stack
|
|
push cx // Set transfer count
|
|
push 16 // Set size of packet to 10h
|
|
mov si, sp // Setup disk address packet on stack
|
|
|
|
mov dl, byte ptr BP_REL(BootDrive) // Drive number
|
|
mov ah, HEX(42) // Int 13h, AH = 42h - Extended Read
|
|
int HEX(13) // Call BIOS
|
|
jc PrintDiskError // If the read failed then abort
|
|
|
|
add sp, 16 // Remove disk address packet from stack
|
|
|
|
popad // Restore sector count & logical sector number
|
|
|
|
push bx
|
|
mov ebx, dword ptr BP_REL(LBASectorsRead)
|
|
add eax, ebx // Increment sector to read
|
|
shl ebx, 5
|
|
mov dx, es
|
|
add dx, bx // Setup read buffer for next sector
|
|
mov es, dx
|
|
pop bx
|
|
|
|
sub cx, word ptr BP_REL(LBASectorsRead)
|
|
jnz ReadSectorsLBA // Read next sector
|
|
|
|
pop es
|
|
ret
|
|
|
|
// Displays a disk error message
|
|
// And reboots
|
|
PrintDiskError:
|
|
mov si, offset msgDiskError // Bad boot disk message
|
|
call PutChars // Display it
|
|
|
|
Reboot:
|
|
mov si, offset msgAnyKey // Press any key message
|
|
call PutChars // Display it
|
|
xor ax, ax
|
|
int HEX(16) // Wait for a keypress
|
|
int HEX(19) // Reboot
|
|
|
|
.PutCharsLoop:
|
|
mov ah, HEX(0E)
|
|
mov bx, 7
|
|
int HEX(10)
|
|
PutChars:
|
|
lodsb
|
|
or al, al
|
|
jnz .PutCharsLoop
|
|
ret
|
|
|
|
SwapESWithDS:
|
|
// Swap ES and DS
|
|
push es
|
|
push ds
|
|
pop es
|
|
pop ds
|
|
ret
|
|
|
|
ExtReadGroupDescriptor:
|
|
mov eax, dword ptr BP_REL(ExtReadInodeGroupBlock) // Get Inode group block
|
|
add eax, dword ptr BP_REL(ExtFirstDataBlock) // Add the Group Descriptor offset
|
|
call ExtSetInodeSegment
|
|
|
|
ExtReadBlock:
|
|
xor edx, edx
|
|
mov ecx, dword ptr BP_REL(ExtBlockSize)
|
|
mul ecx
|
|
jmp ReadSectors
|
|
|
|
// EAX
|
|
ExtCalculateBlock:
|
|
xor edx, edx // Clear EDX before division
|
|
div dword ptr BP_REL(ExtBlockSizeInBytes) // Inode /= ExtBlockSizeInBytes
|
|
mov dword ptr ds:[bp + si], eax // Store the Inode block
|
|
ret
|
|
|
|
// SI, DI
|
|
ExtCalculateOffset:
|
|
add bx, bp // Sum BX with BP for absolute address
|
|
xor edx, edx // Clear EDX before multiplication
|
|
mov eax, dword ptr ds:[bp + si] // Get the Inode block
|
|
mul dword ptr BP_REL(ExtBlockSizeInBytes) // Inode *= ExtBlockSizeInBytes
|
|
mov ecx, dword ptr ds:[bx] // Get the Inode
|
|
sub ecx, eax // Subtract the original Inode with rounded down Inode
|
|
mov word ptr ds:[bp + di], cx // Store the rounded down Inode
|
|
ret
|
|
|
|
ExtSetOldFileSegment:
|
|
mov ebx, dword ptr BP_REL(ExtFileAddressOld) // Get the EXT old file address
|
|
jmp .ExtSegSkip
|
|
ExtSetFileSegment:
|
|
mov ebx, dword ptr BP_REL(ExtFileAddress) // Get the EXT file address
|
|
.ExtSegSkip:
|
|
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
|
|
.ExtSkip:
|
|
mov es, bx // Set ES
|
|
xor bx, bx // Clear BX
|
|
ret
|
|
|
|
// Read the Inode in EAX register
|
|
ExtReadInode:
|
|
xor edx, edx // Clear EDX before division
|
|
dec eax // Inode--
|
|
div dword ptr BP_REL(ExtInodesPerGroup) // Inode /= ExtInodesPerGroup
|
|
mov dword ptr BP_REL(ExtReadInodeGroup), eax // Store the Inode group
|
|
mov dword ptr BP_REL(ExtReadInodeIndex), edx // Store the Inode index
|
|
|
|
xor edx, edx // Clear EDX before multiplication
|
|
mul dword ptr BP_REL(ExtGroupDescSize) // Inode group *= ExtGroupDescSize
|
|
mov dword ptr BP_REL(ExtReadInodeGroup), eax // Store the precalculated Inode group
|
|
|
|
xor edx, edx // Clear EDX before multiplication
|
|
mov eax, dword ptr BP_REL(ExtReadInodeIndex) // Get the read Inode index
|
|
mul dword ptr BP_REL(ExtInodeSize) // Inode group *= ExtInodeSize
|
|
mov dword ptr BP_REL(ExtReadInodeIndex), eax // Store the Inode index
|
|
|
|
// Calculate the Inode index block
|
|
mov si, offset ExtReadInodeIndexBlock - start
|
|
call ExtCalculateBlock
|
|
|
|
// Calculate the Inode group block
|
|
mov eax, dword ptr BP_REL(ExtReadInodeGroup)
|
|
mov si, offset ExtReadInodeGroupBlock - start
|
|
call ExtCalculateBlock
|
|
|
|
// Calculate the Inode group offset
|
|
mov bx, offset ExtReadInodeGroup - start
|
|
mov si, offset ExtReadInodeGroupBlock - start
|
|
mov di, offset ExtReadInodeGroupOffset - start
|
|
call ExtCalculateOffset
|
|
|
|
// Calculate the Inode index offset
|
|
mov bx, offset ExtReadInodeIndex - start
|
|
mov si, offset ExtReadInodeIndexBlock - start
|
|
mov di, offset ExtReadInodeIndexOffset - start
|
|
call ExtCalculateOffset
|
|
|
|
// Read group descriptor
|
|
call ExtReadGroupDescriptor
|
|
|
|
// Set the offset address
|
|
mov si, word ptr BP_REL(ExtReadInodeGroupOffset)
|
|
|
|
// Get InodeTable field from the ExtGroupDescriptor structure
|
|
mov eax, dword ptr es:[si + EXT_GROUP_DESC_INODE_TABLE_OFFSET]
|
|
|
|
// Sum EAX with Inode index block
|
|
add eax, dword ptr BP_REL(ExtReadInodeIndexBlock)
|
|
|
|
jmp ExtReadBlock
|
|
|
|
msgDiskError:
|
|
.ascii "Disk error", CR, LF, NUL
|
|
msgAnyKey:
|
|
.ascii "Press any key", CR, LF, NUL
|
|
|
|
.org 509
|
|
|
|
BootPartition:
|
|
.byte 0
|
|
|
|
.word HEX(AA55) // BootSector signature
|
|
|
|
// End of bootsector
|
|
//
|
|
// Now starts the extra boot code that we will store
|
|
// at sector 1 on a EXT volume
|
|
|
|
LoadRootDirectory:
|
|
mov al, EXT_ROOT_INODE // Put the root directory inode number in AL
|
|
movzx eax, al // Convert AL to EAX
|
|
|
|
call ExtReadInode // Read the inode
|
|
call BasicReadFile // Load the directory entries using basic function
|
|
call SearchFile // Find the extended loader and run it
|
|
|
|
jmp ExtLdrPrintFileNotFound // If the extended loader wasn't found, display an error
|
|
|
|
ExtInodeDetectExtentsFlag:
|
|
mov eax, es:[si + EXT_INODE_FLAGS_OFFSET]
|
|
test eax, EXT_INODE_FLAG_EXTENTS
|
|
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
|
|
|
|
ExtReadFileDone:
|
|
push eax
|
|
mov eax, dword ptr BP_REL(ExtFileSizeState)
|
|
cmp eax, dword ptr BP_REL(ExtBlockSizeInBytes)
|
|
pop eax
|
|
ret
|
|
|
|
ExtFileReadBlocks:
|
|
push es
|
|
.FRLoop:
|
|
// Check if there is no more blocks to read
|
|
call ExtReadFileDone
|
|
jb .FRDone
|
|
|
|
// If the block count is zero then do nothing
|
|
test bx, bx
|
|
jz .FRDone
|
|
|
|
// Read the block
|
|
pushad
|
|
call ExtSetFileSegment
|
|
call ExtReadBlock
|
|
popad
|
|
|
|
// Update the file size
|
|
call ExtUpdateFileSize
|
|
|
|
// Go to the next block and decrement the block count
|
|
inc eax
|
|
dec bx
|
|
|
|
// Loop until all blocks are read
|
|
jmp .FRLoop
|
|
.FRDone:
|
|
pop es
|
|
ret
|
|
|
|
BasicReadFileExtents:
|
|
// Add block pointer offset
|
|
add si, EXT_INODE_BLOCK_POINTER_OFFSET
|
|
|
|
.DepthExtentsLoop:
|
|
// Load extent header depth
|
|
mov dx, word ptr es:[si + EXT_EXTENT_HEADER_DEPTH_OFFSET]
|
|
|
|
// Check if depth is zero
|
|
test dx, dx
|
|
jz .DepthExtentsDone
|
|
|
|
// Go to next extent
|
|
add si, EXT_EXTENT_SIZE
|
|
|
|
// Push all registers
|
|
pushad
|
|
|
|
// Read the extent block
|
|
mov eax, dword ptr es:[si + EXT_EXTENT_INDEX_LEAF_OFFSET]
|
|
call ExtSetInodeSegment
|
|
call ExtReadBlock
|
|
|
|
// Pop all registers
|
|
popad
|
|
|
|
// Reset SI
|
|
xor si, si
|
|
|
|
jmp .DepthExtentsLoop
|
|
.DepthExtentsDone:
|
|
// Load extent header entries
|
|
mov cx, word ptr es:[si + EXT_EXTENT_HEADER_ENTRIES_OFFSET]
|
|
|
|
.FinalExtentsLoop:
|
|
// Check if there is no more blocks to read
|
|
call ExtReadFileDone
|
|
jb .FinalExtentsDone
|
|
|
|
// 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
|
|
|
|
// Adjust extent length to byte count
|
|
// by multiplying extent length to block size
|
|
xor edx, edx
|
|
mov eax, dword ptr BP_REL(ExtBlockSizeInBytes)
|
|
mul ebx
|
|
|
|
// Adjust file size for sparse extent
|
|
call ExtAdjustFileSize
|
|
|
|
jmp .FinalExtentsSkip
|
|
|
|
.NotSparse:
|
|
// Read blocks from extent start
|
|
mov eax, dword ptr es:[si + EXT_EXTENT_START_OFFSET]
|
|
call ExtFileReadBlocks
|
|
|
|
.FinalExtentsSkip:
|
|
// Loop to process next extent
|
|
loop .FinalExtentsLoop
|
|
|
|
.FinalExtentsDone:
|
|
ret
|
|
|
|
BasicReadFile:
|
|
push es
|
|
pushad
|
|
call ExtSetInodeSegment
|
|
|
|
// Set the correct Inode offset
|
|
mov si, word ptr BP_REL(ExtReadInodeIndexOffset)
|
|
|
|
// Set the old file address
|
|
mov eax, dword ptr BP_REL(ExtFileAddress)
|
|
mov dword ptr BP_REL(ExtFileAddressOld), eax
|
|
|
|
// Set the file size limit
|
|
mov eax, EXT_INODE_DATA_SIZE_LIMIT
|
|
|
|
// Load file size from Inode
|
|
mov ebx, dword ptr es:[si + EXT_INODE_SIZE_OFFSET]
|
|
|
|
// Compare and limit file size
|
|
cmp ebx, eax
|
|
jbe .BelowOrEqualSize
|
|
mov ebx, eax
|
|
.BelowOrEqualSize:
|
|
// Store the file size in the ExtFileSize variable
|
|
mov dword ptr BP_REL(ExtFileSize), ebx
|
|
|
|
// Set rounded up file size
|
|
add ebx, dword ptr BP_REL(ExtBlockSizeInBytes)
|
|
dec ebx
|
|
mov dword ptr BP_REL(ExtFileSizeState), ebx
|
|
|
|
// Don't use the extents method if theres no extents flag
|
|
call ExtInodeDetectExtentsFlag
|
|
jz .NoExtents
|
|
|
|
// If this Inode use Extents mapping then use the extents method and skip the entire classic method
|
|
call BasicReadFileExtents
|
|
jmp .LDone
|
|
|
|
.NoExtents:
|
|
// Set up for reading direct block addresses
|
|
xor ecx, ecx
|
|
mov cl, EXT_INODE_BLOCKS
|
|
add si, EXT_INODE_BLOCK_POINTER_OFFSET
|
|
.LLoop:
|
|
call ExtSetInodeSegment
|
|
|
|
call ExtReadFileDone
|
|
jb .LDone
|
|
|
|
// Get the block address
|
|
mov eax, dword ptr es:[si]
|
|
|
|
// If the block address is zero, skip the block
|
|
test eax, eax
|
|
jz .LSkipBlock
|
|
|
|
// Set the file segment
|
|
call ExtSetFileSegment
|
|
|
|
// Read the block
|
|
call ExtReadBlock
|
|
.LSkipBlock:
|
|
call ExtUpdateFileSize
|
|
|
|
// Increment block
|
|
add si, EXT_POINTER_SIZE
|
|
|
|
// Loop until all blocks are loaded
|
|
loop .LLoop
|
|
.LDone:
|
|
popad
|
|
pop es
|
|
ret
|
|
|
|
SearchFile:
|
|
call ExtSetOldFileSegment
|
|
call SwapESWithDS
|
|
|
|
xor si, si
|
|
mov dx, word ptr BP_REL(ExtFileSize)
|
|
.FLoop:
|
|
mov eax, dword ptr ds:[si] // Load directory Inode
|
|
|
|
cmp si, dx // End of buffer reached?
|
|
jae .Done // Abort the search if yes
|
|
|
|
// Save SI
|
|
push si
|
|
|
|
test eax, eax // Check if Inode is zero
|
|
jz .Skip // Skip this entry if yes
|
|
|
|
mov di, offset ExtLdrFileName // Load target filename address
|
|
mov cx, offset ExtLdrFileNameEnd - ExtLdrFileName // 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 .Skip // Skip this entry if not
|
|
add si, EXT_DIRECTORY_ENTRY_NAME_OFFSET // Move to filename in entry
|
|
repe cmpsb // Compare filenames
|
|
pop si // Restore SI
|
|
jz LoadExtLdr // Found matching file
|
|
push si // Save SI
|
|
|
|
.Skip:
|
|
// Restore SI
|
|
pop si
|
|
|
|
// Move to next directory entry and continue looping
|
|
add si, word ptr ds:[si + EXT_DIRECTORY_ENTRY_SIZE_OFFSET]
|
|
jmp .FLoop
|
|
.Done:
|
|
ret
|
|
|
|
LoadExtLdr:
|
|
// Swap ES and DS
|
|
call SwapESWithDS
|
|
|
|
push si // Save SI
|
|
mov si, offset msgLoadingExtLdr // Point SI to a loading message
|
|
call PutChars // Show the message
|
|
pop si // Restore SI
|
|
|
|
mov eax, dword ptr es:[si] // Load directory Inode
|
|
call ExtReadInode // Read the inode
|
|
mov si, word ptr BP_REL(ExtReadInodeIndexOffset) // Set the correct offset
|
|
|
|
// Get Inode type
|
|
mov ax, word ptr es:[si]
|
|
and ax, EXT_INODE_TYPE_MASK
|
|
|
|
cmp ax, EXT_INODE_TYPE_REGULAR // Check if regular file
|
|
jnz ExtLdrPrintRegFileError // If not, handle error
|
|
|
|
call BasicReadFile // Load the file using basic function
|
|
call ExtSetOldFileSegment // Set old file segment
|
|
call SwapESWithDS // Swap ES with DS before copy
|
|
|
|
// Copy the loaded file to 1KB ahead of this bootsector
|
|
xor si, si
|
|
mov di, offset ExtLdrEntryPoint
|
|
mov cx, EXTLDR_BOOTSECTOR_SIZE
|
|
rep movsb
|
|
|
|
ljmp16 0, ExtLdrEntryPoint
|
|
|
|
ExtLdrPrintFileNotFound:
|
|
// Make DS correct, display it and reboot
|
|
call SwapESWithDS
|
|
mov si, offset msgExtLdr
|
|
jmp DisplayItAndReboot
|
|
|
|
ExtLdrPrintRegFileError:
|
|
mov si, offset msgExtLdrNotRegularFile // ExtLdr not found message
|
|
DisplayItAndReboot:
|
|
call PutChars // Display it
|
|
jmp Reboot
|
|
|
|
ExtLdrFileName:
|
|
.ascii "extldr.sys"
|
|
ExtLdrFileNameEnd:
|
|
|
|
msgExtLdr:
|
|
.ascii "extldr.sys not found", CR, LF, NUL
|
|
msgExtLdrNotRegularFile:
|
|
.ascii "extldr.sys is not a regular file", CR, LF, NUL
|
|
msgLoadingExtLdr:
|
|
.ascii "Loading ExtLoader...", CR, LF, NUL
|
|
|
|
.org 1022
|
|
|
|
.word HEX(AA55) // BootSector signature
|
|
|
|
ExtLdrEntryPoint:
|
|
// ExtLdr is loaded here
|
|
|
|
.endcode16
|
|
|
|
END
|
|
|
|
#else
|
|
|
|
#define start BOOTSECTOR_BASE_ADDRESS
|
|
|
|
#define BootDrive (start + 3)
|
|
#define ExtVolumeStartSector (BootDrive + 1)
|
|
#define ExtBlockSize (ExtVolumeStartSector + 4)
|
|
#define ExtBlockSizeInBytes (ExtBlockSize + 4)
|
|
#define ExtPointersPerBlock (ExtBlockSizeInBytes + 4)
|
|
#define ExtGroupDescSize (ExtPointersPerBlock + 4)
|
|
#define ExtFirstDataBlock (ExtGroupDescSize + 4)
|
|
#define ExtInodeSize (ExtFirstDataBlock + 4)
|
|
#define ExtInodesPerGroup (ExtInodeSize + 4)
|
|
|
|
#define ExtFileSize (ExtInodesPerGroup + 4)
|
|
#define ExtFileAddress (ExtFileSize + 4)
|
|
#define ExtFileAddressOld (ExtFileAddress + 4)
|
|
|
|
#define ExtReadInodeGroup (ExtFileAddressOld + 4)
|
|
#define ExtReadInodeIndex (ExtReadInodeGroup + 4)
|
|
#define ExtReadInodeGroupBlock (ExtReadInodeIndex + 4)
|
|
#define ExtReadInodeIndexBlock (ExtReadInodeGroupBlock + 4)
|
|
#define ExtReadInodeGroupOffset (ExtReadInodeIndexBlock + 4)
|
|
#define ExtReadInodeIndexOffset (ExtReadInodeGroupOffset + 2)
|
|
|
|
#define BootPartition (BootDrive + 506)
|
|
|
|
#define ExtReadBlock word ptr ss:[bp-ExtReadBlockOffset]
|
|
#define ExtReadInode word ptr ss:[bp-ExtReadInodeOffset]
|
|
#define DisplayItAndReboot word ptr ss:[bp-DisplayItAndRebootOffset]
|
|
#define PutChars word ptr ss:[bp-PutCharsOffset]
|
|
|
|
#endif
|