📄 extlinux.asm
字号:
; -*- fundamental -*- (asm-mode sucks); $Id$; ****************************************************************************;; extlinux.asm;; A program to boot Linux kernels off an ext2/ext3 filesystem.;; Copyright (C) 1994-2005 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 "layout.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 sizeNULLFILE equ 0 ; Null character == empty filenameNULLOFFSET equ 0 ; Position in which to lookretry_count equ 16 ; How patient are we with the disk?%assign HIGHMEM_SLOP 0 ; Avoid this much memory near the topLDLINUX_MAGIC equ 0x3eb202fe ; A random number to identify ourselves withMAX_OPEN_LG2 equ 6 ; log2(Max number of open files)MAX_OPEN equ (1 << MAX_OPEN_LG2)SECTOR_SHIFT equ 9SECTOR_SIZE equ (1 << SECTOR_SHIFT)MAX_SYMLINKS equ 64 ; Maximum number of symlinks per lookupSYMLINK_SECTORS equ 2 ; Max number of sectors in a symlink ; (should be >= FILENAME_MAX);; 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.; struc vkernelvk_vname: resb FILENAME_MAX ; Virtual name **MUST BE FIRST!**vk_rname: resb FILENAME_MAX ; Real namevk_appendlen: resw 1 alignb 4vk_append: resb max_cmd_len+1 ; Command line alignb 4vk_end: equ $ ; Should be <= vk_size endstruc;; 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 4000hcache_seg equ 3000h ; 64K area for metadata cachevk_seg equ 2000h ; Virtual kernelsxfer_buf_seg equ 1000h ; Bounce buffer for I/O to high memcomboot_seg equ real_mode_seg ; COMBOOT image loading zone;; File structure. This holds the information for each currently open file.; struc open_file_tfile_left resd 1 ; Number of sectors left (0 = free)file_sector resd 1 ; Next linear sector to readfile_in_sec resd 1 ; Sector where inode livesfile_in_off resw 1file_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 .earlybsstrackbufsize equ 8192trackbuf resb trackbufsize ; Track buffer goes heregetcbuf resb trackbufsize ; ends at 4800h section .latebssSuperBlock resb 1024 ; ext2 superblockSuperInfo resq 16 ; DOS superblock expandedClustSize resd 1 ; Bytes/cluster ("block")SecPerClust resd 1 ; Sectors/clusterClustMask resd 1 ; Sectors/cluster - 1PtrsPerBlock1 resd 1 ; Pointers/clusterPtrsPerBlock2 resd 1 ; (Pointers/cluster)^2DriveNumber resb 1 ; BIOS drive numberClustShift resb 1 ; Shift count for sectors/clusterClustByteShift resb 1 ; Shift count for bytes/cluster alignb open_file_t_sizeFiles 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 changexbs_vgabuf equ trackbufsizexbs_vgatmpbuf equ 2*trackbufsize section .text;; 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 entryFloppyTable 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 1bx %+ %1 equ SuperInfo+($-superblock)*8+4bs %+ %1 equ $ zb 1%endmacro%macro superw 1bx %+ %1 equ SuperInfo+($-superblock)*8bs %+ %1 equ $ zw 1%endmacro%macro superd 1bx %+ %1 equ $ ; no expansion for dwordsbs %+ %1 equ $ zd 1%endmacrosuperblock equ $ superw BytesPerSec superb SecPerClust superw ResSectors superb FATs superw RootDirEnts superw Sectors superb Media superw FATsecs superw SecPerTrack superw Headssuperinfo_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 bytessuperblock_len equ $-superblock;; 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 harddiskfloppy: 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],eaxno_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],cxno_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.jmp+1],(getlinsec_ebios-(getlinsec.jmp+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+4],LDLINUX_MAGIC^HEXDATE jne kaboom ; Go for it... jmp ldlinux_ent;; 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: add eax,[bsHidden] ; Add partition offset xor edx,edx ; Zero-extend LBA (eventually allow 64 bits).jmp: jmp strict short getlinsec_cbios;; getlinsec_ebios:;; getlinsec implementation for EBIOS (EDD);getlinsec_ebios:.loop: push bp ; Sectors left.retry2: call maxtrans ; Enforce maximum transfer size movzx edi,bp ; Sectors we are about to read mov cx,retry_count.retry: ; Form DAPA on stack push edx push eax push es push bx push di push word 16 mov si,sp pushad mov dl,[DriveNumber] push ds push ss pop ds ; DS <- SS mov ah,42h ; Extended Read int 13h pop ds popad lea sp,[si+16] ; Remove DAPA jc .error pop bp add eax,edi ; Advance sector pointer sub bp,di ; Sectors left shl di,SECTOR_SHIFT ; 512-byte sectors add bx,di ; Advance buffer pointer and bp,bp jnz .loop ret.error: ; Some systems seem to get "stuck" in an error state when ; using EBIOS. Doesn't happen when using CBIOS, which is ; good, since some other systems get timeout failures ; waiting for the floppy disk to spin up. pushad ; Try resetting the device xor ax,ax mov dl,[DriveNumber] int 13h popad loop .retry ; CX-- and jump if not zero ;shr word [MaxTransfer],1 ; Reduce the transfer size ;jnz .retry2 ; Total failure. Try falling back to CBIOS. mov byte [getlinsec.jmp+1],(getlinsec_cbios-(getlinsec.jmp+2)) ;mov byte [MaxTransfer],63 ; Max possibe CBIOS transfer pop bp ; ... fall through ...;; getlinsec_cbios:;; getlinsec implementation for legacy CBIOS;getlinsec_cbios:.loop: push edx 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. ; div esi xor cx,cx xchg cx,dx ; CX <- sector index (0-based) ; EDX <- 0 ; eax = track # div edi ; Convert track to head/cyl ; We should test this, but it doesn't fit... ; cmp eax,1023 ; ja .error ; ; 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 mov bp,retry_count.retry: pushad int 13h popad jc .error.resume: movzx ecx,al ; ECX <- sectors transferred shl ax,SECTOR_SHIFT ; Convert sectors in AL to bytes in AX pop bx add bx,ax pop bp pop eax pop edx add eax,ecx sub bp,cx jnz .loop ret.error: dec bp jnz .retry xchg ax,bp ; Sectors transferred <- 0 shr word [MaxTransfer],1 jnz .resume ; Fall through to disk_error ;; kaboom: write a message and bail out.;disk_error: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: ; When we have full code, intercept here mov si,bailmsg ; Write error message, this assumes screen page 0.loop: lodsb and al,al jz .done mov ah,0Eh ; Write to screen as TTY mov bx,0007h ; Attribute int 10h jmp short .loop.done: 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;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -