1 ; -*- fundamental -*- (asm-mode sucks)
2 ; $Id: extlinux.asm,v 1.21 2005/04/03 00:00:36 hpa Exp $
3 ; ****************************************************************************
7 ; A program to boot Linux kernels off an ext2/ext3 filesystem.
9 ; Copyright (C) 1994-2005 H. Peter Anvin
11 ; This program is free software; you can redistribute it and/or modify
12 ; it under the terms of the GNU General Public License as published by
13 ; the Free Software Foundation, Inc., 53 Temple Place Ste 330,
14 ; Boston MA 02111-1307, USA; either version 2 of the License, or
15 ; (at your option) any later version; incorporated herein by reference.
17 ; ****************************************************************************
24 %include "tracers.inc"
27 %include "ext2_fs.inc"
30 ; Some semi-configurable constants... change on your own risk.
33 ; NASM 0.98.38 croaks if these are equ's rather than macros...
34 FILENAME_MAX_LG2 equ 8 ; log2(Max filename size Including final null)
35 FILENAME_MAX equ (1 << FILENAME_MAX_LG2) ; Max mangled filename size
36 NULLFILE equ 0 ; Null character == empty filename
37 NULLOFFSET equ 0 ; Position in which to look
38 retry_count equ 6 ; How patient are we with the disk?
39 %assign HIGHMEM_SLOP 0 ; Avoid this much memory near the top
40 LDLINUX_MAGIC equ 0x3eb202fe ; A random number to identify ourselves with
42 MAX_OPEN_LG2 equ 6 ; log2(Max number of open files)
43 MAX_OPEN equ (1 << MAX_OPEN_LG2)
46 SECTOR_SIZE equ (1 << SECTOR_SHIFT)
49 ; This is what we need to do when idle
59 ; The following structure is used for "virtual kernels"; i.e. LILO-style
60 ; option labels. The options we permit here are `kernel' and `append
61 ; Since there is no room in the bottom 64K for all of these, we
62 ; stick them at vk_seg:0000 and copy them down before we need them.
65 vk_vname: resb FILENAME_MAX ; Virtual name **MUST BE FIRST!**
66 vk_rname: resb FILENAME_MAX ; Real name
69 vk_append: resb max_cmd_len+1 ; Command line
71 vk_end: equ $ ; Should be <= vk_size
75 ; Segment assignments in the bottom 640K
76 ; Stick to the low 512K in case we're using something like M-systems flash
77 ; which load a driver into low RAM (evil!!)
79 ; 0000h - main code/data segment (and BIOS segment)
81 real_mode_seg equ 4000h
82 cache_seg equ 3000h ; 64K area for metadata cache
83 vk_seg equ 2000h ; Virtual kernels
84 xfer_buf_seg equ 1000h ; Bounce buffer for I/O to high mem
85 comboot_seg equ real_mode_seg ; COMBOOT image loading zone
88 ; File structure. This holds the information for each currently open file.
91 file_left resd 1 ; Number of sectors left (0 = free)
92 file_sector resd 1 ; Next linear sector to read
93 file_in_sec resd 1 ; Sector where inode lives
99 %if (open_file_t_size & (open_file_t_size-1))
100 %error "open_file_t is not a power of 2"
104 ; ---------------------------------------------------------------------------
106 ; ---------------------------------------------------------------------------
109 ; Memory below this point is reserved for the BIOS and the MBR
112 trackbufsize equ 8192
113 trackbuf resb trackbufsize ; Track buffer goes here
114 getcbuf resb trackbufsize
118 SuperBlock resb 1024 ; ext2 superblock
119 SuperInfo resq 16 ; DOS superblock expanded
120 ClustSize resd 1 ; Bytes/cluster ("block")
121 SecPerClust resd 1 ; Sectors/cluster
122 ClustMask resd 1 ; Sectors/cluster - 1
123 PtrsPerBlock1 resd 1 ; Pointers/cluster
124 PtrsPerBlock2 resd 1 ; (Pointers/cluster)^2
125 DriveNumber resb 1 ; BIOS drive number
126 ClustShift resb 1 ; Shift count for sectors/cluster
127 ClustByteShift resb 1 ; Shift count for bytes/cluster
129 alignb open_file_t_size
130 Files resb MAX_OPEN*open_file_t_size
133 ; Constants for the xfer_buf_seg
135 ; The xfer_buf_seg is also used to store message file buffers. We
136 ; need two trackbuffers (text and graphics), plus a work buffer
137 ; for the graphics decompressor.
139 xbs_textbuf equ 0 ; Also hard-coded, do not change
140 xbs_vgabuf equ trackbufsize
141 xbs_vgatmpbuf equ 2*trackbufsize
146 ; Some of the things that have to be saved very early are saved
147 ; "close" to the initial stack pointer offset, in order to
148 ; reduce the code size...
150 StackBuf equ $-44-32 ; Start the stack here (grow down - 4K)
151 PartInfo equ StackBuf ; Saved partition table entry
152 FloppyTable equ PartInfo+16 ; Floppy info table (must follow PartInfo)
153 OrigFDCTabPtr equ StackBuf-4 ; The high dword on the stack
156 ; Primary entry point. Tempting as though it may be, we can't put the
157 ; initial "cli" here; the jmp opcode in the first byte is part of the
158 ; "magic number" (using the term very loosely) for the DOS superblock.
161 jmp short start ; 2 bytes
164 ; "Superblock" follows -- it's in the boot sector, so it's already
165 ; loaded and ready for us
167 bsOemName db 'EXTLINUX' ; The SYS command sets this, so...
169 ; These are the fields we actually care about. We end up expanding them
170 ; all to dword size early in the code, so generate labels for both
171 ; the expanded and unexpanded versions.
174 bx %+ %1 equ SuperInfo+($-superblock)*8+4
179 bx %+ %1 equ SuperInfo+($-superblock)*8
184 bx %+ %1 equ $ ; no expansion for dwords
199 superinfo_size equ ($-superblock)-1 ; How much to expand
203 ; This is as far as FAT12/16 and FAT32 are consistent
205 zb 54 ; FAT12/16 need 26 more bytes,
206 ; FAT32 need 54 more bytes
207 superblock_len equ $-superblock
210 ; Note we don't check the constraints above now; we did that at install
214 cli ; No interrupts yet, please
221 mov sp,StackBuf ; Just below BSS
224 ; DS:SI may contain a partition table entry. Preserve it for us.
226 mov cx,8 ; Save partition info
230 mov ds,ax ; Now we can initialize DS...
233 ; Now sautee the BIOS floppy info block to that it will support decent-
234 ; size transfers; the floppy block is 11 bytes and is stored in the
235 ; INT 1Eh vector (brilliant waste of resources, eh?)
237 ; Of course, if BIOSes had been properly programmed, we wouldn't have
238 ; had to waste precious space with this code.
241 lfs si,[bx] ; FS:SI -> original fdctab
242 push fs ; Save on stack in case we need to bail
245 ; Save the old fdctab even if hard disk so the stack layout
246 ; is the same. The instructions above do not change the flags
247 mov [DriveNumber],dl ; Save drive number in DL
248 and dl,dl ; If floppy disk (00-7F), assume no
253 mov cl,6 ; 12 bytes (CX == 0)
254 ; es:di -> FloppyTable already
255 ; This should be safe to do now, interrupts are off...
256 mov [bx],di ; FloppyTable
257 mov [bx+2],ax ; Segment 0
258 fs rep movsw ; Faster to move words
259 mov cl,[bsSecPerTrack] ; Patch the sector count
262 int 13h ; Some BIOSes need this
264 jmp short not_harddisk
266 ; The drive number and possibly partition information was passed to us
267 ; by the BIOS or previous boot loader (MBR). Current "best practice" is to
268 ; trust that rather than what the superblock contains.
270 ; Would it be better to zero out bsHidden if we don't have a partition table?
272 ; Note: di points to beyond the end of PartInfo
275 test byte [di-16],7Fh ; Sanity check: "active flag" should
276 jnz no_partition ; be 00 or 80
277 mov eax,[di-8] ; Partition offset (dword)
281 ; Get disk drive parameters (don't trust the superblock.) Don't do this for
282 ; floppy drives -- INT 13:08 on floppy drives will (may?) return info about
283 ; what the *drive* supports, not about the *media*. Fortunately floppy disks
284 ; tend to have a fixed, well-defined geometry which is stored in the superblock.
286 ; DL == drive # still
293 inc dx ; Contains # of heads - 1
296 mov [bsSecPerTrack],cx
300 ; Ready to enable interrupts, captain
306 ; Do we have EBIOS (EDD)?
310 mov ah,41h ; EDD existence query
316 test cl,1 ; Extended disk access functionality set
319 ; We have EDD support...
321 mov byte [getlinsec.jmp+1],getlinsec_ebios-(getlinsec.jmp+2)
325 ; Load the first sector of LDLINUX.SYS; this used to be all proper
326 ; with parsing the superblock and root directory; it doesn't fit
327 ; together with EBIOS support, unfortunately.
329 mov eax,[FirstSector] ; Sector start
330 mov bx,ldlinux_sys ; Where to load it
333 ; Some modicum of integrity checking
334 cmp dword [ldlinux_magic],LDLINUX_MAGIC
336 cmp dword [ldlinux_magic+4],HEXDATE
343 ; kaboom: write a message and bail out.
348 mov sp,StackBuf-4 ; Reset stack
349 mov ds,si ; Reset data segment
350 pop dword [fdctab] ; Restore FDC table
351 .patch: mov si,bailmsg
352 call writestr ; Returns with AL = 0
354 int 16h ; Wait for keypress
355 int 19h ; And try once more to boot...
356 .norge: jmp short .norge ; If int 19h returned; this is the end
360 ; writestr: write a null-terminated string to the console
361 ; This assumes we're on page 0. This is only used for early
362 ; messages, so it should be OK.
368 mov ah,0Eh ; Write to screen as TTY
369 mov bx,0007h ; Attribute
375 ; xint13: wrapper for int 13h which will retry 6 times and then die,
376 ; AND save all registers except BP
388 jmp strict near kaboom ; Patched
392 ; getonesec: get one disk sector
395 mov bp,1 ; One sector
399 ; getlinsec: load a sequence of BP floppy sector given by the linear sector
400 ; number in EAX into the buffer at ES:BX. We try to optimize
401 ; by loading up to a whole track at a time, but the user
402 ; is responsible for not crossing a 64K boundary.
403 ; (Yes, BP is weird for a count, but it was available...)
405 ; On return, BX points to the first byte after the transferred
408 ; This routine assumes CS == DS, and trashes most registers.
410 ; Stylistic note: use "xchg" instead of "mov" when the source is a register
411 ; that is dead from that point; this saves space. However, please keep
412 ; the order to dst,src to keep things sane.
415 add eax,[bsHidden] ; Add partition offset
416 .jmp: jmp strict short getlinsec_cbios ; This is patched
421 ; getlinsec implementation for EBIOS (EDD)
424 mov si,dapa ; Load up the DAPA
429 push bp ; Sectors left
430 call maxtrans ; Enforce maximum transfer size
434 mov ah,42h ; Extended Read
437 movzx eax,word [si+2] ; Sectors we read
438 add [si+8],eax ; Advance sector pointer
439 sub bp,ax ; Sectors left
440 shl ax,9 ; 512-byte sectors
441 add [si+4],ax ; Advance buffer pointer
444 mov eax,[si+8] ; Next sector
445 mov bx,[si+4] ; Buffer pointer
451 ; getlinsec implementation for legacy CBIOS
459 movzx esi,word [bsSecPerTrack]
460 movzx edi,word [bsHeads]
462 ; Dividing by sectors to get (track,sector): we may have
463 ; up to 2^18 tracks, so we need to use 32-bit arithmetric.
465 xor edx,edx ; Zero-extend LBA to 64 bits
468 xchg cx,dx ; CX <- sector index (0-based)
471 div edi ; Convert track to head/cyl
473 ; Now we have AX = cyl, DX = head, CX = sector (0-based),
474 ; BP = sectors to transfer, SI = bsSecPerTrack,
475 ; ES:BX = data target
478 call maxtrans ; Enforce maximum transfer size
480 ; Must not cross track boundaries, so BP <= SI-CX
487 shl ah,6 ; Because IBM was STOOPID
488 ; and thought 8 bits were enough
489 ; then thought 10 bits were enough...
490 inc cx ; Sector numbers are 1-based, sigh
495 xchg ax,bp ; Sector to transfer count
496 mov ah,02h ; Read sectors
499 shl ax,9 ; Convert sectors in AL to bytes in AX
510 ; Truncate BP to MaxTransfer
519 ; Error message on failure
521 bailmsg: db 'Boot failed', 0Dh, 0Ah, 0
524 ; EBIOS disk address packet
529 .count: dw 0 ; Block count
530 .off: dw 0 ; Offset of buffer
531 .seg: dw 0 ; Segment of buffer
532 .lba: dd 0 ; LBA (LSW)
537 bs_checkpt_off equ ($-$$)
539 %if bs_checkpt_off > 1F8h
540 %error "Boot sector overflow"
546 FirstSector dd 0xDEADBEEF ; Location of sector 1
547 MaxTransfer dw 0x007F ; Max transfer size
548 bootsignature dw 0AA55h
551 ; ===========================================================================
553 ; ===========================================================================
554 ; Start of LDLINUX.SYS
555 ; ===========================================================================
559 syslinux_banner db 0Dh, 0Ah
561 db version_str, ' ', date, ' ', 0
562 db 0Dh, 0Ah, 1Ah ; EOF if we "type" this in DOS
565 ldlinux_magic dd LDLINUX_MAGIC
569 ; This area is patched by the installer. It is found by looking for
570 ; LDLINUX_MAGIC, plus 8 bytes.
573 LDLDwords dw 0 ; Total dwords starting at ldlinux_sys
574 LDLSectors dw 0 ; Number of sectors - (bootsec+this sec)
575 CheckSum dd 0 ; Checksum starting at ldlinux_sys
576 ; value = LDLINUX_MAGIC - [sum of dwords]
577 CurrentDir dd 2 ; "Current" directory inode number
579 ; Space for up to 64 sectors, the theoretical maximum
580 SectorPtrs times 64 dd 0
584 ; Note that some BIOSes are buggy and run the boot sector at 07C0:0000
585 ; instead of 0000:7C00 and the like. We don't want to add anything
586 ; more to the boot sector, so it is written to not assume a fixed
587 ; value in CS, but we don't want to deal with that anymore from now
594 ; Tell the user we got this far
596 mov si,syslinux_banner
600 ; Patch disk error handling
602 mov word [xint13.disk_error+1],do_disk_error-(xint13.disk_error+3)
605 ; Now we read the rest of LDLINUX.SYS. Don't bother loading the first
606 ; sector again, though.
610 mov bx,7C00h+2*SECTOR_SIZE ; Where we start loading
616 lodsd ; First sector of this chunk
624 inc edx ; Next linear sector
625 cmp [esi],edx ; Does it match
626 jnz .chunk_ready ; If not, this is it
627 add esi,4 ; If so, add sector to chunk
628 jmp short .make_chunk
639 ; All loaded up, verify that we got what we needed.
640 ; Note: the checksum field is embedded in the checksum region, so
641 ; by the time we get to the end it should all cancel out.
646 mov edx,-LDLINUX_MAGIC
652 and edx,edx ; Should be zero
653 jz all_read ; We're cool, go for it!
656 ; Uh-oh, something went bad...
658 mov si,checksumerr_msg
663 ; -----------------------------------------------------------------------------
664 ; Subroutines that have to be in the first sector
665 ; -----------------------------------------------------------------------------
668 ; getlinsecsr: save registers, call getlinsec, restore registers
676 ; This routine captures disk errors, and tries to decide if it is
677 ; time to reduce the transfer size.
682 shr al,1 ; Try reducing the transfer size
684 jz kaboom ; If we can't, we're dead...
685 jmp xint13 ; Try again
696 ; Checksum error message
698 checksumerr_msg db 'Load error - ', 0 ; Boot failed appended
705 cmp word [Debug_Magic],0D00Dh
710 rl_checkpt equ $ ; Must be <= 8000h
712 rl_checkpt_off equ ($-$$)
714 %if rl_checkpt_off > 400h
715 %error "Sector 1 overflow"
719 ; ----------------------------------------------------------------------------
720 ; End of code and data that have to be in the first sector
721 ; ----------------------------------------------------------------------------
725 ; Let the user (and programmer!) know we got this far. This used to be
726 ; in Sector 1, but makes a lot more sense here.
732 ; Insane hack to expand the DOS superblock to dwords
738 mov cx,superinfo_size
742 stosd ; Store expanded word
744 stosd ; Store expanded byte
748 ; Load the real (ext2) superblock; 1024 bytes long at offset 1024
751 mov eax,1024 >> SECTOR_SHIFT
756 ; Compute some values...
761 ; s_log_block_size = log2(blocksize) - 10
762 mov cl,[SuperBlock+s_log_block_size]
764 mov [ClustByteShift],cl
772 mov [SecPerClust],eax
776 add cl,SECTOR_SHIFT-2 ; 4 bytes/pointer
778 mov [PtrsPerBlock1],edx
780 mov [PtrsPerBlock2],edx
783 ; Common initialization code
786 %include "cpuinit.inc"
789 ; Initialize the metadata cache
794 ; Now, everything is "up and running"... patch kaboom for more
795 ; verbosity and using the full screen system
798 mov dword [kaboom.patch],0e9h+((kaboom2-(kaboom.patch+3)) << 8)
801 ; Now we're all set to start with our *real* business. First load the
802 ; configuration file (if any) and parse it.
804 ; In previous versions I avoided using 32-bit registers because of a
805 ; rumour some BIOSes clobbered the upper half of 32-bit registers at
806 ; random. I figure, though, that if there are any of those still left
807 ; they probably won't be trying to install Linux on them...
809 ; The code is still ripe with 16-bitisms, though. Not worth the hassle
810 ; to take'm out. In fact, we may want to put them back if we're going
811 ; to boot ELKS at some point.
815 ; Load configuration file
823 ; Now we have the config file open. Parse the config file and
824 ; run the user interface.
829 ; Linux kernel loading code is common.
831 %include "runkernel.inc"
834 ; COMBOOT-loading code
836 %include "comboot.inc"
838 %include "cmdline.inc"
841 ; Boot sector loading code
843 %include "bootsect.inc"
847 ; getlinsec_ext: same as getlinsec, except load any sector from the zero
848 ; block as all zeros; use to load any data derived
849 ; from an ext2 block pointer, i.e. anything *except the
856 cmp eax,[SecPerClust]
857 jae getlinsec ; Nothing fancy
859 ; If we get here, at least part of what we want is in the
860 ; zero block. Zero one sector at a time and loop.
865 mov cx,SECTOR_SIZE >> 2
876 ; abort_check: let the user abort with <ESC> or <Ctrl-C>
887 ac_kill: mov si,aborted_msg
890 ; abort_load: Called by various routines which wants to print a fatal
891 ; error message and return to the command prompt. Since this
892 ; may happen at just about any stage of the boot process, assume
893 ; our state is messed up, and just reset the segment registers
894 ; and the stack forcibly.
896 ; SI = offset (in _text) of error message to print
899 mov ax,cs ; Restore CS = DS = ES
903 mov sp,StackBuf-2*3 ; Reset stack
904 mov ss,ax ; Just in case...
906 call cwritestr ; Expects SI -> error msg
907 al_ok: jmp enter_command ; Return to command prompt
915 ; allocate_file: Allocate a file structure
928 .check: cmp dword [bx], byte 0
930 add bx,open_file_t_size ; ZF = 0
932 ; ZF = 0 if we fell out of the loop
937 ; Open a file indicated by an inode number in EAX
939 ; NOTE: This file considers finding a zero-length file an
940 ; error. This is so we don't have to deal with that special
941 ; case elsewhere in the program (most loops have the test
947 ; DX:AX = EAX = file length in bytes
951 open_inode.allocate_failure:
957 jnz .allocate_failure
960 ; First, get the appropriate inode group and index
961 dec eax ; There is no inode 0
963 mov [bx+file_sector],edx
964 div dword [SuperBlock+s_inodes_per_group]
965 ; EAX = inode group; EDX = inode within group
968 ; Now, we need the block group descriptor.
969 ; To get that, we first need the relevant descriptor block.
971 shl eax, ext2_group_desc_lg2size ; Get byte offset in desc table
973 div dword [ClustSize]
974 ; eax = block #, edx = offset in block
975 add eax,dword [SuperBlock+s_first_data_block]
976 inc eax ; s_first_data_block+1
984 call getcachesector ; Get the group descriptor
986 mov esi,[gs:si+bg_inode_table] ; Get inode table block #
987 pop eax ; Get inode within group
988 movzx edx, word [SuperBlock+s_inode_size]
990 ; edx:eax = byte offset in inode table
991 div dword [ClustSize]
992 ; eax = block # versus inode table, edx = offset in block
994 shl eax,cl ; Turn into sector
998 mov [bx+file_in_sec],eax
1000 and dx,SECTOR_SIZE-1
1001 mov [bx+file_in_off],dx
1005 mov ax,[gs:si+i_mode]
1006 mov [bx+file_mode],ax
1007 mov eax,[gs:si+i_size]
1009 add eax,SECTOR_SIZE-1
1010 shr eax,SECTOR_SHIFT
1011 mov [bx+file_left],eax
1015 shr edx,16 ; 16-bitism, sigh
1016 and eax,eax ; ZF clear unless zero-length file
1022 ; Deallocates a file structure (pointer in SI)
1026 mov dword [si],0 ; First dword == file_left
1031 ; Search the root directory for a pre-mangled filename in DS:DI.
1033 ; NOTE: This file considers finding a zero-length file an
1034 ; error. This is so we don't have to deal with that special
1035 ; case elsewhere in the program (most loops have the test
1041 ; DX:AX = EAX = file length in bytes
1045 ; Assumes CS == DS == ES; *** IS THIS CORRECT ***?
1051 mov eax,[CurrentDir]
1053 cmp byte [di],'/' ; Absolute filename?
1055 mov eax,EXT2_ROOT_INO
1059 ; At this point, EAX contains the directory inode,
1060 ; and DS:DI contains a pathname tail.
1066 mov cx,[SecPerClust]
1069 pushf ; Save EOF flag
1070 push si ; Save filesystem pointer
1072 cmp dword [bx+d_inode],0
1076 movzx cx,byte [bx+d_name_len]
1083 add bx,[bx+d_rec_len]
1089 jnc .readdir ; There is more
1094 mov eax,[bx+d_inode]
1097 je .finish ; It's a real file; done
1099 jne .nope ; False alarm
1101 ; It's a match, but it's a directory.
1103 pop bx ; Adjust stack (di)
1106 pop bx ; Adjust stack (flags)
1111 .finish: ; We found it; now we need to open the file
1112 pop bx ; Adjust stack (di)
1114 call close ; Close directory
1115 pop bx ; Adjust stack (flags)
1123 ; mangle_name: Mangle a filename pointed to by DS:SI into a buffer pointed
1124 ; to by ES:DI; ends on encountering any whitespace.
1126 ; This verifies that a filename is < FILENAME_MAX characters,
1127 ; doesn't contain whitespace, zero-pads the output buffer,
1128 ; and removes redundant slashes,
1129 ; so "repe cmpsb" can do a compare, and the
1130 ; path-searching routine gets a bit of an easier job.
1132 ; FIX: we may want to support \-escapes here (and this would
1138 mov cx,FILENAME_MAX-1
1143 cmp al,' ' ; If control or space, end
1145 cmp al,ah ; Repeated slash?
1152 .mn_skip: loop .mn_loop
1154 cmp bx,di ; At the beginning of the buffer?
1156 cmp byte [di-1],'/' ; Terminal slash?
1158 .mn_kill: dec di ; If so, remove it
1162 inc cx ; At least one null byte
1163 xor ax,ax ; Zero-fill name
1169 ; unmangle_name: Does the opposite of mangle_name; converts a DOS-mangled
1170 ; filename to the conventional representation. This is needed
1171 ; for the BOOT_IMAGE= parameter for the kernel.
1172 ; NOTE: A 13-byte buffer is mandatory, even if the string is
1173 ; known to be shorter.
1175 ; DS:SI -> input mangled file name
1176 ; ES:DI -> output buffer
1178 ; On return, DI points to the first byte after the output name,
1179 ; which is set to a null byte.
1181 unmangle_name: call strcpy
1182 dec di ; Point to final null byte
1186 ; writechr: Write a single character in AL to the console without
1187 ; mangling any registers; handle video pages correctly.
1190 call write_serial ; write to serial port if needed
1192 test byte [cs:DisplayCon],01h
1196 mov bl,07h ; attribute
1197 mov bh,[cs:BIOS_page] ; current page
1206 ; kaboom2: once everything is loaded, replace the part of kaboom
1207 ; starting with "kaboom.patch" with this part
1210 mov si,err_bootfailed
1214 int 19h ; And try once more to boot...
1215 .norge: jmp short .norge ; If int 19h returned; this is the end
1219 ; linsector: Convert a linear sector index in a file to a linear sector number
1220 ; EAX -> linear sector number
1221 ; DS:SI -> open_file_t
1223 ; Returns next sector number in EAX; CF on EOF (not an error!)
1234 push eax ; Save sector index
1236 shr eax,cl ; Convert to block number
1238 mov eax,[si+file_in_sec]
1240 call getcachesector ; Get inode
1241 add si,[bx+file_in_off] ; Get *our* inode
1243 lea ebx,[i_block+4*eax]
1244 cmp eax,EXT2_NDIR_BLOCKS
1246 mov ebx,i_block+4*EXT2_IND_BLOCK
1247 sub eax,EXT2_NDIR_BLOCKS
1248 mov ebp,[PtrsPerBlock1]
1251 mov ebx,i_block+4*EXT2_DIND_BLOCK
1253 mov ebp,[PtrsPerBlock2]
1256 mov ebx,i_block+4*EXT2_TIND_BLOCK
1260 ; Triple indirect; eax contains the block no
1261 ; with respect to the start of the tind area;
1262 ; ebx contains the pointer to the tind block.
1264 div dword [PtrsPerBlock2]
1265 ; EAX = which dind block, EDX = pointer within dind block
1267 shr eax,SECTOR_SHIFT-2
1273 and bx,(SECTOR_SIZE >> 2)-1
1275 mov eax,edx ; The ind2 code wants the remainder...
1278 ; Double indirect; eax contains the block no
1279 ; with respect to the start of the dind area;
1280 ; ebx contains the pointer to the dind block.
1282 div dword [PtrsPerBlock1]
1283 ; EAX = which ind block, EDX = pointer within ind block
1285 shr eax,SECTOR_SHIFT-2
1291 and bx,(SECTOR_SIZE >> 2)-1
1293 mov eax,edx ; The int1 code wants the remainder...
1296 ; Single indirect; eax contains the block no
1297 ; with respect to the start of the ind area;
1298 ; ebx contains the pointer to the ind block.
1300 shr eax,SECTOR_SHIFT-2
1306 and bx,(SECTOR_SIZE >> 2)-1
1310 mov ebx,[gs:bx+si] ; Get the pointer
1312 pop eax ; Get the sector index again
1313 shl ebx,cl ; Convert block number to sector
1314 and eax,[ClustMask] ; Add offset within block
1327 ; getfssec: Get multiple sectors from a file
1329 ; Same as above, except SI is a pointer to a open_file_t
1332 ; DS:SI -> Pointer to open_file_t
1333 ; CX -> Sector count (0FFFFh = until end of file)
1334 ; Must not exceed the ES segment
1335 ; Returns CF=1 on EOF (not necessarily error)
1336 ; All arguments are advanced to reflect data read.
1345 cmp ecx,[si] ; Number of sectors left
1350 mov eax,[si+file_sector] ; Current start index
1353 push eax ; Fragment start sector
1355 xor ebp,ebp ; Fragment sector count
1363 add ax,bx ; Now DI = how far into 64K block we are
1364 not ax ; Bytes left in 64K block
1366 shr eax,SECTOR_SHIFT ; Sectors left in 64K block
1368 jnb .do_read ; Unless there is at least 1 more sector room...
1369 inc edi ; Sector index
1370 inc edx ; Linearly next sector
1377 pop eax ; Linear start sector
1383 add bx,bp ; Adjust buffer pointer
1385 add [si+file_sector],ebp ; Next sector index
1386 sub [si],ebp ; Sectors consumed
1391 cmp dword [si],1 ; Did we run out of file?
1392 ; CF set if [SI] < 1, i.e. == 0
1399 ; -----------------------------------------------------------------------------
1401 ; -----------------------------------------------------------------------------
1403 %include "getc.inc" ; getc et al
1404 %include "conio.inc" ; Console I/O
1405 %include "writestr.inc" ; String output
1406 %include "parseconfig.inc" ; High-level config file handling
1407 %include "parsecmd.inc" ; Low-level config file handling
1408 %include "bcopy32.inc" ; 32-bit bcopy
1409 %include "loadhigh.inc" ; Load a file into high memory
1410 %include "font.inc" ; VGA font stuff
1411 %include "graphics.inc" ; VGA graphics
1412 %include "highmem.inc" ; High memory sizing
1413 %include "strcpy.inc" ; strcpy()
1414 %include "cache.inc"
1416 ; -----------------------------------------------------------------------------
1417 ; Begin data section
1418 ; -----------------------------------------------------------------------------
1421 copyright_str db ' Copyright (C) 1994-', year, ' H. Peter Anvin'
1423 boot_prompt db 'boot: ', 0
1424 wipe_char db BS, ' ', BS, 0
1425 err_notfound db 'Could not find kernel image: ',0
1426 err_notkernel db CR, LF, 'Invalid or corrupt kernel image.', CR, LF, 0
1427 err_noram db 'It appears your computer has less than '
1429 db 'K of low ("DOS")'
1431 db 'RAM. Linux needs at least this amount to boot. If you get'
1433 db 'this message in error, hold down the Ctrl key while'
1435 db 'booting, and I will take your word for it.', CR, LF, 0
1436 err_badcfg db 'Unknown keyword in extlinux.conf.', CR, LF, 0
1437 err_noparm db 'Missing parameter in extlinux.conf.', CR, LF, 0
1438 err_noinitrd db CR, LF, 'Could not find ramdisk image: ', 0
1439 err_nohighmem db 'Not enough memory to load specified kernel.', CR, LF, 0
1440 err_highload db CR, LF, 'Kernel transfer failure.', CR, LF, 0
1441 err_oldkernel db 'Cannot load a ramdisk with an old kernel image.'
1443 err_notdos db ': attempted DOS system call', CR, LF, 0
1444 err_comlarge db 'COMBOOT image too large.', CR, LF, 0
1445 err_bssimage db 'BSS images not supported.', CR, LF, 0
1446 err_a20 db CR, LF, 'A20 gate not responding!', CR, LF, 0
1447 err_bootfailed db CR, LF, 'Boot failed: please change disks and press '
1448 db 'a key to continue.', CR, LF, 0
1449 ready_msg db 'Ready.', CR, LF, 0
1450 crlfloading_msg db CR, LF
1451 loading_msg db 'Loading ', 0
1454 aborted_msg db ' aborted.' ; Fall through to crlf_msg!
1457 crff_msg db CR, FF, 0
1458 ConfigName db 'extlinux.conf',0 ; Unmangled form
1461 ; Command line options we'd like to take a look at
1463 ; mem= and vga= are handled as normal 32-bit integer values
1464 initrd_cmd db 'initrd='
1465 initrd_cmd_len equ 7
1468 ; Config file keyword table
1470 %include "keywords.inc"
1473 ; Extensions to search for (in *forward* order).
1476 exten_table: db '.cbt' ; COMBOOT (specific)
1477 db '.img' ; Disk image
1478 db '.bs', 0 ; Boot sector
1479 db '.com' ; COMBOOT (same as DOS)
1482 dd 0, 0 ; Need 8 null bytes here
1485 ; Misc initialized (data) variables
1487 %ifdef debug ; This code for debugging only
1488 debug_magic dw 0D00Dh ; Debug code sentinel
1492 BufSafe dw trackbufsize/SECTOR_SIZE ; Clusters we can load into trackbuf
1493 BufSafeSec dw trackbufsize/SECTOR_SIZE ; = how many sectors?
1494 BufSafeBytes dw trackbufsize ; = how many bytes?
1495 EndOfGetCBuf dw getcbuf+trackbufsize ; = getcbuf+BufSafeBytes
1497 %if ( trackbufsize % SECTOR_SIZE ) != 0
1498 %error trackbufsize must be a multiple of SECTOR_SIZE