Something went wrong. Try again.
Reactos
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727/* * 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_ASMstart: jmp short main nop// Fields that will be changed by the installerBootDrive: .byte HEX(FF)ExtVolumeStartSector: .long 263088 // Start sector of the ext2 volumeExtBlockSize: .long 2 // Block size in sectorsExtBlockSizeInBytes: .long 1024 // Block size in bytesExtPointersPerBlock: .long 256 // Number of block pointers that can be contained in one blockExtGroupDescSize: .long 32 // Size of Group DescriptorExtFirstDataBlock: .long 2 // First data block (2 for 1024-byte blocks, 1 for bigger sizes)ExtInodeSize: .long 128 // Size of InodeExtInodesPerGroup: .long 2048 // Number of inodes per group// File variablesExtFileSize: .long 0 // File size in bytesExtFileAddress: .long FREELDR_BASE // File addressExtFileAddressOld: .long FREELDR_BASE // Old file address// Inode variablesExtReadInodeGroup: .long 0ExtReadInodeIndex: .long 0ExtReadInodeGroupBlock: .long 0ExtReadInodeIndexBlock: .long 0ExtReadInodeGroupOffset: .word 0ExtReadInodeIndexOffset: .word 0main: 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) supportedLoadExtraBootCode: // 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 readReadSectors: 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 readReadSectorsLBA: 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 iterationReadSectorsSetupDiskAddressPacket: 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 rebootsPrintDiskError: mov si, offset msgDiskError // Bad boot disk message call PutChars // Display itReboot: 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 retSwapESWithDS: // Swap ES and DS push es push ds pop es pop ds retExtReadGroupDescriptor: mov eax, dword ptr BP_REL(ExtReadInodeGroupBlock) // Get Inode group block add eax, dword ptr BP_REL(ExtFirstDataBlock) // Add the Group Descriptor offset call ExtSetInodeSegmentExtReadBlock: xor edx, edx mov ecx, dword ptr BP_REL(ExtBlockSize) mul ecx jmp ReadSectors// EAXExtCalculateBlock: 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, DIExtCalculateOffset: 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 retExtSetOldFileSegment: mov ebx, dword ptr BP_REL(ExtFileAddressOld) // Get the EXT old file address jmp .ExtSegSkipExtSetFileSegment: 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 .ExtSkipExtSetInodeSegment: 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 registerExtReadInode: 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 ExtReadBlockmsgDiskError: .ascii "Disk error", CR, LF, NULmsgAnyKey: .ascii "Press any key", CR, LF, NUL.org 509BootPartition: .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 volumeLoadRootDirectory: 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 errorExtInodeDetectExtentsFlag: mov eax, es:[si + EXT_INODE_FLAGS_OFFSET] test eax, EXT_INODE_FLAG_EXTENTS retExtUpdateFileSize: 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 retExtReadFileDone: push eax mov eax, dword ptr BP_REL(ExtFileSizeState) cmp eax, dword ptr BP_REL(ExtBlockSizeInBytes) pop eax retExtFileReadBlocks: 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 retBasicReadFileExtents: // 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: retBasicReadFile: 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 retSearchFile: 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: retLoadExtLdr: // 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, ExtLdrEntryPointExtLdrPrintFileNotFound: // Make DS correct, display it and reboot call SwapESWithDS mov si, offset msgExtLdr jmp DisplayItAndRebootExtLdrPrintRegFileError: mov si, offset msgExtLdrNotRegularFile // ExtLdr not found messageDisplayItAndReboot: call PutChars // Display it jmp RebootExtLdrFileName: .ascii "extldr.sys"ExtLdrFileNameEnd:msgExtLdr: .ascii "extldr.sys not found", CR, LF, NULmsgExtLdrNotRegularFile: .ascii "extldr.sys is not a regular file", CR, LF, NULmsgLoadingExtLdr: .ascii "Loading ExtLoader...", CR, LF, NUL.org 1022.word HEX(AA55) // BootSector signatureExtLdrEntryPoint:// ExtLdr is loaded here.endcode16END#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