diff options
Diffstat (limited to 'core/extlinux.asm')
-rw-r--r-- | core/extlinux.asm | 902 |
1 files changed, 3 insertions, 899 deletions
diff --git a/core/extlinux.asm b/core/extlinux.asm index 503bbf7e..1f5423b2 100644 --- a/core/extlinux.asm +++ b/core/extlinux.asm @@ -18,909 +18,13 @@ %define IS_EXTLINUX 1 %include "head.inc" -%include "ext2_fs.inc" ; ; Some semi-configurable constants... change on your own risk. ; my_id equ extlinux_id -; NASM 0.98.38 croaks if these are equ's rather than macros... -FILENAME_MAX_LG2 equ 8 ; log2(Max filename size Including final null) -FILENAME_MAX equ (1 << FILENAME_MAX_LG2) ; Max mangled filename size -NULLFILE equ 0 ; Null character == empty filename -NULLOFFSET equ 0 ; Position in which to look -retry_count equ 16 ; 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) + extern ext2_fs_ops +ROOT_FS_OPS equ ext2_fs_ops -SECTOR_SHIFT equ 9 -SECTOR_SIZE equ (1 << SECTOR_SHIFT) - -MAX_SYMLINKS equ 64 ; Maximum number of symlinks per lookup -SYMLINK_SECTORS equ 2 ; Max number of sectors in a symlink - ; (should be >= FILENAME_MAX) - -ROOT_DIR_WORD equ 0x002F -CUR_DIR_DWORD equ 0x00002F2E - -; -; 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 in high memory and copy them down before we need them. -; - struc vkernel -vk_vname: resb FILENAME_MAX ; Virtual name **MUST BE FIRST!** -vk_rname: resb FILENAME_MAX ; Real name -vk_appendlen: resw 1 -vk_type: resb 1 ; Type of file - alignb 4 -vk_append: resb max_cmd_len+1 ; Command line - alignb 4 -vk_end: equ $ ; Should be <= vk_size - endstruc - -; -; File structure. This holds the information for each currently open file. -; - struc open_file_t -file_bytesleft resd 1 ; Number of bytes left (0 = free) -file_sector resd 1 ; Next linear sector to read -file_in_sec resd 1 ; Sector where inode lives -file_in_off resw 1 -file_mode resw 1 - 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 -; - section .earlybss -trackbufsize equ 8192 -trackbuf resb trackbufsize ; Track buffer goes here - ; ends at 2800h - - section .bss16 -SuperBlock resb 1024 ; ext2 superblock -ClustSize resd 1 ; Bytes/cluster ("block") -ClustMask resd 1 ; Sectors/cluster - 1 -PtrsPerBlock1 resd 1 ; Pointers/cluster -PtrsPerBlock2 resd 1 ; (Pointers/cluster)^2 -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 - -; -; Common bootstrap code for disk-based derivatives -; -%include "diskstart.inc" - -; -; Common initialization code -; -%include "init.inc" -%include "cpuinit.inc" - -; -; Load the real (ext2) superblock; 1024 bytes long at offset 1024 -; - mov bx,SuperBlock - mov eax,1024 >> SECTOR_SHIFT - mov bp,ax - call getlinsecsr - -; -; Compute some values... -; - xor edx,edx - inc edx - - ; s_log_block_size = log2(blocksize) - 10 - mov cl,[SuperBlock+s_log_block_size] - add cl,10 - mov [ClustByteShift],cl - mov eax,edx - shl eax,cl - mov [ClustSize],eax - - sub cl,SECTOR_SHIFT - mov [ClustShift],cl - shr eax,SECTOR_SHIFT - mov [SecPerClust],eax - dec eax - mov [ClustMask],eax - - add cl,SECTOR_SHIFT-2 ; 4 bytes/pointer - shl edx,cl - mov [PtrsPerBlock1],edx - shl edx,cl - mov [PtrsPerBlock2],edx - -; -; Initialize the metadata cache -; - call initcache - -; -; Now, everything is "up and running"... patch kaboom for more -; verbosity and using the full screen system -; - ; E9 = JMP NEAR - mov di,kaboom.patch - mov al,0e9h - stosb - mov ax,kaboom2-2 - sub ax,di - stosw - -; -; 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. -; - -; -; Load configuration file -; -load_config: - - mov si,config_name ; Save config file name - mov di,ConfigName - call strcpy - mov dword [CurrentDirName],CUR_DIR_DWORD ; Write './',0,0 to the CurrentDirName - call build_curdir_str - - mov di,ConfigName - pm_call load_env32 - - 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" - -; -; getlinsec_ext: same as getlinsec, except load any sector from the zero -; block as all zeros; use to load any data derived -; from an ext2 block pointer, i.e. anything *except the -; superblock.* -; -getonesec_ext: - mov bp,1 - -getlinsec_ext: - cmp eax,[SecPerClust] - jae getlinsecsr ; Nothing fancy - - ; If we get here, at least part of what we want is in the - ; zero block. Zero one sector at a time and loop. - push eax - push cx - xchg di,bx - xor eax,eax - mov cx,SECTOR_SIZE >> 2 - rep stosd - xchg di,bx - pop cx - pop eax - inc eax - dec bp - jnz getlinsec_ext - 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 -; -; open_inode: -; Open a file indicated by an inode number in EAX -; -; 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 -; EAX = file length in bytes -; ThisInode = the first 128 bytes of the inode -; If unsuccessful -; ZF set -; -; Assumes CS == DS == ES. -; -open_inode.allocate_failure: - xor eax,eax - pop bx - pop di - ret - -open_inode: - push di - push bx - call allocate_file - jnz .allocate_failure - - push cx - push gs - ; First, get the appropriate inode group and index - dec eax ; There is no inode 0 - xor edx,edx - mov [bx+file_sector],edx - div dword [SuperBlock+s_inodes_per_group] - ; EAX = inode group; EDX = inode within group - push edx - - ; Now, we need the block group descriptor. - ; To get that, we first need the relevant descriptor block. - - shl eax, ext2_group_desc_lg2size ; Get byte offset in desc table - xor edx,edx - div dword [ClustSize] - ; eax = block #, edx = offset in block - add eax,dword [SuperBlock+s_first_data_block] - inc eax ; s_first_data_block+1 - mov cl,[ClustShift] - shl eax,cl - push edx - shr edx,SECTOR_SHIFT - add eax,edx - pop edx - and dx,SECTOR_SIZE-1 - call getcachesector ; Get the group descriptor - add si,dx - mov esi,[gs:si+bg_inode_table] ; Get inode table block # - pop eax ; Get inode within group - movzx edx, word [SuperBlock+s_inode_size] - mul edx - ; edx:eax = byte offset in inode table - div dword [ClustSize] - ; eax = block # versus inode table, edx = offset in block - add eax,esi - shl eax,cl ; Turn into sector - push dx - shr edx,SECTOR_SHIFT - add eax,edx - mov [bx+file_in_sec],eax - pop dx - and dx,SECTOR_SIZE-1 - mov [bx+file_in_off],dx - - call getcachesector - add si,dx - mov cx,EXT2_GOOD_OLD_INODE_SIZE >> 2 - mov di,ThisInode - gs rep movsd - - mov ax,[ThisInode+i_mode] - mov [bx+file_mode],ax - mov eax,[ThisInode+i_size] - mov [bx+file_bytesleft],eax - mov si,bx - and eax,eax ; ZF clear unless zero-length file - pop gs - pop cx - pop bx - pop di - ret - - section .bss16 - alignb 4 -ThisInode resb EXT2_GOOD_OLD_INODE_SIZE ; The most recently opened inode - - section .text16 -; -; close_file: -; Deallocates a file structure (pointer in SI) -; Assumes CS == DS. -; -close_file: - and si,si - jz .closed - mov dword [si],0 ; First dword == file_bytesleft - xor si,si -.closed: 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 = EAX = file length in bytes -; If unsuccessful -; ZF set -; -; Assumes CS == DS == ES; *** IS THIS CORRECT ***? -; -searchdir: - push bx - push cx - push bp - mov byte [SymlinkCtr],MAX_SYMLINKS - - mov eax,[CurrentDir] -.begin_path: -.leadingslash: - cmp byte [di],'/' ; Absolute filename? - jne .gotdir - mov eax,EXT2_ROOT_INO - inc di ; Skip slash - jmp .leadingslash -.gotdir: - - ; At this point, EAX contains the directory inode, - ; and DS:DI contains a pathname tail. -.open: - push eax ; Save directory inode - - call open_inode - jz .missing ; If error, done - - mov cx,[si+file_mode] - shr cx,S_IFSHIFT ; Get file type - - cmp cx,T_IFDIR - je .directory - - add sp,4 ; Drop directory inode - - cmp cx,T_IFREG - je .file - cmp cx,T_IFLNK - je .symlink - - ; Otherwise, something bad... -.err: - call close_file -.err_noclose: - xor eax,eax - xor si,si - cwd ; DX <- 0 - -.done: - and eax,eax ; Set/clear ZF - pop bp - pop cx - pop bx - ret - -.missing: - add sp,4 ; Drop directory inode - jmp .done - - ; - ; It's a file. - ; -.file: - cmp byte [di],0 ; End of path? - je .done ; If so, done - jmp .err ; Otherwise, error - - ; - ; It's a directory. - ; -.directory: - pop dword [ThisDir] ; Remember what directory we're searching - - cmp byte [di],0 ; More path? - je .err ; If not, bad - -.skipslash: ; Skip redundant slashes - cmp byte [di],'/' - jne .readdir - inc di - jmp .skipslash - -.readdir: - mov cx,[SecPerClust] - push cx - shl cx,SECTOR_SHIFT - mov bx,trackbuf - add cx,bx - mov [EndBlock],cx - pop cx - push bx - call getfssec - pop bx - pushf ; Save EOF flag - push si ; Save filesystem pointer -.getent: - cmp bx,[EndBlock] - jae .endblock - - push di - cmp dword [bx+d_inode],0 ; Zero inode = void entry - je .nope - - movzx cx,byte [bx+d_name_len] - lea si,[bx+d_name] - repe cmpsb - je .maybe -.nope: - pop di - add bx,[bx+d_rec_len] - jmp .getent - -.endblock: - pop si - popf - jnc .readdir ; There is more - jmp .err ; Otherwise badness... - -.maybe: - mov eax,[bx+d_inode] - - ; Does this match the end of the requested filename? - cmp byte [di],0 - je .finish - cmp byte [di],'/' - jne .nope - - ; We found something; now we need to open the file -.finish: - pop bx ; Adjust stack (di) - pop si - call close_file ; Close directory - pop bx ; Adjust stack (flags) - jmp .open - - ; - ; It's a symlink. We have to determine if it's a fast symlink - ; (data stored in the inode) or not (data stored as a regular - ; file.) Either which way, we start from the directory - ; which we just visited if relative, or from the root directory - ; if absolute, and append any remaining part of the path. - ; -.symlink: - dec byte [SymlinkCtr] - jz .err ; Too many symlink references - - cmp eax,SYMLINK_SECTORS*SECTOR_SIZE - jae .err ; Symlink too long - - ; Computation for fast symlink, as defined by ext2/3 spec - xor ecx,ecx - cmp [ThisInode+i_file_acl],ecx - setne cl ; ECX <- i_file_acl ? 1 : 0 - cmp [ThisInode+i_blocks],ecx - jne .slow_symlink - - ; It's a fast symlink -.fast_symlink: - call close_file ; We've got all we need - mov si,ThisInode+i_block - - push di - mov di,SymlinkTmpBuf - mov ecx,eax - rep movsb - pop si - -.symlink_finish: - cmp byte [si],0 - je .no_slash - mov al,'/' - stosb -.no_slash: - mov bp,SymlinkTmpBufEnd - call strecpy - jc .err_noclose ; Buffer overflow - - ; Now copy it to the "real" buffer; we need to have - ; two buffers so we avoid overwriting the tail on the - ; next copy - mov si,SymlinkTmpBuf - mov di,SymlinkBuf - push di - call strcpy - pop di - mov eax,[ThisDir] ; Resume searching previous directory - jmp .begin_path - -.slow_symlink: - mov bx,SymlinkTmpBuf - mov cx,SYMLINK_SECTORS - call getfssec - ; The EOF closed the file - - mov si,di ; SI = filename tail - mov di,SymlinkTmpBuf - add di,ax ; AX = file length - jmp .symlink_finish - - - section .bss16 - alignb 4 -SymlinkBuf resb SYMLINK_SECTORS*SECTOR_SIZE+64 -SymlinkTmpBuf equ trackbuf -SymlinkTmpBufEnd equ trackbuf+SYMLINK_SECTORS*SECTOR_SIZE+64 -ThisDir resd 1 -EndBlock resw 1 -SymlinkCtr resb 1 - - section .text16 -; -; mangle_name: Mangle a filename pointed to by DS:SI into a buffer pointed -; to by ES:DI; ends on encountering any whitespace. -; DI is preserved. -; -; This verifies that a filename is < FILENAME_MAX characters, -; doesn't contain whitespace, zero-pads the output buffer, -; and removes redundant slashes, -; so "repe cmpsb" can do a compare, and the -; path-searching routine gets a bit of an easier job. -; -; FIX: we may want to support \-escapes here (and this would -; be the place.) -; -mangle_name: - push di - push bx - xor ax,ax - mov cx,FILENAME_MAX-1 - mov bx,di - -.mn_loop: - lodsb - cmp al,' ' ; If control or space, end - jna .mn_end - cmp al,ah ; Repeated slash? - je .mn_skip - xor ah,ah - cmp al,'/' - jne .mn_ok - mov ah,al -.mn_ok stosb -.mn_skip: loop .mn_loop -.mn_end: - cmp bx,di ; At the beginning of the buffer? - jbe .mn_zero - cmp byte [di-1],'/' ; Terminal slash? - jne .mn_zero -.mn_kill: dec di ; If so, remove it - inc cx - jmp short .mn_end -.mn_zero: - inc cx ; At least one null byte - xor ax,ax ; Zero-fill name - rep stosb - pop bx - pop di - ret ; Done - -; -; 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. -; -; 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: call strcpy - dec di ; Point to final null byte - ret - -; -; -; kaboom2: once everything is loaded, replace the part of kaboom -; starting with "kaboom.patch" with this part - -kaboom2: - mov si,err_bootfailed - call writestr - cmp byte [kaboom.again+1],18h ; INT 18h version? - je .int18 - call getchar - call vgaclearmode - int 19h ; And try once more to boot... -.norge: jmp short .norge ; If int 19h returned; this is the end -.int18: - call vgaclearmode - int 18h -.noreg: jmp short .noreg ; Nynorsk - - -; -; linsector: 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!) -; -linsector: - push gs - push ebx - push esi - push edi - push ecx - push edx - push ebp - - push eax ; Save sector index - 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,i_block+4*EXT2_IND_BLOCK - sub eax,EXT2_NDIR_BLOCKS - mov ebp,[PtrsPerBlock1] - cmp eax,ebp - jb .ind1 - mov ebx,i_block+4*EXT2_DIND_BLOCK - sub eax,ebp - mov ebp,[PtrsPerBlock2] - cmp eax,ebp - jb .ind2 - mov ebx,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 [PtrsPerBlock2] - ; EAX = which dind block, EDX = pointer within dind block - push ax - shr eax,SECTOR_SHIFT-2 - mov ebp,[gs:si+bx] - shl ebp,cl - add eax,ebp - call getcachesector - pop bx - and bx,(SECTOR_SIZE >> 2)-1 - shl bx,2 - mov eax,edx ; The ind2 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 [PtrsPerBlock1] - ; EAX = which ind block, EDX = pointer within ind block - push ax - shr eax,SECTOR_SHIFT-2 - mov ebp,[gs:si+bx] - shl ebp,cl - add eax,ebp - call getcachesector - pop bx - and bx,(SECTOR_SIZE >> 2)-1 - shl bx,2 - mov eax,edx ; The int1 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. - push ax - shr eax,SECTOR_SHIFT-2 - mov ebp,[gs:si+bx] - shl ebp,cl - add eax,ebp - call getcachesector - pop bx - and bx,(SECTOR_SIZE >> 2)-1 - shl bx,2 - -.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) -; On return ECX = number of bytes read -; All arguments are advanced to reflect data read. -; -getfssec: - push ebp - push eax - push edx - push edi - - movzx ecx,cx - push ecx ; Sectors requested read - mov eax,[si+file_bytesleft] - add eax,SECTOR_SIZE-1 - shr eax,SECTOR_SHIFT - cmp ecx,eax ; Number of sectors left - jbe .lenok - mov cx,ax -.lenok: -.getfragment: - mov eax,[si+file_sector] ; Current start index - mov edi,eax - call linsector - push eax ; Fragment start sector - mov edx,eax - xor ebp,ebp ; Fragment sector count -.getseccnt: - inc bp - dec cx - jz .do_read - xor eax,eax - mov ax,es - shl ax,4 - add ax,bx ; Now DI = how far into 64K block we are - not ax ; Bytes left in 64K block - inc eax - shr eax,SECTOR_SHIFT ; Sectors left in 64K block - cmp bp,ax - jnb .do_read ; Unless there is at least 1 more sector room... - inc edi ; Sector index - inc edx ; Linearly next sector - mov eax,edi - call linsector - ; jc .do_read - cmp edx,eax - je .getseccnt -.do_read: - pop eax ; Linear start sector - pushad - call getlinsec_ext - popad - push bp - shl bp,9 - add bx,bp ; Adjust buffer pointer - pop bp - add [si+file_sector],ebp ; Next sector index - jcxz .done - jnz .getfragment - ; Fall through -.done: - pop ecx ; Sectors requested read - shl ecx,SECTOR_SHIFT - sub [si+file_bytesleft],ecx - jnbe .noteof ; CF=0 in this case - add ecx,[si+file_bytesleft] ; Actual number of bytes read - call close_file - stc ; We hit EOF -.noteof: - pop edi - pop edx - pop eax - pop ebp - ret - -build_curdir_str: - ret - -; ----------------------------------------------------------------------------- -; Common modules -; ----------------------------------------------------------------------------- - -%include "common.inc" ; Universal modules -%include "plaincon.inc" ; writechr -%include "writestr.inc" ; String output -%include "writehex.inc" ; Hexadecimal output -%include "strecpy.inc" ; strcpy with end pointer check -%include "cache.inc" ; Metadata disk cache -%include "localboot.inc" ; Disk-based local boot - -; ----------------------------------------------------------------------------- -; Begin data section -; ----------------------------------------------------------------------------- - - section .data16 -copyright_str db ' Copyright (C) 1994-' - asciidec YEAR - db ' H. Peter Anvin et al', CR, LF, 0 -err_bootfailed db CR, LF, 'Boot failed: please change disks and press ' - db 'a key to continue.', CR, LF, 0 -config_name db 'extlinux.conf',0 ; Unmangled form - -; -; Config file keyword table -; -%include "keywords.inc" - -; -; Extensions to search for (in *forward* order). -; - alignz 4 -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 - - alignz 4 -BufSafe dw trackbufsize/SECTOR_SIZE ; Clusters we can load into trackbuf -BufSafeBytes dw trackbufsize ; = how many bytes? -%ifndef DEPEND -%if ( trackbufsize % SECTOR_SIZE ) != 0 -%error trackbufsize must be a multiple of SECTOR_SIZE -%endif -%endif +%include "diskfs.inc" |