mirror of
https://github.com/reactos/reactos.git
synced 2025-08-06 02:43:09 +00:00

Rewrite the Ext bootsector because the older one was broken and had no compatibility with Ext4 extents. Introduce a 3rd-stage bootsector for complex code. CORE-14235 - Why was the previous bootsector broken? Because of hardcoded inode size, hardcoded freeldr base address, etc. - Why is there a extldr.sys? The extldr.sys was introduced because the limited code space in the bootsector prevents adding new features, such as Ext4 full extents support. - What is extldr.sys and What does it do? It is the helper file for the Ext bootsector and that is necessary for adding Ext4 support. It locates the freeldr.sys file, loads it into memory and runs it.
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 yes
|
|
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
|