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 ; ****************************************************************************
30 ; Some semi-configurable constants... change on your own risk. Most are imposed
33 max_cmd_len equ 255 ; Must be odd; 255 is the kernel limit
34 retry_count equ 6 ; How patient are we with the disk?
35 HIGHMEM_MAX equ 037FFFFFFh ; DEFAULT highest address for an initrd
36 DEFAULT_BAUD equ 9600 ; Default baud rate for serial port
37 BAUD_DIVISOR equ 115200 ; Serial port parameter
39 ; Should be updated with every release to avoid bootsector/SYS file mismatch
41 %define version_str VERSION ; Must be 4 characters long!
42 %define date DATE_STR ; Defined from the Makefile
47 ; %define debug 1 ; Uncomment to enable debugging
49 ; ID for SYSLINUX (reported to kernel)
51 syslinux_id equ 031h ; SYSLINUX (3) version 1.x (1)
53 ; Segments used by Linux
55 ; Note: the real_mode_seg is supposed to be 9000h, but some device drivers
56 ; hog some of high memory. Therefore, we load it at 7000:0000h and copy
57 ; it before starting the Linux kernel.
59 real_mode_seg equ 7000h
60 fake_setup_seg equ real_mode_seg+020h
63 resb 20h-($-$$) ; org 20h
64 kern_cmd_magic resw 1 ; 0020 Magic # for command line
65 kern_cmd_offset resw 1 ; 0022 Offset for kernel command line
66 resb 497-($-$$) ; org 497d
67 bs_setupsecs resb 1 ; 01F1 Sectors for setup code (0 -> 4)
68 bs_rootflags resw 1 ; 01F2 Root readonly flag
69 bs_syssize resw 1 ; 01F4
70 bs_swapdev resw 1 ; 01F6 Swap device (obsolete)
71 bs_ramsize resw 1 ; 01F8 Ramdisk flags, formerly ramdisk size
72 bs_vidmode resw 1 ; 01FA Video mode
73 bs_rootdev resw 1 ; 01FC Root device
74 bs_bootsign resw 1 ; 01FE Boot sector signature (0AA55h)
75 su_jump resb 1 ; 0200 0EBh
76 su_jump2 resb 1 ; 0201 Size of following header
77 su_header resd 1 ; 0202 New setup code: header
78 su_version resw 1 ; 0206 See linux/arch/i386/boot/setup.S
79 su_switch resw 1 ; 0208
80 su_setupseg resw 1 ; 020A
81 su_startsys resw 1 ; 020C
82 su_kver resw 1 ; 020E Kernel version pointer
83 su_loader resb 1 ; 0210 Loader ID
84 su_loadflags resb 1 ; 0211 Load high flag
85 su_movesize resw 1 ; 0212
86 su_code32start resd 1 ; 0214 Start of code loaded high
87 su_ramdiskat resd 1 ; 0218 Start of initial ramdisk
88 su_ramdisklen equ $ ; Length of initial ramdisk
89 su_ramdisklen1 resw 1 ; 021C
90 su_ramdisklen2 resw 1 ; 021E
91 su_bsklugeoffs resw 1 ; 0220
92 su_bsklugeseg resw 1 ; 0222
93 su_heapend resw 1 ; 0224
95 su_cmd_line_ptr resd 1 ; 0228
96 su_ramdisk_max resd 1 ; 022C
97 resb (9000h-12)-($-$$) ; The setup is up to 32K long
98 linux_stack equ $ ; 8FF4
101 cmd_line_here equ $ ; 9000 Should be out of the way
105 ; Kernel command line signature
107 CMD_MAGIC equ 0A33Fh ; Command line magic
110 ; Magic number of su_header field
112 HEADER_ID equ 'HdrS' ; HdrS (in littleendian hex)
115 ; Flags for the su_loadflags field
117 LOAD_HIGH equ 01h ; Large kernel, load high
118 CAN_USE_HEAP equ 80h ; Boot loader reports heap size
121 ; The following structure is used for "virtual kernels"; i.e. LILO-style
122 ; option labels. The options we permit here are `kernel' and `append
123 ; Since there is no room in the bottom 64K for all of these, we
124 ; stick them at vk_seg:0000 and copy them down before we need them.
126 ; Note: this structure can be added to, but it must
128 %define vk_power 7 ; log2(max number of vkernels)
129 %define max_vk (1 << vk_power) ; Maximum number of vkernels
130 %define vk_shift (16-vk_power) ; Number of bits to shift
131 %define vk_size (1 << vk_shift) ; Size of a vkernel buffer
134 vk_vname: resb 11 ; Virtual name **MUST BE FIRST!**
135 vk_rname: resb 11 ; Real name
138 vk_append: resb max_cmd_len+1 ; Command line
140 vk_end: equ $ ; Should be <= vk_size
143 %if (vk_end > vk_size) || (vk_size*max_vk > 65536)
144 %error "Too many vkernels defined, reduce vk_power"
148 ; Segment assignments in the bottom 640K
149 ; Stick to the low 512K in case we're using something like M-systems flash
150 ; which load a driver into low RAM (evil!!)
152 ; 0000h - main code/data segment (and BIOS segment)
153 ; 7000h - real_mode_seg
155 fat_seg equ 5000h ; 128K area for FAT (2x64K)
156 vk_seg equ 4000h ; Virtual kernels
157 xfer_buf_seg equ 3000h ; Bounce buffer for I/O to high mem
158 comboot_seg equ 2000h ; COMBOOT image loading zone
161 ; For our convenience: define macros for jump-over-unconditinal jumps
212 ; Macros similar to res[bwd], but which works in the code segment (after
227 ; ---------------------------------------------------------------------------
228 ; BEGIN THE BIOS/CODE/DATA SEGMENT
229 ; ---------------------------------------------------------------------------
230 absolute 4*1Eh ; In the interrupt table
236 serial_base resw 4 ; Base addresses for 4 serial ports
239 BIOS_vidrows resb 1 ; Number of screen rows
242 ; Memory below this point is reserved for the BIOS and the MBR
245 trackbuf equ $ ; Track buffer goes here
246 trackbufsize equ 16384 ; Safe size of track buffer
247 ; trackbuf ends at 5000h
251 ; Constants for the xfer_buf_seg
253 ; The xfer_buf_seg is also used to store message file buffers. We
254 ; need two trackbuffers (text and graphics), plus a work buffer
255 ; for the graphics decompressor.
257 xbs_textbuf equ 0 ; Also hard-coded, do not change
258 xbs_vgabuf equ trackbufsize
259 xbs_vgatmpbuf equ 2*trackbufsize
262 absolute 5000h ; Here we keep our BSS stuff
263 VKernelBuf: resb vk_size ; "Current" vkernel
265 AppendBuf resb max_cmd_len+1 ; append=
266 KbdMap resb 256 ; Keyboard map
267 FKeyName resb 10*16 ; File names for F-key help
268 NumBuf resb 15 ; Buffer to load number
269 NumBufEnd resb 1 ; Last byte in NumBuf
271 PartInfo resb 16 ; Partition table entry
272 E820Buf resd 5 ; INT 15:E820 data buffer
273 HiLoadAddr resd 1 ; Address pointer for high load loop
274 HighMemSize resd 1 ; End of memory pointer (bytes)
275 RamdiskMax resd 1 ; Highest address for a ramdisk
276 KernelSize resd 1 ; Size of kernel (bytes)
277 KernelName resb 12 ; Mangled name for kernel
278 ; (note the spare byte after!)
279 RootDir equ $ ; Location of root directory
282 DataArea equ $ ; Location of data area
285 FBytes equ $ ; Used by open/getc
288 RootDirSize resw 1 ; Root dir size in sectors
289 DirScanCtr resw 1 ; Used while searching directory
290 DirBlocksLeft resw 1 ; Ditto
291 EndofDirSec resw 1 ; = trackbuf+bsBytesPerSec-31
292 RunLinClust resw 1 ; Cluster # for LDLINUX.SYS
293 ClustSize resw 1 ; Bytes/cluster
294 SecPerClust resw 1 ; Same as bsSecPerClust, but a word
295 NextCluster resw 1 ; Pointer to "nextcluster" routine
296 BufSafe resw 1 ; Clusters we can load into trackbuf
297 BufSafeSec resw 1 ; = how many sectors?
298 BufSafeBytes resw 1 ; = how many bytes?
299 EndOfGetCBuf resw 1 ; = getcbuf+BufSafeBytes
300 KernelClust resw 1 ; Kernel size in clusters
301 ClustPerMoby resw 1 ; Clusters per 64K
302 FClust resw 1 ; Number of clusters in open/getc file
303 FNextClust resw 1 ; Pointer to next cluster in d:o
304 FPtr resw 1 ; Pointer to next char in buffer
305 CmdOptPtr resw 1 ; Pointer to first option on cmd line
306 KernelCNameLen resw 1 ; Length of unmangled kernel name
307 InitRDCNameLen resw 1 ; Length of unmangled initrd name
308 NextCharJump resw 1 ; Routine to interpret next print char
309 SetupSecs resw 1 ; Number of setup sectors
310 SavedSP resw 1 ; Our SP while running a COMBOOT image
311 A20Test resw 1 ; Counter for testing status of A20
312 CmdLineLen resw 1 ; Length of command line including null
313 GraphXSize resw 1 ; Width of splash screen file
314 VGAPos resw 1 ; Pointer into VGA memory
315 VGACluster resw 1 ; Cluster pointer for VGA image file
316 VGAFilePtr resw 1 ; Pointer into VGAFileBuf
318 TextAttribute resb 1 ; Text attribute for message file
319 TextPage resb 1 ; Active display page
321 CursorCol resb 1 ; Cursor column for message file
322 CursorRow resb 1 ; Cursor row for message file
324 VidCols resb 1 ; Columns on screen-1
325 VidRows resb 1 ; Rows on screen-1
327 FlowOutput resb 1 ; Outputs to assert for serial flow
328 FlowInput resb 1 ; Input bits for serial flow
329 FlowIgnore resb 1 ; Ignore input unless these bits set
330 RetryCount resb 1 ; Used for disk access retries
331 KbdFlags resb 1 ; Check for keyboard escapes
332 LoadFlags resb 1 ; Loadflags from kernel
333 A20Tries resb 1 ; Times until giving up on A20
334 FuncFlag resb 1 ; Escape sequences received from keyboard
335 DisplayMask resb 1 ; Display modes mask
336 MNameBuf resb 11 ; Generic mangled file name buffer
337 InitRD resb 11 ; initrd= mangled name
338 KernelCName resb 13 ; Unmangled kernel name
339 InitRDCName resb 13 ; Unmangled initrd name
340 TextColorReg resb 17 ; VGA color registers for text mode
341 VGAFileBuf resb 13 ; Unmangled VGA image name
343 VGAFileMBuf resb 11 ; Mangled VGA image name
347 StackBuf equ $ ; Start the stack here (grow down - 4K)
350 ; Primary entry point. Tempting as though it may be, we can't put the
351 ; initial "cli" here; the jmp opcode in the first byte is part of the
352 ; "magic number" (using the term very loosely) for the DOS superblock.
355 jmp short start ; 2 bytes
358 ; "Superblock" follows -- it's in the boot sector, so it's already
359 ; loaded and ready for us
361 bsOemName db 'SYSLINUX' ; The SYS command sets this, so...
381 bsBootSignature zb 1 ; 29h if the following fields exist
384 bsFileSysType zb 8 ; Must be FAT12 for this version
385 superblock_len equ $-superblock
387 ; Note we don't check the constraints above now; we did that at install
391 ;floppy_table equ $ ; No sense in wasting memory, overwrite start
394 cli ; No interrupts yet, please
401 mov sp,StackBuf ; Just below BSS
404 ; DS:SI may contain a partition table entry. Preserve it for us.
406 mov cl,8 ; Save partition info (CH == 0)
410 ; Now sautee the BIOS floppy info block to that it will support decent-
411 ; size transfers; the floppy block is 11 bytes and is stored in the
412 ; INT 1Eh vector (brilliant waste of resources, eh?)
414 ; Of course, if BIOSes had been properly programmed, we wouldn't have
415 ; had to waste precious boot sector space with this code.
417 ; This code no longer fits. Hope that noone really needs it anymore.
418 ; (If so, it needs serious updating.) In fact, some indications is that
419 ; this code does more harm than good with all the new kinds of drives and
422 %ifdef SUPPORT_REALLY_BROKEN_BIOSES
423 lds si,[ss:fdctab] ; DS:SI -> original
424 push ds ; Save on stack in case
425 push si ; we have to bail
431 rep movsw ; Faster to move words
433 mov ds,ax ; Now we can point DS to here, too
434 mov cl,[bsSecPerTrack] ; Patch the sector count
436 mov [fdctab+2],ax ; Segment 0
437 mov [fdctab],di ; offset floppy_block
442 ; Ready to enable interrupts, captain
446 ; The drive number and possibly partition information was passed to us
447 ; by the BIOS or previous boot loader (MBR). Current "best practice" is to
448 ; trust that rather than what the superblock contains.
450 ; Would it be better to zero out bsHidden if we don't have a partition table?
452 ; Note: di points to beyond the end of PartInfo
454 mov [bsDriveNumber],dl
455 test dl,80h ; If floppy disk (00-7F), assume no
456 jz not_harddisk ; partition table
457 test byte [di-16],7Fh ; Sanity check: "active flag" should
458 jnz no_partition ; be 00 or 80
459 lea si,[di-8] ; Partition offset (dword)
465 ; Get disk drive parameters (don't trust the superblock.) Don't do this for
466 ; floppy drives -- INT 13:08 on floppy drives will (may?) return info about
467 ; what the *drive* supports, not about the *media*. Fortunately floppy disks
468 ; tend to have a fixed, well-defined geometry which is stored in the superblock.
470 ; DL == drive # still
476 inc dh ; Contains # of heads - 1
479 mov [bsSecPerTrack],cx
483 ; Now we have to do some arithmetric to figure out where things are located.
484 ; If Micro$oft had had brains they would already have done this for us,
485 ; and stored it in the superblock at format time, but here we go,
486 ; wasting precious boot sector space again...
489 xor ax,ax ; INT 13:08 destroys ES
491 mov al,[bsFATs] ; Number of FATs (AH == 0)
492 mul word [bsFATsecs] ; Get the size of the FAT area
493 add ax,[bsHidden1] ; Add hidden sectors
495 add ax,[bsResSectors] ; And reserved sectors
498 mov [RootDir1],ax ; Location of root directory
505 mov ax,32 ; Size of a directory entry
506 mul word [bsRootDirEnts]
507 mov bx,[bsBytesPerSec]
508 add ax,bx ; Round up, not down
510 div bx ; Now we have the size of the root dir
514 mov [EndofDirSec],bx ; End of a single directory sector
517 adc word [DataArea2],byte 0
519 pop dx ; Reload root directory starting point
522 ; Now the fun begins. We have to search the root directory for
523 ; LDLINUX.SYS and load the first sector, so we have a little more
524 ; space to have fun with. Then we can go chasing through the FAT.
533 sd_nextentry: cmp byte [si],0 ; Directory high water mark
535 test byte [si+11],18h ; Must be a file
543 sd_not_file: add si,byte 32 ; Distance to next
550 dec word [DirScanCtr]
553 ; kaboom: write a message and bail out.
558 mov sp,StackBuf ; Reset stack
559 mov ds,si ; Reset data segment
560 .patch: mov si,bailmsg
561 call writestr ; Returns with AL = 0
563 int 16h ; Wait for keypress
564 int 19h ; And try once more to boot...
565 .norge: jmp short .norge ; If int 19h returned; this is the end
568 ; found_it: now we compute the location of the first sector, then
569 ; load it and JUMP (since we're almost out of space)
571 found_it: ; Note: we actually leave two words on the stack here
574 mov al,[bsSecPerClust]
575 mov bp,ax ; Load an entire cluster
576 mov bx,[si+26] ; First cluster
577 mov [RunLinClust],bx ; Save for later use
578 dec bx ; First cluster is "cluster 2"
588 repe cmpsb ; Make sure that the bootsector
589 jne kaboom ; matches LDLINUX.SYS
591 ; Done! Jump to the entry point!
593 ; Note that some BIOSes are buggy and run the boot sector at 07C0:0000
594 ; instead of 0000:7C00 and the like. We don't want to add anything
595 ; more to the boot sector, so it is written to not assume a fixed
596 ; value in CS, but we don't want to deal with that anymore from now
603 ; writestr: write a null-terminated string to the console
609 mov ah,0Eh ; Write to screen as TTY
610 mov bx,0007h ; White on black, current page
614 ; disk_error: decrement the retry count and bail if zero
616 disk_error: dec si ; SI holds the disk retry counter
621 pop ax ; <F> (AH = 0)
622 mov al,1 ; Once we fail, only transfer 1 sector
623 jmp short disk_try_again
628 ; getonesec: like getlinsec, but pre-sets the count to 1
632 ; Fall through to getlinsec
635 ; getlinsec: load a sequence of BP floppy sector given by the linear sector
636 ; number in DX:AX into the buffer at ES:BX. We try to optimize
637 ; by loading up to a whole track at a time, but the user
638 ; is responsible for not crossing a 64K boundary.
639 ; (Yes, BP is weird for a count, but it was available...)
641 ; On return, BX points to the first byte after the transferred
644 ; The "stupid patch area" gets replaced by the code
645 ; mov bp,1 ; nop ... (BD 01 00 90 90...) when installing with
648 ; Stylistic note: use "xchg" instead of "mov" when the source is a register
649 ; that is dead from that point; this saves space. However, please keep
650 ; the order to dst,src to keep things sane.
653 mov si,[bsSecPerTrack]
655 ; Dividing by sectors to get (track,sector): we may have
656 ; up to 2^18 tracks, so we need to do this in two steps
657 ; to produce a 32-bit quotient.
659 xchg cx,ax ; CX <- LSW of LBA
661 xor dx,dx ; DX:AX now == MSW of LBA
662 div si ; Obtain MSW of track #
663 xchg ax,cx ; Remainder -> MSW of new dividend
664 ; LSW of LBA -> LSW of new dividend
665 ; Quotient -> MSW of track #
666 div si ; Obtain LSW of track #, remainder
667 xchg cx,dx ; CX <- Sector index (0-based)
668 ; DX <- MSW of track #
669 div word [bsHeads] ; Convert track to head/cyl
671 ; Now we have AX = cyl, DX = head, CX = sector (0-based),
672 ; BP = sectors to transfer, SI = bsSecPerTrack,
673 ; ES:BX = data target
675 gls_nextchunk: push si ; <A> bsSecPerTrack
676 push bp ; <B> Sectors to transfer
678 __BEGIN_STUPID_PATCH_AREA:
679 sub si,cx ; Sectors left on track
682 mov bp,si ; No more than a trackful, please!
683 __END_STUPID_PATCH_AREA:
685 push ax ; <C> Cylinder #
688 push cx ; <E> Sector #
689 mov cl,6 ; Because IBM was STOOPID
690 shl ah,cl ; and thought 8 bits were enough
691 ; then thought 10 bits were enough...
692 pop cx ; <E> Sector #
693 push cx ; <E> Sector #
694 inc cx ; Sector numbers are 1-based
698 mov dl,[bsDriveNumber]
699 xchg ax,bp ; Sector to transfer count
700 ; (xchg shorter than mov)
701 mov si,retry_count ; # of times to retry a disk access
703 ; Do the disk transfer... save the registers in case we fail :(
706 push ax ; <F> Number of sectors we're transferring
707 mov ah,02h ; READ DISK
716 ; Disk access successful
718 pop bx ; <I> Buffer location
719 pop ax ; <H> No longer needed
720 pop ax ; <G> No longer needed
721 pop di ; <F> Sector transferred count
722 pop cx ; <E> Sector #
723 mov ax,di ; Reduce sector left count
724 mul word [bsBytesPerSec] ; Figure out how much to advance ptr
725 add bx,ax ; Update buffer location
728 pop bp ; <B> Sectors left to transfer
729 pop si ; <A> Number of sectors/track
730 sub bp,di ; Reduce with # of sectors just read
735 inc dx ; Next track on cyl
736 cmp dx,[bsHeads] ; Was this the last one?
738 inc ax ; If so, new cylinder
739 xor dx,dx ; First head on new cylinder
740 gls_nonewcyl: sub cx,si ; First sector on new track
741 jmp short gls_nextchunk
743 bailmsg: db 'Boot failed', 0Dh, 0Ah, 0
745 bs_checkpt equ $ ; Must be <= 7DEFh
747 bs_checkpt_off equ ($-$$)
748 %if bs_checkpt_off > 1EFh
749 %error "Boot sector overflow"
753 bs_magic equ $ ; From here to the magic_len equ
754 ; must match ldlinux_magic
755 ldlinux_name: db 'LDLINUX SYS' ; Looks like this in the root dir
756 dd HEXDATE ; Hopefully unique between compiles
758 bootsignature dw 0AA55h
759 magic_len equ $-bs_magic
762 ; ===========================================================================
764 ; ===========================================================================
765 ; Start of LDLINUX.SYS
766 ; ===========================================================================
770 syslinux_banner db 0Dh, 0Ah, 'SYSLINUX ', version_str, ' ', date, ' ', 0
771 db 0Dh, 0Ah, 1Ah ; EOF if we "type" this in DOS
773 ldlinux_magic db 'LDLINUX SYS'
781 ; Tell the user we got this far
783 mov si,syslinux_banner
786 ; Remember, the boot sector loaded only the first cluster of LDLINUX.SYS.
787 ; We can really only rely on a single sector having been loaded. Hence
788 ; we should load the FAT into RAM and start chasing pointers...
792 div word [bsBytesPerSec] ; sectors/64K
796 mov bx,fat_seg ; Load into fat_seg:0000
799 mov ax,[bsHidden1] ; Hidden sectors
801 add ax,[bsResSectors] ; plus reserved sectors = FAT
803 mov cx,[bsFATsecs] ; Sectors/FAT
808 mov bp,si ; A full 64K moby
810 xor bx,bx ; Offset 0 in the current ES
813 jz fat_load_done ; Last moby?
814 add ax,bp ; Advance sector count
816 mov bx,es ; Next 64K moby
819 jmp short fat_load_loop
823 ; Fine, now we have the FAT in memory. How big is a cluster, really?
824 ; Also figure out how many clusters will fit in an 8K buffer, and how
825 ; many sectors and bytes that is
827 mov di,[bsBytesPerSec] ; Used a lot below
829 mov al,[bsSecPerClust] ; We do this in the boot
830 xor ah,ah ; sector, too, but there
831 mov [SecPerClust],ax ; wasn't space to save it
832 mov si,ax ; Also used a lot...
834 mov [ClustSize],ax ; Bytes/cluster
839 mov [BufSafe],ax ; # of cluster in trackbuf
840 mul word [SecPerClust]
843 mov [BufSafeBytes],ax
844 add ax,getcbuf ; Size of getcbuf is the same
845 mov [EndOfGetCBuf],ax ; as for trackbuf
847 ; FAT12 or FAT16? This computation is fscking ridiculous...
854 mov ax,[bsHugeSectors]
855 mov dx,[bsHugeSectors+2]
856 have_secs: sub ax,[bsResSectors]
859 sec_fat_loop: sub ax,[bsFATsecs]
864 mov ax,[bsRootDirEnts]
865 mov bx,32 ; Smaller than shift since we
866 mul bx ; need the doubleword product
878 cmp ax,4086 ; Right value?
879 mov ax,nextcluster_fat16
881 have_fat12: mov ax,nextcluster_fat12
882 have_fat_type: mov word [NextCluster],ax
885 ; Now we read the rest of LDLINUX.SYS. Don't bother loading the first
886 ; cluster again, though.
895 mov ax,ldlinux_len-1 ; To be on the safe side
897 div cx ; the number of clusters
898 dec ax ; We've already read one
908 ; -----------------------------------------------------------------------------
909 ; Subroutines that have to be in the first sector
910 ; -----------------------------------------------------------------------------
912 ; getfssec: Get multiple clusters from a file, given the starting cluster.
914 ; This routine makes sure the subtransfers do not cross a 64K boundary,
915 ; and will correct the situation if it does, UNLESS *sectors* cross
919 ; SI -> Starting cluster number (2-based)
920 ; CX -> Cluster count (0FFFFh = until end of file)
924 getfragment: xor bp,bp ; Fragment sector count
925 mov ax,si ; Get sector address
926 dec ax ; Convert to 0-based
928 mul word [SecPerClust]
931 getseccnt: ; See if we can read > 1 clust
933 dec cx ; Reduce clusters left to find
934 mov di,si ; Predict next cluster
938 jcxz endfragment ; Or was it the last we wanted?
939 cmp si,di ; Is file continuous?
940 jz getseccnt ; Yes, we can get
941 endfragment: clc ; Not at EOF
942 gfs_eof: pushf ; Remember EOF or not
948 mov ax,es ; Check for 64K boundaries.
955 inc dx ; Full 64K segment
957 div word [bsBytesPerSec] ; How many sectors fit?
959 sub si,ax ; Compute remaining sectors
967 mov bp,si ; Remaining sector count
968 jmp short gfs_getchunk
969 gfs_lastchunk: pop dx
975 jcxz gfs_return ; If we hit the count limit
976 jnc getfragment ; If we didn't hit EOF
980 ; getlinsecsr: save registers, call getlinsec, restore registers
998 ; nextcluster: Advance a cluster pointer in SI to the next cluster
999 ; pointed at in the FAT tables (note: FAT12 assumed)
1000 ; Sets CF on return if end of file.
1002 ; The variable NextCluster gets set to the appropriate
1010 mov ax,si ; Multiply by 3/2
1012 pushf ; CF now set if odd
1017 shr si,1 ; Needed for odd only
1023 cmp si,0FF0h ; Clears CF if at end of file
1024 cmc ; But we want it SET...
1030 ; FAT16 decoding routine. Note that a 16-bit FAT can be up to 128K,
1031 ; so we have to decide if we're in the "low" or the "high" 64K-segment...
1039 mov ax,fat_seg+1000h
1052 cmp word [Debug_Magic],0D00Dh
1057 rl_checkpt equ $ ; Must be <= 8000h
1059 rl_checkpt_off equ ($-$$)
1060 %if rl_checkpt_off > 400h
1061 %error "Sector 1 overflow"
1064 ; ----------------------------------------------------------------------------
1065 ; End of code and data that have to be in the first sector
1066 ; ----------------------------------------------------------------------------
1070 ; Let the user (and programmer!) know we got this far. This used to be
1071 ; in Sector 1, but makes a lot more sense here.
1073 mov si,copyright_str
1076 ; Check that no moron is trying to boot Linux on a 286 or so. According
1077 ; to Intel, the way to check is to see if the high 4 bits of the FLAGS
1078 ; register are either all stuck at 1 (8086/8088) or all stuck at 0
1079 ; (286 in real mode), if not it is a 386 or higher. They didn't
1080 ; say how to check for a 186/188, so I *hope* it falls out as a 8086
1081 ; or 286 in this test.
1083 ; Also, provide an escape route in case it doesn't work.
1086 mov ah,02h ; Check keyboard flags
1088 mov [KbdFlags],al ; Save for boot prompt check
1089 test al,04h ; Ctrl->skip 386 check
1094 and ax,0FFFh ; Clear top 4 bits
1095 push ax ; Load into FLAGS
1097 pushf ; And load back
1099 and ax,0F000h ; Get top 4 bits
1100 cmp ax,0F000h ; If set -> 8086/8088
1105 or ax,0F000h ; Set top 4 bits
1110 and ax,0F000h ; Get top 4 bits
1111 jnz is_386 ; If not clear -> 386
1117 ; Now we know it's a 386 or higher
1119 ; Now check that there is sufficient low (DOS) memory
1122 cmp ax,(real_mode_seg+0xa00) >> 6
1130 ; Check if we're 386 (as opposed to 486+); if so we need to blank out
1131 ; the WBINVD instruction
1133 ; We check for 486 by setting EFLAGS.AC
1135 pushfd ; Save the good flags
1139 xor eax,(1 << 18) ; AC bit
1144 popfd ; Restore the original flags
1148 ; 386 - Looks like we better blot out the WBINVD instruction
1150 mov byte [try_wbinvd],0c3h ; Near RET
1154 ; Initialization that does not need to go into the any of the pre-load
1157 ; Now set up screen parameters
1160 ; Now, everything is "up and running"... patch kaboom for more
1161 ; verbosity and using the full screen system
1163 mov byte [kaboom.patch],0e9h ; JMP NEAR
1164 mov word [kaboom.patch+1],kaboom2-(kaboom.patch+3)
1167 ; Now we're all set to start with our *real* business. First load the
1168 ; configuration file (if any) and parse it.
1170 ; In previous versions I avoided using 32-bit registers because of a
1171 ; rumour some BIOSes clobbered the upper half of 32-bit registers at
1172 ; random. I figure, though, that if there are any of those still left
1173 ; they probably won't be trying to install Linux on them...
1175 ; The code is still ripe with 16-bitisms, though. Not worth the hassle
1176 ; to take'm out. In fact, we may want to put them back if we're going
1177 ; to boot ELKS at some point.
1179 mov si,linuxauto_cmd ; Default command: "linux auto"
1181 mov cx,linuxauto_len
1184 mov di,KbdMap ; Default keymap 1:1
1192 ; Load configuration file
1196 jz near no_config_file
1199 jc near end_config_file ; Config file loaded
1200 cmp ax,'de' ; DEfault
1202 cmp ax,'ap' ; APpend
1204 cmp ax,'ti' ; TImeout
1206 cmp ax,'pr' ; PRompt
1212 cmp ax,'di' ; DIsplay
1216 cmp ax,'ke' ; KErnel
1218 cmp ax,'im' ; IMplicit
1220 cmp ax,'se' ; SErial
1226 pc_default: mov di,default_cmd ; "default" command
1229 stosb ; null-terminate
1230 jmp short parse_config
1232 pc_append: cmp word [VKernelCtr],byte 0 ; "append" command
1237 pc_app1: mov [AppendLen],di
1238 jmp short parse_config
1239 pc_append_vk: mov di,VKernelBuf+vk_append ; "append" command (vkernel)
1241 sub di,VKernelBuf+vk_append
1244 cmp byte [VKernelBuf+vk_append],'-'
1246 mov di,0 ; If "append -" -> null string
1247 pc_app2: mov [VKernelBuf+vk_appendlen],di
1248 jmp short parse_config_2
1250 pc_kernel: cmp word [VKernelCtr],byte 0 ; "kernel" command
1251 je near parse_config ; ("label" section only)
1256 mov di,VKernelBuf+vk_rname
1258 jmp short parse_config_2
1260 pc_timeout: call getint ; "timeout" command
1262 mov ax,0D215h ; There are approx 1.D215h
1263 mul bx ; clock ticks per 1/10 s
1266 jmp short parse_config_2
1268 pc_display: call pc_getfile ; "display" command
1269 jz parse_config_2 ; File not found?
1270 call get_msg_file ; Load and display file
1271 parse_config_2: jmp parse_config
1273 pc_prompt: call getint ; "prompt" command
1275 mov [ForcePrompt],bx
1276 jmp short parse_config_2
1278 pc_implicit: call getint ; "implicit" command
1280 mov [AllowImplicit],bx
1281 jmp short parse_config_2
1283 pc_serial: call getint ; "serial" command
1285 push bx ; Serial port #
1290 mov [FlowControl], word 0 ; Default to no flow control
1297 call getint ; Hardware flow control?
1300 xor bx,bx ; Default -> no flow control
1302 and bh,0Fh ; FlowIgnore
1306 and bx,0F003h ; Valid bits
1307 mov [FlowControl],bx
1309 jmp short .parse_baud
1311 mov ebx,DEFAULT_BAUD ; No baud rate given
1313 pop di ; Serial port #
1315 jb parse_config_2 ; < 75 baud == bogus
1316 mov eax,BAUD_DIVISOR
1319 push ax ; Baud rate divisor
1321 ja .port_is_io ; If port > 3 then port is I/O addr
1323 mov di,[di+serial_base] ; Get the I/O port from the BIOS
1326 lea dx,[di+3] ; DX -> LCR
1327 mov al,83h ; Enable DLAB
1330 mov dx,di ; DX -> LS
1335 mov al,03h ; Disable DLAB
1336 add dx,byte 2 ; DX -> LCR
1338 in al,dx ; Read back LCR (detect missing hw)
1339 cmp al,03h ; If nothing here we'll read 00 or FF
1340 jne .serial_port_bad ; Assume serial port busted
1341 sub dx,byte 2 ; DX -> IER
1342 xor al,al ; IRQ disable
1345 add dx,byte 3 ; DX -> MCR
1347 or al,[FlowOutput] ; Assert bits
1351 mov si,syslinux_banner
1352 call write_serial_str
1353 mov si,copyright_str
1354 call write_serial_str
1356 jmp short parse_config_3
1359 mov [SerialPort], word 0
1360 jmp short parse_config_3
1370 or [FKeyMap], ax ; Mark that we have this loaded
1373 call getline ; Get filename to display
1376 shl di,4 ; Multiply number by 16
1378 call mangle_name ; Mangle file name
1379 jmp short parse_config_3
1381 pc_label: call commit_vk ; Commit any current vkernel
1382 mov di,trackbuf ; Get virtual filename
1386 mov di,VKernelBuf+vk_vname
1387 call mangle_name ; Mangle virtual name
1388 inc word [VKernelCtr] ; One more vkernel
1389 mov si,VKernelBuf+vk_vname ; By default, rname == vname
1390 mov di,VKernelBuf+vk_rname
1393 mov si,AppendBuf ; Default append==global append
1394 mov di,VKernelBuf+vk_append
1396 mov [VKernelBuf+vk_appendlen],cx
1398 jmp near parse_config_3
1400 pc_font: call pc_getfile ; "font" command
1401 jz parse_config_3 ; File not found?
1402 call loadfont ; Load and install font
1403 jmp short parse_config_3
1405 pc_kbd: call pc_getfile ; "kbd" command
1408 parse_config_3: jmp parse_config
1411 ; pc_getfile: For command line options that take file argument, this
1412 ; routine decodes the file argument and runs it through searchdir
1414 pc_getfile: mov di,trackbuf
1422 jmp searchdir ; Tailcall
1425 ; commit_vk: Store the current VKernelBuf into buffer segment
1428 cmp word [VKernelCtr],byte 0
1429 je cvk_ret ; No VKernel = return
1430 cmp word [VKernelCtr],max_vk ; Above limit?
1436 mov cx,(vk_size >> 2)
1440 rep movsd ; Copy to buffer segment
1443 cvk_overflow: mov word [VKernelCtr],max_vk ; No more than max_vk, please
1447 ; End of configuration file
1450 call commit_vk ; Commit any current vkernel
1453 ; Check whether or not we are supposed to display the boot prompt.
1456 cmp word [ForcePrompt],byte 0 ; Force prompt?
1458 test byte [KbdFlags],5Bh ; Caps, Scroll, Shift, Alt
1459 jz near auto_boot ; If neither, default boot
1465 mov byte [FuncFlag],0 ; <Ctrl-F> not pressed
1468 ; get the very first character -- we can either time
1469 ; out, or receive a character press at this time. Some dorky BIOSes stuff
1470 ; a return in the buffer on bootup, so wipe the keyboard buffer first.
1472 clear_buffer: mov ah,1 ; Check for pending char
1475 xor ax,ax ; Get char
1477 jmp short clear_buffer
1482 jz get_char ; Timeout == 0 -> no timeout
1483 inc cx ; The first loop will happen
1484 ; immediately as we don't
1485 ; know the appropriate DX value
1491 int 1Ah ; Get time "of day"
1493 cmp dx,ax ; Has the timer advanced?
1496 loop time_loop ; If so, decrement counter
1498 jmp command_done ; Timeout!
1500 get_char_pop: pop eax ; Clear stack
1508 got_ascii: cmp al,7Fh ; <DEL> == <BS>
1513 cmp di,command_line ; Space must not be first
1515 enter_char: test byte [FuncFlag],1
1517 mov byte [FuncFlag],0
1523 .not_ctrl_f: cmp di,max_cmd_len+command_line ; Check there's space
1526 call writechr ; Echo to screen
1527 get_char_2: jmp short get_char
1528 not_ascii: mov byte [FuncFlag],0
1531 cmp al,06h ; <Ctrl-F>
1533 cmp al,08h ; Backspace
1535 backspace: cmp di,command_line ; Make sure there is anything
1536 je get_char ; to erase
1537 dec di ; Unstore one character
1538 mov si,wipe_char ; and erase it from the screen
1540 jmp short get_char_2
1543 mov byte [FuncFlag],1
1544 jmp short get_char_2
1546 ctrl_f_0: add al,10 ; <Ctrl-F>0 == F10
1559 show_help: ; AX = func key # (0 = F1, 9 = F10)
1561 shl ax,4 ; Convert to x16
1565 jz get_char_2 ; Undefined F-key
1580 pop di ; Command line write pointer
1582 mov byte [di],0 ; Null-terminate command line
1584 call cwritestr ; Write command line so far
1586 jmp short get_char_2
1590 mov cx,(max_cmd_len+4) >> 2
1592 jmp short load_kernel
1595 cmp di,command_line ; Did we just hit return?
1597 xor al,al ; Store a final null
1600 load_kernel: ; Load the kernel now
1602 ; First we need to mangle the kernel name the way DOS would...
1612 ; Fast-forward to first option (we start over from the beginning, since
1613 ; mangle_name doesn't necessarily return a consistent ending state.)
1618 clin_is_wsp: and al,al
1623 clin_opt_ptr: dec si ; Point to first nonblank
1624 mov [CmdOptPtr],si ; Save ptr to first option
1626 ; Now check if it is a "virtual kernel"
1634 xor si,si ; Point to first vkernel
1637 repe cmpsb ; Is this it?
1644 ; Not a "virtual kernel" - check that's OK and construct the command line
1646 cmp word [AllowImplicit],byte 0
1651 mov di,real_mode_seg
1654 mov di,cmd_line_here
1661 mov bx,exten_count << 2 ; Alternates to try
1663 ; Find the kernel on disk
1665 get_kernel: mov byte [KernelName+11],0 ; Zero-terminate filename/extension
1666 mov eax,[KernelName+8] ; Save initial extension
1667 mov [OrigKernelExt],eax
1668 .search_loop: push bx
1669 mov di,KernelName ; Search on disk
1673 mov eax,[exten_table+bx] ; Try a different extension
1674 mov [KernelName+8],eax
1681 call unmangle_name ; Get human form
1682 mov si,err_notfound ; Complain about missing kernel
1684 pop si ; KernelCName
1687 jmp abort_load ; Ask user for clue
1689 ; bad_implicit: The user entered a nonvirtual kernel name, with "implicit 0"
1691 bad_implicit: mov si,KernelName ; For the error message
1694 jmp short bad_kernel
1696 ; vk_found: We *are* using a "virtual kernel"
1703 push es ; Restore old DS
1706 push word real_mode_seg
1708 mov di,cmd_line_here
1709 mov si,VKernelBuf+vk_append
1710 mov cx,[VKernelBuf+vk_appendlen]
1712 mov [CmdLinePtr],di ; Where to add rest of cmd
1714 pop di ; DI -> KernelName
1716 mov si,VKernelBuf+vk_rname
1717 mov cx,11 ; We need ECX == CX later
1720 xor bx,bx ; Try only one version
1723 ; kernel_corrupt: Called if the kernel file does not seem healthy
1725 kernel_corrupt: mov si,err_notkernel
1728 ; This is it! We have a name (and location on the disk)... let's load
1729 ; that sucker!! First we have to decide what kind of file this is; base
1730 ; that decision on the file extension. The following extensions are
1733 ; .COM - COMBOOT image
1734 ; .CBT - COMBOOT image
1736 ; .BSS - Boot sector, but transfer over DOS superblock
1738 ; Anything else is assumed to be a Linux kernel.
1744 call unmangle_name ; Get human form
1746 mov [KernelCNameLen],di
1749 mov ecx,[KernelName+8] ; Get (mangled) extension
1751 je near is_comboot_image
1753 je near is_comboot_image
1755 je near is_bootsector
1757 je near is_bss_sector
1758 ; Otherwise Linux kernel
1760 ; A Linux kernel consists of three parts: boot sector, setup code, and
1761 ; kernel code. The boot sector is never executed when using an external
1762 ; booting utility, but it contains some status bytes that are necessary.
1763 ; The boot sector and setup code together form exactly 5 sectors that
1764 ; should be loaded at 9000:0. The subsequent code should be loaded
1765 ; at 1000:0. For simplicity, we load the whole thing at 0F60:0, and
1766 ; copy the latter stuff afterwards.
1768 ; NOTE: In the previous code I have avoided making any assumptions regarding
1769 ; the size of a sector, in case this concept ever gets extended to other
1770 ; media like CD-ROM (not that a CD-ROM would be bloody likely to use a FAT
1771 ; filesystem, of course). However, a "sector" when it comes to Linux booting
1772 ; stuff means 512 bytes *no matter what*, so here I am using that piece
1775 ; First check that our kernel is at least 1K and less than 8M (if it is
1776 ; more than 8M, we need to change the logic for loading it anyway...)
1778 ; We used to require the kernel to be 64K or larger, but it has gotten
1779 ; popular to use the Linux kernel format for other things, which may
1787 cmp ax,1024 ; Bootsect + 1 setup sect
1789 kernel_sane: push ax
1795 ; Now start transferring the kernel
1797 push word real_mode_seg
1802 div word [ClustSize] ; # of clusters total
1803 and dx,dx ; Round up
1807 mov [KernelClust],ax
1811 mov [KernelSize+2],dx
1813 ; Now, if we transfer these straight, we'll hit 64K boundaries. Hence we
1814 ; have to see if we're loading more than 64K, and if so, load it step by
1819 div word [ClustSize]
1820 mov [ClustPerMoby],ax ; Clusters/64K
1822 ; Start by loading the bootsector/setup code, to see if we need to
1823 ; do something funky. It should fit in the first 32K (loading 64K won't
1824 ; work since we might have funny stuff up near the end of memory).
1825 ; If we have larger than 32K clusters, yes, we're hosed.
1827 call abort_check ; Check for abort key
1828 mov cx,[ClustPerMoby]
1829 shr cx,1 ; Half a moby
1830 cmp cx,[KernelClust]
1832 mov cx,[KernelClust]
1834 sub [KernelClust],cx
1836 pop si ; Cluster pointer on stack
1838 cmp word [es:bs_bootsign],0AA55h
1839 jne near kernel_corrupt ; Boot sec signature missing
1841 ; Get the BIOS' idea of what the size of high memory is.
1843 push si ; Save our cluster pointer!
1845 ; First, try INT 15:E820 (get BIOS memory map)
1849 xor ebx,ebx ; Start with first record
1850 mov es,bx ; Need ES = DS = 0 for now
1851 jmp short .do_e820 ; Skip "at end" check first time!
1852 .int_loop: and ebx,ebx ; If we're back at beginning...
1853 jz no_e820 ; ... bail; nothing found
1854 .do_e820: mov eax,0000E820h
1855 mov edx,534D4150h ; "SMAP" backwards
1863 ; Look for a memory block starting at <= 1 MB and continuing upward
1865 cmp dword [E820Buf+4], byte 0
1866 ja .int_loop ; Start >= 4 GB?
1869 jb .int_loop ; Start >= 1 MB?
1871 cmp dword [E820Buf+12], byte 0
1872 ja .huge ; Size >= 4 GB
1873 mov eax, [E820Buf+8]
1874 .huge: sub eax, edx ; Adjust size to start at 1 MB
1875 jbe .int_loop ; Completely below 1 MB?
1877 ; Now EAX contains the size of memory 1 MB...up
1878 cmp dword [E820Buf+16], byte 1
1879 jne near err_nohighmem ; High memory isn't usable memory!!!!
1883 jmp short got_highmem_add1mb ; Still need to add low 1 MB
1886 ; INT 15:E820 failed. Try INT 15:E801.
1890 mov ax,0e801h ; Query high memory (semi-recent)
1894 ja no_e801 ; > 3C00h something's wrong with this call
1895 jb e801_hole ; If memory hole we can only use low part
1898 shl eax,16 ; 64K chunks
1899 add eax,(16 << 20) ; Add first 16M
1900 jmp short got_highmem
1903 ; INT 15:E801 failed. Try INT 15:88.
1906 mov ah,88h ; Query high memory (oldest)
1908 cmp ax,14*1024 ; Don't trust memory >15M
1913 shl eax,10 ; Convert from kilobytes
1915 add eax,(1 << 20) ; First megabyte
1917 mov [HighMemSize],eax
1920 ; Construct the command line (append options have already been copied)
1923 mov si,boot_image ; BOOT_IMAGE=
1924 mov cx,boot_image_len
1926 mov si,KernelCName ; Unmangled kernel name
1927 mov cx,[KernelCNameLen]
1931 mov si,[CmdOptPtr] ; Options from user input
1932 mov cx,(kern_cmd_len+3) >> 2
1936 push ds ; DEBUG DEBUG DEBUG
1939 mov si,cmd_line_here
1945 ; Scan through the command line for anything that looks like we might be
1946 ; interested in. The original version of this code automatically assumed
1947 ; the first option was BOOT_IMAGE=, but that is no longer certain.
1949 mov si,cmd_line_here
1950 mov byte [initrd_flag],0
1951 push es ; Set DS <- real_mode_seg
1964 push es ; Save ES -> real_mode_seg
1966 pop es ; Set ES <- normal DS
1968 mov cx,initrd_cmd_len
1972 push si ; mangle_dir mangles si
1973 call mangle_name ; Mangle ramdisk name
1975 cmp byte [es:InitRD],' ' ; Null filename?
1976 seta byte [es:initrd_flag] ; Set flag if not
1977 not_initrd: pop es ; Restore ES -> real_mode_seg
1978 skip_this_opt: lodsb ; Load from command line
1982 jmp short get_next_opt
1987 cmp eax, 'norm' ; vga=normal
1989 and eax,0ffffffh ; 3 bytes
1991 cmp eax, 'ext' ; vga=ext
1994 cmp eax, 'ask' ; vga=ask
1996 call parseint ; vga=<number>
1997 jc skip_this_opt ; Not an integer
1998 vc0: mov [bs_vidmode],bx ; Set video mode
1999 jmp short skip_this_opt
2003 jc skip_this_opt ; Not an integer
2004 mov [cs:HighMemSize],ebx
2005 jmp short skip_this_opt
2007 push cs ; Restore standard DS
2009 sub si,cmd_line_here
2010 mov [CmdLineLen],si ; Length including final null
2012 ; Now check if we have a large kernel, which needs to be loaded high
2014 mov dword [RamdiskMax], HIGHMEM_MAX ; Default initrd limit
2015 cmp dword [es:su_header],HEADER_ID ; New setup code ID
2016 jne near old_kernel ; Old kernel, load low
2017 cmp word [es:su_version],0200h ; Setup code version 2.0
2018 jb near old_kernel ; Old kernel, load low
2019 cmp word [es:su_version],0201h ; Version 2.01+?
2020 jb new_kernel ; If 2.00, skip this step
2021 mov word [es:su_heapend],linux_stack ; Set up the heap
2022 or byte [es:su_loadflags],80h ; Let the kernel know we care
2023 cmp word [es:su_version],0203h ; Version 2.03+?
2024 jb new_kernel ; Not 2.03+
2025 mov eax,[es:su_ramdisk_max]
2026 mov [RamdiskMax],eax ; Set the ramdisk limit
2029 ; We definitely have a new-style kernel. Let the kernel know who we are,
2030 ; and that we are clueful
2033 mov byte [es:su_loader],syslinux_id ; Show some ID
2034 movzx ax,byte [es:bs_setupsecs] ; Variable # of setup sectors
2037 ; About to load the kernel. This is a modern kernel, so use the boot flags
2040 mov al,[es:su_loadflags]
2043 ; Load the kernel. We always load it at 100000h even if we're supposed to
2044 ; load it "low"; for a "low" load we copy it down to low memory right before
2048 mov si,KernelCName ; Print kernel name part of
2049 call cwritestr ; "Loading" message
2050 mov si,dotdot_msg ; Print dots
2053 mov eax,[HighMemSize]
2054 sub eax,100000h ; Load address
2055 cmp eax,[KernelSize]
2056 jb near no_high_mem ; Not enough high memory
2058 ; Move the stuff beyond the setup code to high memory at 100000h
2060 movzx esi,word [SetupSecs] ; Setup sectors
2061 inc esi ; plus 1 boot sector
2062 shl esi,9 ; Convert to bytes
2064 sub ecx,esi ; Number of bytes to copy
2066 shr ecx,2 ; Convert to dwords
2067 add esi,(real_mode_seg << 4) ; Pointer to source
2068 mov edi,100000h ; Copy to address 100000h
2069 call bcopy ; Transfer to high memory
2071 ; On exit EDI -> where to load the rest
2073 mov si,dot_msg ; Progress report
2077 pop ecx ; Number of bytes in the initial portion
2078 pop si ; Restore file handle/cluster pointer
2079 mov eax,[KernelSize]
2080 sub eax,ecx ; Amount of kernel left over
2081 jbe high_load_done ; Zero left (tiny kernel)
2083 call load_high ; Copy the file
2086 mov ax,real_mode_seg ; Set to real mode seg
2093 ; Now see if we have an initial RAMdisk; if so, do requisite computation
2094 ; We know we have a new kernel; the old_kernel code already will have objected
2095 ; if we tried to load initrd using an old kernel
2098 test byte [initrd_flag],1
2100 push es ; ES->real_mode_seg
2102 pop es ; We need ES==DS
2105 call unmangle_name ; Create human-readable name
2107 mov [InitRDCNameLen],di
2109 call searchdir ; Look for it in directory
2112 mov [es:su_ramdisklen1],ax ; Ram disk length
2113 mov [es:su_ramdisklen2],dx
2114 mov edx,[HighMemSize] ; End of memory
2116 mov eax,[RamdiskMax] ; Highest address allowed by kernel
2119 mov edx,eax ; Adjust to fit inside limit
2122 xor dx,dx ; Round down to 64K boundary
2123 sub edx,[es:su_ramdisklen] ; Subtract size of ramdisk
2124 xor dx,dx ; Round down to 64K boundary
2125 mov [es:su_ramdiskat],edx ; Load address
2126 call loadinitrd ; Load initial ramdisk
2127 jmp short initrd_end
2137 no_high_mem: mov si,err_nohighmem ; Error routine
2143 ; Abandon hope, ye that enter here! We do no longer permit aborts.
2145 call abort_check ; Last chance!!
2150 call vgaclearmode ; We can't trust ourselves after this
2152 ; Now, if we were supposed to load "low", copy the kernel down to 10000h
2153 ; and the real mode stuff to 90000h. We assume that all bzImage kernels are
2154 ; capable of starting their setup from a different address.
2156 mov ax,real_mode_seg
2160 ; Copy command line. Unfortunately, the kernel boot protocol requires
2161 ; the command line to exist in the 9xxxxh range even if the rest of the
2164 cli ; In case of hooked interrupts
2165 test byte [LoadFlags],LOAD_HIGH
2166 jz need_high_cmdline
2167 cmp word [fs:su_version],0202h ; Support new cmdline protocol?
2168 jb need_high_cmdline
2169 ; New cmdline protocol
2170 ; Store 32-bit (flat) pointer to command line
2171 mov dword [fs:su_cmd_line_ptr],(real_mode_seg << 4) + cmd_line_here
2172 jmp short in_proper_place
2176 ; Copy command line up to 90000h
2180 mov si,cmd_line_here
2182 mov [fs:kern_cmd_magic],word CMD_MAGIC ; Store magic
2183 mov [fs:kern_cmd_offset],di ; Store pointer
2187 shr cx,2 ; Convert to dwords
2193 test byte [LoadFlags],LOAD_HIGH
2194 jnz in_proper_place ; If high load, we're done
2197 ; Loading low; we can't assume it's safe to run in place.
2199 ; Copy real_mode stuff up to 90000h
2204 inc cx ; Setup + boot sector
2205 shl cx,7 ; Sectors -> dwords
2208 fs rep movsd ; Copy setup + boot sector
2210 ; Some kernels in the 1.2 ballpark but pre-bzImage have more than 4
2211 ; setup sectors, but the boot protocol had not yet been defined. They
2212 ; rely on a signature to figure out if they need to copy stuff from
2213 ; the "protected mode" kernel area. Unfortunately, we used that area
2214 ; as a transfer buffer, so it's going to find the signature there.
2215 ; Hence, zero the low 32K beyond the setup area.
2218 inc di ; Setup + boot sector
2219 mov cx,32768/512 ; Sectors/32K
2220 sub cx,di ; Remaining sectors
2221 shl di,9 ; Sectors -> bytes
2222 shl cx,7 ; Sectors -> dwords
2224 rep stosd ; Clear region
2226 ; Copy the kernel down to the "low" location
2228 mov ecx,[KernelSize]
2229 add ecx,3 ; Round upwards
2230 shr ecx,2 ; Bytes -> dwords
2236 ; Now everything is where it needs to be...
2238 ; When we get here, es points to the final segment, either
2239 ; 9000h or real_mode_seg
2244 ; If the default root device is set to FLOPPY (0000h), change to
2247 cmp word [es:bs_rootdev],byte 0
2249 mov word [es:bs_rootdev],0200h
2252 ; Copy the disk table to high memory, then re-initialize the floppy
2255 ; This needs to be moved before the copy
2266 mov [fdctab1],di ; Save new floppy tab pos
2275 ; Linux wants the floppy motor shut off before starting the kernel,
2276 ; at least bootsect.S seems to imply so
2283 ; If we're debugging, wait for a keypress so we can read any debug messages
2290 ; Set up segment registers and the Linux real-mode stack
2291 ; Note: es == the real mode segment
2301 ; We're done... now RUN THAT KERNEL!!!!
2302 ; Setup segment == real mode segment + 020h; we need to jump to offset
2303 ; zero in the real mode segment.
2311 ; Load an older kernel. Older kernels always have 4 setup sectors, can't have
2312 ; initrd, and are always loaded low.
2315 test byte [initrd_flag],1 ; Old kernel can't have initrd
2317 mov si,err_oldkernel
2320 mov word [SetupSecs],4 ; Always 4 setup sectors
2321 mov byte [LoadFlags],0 ; Always low
2325 ; Load a COMBOOT image. A COMBOOT image is basically a DOS .COM file,
2326 ; except that it may, of course, not contain any DOS system calls. We
2327 ; do, however, allow the execution of INT 20h to return to SYSLINUX.
2331 jnz comboot_too_large
2332 cmp ax,0ff00h ; Max size in bytes
2333 jae comboot_too_large
2336 ; Set up the DOS vectors in the IVT (INT 20h-3fh)
2338 mov dword [4*0x20],comboot_return ; INT 20h vector
2339 mov eax,comboot_bogus
2341 mov cx,31 ; All remaining DOS vectors
2347 mov bx,100h ; Load at <seg>:0100h
2349 mov cx,[ClustPerMoby] ; Absolute maximum # of clusters
2353 mov cx,64 ; 256 bytes (size of PSP)
2354 xor eax,eax ; Clear PSP
2357 mov word [es:0], 020CDh ; INT 20h instruction
2358 ; First non-free paragraph
2359 mov word [es:02h], comboot_seg+1000h
2361 ; Copy the command line from high memory
2362 mov cx,125 ; Max cmdline len (minus space and CR)
2364 mov di,081h ; Offset in PSP for command line
2365 mov al,' ' ; DOS command lines begin with a space
2368 comboot_cmd_cp: lodsb
2373 comboot_end_cmd: mov al,0Dh ; CR after last character
2375 mov al,126 ; Include space but not CR
2377 mov [es:80h], al ; Store command line length
2379 call vgaclearmode ; Reset video
2385 push word 0 ; Return to address 0 -> exit
2387 jmp comboot_seg:100h ; Run it
2389 ; Looks like a COMBOOT image but too large
2393 cb_enter: jmp enter_command
2395 ; Proper return vector
2396 comboot_return: cli ; Don't trust anyone
2406 ; Attempted to execute DOS system call
2407 comboot_bogus: cli ; Don't trust anyone
2422 ; Load a boot sector
2425 ; Transfer zero bytes
2427 jmp short load_bootsec
2429 ; Transfer the superblock
2430 push word superblock_len
2434 mov bx,[bsBytesPerSec]
2438 ; Make sure we don't test this uninitialized
2439 mov [bx+trackbuf-2],dx ; Note DX == 0
2442 mov cx,1 ; 1 cluster >= 1 sector
2445 mov bx,[bsBytesPerSec]
2446 mov ax,[bx+trackbuf-2]
2447 cmp ax,0AA55h ; Boot sector signature
2451 mov di,trackbuf+(superblock-bootsec)
2452 pop cx ; Transfer count
2455 ; Okay, here we go... copy over our own boot sector and run the new one
2457 call vgaclearmode ; Reset video
2459 cli ; Point of no return
2461 mov dl,[bsDriveNumber] ; May not be in new bootsector!
2465 mov cx,[bsBytesPerSec]
2466 rep movsb ; Copy the boot sector!
2469 mov di,800h-18 ; Put partition info here
2473 pop si ; DS:SI points to partition info
2483 ; 32-bit bcopy routine for real mode
2485 ; We enter protected mode, set up a flat 32-bit environment, run rep movsd
2486 ; and then exit. IMPORTANT: This code assumes cs == ss == 0.
2488 ; This code is probably excessively anal-retentive in its handling of
2489 ; segments, but this stuff is painful enough as it is without having to rely
2490 ; on everything happening "as it ought to."
2493 bcopy_gdt: dw bcopy_gdt_size-1 ; Null descriptor - contains GDT
2494 dd bcopy_gdt ; pointer for LGDT instruction
2496 dd 0000ffffh ; Code segment, use16, readable,
2497 dd 00009b00h ; present, dpl 0, cover 64K
2498 dd 0000ffffh ; Data segment, use16, read/write,
2499 dd 008f9300h ; present, dpl 0, cover all 4G
2500 dd 0000ffffh ; Data segment, use16, read/write,
2501 dd 00009300h ; present, dpl 0, cover 64K
2502 bcopy_gdt_size: equ $-bcopy_gdt
2505 pushf ; Saves, among others, the IF flag
2514 o32 lgdt [cs:bcopy_gdt]
2517 mov cr0,eax ; Enter protected mode
2520 .in_pm: mov ax,10h ; Data segment selector
2524 mov al,18h ; "Real-mode-like" data segment
2529 a32 rep movsd ; Do our business
2531 mov es,ax ; Set to "real-mode-like"
2536 mov cr0,eax ; Disable protected mode
2539 .in_rm: xor ax,ax ; Back in real mode
2547 popf ; Re-enables interrupts
2552 ; Routines to enable and disable (yuck) A20. These routines are gathered
2553 ; from tips from a couple of sources, including the Linux kernel and
2554 ; http://www.x86.org/. The need for the delay to be as large as given here
2555 ; is indicated by Donnie Barnes of RedHat, the problematic system being an
2556 ; IBM ThinkPad 760EL.
2558 ; We typically toggle A20 twice for every 64K transferred.
2560 %define io_delay call _io_delay
2561 %define IO_DELAY_PORT 80h ; Invalid port (we hope!)
2562 %define disable_wait 32 ; How long to wait for a disable
2564 %define A20_DUNNO 0 ; A20 type unknown
2565 %define A20_NONE 1 ; A20 always on?
2566 %define A20_BIOS 2 ; A20 BIOS enable
2567 %define A20_KBC 3 ; A20 through KBC
2568 %define A20_FAST 4 ; A20 through port 92h
2570 slow_out: out dx, al ; Fall through
2572 _io_delay: out IO_DELAY_PORT,al
2573 out IO_DELAY_PORT,al
2578 mov byte [cs:A20Tries],255 ; Times to try to make this work
2587 ; If the A20 type is known, jump straight to type
2590 add bp,bp ; Convert to word offset
2591 jmp word [cs:bp+A20List]
2594 ; First, see if we are on a system with no A20 gate
2598 mov byte [cs:A20Type], A20_NONE
2603 ; Next, try the BIOS (INT 15h AX=2401h)
2606 mov byte [cs:A20Type], A20_BIOS
2608 pushf ; Some BIOSes muck with IF
2616 ; Enable the keyboard controller A20 gate
2619 mov dl, 1 ; Allow early exit
2621 jnz a20_done ; A20 live, no need to use KBC
2623 mov byte [cs:A20Type], A20_KBC ; Starting KBC command sequence
2625 mov al,0D1h ; Command write
2627 call empty_8042_uncond
2629 mov al,0DFh ; A20 on
2631 call empty_8042_uncond
2633 ; Verify that A20 actually is enabled. Do that by
2634 ; observing a word in low memory and the same word in
2635 ; the HMA until they are no longer coherent. Note that
2636 ; we don't do the same check in the disable case, because
2637 ; we don't want to *require* A20 masking (SYSLINUX should
2638 ; work fine without it, if the BIOS does.)
2648 ; Running out of options here. Final attempt: enable the "fast A20 gate"
2651 mov byte [cs:A20Type], A20_FAST ; Haven't used the KBC yet
2654 and al,~01h ; Don't accidentally reset the machine!
2662 loop .fast_wait_loop
2667 ; Oh bugger. A20 is not responding. Try frobbing it again; eventually give up
2668 ; and report failure to the user.
2672 dec byte [cs:A20Tries]
2678 ; A20 unmasked, proceed...
2680 a20_done_pop: pop cx
2685 ; This routine tests if A20 is enabled (ZF = 0). This routine
2686 ; must not destroy any register contents.
2692 mov cx,0FFFFh ; HMA = segment 0FFFFh
2694 mov cx,32 ; Loop count
2698 io_delay ; Serialize, and fix delay
2699 cmp ax,[es:A20Test+10h]
2714 add bp,bp ; Convert to word offset
2715 jmp word [cs:bp+A20DList]
2719 pushf ; Some BIOSes muck with IF
2722 jmp short a20d_snooze
2725 ; Disable the "fast A20 gate"
2731 jmp short a20d_snooze
2734 ; Disable the keyboard controller A20 gate
2737 call empty_8042_uncond
2739 out 064h, al ; Command write
2740 call empty_8042_uncond
2741 mov al,0DDh ; A20 off
2743 call empty_8042_uncond
2744 ; Wait a bit for it to take effect
2747 mov cx, disable_wait
2748 .delayloop: call a20_test
2758 ; Routine to empty the 8042 KBC controller. If dl != 0
2759 ; then we will test A20 in the loop and exit if A20 is
2770 in al, 064h ; Status port
2774 in al, 060h ; Read input
2775 jmp short empty_8042
2783 ; WBINVD instruction; gets auto-eliminated on 386 CPUs
2790 ; Load RAM disk into high memory
2793 ; su_ramdiskat - Where in memory to load
2794 ; su_ramdisklen - Size of file
2795 ; SI - initrd filehandle/cluster pointer
2798 push es ; Save ES on entry
2799 mov ax,real_mode_seg
2801 mov edi,[es:su_ramdiskat] ; initrd load address
2803 mov si,crlfloading_msg ; Write "Loading "
2805 mov si,InitRDCName ; Write ramdisk name
2807 mov si,dotdot_msg ; Write dots
2811 mov eax,[es:su_ramdisklen]
2812 call load_high ; Load the file
2815 pop es ; Restore original ES
2819 ; load_high: loads (the remainder of) a file into high memory.
2820 ; This routine prints dots for each 64K transferred, and
2821 ; calls abort_check periodically.
2823 ; The xfer_buf_seg is used as a bounce buffer.
2825 ; The input address (EDI) should be dword aligned, and the final
2826 ; dword written is padded with zeroes if necessary.
2828 ; Inputs: SI = file handle/cluster pointer
2829 ; EDI = target address in high memory
2830 ; EAX = size of remaining file in bytes
2832 ; Outputs: SI = file handle/cluster pointer
2833 ; EDI = first untouched address (not including padding)
2842 and si,si ; If SI == 0 then we have end of file
2850 push eax ; <A> Total bytes to transfer
2851 cmp eax,(1 << 16) ; Max 64K in one transfer
2856 push eax ; <B> Bytes transferred this chunk
2857 movzx ecx,word [ClustSize]
2858 div ecx ; Convert to clusters
2860 add edx,byte -1 ; Sets CF if EDX >= 1
2861 adc eax,byte 0 ; Add 1 to EAX if CF set
2863 ; Now (e)ax contains the number of clusters to get
2864 push edi ; <C> Target buffer
2867 call getfssec ; Load the data into xfer_buf_seg
2868 pop edi ; <C> Target buffer
2869 pop ecx ; <B> Byte count this round
2870 push ecx ; <B> Byte count this round
2871 push edi ; <C> Target buffer
2875 ; The last dword fractional - pad with zeroes
2876 ; Zero-padding is critical for multi-file initramfs.
2881 shr ecx,2 ; Convert to dwords
2882 push esi ; <D> File handle/cluster pointer
2883 mov esi,(xfer_buf_seg << 4) ; Source address
2884 call bcopy ; Copy to high memory
2885 pop esi ; <D> File handle/cluster pointer
2886 pop edi ; <C> Target buffer
2887 pop ecx ; <B> Byte count this round
2888 pop eax ; <A> Total bytes to transfer
2891 jnz .read_loop ; More to read...
2898 ; abort_check: let the user abort with <ESC> or <Ctrl-C>
2909 ac_kill: mov si,aborted_msg
2912 ; abort_load: Called by various routines which wants to print a fatal
2913 ; error message and return to the command prompt. Since this
2914 ; may happen at just about any stage of the boot process, assume
2915 ; our state is messed up, and just reset the segment registers
2916 ; and the stack forcibly.
2918 ; SI = offset (in _text) of error message to print
2921 mov ax,cs ; Restore CS = DS = ES
2925 mov sp,StackBuf-2*3 ; Reset stack
2926 mov ss,ax ; Just in case...
2928 call cwritestr ; Expects SI -> error msg
2929 al_ok: jmp enter_command ; Return to command prompt
2931 ; End of abort_check
2937 ; searchdir: Search the root directory for a pre-mangled filename in
2938 ; DS:DI. This routine is similar to the one in the boot
2939 ; sector, but is a little less Draconian when it comes to
2940 ; error handling, plus it reads the root directory in
2941 ; larger chunks than a sector at a time (which is probably
2942 ; a waste of coding effort, but I like to do things right).
2944 ; FIXME: usually we can load the entire root dir in memory,
2945 ; and files are usually at the beginning anyway. It probably
2946 ; would be worthwhile to remember if we have the first chunk
2947 ; in memory and skip the load if that (it would speed up online
2950 ; NOTE: This file considers finding a zero-length file an
2951 ; error. This is so we don't have to deal with that special
2952 ; case elsewhere in the program (most loops have the test
2957 ; SI = cluster # for the first cluster
2958 ; DX:AX = file length in bytes
2964 mov ax,[bsRootDirEnts]
2966 mov ax,[RootDirSize]
2967 mov [DirBlocksLeft],ax
2971 mov bp,[DirBlocksLeft]
2978 sub [DirBlocksLeft],bp
2981 mov ax,[bsBytesPerSec]
2984 mov [EndofDirSec],ax ; End of loaded
2987 push bp ; Save number of sectors
2988 push ax ; Save present location
2998 dir_test_name: cmp byte [si],0 ; Directory high water mark
2999 je dir_return ; Failed
3000 test byte [si+11],18h ; Check it really is a file
3004 mov cx,11 ; Filename = 11 bytes
3009 dir_not_this: add si,byte 32
3010 dec word [DirScanCtr]
3011 jz dir_return ; Out of it...
3012 cmp si,[EndofDirSec]
3014 add ax,bp ; Increment linear sector number
3016 jmp short scan_group
3018 mov ax,[si+28] ; Length of file
3020 mov si,[si+26] ; Cluster pointer
3022 or bx,dx ; Sets ZF iff DX:AX is zero
3027 ; loadfont: Load a .psf font file and install it onto the VGA console
3028 ; (if we're not on a VGA screen then ignore.) It is called with
3029 ; SI and DX:AX set by routine searchdir
3032 mov bx,trackbuf ; The trackbuf is >= 16K; the part
3033 mov cx,[BufSafe] ; of a PSF file we care about is no
3034 call getfssec ; more than 8K+4 bytes
3036 mov ax,[trackbuf] ; Magic number
3040 mov al,[trackbuf+2] ; File mode
3041 cmp al,5 ; Font modes 0-5 supported
3044 mov bh,byte [trackbuf+3] ; Height of font
3045 cmp bh,2 ; VGA minimum
3047 cmp bh,32 ; VGA maximum
3050 ; Copy to font buffer
3051 mov si,trackbuf+4 ; Start of font data
3052 mov [VGAFontSize],bh
3054 mov cx,(32*256) >> 2 ; Maximum size
3057 mov [UserFont], byte 1 ; Set font flag
3059 ; Fall through to use_font
3063 ; This routine activates whatever font happens to be in the
3064 ; vgafontbuf, and updates the adjust_screen data.
3065 ; Must be called with CS = DS = ES
3068 test [UserFont], byte 1 ; Are we using a user-specified font?
3069 jz adjust_screen ; If not, just do the normal stuff
3072 mov bh,[VGAFontSize]
3074 xor bl,bl ; Needed by both INT 10h calls
3075 cmp [UsingVGA], byte 1 ; Are we in graphics mode?
3080 mov cl,bh ; CX = bytes/character
3082 div cl ; Compute char rows per screen
3086 mov ax,1121h ; Set user character table
3088 mov [VidCols], byte 79 ; Always 80 bytes/line
3089 mov [TextPage], byte 0 ; Always page 0
3090 ret ; No need to call adjust_screen
3096 int 10h ; Load into VGA RAM
3099 mov ax,1103h ; Select page 0
3102 ; Fall through to adjust_screen
3105 ; adjust_screen: Set the internal variables associated with the screen size.
3106 ; This is a subroutine in case we're loading a custom font.
3109 mov al,[BIOS_vidrows]
3112 mov al,24 ; No vidrows in BIOS, assume 25
3113 ; (Remember: vidrows == rows-1)
3114 vidrows_ok: mov [VidRows],al
3116 int 10h ; Read video state
3118 dec ah ; Store count-1 (same as rows)
3123 ; loadkeys: Load a LILO-style keymap; SI and DX:AX set by searchdir
3126 and dx,dx ; Should be 256 bytes exactly
3132 mov cx,1 ; 1 cluster should be >= 256 bytes
3143 ; get_msg_file: Load a text file and write its contents to the screen,
3144 ; interpreting color codes. Is called with SI and DX:AX
3145 ; set by routine searchdir
3149 shl edx,16 ; EDX <- DX:AX (length of file)
3151 mov ax,xfer_buf_seg ; Use for temporary storage
3154 mov byte [TextAttribute],07h ; Default grey on white
3155 mov byte [DisplayMask],07h ; Display text in all modes
3158 get_msg_chunk: push edx ; EDX = length of file
3159 xor bx,bx ; == xbs_textbuf
3163 push si ; Save current cluster
3164 xor si,si ; == xbs_textbuf
3165 mov cx,[BufSafeBytes] ; Number of bytes left in chunk
3170 cmp al,1Ah ; DOS EOF?
3174 inc cl ; 01h = text mode, 02h = graphics
3175 call [NextCharJump] ; Do what shall be done
3183 jmp short get_msg_chunk
3185 add sp,byte 6 ; Drop pushed EDX, CX
3190 msg_putchar: ; Normal character
3191 cmp al,0Fh ; ^O = color code follows
3193 cmp al,0Dh ; Ignore <CR>
3195 cmp al,0Ah ; <LF> = newline
3197 cmp al,0Ch ; <FF> = clear screen
3199 cmp al,19h ; <EM> = return to text mode
3201 cmp al,18h ; <CAN> = VGA filename follows
3204 cmp al,10h ; 10h to 17h are mode controls
3205 jae near msg_modectl
3208 msg_normal: call write_serial_displaymask ; Write to serial port
3209 test [DisplayMask],cl
3210 jz msg_ignore ; Not screen
3212 mov ah,09h ; Write character/attribute
3213 mov cx,1 ; One character only
3214 int 10h ; Write to screen
3218 ja msg_line_wrap ; Screen wraparound
3221 msg_gotoxy: mov bh,[TextPage]
3223 mov ah,02h ; Set cursor position
3226 msg_ctrl_o: ; ^O = color code follows
3227 mov word [NextCharJump],msg_setbg
3229 msg_newline: ; Newline char or end of line
3231 call write_serial_str_displaymask
3232 msg_line_wrap: ; Screen wraparound
3233 test [DisplayMask],cl
3235 mov byte [CursorCol],0
3241 jmp short msg_gotoxy
3242 msg_scroll: xor cx,cx ; Upper left hand corner
3244 mov [CursorRow],dh ; New cursor at the bottom
3245 mov bh,[ScrollAttribute]
3246 mov ax,0601h ; Scroll up one line
3248 jmp short msg_gotoxy
3249 msg_formfeed: ; Form feed character
3251 call write_serial_str_displaymask
3252 test [DisplayMask],cl
3255 mov [CursorDX],cx ; Upper lefthand corner
3257 mov bh,[TextAttribute]
3258 mov ax,0600h ; Clear screen region
3260 jmp short msg_gotoxy
3261 msg_setbg: ; Color background character
3265 test [DisplayMask],cl
3267 mov [TextAttribute],al
3269 mov word [NextCharJump],msg_setfg
3271 msg_setfg: ; Color foreground character
3274 test [DisplayMask],cl
3276 or [TextAttribute],al ; setbg set foreground to 0
3278 jmp short msg_putcharnext
3280 mov word [NextCharJump],msg_filename
3282 jmp short msg_setvgafileptr
3285 mov byte [TextAttribute],07h ; Default attribute
3287 mov word [NextCharJump],msg_putchar
3290 msg_filename: ; Getting VGA filename
3291 cmp al,0Ah ; <LF> = end of filename
3294 jbe msg_ret ; Ignore space/control char
3296 cmp di,VGAFileBufEnd
3298 mov [di],al ; Can't use stosb (DS:)
3306 jmp short msg_initvars
3319 jz msg_putcharnext ; Not there
3323 ; Subroutine to initialize variables, also needed
3324 ; after loading a graphics file
3328 mov ah,03h ; Read cursor position
3332 jmp short msg_putcharnext ; Initialize state machine
3336 mov [DisplayMask],al
3337 jmp short msg_putcharnext
3340 ; write_serial: If serial output is enabled, write character on serial port
3341 ; write_serial_displaymask: d:o, but ignore if DisplayMask & 04h == 0
3343 write_serial_displaymask:
3344 test byte [DisplayMask], 04h
3355 ; Wait for space in transmit register
3356 lea dx,[bx+5] ; DX -> LSR
3361 ; Wait for input flow control
3369 xchg dx,bx ; DX -> THR
3371 call slow_out ; Send data
3377 ; write_serial_str: write_serial for strings
3378 ; write_serial_str_displaymask: d:o, but ignore if DisplayMask & 04h == 0
3380 write_serial_str_displaymask:
3381 test byte [DisplayMask], 04h
3382 jz write_serial_str.end
3393 ; writechr: Write a single character in AL to the console without
3394 ; mangling any registers
3397 call write_serial ; write to serial port if needed
3401 mov bx,0007h ; white text on this page
3408 ; crlf: Print a newline
3410 crlf: mov si,crlf_msg
3414 ; cwritestr: write a null-terminated string to the console, saving
3415 ; registers on entry.
3432 ; writehex[248]: Write a hex number in (AL, AX, EAX) to the console
3439 jmp short writehex_common
3445 jmp short writehex_common
3458 .high: add al,'A'-10
3459 .ischar: call writechr
3480 ; pollchar: check if we have an input character pending (ZF = 0)
3484 mov ah,1 ; Poll keyboard
3486 jnz .done ; Keyboard response
3489 jz .done ; No serial port -> no input
3490 add dx,byte 5 ; DX -> LSR
3492 test al,1 ; ZF = 0 if data pending
3495 mov ah,[FlowIgnore] ; Required status bits
3500 dec al ; Set ZF = 0 if equal
3505 ; getchar: Read a character from keyboard or serial port
3508 .again: mov ah,1 ; Poll keyboard
3510 jnz .kbd ; Keyboard input?
3514 lea dx,[bx+5] ; DX -> LSR
3524 .serial: xor ah,ah ; Avoid confusion
3525 xchg dx,bx ; Data port
3528 .kbd: xor ax,ax ; Get keyboard input
3532 mov bx,KbdMap ; Convert character sets
3538 ; kaboom2: once everything is loaded, replace the part of kaboom
3539 ; starting with "kaboom.patch" with this part
3542 mov si,err_bootfailed
3546 int 19h ; And try once more to boot...
3547 .norge: jmp short .norge ; If int 19h returned; this is the end
3550 ; open,getc: Load a file a character at a time for parsing in a manner
3551 ; similar to the C library getc routine. Only one simultaneous
3552 ; use is supported. Note: "open" trashes the trackbuf.
3554 ; open: Input: mangled filename in DS:DI
3555 ; Output: ZF set on file not found or zero length
3557 ; getc: Output: CF set on end of file
3558 ; Character loaded in AL
3570 div word [ClustSize]
3571 mov [FClust],ax ; Number of clusters
3572 mov [FNextClust],si ; Cluster pointer
3573 mov ax,[EndOfGetCBuf] ; Pointer at end of buffer ->
3574 mov [FPtr],ax ; nothing loaded yet
3575 popf ; Restore no ZF
3579 stc ; If we exit here -> EOF
3583 cmp si,[EndOfGetCBuf]
3585 ; Buffer empty -- load another set
3590 getc_oksize: sub [FClust],cx ; Reduce remaining clusters
3592 push es ; ES may be != DS, save old ES
3597 call getfssec ; Load a trackbuf full of data
3598 mov [FNextClust],si ; Store new next pointer
3599 pop si ; SI -> newly loaded data
3601 getc_loaded: lodsb ; Load a byte
3602 mov [FPtr],si ; Update next byte pointer
3603 dec dword [FBytes] ; Update bytes left counter
3608 ; ungetc: Push a character (in AL) back into the getc buffer
3609 ; Note: if more than one byte is pushed back, this may cause
3610 ; bytes to be written below the getc buffer boundary. If there
3611 ; is a risk for this to occur, the getcbuf base address should
3623 ; skipspace: Skip leading whitespace using "getc". If we hit end-of-line
3624 ; or end-of-file, return with carry set; ZF = true of EOF
3625 ; ZF = false for EOLN; otherwise CF = ZF = 0.
3627 ; Otherwise AL = first character after whitespace
3630 skipspace_loop: call getc
3632 cmp al,1Ah ; DOS EOF
3639 skipspace_eof: cmp al,al ; Set ZF
3642 skipspace_eoln: add al,0FFh ; Set CF, clear ZF
3646 ; getkeyword: Get a keyword from the current "getc" file; only the two
3647 ; first characters are considered significant.
3649 ; Lines beginning with ASCII characters 33-47 are treated
3650 ; as comments and ignored; other lines are checked for
3651 ; validity by scanning through the keywd_table.
3653 ; The keyword and subsequent whitespace is skipped.
3655 ; On EOF, CF = 1; otherwise, CF = 0, AL:AH = lowercase char pair
3658 gkw_find: call skipspace
3659 jz gkw_eof ; end of file
3660 jc gkw_find ; end of line: try again
3662 jb gkw_skipline ; skip comment line
3667 mov bh,al ; Move character pair into BL:BH
3668 or bx,2020h ; Lower-case it
3672 jz gkw_badline ; Bad keyword, write message
3684 jc gkw_missingpar ; Missing parameter after keyword
3685 call ungetc ; Return character to buffer
3686 clc ; Successful return
3688 gkw_eof: ret ; CF = 1 on all EOF conditions
3689 gkw_missingpar: pop ax
3693 gkw_badline_pop: pop ax
3694 gkw_badline: mov si,err_badcfg
3697 gkw_skipline: cmp al,10 ; Scan for LF
3701 jmp short gkw_skipline
3704 ; getint: Load an integer from the getc file.
3705 ; Return CF if error; otherwise return integer in EBX
3709 gi_getnum: cmp di,NumBufEnd ; Last byte in NumBuf
3718 call ungetc ; Unget non-numeric
3719 gi_loaded: mov byte [di],0
3721 ; Fall through to parseint
3724 ; parseint: Convert an integer to a number in EBX
3725 ; Get characters from string in DS:SI
3726 ; Return CF on error
3727 ; DS:SI points to first character after number
3729 ; Syntaxes accepted: [-]dec, [-]0+oct, [-]0x+hex, val+K, val+M
3735 xor eax,eax ; Current digit (keep eax == al)
3736 mov ebx,eax ; Accumulator
3738 xor bp,bp ; Used for negative flag
3742 xor bp,1 ; Set unary minus flag
3750 mov cl,10 ; Base = decimal
3751 jmp short pi_foundbase
3755 jb pi_km ; Value is zero
3756 or al,20h ; Downcase
3761 mov cl,8 ; Base = octal
3762 jmp short pi_foundbase
3764 mov al,'0' ; No numeric value accrued yet
3765 mov cl,16 ; Base = hex
3768 jc pi_km ; Not a (hex) digit
3770 jae pi_km ; Invalid for base
3771 imul ebx,ecx ; Multiply accumulated by base
3772 add ebx,eax ; Add current digit
3774 jmp short pi_foundbase
3776 dec si ; Back up to last non-numeric
3786 neg ebx ; Value was negative
3794 pi_isk: shl ebx,10 ; x 2^10
3796 pi_ism: shl ebx,20 ; x 2^20
3800 ; unhexchar: Convert a hexadecimal digit in AL to the equivalent number;
3801 ; return CF=1 if not a hex digit
3805 jb uxc_ret ; If failure, CF == 1 already
3808 sub al,'0' ; CF <- 0
3810 uxc_1: or al,20h ; upper case -> lower case
3812 jb uxc_ret ; If failure, CF == 1 already
3815 sub al,'a'-10 ; CF <- 0
3822 ; getline: Get a command line, converting control characters to spaces
3823 ; and collapsing streches to one; a space is appended to the
3824 ; end of the string, unless the line is empty.
3825 ; The line is terminated by ^J, ^Z or EOF and is written
3826 ; to ES:DI. On return, DI points to first char after string.
3827 ; CF is set if we hit EOF.
3831 mov dl,1 ; Empty line -> empty string.
3835 gl_fillloop: push dx
3845 jmp short gl_fillloop
3847 je gl_ret ; CF clear!
3851 jnz gl_fillloop ; Ignore multiple spaces
3852 mov al,' ' ; Ctrl -> space
3855 gl_eoln: clc ; End of line is not end of file
3858 gl_ret: pushf ; We want the last char to be space!
3868 ; mangle_name: Mangle a DOS filename pointed to by DS:SI into a buffer pointed
3869 ; to by ES:DI; ends on encountering any whitespace
3873 mov cx,11 ; # of bytes to write
3876 cmp al,' ' ; If control or space, end
3878 cmp al,'.' ; Period -> space-fill
3885 jmp short mn_not_lower
3886 mn_is_period: mov al,' ' ; We need to space-fill
3887 mn_period_loop: cmp cx,3 ; If <= 3 characters left
3888 jbe mn_loop ; Just ignore it
3889 stosb ; Otherwise, write a period
3890 loop mn_period_loop ; Dec CX and (always) jump
3891 mn_not_uslower: cmp al,ucase_low
3895 mov bx,ucase_tab-ucase_low
3898 loop mn_loop ; Don't continue if too long
3900 mov al,' ' ; Space-fill name
3901 rep stosb ; Doesn't do anything if CX=0
3905 ; Upper-case table for extended characters; this is technically code page 865,
3906 ; but code page 437 users will probably not miss not being able to use the
3907 ; cent sign in kernel images too much :-)
3909 ; The table only covers the range 129 to 164; the rest we can deal with.
3913 ucase_tab db 154, 144, 'A', 142, 'A', 143, 128, 'EEEIII'
3914 db 142, 143, 144, 146, 146, 'O', 153, 'OUUY', 153, 154
3915 db 157, 156, 157, 158, 159, 'AIOU', 165
3918 ; unmangle_name: Does the opposite of mangle_name; converts a DOS-mangled
3919 ; filename to the conventional representation. This is needed
3920 ; for the BOOT_IMAGE= parameter for the kernel.
3921 ; NOTE: A 13-byte buffer is mandatory, even if the string is
3922 ; known to be shorter.
3924 ; DS:SI -> input mangled file name
3925 ; ES:DI -> output buffer
3927 ; On return, DI points to the first byte after the output name,
3928 ; which is set to a null byte.
3931 push si ; Save pointer to original name
3939 mov bp,di ; Position of last nonblank+1
3940 un_cb_space: loop un_copy_body
3942 mov al,'.' ; Don't save
3951 un_ce_space: loop un_copy_ext
3958 ; lower_case: Lower case a character in AL
3967 lc_1: cmp al,lcase_low
3972 mov bx,lcase_tab-lcase_low
3977 ; ----------------------------------------------------------------------------------
3978 ; VGA splash screen code
3979 ; ----------------------------------------------------------------------------------
3983 ; Display a graphical splash screen.
3987 ; SI = cluster/socket pointer
3993 ; This is a cheap and easy way to make sure the screen is
3994 ; cleared in case we were in graphics mode already
4000 mov ax,xfer_buf_seg ; Use as temporary storage
4004 call vgagetchunk ; Get the first chunk
4006 ; The header WILL be in the first chunk.
4007 cmp dword [es:xbs_vgabuf],0x1413f33d ; Magic number
4008 .error_nz: jne near .error
4009 mov ax,[es:xbs_vgabuf+4]
4012 mov dx,xbs_vgabuf+8 ; Color map offset
4013 mov ax,1012h ; Set RGB registers
4014 xor bx,bx ; First register number
4015 mov cx,16 ; 16 registers
4019 mov ax,[es:xbs_vgabuf+6] ; Number of pixel rows
4020 mov dx,[VGAFontSize]
4024 xor dx,dx ; Set column to 0
4033 int 10h ; Set cursor below image
4035 mov cx,[es:xbs_vgabuf+6] ; Number of graphics rows
4037 mov si,xbs_vgabuf+8+3*16 ; Beginning of pixel data
4043 mov di,xbs_vgatmpbuf ; Row buffer
4044 call rledecode ; Decode one row
4046 mov si,xbs_vgatmpbuf
4051 rep stosd ; Clear rest of row
4052 mov di,0A000h ; VGA segment
4056 call packedpixel2vga
4057 add word [VGAPos],byte 80 ; Advance to next pixel row
4070 ; Decode a pixel row in RLE16 format.
4074 ; ES:DI -> output (packed pixel)
4077 shl esi,1 ; Nybble pointer
4078 xor dl,dl ; Last pixel
4082 je .run ; Start of run sequence
4126 cmp si,xbs_vgabuf+trackbufsize ; Chunk overrun
4129 mov si,xbs_vgabuf ; Start at beginning of buffer
4136 ; Get a new trackbufsize chunk of VGA image data
4138 ; On input, ES is assumed to point to the buffer segment.
4144 jz .eof ; EOF overrun, not much to do...
4146 mov cx,[BufSafe] ; One trackbuf worth of data
4152 .noteof: mov [VGACluster],si
4159 ; Convert packed-pixel to VGA bitplanes
4161 ; FS:SI -> packed pixel string
4162 ; BP -> pixel count (multiple of 8)
4166 mov dx,3C4h ; VGA Sequencer Register select port
4167 mov al,2 ; Sequencer mask
4168 out dx,al ; Select the sequencer mask
4169 inc dx ; VGA Sequencer Register data port
4181 rcl ch,1 ; VGA is bigendian. Sigh.
4197 ; Enable VGA graphics, if possible; return ZF=1 on success
4198 ; DS must be set to the base segment; ES is set to DS.
4203 mov ax,1A00h ; Get video card and monitor
4206 sub bl, 7 ; BL=07h and BL=08h OK
4209 ; mov bx,TextColorReg
4210 ; mov dx,1009h ; Read color registers
4212 mov ax,0012h ; Set mode = 640x480 VGA 16 colors
4215 mov ax,1002h ; Write color registers
4217 mov [UsingVGA], byte 1
4219 call use_font ; Set graphics font/data
4220 mov byte [ScrollAttribute], 00h
4228 ; Disable VGA graphics. It is not safe to assume any value
4238 cmp [UsingVGA], byte 1
4240 mov ax,0003h ; Return to normal video mode
4242 ; mov dx,TextColorReg ; Restore color registers
4245 mov [UsingVGA], byte 0
4247 call use_font ; Restore text font/data
4248 mov byte [ScrollAttribute], 07h
4256 ; vgashowcursor/vgahidecursor:
4257 ; If VGA graphics is enabled, draw a cursor/clear a cursor
4262 jmp short vgacursorcommon
4267 cmp [UsingVGA], byte 1
4278 ; Map colors to consecutive DAC registers
4279 linear_color db 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 0
4282 ; ----------------------------------------------------------------------------------
4283 ; Begin data section
4284 ; ----------------------------------------------------------------------------------
4286 CR equ 13 ; Carriage Return
4287 LF equ 10 ; Line Feed
4288 FF equ 12 ; Form Feed
4289 BS equ 8 ; Backspace
4292 ; Lower-case table for codepage 865
4296 lcase_tab db 135, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138
4297 db 139, 140, 141, 132, 134, 130, 145, 145, 147, 148, 149
4298 db 150, 151, 152, 148, 129, 155, 156, 155, 158, 159, 160
4299 db 161, 162, 163, 164, 164
4301 copyright_str db ' Copyright (C) 1994-', year, ' H. Peter Anvin'
4303 boot_prompt db 'boot: ', 0
4304 wipe_char db BS, ' ', BS, 0
4305 err_notfound db 'Could not find kernel image: ',0
4306 err_notkernel db CR, LF, 'Invalid or corrupt kernel image.', CR, LF, 0
4307 err_not386 db 'It appears your computer uses a 286 or lower CPU.'
4309 db 'You cannot run Linux unless you have a 386 or higher CPU'
4311 db 'in your machine. If you get this message in error, hold'
4313 db 'down the Ctrl key while booting, and I will take your'
4315 db 'word for it.', CR, LF, 0
4316 err_noram db 'It appears your computer has less than 488K of low ("DOS")'
4318 db 'RAM. Linux needs at least this amount to boot. If you get'
4320 db 'this message in error, hold down the Ctrl key while'
4322 db 'booting, and I will take your word for it.', CR, LF, 0
4323 err_badcfg db 'Unknown keyword in syslinux.cfg.', CR, LF, 0
4324 err_noparm db 'Missing parameter in syslinux.cfg.', CR, LF, 0
4325 err_noinitrd db CR, LF, 'Could not find ramdisk image: ', 0
4326 err_nohighmem db 'Not enough memory to load specified kernel.', CR, LF, 0
4327 err_highload db CR, LF, 'Kernel transfer failure.', CR, LF, 0
4328 err_oldkernel db 'Cannot load a ramdisk with an old kernel image.'
4330 err_notdos db ': attempted DOS system call', CR, LF, 0
4331 err_comlarge db 'COMBOOT image too large.', CR, LF, 0
4332 err_bootsec db 'Invalid or corrupt boot sector image.', CR, LF, 0
4333 err_a20 db CR, LF, 'A20 gate not responding!', CR, LF, 0
4334 err_bootfailed db CR, LF, 'Boot failed: please change disks and press '
4335 db 'a key to continue.', CR, LF, 0
4336 ready_msg db 'Ready.', CR, LF, 0
4337 crlfloading_msg db CR, LF
4338 loading_msg db 'Loading ', 0
4341 aborted_msg db ' aborted.' ; Fall through to crlf_msg!
4342 crlf_msg db CR, LF, 0
4343 crff_msg db CR, FF, 0
4344 syslinux_cfg db 'SYSLINUXCFG'
4346 ; Command line options we'd like to take a look at
4348 ; mem= and vga= are handled as normal 32-bit integer values
4349 initrd_cmd db 'initrd='
4350 initrd_cmd_len equ 7
4352 ; Config file keyword table
4355 keywd_table db 'ap' ; append
4378 ; Extensions to search for (in *reverse* order). Note that the last
4379 ; (lexically first) entry in the table is a placeholder for the original
4380 ; extension, needed for error messages. The exten_table is shifted so
4381 ; the table is 1-based; this is because a "loop" cx is used as index.
4384 OrigKernelExt: dd 0 ; Original extension
4385 db 'COM',0 ; COMBOOT (same as DOS)
4386 db 'BS ',0 ; Boot Sector
4387 db 'BSS',0 ; Boot Sector (add superblock)
4388 db 'CBT',0 ; COMBOOT (specific)
4390 exten_count equ (($-exten_table) >> 2) - 1 ; Number of alternates
4392 ; Misc initialized (data) variables
4394 %ifdef debug ; This code for debugging only
4395 debug_magic dw 0D00Dh ; Debug code sentinel
4397 AppendLen dw 0 ; Bytes in append= command
4398 KbdTimeOut dw 0 ; Keyboard timeout (if any)
4399 FKeyMap dw 0 ; Bitmap for F-keys loaded
4400 CmdLinePtr dw cmd_line_here ; Command line advancing pointer
4402 initrd_ptr dw 0 ; Initial ramdisk pointer/flag
4403 VKernelCtr dw 0 ; Number of registered vkernels
4404 ForcePrompt dw 0 ; Force prompt
4405 AllowImplicit dw 1 ; Allow implicit kernels
4406 SerialPort dw 0 ; Serial port base (or 0 for no serial port)
4407 A20List dw a20_dunno, a20_none, a20_bios, a20_kbc, a20_fast
4408 A20DList dw a20d_dunno, a20d_none, a20d_bios, a20d_kbc, a20d_fast
4409 A20Type dw A20_DUNNO ; A20 type unknown
4410 VGAFontSize dw 16 ; Defaults to 16 byte font
4411 UserFont db 0 ; Using a user-specified font
4412 ScrollAttribute db 07h ; White on black (for text mode)
4414 ; Stuff for the command line; we do some trickery here with equ to avoid
4415 ; tons of zeros appended to our file and wasting space
4417 linuxauto_cmd db 'linux auto',0
4418 linuxauto_len equ $-linuxauto_cmd
4419 boot_image db 'BOOT_IMAGE='
4420 boot_image_len equ $-boot_image
4421 align 4, db 0 ; For the good of REP MOVSD
4423 default_cmd equ $+(max_cmd_len+2)
4424 ldlinux_end equ default_cmd+(max_cmd_len+1)
4425 kern_cmd_len equ ldlinux_end-command_line
4426 ldlinux_len equ ldlinux_end-ldlinux_magic
4428 ; Put the getcbuf right after the code, aligned on a sector boundary
4430 end_of_code equ (ldlinux_end-bootsec)+7C00h
4431 getcbuf equ (end_of_code + 511) & 0FE00h
4433 ; VGA font buffer at the end of memory (so loading a font works even
4434 ; in graphics mode.)
4435 vgafontbuf equ 0E000h
4437 ; This is a compile-time assert that we didn't run out of space
4438 %if (getcbuf+trackbufsize) > vgafontbuf
4439 %error "Out of memory, better reorganize something..."