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 ; ****************************************************************************
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
49 ; This is what we need to do when idle
56 ; The following structure is used for "virtual kernels"; i.e. LILO-style
57 ; option labels. The options we permit here are `kernel' and `append
58 ; Since there is no room in the bottom 64K for all of these, we
59 ; stick them at vk_seg:0000 and copy them down before we need them.
61 ; Note: this structure can be added to, but it must
63 %define vk_power 7 ; log2(max number of vkernels)
64 %define max_vk (1 << vk_power) ; Maximum number of vkernels
65 %define vk_shift (16-vk_power) ; Number of bits to shift
66 %define vk_size (1 << vk_shift) ; Size of a vkernel buffer
69 vk_vname: resb FILENAME_MAX ; Virtual name **MUST BE FIRST!**
70 vk_rname: resb FILENAME_MAX ; Real name
73 vk_append: resb max_cmd_len+1 ; Command line
75 vk_end: equ $ ; Should be <= vk_size
79 %if (vk_end > vk_size) || (vk_size*max_vk > 65536)
80 %error "Too many vkernels defined, reduce vk_power"
85 ; Segment assignments in the bottom 640K
86 ; Stick to the low 512K in case we're using something like M-systems flash
87 ; which load a driver into low RAM (evil!!)
89 ; 0000h - main code/data segment (and BIOS segment)
91 real_mode_seg equ 5000h
92 fat_seg equ 3000h ; 128K area for FAT (2x64K)
93 vk_seg equ 2000h ; Virtual kernels
94 xfer_buf_seg equ 1000h ; Bounce buffer for I/O to high mem
95 comboot_seg equ real_mode_seg ; COMBOOT image loading zone
97 ; ---------------------------------------------------------------------------
99 ; ---------------------------------------------------------------------------
102 ; Memory below this point is reserved for the BIOS and the MBR
105 trackbuf equ $ ; Track buffer goes here
106 trackbufsize equ 16384 ; Safe size of track buffer
107 ; trackbuf ends at 5000h
111 ; Constants for the xfer_buf_seg
113 ; The xfer_buf_seg is also used to store message file buffers. We
114 ; need two trackbuffers (text and graphics), plus a work buffer
115 ; for the graphics decompressor.
117 xbs_textbuf equ 0 ; Also hard-coded, do not change
118 xbs_vgabuf equ trackbufsize
119 xbs_vgatmpbuf equ 2*trackbufsize
122 absolute 5000h ; Here we keep our BSS stuff
123 VKernelBuf: resb vk_size ; "Current" vkernel
125 AppendBuf resb max_cmd_len+1 ; append=
126 Ontimeout resb max_cmd_len+1 ; ontimeout
127 KbdMap resb 256 ; Keyboard map
128 FKeyName resb 10*16 ; File names for F-key help
129 NumBuf resb 15 ; Buffer to load number
130 NumBufEnd resb 1 ; Last byte in NumBuf
133 ; Expanded superblock
135 resq 16 ; The first 16 bytes expanded 8 times
137 ; These need to follow SuperInfo
139 RootDir resd 1 ; Location of root directory
140 DataArea resd 1 ; Location of data area
141 RootDirSize resw 1 ; Root dir size in sectors
142 DirScanCtr resw 1 ; Used while searching directory
143 EndofDirSec resw 1 ; = trackbuf+bsBytesPerSec-31
146 E820Buf resd 5 ; INT 15:E820 data buffer
147 HiLoadAddr resd 1 ; Address pointer for high load loop
148 HighMemSize resd 1 ; End of memory pointer (bytes)
149 RamdiskMax resd 1 ; Highest address for a ramdisk
150 KernelSize resd 1 ; Size of kernel (bytes)
151 SavedSSSP resd 1 ; Our SS:SP while running a COMBOOT image
152 PMESP resd 1 ; Protected-mode ESP
153 ClustPerMoby resd 1 ; Clusters per 64K
154 ClustSize resd 1 ; Bytes/cluster
155 KernelName resb 12 ; Mangled name for kernel
156 ; (note the spare byte after!)
157 OrigKernelExt resd 1 ; Original kernel extension
158 FBytes equ $ ; Used by open/getc
161 DirBlocksLeft resw 1 ; Ditto
162 RunLinClust resw 1 ; Cluster # for LDLINUX.SYS
163 BufSafe resw 1 ; Clusters we can load into trackbuf
164 BufSafeSec resw 1 ; = how many sectors?
165 BufSafeBytes resw 1 ; = how many bytes?
166 EndOfGetCBuf resw 1 ; = getcbuf+BufSafeBytes
167 KernelClust resw 1 ; Kernel size in clusters
168 FClust resw 1 ; Number of clusters in open/getc file
169 FNextClust resw 1 ; Pointer to next cluster in d:o
170 FPtr resw 1 ; Pointer to next char in buffer
171 CmdOptPtr resw 1 ; Pointer to first option on cmd line
172 KernelCNameLen resw 1 ; Length of unmangled kernel name
173 InitRDCNameLen resw 1 ; Length of unmangled initrd name
174 NextCharJump resw 1 ; Routine to interpret next print char
175 SetupSecs resw 1 ; Number of setup sectors
176 A20Test resw 1 ; Counter for testing status of A20
177 A20Type resw 1 ; A20 type
178 CmdLineLen resw 1 ; Length of command line including null
179 GraphXSize resw 1 ; Width of splash screen file
180 VGAPos resw 1 ; Pointer into VGA memory
181 VGACluster resw 1 ; Cluster pointer for VGA image file
182 VGAFilePtr resw 1 ; Pointer into VGAFileBuf
183 Com32SysSP resw 1 ; SP saved during COM32 syscall
185 TextAttribute resb 1 ; Text attribute for message file
186 TextPage resb 1 ; Active display page
188 CursorCol resb 1 ; Cursor column for message file
189 CursorRow resb 1 ; Cursor row for message file
191 VidCols resb 1 ; Columns on screen-1
192 VidRows resb 1 ; Rows on screen-1
193 BaudDivisor resw 1 ; Baud rate divisor
195 FlowOutput resb 1 ; Outputs to assert for serial flow
196 FlowInput resb 1 ; Input bits for serial flow
197 FlowIgnore resb 1 ; Ignore input unless these bits set
198 RetryCount resb 1 ; Used for disk access retries
199 KbdFlags resb 1 ; Check for keyboard escapes
200 LoadFlags resb 1 ; Loadflags from kernel
201 A20Tries resb 1 ; Times until giving up on A20
202 FuncFlag resb 1 ; Escape sequences received from keyboard
203 DisplayMask resb 1 ; Display modes mask
204 CopySuper resb 1 ; Distinguish .bs versus .bss
205 MNameBuf resb 11 ; Generic mangled file name buffer
206 InitRD resb 11 ; initrd= mangled name
207 KernelCName resb 13 ; Unmangled kernel name
208 InitRDCName resb 13 ; Unmangled initrd name
209 TextColorReg resb 17 ; VGA color registers for text mode
210 VGAFileBuf resb 13 ; Unmangled VGA image name
212 VGAFileMBuf resb 11 ; Mangled VGA image name
217 ; Some of the things that have to be saved very early are saved
218 ; "close" to the initial stack pointer offset, in order to
219 ; reduce the code size...
221 StackBuf equ $-44-32 ; Start the stack here (grow down - 4K)
222 PartInfo equ StackBuf ; Saved partition table entry
223 FloppyTable equ PartInfo+16 ; Floppy info table (must follow PartInfo)
224 OrigFDCTabPtr equ StackBuf-4 ; The high dword on the stack
227 ; Primary entry point. Tempting as though it may be, we can't put the
228 ; initial "cli" here; the jmp opcode in the first byte is part of the
229 ; "magic number" (using the term very loosely) for the DOS superblock.
232 jmp short start ; 2 bytes
235 ; "Superblock" follows -- it's in the boot sector, so it's already
236 ; loaded and ready for us
238 bsOemName db 'SYSLINUX' ; The SYS command sets this, so...
240 ; These are the fields we actually care about. We end up expanding them
241 ; all to dword size early in the code, so generate labels for both
242 ; the expanded and unexpanded versions.
245 bx %+ %1 equ SuperInfo+($-superblock)*8+4
250 bx %+ %1 equ SuperInfo+($-superblock)*8
255 bx %+ %1 equ $ ; no expansion for dwords
270 superinfo_size equ ($-superblock)-1 ; How much to expand
275 superb BootSignature ; 29h if the following fields exist
278 bsFileSysType zb 8 ; Must be FAT12 or FAT16 for this version
279 superblock_len equ $-superblock
281 SecPerClust equ bxSecPerClust
283 ; Note we don't check the constraints above now; we did that at install
287 ;floppy_table equ $ ; No sense in wasting memory, overwrite start
290 cli ; No interrupts yet, please
297 mov sp,StackBuf ; Just below BSS
300 ; DS:SI may contain a partition table entry. Preserve it for us.
302 mov cx,8 ; Save partition info
306 mov ds,ax ; Now we can initialize DS...
308 mov [di+bsDriveNumber-FloppyTable],dl
310 ; Now sautee the BIOS floppy info block to that it will support decent-
311 ; size transfers; the floppy block is 11 bytes and is stored in the
312 ; INT 1Eh vector (brilliant waste of resources, eh?)
314 ; Of course, if BIOSes had been properly programmed, we wouldn't have
315 ; had to waste precious space with this code.
318 lfs si,[bx] ; FS:SI -> original fdctab
319 push fs ; Save on stack in case we need to bail
322 ; Save the old fdctab even if hard disk so the stack layout
323 ; is the same. The instructions above do not change the flags
324 and dl,dl ; If floppy disk (00-7F), assume no
329 mov cl,6 ; 12 bytes (CX == 0)
330 ; es:di -> FloppyTable already
331 ; This should be safe to do now, interrupts are off...
332 mov [bx],di ; FloppyTable
333 mov [bx+2],ax ; Segment 0
334 fs rep movsw ; Faster to move words
335 mov cl,[bsSecPerTrack] ; Patch the sector count
338 int 13h ; Some BIOSes need this
340 jmp short not_harddisk
342 ; The drive number and possibly partition information was passed to us
343 ; by the BIOS or previous boot loader (MBR). Current "best practice" is to
344 ; trust that rather than what the superblock contains.
346 ; Would it be better to zero out bsHidden if we don't have a partition table?
348 ; Note: di points to beyond the end of PartInfo
351 test byte [di-16],7Fh ; Sanity check: "active flag" should
352 jnz no_partition ; be 00 or 80
353 mov eax,[di-8] ; Partition offset (dword)
357 ; Get disk drive parameters (don't trust the superblock.) Don't do this for
358 ; floppy drives -- INT 13:08 on floppy drives will (may?) return info about
359 ; what the *drive* supports, not about the *media*. Fortunately floppy disks
360 ; tend to have a fixed, well-defined geometry which is stored in the superblock.
362 ; DL == drive # still
369 inc dx ; Contains # of heads - 1
372 mov [bsSecPerTrack],cx
376 ; Ready to enable interrupts, captain
380 ; Insane hack to expand the superblock to dwords
384 mov es,ax ; INT 13:08 destroys ES
387 mov cl,superinfo_size ; CH == 0
391 stosd ; Store expanded word
393 stosd ; Store expanded byte
397 ; Now we have to do some arithmetric to figure out where things are located.
398 ; If Micro$oft had had brains they would already have done this for us,
399 ; and stored it in the superblock at format time, but here we go,
400 ; wasting precious boot sector space again...
402 %define Z di-superinfo_size*8-SuperInfo
404 mov ax,[bxFATs] ; Number of FATs (eax<31:16> == 0)
405 mov edx,[Z+bxFATsecs] ; Sectors/FAT
406 mul edx ; Get the size of the FAT area
408 add eax,[bxHidden] ; Add hidden sectors
409 add eax,[Z+bxResSectors] ; And reserved sectors
411 mov [RootDir],eax ; Location of root directory
412 mov [DataArea],eax ; First data sector
415 mov eax,[Z+bxRootDirEnts]
416 shl ax,5 ; Size of a directory entry
417 mov bx,[Z+bxBytesPerSec]
418 add ax,bx ; Round up, not down
420 div bx ; Now we have the size of the root dir
424 mov [Z+EndofDirSec],bx ; End of a single directory sector
426 pop eax ; Reload root directory starting point
429 ; Now the fun begins. We have to search the root directory for
430 ; LDLINUX.SYS and load the first sector, so we have a little more
431 ; space to have fun with. Then we can go chasing through the FAT.
439 sd_nextentry: mov cx,11
440 cmp [si],ch ; Directory high water mark
442 ; This no longer fits... since we'd be dead anyway if there
443 ; was a nonfile named LDLINUX.SYS on the disk, it shouldn't
445 ; test byte [si+11],18h ; Must be a file
452 sd_not_file: add si,byte 32 ; Distance to next
457 dec word [DirScanCtr]
460 ; kaboom: write a message and bail out.
465 mov sp,StackBuf-4 ; Reset stack
466 mov ds,si ; Reset data segment
467 pop dword [fdctab] ; Restore FDC table
468 .patch: mov si,bailmsg
469 call writestr ; Returns with AL = 0
471 int 16h ; Wait for keypress
472 int 19h ; And try once more to boot...
473 .norge: jmp short .norge ; If int 19h returned; this is the end
476 ; found_it: now we compute the location of the first sector, then
477 ; load it and JUMP (since we're almost out of space)
479 found_it: ; Note: we actually leave two words on the stack here
481 mov eax,[bxSecPerClust]
482 mov bp,ax ; Load an entire cluster
483 movzx ebx,word [si+26] ; First cluster
484 mov [RunLinClust],bx ; Save for later use
485 dec bx ; First cluster is "cluster 2"
494 repe cmpsb ; Make sure that the bootsector
495 jne kaboom ; matches LDLINUX.SYS
497 ; Done! Jump to the entry point!
502 ; writestr: write a null-terminated string to the console
508 mov ah,0Eh ; Write to screen as TTY
509 mov bx,0007h ; White on black, current page
515 ; disk_error: decrement the retry count and bail if zero.
516 ; This gets patched once we have more space to try to
517 ; optimize transfer sizes on broken machines.
519 disk_error: dec si ; SI holds the disk retry counter
521 ; End of patched "call" instruction!
522 jmp short disk_try_again
525 ; getonesec: like getlinsec, but pre-sets the count to 1
529 ; Fall through to getlinsec
532 ; getlinsec: load a sequence of BP floppy sector given by the linear sector
533 ; number in EAX into the buffer at ES:BX. We try to optimize
534 ; by loading up to a whole track at a time, but the user
535 ; is responsible for not crossing a 64K boundary.
536 ; (Yes, BP is weird for a count, but it was available...)
538 ; On return, BX points to the first byte after the transferred
541 ; The "stupid patch area" gets replaced by the code
542 ; mov bp,1 ; nop ... (BD 01 00 90 90...) when installing with
545 ; This routine assumes CS == DS.
547 ; Stylistic note: use "xchg" instead of "mov" when the source is a register
548 ; that is dead from that point; this saves space. However, please keep
549 ; the order to dst,src to keep things sane.
552 mov esi,[bxSecPerTrack]
554 ; Dividing by sectors to get (track,sector): we may have
555 ; up to 2^18 tracks, so we need to use 32-bit arithmetric.
557 xor edx,edx ; Zero-extend LBA to 64 bits
560 xchg cx,dx ; CX <- sector index (0-based)
563 div dword [bxHeads] ; Convert track to head/cyl
565 ; Now we have AX = cyl, DX = head, CX = sector (0-based),
566 ; BP = sectors to transfer, SI = bsSecPerTrack,
567 ; ES:BX = data target
569 gls_nextchunk: push si ; <A> bsSecPerTrack
570 push bp ; <B> Sectors to transfer
572 ; Important - this gets patched with a call. The call
573 ; assumes cx, si and bp are set up, and can modify bp
574 ; and destroy si. Until we have the space to do so,
575 ; transfer one sector at a time.
577 __BEGIN_STUPID_PATCH_AREA:
578 mov bp,1 ; 3 bytes, same as a call insn
579 __END_STUPID_PATCH_AREA:
581 push ax ; <C> Cylinder #
584 push cx ; <E> Sector #
585 shl ah,6 ; Because IBM was STOOPID
586 ; and thought 8 bits were enough
587 ; then thought 10 bits were enough...
588 pop cx ; <E> Sector #
589 push cx ; <E> Sector #
590 inc cx ; Sector numbers are 1-based, sigh
594 mov dl,[bsDriveNumber]
595 xchg ax,bp ; Sector to transfer count
596 ; (xchg shorter than mov)
597 mov si,retry_count ; # of times to retry a disk access
599 ; Do the disk transfer... save the registers in case we fail :(
603 mov ah,02h ; READ DISK
608 ; Disk access successful
610 pop cx ; <E> Sector #
611 mov di,ax ; Reduce sector left count
612 mul word [bsBytesPerSec] ; Figure out how much to advance ptr
613 add bx,ax ; Update buffer location
616 pop bp ; <B> Sectors left to transfer
617 pop si ; <A> Number of sectors/track
618 sub bp,di ; Reduce with # of sectors just read
619 jz writestr.return ; Done!
623 inc dx ; Next track on cyl
624 cmp dx,[bsHeads] ; Was this the last one?
626 inc ax ; If so, new cylinder
627 xor dx,dx ; First head on new cylinder
628 gls_nonewcyl: sub cx,si ; First sector on new track
629 jmp short gls_nextchunk
631 bailmsg: db 'Boot failed', 0Dh, 0Ah, 0
633 bs_checkpt equ $ ; Must be <= 7DEFh
636 bs_checkpt_off equ ($-$$)
638 %if bs_checkpt_off > 1EFh
639 %error "Boot sector overflow"
645 bs_magic equ $ ; From here to the magic_len equ
646 ; must match ldlinux_magic
647 ldlinux_name: db 'LDLINUX SYS' ; Looks like this in the root dir
648 dd HEXDATE ; Hopefully unique between compiles
650 bootsignature dw 0AA55h
651 magic_len equ $-bs_magic
654 ; ===========================================================================
656 ; ===========================================================================
657 ; Start of LDLINUX.SYS
658 ; ===========================================================================
662 syslinux_banner db 0Dh, 0Ah
668 db version_str, ' ', date, ' ', 0
669 db 0Dh, 0Ah, 1Ah ; EOF if we "type" this in DOS
671 ldlinux_magic db 'LDLINUX SYS'
676 ; This area is possibly patched by the installer. It is located
677 ; immediately after the EOF + LDLINUX SYS + 4 bytes + 55 AA + alignment,
678 ; so we can find it algorithmically.
681 MaxTransfer dw 00FFh ; Absolutely maximum transfer size
686 ; Note that some BIOSes are buggy and run the boot sector at 07C0:0000
687 ; instead of 0000:7C00 and the like. We don't want to add anything
688 ; more to the boot sector, so it is written to not assume a fixed
689 ; value in CS, but we don't want to deal with that anymore from now
696 ; Tell the user we got this far
698 mov si,syslinux_banner
701 ; Remember, the boot sector loaded only the first cluster of LDLINUX.SYS.
702 ; We can really only rely on a single sector having been loaded. Hence
703 ; we should load the FAT into RAM and start chasing pointers...
707 inc dx ; DX:AX <- 64K
708 div word [bxBytesPerSec] ; sectors/64K
712 mov bx,fat_seg ; Load into fat_seg:0000
715 mov eax,[bsHidden] ; Hidden sectors
716 add edx,[bxResSectors]
718 mov ecx,[bxFATsecs] ; Sectors/FAT
720 mov ebp,ecx ; Make sure high EBP = 0
723 mov bp,si ; A full 64K moby
725 xor bx,bx ; Offset 0 in the current ES
728 jz fat_load_done ; Last moby?
729 add eax,ebp ; Advance sector count
730 mov bx,es ; Next 64K moby
733 jmp short fat_load_loop
737 ; Fine, now we have the FAT in memory. How big is a cluster, really?
738 ; Also figure out how many clusters will fit in an 8K buffer, and how
739 ; many sectors and bytes that is
741 mov edi,[bxBytesPerSec] ; Used a lot below
742 mov eax,[SecPerClust]
743 mov si,ax ; Also used a lot
745 mov [ClustSize],eax ; Bytes/cluster
747 mov ax,trackbufsize ; High bit 0
750 mov [BufSafe],ax ; # of cluster in trackbuf
754 mov [BufSafeBytes],ax
755 add ax,getcbuf ; Size of getcbuf is the same
756 mov [EndOfGetCBuf],ax ; as for trackbuf
758 ; FAT12 or FAT16? This computation is fscking ridiculous...
763 mov eax,[bsHugeSectors]
764 have_secs: add eax,[bsHidden] ; These are not included
765 sub eax,[RootDir] ; Start of root directory
766 movzx ebx,word [RootDirSize]
767 sub eax,ebx ; Subtract root directory size
769 div esi ; Convert to clusters
770 cmp ax,4086 ; FAT12 limit
773 mov byte [nextcluster+1],nextcluster_fat16-(nextcluster+2)
777 ; Patch gls_set_size so we can transfer more than one sector at a time.
779 mov byte [gls_set_size],0xe8 ; E8 = CALL NEAR
780 mov word [gls_set_size+1],do_gls_set_size-(gls_set_size+3)
781 mov byte [disk_error],0xe8
782 mov word [disk_error+1],do_disk_error-(disk_error+3)
785 ; Now we read the rest of LDLINUX.SYS. Don't bother loading the first
786 ; cluster again, though.
795 mov ax,ldlinux_len-1 ; To be on the safe side
797 div cx ; the number of clusters
798 dec ax ; We've already read one
808 ; -----------------------------------------------------------------------------
809 ; Subroutines that have to be in the first sector
810 ; -----------------------------------------------------------------------------
812 ; getfssec: Get multiple clusters from a file, given the starting cluster.
814 ; This routine makes sure the subtransfers do not cross a 64K boundary,
815 ; and will correct the situation if it does, UNLESS *sectors* cross
819 ; SI -> Starting cluster number (2-based)
820 ; CX -> Cluster count (0FFFFh = until end of file)
822 ; Returns CF=1 on EOF
825 .getfragment: xor ebp,ebp ; Fragment sector count
826 lea eax,[si-2] ; Get 0-based sector address
827 mul dword [SecPerClust]
829 .getseccnt: ; See if we can read > 1 clust
831 dec cx ; Reduce clusters left to find
836 jcxz .endfragment ; Or was it the last we wanted?
837 cmp si,di ; Is file continuous?
838 je .getseccnt ; Yes, we can get
839 .endfragment: clc ; Not at EOF
840 .eof: pushf ; Remember EOF or not
845 mov ax,es ; Check for 64K boundaries.
850 setz dl ; DX <- 1 if full 64K segment
851 div word [bsBytesPerSec] ; How many sectors fit?
853 sub si,ax ; Compute remaining sectors
858 add eax,ebp ; EBP<31:16> == 0
859 mov bp,si ; Remaining sector count
866 jcxz .return ; If we hit the count limit
867 jnc .getfragment ; If we didn't hit EOF
871 ; getlinsecsr: save registers, call getlinsec, restore registers
879 ; nextcluster: Advance a cluster pointer in SI to the next cluster
880 ; pointed at in the FAT tables. CF=0 on return if end of file.
883 jmp short nextcluster_fat12 ; This gets patched
890 mov bx,si ; Multiply by 3/2
891 shr bx,1 ; CF now set if odd
894 shr si,4 ; Needed for odd only
897 cmp si,0FF0h ; Clears CF if at end of file
903 ; FAT16 decoding routine. Note that a 16-bit FAT can be up to 128K,
904 ; so we have to decide if we're in the "low" or the "high" 64K-segment...
921 ; Routine that controls how much we can transfer in one chunk. Called
922 ; from gls_set_size in getlinsec.
925 sub si,cx ; Sectors left on track
928 mov bp,si ; No more than a trackful, please!
930 cmp bp,[MaxTransfer] ; Absolute maximum transfer size
937 ; This routine captures disk errors, and tries to decide if it is
938 ; time to reduce the transfer size.
941 dec si ; Decrement the retry counter
942 jz kaboom ; If expired, croak
943 cmp si,2 ; If only 2 attempts left
945 mov al,1 ; Drop transfer size to 1
949 ja .again ; First time, just try again
950 shr al,1 ; Otherwise, try to reduce
951 adc al,0 ; the max transfer size, but not to 0
962 cmp word [Debug_Magic],0D00Dh
967 rl_checkpt equ $ ; Must be <= 8000h
969 rl_checkpt_off equ ($-$$)
971 %if rl_checkpt_off > 400h
972 %error "Sector 1 overflow"
976 ; ----------------------------------------------------------------------------
977 ; End of code and data that have to be in the first sector
978 ; ----------------------------------------------------------------------------
982 ; Let the user (and programmer!) know we got this far. This used to be
983 ; in Sector 1, but makes a lot more sense here.
989 ; Common initialization code
991 %include "cpuinit.inc"
994 ; Initialization that does not need to go into the any of the pre-load
997 ; Now set up screen parameters
1000 ; Wipe the F-key area
1003 mov cx,10*(1 << FILENAME_MAX_LG2)
1007 ; Now, everything is "up and running"... patch kaboom for more
1008 ; verbosity and using the full screen system
1011 mov dword [kaboom.patch],0e9h+((kaboom2-(kaboom.patch+3)) << 8)
1014 ; Compute some parameters that depend on cluster size
1018 inc dx ; DX:AX <- 64K
1019 div word [ClustSize]
1020 mov [ClustPerMoby],eax ; Clusters/64K
1023 ; Now we're all set to start with our *real* business. First load the
1024 ; configuration file (if any) and parse it.
1026 ; In previous versions I avoided using 32-bit registers because of a
1027 ; rumour some BIOSes clobbered the upper half of 32-bit registers at
1028 ; random. I figure, though, that if there are any of those still left
1029 ; they probably won't be trying to install Linux on them...
1031 ; The code is still ripe with 16-bitisms, though. Not worth the hassle
1032 ; to take'm out. In fact, we may want to put them back if we're going
1033 ; to boot ELKS at some point.
1035 mov si,linuxauto_cmd ; Default command: "linux auto"
1037 mov cx,linuxauto_len
1040 mov di,KbdMap ; Default keymap 1:1
1048 ; Load configuration file
1055 ; Now we have the config file open. Parse the config file and
1056 ; run the user interface.
1061 ; Linux kernel loading code is common.
1063 %include "runkernel.inc"
1066 ; COMBOOT-loading code
1068 %include "comboot.inc"
1069 %include "com32.inc"
1070 %include "cmdline.inc"
1073 ; Boot sector loading code
1075 %include "bootsect.inc"
1078 ; abort_check: let the user abort with <ESC> or <Ctrl-C>
1089 ac_kill: mov si,aborted_msg
1092 ; abort_load: Called by various routines which wants to print a fatal
1093 ; error message and return to the command prompt. Since this
1094 ; may happen at just about any stage of the boot process, assume
1095 ; our state is messed up, and just reset the segment registers
1096 ; and the stack forcibly.
1098 ; SI = offset (in _text) of error message to print
1101 mov ax,cs ; Restore CS = DS = ES
1105 mov sp,StackBuf-2*3 ; Reset stack
1106 mov ss,ax ; Just in case...
1108 call cwritestr ; Expects SI -> error msg
1109 al_ok: jmp enter_command ; Return to command prompt
1111 ; End of abort_check
1117 ; searchdir: Search the root directory for a pre-mangled filename in
1118 ; DS:DI. This routine is similar to the one in the boot
1119 ; sector, but is a little less Draconian when it comes to
1120 ; error handling, plus it reads the root directory in
1121 ; larger chunks than a sector at a time (which is probably
1122 ; a waste of coding effort, but I like to do things right).
1124 ; FIXME: usually we can load the entire root dir in memory,
1125 ; and files are usually at the beginning anyway. It probably
1126 ; would be worthwhile to remember if we have the first chunk
1127 ; in memory and skip the load if that (it would speed up online
1130 ; NOTE: This file considers finding a zero-length file an
1131 ; error. This is so we don't have to deal with that special
1132 ; case elsewhere in the program (most loops have the test
1137 ; SI = cluster # for the first cluster
1138 ; DX:AX = file length in bytes
1144 mov ax,[bsRootDirEnts]
1146 mov ax,[RootDirSize]
1147 mov [DirBlocksLeft],ax
1150 movzx ebp,word [DirBlocksLeft]
1157 sub [DirBlocksLeft],bp
1159 mov ax,[bsBytesPerSec]
1162 mov [EndofDirSec],ax ; End of loaded
1167 dir_test_name: cmp byte [si],0 ; Directory high water mark
1168 je dir_return ; Failed
1169 test byte [si+11],18h ; Check it really is a file
1173 mov cx,11 ; Filename = 11 bytes
1178 dir_not_this: add si,byte 32
1179 dec word [DirScanCtr]
1180 jz dir_return ; Out of it...
1181 cmp si,[EndofDirSec]
1183 add eax,ebp ; Increment linear sector number
1184 jmp short scan_group
1186 mov ax,[si+28] ; Length of file
1188 mov si,[si+26] ; Cluster pointer
1190 or bx,dx ; Sets ZF iff DX:AX is zero
1195 ; writechr: Write a single character in AL to the console without
1196 ; mangling any registers
1199 call write_serial ; write to serial port if needed
1203 mov bx,0007h ; white text on this page
1211 ; kaboom2: once everything is loaded, replace the part of kaboom
1212 ; starting with "kaboom.patch" with this part
1215 mov si,err_bootfailed
1219 int 19h ; And try once more to boot...
1220 .norge: jmp short .norge ; If int 19h returned; this is the end
1223 ; mangle_name: Mangle a DOS filename pointed to by DS:SI into a buffer pointed
1224 ; to by ES:DI; ends on encountering any whitespace
1228 mov cx,11 ; # of bytes to write
1231 cmp al,' ' ; If control or space, end
1233 cmp al,'.' ; Period -> space-fill
1240 jmp short mn_not_lower
1241 mn_is_period: mov al,' ' ; We need to space-fill
1242 mn_period_loop: cmp cx,3 ; If <= 3 characters left
1243 jbe mn_loop ; Just ignore it
1244 stosb ; Otherwise, write a period
1245 loop mn_period_loop ; Dec CX and (always) jump
1246 mn_not_uslower: cmp al,ucase_low
1250 mov bx,ucase_tab-ucase_low
1253 loop mn_loop ; Don't continue if too long
1255 mov al,' ' ; Space-fill name
1256 rep stosb ; Doesn't do anything if CX=0
1260 ; Upper-case table for extended characters; this is technically code page 865,
1261 ; but code page 437 users will probably not miss not being able to use the
1262 ; cent sign in kernel images too much :-)
1264 ; The table only covers the range 129 to 164; the rest we can deal with.
1268 ucase_tab db 154, 144, 'A', 142, 'A', 143, 128, 'EEEIII'
1269 db 142, 143, 144, 146, 146, 'O', 153, 'OUUY', 153, 154
1270 db 157, 156, 157, 158, 159, 'AIOU', 165
1273 ; unmangle_name: Does the opposite of mangle_name; converts a DOS-mangled
1274 ; filename to the conventional representation. This is needed
1275 ; for the BOOT_IMAGE= parameter for the kernel.
1276 ; NOTE: A 13-byte buffer is mandatory, even if the string is
1277 ; known to be shorter.
1279 ; DS:SI -> input mangled file name
1280 ; ES:DI -> output buffer
1282 ; On return, DI points to the first byte after the output name,
1283 ; which is set to a null byte.
1286 push si ; Save pointer to original name
1294 mov bp,di ; Position of last nonblank+1
1295 un_cb_space: loop un_copy_body
1297 mov al,'.' ; Don't save
1306 un_ce_space: loop un_copy_ext
1313 ; lower_case: Lower case a character in AL
1322 lc_1: cmp al,lcase_low
1327 mov bx,lcase_tab-lcase_low
1332 ; -----------------------------------------------------------------------------
1334 ; -----------------------------------------------------------------------------
1336 %include "getc.inc" ; getc et al
1337 %include "conio.inc" ; Console I/O
1338 %include "writestr.inc" ; String output
1339 %include "parseconfig.inc" ; High-level config file handling
1340 %include "parsecmd.inc" ; Low-level config file handling
1341 %include "bcopy32.inc" ; 32-bit bcopy
1342 %include "loadhigh.inc" ; Load a file into high memory
1343 %include "font.inc" ; VGA font stuff
1344 %include "graphics.inc" ; VGA graphics
1345 %include "highmem.inc" ; High memory sizing
1347 ; -----------------------------------------------------------------------------
1348 ; Begin data section
1349 ; -----------------------------------------------------------------------------
1351 CR equ 13 ; Carriage Return
1352 LF equ 10 ; Line Feed
1353 FF equ 12 ; Form Feed
1354 BS equ 8 ; Backspace
1357 ; Lower-case table for codepage 865
1361 lcase_tab db 135, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138
1362 db 139, 140, 141, 132, 134, 130, 145, 145, 147, 148, 149
1363 db 150, 151, 152, 148, 129, 155, 156, 155, 158, 159, 160
1364 db 161, 162, 163, 164, 164
1366 copyright_str db ' Copyright (C) 1994-', year, ' H. Peter Anvin'
1368 boot_prompt db 'boot: ', 0
1369 wipe_char db BS, ' ', BS, 0
1370 err_notfound db 'Could not find kernel image: ',0
1371 err_notkernel db CR, LF, 'Invalid or corrupt kernel image.', CR, LF, 0
1372 err_noram db 'It appears your computer has less than '
1374 db 'K of low ("DOS")'
1376 db 'RAM. Linux needs at least this amount to boot. If you get'
1378 db 'this message in error, hold down the Ctrl key while'
1380 db 'booting, and I will take your word for it.', CR, LF, 0
1381 err_badcfg db 'Unknown keyword in syslinux.cfg.', CR, LF, 0
1382 err_noparm db 'Missing parameter in syslinux.cfg.', CR, LF, 0
1383 err_noinitrd db CR, LF, 'Could not find ramdisk image: ', 0
1384 err_nohighmem db 'Not enough memory to load specified kernel.', CR, LF, 0
1385 err_highload db CR, LF, 'Kernel transfer failure.', CR, LF, 0
1386 err_oldkernel db 'Cannot load a ramdisk with an old kernel image.'
1388 err_notdos db ': attempted DOS system call', CR, LF, 0
1389 err_comlarge db 'COMBOOT image too large.', CR, LF, 0
1390 err_a20 db CR, LF, 'A20 gate not responding!', CR, LF, 0
1391 err_bootfailed db CR, LF, 'Boot failed: please change disks and press '
1392 db 'a key to continue.', CR, LF, 0
1393 ready_msg db 'Ready.', CR, LF, 0
1394 crlfloading_msg db CR, LF
1395 loading_msg db 'Loading ', 0
1398 aborted_msg db ' aborted.' ; Fall through to crlf_msg!
1401 crff_msg db CR, FF, 0
1402 syslinux_cfg db 'SYSLINUXCFG'
1404 manifest db 'MANIFEST '
1407 ; Command line options we'd like to take a look at
1409 ; mem= and vga= are handled as normal 32-bit integer values
1410 initrd_cmd db 'initrd='
1411 initrd_cmd_len equ 7
1414 ; Config file keyword table
1416 %include "keywords.inc"
1419 ; Extensions to search for (in *forward* order).
1421 exten_table: db 'CBT',0 ; COMBOOT (specific)
1422 db 'BSS',0 ; Boot Sector (add superblock)
1423 db 'BS ',0 ; Boot Sector
1424 db 'COM',0 ; COMBOOT (same as DOS)
1427 dd 0, 0 ; Need 8 null bytes here
1430 ; Misc initialized (data) variables
1432 %ifdef debug ; This code for debugging only
1433 debug_magic dw 0D00Dh ; Debug code sentinel
1435 AppendLen dw 0 ; Bytes in append= command
1436 OntimeoutLen dw 0 ; Bytes in ontimeout command
1437 KbdTimeOut dw 0 ; Keyboard timeout (if any)
1438 CmdLinePtr dw cmd_line_here ; Command line advancing pointer
1440 initrd_ptr dw 0 ; Initial ramdisk pointer/flag
1441 VKernelCtr dw 0 ; Number of registered vkernels
1442 ForcePrompt dw 0 ; Force prompt
1443 AllowImplicit dw 1 ; Allow implicit kernels
1444 SerialPort dw 0 ; Serial port base (or 0 for no serial port)
1445 VGAFontSize dw 16 ; Defaults to 16 byte font
1446 UserFont db 0 ; Using a user-specified font
1447 ScrollAttribute db 07h ; White on black (for text mode)
1449 ; Stuff for the command line; we do some trickery here with equ to avoid
1450 ; tons of zeros appended to our file and wasting space
1452 linuxauto_cmd db 'linux auto',0
1453 linuxauto_len equ $-linuxauto_cmd
1454 boot_image db 'BOOT_IMAGE='
1455 boot_image_len equ $-boot_image
1456 align 4, db 0 ; For the good of REP MOVSD
1458 default_cmd equ $+(max_cmd_len+2)
1459 ldlinux_end equ default_cmd+(max_cmd_len+1)
1460 kern_cmd_len equ ldlinux_end-command_line
1461 ldlinux_len equ ldlinux_end-ldlinux_magic
1463 ; Put the getcbuf right after the code, aligned on a sector boundary
1465 end_of_code equ (ldlinux_end-bootsec)+7C00h
1466 getcbuf equ (end_of_code + 511) & 0FE00h
1468 ; VGA font buffer at the end of memory (so loading a font works even
1469 ; in graphics mode.)
1470 vgafontbuf equ 0E000h
1472 ; This is a compile-time assert that we didn't run out of space
1474 %if (getcbuf+trackbufsize) > vgafontbuf
1475 %error "Out of memory, better reorganize something..."