diff options
-rw-r--r-- | ext2_fs.inc | 130 | ||||
-rw-r--r-- | extlinux.asm | 1265 |
2 files changed, 1395 insertions, 0 deletions
diff --git a/ext2_fs.inc b/ext2_fs.inc new file mode 100644 index 00000000..6d85596a --- /dev/null +++ b/ext2_fs.inc @@ -0,0 +1,130 @@ +; $Id$ +; ----------------------------------------------------------------------- +; +; Copyright 1998-1999 H. Peter Anvin - All Rights Reserved +; +; This program is free software; you can redistribute it and/or modify +; it under the terms of the GNU General Public License as published by +; the Free Software Foundation, Inc., 675 Mass Ave, Cambridge MA 02139, +; USA; either version 2 of the License, or (at your option) any later +; version; incorporated herein by reference. +; +; ----------------------------------------------------------------------- + +; +; ext2_fs.inc +; +; NASM include file for ext2fs data structures +; + +%define EXT2_SUPER_MAGIC 0xEF53 + +%define EXT2_GOOD_OLD_REV 0 ; The good old (original) format +%define EXT2_DYNAMIC_REV 1 ; V2 format w/ dynamic inode sizes + +%define EXT2_NDIR_BLOCKS 12 +%define EXT2_IND_BLOCK EXT2_NDIR_BLOCKS +%define EXT2_DIND_BLOCK (EXT2_IND_BLOCK+1) +%define EXT2_TIND_BLOCK (EXT2_DIND_BLOCK+1) +%define EXT2_N_BLOCKS (EXT2_TIND_BLOCK+1) + +; +; Structure definition for the ext2 superblock +; + struc ext2_super_block +s_inodes_count resd 1 ; Inodes count +s_blocks_count resd 1 ; Blocks count +s_r_blocks_count resd 1 ; Reserved blocks count +s_free_blocks_count resd 1 ; Free blocks count +s_free_inodes_count resd 1 ; Free inodes count +s_first_data_block resd 1 ; First Data Block +s_log_block_size resd 1 ; Block size +s_log_frag_size resd 1 ; Fragment size +s_blocks_per_group resd 1 ; # Blocks per group +s_frags_per_group resd 1 ; # Fragments per group +s_inodes_per_group resd 1 ; # Inodes per group +s_mtime resd 1 ; Mount time +s_wtime resd 1 ; Write time +s_mnt_count resw 1 ; Mount count +s_max_mnt_count resw 1 ; Maximal mount count +s_magic resw 1 ; Magic signature +s_state resw 1 ; File system state +s_errors resw 1 ; Behaviour when detecting errors +s_minor_rev_level resw 1 ; minor revision level +s_lastcheck resd 1 ; time of last check +s_checkinterval resd 1 ; max. time between checks +s_creator_os resd 1 ; OS +s_rev_level resd 1 ; Revision level +s_def_resuid resw 1 ; Default uid for reserved blocks +s_def_resgid resw 1 ; Default gid for reserved blocks +s_first_ino resd 1 ; First non-reserved inode +s_inode_size resw 1 ; size of inode structure +s_block_group_nr resw 1 ; block group # of this superblock +s_feature_compat resd 1 ; compatible feature set +s_feature_incompat resd 1 ; incompatible feature set +s_feature_ro_compat resd 1 ; readonly-compatible feature set +s_uuid resb 16 ; 128-bit uuid for volume +s_volume_name resb 16 ; volume name +s_last_mounted resb 64 ; directory where last mounted +s_algorithm_usage_bitmap resd 1 ; For compression +s_prealloc_blocks resb 1 ; Nr of blocks to try to preallocate +s_prealloc_dir_blocks resb 1 ; Nr to preallocate for dirs +s_padding1 resw 1 +s_reserved resd 204 ; Padding to the end of the block + endstruc + +%if ext2_super_block_size != 1024 +%error "ext2_super_block definition bogus" +%endif + +; +; Structure definition for the ext2 inode +; + struc ext2_inode +i_mode resw 1 ; File mode +i_uid resw 1 ; Owner Uid +i_size resd 1 ; Size in bytes +i_atime resd 1 ; Access time +i_ctime resd 1 ; Creation time +i_mtime resd 1 ; Modification time +i_dtime resd 1 ; Deletion Time +i_gid resw 1 ; Group Id +i_links_count resw 1 ; Links count +i_blocks resd 1 ; Blocks count +i_flags resd 1 ; File flags +l_i_reserved1 resd 1 +i_block resd EXT2_N_BLOCKS ; Pointer to blocks +i_version resd 1 ; File version (for NFS) +i_file_acl resd 1 ; File ACL +i_dir_acl resd 1 ; Directory ACL +i_faddr resd 1 ; Fragment address +l_i_frag resb 1 ; Fragment number +l_i_fsize resb 1 ; Fragment size +i_pad1 resw 1 +l_i_reserved2 resd 2 + endstruc + +%if ext2_inode_size != 128 +%error "ext2_inode definition bogus" +%endif + +; +; Structure definition for ext2 block group descriptor +; + struc ext2_group_desc +bg_block_bitmap resd 1 ; Block bitmap block +bg_inode_bitmap resd 1 ; Inode bitmap block +bg_inode_table resd 1 ; Inode table block +bg_free_blocks_count resw 1 ; Free blocks count +bg_free_inodes_count resw 1 ; Free inodes count +bg_used_dirs_count resw 1 ; Used inodes count +bg_pad resw 1 +bg_reserved resd 3 + endstruc + +%if ext2_group_desc_size != 32 +%error "ext2_group_desc definition bogus" +%endif + +%define ext2_group_desc_lg2size 5 + diff --git a/extlinux.asm b/extlinux.asm new file mode 100644 index 00000000..0b08a1f4 --- /dev/null +++ b/extlinux.asm @@ -0,0 +1,1265 @@ +; -*- fundamental -*- (asm-mode sucks) +; $Id$ +; **************************************************************************** +; +; extlinux.asm +; +; A program to boot Linux kernels off an ext2/ext3 filesystem. +; +; Copyright (C) 1994-2004 H. Peter Anvin +; +; This program is free software; you can redistribute it and/or modify +; it under the terms of the GNU General Public License as published by +; the Free Software Foundation, Inc., 53 Temple Place Ste 330, +; Boston MA 02111-1307, USA; either version 2 of the License, or +; (at your option) any later version; incorporated herein by reference. +; +; **************************************************************************** + +%define IS_EXTLINUX 1 +%include "macros.inc" +%include "config.inc" +%include "kernel.inc" +%include "bios.inc" +%include "tracers.inc" + +%include "ext2_fs.inc" + +; +; Some semi-configurable constants... change on your own risk. +; +my_id equ extlinux_id +FILENAME_MAX_LG2 equ 8 ; log2(Max filename size Including final null) +FILENAME_MAX equ (1 << FILENAME_MAX) ; Max mangled filename size +NULLFILE equ ' ' ; First char space == null filename +retry_count equ 6 ; How patient are we with the disk? +%assign HIGHMEM_SLOP 0 ; Avoid this much memory near the top +LDLINUX_MAGIC equ 0x3eb202fe ; A random number to identify ourselves with + +MAX_OPEN_LG2 equ 6 ; log2(Max number of open files) +MAX_OPEN equ (1 << MAX_OPEN_LG2) + +SECTOR_SHIFT equ 9 +SECTOR_SIZE equ (1 << SECTOR_SHIFT) + +; +; This is what we need to do when idle +; +%macro RESET_IDLE 0 + ; Nothing +%endmacro +%macro DO_IDLE 0 + ; Nothing +%endmacro + +; +; The following structure is used for "virtual kernels"; i.e. LILO-style +; option labels. The options we permit here are `kernel' and `append +; Since there is no room in the bottom 64K for all of these, we +; stick them at vk_seg:0000 and copy them down before we need them. +; +; Note: this structure can be added to, but it must +; +%define vk_power 6 ; log2(max number of vkernels) +%define max_vk (1 << vk_power) ; Maximum number of vkernels +%define vk_shift (16-vk_power) ; Number of bits to shift +%define vk_size (1 << vk_shift) ; Size of a vkernel buffer + + struc vkernel +vk_vname: resb FILENAME_MAX ; Virtual name **MUST BE FIRST!** +vk_rname: resb FILENAME_MAX ; Real name +vk_appendlen: resw 1 + alignb 4 +vk_append: resb max_cmd_len+1 ; Command line + alignb 4 +vk_end: equ $ ; Should be <= vk_size + endstruc + +%ifndef DEPEND +%if (vk_end > vk_size) || (vk_size*max_vk > 65536) +%error "Too many vkernels defined, reduce vk_power" +%endif +%endif + +; +; Segment assignments in the bottom 640K +; Stick to the low 512K in case we're using something like M-systems flash +; which load a driver into low RAM (evil!!) +; +; 0000h - main code/data segment (and BIOS segment) +; +real_mode_seg equ 4000h +cache_seg equ 3000h ; 64K area for metadata cache +vk_seg equ 2000h ; Virtual kernels +xfer_buf_seg equ 1000h ; Bounce buffer for I/O to high mem +comboot_seg equ real_mode_seg ; COMBOOT image loading zone + +; +; File structure. This holds the information for each currently open file. +; + struc open_file_t +file_sector resd 1 ; Next linear sector to read +file_left resd 1 ; Number of sectors left +file_in_sec resd 1 ; Sector where inode lives +file_in_off resw 1 ; +file_in_start resw 1 ; Offset to beginning of inode +file_1i_sec resd 1 ; First sector of current ind block +file_1i_off resw 1 ; Offset to next block ptr + resw 1 +file_2i_sec resd 1 ; First sector of current dind block +file_2i_off resw 1 ; Offset to next block ptr + resw 1 +file_3i_sec resd 1 ; First sector of current dind block +file_3i_off resw 1 ; Offset to next block ptr + resw 1 + resb 24 + endstruc + +%ifndef DEPEND +%if (open_file_t_size & (open_file_t_size-1)) +%error "open_file_t is not a power of 2" +%endif +%endif + +; --------------------------------------------------------------------------- +; BEGIN CODE +; --------------------------------------------------------------------------- + +; +; Memory below this point is reserved for the BIOS and the MBR +; +BSS_START equ 1000h + section .bss start=BSS_START +trackbufsize equ 8192 +trackbuf resb trackbufsize ; Track buffer goes here +getcbuf resb trackbufsize + ; ends at 5000h + + alignb 8 + + ; Expanded superblock +SuperInfo equ $ + resq 16 ; The first 16 bytes expanded 8 times +FAT resd 1 ; Location of (first) FAT +RootDirArea resd 1 ; Location of root directory area +RootDir resd 1 ; Location of root directory proper +DataArea resd 1 ; Location of data area +RootDirSize resd 1 ; Root dir size in sectors +TotalSectors resd 1 ; Total number of sectors +EndSector resd 1 ; Location of filesystem end +ClustSize resd 1 ; Bytes/cluster +ClustMask resd 1 ; Sectors/cluster - 1 +CachePtrs resw 65536/SECTOR_SIZE ; Cached sector pointers +NextCacheSlot resw 1 ; Next cache slot to occupy +CopySuper resb 1 ; Distinguish .bs versus .bss +DriveNumber resb 1 ; BIOS drive number +ClustShift resb 1 ; Shift count for sectors/cluster +ClustByteShift resb 1 ; Shift count for bytes/cluster + + alignb open_file_t_size +Files resb MAX_OPEN*open_file_t_size + +; +; Constants for the xfer_buf_seg +; +; The xfer_buf_seg is also used to store message file buffers. We +; need two trackbuffers (text and graphics), plus a work buffer +; for the graphics decompressor. +; +xbs_textbuf equ 0 ; Also hard-coded, do not change +xbs_vgabuf equ trackbufsize +xbs_vgatmpbuf equ 2*trackbufsize + + + section .text + org 7C00h +; +; Some of the things that have to be saved very early are saved +; "close" to the initial stack pointer offset, in order to +; reduce the code size... +; +StackBuf equ $-44-32 ; Start the stack here (grow down - 4K) +PartInfo equ StackBuf ; Saved partition table entry +FloppyTable equ PartInfo+16 ; Floppy info table (must follow PartInfo) +OrigFDCTabPtr equ StackBuf-4 ; The high dword on the stack + +; +; Primary entry point. Tempting as though it may be, we can't put the +; initial "cli" here; the jmp opcode in the first byte is part of the +; "magic number" (using the term very loosely) for the DOS superblock. +; +bootsec equ $ + jmp short start ; 2 bytes + nop ; 1 byte +; +; "Superblock" follows -- it's in the boot sector, so it's already +; loaded and ready for us +; +bsOemName db 'EXTLINUX' ; The SYS command sets this, so... +; +; These are the fields we actually care about. We end up expanding them +; all to dword size early in the code, so generate labels for both +; the expanded and unexpanded versions. +; +%macro superb 1 +bx %+ %1 equ SuperInfo+($-superblock)*8+4 +bs %+ %1 equ $ + zb 1 +%endmacro +%macro superw 1 +bx %+ %1 equ SuperInfo+($-superblock)*8 +bs %+ %1 equ $ + zw 1 +%endmacro +%macro superd 1 +bx %+ %1 equ $ ; no expansion for dwords +bs %+ %1 equ $ + zd 1 +%endmacro +superblock equ $ + superw BytesPerSec + superb SecPerClust + superw ResSectors + superb FATs + superw RootDirEnts + superw Sectors + superb Media + superw FATsecs + superw SecPerTrack + superw Heads +superinfo_size equ ($-superblock)-1 ; How much to expand + superd Hidden + superd HugeSectors + ; + ; This is as far as FAT12/16 and FAT32 are consistent + ; + zb 54 ; FAT12/16 need 26 more bytes, + ; FAT32 need 54 more bytes +superblock_len equ $-superblock + +SecPerClust equ bxSecPerClust +; +; Note we don't check the constraints above now; we did that at install +; time (we hope!) +; +start: + cli ; No interrupts yet, please + cld ; Copy upwards +; +; Set up the stack +; + xor ax,ax + mov ss,ax + mov sp,StackBuf ; Just below BSS + mov es,ax +; +; DS:SI may contain a partition table entry. Preserve it for us. +; + mov cx,8 ; Save partition info + mov di,sp + rep movsw + + mov ds,ax ; Now we can initialize DS... + +; +; Now sautee the BIOS floppy info block to that it will support decent- +; size transfers; the floppy block is 11 bytes and is stored in the +; INT 1Eh vector (brilliant waste of resources, eh?) +; +; Of course, if BIOSes had been properly programmed, we wouldn't have +; had to waste precious space with this code. +; + mov bx,fdctab + lfs si,[bx] ; FS:SI -> original fdctab + push fs ; Save on stack in case we need to bail + push si + + ; Save the old fdctab even if hard disk so the stack layout + ; is the same. The instructions above do not change the flags + mov [DriveNumber],dl ; Save drive number in DL + and dl,dl ; If floppy disk (00-7F), assume no + ; partition table + js harddisk + +floppy: + mov cl,6 ; 12 bytes (CX == 0) + ; es:di -> FloppyTable already + ; This should be safe to do now, interrupts are off... + mov [bx],di ; FloppyTable + mov [bx+2],ax ; Segment 0 + fs rep movsw ; Faster to move words + mov cl,[bsSecPerTrack] ; Patch the sector count + mov [di-8],cl + ; AX == 0 here + int 13h ; Some BIOSes need this + + jmp short not_harddisk +; +; The drive number and possibly partition information was passed to us +; by the BIOS or previous boot loader (MBR). Current "best practice" is to +; trust that rather than what the superblock contains. +; +; Would it be better to zero out bsHidden if we don't have a partition table? +; +; Note: di points to beyond the end of PartInfo +; +harddisk: + test byte [di-16],7Fh ; Sanity check: "active flag" should + jnz no_partition ; be 00 or 80 + mov eax,[di-8] ; Partition offset (dword) + mov [bsHidden],eax +no_partition: +; +; Get disk drive parameters (don't trust the superblock.) Don't do this for +; floppy drives -- INT 13:08 on floppy drives will (may?) return info about +; what the *drive* supports, not about the *media*. Fortunately floppy disks +; tend to have a fixed, well-defined geometry which is stored in the superblock. +; + ; DL == drive # still + mov ah,08h + int 13h + jc no_driveparm + and ah,ah + jnz no_driveparm + shr dx,8 + inc dx ; Contains # of heads - 1 + mov [bsHeads],dx + and cx,3fh + mov [bsSecPerTrack],cx +no_driveparm: +not_harddisk: +; +; Ready to enable interrupts, captain +; + sti + + +; +; Do we have EBIOS (EDD)? +; +eddcheck: + mov bx,55AAh + mov ah,41h ; EDD existence query + mov dl,[DriveNumber] + int 13h + jc .noedd + cmp bx,0AA55h + jne .noedd + test cl,1 ; Extended disk access functionality set + jz .noedd + ; + ; We have EDD support... + ; + mov byte [getlinsec+1],getlinsec_ebios-(getlinsec+2) +.noedd: + +; +; Load the first sector of LDLINUX.SYS; this used to be all proper +; with parsing the superblock and root directory; it doesn't fit +; together with EBIOS support, unfortunately. +; + mov eax,[FirstSector] ; Sector start + mov bx,ldlinux_sys ; Where to load it + call getonesec + + ; Some modicum of integrity checking + cmp dword [ldlinux_magic],LDLINUX_MAGIC + jne kaboom + cmp dword [ldlinux_magic+4],HEXDATE + jne kaboom + + ; Go for it... + jmp ldlinux_ent + +; +; kaboom: write a message and bail out. +; +kaboom: + xor si,si + mov ss,si + mov sp,StackBuf-4 ; Reset stack + mov ds,si ; Reset data segment + pop dword [fdctab] ; Restore FDC table +.patch: mov si,bailmsg + call writestr ; Returns with AL = 0 + cbw ; AH <- 0 + int 16h ; Wait for keypress + int 19h ; And try once more to boot... +.norge: jmp short .norge ; If int 19h returned; this is the end + +; +; +; writestr: write a null-terminated string to the console +; This assumes we're on page 0. This is only used for early +; messages, so it should be OK. +; +writestr: +.loop: lodsb + and al,al + jz .return + mov ah,0Eh ; Write to screen as TTY + mov bx,0007h ; Attribute + int 10h + jmp short .loop +.return: ret + +; +; xint13: wrapper for int 13h which will retry 6 times and then die, +; AND save all registers except BP +; +xint13: +.again: + mov bp,retry_count +.loop: pushad + int 13h + popad + jnc writestr.return + dec bp + jnz .loop +.disk_error: + jmp strict near kaboom ; Patched + + +; +; getonesec: get one disk sector +; +getonesec: + mov bp,1 ; One sector + ; Fall through + +; +; getlinsec: load a sequence of BP floppy sector given by the linear sector +; number in EAX into the buffer at ES:BX. We try to optimize +; by loading up to a whole track at a time, but the user +; is responsible for not crossing a 64K boundary. +; (Yes, BP is weird for a count, but it was available...) +; +; On return, BX points to the first byte after the transferred +; block. +; +; This routine assumes CS == DS, and trashes most registers. +; +; Stylistic note: use "xchg" instead of "mov" when the source is a register +; that is dead from that point; this saves space. However, please keep +; the order to dst,src to keep things sane. +; +getlinsec: + jmp strict short getlinsec_cbios ; This is patched + +; +; getlinsec_ebios: +; +; getlinsec implementation for EBIOS (EDD) +; +getlinsec_ebios: + mov si,dapa ; Load up the DAPA + mov [si+4],bx + mov [si+6],es + mov [si+8],eax +.loop: + push bp ; Sectors left + call maxtrans ; Enforce maximum transfer size +.bp_ok: + mov [si+2],bp + mov dl,[DriveNumber] + mov ah,42h ; Extended Read + call xint13 + pop bp + movzx eax,word [si+2] ; Sectors we read + add [si+8],eax ; Advance sector pointer + sub bp,ax ; Sectors left + shl ax,9 ; 512-byte sectors + add [si+4],ax ; Advance buffer pointer + and bp,bp + jnz .loop + mov eax,[si+8] ; Next sector + mov bx,[si+4] ; Buffer pointer + ret + +; +; getlinsec_cbios: +; +; getlinsec implementation for legacy CBIOS +; +getlinsec_cbios: +.loop: + push eax + push bp + push bx + + movzx esi,word [bsSecPerTrack] + movzx edi,word [bsHeads] + ; + ; Dividing by sectors to get (track,sector): we may have + ; up to 2^18 tracks, so we need to use 32-bit arithmetric. + ; + xor edx,edx ; Zero-extend LBA to 64 bits + div esi + xor cx,cx + xchg cx,dx ; CX <- sector index (0-based) + ; EDX <- 0 + ; eax = track # + div edi ; Convert track to head/cyl + ; + ; Now we have AX = cyl, DX = head, CX = sector (0-based), + ; BP = sectors to transfer, SI = bsSecPerTrack, + ; ES:BX = data target + ; + + call maxtrans ; Enforce maximum transfer size + + ; Must not cross track boundaries, so BP <= SI-CX + sub si,cx + cmp bp,si + jna .bp_ok + mov bp,si +.bp_ok: + + shl ah,6 ; Because IBM was STOOPID + ; and thought 8 bits were enough + ; then thought 10 bits were enough... + inc cx ; Sector numbers are 1-based, sigh + or cl,ah + mov ch,al + mov dh,dl + mov dl,[DriveNumber] + xchg ax,bp ; Sector to transfer count + mov ah,02h ; Read sectors + call xint13 + movzx ecx,al + shl ax,9 ; Convert sectors in AL to bytes in AX + pop bx + add bx,ax + pop bp + pop eax + add eax,ecx + sub bp,cx + jnz .loop + ret + +; +; Truncate BP to MaxTransfer +; +maxtrans: + cmp bp,[MaxTransfer] + jna .ok + mov bp,[MaxTransfer] +.ok: ret + +; +; Error message on failure +; +bailmsg: db 'Boot failed', 0Dh, 0Ah, 0 + +; +; EBIOS disk address packet +; + align 4, db 0 +dapa: + dw 16 ; Packet size +.count: dw 0 ; Block count +.off: dw 0 ; Offset of buffer +.seg: dw 0 ; Segment of buffer +.lba: dd 0 ; LBA (LSW) + dd 0 ; LBA (MSW) + + +%if 1 +bs_checkpt_off equ ($-$$) +%ifndef DEPEND +%if bs_checkpt_off > 1F8h +%error "Boot sector overflow" +%endif +%endif + + zb 1F8h-($-$$) +%endif +FirstSector dd 0xDEADBEEF ; Location of sector 1 +MaxTransfer dw 0x007F ; Max transfer size +bootsignature dw 0AA55h + +; +; =========================================================================== +; End of boot sector +; =========================================================================== +; Start of LDLINUX.SYS +; =========================================================================== + +ldlinux_sys: + +syslinux_banner db 0Dh, 0Ah + db 'EXTLINUX ' + db version_str, ' ', date, ' ', 0 + db 0Dh, 0Ah, 1Ah ; EOF if we "type" this in DOS + + align 8, db 0 +ldlinux_magic dd LDLINUX_MAGIC + dd HEXDATE + +; +; This area is patched by the installer. It is found by looking for +; LDLINUX_MAGIC, plus 8 bytes. +; +patch_area: +LDLDwords dw 0 ; Total dwords starting at ldlinux_sys +LDLSectors dw 0 ; Number of sectors - (bootsec+this sec) +CheckSum dd 0 ; Checksum starting at ldlinux_sys + ; value = LDLINUX_MAGIC - [sum of dwords] + +; Space for up to 64 sectors, the theoretical maximum +SectorPtrs times 64 dd 0 + +ldlinux_ent: +; +; Note that some BIOSes are buggy and run the boot sector at 07C0:0000 +; instead of 0000:7C00 and the like. We don't want to add anything +; more to the boot sector, so it is written to not assume a fixed +; value in CS, but we don't want to deal with that anymore from now +; on. +; + jmp 0:.next +.next: + +; +; Tell the user we got this far +; + mov si,syslinux_banner + call writestr + +; +; Patch disk error handling +; + mov word [xint13.disk_error+1],do_disk_error-(xint13.disk_error+3) + +; +; Now we read the rest of LDLINUX.SYS. Don't bother loading the first +; sector again, though. +; +load_rest: + mov si,SectorPtrs + mov bx,7C00h+2*SECTOR_SIZE ; Where we start loading + mov cx,[LDLSectors] + +.get_chunk: + jcxz .done + xor bp,bp + lodsd ; First sector of this chunk + + mov edx,eax + +.make_chunk: + inc bp + dec cx + jz .chunk_ready + inc edx ; Next linear sector + cmp [esi],edx ; Does it match + jnz .chunk_ready ; If not, this is it + add esi,4 ; If so, add sector to chunk + jmp short .make_chunk + +.chunk_ready: + call getlinsecsr + shl bp,SECTOR_SHIFT + add bx,bp + jmp .get_chunk + +.done: + +; +; All loaded up, verify that we got what we needed. +; Note: the checksum field is embedded in the checksum region, so +; by the time we get to the end it should all cancel out. +; +verify_checksum: + mov si,ldlinux_sys + mov cx,[LDLDwords] + mov edx,-LDLINUX_MAGIC +.checksum: + lodsd + add edx,eax + loop .checksum + + and edx,edx ; Should be zero + jz all_read ; We're cool, go for it! + +; +; Uh-oh, something went bad... +; + mov si,checksumerr_msg + call writestr + jmp kaboom + +; +; ----------------------------------------------------------------------------- +; Subroutines that have to be in the first sector +; ----------------------------------------------------------------------------- + +; +; getlinsecsr: save registers, call getlinsec, restore registers +; +getlinsecsr: pushad + call getlinsec + popad + ret + +; +; This routine captures disk errors, and tries to decide if it is +; time to reduce the transfer size. +; +do_disk_error: + cmp ah,42h + je .ebios + shr al,1 ; Try reducing the transfer size + mov [MaxTransfer],al + jz kaboom ; If we can't, we're dead... + jmp xint13 ; Try again +.ebios: + push ax + mov ax,[si+2] + shr ax,1 + mov [MaxTransfer],ax + mov [si+2],ax + pop ax + jmp xint13 + +; +; Checksum error message +; +checksumerr_msg db 'Load error - ', 0 ; Boot failed appended + +; +; Debug routine +; +%ifdef debug +safedumpregs: + cmp word [Debug_Magic],0D00Dh + jnz nc_return + jmp dumpregs +%endif + +rl_checkpt equ $ ; Must be <= 8000h + +rl_checkpt_off equ ($-$$) +%if 0 ; ndef DEPEND +%if rl_checkpt_off > 400h +%error "Sector 1 overflow" +%endif +%endif + +; ---------------------------------------------------------------------------- +; End of code and data that have to be in the first sector +; ---------------------------------------------------------------------------- + +all_read: +; +; Let the user (and programmer!) know we got this far. This used to be +; in Sector 1, but makes a lot more sense here. +; + mov si,copyright_str + call writestr + +; +; Insane hack to expand the DOS superblock to dwords +; +expand_super: + xor eax,eax + mov si,superblock + mov di,SuperInfo + mov cx,superinfo_size +.loop: + lodsw + dec si + stosd ; Store expanded word + xor ah,ah + stosd ; Store expanded byte + loop .loop + +; +; Compute some information about this filesystem. +; + +; +; Common initialization code +; +%include "cpuinit.inc" + +; +; Clear Files structures +; + mov di,Files + mov cx,(MAX_OPEN*open_file_t_size)/4 + xor eax,eax + rep stosd + +; +; Initialize the metadata cache +; + call initcache + +; +; Initialization that does not need to go into the any of the pre-load +; areas +; + ; Now set up screen parameters + call adjust_screen + + ; Wipe the F-key area + mov al,NULLFILE + mov di,FKeyName + mov cx,10*(1 << FILENAME_MAX_LG2) + rep stosb + +; +; Now, everything is "up and running"... patch kaboom for more +; verbosity and using the full screen system +; + ; E9 = JMP NEAR + mov dword [kaboom.patch],0e9h+((kaboom2-(kaboom.patch+3)) << 8) + +; +; Now we're all set to start with our *real* business. First load the +; configuration file (if any) and parse it. +; +; In previous versions I avoided using 32-bit registers because of a +; rumour some BIOSes clobbered the upper half of 32-bit registers at +; random. I figure, though, that if there are any of those still left +; they probably won't be trying to install Linux on them... +; +; The code is still ripe with 16-bitisms, though. Not worth the hassle +; to take'm out. In fact, we may want to put them back if we're going +; to boot ELKS at some point. +; + mov si,linuxauto_cmd ; Default command: "linux auto" + mov di,default_cmd + mov cx,linuxauto_len + rep movsb + + mov di,KbdMap ; Default keymap 1:1 + xor al,al + inc ch ; CX <- 256 +mkkeymap: stosb + inc al + loop mkkeymap + +; +; Load configuration file +; + mov di,syslinux_cfg + call open + jz no_config_file + +; +; Now we have the config file open. Parse the config file and +; run the user interface. +; +%include "ui.inc" + +; +; Linux kernel loading code is common. +; +%include "runkernel.inc" + +; +; COMBOOT-loading code +; +%include "comboot.inc" +%include "com32.inc" +%include "cmdline.inc" + +; +; Boot sector loading code +; +%include "bootsect.inc" + +; +; abort_check: let the user abort with <ESC> or <Ctrl-C> +; +abort_check: + call pollchar + jz ac_ret1 + pusha + call getchar + cmp al,27 ; <ESC> + je ac_kill + cmp al,3 ; <Ctrl-C> + jne ac_ret2 +ac_kill: mov si,aborted_msg + +; +; abort_load: Called by various routines which wants to print a fatal +; error message and return to the command prompt. Since this +; may happen at just about any stage of the boot process, assume +; our state is messed up, and just reset the segment registers +; and the stack forcibly. +; +; SI = offset (in _text) of error message to print +; +abort_load: + mov ax,cs ; Restore CS = DS = ES + mov ds,ax + mov es,ax + cli + mov sp,StackBuf-2*3 ; Reset stack + mov ss,ax ; Just in case... + sti + call cwritestr ; Expects SI -> error msg +al_ok: jmp enter_command ; Return to command prompt +; +; End of abort_check +; +ac_ret2: popa +ac_ret1: ret + +; +; allocate_file: Allocate a file structure +; +; If successful: +; ZF set +; BX = file pointer +; In unsuccessful: +; ZF clear +; +allocate_file: + TRACER 'a' + push cx + mov bx,Files + mov cx,MAX_OPEN +.check: cmp dword [bx], byte 0 + je .found + add bx,open_file_t_size ; ZF = 0 + loop .check + ; ZF = 0 if we fell out of the loop +.found: pop cx + ret + +; +; searchdir: +; Search the root directory for a pre-mangled filename in DS:DI. +; +; NOTE: This file considers finding a zero-length file an +; error. This is so we don't have to deal with that special +; case elsewhere in the program (most loops have the test +; at the end). +; +; If successful: +; ZF clear +; SI = file pointer +; DX:AX = file length in bytes +; If unsuccessful +; ZF set +; + +searchdir: + call allocate_file + jnz .alloc_failure + + ret +; +; writechr: Write a single character in AL to the console without +; mangling any registers; handle video pages correctly. +; +writechr: + call write_serial ; write to serial port if needed + pushfd + pushad + mov ah,0Eh + mov bl,07h ; attribute + mov bh,[cs:BIOS_page] ; current page + int 10h + popad + popfd + ret + +; +; +; kaboom2: once everything is loaded, replace the part of kaboom +; starting with "kaboom.patch" with this part + +kaboom2: + mov si,err_bootfailed + call cwritestr + call getchar + call vgaclearmode + int 19h ; And try once more to boot... +.norge: jmp short .norge ; If int 19h returned; this is the end + +; +; mangle_name: Mangle a DOS filename pointed to by DS:SI into a buffer pointed +; to by ES:DI; ends on encountering any whitespace +; + +mangle_name: + ret + +; +; unmangle_name: Does the opposite of mangle_name; converts a DOS-mangled +; filename to the conventional representation. This is needed +; for the BOOT_IMAGE= parameter for the kernel. +; NOTE: A 13-byte buffer is mandatory, even if the string is +; known to be shorter. +; +; DS:SI -> input mangled file name +; ES:DI -> output buffer +; +; On return, DI points to the first byte after the output name, +; which is set to a null byte. +; +unmangle_name: + ret + + +; +; getsector: Convert a linear sector index in a file to a linear sector number +; EAX -> linear sector number +; DS:SI -> open_file_t +; +; Returns next sector number in EAX; CF on EOF (not an error!) +; +nextsector: + push gs + push ebx + push esi + push edi + push ecx + push edx + push ebp + + push eax + mov cl,[ClustShift] + shr eax,cl ; Convert to block number + push eax + mov eax,[si+file_in_sec] + mov bx,si + call getcachesector ; Get inode + add si,[bx+file_in_off] ; Get *our* inode + pop eax + lea ebx,[i_block+4*eax] + cmp eax,EXT2_NDIR_BLOCKS + jb .direct + mov ebx,[gs:si+i_block+4*EXT2_IND_BLOCK] + sub eax,EXT2_NDIR_BLOCKS + mov ebp,[PtrsPerBlk1] + jb .ind1 + mov ebx,[gs:si+i_block+4*EXT2_DIND_BLOCK] + sub eax,ebp + mov ebp,[PtrsPerBlock2] + cmp eax,ebp + jb .ind2 + mov ebx,[gs:si+i_block+4*EXT2_TIND_BLOCK] + sub eax,ebp + +.ind3: + ; Triple indirect; eax contains the block no + ; with respect to the start of the tind area; + ; ebx contains the pointer to the tind block. + xor edx,edx + div dword [PtrsPerBlock3] + ; EAX = which dind block, EDX = block no in dind block + + shl ebx,cl ; Sector # for tind block + push ax + shr eax,SECTOR_SHIFT-2 ; Sector within tind block + add eax,ebx + call getcachesector + pop bx + shl bx,2 ; Convert to byte offset + and bx,SECTOR_SIZE-1 + + mov ebx,[gs:bx+si] ; Get the pointer + mov eax,edx ; The int2 code wants the remainder... +.ind2: + ; Double indirect; eax contains the block no + ; with respect to the start of the dind area; + ; ebx contains the pointer to the dind block. + xor edx,edx + div dword [PtrsPerBlock2] + ; EAX = which dind block, EDX = block no in dind block + + shl ebx,cl ; Sector # for tind block + push ax + shr eax,SECTOR_SHIFT-2 ; Sector within tind block + add eax,ebx + call getcachesector + pop bx + shl bx,2 ; Convert to byte offset + and bx,SECTOR_SIZE-1 + + mov ebx,[gs:bx+si] ; Get the pointer + mov eax,edx ; The ind1 code wants the remainder... +.ind1: + ; Single indirect; eax contains the block no + ; with respect to the start of the ind area; + ; ebx contains the pointer to the ind block. + xor edx,edx + div dword [PtrsPerBlock1] + ; EAX = which dind block, EDX = block no in dind block + + shl ebx,cl ; Sector # for tind block + push ax + shr eax,SECTOR_SHIFT-2 ; Sector within tind block + add eax,ebx + call getcachesector + pop bx + shl bx,2 ; Convert to byte offset + and bx,SECTOR_SIZE-1 + +.direct: + mov ebx,[gs:bx+si] ; Get the pointer + + pop eax ; Get the sector index again + shl ebx,cl ; Convert block number to sector + and eax,[ClustMask] ; Add offset within block + add eax,ebx + + pop ebp + pop edx + pop ecx + pop edi + pop esi + pop ebx + pop gs + ret + +; +; getfssec: Get multiple sectors from a file +; +; Same as above, except SI is a pointer to a open_file_t +; +; ES:BX -> Buffer +; DS:SI -> Pointer to open_file_t +; CX -> Sector count (0FFFFh = until end of file) +; Must not exceed the ES segment +; Returns CF=1 on EOF (not necessarily error) +; All arguments are advanced to reflect data read. +; +getfssec: + ret + + +; ----------------------------------------------------------------------------- +; Common modules +; ----------------------------------------------------------------------------- + +%include "getc.inc" ; getc et al +%include "conio.inc" ; Console I/O +%include "writestr.inc" ; String output +%include "parseconfig.inc" ; High-level config file handling +%include "parsecmd.inc" ; Low-level config file handling +%include "bcopy32.inc" ; 32-bit bcopy +%include "loadhigh.inc" ; Load a file into high memory +%include "font.inc" ; VGA font stuff +%include "graphics.inc" ; VGA graphics +%include "highmem.inc" ; High memory sizing +%include "strcpy.inc" ; strcpy() +%include "cache.inc" + +; ----------------------------------------------------------------------------- +; Begin data section +; ----------------------------------------------------------------------------- + + section .data +copyright_str db ' Copyright (C) 1994-', year, ' H. Peter Anvin' + db CR, LF, 0 +boot_prompt db 'boot: ', 0 +wipe_char db BS, ' ', BS, 0 +err_notfound db 'Could not find kernel image: ',0 +err_notkernel db CR, LF, 'Invalid or corrupt kernel image.', CR, LF, 0 +err_noram db 'It appears your computer has less than ' + asciidec dosram_k + db 'K of low ("DOS")' + db CR, LF + db 'RAM. Linux needs at least this amount to boot. If you get' + db CR, LF + db 'this message in error, hold down the Ctrl key while' + db CR, LF + db 'booting, and I will take your word for it.', CR, LF, 0 +err_badcfg db 'Unknown keyword in syslinux.cfg.', CR, LF, 0 +err_noparm db 'Missing parameter in syslinux.cfg.', CR, LF, 0 +err_noinitrd db CR, LF, 'Could not find ramdisk image: ', 0 +err_nohighmem db 'Not enough memory to load specified kernel.', CR, LF, 0 +err_highload db CR, LF, 'Kernel transfer failure.', CR, LF, 0 +err_oldkernel db 'Cannot load a ramdisk with an old kernel image.' + db CR, LF, 0 +err_notdos db ': attempted DOS system call', CR, LF, 0 +err_comlarge db 'COMBOOT image too large.', CR, LF, 0 +err_a20 db CR, LF, 'A20 gate not responding!', CR, LF, 0 +err_bootfailed db CR, LF, 'Boot failed: please change disks and press ' + db 'a key to continue.', CR, LF, 0 +ready_msg db 'Ready.', CR, LF, 0 +crlfloading_msg db CR, LF +loading_msg db 'Loading ', 0 +dotdot_msg db '.' +dot_msg db '.', 0 +aborted_msg db ' aborted.' ; Fall through to crlf_msg! +crlf_msg db CR, LF +null_msg db 0 +crff_msg db CR, FF, 0 +ConfigName db 'syslinux.cfg',0 ; Unmangled form + +; +; Command line options we'd like to take a look at +; +; mem= and vga= are handled as normal 32-bit integer values +initrd_cmd db 'initrd=' +initrd_cmd_len equ 7 + +; +; Config file keyword table +; +%include "keywords.inc" + +; +; Extensions to search for (in *forward* order). +; + align 4, db 0 +exten_table: db '.cbt' ; COMBOOT (specific) + db '.img' ; Disk image + db '.bs', 0 ; Boot sector + db '.com' ; COMBOOT (same as DOS) + db '.c32' ; COM32 +exten_table_end: + dd 0, 0 ; Need 8 null bytes here + +; +; Misc initialized (data) variables +; +%ifdef debug ; This code for debugging only +debug_magic dw 0D00Dh ; Debug code sentinel +%endif +AppendLen dw 0 ; Bytes in append= command +OntimeoutLen dw 0 ; Bytes in ontimeout command +OnerrorLen dw 0 ; Bytes in onerror command +KbdTimeOut dw 0 ; Keyboard timeout (if any) +CmdLinePtr dw cmd_line_here ; Command line advancing pointer +initrd_flag equ $ +initrd_ptr dw 0 ; Initial ramdisk pointer/flag +VKernelCtr dw 0 ; Number of registered vkernels +ForcePrompt dw 0 ; Force prompt +AllowImplicit dw 1 ; Allow implicit kernels +AllowOptions dw 1 ; User-specified options allowed +SerialPort dw 0 ; Serial port base (or 0 for no serial port) +VGAFontSize dw 16 ; Defaults to 16 byte font +UserFont db 0 ; Using a user-specified font +ScrollAttribute db 07h ; White on black (for text mode) + + alignb 4, db 0 +BufSafe dw trackbufsize/SECTOR_SIZE ; Clusters we can load into trackbuf +BufSafeSec dw trackbufsize/SECTOR_SIZE ; = how many sectors? +BufSafeBytes dw trackbufsize ; = how many bytes? +EndOfGetCBuf dw getcbuf+trackbufsize ; = getcbuf+BufSafeBytes +%ifndef DEPEND +%if ( trackbufsize % SECTOR_SIZE ) != 0 +%error trackbufsize must be a multiple of SECTOR_SIZE +%endif +%endif +; +; Stuff for the command line; we do some trickery here with equ to avoid +; tons of zeros appended to our file and wasting space +; +linuxauto_cmd db 'linux auto',0 +linuxauto_len equ $-linuxauto_cmd +boot_image db 'BOOT_IMAGE=' +boot_image_len equ $-boot_image + + align 4, db 0 ; Pad out any unfinished dword +ldlinux_end equ $ |