From 81b6d1dc782ee1c28d1982bd17b05877864d9812 Mon Sep 17 00:00:00 2001 From: Timo Kreuzer Date: Sun, 2 Oct 2011 13:15:18 +0000 Subject: [PATCH] [EXT2] Duplicate ext2.asm to ext2.S, no changes yet svn path=/trunk/; revision=53925 --- reactos/boot/freeldr/bootsect/ext2.S | 665 +++++++++++++++++++++++++++ 1 file changed, 665 insertions(+) create mode 100644 reactos/boot/freeldr/bootsect/ext2.S diff --git a/reactos/boot/freeldr/bootsect/ext2.S b/reactos/boot/freeldr/bootsect/ext2.S new file mode 100644 index 00000000000..3dd20a2a883 --- /dev/null +++ b/reactos/boot/freeldr/bootsect/ext2.S @@ -0,0 +1,665 @@ +; EXT2.ASM +; EXT2 Boot Sector +; Copyright (c) 2002, 2003 Brian Palmer + +; [bp-0x04] Here we will store the number of sectors per track +; [bp-0x08] Here we will store the number of heads +; [bp-0x0c] Here we will store the size of the disk as the BIOS reports in CHS form +; [bp-0x10] Here we will store the number of LBA sectors read + +SECTORS_PER_TRACK equ 0x04 +NUMBER_OF_HEADS equ 0x08 +BIOS_CHS_DRIVE_SIZE equ 0x0C +LBA_SECTORS_READ equ 0x10 + + +EXT2_ROOT_INO equ 2 +EXT2_S_IFMT equ 0f0h +EXT2_S_IFREG equ 080h + + +org 7c00h + +segment .text + +bits 16 + +start: + jmp short main + nop + +BootDrive db 0x80 +;BootPartition db 0 ; Moved to end of boot sector to have a standard format across all boot sectors +;SectorsPerTrack db 63 ; Moved to [bp-SECTORS_PER_TRACK] +;NumberOfHeads dw 16 ; Moved to [bp-NUMBER_OF_HEADS] +;BiosCHSDriveSize dd (1024 * 1024 * 63) ; Moved to [bp-BIOS_CHS_DRIVE_SIZE] +;LBASectorsRead dd 0 ; Moved to [bp-LBA_SECTORS_READ] + +Ext2VolumeStartSector dd 263088 ; Start sector of the ext2 volume +Ext2BlockSize dd 2 ; Block size in sectors +Ext2BlockSizeInBytes dd 1024 ; Block size in bytes +Ext2PointersPerBlock dd 256 ; Number of block pointers that can be contained in one block +Ext2GroupDescPerBlock dd 32 ; Number of group descriptors per block +Ext2FirstDataBlock dd 1 ; First data block (1 for 1024-byte blocks, 0 for bigger sizes) +Ext2InodesPerGroup dd 2048 ; Number of inodes per group +Ext2InodesPerBlock dd 8 ; Number of inodes per block + +Ext2ReadEntireFileLoadSegment: + dw 0 +Ext2InodeIndirectPointer: + dd 0 +Ext2InodeDoubleIndirectPointer: + dd 0 +Ext2BlocksLeftToRead: + dd 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,7c00h + mov sp,7b00h ; Setup a stack + + + cmp BYTE [BYTE bp+BootDrive],BYTE 0xff ; If they have specified a boot drive then use it + jne GetDriveParameters + + mov [BYTE bp+BootDrive],dl ; Save the boot drive + + +GetDriveParameters: + mov ah,08h + mov dl,[BYTE bp+BootDrive] ; Get boot drive in dl + int 13h ; Request drive parameters from the bios + jnc CalcDriveSize ; If the call succeeded then calculate the drive size + + ; If we get here then the call to the BIOS failed + ; so just set CHS equal to the maximum addressable + ; size + mov cx,0ffffh + mov dh,cl + +CalcDriveSize: + ; Now that we have the drive geometry + ; lets calculate the drive size + mov bl,ch ; Put the low 8-bits of the cylinder count into BL + mov bh,cl ; Put the high 2-bits in BH + shr bh,6 ; Shift them into position, now BX contains the cylinder count + and cl,3fh ; Mask off cylinder bits from sector count + ; CL now contains sectors per track and DH contains head count + movzx eax,dh ; Move the heads into EAX + movzx ebx,bx ; Move the cylinders into EBX + movzx ecx,cl ; Move the sectors per track into ECX + inc eax ; Make it one based because the bios returns it zero based + mov [BYTE bp-NUMBER_OF_HEADS],eax ; Save number of heads + mov [BYTE bp-SECTORS_PER_TRACK],ecx ; Save number of sectors per track + inc ebx ; Make the cylinder count one based also + mul ecx ; Multiply heads with the sectors per track, result in edx:eax + mul ebx ; Multiply the cylinders with (heads * sectors) [stored in edx:eax already] + + ; We now have the total number of sectors as reported + ; by the bios in eax, so store it in our variable + mov [BYTE bp-BIOS_CHS_DRIVE_SIZE],eax + + +LoadExtraBootCode: + ; First we have to load our extra boot code at + ; sector 1 into memory at [0000:7e00h] + ;mov eax,01h + xor eax,eax + inc eax ; Read logical sector 1, EAX now = 1 + mov cx,1 ; Read one sector + mov bx,7e00h ; Read sector to [0000:7e00h] + call ReadSectors + + jmp LoadRootDirectory + + + +; Reads ext2 group descriptor into [7000:8000] +; We read it to this arbitrary location so +; it will not cross a 64k boundary +; EAX has group descriptor number to read +Ext2ReadGroupDesc: + shl eax,5 ; Group = (Group * sizeof(GROUP_DESCRIPTOR) /* 32 */) + xor edx,edx + div DWORD [BYTE bp+Ext2GroupDescPerBlock] ; Group = (Group / Ext2GroupDescPerBlock) + add eax,DWORD [BYTE bp+Ext2FirstDataBlock] ; Group = Group + Ext2FirstDataBlock + 1 + inc eax ; EAX now has the group descriptor block number + ; EDX now has the group descriptor offset in the block + + ; Adjust the read offset so that the + ; group descriptor is read to 7000:8000 + mov ebx,78000h + sub ebx,edx + shr ebx,4 + mov es,bx + xor bx,bx + + + ; Everything is now setup to call Ext2ReadBlock + ; Instead of using the call instruction we will + ; just put Ext2ReadBlock right after this routine + +; Reads ext2 block into [ES:BX] +; EAX has logical block number to read +Ext2ReadBlock: + mov ecx,DWORD [BYTE bp+Ext2BlockSize] + mul ecx + jmp ReadSectors + +; Reads ext2 inode into [6000:8000] +; We read it to this arbitrary location so +; it will not cross a 64k boundary +; EAX has inode number to read +Ext2ReadInode: + dec eax ; Inode = Inode - 1 + xor edx,edx + div DWORD [BYTE bp+Ext2InodesPerGroup] ; Inode = (Inode / Ext2InodesPerGroup) + mov ebx,eax ; EBX now has the inode group number + mov eax,edx + xor edx,edx + div DWORD [BYTE bp+Ext2InodesPerBlock] ; Inode = (Inode / Ext2InodesPerBlock) + shl edx,7 ; FIXME: InodeOffset *= 128 (make the array index a byte offset) + ; EAX now has the inode offset block number from inode table + ; EDX now has the inode offset in the block + + ; Save the inode values and put the group + ; descriptor number in EAX and read it in + push edx + push eax + mov eax,ebx + call Ext2ReadGroupDesc + + ; Group descriptor has been read, now + ; grab the inode table block number from it + push WORD 7000h + pop es + mov di,8008h + pop eax ; Restore inode offset block number from stack + add eax,DWORD [es:di] ; Add the inode table start block + + ; Adjust the read offset so that the + ; inode we want is read to 6000:8000 + pop edx ; Restore inode offset in the block from stack + mov ebx,68000h + sub ebx,edx + shr ebx,4 + mov es,bx + xor bx,bx + + call Ext2ReadBlock + ret + + +; Reads logical sectors into [ES:BX] +; EAX has logical sector number to read +; CX has number of sectors to read +ReadSectors: + add eax,DWORD [BYTE bp+Ext2VolumeStartSector] ; Add the start of the volume + cmp eax,DWORD [BYTE bp-BIOS_CHS_DRIVE_SIZE] ; Check if they are reading a sector outside CHS range + jae ReadSectorsLBA ; Yes - go to the LBA routine + ; If at all possible we want to use LBA routines because + ; They are optimized to read more than 1 sector per read + + pushad ; Save logical sector number & sector count + +CheckInt13hExtensions: ; Now check if this computer supports extended reads + mov ah,0x41 ; AH = 41h + mov bx,0x55aa ; BX = 55AAh + mov dl,[BYTE bp+BootDrive] ; DL = drive (80h-FFh) + int 13h ; IBM/MS INT 13 Extensions - INSTALLATION CHECK + jc ReadSectorsCHS ; CF set on error (extensions not supported) + cmp bx,0xaa55 ; BX = AA55h if installed + jne ReadSectorsCHS + test cl,1 ; CX = API subset support bitmap + jz ReadSectorsCHS ; Bit 0, extended disk access functions (AH=42h-44h,47h,48h) supported + + popad ; Restore sector count & logical sector number + +ReadSectorsLBA: + pushad ; Save logical sector number & sector count + + cmp cx,byte 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: + mov [BYTE bp-LBA_SECTORS_READ],cx + mov WORD [BYTE bp-LBA_SECTORS_READ+2],0 + o32 push byte 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 byte 0x10 ; Set size of packet to 10h + mov si,sp ; Setup disk address packet on stack + + + mov dl,[BYTE bp+BootDrive] ; Drive number + mov ah,42h ; Int 13h, AH = 42h - Extended Read + int 13h ; Call BIOS + jc PrintDiskError ; If the read failed then abort + + add sp,byte 0x10 ; Remove disk address packet from stack + + popad ; Restore sector count & logical sector number + + push bx + mov ebx,DWORD [BYTE bp-LBA_SECTORS_READ] + 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,[BYTE bp-LBA_SECTORS_READ] + jnz ReadSectorsLBA ; Read next sector + + ret + + +; Reads logical sectors into [ES:BX] +; EAX has logical sector number to read +; CX has number of sectors to read +ReadSectorsCHS: + popad ; Get logical sector number & sector count off stack + +ReadSectorsCHSLoop: + pushad + xor edx,edx + mov ecx,DWORD [BYTE bp-SECTORS_PER_TRACK] + div ecx ; Divide logical by SectorsPerTrack + inc dl ; Sectors numbering starts at 1 not 0 + mov cl,dl ; Sector in CL + mov edx,eax + shr edx,16 + div WORD [BYTE bp-NUMBER_OF_HEADS] ; Divide logical by number of heads + mov dh,dl ; Head in DH + mov dl,[BYTE bp+BootDrive] ; Drive number in DL + mov ch,al ; Cylinder in CX + ror ah,2 ; Low 8 bits of cylinder in CH, high 2 bits + ; in CL shifted to bits 6 & 7 + or cl,ah ; Or with sector number + mov ax,0201h + int 13h ; DISK - READ SECTORS INTO MEMORY + ; AL = number of sectors to read, CH = track, CL = sector + ; DH = head, DL = drive, ES:BX -> buffer to fill + ; Return: CF set on error, AH = status (see AH=01h), AL = number of sectors read + + jc PrintDiskError ; If the read failed then abort + + popad + + inc eax ; Increment Sector to Read + + mov dx,es + add dx,byte 20h ; Increment read buffer for next sector + mov es,dx + + loop ReadSectorsCHSLoop ; Read next sector + + ret + + + + +; Displays a disk error message +; And reboots +PrintDiskError: + mov si,msgDiskError ; Bad boot disk message + call PutChars ; Display it + +Reboot: + mov si,msgAnyKey ; Press any key message + call PutChars ; Display it + xor ax,ax + int 16h ; Wait for a keypress + int 19h ; Reboot + +PutChars: + lodsb + or al,al + jz short Done + call PutCharsCallBios + jmp short PutChars +PutCharsCallBios: + mov ah,0eh + mov bx,07h + int 10h + retn +Done: + mov al,0dh + call PutCharsCallBios + mov al,0ah + call PutCharsCallBios + retn + + + +msgDiskError db 'Disk error',0 +; Sorry, need the space... +;msgAnyKey db 'Press any key to restart',0 +msgAnyKey db 'Press any key',0 + + times 509-($-$$) db 0 ; Pad to 509 bytes + +BootPartition db 0 + + dw 0aa55h ; BootSector signature + + +; End of bootsector +; +; Now starts the extra boot code that we will store +; at sector 1 on a EXT2 volume + + + +LoadRootDirectory: + + mov eax,EXT2_ROOT_INO ; Put the root directory inode number in EAX + call Ext2ReadInode ; Read in the inode + + ; Point ES:DI to the inode structure at 6000:8000 + push WORD 6000h + pop es + mov di,8000h + push di + push es ; Save these for later + + ; Get root directory size from inode structure + mov eax,DWORD [es:di+4] + push eax + + ; Now that the inode has been read in load + ; the root directory file data to 0000:8000 + call Ext2ReadEntireFile + + ; Since the root directory was loaded to 0000:8000 + ; then add 8000h to the root directory's size + pop eax + mov edx,8000h ; Set EDX to the current offset in the root directory + add eax,edx ; Initially add 8000h to the size of the root directory + +SearchRootDirectory: + push edx ; Save current offset in root directory + push eax ; Save the size of the root directory + + ; Now we have to convert the current offset + ; in the root directory to a SEGMENT:OFFSET pair + mov eax,edx + xor edx,edx + mov ecx,16 + div ecx ; Now AX:DX has segment & offset + mov es,ax + mov di,dx + push di ; Save the start of the directory entry + add di,byte 8 ; Add the offset to the filename + mov si,filename + mov cl,11 + rep cmpsb ; Compare the file names + pop di + pop eax + pop edx + jz FoundFile + + ; Nope, didn't find it in this entry, keep looking + movzx ecx,WORD [es:di+4] + add edx,ecx + + ; Check to see if we have reached the + ; end of the root directory + cmp edx,eax + jb SearchRootDirectory + jmp PrintFileNotFound + +FoundFile: + mov eax,[es:di] ; Get inode number from directory entry + call Ext2ReadInode ; Read in the inode + + ; Point ES:DI to the inode structure at 6000:8000 + pop es + pop di ; These were saved earlier + + mov cx,[es:di] ; Get the file mode so we can make sure it's a regular file + and ch,EXT2_S_IFMT ; Mask off everything but the file type + cmp ch,EXT2_S_IFREG ; Make sure it's a regular file + je LoadFreeLoader + jmp PrintRegFileError + +LoadFreeLoader: + mov si,msgLoading ; "Loading FreeLoader..." message + call PutChars ; Display it + + call Ext2ReadEntireFile ; Read freeldr.sys to 0000:8000 + + mov dl,[BYTE bp+BootDrive] + mov dh,[BYTE bp+BootPartition] + push 0 ; push segment (0x0000) + mov eax, [0x8000 + 0xA8] ; load the RVA of the EntryPoint into eax + add eax, 0x8000 ; RVA -> VA + push ax ; push offset + retf ; Transfer control to FreeLoader + + + + + +; Reads ext2 file data into [0000:8000] +; This function assumes that the file's +; inode has been read in to 6000:8000 *and* +; ES:DI points to 6000:8000 +; This will load all the blocks up to +; and including the double-indirect pointers. +; This should be sufficient because it +; allows for ~64MB which is much bigger +; than we need for a boot loader. +Ext2ReadEntireFile: + + ; Reset the load segment + mov WORD [BYTE bp+Ext2ReadEntireFileLoadSegment],800h + + ; Now we must calculate how + ; many blocks to read in + ; We will do this by rounding the + ; file size up to the next block + ; size and then dividing by the block size + mov eax,DWORD [BYTE bp+Ext2BlockSizeInBytes] ; Get the block size in bytes + push eax + dec eax ; Ext2BlockSizeInBytes -= 1 + add eax,DWORD [es:di+4] ; Add the file size + xor edx,edx + pop ecx ; Divide by the block size in bytes + div ecx ; EAX now contains the number of blocks to load + push eax + + ; Make sure the file size isn't zero + cmp eax,byte 0 + jnz Ext2ReadEntireFile2 + jmp PrintFileSizeError + +Ext2ReadEntireFile2: + ; Save the indirect & double indirect pointers + mov edx,DWORD [es:di+0x58] ; Get indirect pointer + mov [BYTE bp+Ext2InodeIndirectPointer],edx ; Save indirect pointer + mov edx,DWORD [es:di+0x5c] ; Get double indirect pointer + mov [BYTE bp+Ext2InodeDoubleIndirectPointer],edx ; Save double indirect pointer + + ; Now copy the direct pointers to 7000:0000 + ; so that we can call Ext2ReadDirectBlocks + push ds ; Save DS + push es + push WORD 7000h + pop es + pop ds + mov si,8028h + xor di,di ; DS:SI = 6000:8028 ES:DI = 7000:0000 + mov cx,24 ; Moving 24 words of data + rep movsw + pop ds ; Restore DS + + ; Now we have all the block pointers in the + ; right location so read them in + pop eax ; Restore the total number of blocks in this file + xor ecx,ecx ; Set the max count of blocks to read to 12 + mov cl,12 ; which is the number of direct block pointers in the inode + call Ext2ReadDirectBlockList + + ; Check to see if we actually have + ; blocks left to read + cmp eax,byte 0 + jz Ext2ReadEntireFileDone + + ; Now we have read all the direct blocks in + ; the inode. So now we have to read the indirect + ; block and read all it's direct blocks + push eax ; Save the total block count + mov eax,DWORD [BYTE bp+Ext2InodeIndirectPointer] ; Get the indirect block pointer + push WORD 7000h + pop es + xor bx,bx ; Set the load address to 7000:0000 + call Ext2ReadBlock ; Read the block + + ; Now we have all the block pointers from the + ; indirect block in the right location so read them in + pop eax ; Restore the total block count + mov ecx,DWORD [BYTE bp+Ext2PointersPerBlock] ; Get the number of block pointers that one block contains + call Ext2ReadDirectBlockList + + ; Check to see if we actually have + ; blocks left to read + cmp eax,byte 0 + jz Ext2ReadEntireFileDone + + ; Now we have read all the direct blocks from + ; the inode's indirect block pointer. So now + ; we have to read the double indirect block + ; and read all it's indirect blocks + ; (whew, it's a good thing I don't support triple indirect blocks) + mov [BYTE bp+Ext2BlocksLeftToRead],eax ; Save the total block count + mov eax,DWORD [BYTE bp+Ext2InodeDoubleIndirectPointer] ; Get the double indirect block pointer + push WORD 7800h + pop es + push es ; Save an extra copy of this value on the stack + xor bx,bx ; Set the load address to 7000:8000 + call Ext2ReadBlock ; Read the block + + pop es ; Put 7800h into ES (saved on the stack already) + xor di,di + +Ext2ReadIndirectBlock: + mov eax,DWORD [es:di] ; Get indirect block pointer + add di,BYTE 4 ; Update DI for next array index + push es + push di + + push WORD 7000h + pop es + xor bx,bx ; Set the load address to 7000:0000 + call Ext2ReadBlock ; Read the indirect block + + ; Now we have all the block pointers from the + ; indirect block in the right location so read them in + mov eax,DWORD [BYTE bp+Ext2BlocksLeftToRead] ; Restore the total block count + mov ecx,DWORD [BYTE bp+Ext2PointersPerBlock] ; Get the number of block pointers that one block contains + call Ext2ReadDirectBlockList + mov [BYTE bp+Ext2BlocksLeftToRead],eax ; Save the total block count + pop di + pop es + + ; Check to see if we actually have + ; blocks left to read + cmp eax,byte 0 + jnz Ext2ReadIndirectBlock + +Ext2ReadEntireFileDone: + ret + +; Reads a maximum number of blocks +; from an array at 7000:0000 +; and updates the total count +; ECX contains the max number of blocks to read +; EAX contains the number of blocks left to read +; On return: +; EAX contians the new number of blocks left to read +Ext2ReadDirectBlockList: + cmp eax,ecx ; Compare it to the maximum number of blocks to read + ja CallExt2ReadDirectBlocks ; If it will take more blocks then just read all of the blocks + mov cx,ax ; Otherwise adjust the block count accordingly + +CallExt2ReadDirectBlocks: + sub eax,ecx ; Subtract the number of blocks being read from the total count + push eax ; Save the new total count + call Ext2ReadDirectBlocks + pop eax ; Restore the total count + ret + + +; Reads a specified number of blocks +; from an array at 7000:0000 +; CX contains the number of blocks to read +Ext2ReadDirectBlocks: + + push WORD 7000h + pop es + xor di,di ; Set ES:DI = 7000:0000 + +Ext2ReadDirectBlocksLoop: + mov eax,[es:di] ; Get direct block pointer from array + add di,BYTE 4 ; Update DI for next array index + + push cx ; Save number of direct blocks left + push es ; Save array segment + push di ; Save array offset + mov es,[BYTE bp+Ext2ReadEntireFileLoadSegment] + xor bx,bx ; Setup load address for next read + + call Ext2ReadBlock ; Read the block (this updates ES for the next read) + + mov [BYTE bp+Ext2ReadEntireFileLoadSegment],es ; Save updated ES + + pop di ; Restore the array offset + pop es ; Restore the array segment + pop cx ; Restore the number of blocks left + + loop Ext2ReadDirectBlocksLoop + + ; At this point all the direct blocks should + ; be loaded and ES (Ext2ReadEntireFileLoadSegment) + ; should be ready for the next read. + ret + + + +; Displays a file not found error message +; And reboots +PrintFileNotFound: + mov si,msgFreeLdr ; FreeLdr not found message + jmp short DisplayItAndReboot + +; Displays a file size is 0 error +; And reboots +PrintFileSizeError: + mov si,msgFileSize ; Error message + jmp short DisplayItAndReboot + +; Displays a file is not a regular file error +; And reboots +PrintRegFileError: + mov si,msgRegFile ; Error message +DisplayItAndReboot: + call PutChars ; Display it + jmp Reboot + +msgFreeLdr db 'freeldr.sys not found',0 +msgFileSize db 'File size is 0',0 +msgRegFile db 'freeldr.sys isnt a regular file',0 +filename db 'freeldr.sys' +msgLoading db 'Loading FreeLoader...',0 + + times 1022-($-$$) db 0 ; Pad to 1022 bytes + + dw 0aa55h ; BootSector signature -- 2.17.1