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
49 MAX_OPEN_LG2 equ 6 ; log2(Max number of open files)
50 MAX_OPEN equ (1 << MAX_OPEN_LG2)
53 SECTOR_SIZE equ (1 << SECTOR_SHIFT)
56 ; This is what we need to do when idle
66 ; The following structure is used for "virtual kernels"; i.e. LILO-style
67 ; option labels. The options we permit here are `kernel' and `append
68 ; Since there is no room in the bottom 64K for all of these, we
69 ; stick them at vk_seg:0000 and copy them down before we need them.
71 ; Note: this structure can be added to, but it must
73 %define vk_power 7 ; log2(max number of vkernels)
74 %define max_vk (1 << vk_power) ; Maximum number of vkernels
75 %define vk_shift (16-vk_power) ; Number of bits to shift
76 %define vk_size (1 << vk_shift) ; Size of a vkernel buffer
79 vk_vname: resb FILENAME_MAX ; Virtual name **MUST BE FIRST!**
80 vk_rname: resb FILENAME_MAX ; Real name
83 vk_append: resb max_cmd_len+1 ; Command line
85 vk_end: equ $ ; Should be <= vk_size
89 %if (vk_end > vk_size) || (vk_size*max_vk > 65536)
90 %error "Too many vkernels defined, reduce vk_power"
95 ; Segment assignments in the bottom 640K
96 ; Stick to the low 512K in case we're using something like M-systems flash
97 ; which load a driver into low RAM (evil!!)
99 ; 0000h - main code/data segment (and BIOS segment)
101 real_mode_seg equ 4000h
102 cache_seg equ 3000h ; 64K area for metadata cache
103 vk_seg equ 2000h ; Virtual kernels
104 xfer_buf_seg equ 1000h ; Bounce buffer for I/O to high mem
105 comboot_seg equ real_mode_seg ; COMBOOT image loading zone
108 ; File structure. This holds the information for each currently open file.
111 file_sector resd 1 ; Sector pointer (0 = structure free)
112 file_left resd 1 ; Number of sectors left
116 %if (open_file_t_size & (open_file_t_size-1))
117 %error "open_file_t is not a power of 2"
121 ; ---------------------------------------------------------------------------
123 ; ---------------------------------------------------------------------------
126 ; Memory below this point is reserved for the BIOS and the MBR
129 trackbufsize equ 8192
130 trackbuf resb trackbufsize ; Track buffer goes here
131 getcbuf resb trackbufsize
136 ; Constants for the xfer_buf_seg
138 ; The xfer_buf_seg is also used to store message file buffers. We
139 ; need two trackbuffers (text and graphics), plus a work buffer
140 ; for the graphics decompressor.
142 xbs_textbuf equ 0 ; Also hard-coded, do not change
143 xbs_vgabuf equ trackbufsize
144 xbs_vgatmpbuf equ 2*trackbufsize
147 absolute 5000h ; Here we keep our BSS stuff
148 VKernelBuf: resb vk_size ; "Current" vkernel
150 AppendBuf resb max_cmd_len+1 ; append=
151 Ontimeout resb max_cmd_len+1 ; ontimeout
152 Onerror resb max_cmd_len+1 ; onerror
153 KbdMap resb 256 ; Keyboard map
154 FKeyName resb 10*16 ; File names for F-key help
155 NumBuf resb 15 ; Buffer to load number
156 NumBufEnd resb 1 ; Last byte in NumBuf
159 ; Expanded superblock
161 resq 16 ; The first 16 bytes expanded 8 times
163 FAT resd 1 ; Location of (first) FAT
164 RootDirArea resd 1 ; Location of root directory area
165 RootDir resd 1 ; Location of root directory proper
166 DataArea resd 1 ; Location of data area
167 RootDirSize resd 1 ; Root dir size in sectors
168 TotalSectors resd 1 ; Total number of sectors
169 EndSector resd 1 ; Location of filesystem end
172 E820Buf resd 5 ; INT 15:E820 data buffer
173 E820Mem resd 1 ; Memory detected by E820
174 E820Max resd 1 ; Is E820 memory capped?
175 HiLoadAddr resd 1 ; Address pointer for high load loop
176 HighMemSize resd 1 ; End of memory pointer (bytes)
177 RamdiskMax resd 1 ; Highest address for a ramdisk
178 KernelSize resd 1 ; Size of kernel (bytes)
179 SavedSSSP resd 1 ; Our SS:SP while running a COMBOOT image
180 PMESP resd 1 ; Protected-mode ESP
181 FSectors resd 1 ; Number of sectors in getc file
182 ClustSize resd 1 ; Bytes/cluster
183 ClustMask resd 1 ; Sectors/cluster - 1
184 KernelName resb 12 ; Mangled name for kernel
185 ; (note the spare byte after!)
186 OrigKernelExt resd 1 ; Original kernel extension
187 FBytes equ $ ; Used by open/getc
190 DirBlocksLeft resw 1 ; Ditto
191 RunLinClust resw 1 ; Cluster # for LDLINUX.SYS
192 KernelSects resw 1 ; Kernel size in clusters
193 FNextClust resw 1 ; Pointer to next cluster in d:o
194 FPtr resw 1 ; Pointer to next char in buffer
195 CmdOptPtr resw 1 ; Pointer to first option on cmd line
196 KernelCNameLen resw 1 ; Length of unmangled kernel name
197 InitRDCNameLen resw 1 ; Length of unmangled initrd name
198 NextCharJump resw 1 ; Routine to interpret next print char
199 SetupSecs resw 1 ; Number of setup sectors
200 A20Test resw 1 ; Counter for testing status of A20
201 A20Type resw 1 ; A20 type
202 CmdLineLen resw 1 ; Length of command line including null
203 GraphXSize resw 1 ; Width of splash screen file
204 VGAPos resw 1 ; Pointer into VGA memory
205 VGACluster resw 1 ; Cluster pointer for VGA image file
206 VGAFilePtr resw 1 ; Pointer into VGAFileBuf
207 Com32SysSP resw 1 ; SP saved during COM32 syscall
208 DirScanCtr resw 1 ; OBSOLETE FIX THIS
209 EndofDirSec resw 1 ; OBSOLETE FIX THIS
210 CachePtrs times (65536/SECTOR_SIZE) resw 1
213 CursorCol resb 1 ; Cursor column for message file
214 CursorRow resb 1 ; Cursor row for message file
216 VidCols resb 1 ; Columns on screen-1
217 VidRows resb 1 ; Rows on screen-1
218 BaudDivisor resw 1 ; Baud rate divisor
220 FlowOutput resb 1 ; Outputs to assert for serial flow
221 FlowInput resb 1 ; Input bits for serial flow
222 FlowIgnore resb 1 ; Ignore input unless these bits set
223 TextAttribute resb 1 ; Text attribute for message file
224 RetryCount resb 1 ; Used for disk access retries
225 KbdFlags resb 1 ; Check for keyboard escapes
226 LoadFlags resb 1 ; Loadflags from kernel
227 A20Tries resb 1 ; Times until giving up on A20
228 FuncFlag resb 1 ; Escape sequences received from keyboard
229 DisplayMask resb 1 ; Display modes mask
230 CopySuper resb 1 ; Distinguish .bs versus .bss
231 DriveNumber resb 1 ; BIOS drive number
232 ClustShift resb 1 ; Shift count for sectors/cluster
233 ClustByteShift resb 1 ; Shift count for bytes/cluster
234 MNameBuf resb 11 ; Generic mangled file name buffer
235 InitRD resb 11 ; initrd= mangled name
236 KernelCName resb 13 ; Unmangled kernel name
237 InitRDCName resb 13 ; Unmangled initrd name
238 TextColorReg resb 17 ; VGA color registers for text mode
239 VGAFileBuf resb 13 ; Unmangled VGA image name
241 VGAFileMBuf resb 11 ; Mangled VGA image name
242 alignb 4 ; For the good of REP MOVSD
243 command_line resb max_cmd_len+2 ; Command line buffer
245 default_cmd resb max_cmd_len+1 ; "default" command line
247 alignb open_file_t_size
248 Files resb MAX_OPEN*open_file_t_size
253 ; Some of the things that have to be saved very early are saved
254 ; "close" to the initial stack pointer offset, in order to
255 ; reduce the code size...
257 StackBuf equ $-44-32 ; Start the stack here (grow down - 4K)
258 PartInfo equ StackBuf ; Saved partition table entry
259 FloppyTable equ PartInfo+16 ; Floppy info table (must follow PartInfo)
260 OrigFDCTabPtr equ StackBuf-4 ; The high dword on the stack
263 ; Primary entry point. Tempting as though it may be, we can't put the
264 ; initial "cli" here; the jmp opcode in the first byte is part of the
265 ; "magic number" (using the term very loosely) for the DOS superblock.
268 jmp short start ; 2 bytes
271 ; "Superblock" follows -- it's in the boot sector, so it's already
272 ; loaded and ready for us
274 bsOemName db 'SYSLINUX' ; The SYS command sets this, so...
276 ; These are the fields we actually care about. We end up expanding them
277 ; all to dword size early in the code, so generate labels for both
278 ; the expanded and unexpanded versions.
281 bx %+ %1 equ SuperInfo+($-superblock)*8+4
286 bx %+ %1 equ SuperInfo+($-superblock)*8
291 bx %+ %1 equ $ ; no expansion for dwords
306 superinfo_size equ ($-superblock)-1 ; How much to expand
310 ; This is as far as FAT12/16 and FAT32 are consistent
312 zb 54 ; FAT12/16 need 26 more bytes,
313 ; FAT32 need 54 more bytes
314 superblock_len equ $-superblock
316 SecPerClust equ bxSecPerClust
318 ; Note we don't check the constraints above now; we did that at install
322 ;floppy_table equ $ ; No sense in wasting memory, overwrite start
325 cli ; No interrupts yet, please
332 mov sp,StackBuf ; Just below BSS
335 ; DS:SI may contain a partition table entry. Preserve it for us.
337 mov cx,8 ; Save partition info
341 mov ds,ax ; Now we can initialize DS...
344 ; Now sautee the BIOS floppy info block to that it will support decent-
345 ; size transfers; the floppy block is 11 bytes and is stored in the
346 ; INT 1Eh vector (brilliant waste of resources, eh?)
348 ; Of course, if BIOSes had been properly programmed, we wouldn't have
349 ; had to waste precious space with this code.
352 lfs si,[bx] ; FS:SI -> original fdctab
353 push fs ; Save on stack in case we need to bail
356 ; Save the old fdctab even if hard disk so the stack layout
357 ; is the same. The instructions above do not change the flags
358 mov [DriveNumber],dl ; Save drive number in DL
359 and dl,dl ; If floppy disk (00-7F), assume no
364 mov cl,6 ; 12 bytes (CX == 0)
365 ; es:di -> FloppyTable already
366 ; This should be safe to do now, interrupts are off...
367 mov [bx],di ; FloppyTable
368 mov [bx+2],ax ; Segment 0
369 fs rep movsw ; Faster to move words
370 mov cl,[bsSecPerTrack] ; Patch the sector count
373 int 13h ; Some BIOSes need this
375 jmp short not_harddisk
377 ; The drive number and possibly partition information was passed to us
378 ; by the BIOS or previous boot loader (MBR). Current "best practice" is to
379 ; trust that rather than what the superblock contains.
381 ; Would it be better to zero out bsHidden if we don't have a partition table?
383 ; Note: di points to beyond the end of PartInfo
386 test byte [di-16],7Fh ; Sanity check: "active flag" should
387 jnz no_partition ; be 00 or 80
388 mov eax,[di-8] ; Partition offset (dword)
392 ; Get disk drive parameters (don't trust the superblock.) Don't do this for
393 ; floppy drives -- INT 13:08 on floppy drives will (may?) return info about
394 ; what the *drive* supports, not about the *media*. Fortunately floppy disks
395 ; tend to have a fixed, well-defined geometry which is stored in the superblock.
397 ; DL == drive # still
404 inc dx ; Contains # of heads - 1
407 mov [bsSecPerTrack],cx
411 ; Ready to enable interrupts, captain
417 ; Do we have EBIOS (EDD)?
421 mov ah,41h ; EDD existence query
427 test cl,1 ; Extended disk access functionality set
430 ; We have EDD support...
432 mov byte [getlinsec+1],getlinsec_ebios-(getlinsec+2)
436 ; Load the first sector of LDLINUX.SYS; this used to be all proper
437 ; with parsing the superblock and root directory; it doesn't fit
438 ; together with EBIOS support, unfortunately.
440 mov eax,[FirstSector] ; Sector start
441 mov bx,ldlinux_sys ; Where to load it
444 ; Some modicum of integrity checking
445 cmp dword [ldlinux_magic],LDLINUX_MAGIC
447 cmp dword [ldlinux_magic+4],HEXDATE
454 ; kaboom: write a message and bail out.
459 mov sp,StackBuf-4 ; Reset stack
460 mov ds,si ; Reset data segment
461 pop dword [fdctab] ; Restore FDC table
462 .patch: mov si,bailmsg
463 call writestr ; Returns with AL = 0
465 int 16h ; Wait for keypress
466 int 19h ; And try once more to boot...
467 .norge: jmp short .norge ; If int 19h returned; this is the end
471 ; writestr: write a null-terminated string to the console
472 ; This assumes we're on page 0. This is only used for early
473 ; messages, so it should be OK.
479 mov ah,0Eh ; Write to screen as TTY
480 mov bx,0007h ; Attribute
486 ; xint13: wrapper for int 13h which will retry 6 times and then die,
487 ; AND save all registers except BP
499 jmp strict near kaboom ; Patched
503 ; getonesec: get one disk sector
506 mov bp,1 ; One sector
510 ; getlinsec: load a sequence of BP floppy sector given by the linear sector
511 ; number in EAX into the buffer at ES:BX. We try to optimize
512 ; by loading up to a whole track at a time, but the user
513 ; is responsible for not crossing a 64K boundary.
514 ; (Yes, BP is weird for a count, but it was available...)
516 ; On return, BX points to the first byte after the transferred
519 ; This routine assumes CS == DS, and trashes most registers.
521 ; Stylistic note: use "xchg" instead of "mov" when the source is a register
522 ; that is dead from that point; this saves space. However, please keep
523 ; the order to dst,src to keep things sane.
526 jmp strict short getlinsec_cbios ; This is patched
531 ; getlinsec implementation for EBIOS (EDD)
534 mov si,dapa ; Load up the DAPA
539 push bp ; Sectors left
540 call maxtrans ; Enforce maximum transfer size
544 mov ah,42h ; Extended Read
547 movzx eax,word [si+2] ; Sectors we read
548 add [si+8],eax ; Advance sector pointer
549 sub bp,ax ; Sectors left
550 shl ax,9 ; 512-byte sectors
551 add [si+4],ax ; Advance buffer pointer
554 mov eax,[si+8] ; Next sector
555 mov bx,[si+4] ; Buffer pointer
561 ; getlinsec implementation for legacy CBIOS
569 movzx esi,word [bsSecPerTrack]
570 movzx edi,word [bsHeads]
572 ; Dividing by sectors to get (track,sector): we may have
573 ; up to 2^18 tracks, so we need to use 32-bit arithmetric.
575 xor edx,edx ; Zero-extend LBA to 64 bits
578 xchg cx,dx ; CX <- sector index (0-based)
581 div edi ; Convert track to head/cyl
583 ; Now we have AX = cyl, DX = head, CX = sector (0-based),
584 ; BP = sectors to transfer, SI = bsSecPerTrack,
585 ; ES:BX = data target
588 call maxtrans ; Enforce maximum transfer size
590 ; Must not cross track boundaries, so BP <= SI-CX
597 shl ah,6 ; Because IBM was STOOPID
598 ; and thought 8 bits were enough
599 ; then thought 10 bits were enough...
600 inc cx ; Sector numbers are 1-based, sigh
605 xchg ax,bp ; Sector to transfer count
606 mov ah,02h ; Read sectors
609 shl ax,9 ; Convert sectors in AL to bytes in AX
620 ; Truncate BP to MaxTransfer
629 ; Error message on failure
631 bailmsg: db 'Boot failed', 0Dh, 0Ah, 0
634 ; EBIOS disk address packet
639 .count: dw 0 ; Block count
640 .off: dw 0 ; Offset of buffer
641 .seg: dw 0 ; Segment of buffer
642 .lba: dd 0 ; LBA (LSW)
647 bs_checkpt_off equ ($-$$)
649 %if bs_checkpt_off > 1F8h
650 %error "Boot sector overflow"
656 FirstSector dd 0xDEADBEEF ; Location of sector 1
657 MaxTransfer dw 0x007F ; Max transfer size
658 bootsignature dw 0AA55h
661 ; ===========================================================================
663 ; ===========================================================================
664 ; Start of LDLINUX.SYS
665 ; ===========================================================================
669 syslinux_banner db 0Dh, 0Ah
675 db version_str, ' ', date, ' ', 0
676 db 0Dh, 0Ah, 1Ah ; EOF if we "type" this in DOS
679 ldlinux_magic dd LDLINUX_MAGIC
683 ; This area is patched by the installer. It is found by looking for
684 ; LDLINUX_MAGIC, plus 8 bytes.
687 LDLDwords dw 0 ; Total dwords starting at ldlinux_sys
688 LDLSectors dw 0 ; Number of sectors - (bootsec+this sec)
689 CheckSum dd 0 ; Checksum starting at ldlinux_sys
690 ; value = LDLINUX_MAGIC - [sum of dwords]
692 ; Space for up to 64 sectors, the theoretical maximum
693 SectorPtrs times 64 dd 0
697 ; Note that some BIOSes are buggy and run the boot sector at 07C0:0000
698 ; instead of 0000:7C00 and the like. We don't want to add anything
699 ; more to the boot sector, so it is written to not assume a fixed
700 ; value in CS, but we don't want to deal with that anymore from now
707 ; Tell the user we got this far
709 mov si,syslinux_banner
713 ; Patch disk error handling
715 mov word [xint13.disk_error+1],do_disk_error-(xint13.disk_error+3)
718 ; Now we read the rest of LDLINUX.SYS. Don't bother loading the first
719 ; sector again, though.
723 mov bx,7C00h+2*SECTOR_SIZE ; Where we start loading
729 lodsd ; First sector of this chunk
737 inc edx ; Next linear sector
738 cmp [esi],edx ; Does it match
739 jnz .chunk_ready ; If not, this is it
740 add esi,4 ; If so, add sector to chunk
741 jmp short .make_chunk
752 ; All loaded up, verify that we got what we needed.
753 ; Note: the checksum field is embedded in the checksum region, so
754 ; by the time we get to the end it should all cancel out.
759 mov edx,-LDLINUX_MAGIC
765 and edx,edx ; Should be zero
766 jz all_read ; We're cool, go for it!
769 ; Uh-oh, something went bad...
771 mov si,checksumerr_msg
776 ; -----------------------------------------------------------------------------
777 ; Subroutines that have to be in the first sector
778 ; -----------------------------------------------------------------------------
781 ; getlinsecsr: save registers, call getlinsec, restore registers
789 ; This routine captures disk errors, and tries to decide if it is
790 ; time to reduce the transfer size.
795 shr al,1 ; Try reducing the transfer size
797 jz kaboom ; If we can't, we're dead...
798 jmp xint13 ; Try again
809 ; Checksum error message
811 checksumerr_msg db 'Load error - ', 0 ; Boot failed appended
818 cmp word [Debug_Magic],0D00Dh
823 rl_checkpt equ $ ; Must be <= 8000h
825 rl_checkpt_off equ ($-$$)
827 %if rl_checkpt_off > 400h
828 %error "Sector 1 overflow"
832 ; ----------------------------------------------------------------------------
833 ; End of code and data that have to be in the first sector
834 ; ----------------------------------------------------------------------------
838 ; Let the user (and programmer!) know we got this far. This used to be
839 ; in Sector 1, but makes a lot more sense here.
846 ; Insane hack to expand the superblock to dwords
850 mov es,ax ; INT 13:08 destroys ES
853 mov cx,superinfo_size
857 stosd ; Store expanded word
859 stosd ; Store expanded byte
862 ; How big is a cluster, really? Also figure out how many clusters
863 ; will fit in the trackbuf, and how many sectors and bytes that is
865 ; FIX THIS: We shouldn't rely on integral sectors in the trackbuf
868 mov edi,[bxBytesPerSec] ; Used a lot below
869 mov eax,[SecPerClust]
870 mov si,ax ; Also used a lot
872 mov [ClustSize],eax ; Bytes/cluster
874 mov ax,trackbufsize ; High bit 0
877 mov [BufSafe],ax ; # of cluster in trackbuf
881 mov [BufSafeBytes],ax
882 add ax,getcbuf ; Size of getcbuf is the same
883 mov [EndOfGetCBuf],ax ; as for trackbuf
886 ; Compute some information about this filesystem.
889 ; First, generate the map of regions
894 mov edx,[bsHugeSectors]
896 mov [TotalSectors],edx
898 mov eax,[bsHidden] ; Hidden sectors aren't included
902 add eax,[bxResSectors]
903 mov [FAT],eax ; Beginning of FAT
907 mov edx,[bootsec+36] ; FAT32 BPB_FATsz32
911 mov [RootDirArea],eax ; Beginning of root directory
912 mov [RootDir],eax ; For FAT12/16 == root dir location
914 mov edx,[bxRootDirEnts]
917 mov [RootDirSize],edx
919 mov [DataArea],eax ; Beginning of data area
921 ; Next, generate a cluster size shift count and mask
922 mov eax,[bxSecPerClust]
927 mov [ClustByteShift],cl
936 ; FAT12, FAT16 or FAT28^H^H32? This computation is fscking ridiculous.
941 shr eax,cl ; cl == ClustShift
942 mov cl,nextcluster_fat12-(nextcluster+2)
943 cmp eax,4085 ; FAT12 limit
945 mov cl,nextcluster_fat16-(nextcluster+2)
946 cmp eax,65525 ; FAT16 limit
949 ; FAT32, root directory is a cluster chain
952 mov eax,[bootsec+44] ; Root directory cluster
957 mov cl,nextcluster_fat28-(nextcluster+2)
959 mov byte [nextcluster+1],cl
962 ; Common initialization code
964 %include "cpuinit.inc"
967 ; Clear Files structures
970 mov cx,(MAX_OPEN*open_file_t_size)/4
975 ; Initialization that does not need to go into the any of the pre-load
978 ; Now set up screen parameters
981 ; Wipe the F-key area
984 mov cx,10*(1 << FILENAME_MAX_LG2)
988 ; Now, everything is "up and running"... patch kaboom for more
989 ; verbosity and using the full screen system
992 mov dword [kaboom.patch],0e9h+((kaboom2-(kaboom.patch+3)) << 8)
995 ; Now we're all set to start with our *real* business. First load the
996 ; configuration file (if any) and parse it.
998 ; In previous versions I avoided using 32-bit registers because of a
999 ; rumour some BIOSes clobbered the upper half of 32-bit registers at
1000 ; random. I figure, though, that if there are any of those still left
1001 ; they probably won't be trying to install Linux on them...
1003 ; The code is still ripe with 16-bitisms, though. Not worth the hassle
1004 ; to take'm out. In fact, we may want to put them back if we're going
1005 ; to boot ELKS at some point.
1007 mov si,linuxauto_cmd ; Default command: "linux auto"
1009 mov cx,linuxauto_len
1012 mov di,KbdMap ; Default keymap 1:1
1020 ; Load configuration file
1027 ; Now we have the config file open. Parse the config file and
1028 ; run the user interface.
1033 ; Linux kernel loading code is common.
1035 %include "runkernel.inc"
1038 ; COMBOOT-loading code
1040 %include "comboot.inc"
1041 %include "com32.inc"
1042 %include "cmdline.inc"
1045 ; Boot sector loading code
1047 %include "bootsect.inc"
1050 ; abort_check: let the user abort with <ESC> or <Ctrl-C>
1061 ac_kill: mov si,aborted_msg
1064 ; abort_load: Called by various routines which wants to print a fatal
1065 ; error message and return to the command prompt. Since this
1066 ; may happen at just about any stage of the boot process, assume
1067 ; our state is messed up, and just reset the segment registers
1068 ; and the stack forcibly.
1070 ; SI = offset (in _text) of error message to print
1073 mov ax,cs ; Restore CS = DS = ES
1077 mov sp,StackBuf-2*3 ; Reset stack
1078 mov ss,ax ; Just in case...
1080 call cwritestr ; Expects SI -> error msg
1081 al_ok: jmp enter_command ; Return to command prompt
1083 ; End of abort_check
1089 ; allocate_file: Allocate a file structure
1102 .check: cmp dword [bx], byte 0
1104 add bx,open_file_t_size ; ZF = 0
1106 ; ZF = 0 if we fell out of the loop
1112 ; Search the root directory for a pre-mangled filename in DS:DI.
1114 ; NOTE: This file considers finding a zero-length file an
1115 ; error. This is so we don't have to deal with that special
1116 ; case elsewhere in the program (most loops have the test
1122 ; DX:AX = file length in bytes
1136 mov edx,[RootDir] ; First root directory sector
1141 ; GS:SI now points to this sector
1143 mov cx,SECTOR_SIZE/32 ; 32 == directory entry size
1146 jz .failure ; Hit directory high water mark
1158 jnc .scansector ; CF is set if we're at end
1160 ; If we get here, we failed
1168 mov eax,[gs:si+28] ; File size
1169 add eax,SECTOR_SIZE-1
1170 shr eax,SECTOR_SHIFT
1171 jz .failure ; Zero-length file
1175 mov dx,[gs:si+20] ; High cluster word
1177 mov dx,[gs:si+26] ; Low cluster word
1180 mov [bx],edx ; Starting sector
1183 shr edx,16 ; 16-bitism, sigh
1185 and eax,eax ; ZF <- 0
1192 ; writechr: Write a single character in AL to the console without
1193 ; mangling any registers; handle video pages correctly.
1196 call write_serial ; write to serial port if needed
1200 mov bl,07h ; attribute
1201 mov bh,[cs:BIOS_page] ; current page
1209 ; kaboom2: once everything is loaded, replace the part of kaboom
1210 ; starting with "kaboom.patch" with this part
1213 mov si,err_bootfailed
1217 int 19h ; And try once more to boot...
1218 .norge: jmp short .norge ; If int 19h returned; this is the end
1221 ; mangle_name: Mangle a DOS filename pointed to by DS:SI into a buffer pointed
1222 ; to by ES:DI; ends on encountering any whitespace
1226 mov cx,11 ; # of bytes to write
1229 cmp al,' ' ; If control or space, end
1231 cmp al,'.' ; Period -> space-fill
1238 jmp short mn_not_lower
1239 mn_is_period: mov al,' ' ; We need to space-fill
1240 mn_period_loop: cmp cx,3 ; If <= 3 characters left
1241 jbe mn_loop ; Just ignore it
1242 stosb ; Otherwise, write a period
1243 loop mn_period_loop ; Dec CX and (always) jump
1244 mn_not_uslower: cmp al,ucase_low
1248 mov bx,ucase_tab-ucase_low
1251 loop mn_loop ; Don't continue if too long
1253 mov al,' ' ; Space-fill name
1254 rep stosb ; Doesn't do anything if CX=0
1258 ; Upper-case table for extended characters; this is technically code page 865,
1259 ; but code page 437 users will probably not miss not being able to use the
1260 ; cent sign in kernel images too much :-)
1262 ; The table only covers the range 129 to 164; the rest we can deal with.
1266 ucase_tab db 154, 144, 'A', 142, 'A', 143, 128, 'EEEIII'
1267 db 142, 143, 144, 146, 146, 'O', 153, 'OUUY', 153, 154
1268 db 157, 156, 157, 158, 159, 'AIOU', 165
1271 ; unmangle_name: Does the opposite of mangle_name; converts a DOS-mangled
1272 ; filename to the conventional representation. This is needed
1273 ; for the BOOT_IMAGE= parameter for the kernel.
1274 ; NOTE: A 13-byte buffer is mandatory, even if the string is
1275 ; known to be shorter.
1277 ; DS:SI -> input mangled file name
1278 ; ES:DI -> output buffer
1280 ; On return, DI points to the first byte after the output name,
1281 ; which is set to a null byte.
1284 push si ; Save pointer to original name
1292 mov bp,di ; Position of last nonblank+1
1293 un_cb_space: loop un_copy_body
1295 mov al,'.' ; Don't save
1304 un_ce_space: loop un_copy_ext
1311 ; lower_case: Lower case a character in AL
1320 lc_1: cmp al,lcase_low
1325 mov bx,lcase_tab-lcase_low
1331 ; getfssec_edx: Get multiple sectors from a file
1333 ; This routine makes sure the subtransfers do not cross a 64K boundary,
1334 ; and will correct the situation if it does, UNLESS *sectors* cross
1338 ; EDX -> Current sector number
1339 ; CX -> Sector count (0FFFFh = until end of file)
1340 ; Must not exceed the ES segment
1341 ; Returns EDX=0, CF=1 on EOF (not necessarily error)
1342 ; All arguments are advanced to reflect data read.
1348 xor ebp,ebp ; Fragment sector count
1355 add ax,bx ; Now DI = how far into 64K block we are
1356 neg ax ; Bytes left in 64K block
1357 shr ax,9 ; Sectors left in 64K block
1359 jnb .do_read ; Unless there is at least 1 more sector room...
1360 lea eax,[edx+1] ; Linearly next sector
1368 lea eax,[eax+ebp-1] ; This is the last sector actually read
1370 add bx,bp ; Adjust buffer pointer
1386 ; getfssec: Get multiple sectors from a file
1388 ; Same as above, except SI is a pointer to a open_file_t
1391 ; DS:SI -> Pointer to open_file_t
1392 ; CX -> Sector count (0FFFFh = until end of file)
1393 ; Must not exceed the ES segment
1394 ; Returns CF=1 on EOF (not necessarily error)
1395 ; All arguments are advanced to reflect data read.
1413 ; nextcluster: Advance a cluster pointer in EDI to the next cluster
1414 ; pointed at in the FAT tables. CF=0 on return if end of file.
1417 jmp strict short nextcluster_fat28 ; This gets patched
1455 ; FAT16 decoding routine.
1462 shr eax,SECTOR_SHIFT-1
1467 movzx edi,word [gs:si+bx]
1474 ; FAT28 ("FAT32") decoding routine.
1481 shr eax,SECTOR_SHIFT-2
1487 mov edi,dword [gs:si+bx]
1488 and edi,0FFFFFFFh ; 28 bits only
1496 ; nextsector: Given a sector in EAX on input, return the next sector
1497 ; of the same filesystem object, which may be the root
1498 ; directory or a cluster chain. Returns EOF.
1518 test edi,[ClustMask]
1521 ; It's not the final sector in a cluster
1526 push gs ; nextcluster trashes gs
1533 ; Now EDI contains the cluster number
1536 jc .exit ; There isn't anything else...
1538 ; New cluster number now in EDI
1540 shl edi,cl ; CF <- 0, unless something is very wrong
1551 ; getfatsector: Check for a particular sector (in EAX) in the FAT cache,
1552 ; and return a pointer in GS:SI, loading it if needed.
1557 add eax,[FAT] ; FAT starting address
1561 ; getcachesector: Check for a particular sector (EAX) in the sector cache,
1562 ; and if it is already there, return a pointer in GS:SI
1563 ; otherwise load it and return said pointer.
1571 mov si,CachePtrs ; Sector cache pointers
1572 mov cx,65536/SECTOR_SIZE
1580 ; Need to load it. Highly inefficient cache replacement
1581 ; algorithm: Least Recently Written (LRW)
1586 mov bx,[NextCacheSlot]
1588 and bx,(1 << (16-SECTOR_SHIFT))-1
1589 mov [NextCacheSlot],bx
1591 mov [CachePtrs+bx],eax
1592 shl bx,SECTOR_SHIFT-2
1602 .hit: ; We have it; get the pointer
1604 shl si,SECTOR_SHIFT-2
1608 ; -----------------------------------------------------------------------------
1610 ; -----------------------------------------------------------------------------
1612 %include "getc.inc" ; getc et al
1613 %include "conio.inc" ; Console I/O
1614 %include "writestr.inc" ; String output
1615 %include "parseconfig.inc" ; High-level config file handling
1616 %include "parsecmd.inc" ; Low-level config file handling
1617 %include "bcopy32.inc" ; 32-bit bcopy
1618 %include "loadhigh.inc" ; Load a file into high memory
1619 %include "font.inc" ; VGA font stuff
1620 %include "graphics.inc" ; VGA graphics
1621 %include "highmem.inc" ; High memory sizing
1622 %include "strcpy.inc" ; strcpy()
1624 ; -----------------------------------------------------------------------------
1625 ; Begin data section
1626 ; -----------------------------------------------------------------------------
1629 ; Lower-case table for codepage 865
1633 lcase_tab db 135, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138
1634 db 139, 140, 141, 132, 134, 130, 145, 145, 147, 148, 149
1635 db 150, 151, 152, 148, 129, 155, 156, 155, 158, 159, 160
1636 db 161, 162, 163, 164, 164
1638 copyright_str db ' Copyright (C) 1994-', year, ' H. Peter Anvin'
1640 boot_prompt db 'boot: ', 0
1641 wipe_char db BS, ' ', BS, 0
1642 err_notfound db 'Could not find kernel image: ',0
1643 err_notkernel db CR, LF, 'Invalid or corrupt kernel image.', CR, LF, 0
1644 err_noram db 'It appears your computer has less than '
1646 db 'K of low ("DOS")'
1648 db 'RAM. Linux needs at least this amount to boot. If you get'
1650 db 'this message in error, hold down the Ctrl key while'
1652 db 'booting, and I will take your word for it.', CR, LF, 0
1653 err_badcfg db 'Unknown keyword in syslinux.cfg.', CR, LF, 0
1654 err_noparm db 'Missing parameter in syslinux.cfg.', CR, LF, 0
1655 err_noinitrd db CR, LF, 'Could not find ramdisk image: ', 0
1656 err_nohighmem db 'Not enough memory to load specified kernel.', CR, LF, 0
1657 err_highload db CR, LF, 'Kernel transfer failure.', CR, LF, 0
1658 err_oldkernel db 'Cannot load a ramdisk with an old kernel image.'
1660 err_notdos db ': attempted DOS system call', CR, LF, 0
1661 err_comlarge db 'COMBOOT image too large.', CR, LF, 0
1662 err_a20 db CR, LF, 'A20 gate not responding!', CR, LF, 0
1663 err_bootfailed db CR, LF, 'Boot failed: please change disks and press '
1664 db 'a key to continue.', CR, LF, 0
1665 ready_msg db 'Ready.', CR, LF, 0
1666 crlfloading_msg db CR, LF
1667 loading_msg db 'Loading ', 0
1670 aborted_msg db ' aborted.' ; Fall through to crlf_msg!
1673 crff_msg db CR, FF, 0
1674 syslinux_cfg db 'SYSLINUXCFG' ; Mangled form
1675 ConfigName db 'syslinux.cfg',0 ; Unmangled form
1677 manifest db 'MANIFEST '
1680 ; Command line options we'd like to take a look at
1682 ; mem= and vga= are handled as normal 32-bit integer values
1683 initrd_cmd db 'initrd='
1684 initrd_cmd_len equ 7
1687 ; Config file keyword table
1689 %include "keywords.inc"
1692 ; Extensions to search for (in *forward* order).
1694 exten_table: db 'CBT',0 ; COMBOOT (specific)
1695 db 'BSS',0 ; Boot Sector (add superblock)
1696 db 'BS ',0 ; Boot Sector
1697 db 'COM',0 ; COMBOOT (same as DOS)
1700 dd 0, 0 ; Need 8 null bytes here
1703 ; Misc initialized (data) variables
1705 %ifdef debug ; This code for debugging only
1706 debug_magic dw 0D00Dh ; Debug code sentinel
1708 AppendLen dw 0 ; Bytes in append= command
1709 OntimeoutLen dw 0 ; Bytes in ontimeout command
1710 OnerrorLen dw 0 ; Bytes in onerror command
1711 KbdTimeOut dw 0 ; Keyboard timeout (if any)
1712 CmdLinePtr dw cmd_line_here ; Command line advancing pointer
1714 initrd_ptr dw 0 ; Initial ramdisk pointer/flag
1715 VKernelCtr dw 0 ; Number of registered vkernels
1716 ForcePrompt dw 0 ; Force prompt
1717 AllowImplicit dw 1 ; Allow implicit kernels
1718 AllowOptions dw 1 ; User-specified options allowed
1719 SerialPort dw 0 ; Serial port base (or 0 for no serial port)
1720 VGAFontSize dw 16 ; Defaults to 16 byte font
1721 UserFont db 0 ; Using a user-specified font
1722 ScrollAttribute db 07h ; White on black (for text mode)
1725 BufSafe dw trackbufsize/SECTOR_SIZE ; Clusters we can load into trackbuf
1726 BufSafeSec dw trackbufsize/SECTOR_SIZE ; = how many sectors?
1727 BufSafeBytes dw trackbufsize ; = how many bytes?
1728 EndOfGetCBuf dw getcbuf+trackbufsize ; = getcbuf+BufSafeBytes
1730 %if ( trackbufsize % SECTOR_SIZE ) != 0
1731 %error trackbufsize must be a multiple of SECTOR_SIZE
1735 ; Stuff for the command line; we do some trickery here with equ to avoid
1736 ; tons of zeros appended to our file and wasting space
1738 linuxauto_cmd db 'linux auto',0
1739 linuxauto_len equ $-linuxauto_cmd
1740 boot_image db 'BOOT_IMAGE='
1741 boot_image_len equ $-boot_image
1743 align 4, db 0 ; Pad out any unfinished dword
1745 ldlinux_len equ $-ldlinux_magic
1747 ; VGA font buffer at the end of memory (so loading a font works even
1748 ; in graphics mode.)
1749 vgafontbuf equ 0E000h
1751 ; This is a compile-time assert that we didn't run out of space
1753 %if (ldlinux_end-bootsec+7C00h) > vgafontbuf
1754 %error "Out of memory, better reorganize something..."