1 ; -*- fundamental -*- (asm-mode sucks)
3 ; ****************************************************************************
7 ; A program to boot Linux kernels off an MS-DOS formatted floppy disk. This
8 ; functionality is good to have for installation floppies, where it may
9 ; be hard to find a functional Linux system to run LILO off.
11 ; This program allows manipulation of the disk to take place entirely
12 ; from MS-LOSS, and can be especially useful in conjunction with the
15 ; This file is loaded in stages; first the boot sector at offset 7C00h,
16 ; then the first sector (cluster, really, but we can only assume 1 sector)
17 ; of LDLINUX.SYS at 7E00h and finally the remainder of LDLINUX.SYS at 8000h.
19 ; Copyright (C) 1994-2004 H. Peter Anvin
21 ; This program is free software; you can redistribute it and/or modify
22 ; it under the terms of the GNU General Public License as published by
23 ; the Free Software Foundation, Inc., 53 Temple Place Ste 330,
24 ; Boston MA 02111-1307, USA; either version 2 of the License, or
25 ; (at your option) any later version; incorporated herein by reference.
27 ; ****************************************************************************
36 %include "tracers.inc"
39 ; Some semi-configurable constants... change on your own risk.
42 FILENAME_MAX_LG2 equ 4 ; log2(Max filename size Including final null)
43 FILENAME_MAX equ 11 ; Max mangled filename size
44 NULLFILE equ ' ' ; First char space == null filename
45 retry_count equ 6 ; How patient are we with the disk?
46 %assign HIGHMEM_SLOP 0 ; Avoid this much memory near the top
47 LDLINUX_MAGIC equ 0x3eb202fe ; A random number to identify ourselves with
50 SECTOR_SIZE equ (1 << SECTOR_SHIFT)
53 ; This is what we need to do when idle
63 ; The following structure is used for "virtual kernels"; i.e. LILO-style
64 ; option labels. The options we permit here are `kernel' and `append
65 ; Since there is no room in the bottom 64K for all of these, we
66 ; stick them at vk_seg:0000 and copy them down before we need them.
68 ; Note: this structure can be added to, but it must
70 %define vk_power 7 ; log2(max number of vkernels)
71 %define max_vk (1 << vk_power) ; Maximum number of vkernels
72 %define vk_shift (16-vk_power) ; Number of bits to shift
73 %define vk_size (1 << vk_shift) ; Size of a vkernel buffer
76 vk_vname: resb FILENAME_MAX ; Virtual name **MUST BE FIRST!**
77 vk_rname: resb FILENAME_MAX ; Real name
80 vk_append: resb max_cmd_len+1 ; Command line
82 vk_end: equ $ ; Should be <= vk_size
86 %if (vk_end > vk_size) || (vk_size*max_vk > 65536)
87 %error "Too many vkernels defined, reduce vk_power"
92 ; Segment assignments in the bottom 640K
93 ; Stick to the low 512K in case we're using something like M-systems flash
94 ; which load a driver into low RAM (evil!!)
96 ; 0000h - main code/data segment (and BIOS segment)
98 real_mode_seg equ 4000h
99 cache_seg equ 3000h ; 64K area for metadata cache
100 vk_seg equ 2000h ; Virtual kernels
101 xfer_buf_seg equ 1000h ; Bounce buffer for I/O to high mem
102 comboot_seg equ real_mode_seg ; COMBOOT image loading zone
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
119 ; Constants for the xfer_buf_seg
121 ; The xfer_buf_seg is also used to store message file buffers. We
122 ; need two trackbuffers (text and graphics), plus a work buffer
123 ; for the graphics decompressor.
125 xbs_textbuf equ 0 ; Also hard-coded, do not change
126 xbs_vgabuf equ trackbufsize
127 xbs_vgatmpbuf equ 2*trackbufsize
130 absolute 5000h ; Here we keep our BSS stuff
131 VKernelBuf: resb vk_size ; "Current" vkernel
133 AppendBuf resb max_cmd_len+1 ; append=
134 Ontimeout resb max_cmd_len+1 ; ontimeout
135 Onerror resb max_cmd_len+1 ; onerror
136 KbdMap resb 256 ; Keyboard map
137 FKeyName resb 10*16 ; File names for F-key help
138 NumBuf resb 15 ; Buffer to load number
139 NumBufEnd resb 1 ; Last byte in NumBuf
142 ; Expanded superblock
144 resq 16 ; The first 16 bytes expanded 8 times
146 FAT resd 1 ; Location of (first) FAT
147 RootDir resd 1 ; Location of root directory
148 DataArea resd 1 ; Location of data area
149 RootDirSize resd 1 ; Root dir size in sectors
150 TotalSectors resd 1 ; Total number of sectors
151 EndSector resd 1 ; Location of filesystem end
154 E820Buf resd 5 ; INT 15:E820 data buffer
155 E820Mem resd 1 ; Memory detected by E820
156 E820Max resd 1 ; Is E820 memory capped?
157 HiLoadAddr resd 1 ; Address pointer for high load loop
158 HighMemSize resd 1 ; End of memory pointer (bytes)
159 RamdiskMax resd 1 ; Highest address for a ramdisk
160 KernelSize resd 1 ; Size of kernel (bytes)
161 SavedSSSP resd 1 ; Our SS:SP while running a COMBOOT image
162 PMESP resd 1 ; Protected-mode ESP
163 ClustPerMoby resd 1 ; Clusters per 64K
164 ClustSize resd 1 ; Bytes/cluster
165 ClustMask resd 1 ; Sectors/cluster - 1
166 KernelName resb 12 ; Mangled name for kernel
167 ; (note the spare byte after!)
168 OrigKernelExt resd 1 ; Original kernel extension
169 FBytes equ $ ; Used by open/getc
172 DirBlocksLeft resw 1 ; Ditto
173 RunLinClust resw 1 ; Cluster # for LDLINUX.SYS
174 BufSafe resw 1 ; Clusters we can load into trackbuf
175 BufSafeSec resw 1 ; = how many sectors?
176 BufSafeBytes resw 1 ; = how many bytes?
177 EndOfGetCBuf resw 1 ; = getcbuf+BufSafeBytes
178 KernelClust resw 1 ; Kernel size in clusters
179 FClust resw 1 ; Number of clusters in open/getc file
180 FNextClust resw 1 ; Pointer to next cluster in d:o
181 FPtr resw 1 ; Pointer to next char in buffer
182 CmdOptPtr resw 1 ; Pointer to first option on cmd line
183 KernelCNameLen resw 1 ; Length of unmangled kernel name
184 InitRDCNameLen resw 1 ; Length of unmangled initrd name
185 NextCharJump resw 1 ; Routine to interpret next print char
186 SetupSecs resw 1 ; Number of setup sectors
187 A20Test resw 1 ; Counter for testing status of A20
188 A20Type resw 1 ; A20 type
189 CmdLineLen resw 1 ; Length of command line including null
190 GraphXSize resw 1 ; Width of splash screen file
191 VGAPos resw 1 ; Pointer into VGA memory
192 VGACluster resw 1 ; Cluster pointer for VGA image file
193 VGAFilePtr resw 1 ; Pointer into VGAFileBuf
194 Com32SysSP resw 1 ; SP saved during COM32 syscall
195 DirScanCtr resw 1 ; OBSOLETE FIX THIS
196 EndofDirSec resw 1 ; OBSOLETE FIX THIS
197 CachePtrs times (65536/SECTOR_SIZE) resw 1
200 CursorCol resb 1 ; Cursor column for message file
201 CursorRow resb 1 ; Cursor row for message file
203 VidCols resb 1 ; Columns on screen-1
204 VidRows resb 1 ; Rows on screen-1
205 BaudDivisor resw 1 ; Baud rate divisor
207 FlowOutput resb 1 ; Outputs to assert for serial flow
208 FlowInput resb 1 ; Input bits for serial flow
209 FlowIgnore resb 1 ; Ignore input unless these bits set
210 TextAttribute resb 1 ; Text attribute for message file
211 RetryCount resb 1 ; Used for disk access retries
212 KbdFlags resb 1 ; Check for keyboard escapes
213 LoadFlags resb 1 ; Loadflags from kernel
214 A20Tries resb 1 ; Times until giving up on A20
215 FuncFlag resb 1 ; Escape sequences received from keyboard
216 DisplayMask resb 1 ; Display modes mask
217 CopySuper resb 1 ; Distinguish .bs versus .bss
218 DriveNumber resb 1 ; BIOS drive number
219 ClustShift resb 1 ; Shift count for sectors/cluster
220 ClustByteShift resb 1 ; Shift count for bytes/cluster
221 MNameBuf resb 11 ; Generic mangled file name buffer
222 InitRD resb 11 ; initrd= mangled name
223 KernelCName resb 13 ; Unmangled kernel name
224 InitRDCName resb 13 ; Unmangled initrd name
225 TextColorReg resb 17 ; VGA color registers for text mode
226 VGAFileBuf resb 13 ; Unmangled VGA image name
228 VGAFileMBuf resb 11 ; Mangled VGA image name
229 alignb 4 ; For the good of REP MOVSD
230 command_line resb max_cmd_len+2 ; Command line buffer
232 default_cmd resb max_cmd_len+1 ; "default" command line
237 ; Some of the things that have to be saved very early are saved
238 ; "close" to the initial stack pointer offset, in order to
239 ; reduce the code size...
241 StackBuf equ $-44-32 ; Start the stack here (grow down - 4K)
242 PartInfo equ StackBuf ; Saved partition table entry
243 FloppyTable equ PartInfo+16 ; Floppy info table (must follow PartInfo)
244 OrigFDCTabPtr equ StackBuf-4 ; The high dword on the stack
247 ; Primary entry point. Tempting as though it may be, we can't put the
248 ; initial "cli" here; the jmp opcode in the first byte is part of the
249 ; "magic number" (using the term very loosely) for the DOS superblock.
252 jmp short start ; 2 bytes
255 ; "Superblock" follows -- it's in the boot sector, so it's already
256 ; loaded and ready for us
258 bsOemName db 'SYSLINUX' ; The SYS command sets this, so...
260 ; These are the fields we actually care about. We end up expanding them
261 ; all to dword size early in the code, so generate labels for both
262 ; the expanded and unexpanded versions.
265 bx %+ %1 equ SuperInfo+($-superblock)*8+4
270 bx %+ %1 equ SuperInfo+($-superblock)*8
275 bx %+ %1 equ $ ; no expansion for dwords
290 superinfo_size equ ($-superblock)-1 ; How much to expand
294 ; This is as far as FAT12/16 and FAT32 are consistent
296 zb 54 ; FAT12/16 need 26 more bytes,
297 ; FAT32 need 54 more bytes
298 superblock_len equ $-superblock
300 SecPerClust equ bxSecPerClust
302 ; Note we don't check the constraints above now; we did that at install
306 ;floppy_table equ $ ; No sense in wasting memory, overwrite start
309 cli ; No interrupts yet, please
316 mov sp,StackBuf ; Just below BSS
319 ; DS:SI may contain a partition table entry. Preserve it for us.
321 mov cx,8 ; Save partition info
325 mov ds,ax ; Now we can initialize DS...
328 ; Now sautee the BIOS floppy info block to that it will support decent-
329 ; size transfers; the floppy block is 11 bytes and is stored in the
330 ; INT 1Eh vector (brilliant waste of resources, eh?)
332 ; Of course, if BIOSes had been properly programmed, we wouldn't have
333 ; had to waste precious space with this code.
336 lfs si,[bx] ; FS:SI -> original fdctab
337 push fs ; Save on stack in case we need to bail
340 ; Save the old fdctab even if hard disk so the stack layout
341 ; is the same. The instructions above do not change the flags
342 mov [DriveNumber],dl ; Save drive number in DL
343 and dl,dl ; If floppy disk (00-7F), assume no
348 mov cl,6 ; 12 bytes (CX == 0)
349 ; es:di -> FloppyTable already
350 ; This should be safe to do now, interrupts are off...
351 mov [bx],di ; FloppyTable
352 mov [bx+2],ax ; Segment 0
353 fs rep movsw ; Faster to move words
354 mov cl,[bsSecPerTrack] ; Patch the sector count
357 int 13h ; Some BIOSes need this
359 jmp short not_harddisk
361 ; The drive number and possibly partition information was passed to us
362 ; by the BIOS or previous boot loader (MBR). Current "best practice" is to
363 ; trust that rather than what the superblock contains.
365 ; Would it be better to zero out bsHidden if we don't have a partition table?
367 ; Note: di points to beyond the end of PartInfo
370 test byte [di-16],7Fh ; Sanity check: "active flag" should
371 jnz no_partition ; be 00 or 80
372 mov eax,[di-8] ; Partition offset (dword)
376 ; Get disk drive parameters (don't trust the superblock.) Don't do this for
377 ; floppy drives -- INT 13:08 on floppy drives will (may?) return info about
378 ; what the *drive* supports, not about the *media*. Fortunately floppy disks
379 ; tend to have a fixed, well-defined geometry which is stored in the superblock.
381 ; DL == drive # still
388 inc dx ; Contains # of heads - 1
391 mov [bsSecPerTrack],cx
395 ; Ready to enable interrupts, captain
401 ; Do we have EBIOS (EDD)?
405 mov ah,41h ; EDD existence query
411 test cl,1 ; Extended disk access functionality set
414 ; We have EDD support...
416 mov byte [getlinsec+1],getlinsec_ebios-(getlinsec+2)
420 ; Load the first sector of LDLINUX.SYS; this used to be all proper
421 ; with parsing the superblock and root directory; it doesn't fit
422 ; together with EBIOS support, unfortunately.
424 mov eax,[FirstSector] ; Sector start
425 mov bx,ldlinux_sys ; Where to load it
428 ; Some modicum of integrity checking
429 cmp dword [ldlinux_magic],LDLINUX_MAGIC
431 cmp dword [ldlinux_magic+4],HEXDATE
438 ; kaboom: write a message and bail out.
443 mov sp,StackBuf-4 ; Reset stack
444 mov ds,si ; Reset data segment
445 pop dword [fdctab] ; Restore FDC table
446 .patch: mov si,bailmsg
447 call writestr ; Returns with AL = 0
449 int 16h ; Wait for keypress
450 int 19h ; And try once more to boot...
451 .norge: jmp short .norge ; If int 19h returned; this is the end
455 ; writestr: write a null-terminated string to the console
456 ; This assumes we're on page 0. This is only used for early
457 ; messages, so it should be OK.
463 mov ah,0Eh ; Write to screen as TTY
464 mov bx,0007h ; Attribute
470 ; xint13: wrapper for int 13h which will retry 6 times and then die,
471 ; AND save all registers except BP
483 jmp strict near kaboom ; Patched
487 ; getonesec: get one disk sector
490 mov bp,1 ; One sector
494 ; getlinsec: load a sequence of BP floppy sector given by the linear sector
495 ; number in EAX into the buffer at ES:BX. We try to optimize
496 ; by loading up to a whole track at a time, but the user
497 ; is responsible for not crossing a 64K boundary.
498 ; (Yes, BP is weird for a count, but it was available...)
500 ; On return, BX points to the first byte after the transferred
503 ; This routine assumes CS == DS, and trashes most registers.
505 ; Stylistic note: use "xchg" instead of "mov" when the source is a register
506 ; that is dead from that point; this saves space. However, please keep
507 ; the order to dst,src to keep things sane.
510 jmp strict short getlinsec_cbios ; This is patched
515 ; getlinsec implementation for EBIOS (EDD)
518 mov si,dapa ; Load up the DAPA
523 push bp ; Sectors left
524 call maxtrans ; Enforce maximum transfer size
528 mov ah,42h ; Extended Read
531 movzx eax,word [si+2] ; Sectors we read
532 add [si+8],eax ; Advance sector pointer
533 sub bp,ax ; Sectors left
534 shl ax,9 ; 512-byte sectors
535 add [si+4],ax ; Advance buffer pointer
538 mov eax,[si+8] ; Next sector
539 mov bx,[si+4] ; Buffer pointer
545 ; getlinsec implementation for legacy CBIOS
553 movzx esi,word [bsSecPerTrack]
554 movzx edi,word [bsHeads]
556 ; Dividing by sectors to get (track,sector): we may have
557 ; up to 2^18 tracks, so we need to use 32-bit arithmetric.
559 xor edx,edx ; Zero-extend LBA to 64 bits
562 xchg cx,dx ; CX <- sector index (0-based)
565 div edi ; Convert track to head/cyl
567 ; Now we have AX = cyl, DX = head, CX = sector (0-based),
568 ; BP = sectors to transfer, SI = bsSecPerTrack,
569 ; ES:BX = data target
572 call maxtrans ; Enforce maximum transfer size
574 ; Must not cross track boundaries, so BP <= SI-CX
581 shl ah,6 ; Because IBM was STOOPID
582 ; and thought 8 bits were enough
583 ; then thought 10 bits were enough...
584 inc cx ; Sector numbers are 1-based, sigh
589 xchg ax,bp ; Sector to transfer count
590 mov ah,02h ; Read sectors
593 shl ax,9 ; Convert sectors in AL to bytes in AX
604 ; Truncate BP to MaxTransfer
613 ; Error message on failure
615 bailmsg: db 'Boot failed', 0Dh, 0Ah, 0
618 ; EBIOS disk address packet
623 .count: dw 0 ; Block count
624 .off: dw 0 ; Offset of buffer
625 .seg: dw 0 ; Segment of buffer
626 .lba: dd 0 ; LBA (LSW)
631 bs_checkpt_off equ ($-$$)
633 %if bs_checkpt_off > 1F8h
634 %error "Boot sector overflow"
640 FirstSector dd 0xDEADBEEF ; Location of sector 1
641 MaxTransfer dw 0x007F ; Max transfer size
642 bootsignature dw 0AA55h
645 ; ===========================================================================
647 ; ===========================================================================
648 ; Start of LDLINUX.SYS
649 ; ===========================================================================
653 syslinux_banner db 0Dh, 0Ah
659 db version_str, ' ', date, ' ', 0
660 db 0Dh, 0Ah, 1Ah ; EOF if we "type" this in DOS
663 ldlinux_magic dd LDLINUX_MAGIC
667 ; This area is patched by the installer. It is found by looking for
668 ; LDLINUX_MAGIC, plus 4 bytes.
671 LDLDwords dw 0 ; Total dwords starting at ldlinux_sys
672 LDLSectors dw 0 ; Number of sectors - (bootsec+this sec)
673 CheckSum dd 0 ; Checksum starting at ldlinux_sys
674 ; value = LDLINUX_MAGIC - [sum of dwords]
676 ; Space for up to 64 sectors, the theoretical maximum
677 SectorPtrs times 64 dd 0
681 ; Note that some BIOSes are buggy and run the boot sector at 07C0:0000
682 ; instead of 0000:7C00 and the like. We don't want to add anything
683 ; more to the boot sector, so it is written to not assume a fixed
684 ; value in CS, but we don't want to deal with that anymore from now
691 ; Tell the user we got this far
693 mov si,syslinux_banner
697 ; Patch disk error handling
699 mov word [xint13.disk_error+1],do_disk_error-(xint13.disk_error+3)
702 ; Now we read the rest of LDLINUX.SYS. Don't bother loading the first
703 ; sector again, though.
707 mov bx,7C00h+2*SECTOR_SIZE ; Where we start loading
713 lodsd ; First sector of this chunk
721 inc edx ; Next linear sector
722 cmp [esi],edx ; Does it match
723 jnz .chunk_ready ; If not, this is it
724 inc esi ; If so, add sector to chunk
725 jmp short .make_chunk
734 ; All loaded up, verify that we got what we needed.
735 ; Note: the checksum field is embedded in the checksum region, so
736 ; by the time we get to the end it should all cancel out.
741 mov edx,-LDLINUX_MAGIC
747 and edx,edx ; Should be zero
748 jz all_read ; We're cool, go for it!
751 ; Uh-oh, something went bad...
753 mov si,checksumerr_msg
758 ; -----------------------------------------------------------------------------
759 ; Subroutines that have to be in the first sector
760 ; -----------------------------------------------------------------------------
763 ; getlinsecsr: save registers, call getlinsec, restore registers
771 ; This routine captures disk errors, and tries to decide if it is
772 ; time to reduce the transfer size.
777 shr al,1 ; Try reducing the transfer size
779 jz kaboom ; If we can't, we're dead...
780 jmp xint13 ; Try again
791 ; Checksum error message
793 checksumerr_msg db 'Load error - ', 0 ; Boot failed appended
800 cmp word [Debug_Magic],0D00Dh
805 rl_checkpt equ $ ; Must be <= 8000h
807 rl_checkpt_off equ ($-$$)
809 %if rl_checkpt_off > 400h
810 %error "Sector 1 overflow"
814 ; ----------------------------------------------------------------------------
815 ; End of code and data that have to be in the first sector
816 ; ----------------------------------------------------------------------------
820 ; Let the user (and programmer!) know we got this far. This used to be
821 ; in Sector 1, but makes a lot more sense here.
828 ; Insane hack to expand the superblock to dwords
832 mov es,ax ; INT 13:08 destroys ES
835 mov cx,superinfo_size
839 stosd ; Store expanded word
841 stosd ; Store expanded byte
844 ; How big is a cluster, really? Also figure out how many clusters
845 ; will fit in the trackbuf, and how many sectors and bytes that is
847 ; FIX THIS: We shouldn't rely on integral sectors in the trackbuf
850 mov edi,[bxBytesPerSec] ; Used a lot below
851 mov eax,[SecPerClust]
852 mov si,ax ; Also used a lot
854 mov [ClustSize],eax ; Bytes/cluster
856 mov ax,trackbufsize ; High bit 0
859 mov [BufSafe],ax ; # of cluster in trackbuf
863 mov [BufSafeBytes],ax
864 add ax,getcbuf ; Size of getcbuf is the same
865 mov [EndOfGetCBuf],ax ; as for trackbuf
868 ; Compute some information about this filesystem.
871 ; First, generate the map of regions
876 mov edx,[bsHugeSectors]
878 mov [TotalSectors],edx
880 mov eax,[bsHidden] ; Hidden sectors aren't included
884 add eax,[bxResSectors]
885 mov [FAT],eax ; Beginning of FAT
889 mov edx,[bootsec+36] ; FAT32 BPB_FATsz32
893 mov [RootDir],eax ; Beginning of root directory
895 mov edx,[bxRootDirEnts]
898 mov [RootDirSize],edx
900 mov [DataArea],eax ; Beginning of data area
902 ; Next, generate a cluster size shift count and mask
903 mov eax,[bxSecPerClust]
908 mov [ClustByteShift],cl
917 ; FAT12, FAT16 or FAT28^H^H32? This computation is fscking ridiculous.
922 shr eax,cl ; cl == ClustShift
923 mov cl,nextcluster_fat12-(nextcluster+2)
924 cmp eax,4085 ; FAT12 limit
926 mov cl,nextcluster_fat16-(nextcluster+2)
927 cmp eax,65525 ; FAT16 limit
929 mov cl,nextcluster_fat28-(nextcluster+2)
931 mov byte [nextcluster+1],cl
934 ; Common initialization code
936 %include "cpuinit.inc"
939 ; Initialization that does not need to go into the any of the pre-load
942 ; Now set up screen parameters
945 ; Wipe the F-key area
948 mov cx,10*(1 << FILENAME_MAX_LG2)
952 ; Now, everything is "up and running"... patch kaboom for more
953 ; verbosity and using the full screen system
956 mov dword [kaboom.patch],0e9h+((kaboom2-(kaboom.patch+3)) << 8)
959 ; Compute some parameters that depend on cluster size
963 inc dx ; DX:AX <- 64K
965 mov [ClustPerMoby],eax ; Clusters/64K
968 ; Now we're all set to start with our *real* business. First load the
969 ; configuration file (if any) and parse it.
971 ; In previous versions I avoided using 32-bit registers because of a
972 ; rumour some BIOSes clobbered the upper half of 32-bit registers at
973 ; random. I figure, though, that if there are any of those still left
974 ; they probably won't be trying to install Linux on them...
976 ; The code is still ripe with 16-bitisms, though. Not worth the hassle
977 ; to take'm out. In fact, we may want to put them back if we're going
978 ; to boot ELKS at some point.
980 mov si,linuxauto_cmd ; Default command: "linux auto"
985 mov di,KbdMap ; Default keymap 1:1
993 ; Load configuration file
1000 ; Now we have the config file open. Parse the config file and
1001 ; run the user interface.
1006 ; Linux kernel loading code is common.
1008 %include "runkernel.inc"
1011 ; COMBOOT-loading code
1013 %include "comboot.inc"
1014 %include "com32.inc"
1015 %include "cmdline.inc"
1018 ; Boot sector loading code
1020 %include "bootsect.inc"
1023 ; abort_check: let the user abort with <ESC> or <Ctrl-C>
1034 ac_kill: mov si,aborted_msg
1037 ; abort_load: Called by various routines which wants to print a fatal
1038 ; error message and return to the command prompt. Since this
1039 ; may happen at just about any stage of the boot process, assume
1040 ; our state is messed up, and just reset the segment registers
1041 ; and the stack forcibly.
1043 ; SI = offset (in _text) of error message to print
1046 mov ax,cs ; Restore CS = DS = ES
1050 mov sp,StackBuf-2*3 ; Reset stack
1051 mov ss,ax ; Just in case...
1053 call cwritestr ; Expects SI -> error msg
1054 al_ok: jmp enter_command ; Return to command prompt
1056 ; End of abort_check
1063 ; Search the root directory for a pre-mangled filename in DS:DI.
1065 ; NOTE: This file considers finding a zero-length file an
1066 ; error. This is so we don't have to deal with that special
1067 ; case elsewhere in the program (most loops have the test
1072 ; SI = cluster # for the first cluster
1073 ; DX:AX = file length in bytes
1080 mov ax,[bsRootDirEnts]
1082 mov ax,[RootDirSize]
1083 mov [DirBlocksLeft],ax
1086 movzx ebp,word [DirBlocksLeft]
1093 sub [DirBlocksLeft],bp
1095 mov ax,[bsBytesPerSec]
1098 mov [EndofDirSec],ax ; End of loaded
1103 dir_test_name: cmp byte [si],0 ; Directory high water mark
1104 je dir_return ; Failed
1105 test byte [si+11],18h ; Check it really is a file
1109 mov cx,11 ; Filename = 11 bytes
1114 dir_not_this: add si,byte 32
1115 dec word [DirScanCtr]
1116 jz dir_return ; Out of it...
1117 cmp si,[EndofDirSec]
1119 add eax,ebp ; Increment linear sector number
1120 jmp short scan_group
1122 mov ax,[si+28] ; Length of file
1124 mov si,[si+26] ; Cluster pointer
1126 or bx,dx ; Sets ZF iff DX:AX is zero
1132 ; writechr: Write a single character in AL to the console without
1133 ; mangling any registers; handle video pages correctly.
1136 call write_serial ; write to serial port if needed
1140 mov bl,07h ; attribute
1141 mov bh,[cs:BIOS_page] ; current page
1149 ; kaboom2: once everything is loaded, replace the part of kaboom
1150 ; starting with "kaboom.patch" with this part
1153 mov si,err_bootfailed
1157 int 19h ; And try once more to boot...
1158 .norge: jmp short .norge ; If int 19h returned; this is the end
1161 ; mangle_name: Mangle a DOS filename pointed to by DS:SI into a buffer pointed
1162 ; to by ES:DI; ends on encountering any whitespace
1166 mov cx,11 ; # of bytes to write
1169 cmp al,' ' ; If control or space, end
1171 cmp al,'.' ; Period -> space-fill
1178 jmp short mn_not_lower
1179 mn_is_period: mov al,' ' ; We need to space-fill
1180 mn_period_loop: cmp cx,3 ; If <= 3 characters left
1181 jbe mn_loop ; Just ignore it
1182 stosb ; Otherwise, write a period
1183 loop mn_period_loop ; Dec CX and (always) jump
1184 mn_not_uslower: cmp al,ucase_low
1188 mov bx,ucase_tab-ucase_low
1191 loop mn_loop ; Don't continue if too long
1193 mov al,' ' ; Space-fill name
1194 rep stosb ; Doesn't do anything if CX=0
1198 ; Upper-case table for extended characters; this is technically code page 865,
1199 ; but code page 437 users will probably not miss not being able to use the
1200 ; cent sign in kernel images too much :-)
1202 ; The table only covers the range 129 to 164; the rest we can deal with.
1206 ucase_tab db 154, 144, 'A', 142, 'A', 143, 128, 'EEEIII'
1207 db 142, 143, 144, 146, 146, 'O', 153, 'OUUY', 153, 154
1208 db 157, 156, 157, 158, 159, 'AIOU', 165
1211 ; unmangle_name: Does the opposite of mangle_name; converts a DOS-mangled
1212 ; filename to the conventional representation. This is needed
1213 ; for the BOOT_IMAGE= parameter for the kernel.
1214 ; NOTE: A 13-byte buffer is mandatory, even if the string is
1215 ; known to be shorter.
1217 ; DS:SI -> input mangled file name
1218 ; ES:DI -> output buffer
1220 ; On return, DI points to the first byte after the output name,
1221 ; which is set to a null byte.
1224 push si ; Save pointer to original name
1232 mov bp,di ; Position of last nonblank+1
1233 un_cb_space: loop un_copy_body
1235 mov al,'.' ; Don't save
1244 un_ce_space: loop un_copy_ext
1251 ; lower_case: Lower case a character in AL
1260 lc_1: cmp al,lcase_low
1265 mov bx,lcase_tab-lcase_low
1271 ; getfssec: Get multiple sectors from a file
1273 ; This routine makes sure the subtransfers do not cross a 64K boundary,
1274 ; and will correct the situation if it does, UNLESS *sectors* cross
1278 ; SI -> Pointer to structure:
1279 ; 0 - dword - Starting cluster number (2-based)
1280 ; 4 - word - Sector number within cluster
1281 ; 8 - dword - Absolute sector number
1282 ; CX -> Sector count (0FFFFh = until end of file)
1283 ; Must not exceed the ES segment
1284 ; Returns CF=1 on EOF
1287 .getfragment: xor ebp,ebp ; Fragment sector count
1292 ; jc .isrootdir ; Use cluster 1 for the root directory
1293 mul dword [SecPerClust]
1295 sub bp,[si+4] ; Sectors already read
1296 .getseccnt: ; See if we can read > 1 clust
1297 add bp,[SecPerClust]
1299 jna .endfragment ; Done?
1303 cmp eax,edi ; Is file continuous?
1304 je .getseccnt ; Yes, we can get
1305 .endfragment: clc ; Not at EOF
1306 .eof: pushf ; Remember EOF or not
1311 mov ax,es ; Check for 64K boundaries.
1316 setz dl ; DX <- 1 if full 64K segment
1317 div word [bsBytesPerSec] ; How many sectors fit?
1319 sub si,ax ; Compute remaining sectors
1324 add eax,ebp ; EBP<31:16> == 0
1325 mov bp,si ; Remaining sector count
1332 jcxz .return ; If we hit the count limit
1333 jnc .getfragment ; If we didn't hit EOF
1337 ; nextcluster: Advance a cluster pointer in EDI to the next cluster
1338 ; pointed at in the FAT tables. CF=0 on return if end of file.
1341 jmp strict short nextcluster_fat28 ; This gets patched
1379 ; FAT16 decoding routine.
1386 shr eax,SECTOR_SHIFT-1
1391 movzx edi,word [gs:si+bx]
1398 ; FAT28 ("FAT32") decoding routine.
1405 shr eax,SECTOR_SHIFT-2
1411 mov edi,dword [gs:si+bx]
1412 and edi,0FFFFFFFh ; 28 bits only
1420 ; getfatsector: Check for a particular sector (in EAX) in the FAT cache,
1421 ; and return a pointer in GS:SI, loading it if needed.
1426 add eax,[bsHidden] ; Hidden sectors
1427 add eax,[bxResSectors] ; Reserved sectors
1431 ; getcachesector: Check for a particular sector (EAX) in the sector cache,
1432 ; and if it is already there, return a pointer in GS:SI
1433 ; otherwise load it and return said pointer.
1441 mov si,CachePtrs ; Sector cache pointers
1442 mov cx,65536/SECTOR_SIZE
1443 repne scasd ; Do we have it?
1445 ; We have it; get the pointer
1447 shl si,SECTOR_SHIFT-2
1451 ; Need to load it. Highly inefficient cache replacement
1452 ; algorithm: Least Recently Written (LRW)
1457 mov bx,[NextCacheSlot]
1459 and bx,(1 << (16-SECTOR_SHIFT))-1
1460 mov [NextCacheSlot],bx
1462 mov [CachePtrs+bx],eax
1463 shl bx,SECTOR_SHIFT-2
1473 ; -----------------------------------------------------------------------------
1475 ; -----------------------------------------------------------------------------
1477 %include "getc.inc" ; getc et al
1478 %include "conio.inc" ; Console I/O
1479 %include "writestr.inc" ; String output
1480 %include "parseconfig.inc" ; High-level config file handling
1481 %include "parsecmd.inc" ; Low-level config file handling
1482 %include "bcopy32.inc" ; 32-bit bcopy
1483 %include "loadhigh.inc" ; Load a file into high memory
1484 %include "font.inc" ; VGA font stuff
1485 %include "graphics.inc" ; VGA graphics
1486 %include "highmem.inc" ; High memory sizing
1487 %include "strcpy.inc" ; strcpy()
1489 ; -----------------------------------------------------------------------------
1490 ; Begin data section
1491 ; -----------------------------------------------------------------------------
1494 ; Lower-case table for codepage 865
1498 lcase_tab db 135, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138
1499 db 139, 140, 141, 132, 134, 130, 145, 145, 147, 148, 149
1500 db 150, 151, 152, 148, 129, 155, 156, 155, 158, 159, 160
1501 db 161, 162, 163, 164, 164
1503 copyright_str db ' Copyright (C) 1994-', year, ' H. Peter Anvin'
1505 boot_prompt db 'boot: ', 0
1506 wipe_char db BS, ' ', BS, 0
1507 err_notfound db 'Could not find kernel image: ',0
1508 err_notkernel db CR, LF, 'Invalid or corrupt kernel image.', CR, LF, 0
1509 err_noram db 'It appears your computer has less than '
1511 db 'K of low ("DOS")'
1513 db 'RAM. Linux needs at least this amount to boot. If you get'
1515 db 'this message in error, hold down the Ctrl key while'
1517 db 'booting, and I will take your word for it.', CR, LF, 0
1518 err_badcfg db 'Unknown keyword in syslinux.cfg.', CR, LF, 0
1519 err_noparm db 'Missing parameter in syslinux.cfg.', CR, LF, 0
1520 err_noinitrd db CR, LF, 'Could not find ramdisk image: ', 0
1521 err_nohighmem db 'Not enough memory to load specified kernel.', CR, LF, 0
1522 err_highload db CR, LF, 'Kernel transfer failure.', CR, LF, 0
1523 err_oldkernel db 'Cannot load a ramdisk with an old kernel image.'
1525 err_notdos db ': attempted DOS system call', CR, LF, 0
1526 err_comlarge db 'COMBOOT image too large.', CR, LF, 0
1527 err_a20 db CR, LF, 'A20 gate not responding!', CR, LF, 0
1528 err_bootfailed db CR, LF, 'Boot failed: please change disks and press '
1529 db 'a key to continue.', CR, LF, 0
1530 ready_msg db 'Ready.', CR, LF, 0
1531 crlfloading_msg db CR, LF
1532 loading_msg db 'Loading ', 0
1535 aborted_msg db ' aborted.' ; Fall through to crlf_msg!
1538 crff_msg db CR, FF, 0
1539 syslinux_cfg db 'SYSLINUXCFG' ; Mangled form
1540 ConfigName db 'syslinux.cfg',0 ; Unmangled form
1542 manifest db 'MANIFEST '
1545 ; Command line options we'd like to take a look at
1547 ; mem= and vga= are handled as normal 32-bit integer values
1548 initrd_cmd db 'initrd='
1549 initrd_cmd_len equ 7
1552 ; Config file keyword table
1554 %include "keywords.inc"
1557 ; Extensions to search for (in *forward* order).
1559 exten_table: db 'CBT',0 ; COMBOOT (specific)
1560 db 'BSS',0 ; Boot Sector (add superblock)
1561 db 'BS ',0 ; Boot Sector
1562 db 'COM',0 ; COMBOOT (same as DOS)
1565 dd 0, 0 ; Need 8 null bytes here
1568 ; Misc initialized (data) variables
1570 %ifdef debug ; This code for debugging only
1571 debug_magic dw 0D00Dh ; Debug code sentinel
1573 AppendLen dw 0 ; Bytes in append= command
1574 OntimeoutLen dw 0 ; Bytes in ontimeout command
1575 OnerrorLen dw 0 ; Bytes in onerror command
1576 KbdTimeOut dw 0 ; Keyboard timeout (if any)
1577 CmdLinePtr dw cmd_line_here ; Command line advancing pointer
1579 initrd_ptr dw 0 ; Initial ramdisk pointer/flag
1580 VKernelCtr dw 0 ; Number of registered vkernels
1581 ForcePrompt dw 0 ; Force prompt
1582 AllowImplicit dw 1 ; Allow implicit kernels
1583 AllowOptions dw 1 ; User-specified options allowed
1584 SerialPort dw 0 ; Serial port base (or 0 for no serial port)
1585 VGAFontSize dw 16 ; Defaults to 16 byte font
1586 UserFont db 0 ; Using a user-specified font
1587 ScrollAttribute db 07h ; White on black (for text mode)
1589 ; Stuff for the command line; we do some trickery here with equ to avoid
1590 ; tons of zeros appended to our file and wasting space
1592 linuxauto_cmd db 'linux auto',0
1593 linuxauto_len equ $-linuxauto_cmd
1594 boot_image db 'BOOT_IMAGE='
1595 boot_image_len equ $-boot_image
1597 align 4, db 0 ; Pad out any unfinished dword
1599 ldlinux_len equ $-ldlinux_magic
1601 ; VGA font buffer at the end of memory (so loading a font works even
1602 ; in graphics mode.)
1603 vgafontbuf equ 0E000h
1605 ; This is a compile-time assert that we didn't run out of space
1607 %if (ldlinux_end-bootsec+7C00h) > vgafontbuf
1608 %error "Out of memory, better reorganize something..."