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 FBytes equ $ ; Used by open/getc
150 DirBlocksLeft resw 1 ; Ditto
151 RunLinClust resw 1 ; Cluster # for LDLINUX.SYS
152 BufSafe resw 1 ; Clusters we can load into trackbuf
153 BufSafeSec resw 1 ; = how many sectors?
154 BufSafeBytes resw 1 ; = how many bytes?
155 EndOfGetCBuf resw 1 ; = getcbuf+BufSafeBytes
156 KernelClust resw 1 ; Kernel size in clusters
157 FClust resw 1 ; Number of clusters in open/getc file
158 FNextClust resw 1 ; Pointer to next cluster in d:o
159 FPtr resw 1 ; Pointer to next char in buffer
160 CmdOptPtr resw 1 ; Pointer to first option on cmd line
161 KernelCNameLen resw 1 ; Length of unmangled kernel name
162 InitRDCNameLen resw 1 ; Length of unmangled initrd name
163 NextCharJump resw 1 ; Routine to interpret next print char
164 SetupSecs resw 1 ; Number of setup sectors
165 A20Test resw 1 ; Counter for testing status of A20
166 A20Type resw 1 ; A20 type
167 CmdLineLen resw 1 ; Length of command line including null
168 GraphXSize resw 1 ; Width of splash screen file
169 VGAPos resw 1 ; Pointer into VGA memory
170 VGACluster resw 1 ; Cluster pointer for VGA image file
171 VGAFilePtr resw 1 ; Pointer into VGAFileBuf
173 TextAttribute resb 1 ; Text attribute for message file
174 TextPage resb 1 ; Active display page
176 CursorCol resb 1 ; Cursor column for message file
177 CursorRow resb 1 ; Cursor row for message file
179 VidCols resb 1 ; Columns on screen-1
180 VidRows resb 1 ; Rows on screen-1
182 FlowOutput resb 1 ; Outputs to assert for serial flow
183 FlowInput resb 1 ; Input bits for serial flow
184 FlowIgnore resb 1 ; Ignore input unless these bits set
185 RetryCount resb 1 ; Used for disk access retries
186 KbdFlags resb 1 ; Check for keyboard escapes
187 LoadFlags resb 1 ; Loadflags from kernel
188 A20Tries resb 1 ; Times until giving up on A20
189 FuncFlag resb 1 ; Escape sequences received from keyboard
190 DisplayMask resb 1 ; Display modes mask
191 MNameBuf resb 11 ; Generic mangled file name buffer
192 InitRD resb 11 ; initrd= mangled name
193 KernelCName resb 13 ; Unmangled kernel name
194 InitRDCName resb 13 ; Unmangled initrd name
195 TextColorReg resb 17 ; VGA color registers for text mode
196 VGAFileBuf resb 13 ; Unmangled VGA image name
198 VGAFileMBuf resb 11 ; Mangled VGA image name
203 ; Some of the things that have to be saved very early are saved
204 ; "close" to the initial stack pointer offset, in order to
205 ; reduce the code size...
207 StackBuf equ $-32 ; Start the stack here (grow down - 4K)
208 PartInfo equ StackBuf ; Saved partition table entry
209 FloppyTable equ PartInfo+16 ; Floppy info table (must follow PartInfo)
210 OrigFDCTabPtr equ StackBuf-4 ; The high dword on the stack
213 ; Primary entry point. Tempting as though it may be, we can't put the
214 ; initial "cli" here; the jmp opcode in the first byte is part of the
215 ; "magic number" (using the term very loosely) for the DOS superblock.
218 jmp short start ; 2 bytes
221 ; "Superblock" follows -- it's in the boot sector, so it's already
222 ; loaded and ready for us
224 bsOemName db 'SYSLINUX' ; The SYS command sets this, so...
226 ; These are the fields we actually care about. We end up expanding them
227 ; all to dword size early in the code, so generate labels for both
228 ; the expanded and unexpanded versions.
231 bx %+ %1 equ SuperInfo+($-superblock)*8+4
236 bx %+ %1 equ SuperInfo+($-superblock)*8
241 bx %+ %1 equ $ ; no expansion for dwords
256 superinfo_size equ ($-superblock)-1 ; How much to expand
261 superb BootSignature ; 29h if the following fields exist
264 bsFileSysType zb 8 ; Must be FAT12 or FAT16 for this version
265 superblock_len equ $-superblock
267 SecPerClust equ bxSecPerClust
269 ; Note we don't check the constraints above now; we did that at install
273 ;floppy_table equ $ ; No sense in wasting memory, overwrite start
276 cli ; No interrupts yet, please
283 mov sp,StackBuf ; Just below BSS
286 ; DS:SI may contain a partition table entry. Preserve it for us.
288 mov cx,8 ; Save partition info
292 mov ds,ax ; Now we can initialize DS...
294 mov [di+bsDriveNumber-FloppyTable],dl
295 and dl,dl ; If floppy disk (00-7F), assume no
296 js harddisk ; partition table
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
309 mov cl,6 ; 12 bytes (CX == 0)
310 ; es:di -> FloppyTable already
311 ; This should be safe to do now, interrupts are off...
312 mov [bx],di ; FloppyTable
313 mov [bx+2],ax ; Segment 0
314 fs rep movsw ; Faster to move words
315 mov cl,[bsSecPerTrack] ; Patch the sector count
318 int 13h ; Some BIOSes need this
320 jmp short not_harddisk
322 ; The drive number and possibly partition information was passed to us
323 ; by the BIOS or previous boot loader (MBR). Current "best practice" is to
324 ; trust that rather than what the superblock contains.
326 ; Would it be better to zero out bsHidden if we don't have a partition table?
328 ; Note: di points to beyond the end of PartInfo
331 ; This sanity check doesn't fit anymore...
332 ; test byte [di-16],7Fh ; Sanity check: "active flag" should
333 ; jnz no_partition ; be 00 or 80
334 mov eax,[di-8] ; Partition offset (dword)
338 ; Get disk drive parameters (don't trust the superblock.) Don't do this for
339 ; floppy drives -- INT 13:08 on floppy drives will (may?) return info about
340 ; what the *drive* supports, not about the *media*. Fortunately floppy disks
341 ; tend to have a fixed, well-defined geometry which is stored in the superblock.
343 ; DL == drive # still
350 inc dx ; Contains # of heads - 1
353 mov [bsSecPerTrack],cx
357 ; Ready to enable interrupts, captain
361 ; Insane hack to expand the superblock to dwords
365 mov es,ax ; INT 13:08 destroys ES
368 mov cl,superinfo_size ; CH == 0
372 stosd ; Store expanded word
374 stosd ; Store expanded byte
378 ; Now we have to do some arithmetric to figure out where things are located.
379 ; If Micro$oft had had brains they would already have done this for us,
380 ; and stored it in the superblock at format time, but here we go,
381 ; wasting precious boot sector space again...
383 %define Z di-superinfo_size*8-SuperInfo
385 mov ax,[bxFATs] ; Number of FATs (eax<31:16> == 0)
386 mov edx,[Z+bxFATsecs] ; Sectors/FAT
387 mul edx ; Get the size of the FAT area
389 add eax,[bxHidden] ; Add hidden sectors
390 add eax,[Z+bxResSectors] ; And reserved sectors
392 mov [RootDir],eax ; Location of root directory
393 mov [DataArea],eax ; First data sector
396 mov eax,[Z+bxRootDirEnts]
397 shl ax,5 ; Size of a directory entry
398 mov bx,[Z+bxBytesPerSec]
399 add ax,bx ; Round up, not down
401 div bx ; Now we have the size of the root dir
405 mov [Z+EndofDirSec],bx ; End of a single directory sector
407 pop eax ; Reload root directory starting point
410 ; Now the fun begins. We have to search the root directory for
411 ; LDLINUX.SYS and load the first sector, so we have a little more
412 ; space to have fun with. Then we can go chasing through the FAT.
420 sd_nextentry: mov cx,11
421 cmp [si],ch ; Directory high water mark
423 ; This no longer fits... since we'd be dead anyway if there
424 ; was a nonfile named LDLINUX.SYS on the disk, it shouldn't
426 ; test byte [si+11],18h ; Must be a file
433 sd_not_file: add si,byte 32 ; Distance to next
438 dec word [DirScanCtr]
441 ; kaboom: write a message and bail out.
446 mov sp,StackBuf-4 ; Reset stack
447 mov ds,si ; Reset data segment
448 pop dword [fdctab] ; Restore FDC table
449 .patch: mov si,bailmsg
450 call writestr ; Returns with AL = 0
452 int 16h ; Wait for keypress
453 int 19h ; And try once more to boot...
454 .norge: jmp short .norge ; If int 19h returned; this is the end
457 ; found_it: now we compute the location of the first sector, then
458 ; load it and JUMP (since we're almost out of space)
460 found_it: ; Note: we actually leave two words on the stack here
462 mov eax,[bxSecPerClust]
463 mov bp,ax ; Load an entire cluster
464 movzx ebx,word [si+26] ; First cluster
465 mov [RunLinClust],bx ; Save for later use
466 dec bx ; First cluster is "cluster 2"
475 repe cmpsb ; Make sure that the bootsector
476 jne kaboom ; matches LDLINUX.SYS
478 ; Done! Jump to the entry point!
483 ; writestr: write a null-terminated string to the console
489 mov ah,0Eh ; Write to screen as TTY
490 mov bx,0007h ; White on black, current page
496 ; disk_error: decrement the retry count and bail if zero.
497 ; This gets patched once we have more space to try to
498 ; optimize transfer sizes on broken machines.
500 disk_error: dec si ; SI holds the disk retry counter
502 ; End of patched "call" instruction!
503 jmp short disk_try_again
506 ; getonesec: like getlinsec, but pre-sets the count to 1
510 ; Fall through to getlinsec
513 ; getlinsec: load a sequence of BP floppy sector given by the linear sector
514 ; number in EAX into the buffer at ES:BX. We try to optimize
515 ; by loading up to a whole track at a time, but the user
516 ; is responsible for not crossing a 64K boundary.
517 ; (Yes, BP is weird for a count, but it was available...)
519 ; On return, BX points to the first byte after the transferred
522 ; The "stupid patch area" gets replaced by the code
523 ; mov bp,1 ; nop ... (BD 01 00 90 90...) when installing with
526 ; This routine assumes CS == DS.
528 ; Stylistic note: use "xchg" instead of "mov" when the source is a register
529 ; that is dead from that point; this saves space. However, please keep
530 ; the order to dst,src to keep things sane.
533 mov esi,[bxSecPerTrack]
535 ; Dividing by sectors to get (track,sector): we may have
536 ; up to 2^18 tracks, so we need to use 32-bit arithmetric.
538 xor edx,edx ; Zero-extend LBA to 64 bits
541 xchg cx,dx ; CX <- sector index (0-based)
544 div dword [bxHeads] ; Convert track to head/cyl
546 ; Now we have AX = cyl, DX = head, CX = sector (0-based),
547 ; BP = sectors to transfer, SI = bsSecPerTrack,
548 ; ES:BX = data target
550 gls_nextchunk: push si ; <A> bsSecPerTrack
551 push bp ; <B> Sectors to transfer
553 ; Important - this gets patched with a call. The call
554 ; assumes cx, si and bp are set up, and can modify bp
555 ; and destroy si. Until we have the space to do so,
556 ; transfer one sector at a time.
558 __BEGIN_STUPID_PATCH_AREA:
559 mov bp,1 ; 3 bytes, same as a call insn
560 __END_STUPID_PATCH_AREA:
562 push ax ; <C> Cylinder #
565 push cx ; <E> Sector #
566 shl ah,6 ; Because IBM was STOOPID
567 ; and thought 8 bits were enough
568 ; then thought 10 bits were enough...
569 pop cx ; <E> Sector #
570 push cx ; <E> Sector #
571 inc cx ; Sector numbers are 1-based, sigh
575 mov dl,[bsDriveNumber]
576 xchg ax,bp ; Sector to transfer count
577 ; (xchg shorter than mov)
578 mov si,retry_count ; # of times to retry a disk access
580 ; Do the disk transfer... save the registers in case we fail :(
584 mov ah,02h ; READ DISK
589 ; Disk access successful
591 pop cx ; <E> Sector #
592 mov di,ax ; Reduce sector left count
593 mul word [bsBytesPerSec] ; Figure out how much to advance ptr
594 add bx,ax ; Update buffer location
597 pop bp ; <B> Sectors left to transfer
598 pop si ; <A> Number of sectors/track
599 sub bp,di ; Reduce with # of sectors just read
600 jz writestr.return ; Done!
604 inc dx ; Next track on cyl
605 cmp dx,[bsHeads] ; Was this the last one?
607 inc ax ; If so, new cylinder
608 xor dx,dx ; First head on new cylinder
609 gls_nonewcyl: sub cx,si ; First sector on new track
610 jmp short gls_nextchunk
612 bailmsg: db 'Boot failed', 0Dh, 0Ah, 0
614 bs_checkpt equ $ ; Must be <= 7DEFh
617 bs_checkpt_off equ ($-$$)
619 %if bs_checkpt_off > 1EFh
620 %error "Boot sector overflow"
626 bs_magic equ $ ; From here to the magic_len equ
627 ; must match ldlinux_magic
628 ldlinux_name: db 'LDLINUX SYS' ; Looks like this in the root dir
629 dd HEXDATE ; Hopefully unique between compiles
631 bootsignature dw 0AA55h
632 magic_len equ $-bs_magic
635 ; ===========================================================================
637 ; ===========================================================================
638 ; Start of LDLINUX.SYS
639 ; ===========================================================================
643 syslinux_banner db 0Dh, 0Ah, 'SYSLINUX ', version_str, ' ', date, ' ', 0
644 db 0Dh, 0Ah, 1Ah ; EOF if we "type" this in DOS
646 ldlinux_magic db 'LDLINUX SYS'
651 ; This area is possibly patched by the installer. It is located
652 ; immediately after the EOF + LDLINUX SYS + 4 bytes + 55 AA + alignment,
653 ; so we can find it algorithmically.
656 MaxTransfer dw 00FFh ; Absolutely maximum transfer size
661 ; Note that some BIOSes are buggy and run the boot sector at 07C0:0000
662 ; instead of 0000:7C00 and the like. We don't want to add anything
663 ; more to the boot sector, so it is written to not assume a fixed
664 ; value in CS, but we don't want to deal with that anymore from now
671 ; Tell the user we got this far
673 mov si,syslinux_banner
676 ; Remember, the boot sector loaded only the first cluster of LDLINUX.SYS.
677 ; We can really only rely on a single sector having been loaded. Hence
678 ; we should load the FAT into RAM and start chasing pointers...
682 inc dx ; DX:AX <- 64K
683 div word [bxBytesPerSec] ; sectors/64K
687 mov bx,fat_seg ; Load into fat_seg:0000
690 mov eax,[bsHidden] ; Hidden sectors
691 add edx,[bxResSectors]
693 mov ecx,[bxFATsecs] ; Sectors/FAT
695 mov ebp,ecx ; Make sure high EBP = 0
698 mov bp,si ; A full 64K moby
700 xor bx,bx ; Offset 0 in the current ES
703 jz fat_load_done ; Last moby?
704 add eax,ebp ; Advance sector count
705 mov bx,es ; Next 64K moby
708 jmp short fat_load_loop
712 ; Fine, now we have the FAT in memory. How big is a cluster, really?
713 ; Also figure out how many clusters will fit in an 8K buffer, and how
714 ; many sectors and bytes that is
716 mov edi,[bxBytesPerSec] ; Used a lot below
717 mov eax,[SecPerClust]
718 mov si,ax ; Also used a lot
720 mov [ClustSize],eax ; Bytes/cluster
722 mov ax,trackbufsize ; High bit 0
725 mov [BufSafe],ax ; # of cluster in trackbuf
729 mov [BufSafeBytes],ax
730 add ax,getcbuf ; Size of getcbuf is the same
731 mov [EndOfGetCBuf],ax ; as for trackbuf
733 ; FAT12 or FAT16? This computation is fscking ridiculous...
738 mov eax,[bsHugeSectors]
739 have_secs: add eax,[bsHidden] ; These are not included
740 sub eax,[RootDir] ; Start of root directory
741 movzx ebx,word [RootDirSize]
742 sub eax,ebx ; Subtract root directory size
744 div esi ; Convert to clusters
745 cmp ax,4086 ; FAT12 limit
748 mov byte [nextcluster+1],nextcluster_fat16-(nextcluster+2)
752 ; Patch gls_set_size so we can transfer more than one sector at a time.
754 mov byte [gls_set_size],0xe8 ; E8 = CALL NEAR
755 mov word [gls_set_size+1],do_gls_set_size-(gls_set_size+3)
756 mov byte [disk_error],0xe8
757 mov word [disk_error+1],do_disk_error-(disk_error+3)
760 ; Now we read the rest of LDLINUX.SYS. Don't bother loading the first
761 ; cluster again, though.
770 mov ax,ldlinux_len-1 ; To be on the safe side
772 div cx ; the number of clusters
773 dec ax ; We've already read one
783 ; -----------------------------------------------------------------------------
784 ; Subroutines that have to be in the first sector
785 ; -----------------------------------------------------------------------------
787 ; getfssec: Get multiple clusters from a file, given the starting cluster.
789 ; This routine makes sure the subtransfers do not cross a 64K boundary,
790 ; and will correct the situation if it does, UNLESS *sectors* cross
794 ; SI -> Starting cluster number (2-based)
795 ; CX -> Cluster count (0FFFFh = until end of file)
797 ; Returns CF=1 on EOF
800 getfragment: xor ebp,ebp ; Fragment sector count
801 movzx eax,si ; Get sector address
802 dec ax ; Convert to 0-based
804 mul dword [SecPerClust]
806 getseccnt: ; See if we can read > 1 clust
808 dec cx ; Reduce clusters left to find
813 jcxz endfragment ; Or was it the last we wanted?
814 cmp si,di ; Is file continuous?
815 jz getseccnt ; Yes, we can get
816 endfragment: clc ; Not at EOF
817 gfs_eof: pushf ; Remember EOF or not
822 mov ax,es ; Check for 64K boundaries.
827 setz dl ; DX <- 1 if full 64K segment
828 div word [bsBytesPerSec] ; How many sectors fit?
830 sub si,ax ; Compute remaining sectors
835 add eax,ebp ; EBP<31:16> == 0
836 mov bp,si ; Remaining sector count
837 jmp short gfs_getchunk
838 gfs_lastchunk: pop eax
843 jcxz gfs_return ; If we hit the count limit
844 jnc getfragment ; If we didn't hit EOF
848 ; getlinsecsr: save registers, call getlinsec, restore registers
856 ; nextcluster: Advance a cluster pointer in SI to the next cluster
857 ; pointed at in the FAT tables. CF=0 on return if end of file.
860 jmp short nextcluster_fat12 ; This gets patched
867 mov bx,si ; Multiply by 3/2
868 shr bx,1 ; CF now set if odd
871 shr si,4 ; Needed for odd only
874 cmp si,0FF0h ; Clears CF if at end of file
880 ; FAT16 decoding routine. Note that a 16-bit FAT can be up to 128K,
881 ; so we have to decide if we're in the "low" or the "high" 64K-segment...
898 ; Routine that controls how much we can transfer in one chunk. Called
899 ; from gls_set_size in getlinsec.
902 sub si,cx ; Sectors left on track
905 mov bp,si ; No more than a trackful, please!
907 cmp bp,[MaxTransfer] ; Absolute maximum transfer size
914 ; This routine captures disk errors, and tries to decide if it is
915 ; time to reduce the transfer size.
918 dec si ; Decrement the retry counter
919 jz kaboom ; If expired, croak
920 cmp si,2 ; If only 2 attempts left
922 mov al,1 ; Drop transfer size to 1
926 ja .again ; First time, just try again
927 shr al,1 ; Otherwise, try to reduce
928 adc al,0 ; the max transfer size, but not to 0
939 cmp word [Debug_Magic],0D00Dh
944 rl_checkpt equ $ ; Must be <= 8000h
946 rl_checkpt_off equ ($-$$)
948 %if rl_checkpt_off > 400h
949 %error "Sector 1 overflow"
953 ; ----------------------------------------------------------------------------
954 ; End of code and data that have to be in the first sector
955 ; ----------------------------------------------------------------------------
959 ; Let the user (and programmer!) know we got this far. This used to be
960 ; in Sector 1, but makes a lot more sense here.
966 ; Common initialization code
968 %include "cpuinit.inc"
971 ; Initialization that does not need to go into the any of the pre-load
974 ; Now set up screen parameters
977 ; Wipe the F-key area
980 mov cx,10*(1 << FILENAME_MAX_LG2)
984 ; Now, everything is "up and running"... patch kaboom for more
985 ; verbosity and using the full screen system
988 mov dword [kaboom.patch],0e9h+((kaboom2-(kaboom.patch+3)) << 8)
991 ; Compute some parameters that depend on cluster size
995 inc dx ; DX:AX <- 64K
997 mov [ClustPerMoby],eax ; Clusters/64K
1000 ; Now we're all set to start with our *real* business. First load the
1001 ; configuration file (if any) and parse it.
1003 ; In previous versions I avoided using 32-bit registers because of a
1004 ; rumour some BIOSes clobbered the upper half of 32-bit registers at
1005 ; random. I figure, though, that if there are any of those still left
1006 ; they probably won't be trying to install Linux on them...
1008 ; The code is still ripe with 16-bitisms, though. Not worth the hassle
1009 ; to take'm out. In fact, we may want to put them back if we're going
1010 ; to boot ELKS at some point.
1012 mov si,linuxauto_cmd ; Default command: "linux auto"
1014 mov cx,linuxauto_len
1017 mov di,KbdMap ; Default keymap 1:1
1025 ; Load configuration file
1031 ; Now we have the config file open
1033 call parse_config ; Parse configuration file
1036 ; Check whether or not we are supposed to display the boot prompt.
1039 cmp word [ForcePrompt],byte 0 ; Force prompt?
1041 test byte [KbdFlags],5Bh ; Caps, Scroll, Shift, Alt
1042 jz auto_boot ; If neither, default boot
1048 mov byte [FuncFlag],0 ; <Ctrl-F> not pressed
1051 ; get the very first character -- we can either time
1052 ; out, or receive a character press at this time. Some dorky BIOSes stuff
1053 ; a return in the buffer on bootup, so wipe the keyboard buffer first.
1055 clear_buffer: mov ah,1 ; Check for pending char
1058 xor ax,ax ; Get char
1060 jmp short clear_buffer
1065 jz get_char ; Timeout == 0 -> no timeout
1066 inc cx ; The first loop will happen
1067 ; immediately as we don't
1068 ; know the appropriate DX value
1074 int 1Ah ; Get time "of day"
1076 cmp dx,ax ; Has the timer advanced?
1079 loop time_loop ; If so, decrement counter
1081 jmp command_done ; Timeout!
1083 get_char_pop: pop eax ; Clear stack
1091 got_ascii: cmp al,7Fh ; <DEL> == <BS>
1096 cmp di,command_line ; Space must not be first
1098 enter_char: test byte [FuncFlag],1
1100 mov byte [FuncFlag],0
1106 .not_ctrl_f: cmp di,max_cmd_len+command_line ; Check there's space
1109 call writechr ; Echo to screen
1110 get_char_2: jmp short get_char
1111 not_ascii: mov byte [FuncFlag],0
1114 cmp al,06h ; <Ctrl-F>
1116 cmp al,08h ; Backspace
1118 backspace: cmp di,command_line ; Make sure there is anything
1119 je get_char ; to erase
1120 dec di ; Unstore one character
1121 mov si,wipe_char ; and erase it from the screen
1123 jmp short get_char_2
1126 mov byte [FuncFlag],1
1127 jmp short get_char_2
1129 ctrl_f_0: add al,10 ; <Ctrl-F>0 == F10
1136 ; AL = 0 if we get here
1143 show_help: ; AX = func key # (0 = F1, 9 = F10)
1144 shl ax,FILENAME_MAX_LG2 ; Convert to pointer
1147 cmp byte [di],NULLFILE
1148 je get_char_2 ; Undefined F-key
1161 pop di ; Command line write pointer
1163 mov byte [di],0 ; Null-terminate command line
1165 call cwritestr ; Write command line so far
1167 jmp short get_char_2
1171 mov cx,(max_cmd_len+4) >> 2
1173 jmp short load_kernel
1176 cmp di,command_line ; Did we just hit return?
1178 xor al,al ; Store a final null
1181 load_kernel: ; Load the kernel now
1183 ; First we need to mangle the kernel name the way DOS would...
1193 ; Fast-forward to first option (we start over from the beginning, since
1194 ; mangle_name doesn't necessarily return a consistent ending state.)
1199 clin_is_wsp: and al,al
1204 clin_opt_ptr: dec si ; Point to first nonblank
1205 mov [CmdOptPtr],si ; Save ptr to first option
1207 ; Now check if it is a "virtual kernel"
1215 xor si,si ; Point to first vkernel
1218 repe cmpsb ; Is this it?
1225 ; Not a "virtual kernel" - check that's OK and construct the command line
1227 cmp word [AllowImplicit],byte 0
1232 mov di,real_mode_seg
1235 mov di,cmd_line_here
1242 mov bx,exten_count << 2 ; Alternates to try
1244 ; Find the kernel on disk
1246 get_kernel: mov byte [KernelName+11],0 ; Zero-terminate filename/extension
1247 mov eax,[KernelName+8] ; Save initial extension
1248 mov [OrigKernelExt],eax
1249 .search_loop: push bx
1250 mov di,KernelName ; Search on disk
1254 mov eax,[exten_table+bx] ; Try a different extension
1255 mov [KernelName+8],eax
1262 call unmangle_name ; Get human form
1263 mov si,err_notfound ; Complain about missing kernel
1265 pop si ; KernelCName
1268 jmp abort_load ; Ask user for clue
1270 ; bad_implicit: The user entered a nonvirtual kernel name, with "implicit 0"
1272 bad_implicit: mov si,KernelName ; For the error message
1275 jmp short bad_kernel
1277 ; vk_found: We *are* using a "virtual kernel"
1284 push es ; Restore old DS
1287 push word real_mode_seg
1289 mov di,cmd_line_here
1290 mov si,VKernelBuf+vk_append
1291 mov cx,[VKernelBuf+vk_appendlen]
1293 mov [CmdLinePtr],di ; Where to add rest of cmd
1295 pop di ; DI -> KernelName
1297 mov si,VKernelBuf+vk_rname
1298 mov cx,11 ; We need ECX == CX later
1301 xor bx,bx ; Try only one version
1304 ; kernel_corrupt: Called if the kernel file does not seem healthy
1306 kernel_corrupt: mov si,err_notkernel
1309 ; This is it! We have a name (and location on the disk)... let's load
1310 ; that sucker!! First we have to decide what kind of file this is; base
1311 ; that decision on the file extension. The following extensions are
1314 ; .com - COMBOOT image
1315 ; .cbt - COMBOOT image
1316 ; .c32 - COM32 image
1318 ; .0 - PXE bootstrap program (PXELINUX only)
1319 ; .bin - Boot sector
1320 ; .bss - Boot sector, but transfer over DOS superblock (SYSLINUX only)
1321 ; .img - Floppy image (ISOLINUX only)
1323 ; Anything else is assumed to be a Linux kernel.
1329 call unmangle_name ; Get human form
1331 mov [KernelCNameLen],di
1334 mov ecx,[KernelName+8] ; Get (mangled) extension
1335 and ecx,00ffffffh ; 3 bytes only
1348 ; Otherwise Linux kernel
1351 ; Linux kernel loading code is common.
1353 %include "runkernel.inc"
1356 ; COMBOOT-loading code
1358 %include "comboot.inc"
1359 %include "com32.inc"
1362 ; Boot sector loading code
1364 %include "bootsect.inc"
1367 ; abort_check: let the user abort with <ESC> or <Ctrl-C>
1378 ac_kill: mov si,aborted_msg
1381 ; abort_load: Called by various routines which wants to print a fatal
1382 ; error message and return to the command prompt. Since this
1383 ; may happen at just about any stage of the boot process, assume
1384 ; our state is messed up, and just reset the segment registers
1385 ; and the stack forcibly.
1387 ; SI = offset (in _text) of error message to print
1390 mov ax,cs ; Restore CS = DS = ES
1394 mov sp,StackBuf-2*3 ; Reset stack
1395 mov ss,ax ; Just in case...
1397 call cwritestr ; Expects SI -> error msg
1398 al_ok: jmp enter_command ; Return to command prompt
1400 ; End of abort_check
1406 ; searchdir: Search the root directory for a pre-mangled filename in
1407 ; DS:DI. This routine is similar to the one in the boot
1408 ; sector, but is a little less Draconian when it comes to
1409 ; error handling, plus it reads the root directory in
1410 ; larger chunks than a sector at a time (which is probably
1411 ; a waste of coding effort, but I like to do things right).
1413 ; FIXME: usually we can load the entire root dir in memory,
1414 ; and files are usually at the beginning anyway. It probably
1415 ; would be worthwhile to remember if we have the first chunk
1416 ; in memory and skip the load if that (it would speed up online
1419 ; NOTE: This file considers finding a zero-length file an
1420 ; error. This is so we don't have to deal with that special
1421 ; case elsewhere in the program (most loops have the test
1426 ; SI = cluster # for the first cluster
1427 ; DX:AX = file length in bytes
1433 mov ax,[bsRootDirEnts]
1435 mov ax,[RootDirSize]
1436 mov [DirBlocksLeft],ax
1439 movzx ebp,word [DirBlocksLeft]
1446 sub [DirBlocksLeft],bp
1448 mov ax,[bsBytesPerSec]
1451 mov [EndofDirSec],ax ; End of loaded
1456 dir_test_name: cmp byte [si],0 ; Directory high water mark
1457 je dir_return ; Failed
1458 test byte [si+11],18h ; Check it really is a file
1462 mov cx,11 ; Filename = 11 bytes
1467 dir_not_this: add si,byte 32
1468 dec word [DirScanCtr]
1469 jz dir_return ; Out of it...
1470 cmp si,[EndofDirSec]
1472 add eax,ebp ; Increment linear sector number
1473 jmp short scan_group
1475 mov ax,[si+28] ; Length of file
1477 mov si,[si+26] ; Cluster pointer
1479 or bx,dx ; Sets ZF iff DX:AX is zero
1484 ; writechr: Write a single character in AL to the console without
1485 ; mangling any registers
1488 call write_serial ; write to serial port if needed
1492 mov bx,0007h ; white text on this page
1500 ; kaboom2: once everything is loaded, replace the part of kaboom
1501 ; starting with "kaboom.patch" with this part
1504 mov si,err_bootfailed
1508 int 19h ; And try once more to boot...
1509 .norge: jmp short .norge ; If int 19h returned; this is the end
1512 ; mangle_name: Mangle a DOS filename pointed to by DS:SI into a buffer pointed
1513 ; to by ES:DI; ends on encountering any whitespace
1517 mov cx,11 ; # of bytes to write
1520 cmp al,' ' ; If control or space, end
1522 cmp al,'.' ; Period -> space-fill
1529 jmp short mn_not_lower
1530 mn_is_period: mov al,' ' ; We need to space-fill
1531 mn_period_loop: cmp cx,3 ; If <= 3 characters left
1532 jbe mn_loop ; Just ignore it
1533 stosb ; Otherwise, write a period
1534 loop mn_period_loop ; Dec CX and (always) jump
1535 mn_not_uslower: cmp al,ucase_low
1539 mov bx,ucase_tab-ucase_low
1542 loop mn_loop ; Don't continue if too long
1544 mov al,' ' ; Space-fill name
1545 rep stosb ; Doesn't do anything if CX=0
1549 ; Upper-case table for extended characters; this is technically code page 865,
1550 ; but code page 437 users will probably not miss not being able to use the
1551 ; cent sign in kernel images too much :-)
1553 ; The table only covers the range 129 to 164; the rest we can deal with.
1557 ucase_tab db 154, 144, 'A', 142, 'A', 143, 128, 'EEEIII'
1558 db 142, 143, 144, 146, 146, 'O', 153, 'OUUY', 153, 154
1559 db 157, 156, 157, 158, 159, 'AIOU', 165
1562 ; unmangle_name: Does the opposite of mangle_name; converts a DOS-mangled
1563 ; filename to the conventional representation. This is needed
1564 ; for the BOOT_IMAGE= parameter for the kernel.
1565 ; NOTE: A 13-byte buffer is mandatory, even if the string is
1566 ; known to be shorter.
1568 ; DS:SI -> input mangled file name
1569 ; ES:DI -> output buffer
1571 ; On return, DI points to the first byte after the output name,
1572 ; which is set to a null byte.
1575 push si ; Save pointer to original name
1583 mov bp,di ; Position of last nonblank+1
1584 un_cb_space: loop un_copy_body
1586 mov al,'.' ; Don't save
1595 un_ce_space: loop un_copy_ext
1602 ; lower_case: Lower case a character in AL
1611 lc_1: cmp al,lcase_low
1616 mov bx,lcase_tab-lcase_low
1621 ; -----------------------------------------------------------------------------
1623 ; -----------------------------------------------------------------------------
1625 %include "getc.inc" ; getc et al
1626 %include "conio.inc" ; Console I/O
1627 %include "writestr.inc" ; String output
1628 %include "parseconfig.inc" ; High-level config file handling
1629 %include "parsecmd.inc" ; Low-level config file handling
1630 %include "bcopy32.inc" ; 32-bit bcopy
1631 %include "loadhigh.inc" ; Load a file into high memory
1632 %include "font.inc" ; VGA font stuff
1633 %include "graphics.inc" ; VGA graphics
1634 %include "highmem.inc" ; High memory sizing
1636 ; -----------------------------------------------------------------------------
1637 ; Begin data section
1638 ; -----------------------------------------------------------------------------
1640 CR equ 13 ; Carriage Return
1641 LF equ 10 ; Line Feed
1642 FF equ 12 ; Form Feed
1643 BS equ 8 ; Backspace
1646 ; Lower-case table for codepage 865
1650 lcase_tab db 135, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138
1651 db 139, 140, 141, 132, 134, 130, 145, 145, 147, 148, 149
1652 db 150, 151, 152, 148, 129, 155, 156, 155, 158, 159, 160
1653 db 161, 162, 163, 164, 164
1655 copyright_str db ' Copyright (C) 1994-', year, ' H. Peter Anvin'
1657 boot_prompt db 'boot: ', 0
1658 wipe_char db BS, ' ', BS, 0
1659 err_notfound db 'Could not find kernel image: ',0
1660 err_notkernel db CR, LF, 'Invalid or corrupt kernel image.', CR, LF, 0
1661 err_noram db 'It appears your computer has less than 488K of low ("DOS")'
1663 db 'RAM. Linux needs at least this amount to boot. If you get'
1665 db 'this message in error, hold down the Ctrl key while'
1667 db 'booting, and I will take your word for it.', CR, LF, 0
1668 err_badcfg db 'Unknown keyword in syslinux.cfg.', CR, LF, 0
1669 err_noparm db 'Missing parameter in syslinux.cfg.', CR, LF, 0
1670 err_noinitrd db CR, LF, 'Could not find ramdisk image: ', 0
1671 err_nohighmem db 'Not enough memory to load specified kernel.', CR, LF, 0
1672 err_highload db CR, LF, 'Kernel transfer failure.', CR, LF, 0
1673 err_oldkernel db 'Cannot load a ramdisk with an old kernel image.'
1675 err_notdos db ': attempted DOS system call', CR, LF, 0
1676 err_comlarge db 'COMBOOT image too large.', CR, LF, 0
1677 err_a20 db CR, LF, 'A20 gate not responding!', CR, LF, 0
1678 err_bootfailed db CR, LF, 'Boot failed: please change disks and press '
1679 db 'a key to continue.', CR, LF, 0
1680 ready_msg db 'Ready.', CR, LF, 0
1681 crlfloading_msg db CR, LF
1682 loading_msg db 'Loading ', 0
1685 aborted_msg db ' aborted.' ; Fall through to crlf_msg!
1688 crff_msg db CR, FF, 0
1689 syslinux_cfg db 'SYSLINUXCFG'
1691 ; Command line options we'd like to take a look at
1693 ; mem= and vga= are handled as normal 32-bit integer values
1694 initrd_cmd db 'initrd='
1695 initrd_cmd_len equ 7
1698 ; Config file keyword table
1700 %include "keywords.inc"
1702 ; Extensions to search for (in *reverse* order). Note that the last
1703 ; (lexically first) entry in the table is a placeholder for the original
1704 ; extension, needed for error messages. The exten_table is shifted so
1705 ; the table is 1-based; this is because a "loop" cx is used as index.
1708 OrigKernelExt: dd 0 ; Original extension
1709 db 'COM',0 ; COMBOOT (same as DOS)
1710 db 'BS ',0 ; Boot Sector
1711 db 'BSS',0 ; Boot Sector (add superblock)
1712 db 'CBT',0 ; COMBOOT (specific)
1714 exten_count equ (($-exten_table) >> 2) - 1 ; Number of alternates
1716 ; Misc initialized (data) variables
1718 %ifdef debug ; This code for debugging only
1719 debug_magic dw 0D00Dh ; Debug code sentinel
1721 AppendLen dw 0 ; Bytes in append= command
1722 KbdTimeOut dw 0 ; Keyboard timeout (if any)
1723 CmdLinePtr dw cmd_line_here ; Command line advancing pointer
1725 initrd_ptr dw 0 ; Initial ramdisk pointer/flag
1726 VKernelCtr dw 0 ; Number of registered vkernels
1727 ForcePrompt dw 0 ; Force prompt
1728 AllowImplicit dw 1 ; Allow implicit kernels
1729 SerialPort dw 0 ; Serial port base (or 0 for no serial port)
1730 VGAFontSize dw 16 ; Defaults to 16 byte font
1731 UserFont db 0 ; Using a user-specified font
1732 ScrollAttribute db 07h ; White on black (for text mode)
1734 ; Stuff for the command line; we do some trickery here with equ to avoid
1735 ; tons of zeros appended to our file and wasting space
1737 linuxauto_cmd db 'linux auto',0
1738 linuxauto_len equ $-linuxauto_cmd
1739 boot_image db 'BOOT_IMAGE='
1740 boot_image_len equ $-boot_image
1741 align 4, db 0 ; For the good of REP MOVSD
1743 default_cmd equ $+(max_cmd_len+2)
1744 ldlinux_end equ default_cmd+(max_cmd_len+1)
1745 kern_cmd_len equ ldlinux_end-command_line
1746 ldlinux_len equ ldlinux_end-ldlinux_magic
1748 ; Put the getcbuf right after the code, aligned on a sector boundary
1750 end_of_code equ (ldlinux_end-bootsec)+7C00h
1751 getcbuf equ (end_of_code + 511) & 0FE00h
1753 ; VGA font buffer at the end of memory (so loading a font works even
1754 ; in graphics mode.)
1755 vgafontbuf equ 0E000h
1757 ; This is a compile-time assert that we didn't run out of space
1759 %if (getcbuf+trackbufsize) > vgafontbuf
1760 %error "Out of memory, better reorganize something..."