1 ; -*- fundamental -*- (asm-mode sucks)
2 ; ****************************************************************************
6 ; A program to boot Linux kernels off an ext2/ext3 filesystem.
8 ; Copyright 1994-2009 H. Peter Anvin - All Rights Reserved
9 ; Copyright 2009 Intel Corporation; author: 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 ; ****************************************************************************
21 %include "ext2_fs.inc"
24 ; Some semi-configurable constants... change on your own risk.
27 ; NASM 0.98.38 croaks if these are equ's rather than macros...
28 FILENAME_MAX_LG2
equ 8 ; log2(Max filename size Including final null)
29 FILENAME_MAX
equ (1 << FILENAME_MAX_LG2
) ; Max mangled filename size
30 NULLFILE
equ 0 ; Null character == empty filename
31 NULLOFFSET
equ 0 ; Position in which to look
32 retry_count
equ 16 ; How patient are we with the disk?
33 %assign HIGHMEM_SLOP
0 ; Avoid this much memory near the top
34 LDLINUX_MAGIC
equ 0x3eb202fe ; A random number to identify ourselves with
36 MAX_OPEN_LG2
equ 6 ; log2(Max number of open files)
37 MAX_OPEN
equ (1 << MAX_OPEN_LG2
)
40 SECTOR_SIZE
equ (1 << SECTOR_SHIFT
)
42 MAX_SYMLINKS
equ 64 ; Maximum number of symlinks per lookup
43 SYMLINK_SECTORS
equ 2 ; Max number of sectors in a symlink
44 ; (should be >= FILENAME_MAX)
46 ROOT_DIR_WORD
equ 0x002F
47 CUR_DIR_DWORD
equ 0x00002F2E
50 ; The following structure is used for "virtual kernels"; i.e. LILO-style
51 ; option labels. The options we permit here are `kernel' and `append
52 ; Since there is no room in the bottom 64K for all of these, we
53 ; stick them in high memory and copy them down before we need them.
56 vk_vname: resb FILENAME_MAX
; Virtual name **MUST BE FIRST!**
57 vk_rname: resb FILENAME_MAX
; Real name
59 vk_type: resb
1 ; Type of file
61 vk_append: resb max_cmd_len
+1 ; Command line
63 vk_end: equ $
; Should be <= vk_size
67 ; File structure. This holds the information for each currently open file.
70 file_bytesleft resd
1 ; Number of bytes left (0 = free)
71 file_sector resd
1 ; Next linear sector to read
72 file_in_sec resd
1 ; Sector where inode lives
78 %if
(open_file_t_size
& (open_file_t_size
-1))
79 %error
"open_file_t is not a power of 2"
83 ; ---------------------------------------------------------------------------
85 ; ---------------------------------------------------------------------------
88 ; Memory below this point is reserved for the BIOS and the MBR
92 trackbuf resb trackbufsize
; Track buffer goes here
96 SuperBlock resb
1024 ; ext2 superblock
97 ClustSize resd
1 ; Bytes/cluster ("block")
98 ClustMask resd
1 ; Sectors/cluster - 1
99 PtrsPerBlock1 resd
1 ; Pointers/cluster
100 PtrsPerBlock2 resd
1 ; (Pointers/cluster)^2
101 DriveNumber resb
1 ; BIOS drive number
102 ClustShift resb
1 ; Shift count for sectors/cluster
103 ClustByteShift resb
1 ; Shift count for bytes/cluster
105 alignb open_file_t_size
106 Files resb MAX_OPEN
*open_file_t_size
109 ; Common bootstrap code for disk-based derivatives
111 %include "diskstart.inc"
114 ; Load the real (ext2) superblock; 1024 bytes long at offset 1024
117 mov eax,1024 >> SECTOR_SHIFT
122 ; Compute some values...
127 ; s_log_block_size = log2(blocksize) - 10
128 mov cl,[SuperBlock
+s_log_block_size
]
130 mov [ClustByteShift
],cl
138 mov [SecPerClust
],eax
142 add cl,SECTOR_SHIFT
-2 ; 4 bytes/pointer
144 mov [PtrsPerBlock1
],edx
146 mov [PtrsPerBlock2
],edx
149 ; Common initialization code
152 %include "cpuinit.inc"
155 ; Initialize the metadata cache
160 ; Now, everything is "up and running"... patch kaboom for more
161 ; verbosity and using the full screen system
164 mov dword [kaboom.patch
],0e9h
+((kaboom2
-(kaboom.patch
+3)) << 8)
167 ; Now we're all set to start with our *real* business. First load the
168 ; configuration file (if any) and parse it.
170 ; In previous versions I avoided using 32-bit registers because of a
171 ; rumour some BIOSes clobbered the upper half of 32-bit registers at
172 ; random. I figure, though, that if there are any of those still left
173 ; they probably won't be trying to install Linux on them...
175 ; The code is still ripe with 16-bitisms, though. Not worth the hassle
176 ; to take'm out. In fact, we may want to put them back if we're going
177 ; to boot ELKS at some point.
181 ; Load configuration file
184 mov si,config_name
; Save config file name
187 mov dword [CurrentDirName
],CUR_DIR_DWORD
; Write './',0,0 to the CurrentDirName
188 call build_curdir_str
195 ; Now we have the config file open. Parse the config file and
196 ; run the user interface.
201 ; getlinsec_ext: same as getlinsec, except load any sector from the zero
202 ; block as all zeros; use to load any data derived
203 ; from an ext2 block pointer, i.e. anything *except the
210 cmp eax,[SecPerClust
]
211 jae getlinsecsr
; Nothing fancy
213 ; If we get here, at least part of what we want is in the
214 ; zero block. Zero one sector at a time and loop.
219 mov cx,SECTOR_SIZE
>> 2
230 ; allocate_file: Allocate a file structure
243 .
check: cmp dword [bx], byte 0
245 add bx,open_file_t_size
; ZF = 0
247 ; ZF = 0 if we fell out of the loop
252 ; Open a file indicated by an inode number in EAX
254 ; NOTE: This file considers finding a zero-length file an
255 ; error. This is so we don't have to deal with that special
256 ; case elsewhere in the program (most loops have the test
262 ; EAX = file length in bytes
263 ; ThisInode = the first 128 bytes of the inode
267 ; Assumes CS == DS == ES.
269 open_inode.
allocate_failure:
279 jnz .allocate_failure
283 ; First, get the appropriate inode group and index
284 dec eax ; There is no inode 0
286 mov [bx+file_sector
],edx
287 div dword [SuperBlock
+s_inodes_per_group
]
288 ; EAX = inode group; EDX = inode within group
291 ; Now, we need the block group descriptor.
292 ; To get that, we first need the relevant descriptor block.
294 shl eax, ext2_group_desc_lg2size
; Get byte offset in desc table
296 div dword [ClustSize
]
297 ; eax = block #, edx = offset in block
298 add eax,dword [SuperBlock
+s_first_data_block
]
299 inc eax ; s_first_data_block+1
307 call getcachesector
; Get the group descriptor
309 mov esi,[gs:si+bg_inode_table
] ; Get inode table block #
310 pop eax ; Get inode within group
311 movzx edx, word [SuperBlock
+s_inode_size
]
313 ; edx:eax = byte offset in inode table
314 div dword [ClustSize
]
315 ; eax = block # versus inode table, edx = offset in block
317 shl eax,cl ; Turn into sector
321 mov [bx+file_in_sec
],eax
324 mov [bx+file_in_off
],dx
328 mov cx,EXT2_GOOD_OLD_INODE_SIZE
>> 2
332 mov ax,[ThisInode
+i_mode
]
333 mov [bx+file_mode
],ax
334 mov eax,[ThisInode
+i_size
]
335 mov [bx+file_bytesleft
],eax
337 and eax,eax ; ZF clear unless zero-length file
346 ThisInode resb EXT2_GOOD_OLD_INODE_SIZE
; The most recently opened inode
351 ; Deallocates a file structure (pointer in SI)
357 mov dword [si],0 ; First dword == file_bytesleft
363 ; Search the root directory for a pre-mangled filename in DS:DI.
365 ; NOTE: This file considers finding a zero-length file an
366 ; error. This is so we don't have to deal with that special
367 ; case elsewhere in the program (most loops have the test
373 ; DX:AX = EAX = file length in bytes
377 ; Assumes CS == DS == ES; *** IS THIS CORRECT ***?
383 mov byte [SymlinkCtr
],MAX_SYMLINKS
388 cmp byte [di],'/' ; Absolute filename?
390 mov eax,EXT2_ROOT_INO
395 ; At this point, EAX contains the directory inode,
396 ; and DS:DI contains a pathname tail.
398 push eax ; Save directory inode
401 jz .missing
; If error, done
403 mov cx,[si+file_mode
]
404 shr cx,S_IFSHIFT
; Get file type
409 add sp,4 ; Drop directory inode
416 ; Otherwise, something bad...
425 and eax,eax ; Set/clear ZF
432 add sp,4 ; Drop directory inode
439 cmp byte [di],0 ; End of path?
440 je .done
; If so, done
441 jmp .err
; Otherwise, error
447 pop dword [ThisDir
] ; Remember what directory we're searching
449 cmp byte [di],0 ; More path?
450 je .err
; If not, bad
452 .
skipslash: ; Skip redundant slashes
469 pushf ; Save EOF flag
470 push si ; Save filesystem pointer
476 cmp dword [bx+d_inode
],0 ; Zero inode = void entry
479 movzx cx,byte [bx+d_name_len
]
485 add bx,[bx+d_rec_len
]
491 jnc .readdir
; There is more
492 jmp .err
; Otherwise badness...
497 ; Does this match the end of the requested filename?
503 ; We found something; now we need to open the file
505 pop bx ; Adjust stack (di)
507 call close_file
; Close directory
508 pop bx ; Adjust stack (flags)
512 ; It's a symlink. We have to determine if it's a fast symlink
513 ; (data stored in the inode) or not (data stored as a regular
514 ; file.) Either which way, we start from the directory
515 ; which we just visited if relative, or from the root directory
516 ; if absolute, and append any remaining part of the path.
519 dec byte [SymlinkCtr
]
520 jz .err
; Too many symlink references
522 cmp eax,SYMLINK_SECTORS
*SECTOR_SIZE
523 jae .err
; Symlink too long
525 ; Computation for fast symlink, as defined by ext2/3 spec
527 cmp [ThisInode
+i_file_acl
],ecx
528 setne cl ; ECX <- i_file_acl ? 1 : 0
529 cmp [ThisInode
+i_blocks
],ecx
532 ; It's a fast symlink
534 call close_file
; We've got all we need
535 mov si,ThisInode
+i_block
549 mov bp,SymlinkTmpBufEnd
551 jc .err_noclose
; Buffer overflow
553 ; Now copy it to the "real" buffer; we need to have
554 ; two buffers so we avoid overwriting the tail on the
561 mov eax,[ThisDir
] ; Resume searching previous directory
566 mov cx,SYMLINK_SECTORS
568 ; The EOF closed the file
570 mov si,di ; SI = filename tail
572 add di,ax ; AX = file length
578 SymlinkBuf resb SYMLINK_SECTORS
*SECTOR_SIZE
+64
579 SymlinkTmpBuf
equ trackbuf
580 SymlinkTmpBufEnd
equ trackbuf
+SYMLINK_SECTORS
*SECTOR_SIZE
+64
587 ; mangle_name: Mangle a filename pointed to by DS:SI into a buffer pointed
588 ; to by ES:DI; ends on encountering any whitespace.
591 ; This verifies that a filename is < FILENAME_MAX characters,
592 ; doesn't contain whitespace, zero-pads the output buffer,
593 ; and removes redundant slashes,
594 ; so "repe cmpsb" can do a compare, and the
595 ; path-searching routine gets a bit of an easier job.
597 ; FIX: we may want to support \-escapes here (and this would
604 mov cx,FILENAME_MAX
-1
609 cmp al,' ' ; If control or space, end
611 cmp al,ah ; Repeated slash?
618 .
mn_skip: loop .mn_loop
620 cmp bx,di ; At the beginning of the buffer?
622 cmp byte [di-1],'/' ; Terminal slash?
624 .
mn_kill: dec di ; If so, remove it
628 inc cx ; At least one null byte
629 xor ax,ax ; Zero-fill name
636 ; unmangle_name: Does the opposite of mangle_name; converts a DOS-mangled
637 ; filename to the conventional representation. This is needed
638 ; for the BOOT_IMAGE= parameter for the kernel.
640 ; DS:SI -> input mangled file name
641 ; ES:DI -> output buffer
643 ; On return, DI points to the first byte after the output name,
644 ; which is set to a null byte.
646 unmangle_name: call strcpy
647 dec di ; Point to final null byte
652 ; kaboom2: once everything is loaded, replace the part of kaboom
653 ; starting with "kaboom.patch" with this part
656 mov si,err_bootfailed
658 cmp byte [kaboom.again
+1],18h ; INT 18h version?
662 int 19h ; And try once more to boot...
663 .
norge: jmp short .norge
; If int 19h returned; this is the end
667 .
noreg: jmp short .noreg
; Nynorsk
671 ; linsector: Convert a linear sector index in a file to a linear sector number
672 ; EAX -> linear sector number
673 ; DS:SI -> open_file_t
675 ; Returns next sector number in EAX; CF on EOF (not an error!)
686 push eax ; Save sector index
688 shr eax,cl ; Convert to block number
690 mov eax,[si+file_in_sec
]
692 call getcachesector
; Get inode
693 add si,[bx+file_in_off
] ; Get *our* inode
695 lea ebx,[i_block
+4*eax]
696 cmp eax,EXT2_NDIR_BLOCKS
698 mov ebx,i_block
+4*EXT2_IND_BLOCK
699 sub eax,EXT2_NDIR_BLOCKS
700 mov ebp,[PtrsPerBlock1
]
703 mov ebx,i_block
+4*EXT2_DIND_BLOCK
705 mov ebp,[PtrsPerBlock2
]
708 mov ebx,i_block
+4*EXT2_TIND_BLOCK
712 ; Triple indirect; eax contains the block no
713 ; with respect to the start of the tind area;
714 ; ebx contains the pointer to the tind block.
716 div dword [PtrsPerBlock2
]
717 ; EAX = which dind block, EDX = pointer within dind block
719 shr eax,SECTOR_SHIFT
-2
725 and bx,(SECTOR_SIZE
>> 2)-1
727 mov eax,edx ; The ind2 code wants the remainder...
730 ; Double indirect; eax contains the block no
731 ; with respect to the start of the dind area;
732 ; ebx contains the pointer to the dind block.
734 div dword [PtrsPerBlock1
]
735 ; EAX = which ind block, EDX = pointer within ind block
737 shr eax,SECTOR_SHIFT
-2
743 and bx,(SECTOR_SIZE
>> 2)-1
745 mov eax,edx ; The int1 code wants the remainder...
748 ; Single indirect; eax contains the block no
749 ; with respect to the start of the ind area;
750 ; ebx contains the pointer to the ind block.
752 shr eax,SECTOR_SHIFT
-2
758 and bx,(SECTOR_SIZE
>> 2)-1
762 mov ebx,[gs:bx+si] ; Get the pointer
764 pop eax ; Get the sector index again
765 shl ebx,cl ; Convert block number to sector
766 and eax,[ClustMask
] ; Add offset within block
779 ; getfssec: Get multiple sectors from a file
781 ; Same as above, except SI is a pointer to a open_file_t
784 ; DS:SI -> Pointer to open_file_t
785 ; CX -> Sector count (0FFFFh = until end of file)
786 ; Must not exceed the ES segment
787 ; Returns CF=1 on EOF (not necessarily error)
788 ; On return ECX = number of bytes read
789 ; All arguments are advanced to reflect data read.
798 push ecx ; Sectors requested read
799 mov eax,[si+file_bytesleft
]
800 add eax,SECTOR_SIZE
-1
802 cmp ecx,eax ; Number of sectors left
807 mov eax,[si+file_sector
] ; Current start index
810 push eax ; Fragment start sector
812 xor ebp,ebp ; Fragment sector count
820 add ax,bx ; Now DI = how far into 64K block we are
821 not ax ; Bytes left in 64K block
823 shr eax,SECTOR_SHIFT
; Sectors left in 64K block
825 jnb .do_read
; Unless there is at least 1 more sector room...
826 inc edi ; Sector index
827 inc edx ; Linearly next sector
834 pop eax ; Linear start sector
840 add bx,bp ; Adjust buffer pointer
842 add [si+file_sector
],ebp ; Next sector index
847 pop ecx ; Sectors requested read
849 sub [si+file_bytesleft
],ecx
850 jnbe .noteof
; CF=0 in this case
851 add ecx,[si+file_bytesleft
] ; Actual number of bytes read
864 ; -----------------------------------------------------------------------------
866 ; -----------------------------------------------------------------------------
868 %include "getc.inc" ; getc et al
869 %include "conio.inc" ; Console I/O
870 %include "plaincon.inc" ; writechr
871 %include "writestr.inc" ; String output
872 %include "writehex.inc" ; Hexadecimal output
873 %include "configinit.inc" ; Initialize configuration
874 %include "parseconfig.inc" ; High-level config file handling
875 %include "parsecmd.inc" ; Low-level config file handling
876 %include "bcopy32.inc" ; 32-bit bcopy
877 %include "loadhigh.inc" ; Load a file into high memory
878 %include "font.inc" ; VGA font stuff
879 %include "graphics.inc" ; VGA graphics
880 %include "highmem.inc" ; High memory sizing
881 %include "strcpy.inc" ; strcpy()
882 %include "strecpy.inc" ; strcpy with end pointer check
883 %include "cache.inc" ; Metadata disk cache
884 %include "idle.inc" ; Idle handling
885 %include "adv.inc" ; Auxillary Data Vector
886 %include "localboot.inc" ; Disk-based local boot
888 ; -----------------------------------------------------------------------------
890 ; -----------------------------------------------------------------------------
893 copyright_str
db ' Copyright (C) 1994-'
895 db ' H. Peter Anvin et al', CR
, LF
, 0
896 err_bootfailed
db CR
, LF
, 'Boot failed: please change disks and press '
897 db 'a key to continue.', CR
, LF
, 0
898 config_name
db 'extlinux.conf',0 ; Unmangled form
901 ; Config file keyword table
903 %include "keywords.inc"
906 ; Extensions to search for (in *forward* order).
909 exten_table: db '.cbt' ; COMBOOT (specific)
910 db '.img' ; Disk image
911 db '.bs', 0 ; Boot sector
912 db '.com' ; COMBOOT (same as DOS)
915 dd 0, 0 ; Need 8 null bytes here
918 ; Misc initialized (data) variables
920 %ifdef debug
; This code for debugging only
921 debug_magic
dw 0D00Dh
; Debug code sentinel
925 BufSafe
dw trackbufsize
/SECTOR_SIZE
; Clusters we can load into trackbuf
926 BufSafeBytes
dw trackbufsize
; = how many bytes?
928 %if
( trackbufsize
% SECTOR_SIZE
) != 0
929 %error trackbufsize must be a multiple of SECTOR_SIZE