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-2002 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., 675 Mass Ave, Cambridge MA 02139,
24 ; USA; either version 2 of the License, or (at your option) any later
25 ; version; incorporated herein by reference.
27 ; ****************************************************************************
34 %include "tracers.inc"
37 ; Some semi-configurable constants... change on your own risk.
40 FILENAME_MAX_LG2 equ 4 ; log2(Max filename size Including final null)
41 FILENAME_MAX equ 11 ; Max mangled filename size
42 NULLFILE equ ' ' ; First char space == null filename
43 retry_count equ 6 ; How patient are we with the disk?
44 %assign HIGHMEM_SLOP 0 ; Avoid this much memory near the top
47 ; The following structure is used for "virtual kernels"; i.e. LILO-style
48 ; option labels. The options we permit here are `kernel' and `append
49 ; Since there is no room in the bottom 64K for all of these, we
50 ; stick them at vk_seg:0000 and copy them down before we need them.
52 ; Note: this structure can be added to, but it must
54 %define vk_power 7 ; log2(max number of vkernels)
55 %define max_vk (1 << vk_power) ; Maximum number of vkernels
56 %define vk_shift (16-vk_power) ; Number of bits to shift
57 %define vk_size (1 << vk_shift) ; Size of a vkernel buffer
60 vk_vname: resb FILENAME_MAX ; Virtual name **MUST BE FIRST!**
61 vk_rname: resb FILENAME_MAX ; Real name
64 vk_append: resb max_cmd_len+1 ; Command line
66 vk_end: equ $ ; Should be <= vk_size
70 %if (vk_end > vk_size) || (vk_size*max_vk > 65536)
71 %error "Too many vkernels defined, reduce vk_power"
76 ; Segment assignments in the bottom 640K
77 ; Stick to the low 512K in case we're using something like M-systems flash
78 ; which load a driver into low RAM (evil!!)
80 ; 0000h - main code/data segment (and BIOS segment)
82 real_mode_seg equ 7000h
83 fat_seg equ 5000h ; 128K area for FAT (2x64K)
84 vk_seg equ 4000h ; Virtual kernels
85 xfer_buf_seg equ 3000h ; Bounce buffer for I/O to high mem
86 comboot_seg equ 2000h ; COMBOOT image loading zone
88 ; ---------------------------------------------------------------------------
90 ; ---------------------------------------------------------------------------
93 ; Memory below this point is reserved for the BIOS and the MBR
96 trackbuf equ $ ; Track buffer goes here
97 trackbufsize equ 16384 ; Safe size of track buffer
98 ; trackbuf ends at 5000h
102 ; Constants for the xfer_buf_seg
104 ; The xfer_buf_seg is also used to store message file buffers. We
105 ; need two trackbuffers (text and graphics), plus a work buffer
106 ; for the graphics decompressor.
108 xbs_textbuf equ 0 ; Also hard-coded, do not change
109 xbs_vgabuf equ trackbufsize
110 xbs_vgatmpbuf equ 2*trackbufsize
113 absolute 5000h ; Here we keep our BSS stuff
114 VKernelBuf: resb vk_size ; "Current" vkernel
116 AppendBuf resb max_cmd_len+1 ; append=
117 KbdMap resb 256 ; Keyboard map
118 FKeyName resb 10*16 ; File names for F-key help
119 NumBuf resb 15 ; Buffer to load number
120 NumBufEnd resb 1 ; Last byte in NumBuf
123 ; Expanded superblock
125 resq 16 ; The first 16 bytes expanded 8 times
127 ; These need to follow SuperInfo
129 RootDir resd 1 ; Location of root directory
130 DataArea resd 1 ; Location of data area
131 RootDirSize resw 1 ; Root dir size in sectors
132 DirScanCtr resw 1 ; Used while searching directory
133 EndofDirSec resw 1 ; = trackbuf+bsBytesPerSec-31
136 E820Buf resd 5 ; INT 15:E820 data buffer
137 HiLoadAddr resd 1 ; Address pointer for high load loop
138 HighMemSize resd 1 ; End of memory pointer (bytes)
139 RamdiskMax resd 1 ; Highest address for a ramdisk
140 KernelSize resd 1 ; Size of kernel (bytes)
141 SavedSSSP resd 1 ; Our SS:SP while running a COMBOOT image
142 PMESP resd 1 ; Protected-mode ESP
143 ClustPerMoby resd 1 ; Clusters per 64K
144 ClustSize resd 1 ; Bytes/cluster
145 KernelName resb 12 ; Mangled name for kernel
146 ; (note the spare byte after!)
147 OrigKernelExt resd 1 ; Original kernel extension
148 FBytes equ $ ; Used by open/getc
151 DirBlocksLeft resw 1 ; Ditto
152 RunLinClust resw 1 ; Cluster # for LDLINUX.SYS
153 BufSafe resw 1 ; Clusters we can load into trackbuf
154 BufSafeSec resw 1 ; = how many sectors?
155 BufSafeBytes resw 1 ; = how many bytes?
156 EndOfGetCBuf resw 1 ; = getcbuf+BufSafeBytes
157 KernelClust resw 1 ; Kernel size in clusters
158 FClust resw 1 ; Number of clusters in open/getc file
159 FNextClust resw 1 ; Pointer to next cluster in d:o
160 FPtr resw 1 ; Pointer to next char in buffer
161 CmdOptPtr resw 1 ; Pointer to first option on cmd line
162 KernelCNameLen resw 1 ; Length of unmangled kernel name
163 InitRDCNameLen resw 1 ; Length of unmangled initrd name
164 NextCharJump resw 1 ; Routine to interpret next print char
165 SetupSecs resw 1 ; Number of setup sectors
166 A20Test resw 1 ; Counter for testing status of A20
167 A20Type resw 1 ; A20 type
168 CmdLineLen resw 1 ; Length of command line including null
169 GraphXSize resw 1 ; Width of splash screen file
170 VGAPos resw 1 ; Pointer into VGA memory
171 VGACluster resw 1 ; Cluster pointer for VGA image file
172 VGAFilePtr resw 1 ; Pointer into VGAFileBuf
174 TextAttribute resb 1 ; Text attribute for message file
175 TextPage resb 1 ; Active display page
177 CursorCol resb 1 ; Cursor column for message file
178 CursorRow resb 1 ; Cursor row for message file
180 VidCols resb 1 ; Columns on screen-1
181 VidRows resb 1 ; Rows on screen-1
182 BaudDivisor resw 1 ; Baud rate divisor
184 FlowOutput resb 1 ; Outputs to assert for serial flow
185 FlowInput resb 1 ; Input bits for serial flow
186 FlowIgnore resb 1 ; Ignore input unless these bits set
187 RetryCount resb 1 ; Used for disk access retries
188 KbdFlags resb 1 ; Check for keyboard escapes
189 LoadFlags resb 1 ; Loadflags from kernel
190 A20Tries resb 1 ; Times until giving up on A20
191 FuncFlag resb 1 ; Escape sequences received from keyboard
192 DisplayMask resb 1 ; Display modes mask
193 MNameBuf resb 11 ; Generic mangled file name buffer
194 InitRD resb 11 ; initrd= mangled name
195 KernelCName resb 13 ; Unmangled kernel name
196 InitRDCName resb 13 ; Unmangled initrd name
197 TextColorReg resb 17 ; VGA color registers for text mode
198 VGAFileBuf resb 13 ; Unmangled VGA image name
200 VGAFileMBuf resb 11 ; Mangled VGA image name
205 ; Some of the things that have to be saved very early are saved
206 ; "close" to the initial stack pointer offset, in order to
207 ; reduce the code size...
209 StackBuf equ $-32 ; Start the stack here (grow down - 4K)
210 PartInfo equ StackBuf ; Saved partition table entry
211 FloppyTable equ PartInfo+16 ; Floppy info table (must follow PartInfo)
212 OrigFDCTabPtr equ StackBuf-4 ; The high dword on the stack
215 ; Primary entry point. Tempting as though it may be, we can't put the
216 ; initial "cli" here; the jmp opcode in the first byte is part of the
217 ; "magic number" (using the term very loosely) for the DOS superblock.
220 jmp short start ; 2 bytes
223 ; "Superblock" follows -- it's in the boot sector, so it's already
224 ; loaded and ready for us
226 bsOemName db 'SYSLINUX' ; The SYS command sets this, so...
228 ; These are the fields we actually care about. We end up expanding them
229 ; all to dword size early in the code, so generate labels for both
230 ; the expanded and unexpanded versions.
233 bx %+ %1 equ SuperInfo+($-superblock)*8+4
238 bx %+ %1 equ SuperInfo+($-superblock)*8
243 bx %+ %1 equ $ ; no expansion for dwords
258 superinfo_size equ ($-superblock)-1 ; How much to expand
263 superb BootSignature ; 29h if the following fields exist
266 bsFileSysType zb 8 ; Must be FAT12 or FAT16 for this version
267 superblock_len equ $-superblock
269 SecPerClust equ bxSecPerClust
271 ; Note we don't check the constraints above now; we did that at install
275 ;floppy_table equ $ ; No sense in wasting memory, overwrite start
278 cli ; No interrupts yet, please
285 mov sp,StackBuf ; Just below BSS
288 ; DS:SI may contain a partition table entry. Preserve it for us.
290 mov cx,8 ; Save partition info
294 mov ds,ax ; Now we can initialize DS...
296 mov [di+bsDriveNumber-FloppyTable],dl
298 ; Now sautee the BIOS floppy info block to that it will support decent-
299 ; size transfers; the floppy block is 11 bytes and is stored in the
300 ; INT 1Eh vector (brilliant waste of resources, eh?)
302 ; Of course, if BIOSes had been properly programmed, we wouldn't have
303 ; had to waste precious space with this code.
306 lfs si,[bx] ; FS:SI -> original fdctab
307 push fs ; Save on stack in case we need to bail
310 ; Save the old fdctab even if hard disk so the stack layout
311 ; is the same. The instructions above do not change the flags
312 and dl,dl ; If floppy disk (00-7F), assume no
317 mov cl,6 ; 12 bytes (CX == 0)
318 ; es:di -> FloppyTable already
319 ; This should be safe to do now, interrupts are off...
320 mov [bx],di ; FloppyTable
321 mov [bx+2],ax ; Segment 0
322 fs rep movsw ; Faster to move words
323 mov cl,[bsSecPerTrack] ; Patch the sector count
326 int 13h ; Some BIOSes need this
328 jmp short not_harddisk
330 ; The drive number and possibly partition information was passed to us
331 ; by the BIOS or previous boot loader (MBR). Current "best practice" is to
332 ; trust that rather than what the superblock contains.
334 ; Would it be better to zero out bsHidden if we don't have a partition table?
336 ; Note: di points to beyond the end of PartInfo
339 test byte [di-16],7Fh ; Sanity check: "active flag" should
340 jnz no_partition ; be 00 or 80
341 mov eax,[di-8] ; Partition offset (dword)
345 ; Get disk drive parameters (don't trust the superblock.) Don't do this for
346 ; floppy drives -- INT 13:08 on floppy drives will (may?) return info about
347 ; what the *drive* supports, not about the *media*. Fortunately floppy disks
348 ; tend to have a fixed, well-defined geometry which is stored in the superblock.
350 ; DL == drive # still
357 inc dx ; Contains # of heads - 1
360 mov [bsSecPerTrack],cx
364 ; Ready to enable interrupts, captain
368 ; Insane hack to expand the superblock to dwords
372 mov es,ax ; INT 13:08 destroys ES
375 mov cl,superinfo_size ; CH == 0
379 stosd ; Store expanded word
381 stosd ; Store expanded byte
385 ; Now we have to do some arithmetric to figure out where things are located.
386 ; If Micro$oft had had brains they would already have done this for us,
387 ; and stored it in the superblock at format time, but here we go,
388 ; wasting precious boot sector space again...
390 %define Z di-superinfo_size*8-SuperInfo
392 mov ax,[bxFATs] ; Number of FATs (eax<31:16> == 0)
393 mov edx,[Z+bxFATsecs] ; Sectors/FAT
394 mul edx ; Get the size of the FAT area
396 add eax,[bxHidden] ; Add hidden sectors
397 add eax,[Z+bxResSectors] ; And reserved sectors
399 mov [RootDir],eax ; Location of root directory
400 mov [DataArea],eax ; First data sector
403 mov eax,[Z+bxRootDirEnts]
404 shl ax,5 ; Size of a directory entry
405 mov bx,[Z+bxBytesPerSec]
406 add ax,bx ; Round up, not down
408 div bx ; Now we have the size of the root dir
412 mov [Z+EndofDirSec],bx ; End of a single directory sector
414 pop eax ; Reload root directory starting point
417 ; Now the fun begins. We have to search the root directory for
418 ; LDLINUX.SYS and load the first sector, so we have a little more
419 ; space to have fun with. Then we can go chasing through the FAT.
427 sd_nextentry: mov cx,11
428 cmp [si],ch ; Directory high water mark
430 ; This no longer fits... since we'd be dead anyway if there
431 ; was a nonfile named LDLINUX.SYS on the disk, it shouldn't
433 ; test byte [si+11],18h ; Must be a file
440 sd_not_file: add si,byte 32 ; Distance to next
445 dec word [DirScanCtr]
448 ; kaboom: write a message and bail out.
453 mov sp,StackBuf-4 ; Reset stack
454 mov ds,si ; Reset data segment
455 pop dword [fdctab] ; Restore FDC table
456 .patch: mov si,bailmsg
457 call writestr ; Returns with AL = 0
459 int 16h ; Wait for keypress
460 int 19h ; And try once more to boot...
461 .norge: jmp short .norge ; If int 19h returned; this is the end
464 ; found_it: now we compute the location of the first sector, then
465 ; load it and JUMP (since we're almost out of space)
467 found_it: ; Note: we actually leave two words on the stack here
469 mov eax,[bxSecPerClust]
470 mov bp,ax ; Load an entire cluster
471 movzx ebx,word [si+26] ; First cluster
472 mov [RunLinClust],bx ; Save for later use
473 dec bx ; First cluster is "cluster 2"
482 repe cmpsb ; Make sure that the bootsector
483 jne kaboom ; matches LDLINUX.SYS
485 ; Done! Jump to the entry point!
490 ; writestr: write a null-terminated string to the console
496 mov ah,0Eh ; Write to screen as TTY
497 mov bx,0007h ; White on black, current page
503 ; disk_error: decrement the retry count and bail if zero.
504 ; This gets patched once we have more space to try to
505 ; optimize transfer sizes on broken machines.
507 disk_error: dec si ; SI holds the disk retry counter
509 ; End of patched "call" instruction!
510 jmp short disk_try_again
513 ; getonesec: like getlinsec, but pre-sets the count to 1
517 ; Fall through to getlinsec
520 ; getlinsec: load a sequence of BP floppy sector given by the linear sector
521 ; number in EAX into the buffer at ES:BX. We try to optimize
522 ; by loading up to a whole track at a time, but the user
523 ; is responsible for not crossing a 64K boundary.
524 ; (Yes, BP is weird for a count, but it was available...)
526 ; On return, BX points to the first byte after the transferred
529 ; The "stupid patch area" gets replaced by the code
530 ; mov bp,1 ; nop ... (BD 01 00 90 90...) when installing with
533 ; This routine assumes CS == DS.
535 ; Stylistic note: use "xchg" instead of "mov" when the source is a register
536 ; that is dead from that point; this saves space. However, please keep
537 ; the order to dst,src to keep things sane.
540 mov esi,[bxSecPerTrack]
542 ; Dividing by sectors to get (track,sector): we may have
543 ; up to 2^18 tracks, so we need to use 32-bit arithmetric.
545 xor edx,edx ; Zero-extend LBA to 64 bits
548 xchg cx,dx ; CX <- sector index (0-based)
551 div dword [bxHeads] ; Convert track to head/cyl
553 ; Now we have AX = cyl, DX = head, CX = sector (0-based),
554 ; BP = sectors to transfer, SI = bsSecPerTrack,
555 ; ES:BX = data target
557 gls_nextchunk: push si ; <A> bsSecPerTrack
558 push bp ; <B> Sectors to transfer
560 ; Important - this gets patched with a call. The call
561 ; assumes cx, si and bp are set up, and can modify bp
562 ; and destroy si. Until we have the space to do so,
563 ; transfer one sector at a time.
565 __BEGIN_STUPID_PATCH_AREA:
566 mov bp,1 ; 3 bytes, same as a call insn
567 __END_STUPID_PATCH_AREA:
569 push ax ; <C> Cylinder #
572 push cx ; <E> Sector #
573 shl ah,6 ; Because IBM was STOOPID
574 ; and thought 8 bits were enough
575 ; then thought 10 bits were enough...
576 pop cx ; <E> Sector #
577 push cx ; <E> Sector #
578 inc cx ; Sector numbers are 1-based, sigh
582 mov dl,[bsDriveNumber]
583 xchg ax,bp ; Sector to transfer count
584 ; (xchg shorter than mov)
585 mov si,retry_count ; # of times to retry a disk access
587 ; Do the disk transfer... save the registers in case we fail :(
591 mov ah,02h ; READ DISK
596 ; Disk access successful
598 pop cx ; <E> Sector #
599 mov di,ax ; Reduce sector left count
600 mul word [bsBytesPerSec] ; Figure out how much to advance ptr
601 add bx,ax ; Update buffer location
604 pop bp ; <B> Sectors left to transfer
605 pop si ; <A> Number of sectors/track
606 sub bp,di ; Reduce with # of sectors just read
607 jz writestr.return ; Done!
611 inc dx ; Next track on cyl
612 cmp dx,[bsHeads] ; Was this the last one?
614 inc ax ; If so, new cylinder
615 xor dx,dx ; First head on new cylinder
616 gls_nonewcyl: sub cx,si ; First sector on new track
617 jmp short gls_nextchunk
619 bailmsg: db 'Boot failed', 0Dh, 0Ah, 0
621 bs_checkpt equ $ ; Must be <= 7DEFh
624 bs_checkpt_off equ ($-$$)
626 %if bs_checkpt_off > 1EFh
627 %error "Boot sector overflow"
633 bs_magic equ $ ; From here to the magic_len equ
634 ; must match ldlinux_magic
635 ldlinux_name: db 'LDLINUX SYS' ; Looks like this in the root dir
636 dd HEXDATE ; Hopefully unique between compiles
638 bootsignature dw 0AA55h
639 magic_len equ $-bs_magic
642 ; ===========================================================================
644 ; ===========================================================================
645 ; Start of LDLINUX.SYS
646 ; ===========================================================================
650 syslinux_banner db 0Dh, 0Ah, 'SYSLINUX ', version_str, ' ', date, ' ', 0
651 db 0Dh, 0Ah, 1Ah ; EOF if we "type" this in DOS
653 ldlinux_magic db 'LDLINUX SYS'
658 ; This area is possibly patched by the installer. It is located
659 ; immediately after the EOF + LDLINUX SYS + 4 bytes + 55 AA + alignment,
660 ; so we can find it algorithmically.
663 MaxTransfer dw 00FFh ; Absolutely maximum transfer size
668 ; Note that some BIOSes are buggy and run the boot sector at 07C0:0000
669 ; instead of 0000:7C00 and the like. We don't want to add anything
670 ; more to the boot sector, so it is written to not assume a fixed
671 ; value in CS, but we don't want to deal with that anymore from now
678 ; Tell the user we got this far
680 mov si,syslinux_banner
683 ; Remember, the boot sector loaded only the first cluster of LDLINUX.SYS.
684 ; We can really only rely on a single sector having been loaded. Hence
685 ; we should load the FAT into RAM and start chasing pointers...
689 inc dx ; DX:AX <- 64K
690 div word [bxBytesPerSec] ; sectors/64K
694 mov bx,fat_seg ; Load into fat_seg:0000
697 mov eax,[bsHidden] ; Hidden sectors
698 add edx,[bxResSectors]
700 mov ecx,[bxFATsecs] ; Sectors/FAT
702 mov ebp,ecx ; Make sure high EBP = 0
705 mov bp,si ; A full 64K moby
707 xor bx,bx ; Offset 0 in the current ES
710 jz fat_load_done ; Last moby?
711 add eax,ebp ; Advance sector count
712 mov bx,es ; Next 64K moby
715 jmp short fat_load_loop
719 ; Fine, now we have the FAT in memory. How big is a cluster, really?
720 ; Also figure out how many clusters will fit in an 8K buffer, and how
721 ; many sectors and bytes that is
723 mov edi,[bxBytesPerSec] ; Used a lot below
724 mov eax,[SecPerClust]
725 mov si,ax ; Also used a lot
727 mov [ClustSize],eax ; Bytes/cluster
729 mov ax,trackbufsize ; High bit 0
732 mov [BufSafe],ax ; # of cluster in trackbuf
736 mov [BufSafeBytes],ax
737 add ax,getcbuf ; Size of getcbuf is the same
738 mov [EndOfGetCBuf],ax ; as for trackbuf
740 ; FAT12 or FAT16? This computation is fscking ridiculous...
745 mov eax,[bsHugeSectors]
746 have_secs: add eax,[bsHidden] ; These are not included
747 sub eax,[RootDir] ; Start of root directory
748 movzx ebx,word [RootDirSize]
749 sub eax,ebx ; Subtract root directory size
751 div esi ; Convert to clusters
752 cmp ax,4086 ; FAT12 limit
755 mov byte [nextcluster+1],nextcluster_fat16-(nextcluster+2)
759 ; Patch gls_set_size so we can transfer more than one sector at a time.
761 mov byte [gls_set_size],0xe8 ; E8 = CALL NEAR
762 mov word [gls_set_size+1],do_gls_set_size-(gls_set_size+3)
763 mov byte [disk_error],0xe8
764 mov word [disk_error+1],do_disk_error-(disk_error+3)
767 ; Now we read the rest of LDLINUX.SYS. Don't bother loading the first
768 ; cluster again, though.
777 mov ax,ldlinux_len-1 ; To be on the safe side
779 div cx ; the number of clusters
780 dec ax ; We've already read one
790 ; -----------------------------------------------------------------------------
791 ; Subroutines that have to be in the first sector
792 ; -----------------------------------------------------------------------------
794 ; getfssec: Get multiple clusters from a file, given the starting cluster.
796 ; This routine makes sure the subtransfers do not cross a 64K boundary,
797 ; and will correct the situation if it does, UNLESS *sectors* cross
801 ; SI -> Starting cluster number (2-based)
802 ; CX -> Cluster count (0FFFFh = until end of file)
804 ; Returns CF=1 on EOF
807 getfragment: xor ebp,ebp ; Fragment sector count
808 movzx eax,si ; Get sector address
809 dec ax ; Convert to 0-based
811 mul dword [SecPerClust]
813 getseccnt: ; See if we can read > 1 clust
815 dec cx ; Reduce clusters left to find
820 jcxz endfragment ; Or was it the last we wanted?
821 cmp si,di ; Is file continuous?
822 jz getseccnt ; Yes, we can get
823 endfragment: clc ; Not at EOF
824 gfs_eof: pushf ; Remember EOF or not
829 mov ax,es ; Check for 64K boundaries.
834 setz dl ; DX <- 1 if full 64K segment
835 div word [bsBytesPerSec] ; How many sectors fit?
837 sub si,ax ; Compute remaining sectors
842 add eax,ebp ; EBP<31:16> == 0
843 mov bp,si ; Remaining sector count
844 jmp short gfs_getchunk
845 gfs_lastchunk: pop eax
850 jcxz gfs_return ; If we hit the count limit
851 jnc getfragment ; If we didn't hit EOF
855 ; getlinsecsr: save registers, call getlinsec, restore registers
863 ; nextcluster: Advance a cluster pointer in SI to the next cluster
864 ; pointed at in the FAT tables. CF=0 on return if end of file.
867 jmp short nextcluster_fat12 ; This gets patched
874 mov bx,si ; Multiply by 3/2
875 shr bx,1 ; CF now set if odd
878 shr si,4 ; Needed for odd only
881 cmp si,0FF0h ; Clears CF if at end of file
887 ; FAT16 decoding routine. Note that a 16-bit FAT can be up to 128K,
888 ; so we have to decide if we're in the "low" or the "high" 64K-segment...
905 ; Routine that controls how much we can transfer in one chunk. Called
906 ; from gls_set_size in getlinsec.
909 sub si,cx ; Sectors left on track
912 mov bp,si ; No more than a trackful, please!
914 cmp bp,[MaxTransfer] ; Absolute maximum transfer size
921 ; This routine captures disk errors, and tries to decide if it is
922 ; time to reduce the transfer size.
925 dec si ; Decrement the retry counter
926 jz kaboom ; If expired, croak
927 cmp si,2 ; If only 2 attempts left
929 mov al,1 ; Drop transfer size to 1
933 ja .again ; First time, just try again
934 shr al,1 ; Otherwise, try to reduce
935 adc al,0 ; the max transfer size, but not to 0
946 cmp word [Debug_Magic],0D00Dh
951 rl_checkpt equ $ ; Must be <= 8000h
953 rl_checkpt_off equ ($-$$)
955 %if rl_checkpt_off > 400h
956 %error "Sector 1 overflow"
960 ; ----------------------------------------------------------------------------
961 ; End of code and data that have to be in the first sector
962 ; ----------------------------------------------------------------------------
966 ; Let the user (and programmer!) know we got this far. This used to be
967 ; in Sector 1, but makes a lot more sense here.
973 ; Common initialization code
975 %include "cpuinit.inc"
978 ; Initialization that does not need to go into the any of the pre-load
981 ; Now set up screen parameters
984 ; Wipe the F-key area
987 mov cx,10*(1 << FILENAME_MAX_LG2)
991 ; Now, everything is "up and running"... patch kaboom for more
992 ; verbosity and using the full screen system
995 mov dword [kaboom.patch],0e9h+((kaboom2-(kaboom.patch+3)) << 8)
998 ; Compute some parameters that depend on cluster size
1002 inc dx ; DX:AX <- 64K
1003 div word [ClustSize]
1004 mov [ClustPerMoby],eax ; Clusters/64K
1007 ; Now we're all set to start with our *real* business. First load the
1008 ; configuration file (if any) and parse it.
1010 ; In previous versions I avoided using 32-bit registers because of a
1011 ; rumour some BIOSes clobbered the upper half of 32-bit registers at
1012 ; random. I figure, though, that if there are any of those still left
1013 ; they probably won't be trying to install Linux on them...
1015 ; The code is still ripe with 16-bitisms, though. Not worth the hassle
1016 ; to take'm out. In fact, we may want to put them back if we're going
1017 ; to boot ELKS at some point.
1019 mov si,linuxauto_cmd ; Default command: "linux auto"
1021 mov cx,linuxauto_len
1024 mov di,KbdMap ; Default keymap 1:1
1032 ; Load configuration file
1039 ; Now we have the config file open. Parse the config file and
1040 ; run the user interface.
1045 ; Linux kernel loading code is common.
1047 %include "runkernel.inc"
1050 ; COMBOOT-loading code
1052 %include "comboot.inc"
1053 %include "com32.inc"
1056 ; Boot sector loading code
1058 %include "bootsect.inc"
1061 ; abort_check: let the user abort with <ESC> or <Ctrl-C>
1072 ac_kill: mov si,aborted_msg
1075 ; abort_load: Called by various routines which wants to print a fatal
1076 ; error message and return to the command prompt. Since this
1077 ; may happen at just about any stage of the boot process, assume
1078 ; our state is messed up, and just reset the segment registers
1079 ; and the stack forcibly.
1081 ; SI = offset (in _text) of error message to print
1084 mov ax,cs ; Restore CS = DS = ES
1088 mov sp,StackBuf-2*3 ; Reset stack
1089 mov ss,ax ; Just in case...
1091 call cwritestr ; Expects SI -> error msg
1092 al_ok: jmp enter_command ; Return to command prompt
1094 ; End of abort_check
1100 ; searchdir: Search the root directory for a pre-mangled filename in
1101 ; DS:DI. This routine is similar to the one in the boot
1102 ; sector, but is a little less Draconian when it comes to
1103 ; error handling, plus it reads the root directory in
1104 ; larger chunks than a sector at a time (which is probably
1105 ; a waste of coding effort, but I like to do things right).
1107 ; FIXME: usually we can load the entire root dir in memory,
1108 ; and files are usually at the beginning anyway. It probably
1109 ; would be worthwhile to remember if we have the first chunk
1110 ; in memory and skip the load if that (it would speed up online
1113 ; NOTE: This file considers finding a zero-length file an
1114 ; error. This is so we don't have to deal with that special
1115 ; case elsewhere in the program (most loops have the test
1120 ; SI = cluster # for the first cluster
1121 ; DX:AX = file length in bytes
1127 mov ax,[bsRootDirEnts]
1129 mov ax,[RootDirSize]
1130 mov [DirBlocksLeft],ax
1133 movzx ebp,word [DirBlocksLeft]
1140 sub [DirBlocksLeft],bp
1142 mov ax,[bsBytesPerSec]
1145 mov [EndofDirSec],ax ; End of loaded
1150 dir_test_name: cmp byte [si],0 ; Directory high water mark
1151 je dir_return ; Failed
1152 test byte [si+11],18h ; Check it really is a file
1156 mov cx,11 ; Filename = 11 bytes
1161 dir_not_this: add si,byte 32
1162 dec word [DirScanCtr]
1163 jz dir_return ; Out of it...
1164 cmp si,[EndofDirSec]
1166 add eax,ebp ; Increment linear sector number
1167 jmp short scan_group
1169 mov ax,[si+28] ; Length of file
1171 mov si,[si+26] ; Cluster pointer
1173 or bx,dx ; Sets ZF iff DX:AX is zero
1178 ; writechr: Write a single character in AL to the console without
1179 ; mangling any registers
1182 call write_serial ; write to serial port if needed
1186 mov bx,0007h ; white text on this page
1194 ; kaboom2: once everything is loaded, replace the part of kaboom
1195 ; starting with "kaboom.patch" with this part
1198 mov si,err_bootfailed
1202 int 19h ; And try once more to boot...
1203 .norge: jmp short .norge ; If int 19h returned; this is the end
1206 ; mangle_name: Mangle a DOS filename pointed to by DS:SI into a buffer pointed
1207 ; to by ES:DI; ends on encountering any whitespace
1211 mov cx,11 ; # of bytes to write
1214 cmp al,' ' ; If control or space, end
1216 cmp al,'.' ; Period -> space-fill
1223 jmp short mn_not_lower
1224 mn_is_period: mov al,' ' ; We need to space-fill
1225 mn_period_loop: cmp cx,3 ; If <= 3 characters left
1226 jbe mn_loop ; Just ignore it
1227 stosb ; Otherwise, write a period
1228 loop mn_period_loop ; Dec CX and (always) jump
1229 mn_not_uslower: cmp al,ucase_low
1233 mov bx,ucase_tab-ucase_low
1236 loop mn_loop ; Don't continue if too long
1238 mov al,' ' ; Space-fill name
1239 rep stosb ; Doesn't do anything if CX=0
1243 ; Upper-case table for extended characters; this is technically code page 865,
1244 ; but code page 437 users will probably not miss not being able to use the
1245 ; cent sign in kernel images too much :-)
1247 ; The table only covers the range 129 to 164; the rest we can deal with.
1251 ucase_tab db 154, 144, 'A', 142, 'A', 143, 128, 'EEEIII'
1252 db 142, 143, 144, 146, 146, 'O', 153, 'OUUY', 153, 154
1253 db 157, 156, 157, 158, 159, 'AIOU', 165
1256 ; unmangle_name: Does the opposite of mangle_name; converts a DOS-mangled
1257 ; filename to the conventional representation. This is needed
1258 ; for the BOOT_IMAGE= parameter for the kernel.
1259 ; NOTE: A 13-byte buffer is mandatory, even if the string is
1260 ; known to be shorter.
1262 ; DS:SI -> input mangled file name
1263 ; ES:DI -> output buffer
1265 ; On return, DI points to the first byte after the output name,
1266 ; which is set to a null byte.
1269 push si ; Save pointer to original name
1277 mov bp,di ; Position of last nonblank+1
1278 un_cb_space: loop un_copy_body
1280 mov al,'.' ; Don't save
1289 un_ce_space: loop un_copy_ext
1296 ; lower_case: Lower case a character in AL
1305 lc_1: cmp al,lcase_low
1310 mov bx,lcase_tab-lcase_low
1315 ; -----------------------------------------------------------------------------
1317 ; -----------------------------------------------------------------------------
1319 %include "getc.inc" ; getc et al
1320 %include "conio.inc" ; Console I/O
1321 %include "writestr.inc" ; String output
1322 %include "parseconfig.inc" ; High-level config file handling
1323 %include "parsecmd.inc" ; Low-level config file handling
1324 %include "bcopy32.inc" ; 32-bit bcopy
1325 %include "loadhigh.inc" ; Load a file into high memory
1326 %include "font.inc" ; VGA font stuff
1327 %include "graphics.inc" ; VGA graphics
1328 %include "highmem.inc" ; High memory sizing
1330 ; -----------------------------------------------------------------------------
1331 ; Begin data section
1332 ; -----------------------------------------------------------------------------
1334 CR equ 13 ; Carriage Return
1335 LF equ 10 ; Line Feed
1336 FF equ 12 ; Form Feed
1337 BS equ 8 ; Backspace
1340 ; Lower-case table for codepage 865
1344 lcase_tab db 135, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138
1345 db 139, 140, 141, 132, 134, 130, 145, 145, 147, 148, 149
1346 db 150, 151, 152, 148, 129, 155, 156, 155, 158, 159, 160
1347 db 161, 162, 163, 164, 164
1349 copyright_str db ' Copyright (C) 1994-', year, ' H. Peter Anvin'
1351 boot_prompt db 'boot: ', 0
1352 wipe_char db BS, ' ', BS, 0
1353 err_notfound db 'Could not find kernel image: ',0
1354 err_notkernel db CR, LF, 'Invalid or corrupt kernel image.', CR, LF, 0
1355 err_noram db 'It appears your computer has less than 488K of low ("DOS")'
1357 db 'RAM. Linux needs at least this amount to boot. If you get'
1359 db 'this message in error, hold down the Ctrl key while'
1361 db 'booting, and I will take your word for it.', CR, LF, 0
1362 err_badcfg db 'Unknown keyword in syslinux.cfg.', CR, LF, 0
1363 err_noparm db 'Missing parameter in syslinux.cfg.', CR, LF, 0
1364 err_noinitrd db CR, LF, 'Could not find ramdisk image: ', 0
1365 err_nohighmem db 'Not enough memory to load specified kernel.', CR, LF, 0
1366 err_highload db CR, LF, 'Kernel transfer failure.', CR, LF, 0
1367 err_oldkernel db 'Cannot load a ramdisk with an old kernel image.'
1369 err_notdos db ': attempted DOS system call', CR, LF, 0
1370 err_comlarge db 'COMBOOT image too large.', CR, LF, 0
1371 err_a20 db CR, LF, 'A20 gate not responding!', CR, LF, 0
1372 err_bootfailed db CR, LF, 'Boot failed: please change disks and press '
1373 db 'a key to continue.', CR, LF, 0
1374 ready_msg db 'Ready.', CR, LF, 0
1375 crlfloading_msg db CR, LF
1376 loading_msg db 'Loading ', 0
1379 aborted_msg db ' aborted.' ; Fall through to crlf_msg!
1382 crff_msg db CR, FF, 0
1383 syslinux_cfg db 'SYSLINUXCFG'
1385 ; Command line options we'd like to take a look at
1387 ; mem= and vga= are handled as normal 32-bit integer values
1388 initrd_cmd db 'initrd='
1389 initrd_cmd_len equ 7
1392 ; Config file keyword table
1394 %include "keywords.inc"
1397 ; Extensions to search for (in *forward* order).
1399 exten_table: db 'CBT',0 ; COMBOOT (specific)
1400 db 'BSS',0 ; Boot Sector (add superblock)
1401 db 'BS ',0 ; Boot Sector
1402 db 'COM',0 ; COMBOOT (same as DOS)
1404 dd 0, 0 ; Need 8 null bytes here
1407 ; Misc initialized (data) variables
1409 %ifdef debug ; This code for debugging only
1410 debug_magic dw 0D00Dh ; Debug code sentinel
1412 AppendLen dw 0 ; Bytes in append= command
1413 KbdTimeOut dw 0 ; Keyboard timeout (if any)
1414 CmdLinePtr dw cmd_line_here ; Command line advancing pointer
1416 initrd_ptr dw 0 ; Initial ramdisk pointer/flag
1417 VKernelCtr dw 0 ; Number of registered vkernels
1418 ForcePrompt dw 0 ; Force prompt
1419 AllowImplicit dw 1 ; Allow implicit kernels
1420 SerialPort dw 0 ; Serial port base (or 0 for no serial port)
1421 VGAFontSize dw 16 ; Defaults to 16 byte font
1422 UserFont db 0 ; Using a user-specified font
1423 ScrollAttribute db 07h ; White on black (for text mode)
1425 ; Stuff for the command line; we do some trickery here with equ to avoid
1426 ; tons of zeros appended to our file and wasting space
1428 linuxauto_cmd db 'linux auto',0
1429 linuxauto_len equ $-linuxauto_cmd
1430 boot_image db 'BOOT_IMAGE='
1431 boot_image_len equ $-boot_image
1432 align 4, db 0 ; For the good of REP MOVSD
1434 default_cmd equ $+(max_cmd_len+2)
1435 ldlinux_end equ default_cmd+(max_cmd_len+1)
1436 kern_cmd_len equ ldlinux_end-command_line
1437 ldlinux_len equ ldlinux_end-ldlinux_magic
1439 ; Put the getcbuf right after the code, aligned on a sector boundary
1441 end_of_code equ (ldlinux_end-bootsec)+7C00h
1442 getcbuf equ (end_of_code + 511) & 0FE00h
1444 ; VGA font buffer at the end of memory (so loading a font works even
1445 ; in graphics mode.)
1446 vgafontbuf equ 0E000h
1448 ; This is a compile-time assert that we didn't run out of space
1450 %if (getcbuf+trackbufsize) > vgafontbuf
1451 %error "Out of memory, better reorganize something..."