summaryrefslogtreecommitdiff
path: root/core
diff options
context:
space:
mode:
authorH. Peter Anvin <hpa@zytor.com>2008-05-29 15:11:38 -0700
committerH. Peter Anvin <hpa@zytor.com>2008-05-29 15:11:38 -0700
commitb536209dfb7bd50c37061735fe10d2c19a97d26d (patch)
tree9d8ca6882fc5d9721fb0efea1abfd6dc09886814 /core
parent3ec40a0119587f63411475c76c69f9db24c7598e (diff)
downloadsyslinux-b536209dfb7bd50c37061735fe10d2c19a97d26d.tar.gz
Move files out of root into core, dos, and utils
Move source files out of the root directory; the root is a mess and has become virtually unmaintainable. The Syslinux core now lives in core/; the Linux and generic utilities has moved into utils/, and copybs.com has moved into dos/; it had to go somewhere, and it seemed as good a place as any.
Diffstat (limited to 'core')
-rw-r--r--core/Makefile174
-rw-r--r--core/abort.inc70
-rw-r--r--core/adv.inc489
-rw-r--r--core/bcopy32.inc633
-rw-r--r--core/bios.inc39
-rw-r--r--core/bootsect.inc158
-rw-r--r--core/cache.inc114
-rwxr-xr-xcore/checksumiso.pl36
-rw-r--r--core/cleanup.inc54
-rw-r--r--core/cmdline.inc38
-rw-r--r--core/com32.inc421
-rw-r--r--core/comboot.inc988
-rw-r--r--core/config.inc60
-rw-r--r--core/configinit.inc59
-rw-r--r--core/conio.inc391
-rw-r--r--core/cpuinit.inc86
-rw-r--r--core/dnsresolv.inc380
-rw-r--r--core/ext2_fs.inc183
-rw-r--r--core/extlinux.asm1588
-rw-r--r--core/font.inc139
-rwxr-xr-xcore/genhash.pl26
-rw-r--r--core/getc.inc381
-rw-r--r--core/graphics.inc331
-rw-r--r--core/head.inc31
-rw-r--r--core/highmem.inc148
-rw-r--r--core/init.inc47
-rw-r--r--core/isolinux-debug.asm2
-rw-r--r--core/isolinux.asm1549
-rw-r--r--core/kernel.inc112
-rw-r--r--core/keywords44
-rw-r--r--core/keywords.inc97
-rw-r--r--core/layout.inc92
-rw-r--r--core/ldlinux.asm1605
-rw-r--r--core/loadhigh.inc120
-rw-r--r--core/localboot.inc75
-rwxr-xr-xcore/lstadjust.pl57
-rw-r--r--core/macros.inc110
-rw-r--r--core/now.pl21
-rw-r--r--core/parsecmd.inc120
-rw-r--r--core/parseconfig.inc454
-rw-r--r--core/plaincon.inc24
-rw-r--r--core/pxe.inc154
-rw-r--r--core/pxelinux.asm2897
-rw-r--r--core/rawcon.inc75
-rw-r--r--core/regdump.inc108
-rw-r--r--core/rllpack.inc156
-rw-r--r--core/runkernel.inc634
-rw-r--r--core/stack.inc47
-rw-r--r--core/strcpy.inc13
-rw-r--r--core/strecpy.inc28
-rw-r--r--core/syslinux.ld119
-rw-r--r--core/tracers.inc40
-rw-r--r--core/ui.inc683
-rw-r--r--core/writehex.inc53
-rw-r--r--core/writestr.inc47
55 files changed, 16600 insertions, 0 deletions
diff --git a/core/Makefile b/core/Makefile
new file mode 100644
index 00000000..de39acad
--- /dev/null
+++ b/core/Makefile
@@ -0,0 +1,174 @@
+## -----------------------------------------------------------------------
+##
+## Copyright 1998-2008 H. Peter Anvin - All Rights Reserved
+##
+## This program is free software; you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published by
+## the Free Software Foundation, Inc., 53 Temple Place Ste 330,
+## Boston MA 02111-1307, USA; either version 2 of the License, or
+## (at your option) any later version; incorporated herein by reference.
+##
+## -----------------------------------------------------------------------
+
+#
+# Makefile for the SYSLINUX core
+#
+
+# No builtin rules
+MAKEFLAGS += -r
+MAKE += -r
+
+CC = gcc
+
+TMPFILE = $(shell mktemp /tmp/gcc_ok.XXXXXX)
+gcc_ok = $(shell tmpf=$(TMPFILE); if $(CC) $(1) dummy.c -o $$tmpf 2>/dev/null; \
+ then echo '$(1)'; else echo '$(2)'; fi; rm -f $$tmpf)
+
+M32 := $(call gcc_ok,-m32,) $(call gcc_ok,-ffreestanding,) \
+ $(call gcc_ok,-fno-stack-protector,) \
+ $(call gcc_ok,-fno-top-level-reorder,$(call gcc_ok,-fno-unit-at-a-time))
+
+LD = ld
+LDFLAGS = -m elf_i386
+OBJCOPY = objcopy
+OBJDUMP = objdump
+
+OPTFLAGS = -g -Os -march=i386 -falign-functions=0 -falign-jumps=0 -falign-loops=0 -fomit-frame-pointer
+INCLUDES =
+CFLAGS = $(M32) -mregparm=3 -DREGPARM=3 -W -Wall -msoft-float $(OPTFLAGS) $(INCLUDES)
+
+NASM = nasm
+NASMOPT = -O9999
+NINCLUDE =
+
+PERL = perl
+
+VERSION := $(shell cat ../version)
+
+# _bin.c files required by both BTARGET and ITARGET installers
+BINFILES = bootsect_bin.c ldlinux_bin.c \
+ extlinux_bss_bin.c extlinux_sys_bin.c
+
+# syslinux.exe is BTARGET so as to not require everyone to have the
+# mingw suite installed
+BTARGET = kwdhash.gen \
+ ldlinux.bss ldlinux.sys ldlinux.bin \
+ pxelinux.0 isolinux.bin isolinux-debug.bin \
+ extlinux.bin extlinux.bss extlinux.sys
+ITARGET =
+
+# All primary source files for the main syslinux files
+NASMSRC = $(wildcard *.asm)
+NASMHDR = $(wildcard *.inc)
+CSRC = $(wildcard *.c)
+CHDR = $(wildcard *.h)
+OTHERSRC = keywords
+ALLSRC = $(NASMSRC) $(NASMHDR) $(CSRC) $(CHDR) $(OTHERSRC)
+
+# The DATE is set on the make command line when building binaries for
+# official release. Otherwise, substitute a hex string that is pretty much
+# guaranteed to be unique to be unique from build to build.
+ifndef HEXDATE
+HEXDATE := $(shell $(PERL) now.pl $(SRCS))
+endif
+ifndef DATE
+DATE := $(HEXDATE)
+endif
+
+#
+# NOTE: If you don't have the mingw compiler suite installed, you probably
+# want to remove win32 from this list; otherwise you're going to get an
+# error every time you try to build.
+#
+
+all: all-local
+
+all-local: $(BTARGET) $(BINFILES)
+
+installer: installer-local
+
+installer-local: $(ITARGET) $(BINFILES)
+
+kwdhash.gen: keywords genhash.pl
+ $(PERL) genhash.pl < keywords > kwdhash.gen
+
+.PRECIOUS: %.elf
+
+# Standard rule for {isolinux,isolinux-debug}.bin
+iso%.bin: iso%.elf
+ $(OBJCOPY) -O binary $< $@
+ $(PERL) checksumiso.pl $@
+
+# Standard rule for {ldlinux,pxelinux,extlinux}.bin
+%.bin: %.elf
+ $(OBJCOPY) -O binary $< $@
+
+%.o: %.asm kwdhash.gen ../version.gen
+ $(NASM) $(NASMOPT) -f elf -F stabs -DDATE_STR="'$(DATE)'" \
+ -DHEXDATE="$(HEXDATE)" \
+ -l $(@:.o=.lsr) -o $@ $<
+
+%.elf: %.o syslinux.ld
+ $(LD) $(LDFLAGS) -T syslinux.ld -M -o $@ $< > $(@:.elf=.map)
+ $(OBJDUMP) -h $@ > $(@:.elf=.sec)
+ $(PERL) lstadjust.pl $(@:.elf=.lsr) $(@:.elf=.sec) $(@:.elf=.lst)
+
+pxelinux.0: pxelinux.bin
+ cp -f $< $@
+
+ldlinux.bss: ldlinux.bin
+ dd if=$< of=$@ bs=512 count=1
+
+ldlinux.sys: ldlinux.bin
+ dd if=$< of=$@ bs=512 skip=1
+
+extlinux.bss: extlinux.bin
+ dd if=$< of=$@ bs=512 count=1
+
+extlinux.sys: extlinux.bin
+ dd if=$< of=$@ bs=512 skip=1
+
+bootsect_bin.c: ldlinux.bss ../bin2c.pl
+ $(PERL) ../bin2c.pl syslinux_bootsect < $< > $@
+
+ldlinux_bin.c: ldlinux.sys ../bin2c.pl
+ $(PERL) ../bin2c.pl syslinux_ldlinux < $< > $@
+
+extlinux_bss_bin.c: extlinux.bss ../bin2c.pl
+ $(PERL) ../bin2c.pl extlinux_bootsect < $< > $@
+
+extlinux_sys_bin.c: extlinux.sys ../bin2c.pl
+ $(PERL) ../bin2c.pl extlinux_image 512 < $< > $@
+
+install: installer
+
+install-lib: installer
+
+install-all: install install-lib
+
+netinstall: installer
+
+tidy dist:
+ rm -f *.o *.elf *_bin.c stupid.* patch.offset
+ rm -f *.lsr *.lst *.map *.sec
+ rm -f $(OBSOLETE)
+
+clean: tidy
+ rm -f $(ITARGET)
+
+spotless: clean
+ rm -f $(BTARGET) .depend *.so.*
+
+.depend:
+ rm -f .depend
+ for csrc in $(CSRC) ; do $(CC) $(INCLUDE) -MM $$csrc >> .depend ; done
+ for nsrc in $(NASMSRC) ; do $(NASM) -DDEPEND $(NINCLUDE) -o `echo $$nsrc | sed -e 's/\.asm/\.o/'` -M $$nsrc >> .depend ; done
+
+local-depend:
+ rm -f .depend
+ $(MAKE) .depend
+
+depend: local-depend
+
+# Include dependencies file
+include .depend
diff --git a/core/abort.inc b/core/abort.inc
new file mode 100644
index 00000000..0f443829
--- /dev/null
+++ b/core/abort.inc
@@ -0,0 +1,70 @@
+; -----------------------------------------------------------------------
+;
+; Copyright 2005-2008 H. Peter Anvin - All Rights Reserved
+;
+; This program is free software; you can redistribute it and/or modify
+; it under the terms of the GNU General Public License as published by
+; the Free Software Foundation, Inc., 53 Temple Place Ste 330,
+; Boston MA 02111-1307, USA; either version 2 of the License, or
+; (at your option) any later version; incorporated herein by reference.
+;
+; -----------------------------------------------------------------------
+
+;
+; abort.inc
+;
+; Code to terminate a kernel load
+;
+
+ section .text
+;
+; abort_check: let the user abort with <ESC> or <Ctrl-C>
+;
+abort_check:
+ call pollchar
+ jz .ret1
+ pusha
+ call getchar
+ cmp al,27 ; <ESC>
+ je .kill
+ cmp al,3 ; <Ctrl-C>
+ je .kill
+.ret2: popa
+.ret1: ret
+
+.kill: mov si,aborted_msg
+ mov bx,enter_command
+ jmp abort_load_chain
+
+;
+; abort_load: Called by various routines which wants to print a fatal
+; error message and return to the command prompt. Since this
+; may happen at just about any stage of the boot process, assume
+; our state is messed up, and just reset the segment registers
+; and the stack forcibly.
+;
+; SI = offset (in _text) of error message to print
+; BX = future entry point (abort_load_chain)
+;
+abort_load:
+ mov bx,error_or_command
+abort_load_chain:
+ RESET_STACK_AND_SEGS AX
+ call cwritestr ; Expects SI -> error msg
+
+ ; Return to the command prompt
+ jmp bx
+
+;
+; error_or_command: Execute ONERROR if appropriate, otherwise enter_command
+;
+error_or_command:
+ mov cx,[OnerrorLen]
+ and cx,cx
+ jnz on_error
+ jmp enter_command
+
+ section .data
+aborted_msg db ' aborted.', CR, LF, 0
+
+ section .text
diff --git a/core/adv.inc b/core/adv.inc
new file mode 100644
index 00000000..c5e8270c
--- /dev/null
+++ b/core/adv.inc
@@ -0,0 +1,489 @@
+;; -----------------------------------------------------------------------
+;;
+;; Copyright 2007-2008 H. Peter Anvin - All Rights Reserved
+;;
+;; This program is free software; you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+;; Boston MA 02110-1301, USA; either version 2 of the License, or
+;; (at your option) any later version; incorporated herein by reference.
+;;
+;; -----------------------------------------------------------------------
+
+;;
+;; adv.inc
+;;
+;; The auxillary data vector and its routines
+;;
+;; The auxillary data vector is a 512-byte aligned block that on the
+;; disk-based derivatives can be part of the syslinux file itself. It
+;; exists in two copies; when written, both copies are written (with a
+;; sync in between, if from the operating system.) The first two
+;; dwords are magic number and inverse checksum, then follows the data
+;; area as a tagged array similar to BOOTP/DHCP, finally a tail
+;; signature.
+;;
+;; Note that unlike BOOTP/DHCP, zero terminates the chain, and FF
+;; has no special meaning.
+;;
+
+;;
+;; List of ADV tags...
+;;
+ADV_BOOTONCE equ 1
+
+;;
+;; Other ADV data...
+;;
+ADV_MAGIC1 equ 0x5a2d2fa5 ; Head signature
+ADV_MAGIC2 equ 0xa3041767 ; Total checksum
+ADV_MAGIC3 equ 0xdd28bf64 ; Tail signature
+
+ADV_LEN equ 500 ; Data bytes
+
+adv_retries equ 6 ; Disk retries
+
+ section .adv
+ ; Introduce the ADVs to valid but blank
+adv0:
+.head resd 1
+.csum resd 1
+.data resb ADV_LEN
+.tail resd 1
+.end equ $
+adv1:
+.head resd 1
+.csum resd 1
+.data resb ADV_LEN
+.tail resd 1
+.end equ $
+ section .text
+
+ ;
+ ; This is called after config file parsing, so we know
+ ; the intended location of the ADV
+ ;
+adv_init:
+ cmp byte [ADVDrive],-1
+ jne adv_read
+
+;%if IS_SYSLINUX || IS_MDSLINUX || IS_EXTLINUX
+%if IS_EXTLINUX ; Not yet implemented for the other derivatives
+ ;
+ ; Update pointers to default ADVs...
+ ;
+ mov bx,[LDLSectors]
+ shl bx,2
+ mov ecx,[bsHidden]
+ mov eax,[bx+SectorPtrs-8]
+ mov edx,[bx+SectorPtrs-4]
+ add eax,ecx
+ add edx,ecx
+ mov [ADVSec0],eax
+ mov [ADVSec1],edx
+ mov al,[DriveNumber]
+ mov [ADVDrive],al
+%endif
+ ; ** fall through to adv_verify **
+
+ ;
+ ; Initialize the ADV data structure in memory
+ ;
+adv_verify:
+ cmp byte [ADVDrive],-1 ; No ADV configured, still?
+ je .reset ; Then unconditionally reset
+
+ mov si,adv0
+ call .check_adv
+ jz .ok ; Primary ADV okay
+ mov si,adv1
+ call .check_adv
+ jz .adv1ok
+
+ ; Neither ADV is usable; initialize to blank
+.reset:
+ mov di,adv0
+ mov eax,ADV_MAGIC1
+ stosd
+ mov eax,ADV_MAGIC2
+ stosd
+ xor eax,eax
+ mov cx,ADV_LEN/4
+ rep stosd
+ mov eax,ADV_MAGIC3
+ stosd
+
+.ok:
+ ret
+
+ ; The primary ADV is bad, but the backup is OK
+.adv1ok:
+ mov di,adv0
+ mov cx,512/4
+ rep movsd
+ ret
+
+
+ ; SI points to the putative ADV; unchanged by routine
+ ; ZF=1 on return if good
+.check_adv:
+ push si
+ lodsd
+ cmp eax,ADV_MAGIC1
+ jne .done ; ZF=0, i.e. bad
+ xor edx,edx
+ mov cx,ADV_LEN/4+1 ; Remaining dwords
+.csum:
+ lodsd
+ add edx,eax
+ loop .csum
+ cmp edx,ADV_MAGIC2
+ jne .done
+ lodsd
+ cmp eax,ADV_MAGIC3
+.done:
+ pop si
+ ret
+
+;
+; adv_get: find an ADV string if present
+;
+; Input: DL = ADV ID
+; Output: CX = byte count (zero on not found)
+; SI = pointer to data
+; DL = unchanged
+;
+; Assumes CS == DS.
+;
+
+adv_get:
+ push ax
+ mov si,adv0.data
+ xor ax,ax ; Keep AH=0 at all times
+.loop:
+ lodsb ; Read ID
+ cmp al,dl
+ je .found
+ and al,al
+ jz .end
+ lodsb ; Read length
+ add si,ax
+ cmp si,adv0.tail
+ jb .loop
+ jmp .end
+
+.found:
+ lodsb
+ mov cx,ax
+ add ax,si ; Make sure it fits
+ cmp ax,adv0.tail
+ jbe .ok
+.end:
+ xor cx,cx
+.ok:
+ pop ax
+ ret
+
+;
+; adv_set: insert a string into the ADV in memory
+;
+; Input: DL = ADV ID
+; FS:BX = input buffer
+; CX = byte count (max = 255!)
+; Output: CF=1 on error
+; CX clobbered
+;
+; Assumes CS == DS == ES.
+;
+adv_set:
+ push ax
+ push si
+ push di
+ and ch,ch
+ jnz .overflow
+
+ push cx
+ mov si,adv0.data
+ xor ax,ax
+.loop:
+ lodsb
+ cmp al,dl
+ je .found
+ and al,al
+ jz .endz
+ lodsb
+ add si,ax
+ cmp si,adv0.tail
+ jb .loop
+ jmp .end
+
+.found: ; Found, need to delete old copy
+ lodsb
+ lea di,[si-2]
+ push di
+ add si,ax
+ mov cx,adv0.tail
+ sub cx,si
+ jb .nukeit
+ rep movsb ; Remove the old one
+ mov [di],ah ; Termination zero
+ pop si
+ jmp .loop
+.nukeit:
+ pop si
+ jmp .end
+.endz:
+ dec si
+.end:
+ ; Now SI points to where we want to put our data
+ pop cx
+ mov di,si
+ jcxz .empty
+ add si,cx
+ cmp si,adv0.tail-2
+ jae .overflow ; CF=0
+
+ mov si,bx
+ mov al,dl
+ stosb
+ mov al,cl
+ stosb
+ fs rep movsb
+
+.empty:
+ mov cx,adv0.tail
+ sub cx,di
+ xor ax,ax
+ rep stosb ; Zero-fill remainder
+
+ clc
+.done:
+ pop di
+ pop si
+ pop ax
+ ret
+.overflow:
+ stc
+ jmp .done
+
+;
+; adv_cleanup: checksum adv0 and copy to adv1
+; Assumes CS == DS == ES.
+;
+adv_cleanup:
+ pushad
+ mov si,adv0.data
+ mov cx,ADV_LEN/4
+ xor edx,edx
+.loop:
+ lodsd
+ add edx,eax
+ loop .loop
+ mov eax,ADV_MAGIC2
+ sub eax,edx
+ lea di,[si+4] ; adv1
+ mov si,adv0
+ mov [si+4],eax ; Store checksum
+ mov cx,(ADV_LEN+12)/4
+ rep movsd
+ popad
+ ret
+
+;
+; adv_write: write the ADV to disk.
+;
+; Location is in memory variables.
+; Assumes CS == DS == ES.
+;
+; Returns CF=1 if the ADV cannot be written.
+;
+adv_write:
+ cmp dword [ADVSec0],0
+ je .bad
+ cmp dword [ADVSec1],0
+ je .bad
+ cmp byte [ADVDrive],-1
+ je .bad
+
+ push ax
+ call adv_cleanup
+ mov ah,3 ; Write
+ call adv_read_write
+ pop ax
+
+ clc
+ ret
+.bad: ; No location for ADV set
+ stc
+ ret
+
+;
+; adv_read: read the ADV from disk
+;
+; Location is in memory variables.
+; Assumes CS == DS == ES.
+;
+adv_read:
+ push ax
+ mov ah,2 ; Read
+ call adv_read_write
+ call adv_verify
+ pop ax
+ ret
+
+;
+; adv_read_write: disk I/O for the ADV
+;
+; On input, AH=2 for read, AH=3 for write.
+; Assumes CS == DS == ES.
+;
+adv_read_write:
+ mov [ADVOp],ah
+ pushad
+
+ mov dl,[ADVDrive]
+ and dl,dl
+ ; Floppies: can't trust INT 13h 08h, we better know
+ ; the geometry a priori, which means it better be our
+ ; boot device. Handle that later.
+ ; jns .floppy ; Floppy drive... urk
+
+ mov ah,08h ; Get disk parameters
+ int 13h
+ jc .noparm
+ and ah,ah
+ jnz .noparm
+ shr dx,8
+ inc dx
+ mov [ADVHeads],dx
+ and cx,3fh
+ mov [ADVSecPerTrack],cx
+
+.noparm:
+ ; Check for EDD
+ mov bx,55AAh
+ mov ah,41h ; EDD existence query
+ mov dl,[ADVDrive]
+ int 13h
+ mov si,.cbios
+ jc .noedd
+ mov si,.ebios
+.noedd:
+
+ mov eax,[ADVSec0]
+ mov bx,adv0
+ call .doone
+
+ mov eax,[ADVSec1]
+ mov bx,adv1
+ call .doone
+
+ popad
+ ret
+
+.doone:
+ xor edx,edx ; Zero-extend LBA
+ push si
+ jmp si
+
+.ebios:
+ mov cx,adv_retries
+.eb_retry:
+ ; Form DAPA on stack
+ push edx
+ push eax
+ push es
+ push bx
+ push word 1 ; Sector count
+ push word 16 ; DAPA size
+ mov si,sp
+ pushad
+ mov dl,[ADVDrive]
+ mov ax,4080h
+ or ah,[ADVOp]
+ push ds
+ push ss
+ pop ds
+ int 13h
+ pop ds
+ popad
+ lea sp,[si+16] ; Remove DAPA
+ jc .eb_error
+ pop si
+ ret
+.eb_error:
+ loop .eb_retry
+ stc
+ pop si
+ ret
+
+.cbios:
+ push edx
+ push eax
+ push bp
+
+ movzx esi,word [ADVSecPerTrack]
+ movzx edi,word [ADVHeads]
+ ;
+ ; Dividing by sectors to get (track,sector): we may have
+ ; up to 2^18 tracks, so we need to use 32-bit arithmetric.
+ ;
+ div esi
+ xor cx,cx
+ xchg cx,dx ; CX <- sector index (0-based)
+ ; EDX <- 0
+ ; eax = track #
+ div edi ; Convert track to head/cyl
+
+ ; Watch out for overflow, we might be writing!
+ cmp eax,1023
+ ja .cb_overflow
+
+ ;
+ ; Now we have AX = cyl, DX = head, CX = sector (0-based),
+ ; BP = sectors to transfer, SI = bsSecPerTrack,
+ ; ES:BX = data target
+ ;
+
+ shl ah,6 ; Because IBM was STOOPID
+ ; and thought 8 bits were enough
+ ; then thought 10 bits were enough...
+ inc cx ; Sector numbers are 1-based, sigh
+ or cl,ah
+ mov ch,al
+ mov dh,dl
+ mov dl,[ADVDrive]
+ xchg ax,bp ; Sector to transfer count
+ mov ah,[ADVOp] ; Operation
+
+ mov bp,adv_retries
+.cb_retry:
+ pushad
+ int 13h
+ popad
+ jc .cb_error
+
+.cb_done:
+ pop bp
+ pop eax
+ pop edx
+ ret
+
+.cb_error:
+ dec bp
+ jnz .cb_retry
+.cb_overflow:
+ stc
+ jmp .cb_done
+
+ section .data
+ align 4, db 0
+ADVSec0 dd 0 ; Not specified
+ADVSec1 dd 0 ; Not specified
+ADVDrive db -1 ; No ADV defined
+
+ section .bss
+ alignb 4
+ADVSecPerTrack resw 1
+ADVHeads resw 1
+ADVOp resb 1
diff --git a/core/bcopy32.inc b/core/bcopy32.inc
new file mode 100644
index 00000000..fd14409f
--- /dev/null
+++ b/core/bcopy32.inc
@@ -0,0 +1,633 @@
+;; -----------------------------------------------------------------------
+;;
+;; Copyright 1994-2008 H. Peter Anvin - All Rights Reserved
+;;
+;; This program is free software; you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation, Inc., 53 Temple Place Ste 330,
+;; Boston MA 02111-1307, USA; either version 2 of the License, or
+;; (at your option) any later version; incorporated herein by reference.
+;;
+;; -----------------------------------------------------------------------
+
+;;
+;; bcopy32.inc
+;;
+;; 32-bit bcopy routine for real mode
+;;
+
+;
+; 32-bit bcopy routine for real mode
+;
+; We enter protected mode, set up a flat 32-bit environment, run rep movsd
+; and then exit. IMPORTANT: This code assumes cs == 0.
+;
+; This code is probably excessively anal-retentive in its handling of
+; segments, but this stuff is painful enough as it is without having to rely
+; on everything happening "as it ought to."
+;
+; NOTE: this code is relocated into low memory, just after the .earlybss
+; segment, in order to support to "bcopy over self" operation.
+;
+
+ section .bcopy32
+ align 8
+__bcopy_start:
+
+ ; This is in the .text segment since it needs to be
+ ; contiguous with the rest of the bcopy stuff
+
+; GDT descriptor entry
+%macro desc 1
+bcopy_gdt.%1:
+PM_%1 equ bcopy_gdt.%1-bcopy_gdt
+%endmacro
+
+bcopy_gdt:
+ dw bcopy_gdt_size-1 ; Null descriptor - contains GDT
+ dd bcopy_gdt ; pointer for LGDT instruction
+ dw 0
+
+ desc CS16
+ dd 0000ffffh ; 08h Code segment, use16, readable,
+ dd 00009b00h ; present, dpl 0, cover 64K
+ desc DS16_4G
+ dd 0000ffffh ; 10h Data segment, use16, read/write,
+ dd 008f9300h ; present, dpl 0, cover all 4G
+ desc DS16_RM
+ dd 0000ffffh ; 18h Data segment, use16, read/write,
+ dd 00009300h ; present, dpl 0, cover 64K
+ ; The next two segments are used for COM32 only
+ desc CS32
+ dd 0000ffffh ; 20h Code segment, use32, readable,
+ dd 00cf9b00h ; present, dpl 0, cover all 4G
+ desc DS32
+ dd 0000ffffh ; 28h Data segment, use32, read/write,
+ dd 00cf9300h ; present, dpl 0, cover all 4G
+
+ ; TSS segment to keep Intel VT happy. Intel VT is
+ ; unhappy about anything that doesn't smell like a
+ ; full-blown 32-bit OS.
+ desc TSS
+ dw 104-1, DummyTSS ; 30h 32-bit task state segment
+ dd 00008900h ; present, dpl 0, 104 bytes @DummyTSS
+
+ ; 16-bit stack segment, which may have a different
+ ; base from DS16 (e.g. if we're booted from PXELINUX)
+ desc SS16
+ dd 0000ffffh ; 38h Data segment, use16, read/write,
+ dd 00009300h ; present, dpl 0, cover 64K
+
+bcopy_gdt_size: equ $-bcopy_gdt
+
+;
+; bcopy:
+; 32-bit copy, overlap safe
+;
+; Inputs:
+; ESI - source pointer (-1 means do bzero rather than bcopy)
+; EDI - target pointer
+; ECX - byte count
+; DF - zero
+;
+; Outputs:
+; ESI - first byte after source (garbage if ESI == -1 on entry)
+; EDI - first byte after target
+;
+bcopy: pushad
+ push word pm_bcopy
+ call simple_pm_call
+ popad
+ add edi,ecx
+ add esi,ecx
+ ret
+
+;
+; This routine is used to invoke a simple routine in 16-bit protected
+; mode (with 32-bit DS and ES, and working 16-bit stack.)
+; Note that all segment registers including CS, except possibly SS,
+; are zero-based in the protected-mode routine.
+;
+; No interrupt thunking services are provided; interrupts are disabled
+; for the duration of the routine. Don't run for too long at a time.
+;
+; Inputs:
+; On stack - pm entrypoint
+; EAX, EBP preserved until real-mode exit
+; EBX, ECX, EDX, ESI and EDI passed to the called routine
+;
+; Outputs:
+; EAX, EBP restored from real-mode entry
+; All other registers as returned from called function
+; PM entrypoint cleaned off stack
+;
+simple_pm_call:
+ push eax
+ push ebp
+ mov bp,sp
+ pushfd ; Saves, among others, the IF flag
+ push ds
+ push es
+ push fs
+ push gs
+
+ cli
+ call enable_a20
+
+ mov byte [cs:bcopy_gdt.TSS+5],89h ; Mark TSS unbusy
+
+ ; Convert the stack segment to a base
+ xor eax,eax
+ mov ax,ss
+ shl eax,4
+ or eax,93000000h
+ mov [cs:bcopy_gdt.SS16+2],eax
+
+ push ss ; Save real-mode SS selector
+
+ o32 lgdt [cs:bcopy_gdt]
+ mov eax,cr0
+ or al,1
+ mov cr0,eax ; Enter protected mode
+ jmp PM_CS16:.in_pm
+.in_pm:
+ mov ax,PM_SS16 ; Make stack usable
+ mov ss,ax
+
+ mov al,PM_DS16_4G ; Data segment selector
+ mov es,ax
+ mov ds,ax
+
+ ; Set fs, gs, tr, and ldtr in case we're on a virtual
+ ; machine running on Intel VT hardware -- it can't
+ ; deal with a partial transition, for no good reason.
+
+ mov al,PM_DS16_RM ; Real-mode-like segment
+ mov fs,ax
+ mov gs,ax
+ mov al,PM_TSS ; Intel VT really doesn't want
+ ltr ax ; an invalid TR and LDTR, so give
+ xor ax,ax ; it something that it can use...
+ lldt ax ; (sigh)
+
+ call [bp+2*4+2] ; Call actual routine
+
+.exit:
+ mov ax,PM_DS16_RM ; "Real-mode-like" data segment
+ mov es,ax
+ mov ds,ax
+
+ pop bp ; Previous value for ss
+
+ mov eax,cr0
+ and al,~1
+ mov cr0,eax ; Disable protected mode
+ jmp 0:.in_rm
+
+.in_rm: ; Back in real mode
+ mov ss,bp
+ pop gs
+ pop fs
+ pop es
+ pop ds
+%if DISABLE_A20
+ call disable_a20
+%endif
+
+ popfd ; Re-enables interrupts
+ pop ebp
+ pop eax
+ ret 2 ; Drops the pm entry
+
+;
+; pm_bcopy:
+;
+; This is the protected-mode core of the "bcopy" routine.
+;
+pm_bcopy:
+ cmp esi,-1
+ je .bzero
+
+ cmp esi,edi ; If source < destination, we might
+ jb .reverse ; have to copy backwards
+
+.forward:
+ mov al,cl ; Save low bits
+ and al,3
+ shr ecx,2 ; Convert to dwords
+ a32 rep movsd ; Do our business
+ ; At this point ecx == 0
+
+ mov cl,al ; Copy any fractional dword
+ a32 rep movsb
+ ret
+
+.reverse:
+ std ; Reverse copy
+ lea esi,[esi+ecx-1] ; Point to final byte
+ lea edi,[edi+ecx-1]
+ mov eax,ecx
+ and ecx,3
+ shr eax,2
+ a32 rep movsb
+
+ ; Change ESI/EDI to point to the last dword, instead
+ ; of the last byte.
+ sub esi,3
+ sub edi,3
+ mov ecx,eax
+ a32 rep movsd
+
+ cld
+ ret
+
+.bzero:
+ xor eax,eax
+ mov si,cx ; Save low bits
+ and si,3
+ shr ecx,2
+ a32 rep stosd
+
+ mov cx,si ; Write fractional dword
+ a32 rep stosb
+ ret
+
+;
+; Routines to enable and disable (yuck) A20. These routines are gathered
+; from tips from a couple of sources, including the Linux kernel and
+; http://www.x86.org/. The need for the delay to be as large as given here
+; is indicated by Donnie Barnes of RedHat, the problematic system being an
+; IBM ThinkPad 760EL.
+;
+; We typically toggle A20 twice for every 64K transferred.
+;
+%define io_delay call _io_delay
+%define IO_DELAY_PORT 80h ; Invalid port (we hope!)
+%define disable_wait 32 ; How long to wait for a disable
+
+; Note the skip of 2 here
+%define A20_DUNNO 0 ; A20 type unknown
+%define A20_NONE 2 ; A20 always on?
+%define A20_BIOS 4 ; A20 BIOS enable
+%define A20_KBC 6 ; A20 through KBC
+%define A20_FAST 8 ; A20 through port 92h
+
+slow_out: out dx, al ; Fall through
+
+_io_delay: out IO_DELAY_PORT,al
+ out IO_DELAY_PORT,al
+ ret
+
+enable_a20:
+ pushad
+ mov byte [cs:A20Tries],255 ; Times to try to make this work
+
+try_enable_a20:
+;
+; Flush the caches
+;
+%if DO_WBINVD
+ call try_wbinvd
+%endif
+
+;
+; If the A20 type is known, jump straight to type
+;
+ mov bp,[cs:A20Type]
+ jmp word [cs:bp+A20List]
+
+;
+; First, see if we are on a system with no A20 gate
+;
+a20_dunno:
+a20_none:
+ mov byte [cs:A20Type], A20_NONE
+ call a20_test
+ jnz a20_done
+
+;
+; Next, try the BIOS (INT 15h AX=2401h)
+;
+a20_bios:
+ mov byte [cs:A20Type], A20_BIOS
+ mov ax,2401h
+ pushf ; Some BIOSes muck with IF
+ int 15h
+ popf
+
+ call a20_test
+ jnz a20_done
+
+;
+; Enable the keyboard controller A20 gate
+;
+a20_kbc:
+ mov dl, 1 ; Allow early exit
+ call empty_8042
+ jnz a20_done ; A20 live, no need to use KBC
+
+ mov byte [cs:A20Type], A20_KBC ; Starting KBC command sequence
+
+ mov al,0D1h ; Command write
+ out 064h, al
+ call empty_8042_uncond
+
+ mov al,0DFh ; A20 on
+ out 060h, al
+ call empty_8042_uncond
+
+ ; Verify that A20 actually is enabled. Do that by
+ ; observing a word in low memory and the same word in
+ ; the HMA until they are no longer coherent. Note that
+ ; we don't do the same check in the disable case, because
+ ; we don't want to *require* A20 masking (SYSLINUX should
+ ; work fine without it, if the BIOS does.)
+.kbc_wait: push cx
+ xor cx,cx
+.kbc_wait_loop:
+ call a20_test
+ jnz a20_done_pop
+ loop .kbc_wait_loop
+
+ pop cx
+;
+; Running out of options here. Final attempt: enable the "fast A20 gate"
+;
+a20_fast:
+ mov byte [cs:A20Type], A20_FAST ; Haven't used the KBC yet
+ in al, 092h
+ or al,02h
+ and al,~01h ; Don't accidentally reset the machine!
+ out 092h, al
+
+.fast_wait: push cx
+ xor cx,cx
+.fast_wait_loop:
+ call a20_test
+ jnz a20_done_pop
+ loop .fast_wait_loop
+
+ pop cx
+
+;
+; Oh bugger. A20 is not responding. Try frobbing it again; eventually give up
+; and report failure to the user.
+;
+
+
+ dec byte [cs:A20Tries]
+ jnz try_enable_a20
+
+ mov si, err_a20
+ jmp abort_load
+
+ section .data
+err_a20 db CR, LF, 'A20 gate not responding!', CR, LF, 0
+ section .bcopy32
+
+;
+; A20 unmasked, proceed...
+;
+a20_done_pop: pop cx
+a20_done: popad
+ ret
+
+;
+; This routine tests if A20 is enabled (ZF = 0). This routine
+; must not destroy any register contents.
+;
+a20_test:
+ push es
+ push cx
+ push ax
+ mov cx,0FFFFh ; HMA = segment 0FFFFh
+ mov es,cx
+ mov cx,32 ; Loop count
+ mov ax,[cs:A20Test]
+.a20_wait: inc ax
+ mov [cs:A20Test],ax
+ io_delay ; Serialize, and fix delay
+ cmp ax,[es:A20Test+10h]
+ loopz .a20_wait
+.a20_done: pop ax
+ pop cx
+ pop es
+ ret
+
+%if DISABLE_A20
+
+disable_a20:
+ pushad
+;
+; Flush the caches
+;
+%if DO_WBINVD
+ call try_wbinvd
+%endif
+
+ mov bp,[cs:A20Type]
+ jmp word [cs:bp+A20DList]
+
+a20d_bios:
+ mov ax,2400h
+ pushf ; Some BIOSes muck with IF
+ int 15h
+ popf
+ jmp short a20d_snooze
+
+;
+; Disable the "fast A20 gate"
+;
+a20d_fast:
+ in al, 092h
+ and al,~03h
+ out 092h, al
+ jmp short a20d_snooze
+
+;
+; Disable the keyboard controller A20 gate
+;
+a20d_kbc:
+ call empty_8042_uncond
+ mov al,0D1h
+ out 064h, al ; Command write
+ call empty_8042_uncond
+ mov al,0DDh ; A20 off
+ out 060h, al
+ call empty_8042_uncond
+ ; Wait a bit for it to take effect
+a20d_snooze:
+ push cx
+ mov cx, disable_wait
+.delayloop: call a20_test
+ jz .disabled
+ loop .delayloop
+.disabled: pop cx
+a20d_dunno:
+a20d_none:
+ popad
+ ret
+
+%endif
+
+;
+; Routine to empty the 8042 KBC controller. If dl != 0
+; then we will test A20 in the loop and exit if A20 is
+; suddenly enabled.
+;
+empty_8042_uncond:
+ xor dl,dl
+empty_8042:
+ call a20_test
+ jz .a20_on
+ and dl,dl
+ jnz .done
+.a20_on: io_delay
+ in al, 064h ; Status port
+ test al,1
+ jz .no_output
+ io_delay
+ in al, 060h ; Read input
+ jmp short empty_8042
+.no_output:
+ test al,2
+ jnz empty_8042
+ io_delay
+.done: ret
+
+;
+; Execute a WBINVD instruction if possible on this CPU
+;
+%if DO_WBINVD
+try_wbinvd:
+ wbinvd
+ ret
+%endif
+
+;
+; shuffle_and_boot:
+;
+; This routine is used to shuffle memory around, followed by
+; invoking an entry point somewhere in low memory. This routine
+; can clobber any memory above 7C00h, we therefore have to move
+; necessary code into the trackbuf area before doing the copy,
+; and do adjustments to anything except BSS area references.
+;
+; NOTE: Since PXELINUX relocates itself, put all these
+; references in the ".earlybss" segment.
+;
+; After performing the copy, this routine resets the stack and
+; jumps to the specified entrypoint.
+;
+; IMPORTANT: This routine does not canonicalize the stack or the
+; SS register. That is the responsibility of the caller.
+;
+; Inputs:
+; DS:BX -> Pointer to list of (dst, src, len) pairs(*)
+; AX -> Number of list entries
+; [CS:EntryPoint] -> CS:IP to jump to
+; On stack - initial state (fd, ad, ds, es, fs, gs)
+;
+; (*) If dst == -1, then (src, len) entry refers to a set of new
+; descriptors to load.
+; If src == -1, then the memory pointed to by (dst, len) is bzeroed;
+; this is handled inside the bcopy routine.
+;
+shuffle_and_boot:
+.restart:
+ and ax,ax
+ jz .done
+.loop:
+ mov edi,[bx]
+ mov esi,[bx+4]
+ mov ecx,[bx+8]
+ cmp edi, -1
+ je .reload
+ call bcopy
+ add bx,12
+ dec ax
+ jnz .loop
+
+.done:
+ pop gs
+ pop fs
+ pop es
+ pop ds
+ popad
+ popfd
+ jmp far [cs:EntryPoint]
+
+.reload:
+ mov bx, trackbuf ; Next descriptor
+ movzx edi,bx
+ push ecx ; Save byte count
+ call bcopy
+ pop eax ; Byte count
+ xor edx,edx
+ mov ecx,12
+ div ecx ; Convert to descriptor count
+ jmp .restart
+
+;
+; trampoline_to_pm:
+;
+; This routine is chained to from shuffle_and_boot to invoke a
+; flat 32-bit protected mode operating system.
+;
+trampoline_to_pm:
+ cli
+ call enable_a20
+ mov byte [cs:bcopy_gdt.TSS+5],89h ; Mark TSS unbusy
+ o32 lgdt [cs:bcopy_gdt]
+ mov eax,cr0
+ or al,1
+ mov cr0,eax ; Enter protected mode
+ jmp PM_CS32:.next ; Synchronize and go to 32-bit mode
+
+ bits 32
+.next: xor eax,eax
+ lldt ax ; TR <- 0 to be nice to Intel VT
+ mov al,PM_TSS
+ ltr ax ; Bogus TSS to be nice to Intel VT
+ mov al,PM_DS32
+ mov es,ax ; 32-bit data segment selector
+ mov ds,ax
+ mov ss,ax
+ mov fs,ax
+ mov gs,ax
+ jmp word TrampolineBuf
+ bits 16
+
+ align 2
+A20List dw a20_dunno, a20_none, a20_bios, a20_kbc, a20_fast
+%if DISABLE_A20
+A20DList dw a20d_dunno, a20d_none, a20d_bios, a20d_kbc, a20d_fast
+%endif
+
+A20Type dw A20_NONE ; A20 type
+
+ ; Total size of .bcopy32 section
+ alignb 4, db 0 ; Even number of dwords
+__bcopy_size equ $-__bcopy_start
+
+ section .earlybss
+ alignb 2
+EntryPoint resd 1 ; CS:IP for shuffle_and_boot
+A20Test resw 1 ; Counter for testing status of A20
+A20Tries resb 1 ; Times until giving up on A20
+
+;
+; This buffer contains synthesized code for shuffle-and-boot.
+; For the PM case, it is 9*5 = 45 bytes long; for the RM case it is
+; 8*6 to set the GPRs, 6*5 to set the segment registers (including a dummy
+; setting of CS), 5 bytes to set CS:IP, for a total of 83 bytes.
+;
+TrampolineBuf resb 83 ; Shuffle and boot trampoline
+
+;
+; Space for a dummy task state segment. It should never be actually
+; accessed, but just in case it is, point to a chunk of memory not used
+; for anything real.
+;
+ alignb 4
+DummyTSS resb 104
diff --git a/core/bios.inc b/core/bios.inc
new file mode 100644
index 00000000..987a2166
--- /dev/null
+++ b/core/bios.inc
@@ -0,0 +1,39 @@
+;; -----------------------------------------------------------------------
+;;
+;; Copyright 1994-2008 H. Peter Anvin - All Rights Reserved
+;;
+;; This program is free software; you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation, Inc., 53 Temple Place Ste 330,
+;; Boston MA 02111-1307, USA; either version 2 of the License, or
+;; (at your option) any later version; incorporated herein by reference.
+;;
+;; -----------------------------------------------------------------------
+
+;;
+;; bios.inc
+;;
+;; Header file for the BIOS data structures etc.
+;;
+
+%ifndef _BIOS_INC
+%define _BIOS_INC
+
+ absolute 4*1Eh ; In the interrupt table
+fdctab equ $
+fdctab1 resw 1
+fdctab2 resw 1
+ absolute 0400h
+serial_base resw 4 ; Base addresses for 4 serial ports
+ absolute 0413h
+BIOS_fbm resw 1 ; Free Base Memory (kilobytes)
+ absolute 0462h
+BIOS_page resb 1 ; Current video page
+ absolute 046Ch
+BIOS_timer resw 1 ; Timer ticks
+ absolute 0472h
+BIOS_magic resw 1 ; BIOS reset magic
+ absolute 0484h
+BIOS_vidrows resb 1 ; Number of screen rows
+
+%endif ; _BIOS_INC
diff --git a/core/bootsect.inc b/core/bootsect.inc
new file mode 100644
index 00000000..7e8f416d
--- /dev/null
+++ b/core/bootsect.inc
@@ -0,0 +1,158 @@
+;; -----------------------------------------------------------------------
+;;
+;; Copyright 1994-2008 H. Peter Anvin - All Rights Reserved
+;;
+;; This program is free software; you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation, Inc., 53 Temple Place Ste 330,
+;; Boston MA 02111-1307, USA; either version 2 of the License, or
+;; (at your option) any later version; incorporated herein by reference.
+;;
+;; -----------------------------------------------------------------------
+
+;;
+;; bootsect.inc
+;;
+;; Load a boot sector (or other bootstrap program.)
+;;
+;; Unlike previous versions of this software, this doesn't require that
+;; the length is 512 bytes. This allows PXE bootstraps and WinNT
+;; "CD boot sectors" to be invoked.
+;;
+
+;
+; Load a boot sector
+;
+is_bootsector:
+%if IS_SYSLINUX || IS_MDSLINUX
+ ; Transfer zero bytes
+ mov byte [CopySuper],0
+ jmp short load_bootsec
+
+is_bss_sector:
+ ; Transfer the superblock
+ mov byte [CopySuper],superblock_len
+%endif
+load_bootsec:
+ mov edi, 100000h
+ mov [trackbuf+4],edi ; Copy from this address
+ push edi ; Save load address
+ xor dx,dx ; No padding
+ mov bx,abort_check ; Don't print dots, but allow abort
+ call load_high
+
+ sub edi,100000h
+ mov [trackbuf+8],edi ; Save length
+
+ mov eax,7C00h ; Entry point
+ mov [trackbuf],eax ; Copy to this address
+ mov [EntryPoint],eax ; Jump to this address when done
+
+%if IS_SYSLINUX || IS_MDSLINUX
+ movzx ecx,byte [CopySuper]
+ jcxz .not_bss
+
+ ; For a BSS boot sector we have to patch.
+ mov esi,superblock
+ mov edi,100000h+(superblock-bootsec)
+ call bcopy
+
+.not_bss:
+%endif
+
+ xor edx,edx
+ xor esi,esi
+%if IS_SYSLINUX || IS_MDSLINUX || IS_EXTLINUX
+ ; Restore original FDC table
+ mov eax,[OrigFDCTabPtr]
+ mov [fdctab],eax
+
+ mov dl,[DriveNumber]
+ mov si,PartInfo ; Partition info buffer
+ mov di,800h-18 ; Put partition info here
+ push di
+ mov cx,8 ; 16 bytes
+ xor ax,ax
+ rep movsw
+ pop si ; DS:SI points to partition info
+%elif IS_ISOLINUX
+ mov dl,[DriveNumber]
+%elif IS_PXELINUX
+ mov byte [KeepPXE],1 ; Chainloading another NBP
+ call reset_pxe
+%endif
+ xor bx,bx
+
+;
+; replace_bootstrap for the special case where we have exactly one
+; descriptor, and it's the first entry in the trackbuf
+;
+
+replace_bootstrap_one:
+ push word 1 ; Length of descriptor list
+ push word trackbuf ; Address of descriptor list
+ ; Fall through
+
+;
+; Entrypoint for "shut down and replace bootstrap" -- also invoked by
+; the COMBOOT API. This routine expects two words on the stack:
+; address of the copy list (versus DS) and count. Additionally,
+; the values of ESI and EDX are passed on to the new bootstrap;
+; the value of BX becomes the new DS.
+;
+replace_bootstrap:
+ ;
+ ; Prepare for shutting down
+ ;
+ call vgaclearmode
+ call cleanup_hardware
+
+ ;
+ ; Set up initial stack frame (not used by PXE if keeppxe is
+ ; set - we use the PXE stack then.)
+ ; AFTER THIS POINT ONLY .earlybss IS AVAILABLE, NOT .bss
+ ;
+ xor ax,ax
+ mov ds,ax
+ mov es,ax
+
+%if IS_PXELINUX
+ test byte [KeepPXE],01h
+ jz .stdstack
+ les di,[InitStack] ; Reset stack to PXE original
+ jmp .stackok
+%endif
+.stdstack:
+ mov di,7C00h-44
+ push di
+ mov cx,22 ; 44 bytes
+ rep stosw
+ pop di
+.stackok:
+
+ mov [es:di+28],edx ; New EDX
+ mov [es:di+12],esi ; New ESI
+ mov [es:di+6],bx ; New DS
+
+%if IS_PXELINUX == 0
+ ; DON'T DO THIS FOR PXELINUX...
+ ; For PXE, ES:BX -> PXENV+, and this would corrupt
+ ; that use.
+
+ ; Restore ES:DI -> $PnP (if we were ourselves called
+ ; that way...)
+ mov ax,[OrigESDI]
+ mov bx,[OrigESDI+2]
+
+ mov [es:di+8],ax ; New DI
+ mov [es:di+4],bx ; New ES
+%endif
+ pop bx ; Copy from...
+ pop ax ; Copy list count
+
+ cli
+ mov cx,es
+ mov ss,cx
+ movzx esp,di
+
+ jmp shuffle_and_boot
diff --git a/core/cache.inc b/core/cache.inc
new file mode 100644
index 00000000..59755576
--- /dev/null
+++ b/core/cache.inc
@@ -0,0 +1,114 @@
+; -*- fundamental -*- ---------------------------------------------------
+;
+; Copyright 2004-2008 H. Peter Anvin - All Rights Reserved
+;
+; This program is free software; you can redistribute it and/or modify
+; it under the terms of the GNU General Public License as published by
+; the Free Software Foundation, Inc., 53 Temple Place Ste 330,
+; Boston MA 02111-1307, USA; either version 2 of the License, or
+; (at your option) any later version; incorporated herein by reference.
+;
+; -----------------------------------------------------------------------
+
+ section .text
+
+ struc cptr
+.sector: resd 1 ; Sector number
+.prev: resw 1 ; LRU pointer to previous (less recent)
+.next: resw 1 ; LRU pointer to next (more recent)
+ endstruc
+cptr_size_lg2 equ 3
+
+NCacheEntries equ 65536/SECTOR_SIZE
+
+;
+; initcache: Initialize the cache data structures
+;
+initcache:
+ xor eax,eax ; We don't care about sector 0
+ mov di,CachePtrs
+ mov cx,NCacheEntries+1
+ mov bx,CachePtrs+NCacheEntries*cptr_size ; "prev" pointer
+.loop:
+ mov [di+cptr.sector],eax ; Zero sector number
+ mov [di+cptr.prev],bx ; Previous pointer
+ mov [bx+cptr.next],di ; Previous entry's next pointer
+ mov bx,di
+ add di,cptr_size
+ loop .loop
+ ret
+
+;
+; getcachesector: Check for a particular sector (EAX) in the sector cache,
+; and if it is already there, return a pointer in GS:SI
+; otherwise load it and return said pointer.
+;
+; Assumes CS == DS.
+;
+getcachesector:
+ push cx
+ push bx
+ push di
+ mov si,cache_seg
+ mov gs,si
+ mov si,CachePtrs+cptr_size ; Real sector cache pointers
+ mov cx,NCacheEntries
+.search:
+ cmp eax,[si]
+ jz .hit
+ add si,cptr_size
+ loop .search
+
+.miss:
+ TRACER 'M'
+ ; Need to load it.
+ push es
+ push gs
+ pop es
+ mov bx,[CachePtrs+cptr.next] ; "Next most recent than head node"
+ mov [bx+cptr.sector],eax
+ mov si,bx
+ sub bx,CachePtrs+cptr_size
+ shl bx,SECTOR_SHIFT-cptr_size_lg2 ; Buffer address
+ pushad
+%if IS_EXTLINUX
+ call getonesec_ext
+%else
+ call getonesec
+%endif
+ popad
+ pop es
+.hit:
+ ; Update LRU, then compute buffer address
+ TRACER 'H'
+
+ ; Remove from current position in the list
+ mov bx,[si+cptr.prev]
+ mov di,[si+cptr.next]
+ mov [bx+cptr.next],di
+ mov [di+cptr.prev],bx
+
+ ; Add to just before head node
+ mov bx,[CachePtrs+cptr.prev]
+ mov [si+cptr.prev],bx
+ mov [bx+cptr.next],si
+ mov [CachePtrs+cptr.prev],si
+ mov word [si+cptr.next],CachePtrs
+
+ sub si,CachePtrs+cptr_size
+ shl si,SECTOR_SHIFT-cptr_size_lg2 ; Buffer address
+
+ pop di
+ pop bx
+ pop cx
+ ret
+
+ section .bss
+
+ ; Each CachePtr contains:
+ ; - Block pointer
+ ; - LRU previous pointer
+ ; - LRU next pointer
+ ; The first entry is the head node of the list
+ alignb 4
+CachePtrs resb (NCacheEntries+1)*cptr_size
diff --git a/core/checksumiso.pl b/core/checksumiso.pl
new file mode 100755
index 00000000..b5527428
--- /dev/null
+++ b/core/checksumiso.pl
@@ -0,0 +1,36 @@
+#!/usr/bin/perl
+#
+# Construct a checksum for isolinux*.bin, compatible
+# with an mkisofs boot-info-table
+#
+
+use bytes;
+use integer;
+
+($file) = @ARGV;
+
+open(FILE, '+<', $file) or die "$0: Cannot open $file: $!\n";
+binmode FILE;
+
+if ( !seek(FILE,64,0) ) {
+ die "$0: Cannot seek past header\n";
+}
+
+$csum = 0;
+$bytes = 64;
+while ( ($n = read(FILE, $dw, 4)) > 0 ) {
+ $dw .= "\0\0\0\0"; # Pad to at least 32 bits
+ ($v) = unpack("V", $dw);
+ $csum = ($csum + $v) & 0xffffffff;
+ $bytes += $n;
+}
+
+if ( !seek(FILE,16,0) ) {
+ die "$0: Cannot seek to header\n";
+}
+
+print FILE pack("VV", $bytes, $csum);
+
+close(FILE);
+
+exit 0;
diff --git a/core/cleanup.inc b/core/cleanup.inc
new file mode 100644
index 00000000..dca6491a
--- /dev/null
+++ b/core/cleanup.inc
@@ -0,0 +1,54 @@
+;; -----------------------------------------------------------------------
+;;
+;; Copyright 2007-2008 H. Peter Anvin - All Rights Reserved
+;;
+;; This program is free software; you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation, Inc., 53 Temple Place Ste 330,
+;; Boston MA 02111-1307, USA; either version 2 of the License, or
+;; (at your option) any later version; incorporated herein by reference.
+;;
+;; -----------------------------------------------------------------------
+
+;;
+;; cleanup.inc
+;;
+;; Some final tidying before jumping to a kernel or bootsector
+;;
+
+ section .text
+;
+; cleanup_hardware:
+;
+; Shut down anything transient. *No segment assumptions*.
+; Can trash any registers.
+;
+cleanup_hardware:
+ pushad
+;
+; Linux wants the floppy motor shut off before starting the kernel,
+; at least bootsect.S seems to imply so. If we don't load the floppy
+; driver, this is *definitely* so!
+;
+ xor ax,ax
+ xor dx,dx
+ int 13h
+
+%if 0 ; This bug report has not been substantiated!
+; Vmware crashes if we scroll in the decompressor! Try to detect vmware
+; and if it is Vmware, clear the screen...
+ mov eax,'VMXh'
+ xor ebx, ebx
+ mov ecx, 10 ; Get version
+ mov dx, 'VX'
+ in eax, dx
+ cmp ebx, 'VMXh'
+ jne .no_vmware
+
+ mov ax,0x0003 ; Set mode (clear screen/home cursor)
+ int 10h
+.no_vmware:
+%endif
+
+ popad
+ ret
diff --git a/core/cmdline.inc b/core/cmdline.inc
new file mode 100644
index 00000000..5d5b3c22
--- /dev/null
+++ b/core/cmdline.inc
@@ -0,0 +1,38 @@
+;; -----------------------------------------------------------------------
+;;
+;; Copyright 2003-2008 H. Peter Anvin - All Rights Reserved
+;;
+;; This program is free software; you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation, Inc., 53 Temple Place Ste 330,
+;; Boston MA 02111-1307, USA; either version 2 of the License, or
+;; (at your option) any later version; incorporated herein by reference.
+;;
+;; -----------------------------------------------------------------------
+
+;;
+;; cmdline.inc
+;;
+;; Common routine to assemble [null-terminated] command line into
+;; real_mode_seg:cmd_line_here.
+;; Not used by plain kernel due to BOOT_IMAGE= etc.
+;;
+
+;
+; Assumes DS == CS
+make_plain_cmdline:
+ push es
+ ; ui.inc has already copied any APPEND options
+ mov ax,real_mode_seg
+ mov es,ax
+
+ mov si,[CmdOptPtr]
+ mov di,[CmdLinePtr]
+
+ call strcpy
+
+ dec di
+ mov [CmdLinePtr],di
+
+ pop es
+ ret
diff --git a/core/com32.inc b/core/com32.inc
new file mode 100644
index 00000000..99954206
--- /dev/null
+++ b/core/com32.inc
@@ -0,0 +1,421 @@
+;; -----------------------------------------------------------------------
+;;
+;; Copyright 1994-2008 H. Peter Anvin - All Rights Reserved
+;;
+;; This program is free software; you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation, Inc., 53 Temple Place Ste 330,
+;; Boston MA 02111-1307, USA; either version 2 of the License, or
+;; (at your option) any later version; incorporated herein by reference.
+;;
+;; -----------------------------------------------------------------------
+
+;;
+;; com32.inc
+;;
+;; Common code for running a COM32 image
+;;
+
+;
+; Load a COM32 image. A COM32 image is the 32-bit analogue to a DOS
+; .com file. A COM32 image is loaded at address 0x101000, with %esp
+; set to the high end of usable memory.
+;
+; A COM32 image should begin with the magic bytes:
+; B8 FF 4C CD 21, which is "mov eax,0x21cd4cff" in 32-bit mode and
+; "mov ax,0x4cff; int 0x21" in 16-bit mode. This will abort the
+; program with an error if run in 16-bit mode.
+;
+pm_idt: equ 0x100000
+pm_entry: equ 0x101000
+
+ bits 16
+ section .data
+ align 2, db 0
+com32_pmidt:
+ dw 8*256 ; Limit
+ dd pm_idt ; Address
+
+com32_rmidt:
+ dw 0ffffh ; Limit
+ dd 0 ; Address
+
+ section .text
+is_com32_image:
+ push si ; Save file handle
+ push eax ; Save file length
+
+ call make_plain_cmdline
+ ; Copy the command line into the low cmdline buffer
+ mov ax,real_mode_seg
+ mov fs,ax
+ mov si,cmd_line_here
+ mov di,command_line
+ mov cx,[CmdLinePtr]
+ inc cx ; Include final null
+ sub cx,si
+ fs rep movsb
+
+ call comboot_setup_api ; Set up the COMBOOT-style API
+
+ mov edi,pm_entry ; Load address
+ pop eax ; File length
+ pop si ; File handle
+ xor dx,dx ; No padding
+ mov bx,abort_check ; Don't print dots, but allow abort
+ call load_high
+
+com32_start:
+ mov ebx,com32_call_start ; Where to go in PM
+
+com32_enter_pm:
+ cli
+ mov ax,cs
+ mov ds,ax
+ mov [RealModeSSSP],sp
+ mov [RealModeSSSP+2],ss
+ cld
+ call a20_test
+ jnz .a20ok
+ call enable_a20
+
+.a20ok:
+ mov byte [bcopy_gdt.TSS+5],89h ; Mark TSS unbusy
+
+ lgdt [bcopy_gdt] ; We can use the same GDT just fine
+ lidt [com32_pmidt] ; Set up the IDT
+ mov eax,cr0
+ or al,1
+ mov cr0,eax ; Enter protected mode
+ jmp PM_CS32:.in_pm
+
+ bits 32
+.in_pm:
+ xor eax,eax ; Available for future use...
+ mov fs,eax
+ mov gs,eax
+ lldt ax
+
+ mov al,PM_DS32 ; Set up data segments
+ mov es,eax
+ mov ds,eax
+ mov ss,eax
+
+ mov al,PM_TSS ; Be nice to Intel's VT by
+ ltr ax ; giving it a valid TR
+
+ mov esp,[PMESP] ; Load protmode %esp if available
+ jmp ebx ; Go to where we need to go
+
+;
+; This is invoked right before the actually starting the COM32
+; progam, in 32-bit mode...
+;
+com32_call_start:
+ ;
+ ; Point the stack to the end of (permitted) high memory
+ ;
+ mov esp,[word HighMemRsvd]
+ xor sp,sp ; Align to a 64K boundary
+
+ ;
+ ; Set up the protmode IDT and the interrupt jump buffers
+ ; We set these up in the system area at 0x100000,
+ ; but we could also put them beyond the stack.
+ ;
+ mov edi,pm_idt
+
+ ; Form an interrupt gate descriptor
+ mov eax,0x00200000+((pm_idt+8*256)&0x0000ffff)
+ mov ebx,0x0000ee00+((pm_idt+8*256)&0xffff0000)
+ xor ecx,ecx
+ inc ch ; ecx <- 256
+
+ push ecx
+.make_idt:
+ stosd
+ add eax,8
+ xchg eax,ebx
+ stosd
+ xchg eax,ebx
+ loop .make_idt
+
+ pop ecx
+
+ ; Each entry in the interrupt jump buffer contains
+ ; the following instructions:
+ ;
+ ; 00000000 60 pushad
+ ; 00000001 B0xx mov al,<interrupt#>
+ ; 00000003 E9xxxxxxxx jmp com32_handle_interrupt
+
+ mov eax,0e900b060h
+ mov ebx,com32_handle_interrupt-(pm_idt+8*256+8)
+
+.make_ijb:
+ stosd
+ sub [edi-2],cl ; Interrupt #
+ xchg eax,ebx
+ stosd
+ sub eax,8
+ xchg eax,ebx
+ loop .make_ijb
+
+ ; Now everything is set up for interrupts...
+
+ push dword com32_cfarcall ; Cfarcall entry point
+ push dword com32_farcall ; Farcall entry point
+ push dword (1 << 16) ; 64K bounce buffer
+ push dword (comboot_seg << 4) ; Bounce buffer address
+ push dword com32_intcall ; Intcall entry point
+ push dword command_line ; Command line pointer
+ push dword 6 ; Argument count
+ sti ; Interrupts OK now
+ call pm_entry ; Run the program...
+ ; ... on return, fall through to com32_exit ...
+
+com32_exit:
+ mov bx,com32_done ; Return to command loop
+
+com32_enter_rm:
+ cli
+ cld
+ mov [PMESP],esp ; Save exit %esp
+ xor esp,esp ; Make sure the high bits are zero
+ jmp PM_CS16:.in_pm16 ; Return to 16-bit mode first
+
+ bits 16
+.in_pm16:
+ mov ax,PM_DS16_RM ; Real-mode-like segment
+ mov es,ax
+ mov ds,ax
+ mov ss,ax
+ mov fs,ax
+ mov gs,ax
+
+ lidt [com32_rmidt] ; Real-mode IDT (rm needs no GDT)
+ mov eax,cr0
+ and al,~1
+ mov cr0,eax
+ jmp 0:.in_rm
+
+.in_rm: ; Back in real mode
+ mov ax,cs ; Set up sane segments
+ mov ds,ax
+ mov es,ax
+ mov fs,ax
+ mov gs,ax
+ lss sp,[RealModeSSSP] ; Restore stack
+ jmp bx ; Go to whereever we need to go...
+
+com32_done:
+%if DISABLE_A20
+ call disable_a20
+%endif
+ sti
+ jmp enter_command
+
+;
+; 16-bit support code
+;
+ bits 16
+
+;
+; 16-bit interrupt-handling code
+;
+com32_int_rm:
+ pushf ; Flags on stack
+ push cs ; Return segment
+ push word .cont ; Return address
+ push dword edx ; Segment:offset of IVT entry
+ retf ; Invoke IVT routine
+.cont: ; ... on resume ...
+ mov ebx,com32_int_resume
+ jmp com32_enter_pm ; Go back to PM
+
+;
+; 16-bit intcall/farcall handling code
+;
+com32_sys_rm:
+ pop gs
+ pop fs
+ pop es
+ pop ds
+ popad
+ popfd
+ mov [cs:Com32SysSP],sp
+ retf ; Invoke routine
+.return:
+ ; We clean up SP here because we don't know if the
+ ; routine returned with RET, RETF or IRET
+ mov sp,[cs:Com32SysSP]
+ pushfd
+ pushad
+ push ds
+ push es
+ push fs
+ push gs
+ mov ebx,com32_syscall.resume
+ jmp com32_enter_pm
+
+;
+; 16-bit cfarcall handing code
+;
+com32_cfar_rm:
+ retf
+.return:
+ mov sp,[cs:Com32SysSP]
+ mov [cs:RealModeEAX],eax
+ mov ebx,com32_cfarcall.resume
+ jmp com32_enter_pm
+
+;
+; 32-bit support code
+;
+ bits 32
+
+;
+; This is invoked on getting an interrupt in protected mode. At
+; this point, we need to context-switch to real mode and invoke
+; the interrupt routine.
+;
+; When this gets invoked, the registers are saved on the stack and
+; AL contains the register number.
+;
+com32_handle_interrupt:
+ movzx eax,al
+ xor ebx,ebx ; Actually makes the code smaller
+ mov edx,[ebx+eax*4] ; Get the segment:offset of the routine
+ mov bx,com32_int_rm
+ jmp com32_enter_rm ; Go to real mode
+
+com32_int_resume:
+ popad
+ iret
+
+;
+; Intcall/farcall invocation. We manifest a structure on the real-mode stack,
+; containing the com32sys_t structure from <com32.h> as well as
+; the following entries (from low to high address):
+; - Target offset
+; - Target segment
+; - Return offset
+; - Return segment (== real mode cs == 0)
+; - Return flags
+;
+com32_farcall:
+ pushfd ; Save IF among other things...
+ pushad ; We only need to save some, but...
+
+ mov eax,[esp+10*4] ; CS:IP
+ jmp com32_syscall
+
+
+com32_intcall:
+ pushfd ; Save IF among other things...
+ pushad ; We only need to save some, but...
+
+ movzx eax,byte [esp+10*4] ; INT number
+ mov eax,[eax*4] ; Get CS:IP from low memory
+
+com32_syscall:
+ cld
+
+ movzx edi,word [word RealModeSSSP]
+ movzx ebx,word [word RealModeSSSP+2]
+ sub edi,54 ; Allocate 54 bytes
+ mov [word RealModeSSSP],di
+ shl ebx,4
+ add edi,ebx ; Create linear address
+
+ mov esi,[esp+11*4] ; Source regs
+ xor ecx,ecx
+ mov cl,11 ; 44 bytes to copy
+ rep movsd
+
+ ; EAX is already set up to be CS:IP
+ stosd ; Save in stack frame
+ mov eax,com32_sys_rm.return ; Return seg:offs
+ stosd ; Save in stack frame
+ mov eax,[edi-12] ; Return flags
+ and eax,0x200cd7 ; Mask (potentially) unsafe flags
+ mov [edi-12],eax ; Primary flags entry
+ stosw ; Return flags
+
+ mov bx,com32_sys_rm
+ jmp com32_enter_rm ; Go to real mode
+
+ ; On return, the 44-byte return structure is on the
+ ; real-mode stack, plus the 10 additional bytes used
+ ; by the target address (see above.)
+.resume:
+ movzx esi,word [word RealModeSSSP]
+ movzx eax,word [word RealModeSSSP+2]
+ mov edi,[esp+12*4] ; Dest regs
+ shl eax,4
+ add esi,eax ; Create linear address
+ and edi,edi ; NULL pointer?
+ jnz .do_copy
+.no_copy: mov edi,esi ; Do a dummy copy-to-self
+.do_copy: xor ecx,ecx
+ mov cl,11 ; 44 bytes
+ rep movsd ; Copy register block
+
+ add dword [word RealModeSSSP],54 ; Remove from stack
+
+ popad
+ popfd
+ ret ; Return to 32-bit program
+
+;
+; Cfarcall invocation. We copy the stack frame to the real-mode stack,
+; followed by the return CS:IP and the CS:IP of the target function.
+;
+com32_cfarcall:
+ pushfd
+ pushad
+
+ cld
+ mov ecx,[esp+12*4] ; Size of stack frame
+
+ movzx edi,word [word RealModeSSSP]
+ movzx ebx,word [word RealModeSSSP+2]
+ mov [word Com32SysSP],di
+ sub edi,ecx ; Allocate space for stack frame
+ and edi,~3 ; Round
+ sub edi,4*2 ; Return pointer, return value
+ mov [word RealModeSSSP],di
+ shl ebx,4
+ add edi,ebx ; Create linear address
+
+ mov eax,[esp+10*4] ; CS:IP
+ stosd ; Save to stack frame
+ mov eax,com32_cfar_rm.return ; Return seg:off
+ stosd
+ mov esi,[esp+11*4] ; Stack frame
+ mov eax,ecx ; Copy the stack frame
+ shr ecx,2
+ rep movsd
+ mov ecx,eax
+ and ecx,3
+ rep movsb
+
+ mov bx,com32_cfar_rm
+ jmp com32_enter_rm
+
+.resume:
+ popad
+ mov eax,[word RealModeEAX]
+ popfd
+ ret
+
+ bits 16
+
+ section .bss1
+ alignb 4
+RealModeSSSP resd 1 ; Real-mode SS:SP
+RealModeEAX resd 1 ; Real mode EAX
+PMESP resd 1 ; Protected-mode ESP
+Com32SysSP resw 1 ; SP saved during COM32 syscall
+
+ section .text
diff --git a/core/comboot.inc b/core/comboot.inc
new file mode 100644
index 00000000..df9bb001
--- /dev/null
+++ b/core/comboot.inc
@@ -0,0 +1,988 @@
+;; -----------------------------------------------------------------------
+;;
+;; Copyright 1994-2008 H. Peter Anvin - All Rights Reserved
+;;
+;; This program is free software; you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation, Inc., 53 Temple Place Ste 330,
+;; Boston MA 02111-1307, USA; either version 2 of the License, or
+;; (at your option) any later version; incorporated herein by reference.
+;;
+;; -----------------------------------------------------------------------
+
+;;
+;; comboot.inc
+;;
+;; Common code for running a COMBOOT image
+;;
+
+ section .text
+
+; Parameter registers definition; this is the definition
+; of the stack frame used by INT 21h and INT 22h.
+%define P_FLAGS word [bp+44]
+%define P_FLAGSL byte [bp+44]
+%define P_FLAGSH byte [bp+45]
+%define P_CS word [bp+42]
+%define P_IP word [bp+40]
+%define P_DS word [bp+38]
+%define P_ES word [bp+36]
+%define P_FS word [bp+34]
+%define P_GS word [bp+32]
+%define P_EAX dword [bp+28]
+%define P_AX word [bp+28]
+%define P_HAX word [bp+30]
+%define P_AL byte [bp+28]
+%define P_AH byte [bp+29]
+%define P_ECX dword [bp+24]
+%define P_CX word [bp+24]
+%define P_HCX word [bp+26]
+%define P_CL byte [bp+24]
+%define P_CH byte [bp+25]
+%define P_EDX dword [bp+20]
+%define P_DX word [bp+20]
+%define P_HDX word [bp+22]
+%define P_DL byte [bp+20]
+%define P_DH byte [bp+21]
+%define P_EBX dword [bp+16]
+%define P_BX word [bp+16]
+%define P_HBX word [bp+18]
+%define P_BL byte [bp+16]
+%define P_BH byte [bp+17]
+%define P_EBP dword [bp+8]
+%define P_BP word [bp+8]
+%define P_HBP word [bp+10]
+%define P_ESI dword [bp+4]
+%define P_SI word [bp+4]
+%define P_HSI word [bp+6]
+%define P_EDI dword [bp]
+%define P_DI word [bp]
+%define P_HDI word [bp+2]
+
+; Looks like a COMBOOT image but too large
+comboot_too_large:
+ call close_file
+ mov si,err_comlarge
+ call cwritestr
+ jmp enter_command
+
+;
+; Load a COMBOOT image. A COMBOOT image is basically a DOS .COM file,
+; except that it may, of course, not contain any DOS system calls. We
+; do, however, allow the execution of INT 20h to return to SYSLINUX.
+;
+is_comboot_image:
+ push si ; Save file handle
+
+ call make_plain_cmdline
+
+ call comboot_setup_api
+
+ mov cx,comboot_seg
+ mov es,cx
+
+ xor di,di
+ mov cx,64 ; 256 bytes (size of PSP)
+ xor eax,eax ; Clear PSP
+ rep stosd
+
+ mov word [es:0], 020CDh ; INT 20h instruction
+ ; First non-free paragraph
+ ; This is valid because comboot_seg == real_mode_seg
+ ; == the highest segment used by all derivatives
+ int 12h ; Get DOS memory size
+ shl ax,6 ; Kilobytes -> paragraphs
+ mov word [es:02h],ax
+
+%ifndef DEPEND
+%if real_mode_seg != comboot_seg
+%error "This code assumes real_mode_seg == comboot_seg"
+%endif
+%endif
+ ; Copy the command line from high memory
+ mov si,cmd_line_here
+ mov cx,125 ; Max cmdline len (minus space and CR)
+ mov di,081h ; Offset in PSP for command line
+ mov al,' ' ; DOS command lines begin with a space
+ stosb
+
+.loop: es lodsb
+ and al,al
+ jz .done
+ stosb
+ loop .loop
+.done:
+
+ mov al,0Dh ; CR after last character
+ stosb
+ mov ax,di
+ sub al,82h ; Include space but not CR
+ mov [es:80h],al ; Store command line length
+
+ ; Now actually load the file...
+ pop si ; File handle
+ mov bx,100h ; Load at <seg>:0100h
+ mov cx,10000h >> SECTOR_SHIFT
+ ; Absolute maximum # of sectors
+ call getfssec
+ cmp ecx,65536-256-2 ; Maximum size
+ ja comboot_too_large
+
+ ; And invoke the program...
+ mov ax,es
+ mov ds,ax
+ mov ss,ax
+ xor sp,sp
+ push word 0 ; Return to address 0 -> exit
+
+ jmp comboot_seg:100h ; Run it
+
+; Proper return vector
+comboot_return: cli ; Don't trust anyone
+ push enter_command ; Normal return to command prompt
+ jmp comboot_exit
+
+;
+; Set up the COMBOOT API interrupt vectors. This is also used
+; by the COM32 code.
+;
+comboot_setup_api:
+ mov di,4*0x20 ; DOS interrupt vectors
+ mov eax,comboot_return ; INT 20h = exit
+ stosd
+ mov ax,comboot_int21 ; INT 21h = DOS-compatible syscalls
+ stosd
+ mov ax,comboot_int22 ; INT 22h = proprietary syscalls
+ stosd
+ mov ax,comboot_bogus
+ mov cx,29 ; All remaining DOS vectors
+ rep stosd
+ ret
+
+; INT 21h: generic DOS system call
+comboot_int21: cli
+ push ds
+ push es
+ push fs
+ push gs
+ pushad
+ cld
+ mov bp,cs
+ mov ds,bp
+ mov es,bp
+ mov bp,sp ; Set up stack frame
+
+ call adjust_screen ; The COMBOOT program might have changed the screen
+
+ mov cx,int21_count
+ mov si,int21_table
+.again: lodsb
+ cmp al,P_AH
+ lodsw
+ loopne .again
+ ; The last function in the list is the
+ ; "no such function" function
+ clc
+ call ax ; Call the invoked function
+comboot_resume:
+ mov bp,sp ; In case the function clobbers BP
+ setc P_FLAGSL ; Propagate CF->error
+ popad
+ pop gs
+ pop fs
+ pop es
+ pop ds
+ iret
+
+; Attempted to execute non-21h DOS system call
+comboot_bogus: cli ; Don't trust anyone
+ mov cx,err_notdos
+ push enter_command
+ jmp comboot_exit_msg
+
+;
+; Generic COMBOOT return to command line code
+; stack -> where to go next
+; CX -> message (for _msg version)
+;
+comboot_exit:
+ xor cx,cx
+comboot_exit_msg:
+ pop bx ; Return address
+ RESET_STACK_AND_SEGS AX
+ call adjust_screen ; The COMBOOT program might have changed the screen
+ jcxz .nomsg
+ mov si,KernelCName
+ call cwritestr
+ mov si,cx
+ call cwritestr
+.nomsg:
+ jmp bx
+
+;
+; INT 21h system calls
+;
+comboot_getkey: ; 01 = get key with echo
+ call vgashowcursor
+ call comboot_getchar
+ call vgahidecursor
+ call writechr
+ clc
+ ret
+
+comboot_writechr: ; 02 = writechr
+ mov al,P_DL
+ call writechr
+ clc
+ ret
+
+comboot_writeserial: ; 04 = write serial port
+ mov al,P_DL
+ call write_serial
+ clc
+ ret
+
+comboot_getkeynoecho: ; 08 = get key w/o echo
+ call comboot_getchar
+ clc
+ ret
+
+comboot_writestr: ; 09 = write DOS string
+ mov es,P_DS
+ mov si,P_DX
+.loop: es lodsb
+ cmp al,'$' ; End string with $ - bizarre
+ je .done
+ call writechr
+ jmp short .loop
+.done: clc
+ ret
+
+comboot_checkkey: ; 0B = check keyboard status
+ cmp byte [APIKeyFlag],00h
+ jnz .waiting
+ call pollchar
+.waiting: setz al
+ dec al ; AL = 0FFh if present, 0 if not
+ mov P_AL,al
+ clc
+ ret
+
+comboot_checkver: ; 30 = check DOS version
+ ; We return 0 in all DOS-compatible version registers,
+ ; but the high part of eax-ebx-ecx-edx spell "SYSLINUX"
+ mov P_EAX,'SY' << 16
+ mov P_EBX,'SL' << 16
+ mov P_ECX,'IN' << 16
+ mov P_EDX,'UX' << 16
+ ret
+
+comboot_getchar:
+ cmp byte [APIKeyFlag],00h
+ jne .queued
+ call getchar ; If not queued get input
+ and al,al ; Function key? (CF <- 0)
+ jnz .done
+ mov [APIKeyWait],ah ; High part of key
+ inc byte [APIKeyFlag] ; Set flag
+.done: mov P_AL,al
+ ret
+.queued: mov al,[APIKeyWait]
+ dec byte [APIKeyFlag]
+ jmp .done
+
+;
+; INT 22h - SYSLINUX-specific system calls
+; System call number in ax
+;
+comboot_int22:
+ cli
+ push ds
+ push es
+ push fs
+ push gs
+ pushad
+ cld
+ mov bp,cs
+ mov ds,bp
+ mov es,bp
+ mov bp,sp ; Set up stack frame
+
+ call adjust_screen ; The COMBOOT program might have changed the screen
+
+ cmp ax,int22_count
+ jb .ok
+ xor ax,ax ; Function 0 -> unimplemented
+.ok:
+ xchg ax,bx
+ add bx,bx ; CF <- 0
+ call [bx+int22_table]
+ jmp comboot_resume ; On return
+
+;
+; INT 22h AX=0000h Unimplemented call
+;
+comapi_err:
+ stc
+ ret
+
+;
+; INT 22h AX=0001h Get SYSLINUX version
+;
+comapi_get_version:
+ ; Number of API functions supported
+ mov P_AX,int22_count
+ ; SYSLINUX version
+ mov P_CX,(VER_MAJOR << 8)+VER_MINOR
+ ; SYSLINUX derivative ID byte
+ mov P_DX,my_id
+ ; For future use
+ mov P_BX,cs ; cs == 0
+
+ mov P_ES,ds
+ ; ES:SI -> version banner
+ mov P_SI,syslinux_banner
+ ; ES:DI -> copyright string
+ mov P_DI,copyright_str
+
+comapi_nop:
+ clc
+ ret
+
+;
+; INT 22h AX=0002h Write string
+;
+; Write null-terminated string in ES:BX
+;
+comapi_writestr:
+ mov ds,P_ES
+ mov si,P_BX
+ call writestr
+ clc
+ ret
+
+;
+; INT 22h AX=0003h Run command
+;
+; Terminates the COMBOOT program and executes the command line in
+; ES:BX as if it had been entered by the user.
+;
+comapi_run:
+ mov ds,P_ES
+ mov si,P_BX
+ mov di,command_line
+ call strcpy
+ push load_kernel ; Run a new kernel
+ jmp comboot_exit ; Terminate task, clean up
+
+;
+; INT 22h AX=0004h Run default command
+;
+; Terminates the COMBOOT program and executes the default command line
+; as if a timeout had happened or the user pressed <Enter>.
+;
+comapi_run_default:
+ push auto_boot
+ jmp comboot_exit
+
+;
+; INT 22h AX=0005h Force text mode
+;
+; Puts the video in standard text mode
+;
+comapi_textmode:
+ call vgaclearmode
+ clc
+ ret
+
+;
+; INT 22h AX=0006h Open file
+;
+comapi_open:
+ push ds
+ mov ds,P_ES
+ mov si,P_SI
+ mov di,InitRD
+ call mangle_name
+ pop ds
+ call searchdir
+ jz comapi_err
+ mov P_EAX,eax
+ mov P_CX,SECTOR_SIZE
+ mov P_SI,si
+ clc
+ ret
+
+;
+; INT 22h AX=0007h Read file
+;
+comapi_read:
+ mov es,P_ES
+ mov bx,P_BX
+ mov si,P_SI
+ mov cx,P_CX
+ call getfssec
+ jnc .noteof
+ xor si,si ; SI <- 0 on EOF, CF <- 0
+.noteof: mov P_SI,si
+ mov P_ECX,ecx
+ ret
+
+;
+; INT 22h AX=0008h Close file
+;
+comapi_close:
+ mov si,P_SI
+ call close_file
+ clc
+ ret
+
+;
+; INT 22h AX=0009h Call PXE stack
+;
+%if IS_PXELINUX
+comapi_pxecall:
+ mov bx,P_BX
+ mov es,P_ES
+ mov di,P_DI
+ call pxenv
+ mov ax,[PXEStatus]
+ mov P_AX,ax
+ ret
+%else
+comapi_pxecall equ comapi_err ; Not available
+%endif
+
+;
+; INT 22h AX=000Ah Get Derivative-Specific Info
+;
+comapi_derinfo:
+ mov P_AL,my_id
+%if IS_PXELINUX
+ mov ax,[APIVer]
+ mov P_DX,ax
+ mov ax,[StrucPtr]
+ mov P_BX,ax
+ mov ax,[StrucPtr+2]
+ mov P_ES,ax
+ mov ax,[InitStack]
+ mov P_SI,ax
+ mov ax,[InitStack+2]
+ mov P_FS,ax
+%else
+ ; Physical medium...
+
+ mov P_CL,SECTOR_SHIFT
+ mov al,[DriveNumber]
+ mov P_DL,al
+ mov P_FS,cs
+ mov P_SI,OrigESDI
+%if IS_SYSLINUX || IS_MDSLINUX || IS_EXTLINUX
+ mov P_ES,cs
+ mov P_BX,PartInfo
+%elif IS_ISOLINUX
+ mov P_ES,cs
+ mov P_BX,spec_packet
+%endif
+%endif
+ clc
+ ret
+
+;
+; INT 22h AX=000Bh Get Serial Console Configuration
+;
+comapi_serialcfg:
+ mov ax,[SerialPort]
+ mov P_DX,ax
+ mov ax,[BaudDivisor]
+ mov P_CX,ax
+ mov ax,[FlowControl]
+ or al,ah
+ mov ah,[FlowIgnore]
+ shr ah,4
+ test byte [DisplayCon],01h
+ jnz .normalconsole
+ or ah,80h
+.normalconsole:
+ mov P_BX,ax
+ clc
+ ret
+
+;
+; INT 22h AX=000Ch Perform final cleanup
+;
+comapi_cleanup:
+%if IS_PXELINUX
+ ; Unload PXE if requested
+ test dl,3
+ setnz [KeepPXE]
+ sub bp,sp ; unload_pxe may move the stack around
+ call unload_pxe
+ add bp,sp ; restore frame pointer...
+%elif IS_SYSLINUX || IS_MDSLINUX || IS_EXTLINUX
+ ; Restore original FDC table
+ mov eax,[OrigFDCTabPtr]
+ mov [fdctab],eax
+%endif
+ ; Reset the floppy disk subsystem
+ xor ax,ax
+ xor dx,dx
+ int 13h
+ clc
+ ret
+
+;
+; INT 22h AX=000Dh Clean up then replace bootstrap
+;
+comapi_chainboot:
+ call comapi_cleanup
+ mov eax,P_EDI
+ mov [trackbuf+4],eax ; Copy from
+ mov eax,P_ECX
+ mov [trackbuf+8],eax ; Total bytes
+ mov eax,7C00h
+ mov [trackbuf],eax ; Copy to
+ mov [EntryPoint],eax ; CS:IP entry point
+ mov esi,P_ESI
+ mov edx,P_EBX
+ mov bx,P_DS
+ jmp replace_bootstrap_one
+
+
+;
+; INT 22h AX=000Eh Get configuration file name
+;
+comapi_configfile:
+ mov P_ES,cs
+ mov P_BX,ConfigName
+ clc
+ ret
+
+;
+; INT 22h AX=000Fh Get IPAPPEND strings
+;
+%if IS_PXELINUX
+comapi_ipappend:
+ mov P_ES,cs
+ mov P_CX,numIPAppends
+ mov P_BX,IPAppends
+ clc
+ ret
+
+ section .data
+ alignb 2, db 0
+IPAppends dw IPOption
+ dw BOOTIFStr
+numIPAppends equ ($-IPAppends)/2
+
+%else
+comapi_ipappend equ comapi_err
+%endif
+
+ section .text
+
+;
+; INT 22h AX=0010h Resolve hostname
+;
+%if IS_PXELINUX
+comapi_dnsresolv:
+ mov ds,P_ES
+ mov si,P_BX
+ call dns_resolv
+ mov P_EAX,eax
+ clc
+ ret
+%else
+comapi_dnsresolv equ comapi_err
+%endif
+
+ section .text
+
+;
+; INT 22h AX=0011h Maximum number of shuffle descriptors
+;
+comapi_maxshuffle:
+ mov P_CX,trackbufsize/12
+ ret
+
+;
+; INT 22h AX=0012h Cleanup, shuffle and boot
+;
+comapi_shuffle:
+ cmp P_CX,(2*trackbufsize)/12
+ ja .error
+
+ call comapi_cleanup
+
+ mov cx, P_CX
+ push cx ; On stack: descriptor count
+
+ lea cx,[ecx+ecx*2] ; CX *= 3
+
+ mov fs,P_ES
+ mov si,P_DI
+ mov di,trackbuf
+ push di ; On stack: descriptor list address
+ fs rep movsd ; Copy the list
+
+ mov eax,P_EBP
+ mov [EntryPoint],eax ; CS:IP entry point
+ mov esi,P_ESI
+ mov edx,P_EBX
+ mov bx,P_DS
+ jmp replace_bootstrap
+.error:
+ stc
+ ret
+
+;
+; INT 22h AX=0013h Idle call
+;
+;
+; *** FIX THIS ***
+; The idle call seems to have detrimental effects on some machines when
+; called from a COM32 context (WHY?) -- disable it for now.
+;
+%if 0 ; def HAVE_IDLE
+
+comapi_idle:
+ DO_IDLE
+ clc
+ ret
+
+%else
+
+comapi_idle equ comapi_err
+
+%endif
+
+;
+; INT 22h AX=0014h Local boot
+;
+%if HAS_LOCALBOOT
+comapi_localboot:
+ mov ax,P_DX
+ jmp local_boot
+%else
+comapi_localboot equ comapi_err
+%endif ; HAS_LOCALBOOT
+
+;
+; INT 22h AX=0015h Feature flags
+;
+comapi_features:
+ mov P_ES,cs
+ mov P_BX,feature_flags
+ mov P_CX,feature_flags_len
+ clc
+ ret
+
+;
+; INT 22h AX=0016h Run kernel image
+;
+comapi_runkernel:
+ mov al,P_DL
+ cmp al,VK_TYPES-1
+ ja .error
+ mov [KernelType],al
+ push ds
+ mov ds,P_DS
+ mov si,P_SI
+ mov di,KernelName
+ call mangle_name
+ pop ds
+ call searchdir
+ jz comapi_err
+
+ ; The kernel image was found, so we can load it...
+ mov [Kernel_SI],si
+ mov [Kernel_EAX],eax
+
+ ; It's not just possible, but quite likely, that ES:BX
+ ; points into real_mode_seg, so we need to exercise some
+ ; special care here... use xfer_buf_seg as an intermediary
+ push ds
+ push es
+ mov ax,xfer_buf_seg
+ mov ds,P_ES
+ mov si,P_BX
+ mov es,ax
+ xor di,di
+ call strcpy
+ pop es
+ pop ds
+
+%if IS_PXELINUX
+ mov al,P_CL
+ mov [IPAppend],al
+%endif
+
+ call comboot_exit
+
+.finish:
+ ; Copy the command line into its proper place
+ push ds
+ push es
+ mov ax,xfer_buf_seg
+ mov dx,real_mode_seg
+ mov ds,ax
+ mov es,dx
+ xor si,si
+ mov di,cmd_line_here
+ call strcpy
+ mov byte [es:di-1],' ' ; Simulate APPEND
+ pop es
+ pop ds
+ mov [CmdLinePtr],di
+ mov word [CmdOptPtr],zero_string
+ jmp kernel_good_saved
+
+.error equ comapi_shuffle.error
+
+;
+; INT 22h AX=0017h Report video mode change
+;
+comapi_usingvga:
+ mov ax,P_BX
+ cmp ax,0Fh ; Unknown flags = failure
+ ja .error
+ mov [UsingVGA],al
+ mov cx,P_CX
+ mov dx,P_DX
+ mov [GXPixCols],cx
+ mov [GXPixRows],dx
+ test al,08h
+ jnz .notext
+ call adjust_screen
+.notext:
+ clc
+ ret
+.error:
+ stc
+ ret
+
+;
+; INT 22h AX=0018h Query custom font
+;
+comapi_userfont:
+ mov al,[UserFont]
+ and al,al
+ jz .done
+ mov al,[VGAFontSize]
+ mov P_ES,ds
+ mov P_BX,vgafontbuf
+
+.done: ; CF=0 here
+ mov P_AL,al
+ ret
+
+;
+; INT 22h AX=0019h Read disk
+;
+%if IS_SYSLINUX || IS_MDSLINUX || IS_ISOLINUX || IS_EXTLINUX
+comapi_readdisk:
+ mov esi,P_ESI ; Enforce ESI == EDI == 0, these
+ or esi,P_EDI ; are reserved for future expansion
+ jnz .err
+ mov eax,P_EDX
+ mov es,P_ES
+ mov bx,P_BX
+ mov bp,P_CX ; WE CANNOT use P_* after touching bp!
+ call getlinsec
+ clc
+ ret
+.err:
+ stc
+ ret
+%else
+comapi_readdisk equ comapi_err
+%endif
+
+;
+; INT 22h AX=001Ah Cleanup, shuffle and boot to flat protected mode
+;
+comapi_shufflepm:
+ cmp P_CX,(2*trackbufsize)/12
+ ja .error
+
+ call comapi_cleanup
+
+ mov cx, P_CX
+ push cx ; On stack: descriptor count
+
+ lea cx,[ecx+ecx*2] ; CX *= 3
+
+ mov fs,P_ES
+ mov si,P_DI
+ mov di,trackbuf
+ push di ; On stack: descriptor list address
+ fs rep movsd ; Copy the list
+
+ mov fs,P_DS
+ mov si,P_SI
+ mov edi,TrampolineBuf
+ mov al,0B8h ; MOV EAX opcode
+ mov cl,9
+.maketramp:
+ stosb ; MOV opcode
+ inc ax ; Next register opcode
+ fs movsd ; immediate value
+ loop .maketramp
+ mov byte [di-5],0E9h ; Last opcode is JMP
+ sub [di-4],edi ; Make JMP target relative
+
+ mov dword [EntryPoint],trampoline_to_pm
+ xor bx,bx ; DS on entry
+ jmp replace_bootstrap
+.error:
+ stc
+ ret
+
+;
+; INT 22h AX=001Bh Cleanup, shuffle and boot with register setting
+;
+comapi_shufflerm:
+ cmp P_CX,(2*trackbufsize)/12
+ ja .error
+
+ call comapi_cleanup
+
+ mov cx, P_CX
+ push cx ; On stack: descriptor count
+
+ lea cx,[ecx+ecx*2] ; CX *= 3
+
+ mov fs,P_ES
+ mov si,P_DI
+ mov di,trackbuf
+ push di ; On stack: descriptor list address
+ fs rep movsd ; Copy the list
+
+ mov fs,P_DS
+ mov si,P_SI
+ mov di,TrampolineBuf
+
+ ; Generate segment-loading instructions
+ mov bx,0C08Eh ; MOV ES,AX
+ mov cl,6 ; 6 segment registers (incl CS)
+.segtramp:
+ mov al,0B8h
+ stosb ; MOV AX,imm16 opcode
+ fs movsw ; imm16
+ mov ax,bx
+ add bh,8
+ stosw ; MOV xS,AX
+ loop .segtramp
+
+ ; Clobber the MOV CS,AX instruction.
+ mov word [di-22], 9090h ; NOP NOP
+
+ ; Generate GPR-loading instructions
+ mov ax,0B866h ; MOV EAX,imm32
+ mov cl,8 ; 8 GPRs
+.gprtramp:
+ stosw ; MOV ExX,imm32 opcode
+ fs movsd ; imm32
+ inc ah
+ loop .gprtramp
+
+ mov al,0EAh ; JMP FAR imm16:imm16 opcode
+ stosb
+ fs movsd ; CS:IP
+
+ mov dword [EntryPoint],TrampolineBuf
+ jmp replace_bootstrap
+.error:
+ stc
+ ret
+
+;
+; INT 22h AX=001Ch Get pointer to auxillary data vector
+;
+comapi_getadv:
+ mov P_ES,ds
+ mov P_BX,adv0.data
+ mov P_CX,ADV_LEN
+ ret
+
+;
+; INT 22h AX=001Dh Write auxillary data vector
+;
+comapi_writeadv equ adv_write
+
+ section .data
+
+%macro int21 2
+ db %1
+ dw %2
+%endmacro
+
+int21_table:
+ int21 00h, comboot_return
+ int21 01h, comboot_getkey
+ int21 02h, comboot_writechr
+ int21 04h, comboot_writeserial
+ int21 08h, comboot_getkeynoecho
+ int21 09h, comboot_writestr
+ int21 0Bh, comboot_checkkey
+ int21 30h, comboot_checkver
+ int21 4Ch, comboot_return
+ int21 -1, comboot_bogus
+int21_count equ ($-int21_table)/3
+
+ align 2, db 0
+int22_table:
+ dw comapi_err ; 0000 unimplemented syscall
+ dw comapi_get_version ; 0001 get SYSLINUX version
+ dw comapi_writestr ; 0002 write string
+ dw comapi_run ; 0003 run specified command
+ dw comapi_run_default ; 0004 run default command
+ dw comapi_textmode ; 0005 force text mode
+ dw comapi_open ; 0006 open file
+ dw comapi_read ; 0007 read file
+ dw comapi_close ; 0008 close file
+ dw comapi_pxecall ; 0009 call PXE stack
+ dw comapi_derinfo ; 000A derivative-specific info
+ dw comapi_serialcfg ; 000B get serial port config
+ dw comapi_cleanup ; 000C perform final cleanup
+ dw comapi_chainboot ; 000D clean up then bootstrap
+ dw comapi_configfile ; 000E get name of config file
+ dw comapi_ipappend ; 000F get ipappend strings
+ dw comapi_dnsresolv ; 0010 resolve hostname
+ dw comapi_maxshuffle ; 0011 maximum shuffle descriptors
+ dw comapi_shuffle ; 0012 cleanup, shuffle and boot
+ dw comapi_idle ; 0013 idle call
+ dw comapi_localboot ; 0014 local boot
+ dw comapi_features ; 0015 feature flags
+ dw comapi_runkernel ; 0016 run kernel image
+ dw comapi_usingvga ; 0017 report video mode change
+ dw comapi_userfont ; 0018 query custom font
+ dw comapi_readdisk ; 0019 read disk
+ dw comapi_shufflepm ; 001A cleanup, shuffle and boot to pm
+ dw comapi_shufflerm ; 001B cleanup, shuffle and boot to rm
+ dw comapi_getadv ; 001C get pointer to ADV
+ dw comapi_writeadv ; 001D write ADV to disk
+int22_count equ ($-int22_table)/2
+
+APIKeyWait db 0
+APIKeyFlag db 0
+
+zero_string db 0 ; Empty, null-terminated string
+
+;
+; This is the feature flag array for INT 22h AX=0015h
+feature_flags:
+%if IS_PXELINUX
+ db 1 ; Have local boot, idle not noop
+%elif IS_ISOLINUX
+ db 3 ; Have local boot, idle is noop
+%else
+ db 2 ; No local boot, idle is noop
+%endif
+feature_flags_len equ ($-feature_flags)
+
+err_notdos db ': attempted DOS system call', CR, LF, 0
+err_comlarge db 'COMBOOT image too large.', CR, LF, 0
+
+ section .bss1
+ConfigName resb FILENAME_MAX
diff --git a/core/config.inc b/core/config.inc
new file mode 100644
index 00000000..f696f291
--- /dev/null
+++ b/core/config.inc
@@ -0,0 +1,60 @@
+;; -----------------------------------------------------------------------
+;;
+;; Copyright 2002-2008 H. Peter Anvin - All Rights Reserved
+;;
+;; This program is free software; you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation, Inc., 53 Temple Place Ste 330,
+;; Boston MA 02111-1307, USA; either version 2 of the License, or
+;; (at your option) any later version; incorporated herein by reference.
+;;
+;; -----------------------------------------------------------------------
+
+;;
+;; config.inc
+;;
+;; Common configuration options. Some of these are imposed by the kernel.
+;;
+
+%ifndef _CONFIG_INC
+%define _CONFIG_INC
+
+max_cmd_len equ 2047 ; Must be &3; 2047 is the kernel limit
+HIGHMEM_MAX equ 037FFFFFFh ; DEFAULT highest address for an initrd
+DEFAULT_BAUD equ 9600 ; Default baud rate for serial port
+BAUD_DIVISOR equ 115200 ; Serial port parameter
+MAX_FKEYS equ 12 ; Number of F-key help files
+
+%assign DO_WBINVD 0 ; Should we use WBINVD or not?
+
+;
+; Local boot supported
+;
+%assign HAS_LOCALBOOT 1
+
+;
+; Set this to return the A20 gate to its previous state, instead of
+; leaving it open. This has caused problems, because there appear
+; to be a race condition between disabling the A20 gate and trying to
+; re-enter protected mode, causing the A20 gate disable to take effect
+; after we have already done the A20 enabled check, with disastrous
+; consequences. Plus, there seems to be little or no demand for it.
+;
+%assign DISABLE_A20 0
+
+
+;
+; Version number definitinons
+;
+%ifndef DEPEND ; Generated file
+%include "../version.gen"
+%endif
+
+;
+; Should be updated with every release to avoid bootsector/SYS file mismatch
+;
+%define version_str VERSION ; Must be 4 characters long!
+%define date DATE_STR ; Defined from the Makefile
+%define year '2008'
+
+%endif ; _CONFIG_INC
diff --git a/core/configinit.inc b/core/configinit.inc
new file mode 100644
index 00000000..cf6a814c
--- /dev/null
+++ b/core/configinit.inc
@@ -0,0 +1,59 @@
+;; -----------------------------------------------------------------------
+;;
+;; Copyright 1994-2008 H. Peter Anvin - All Rights Reserved
+;;
+;; This program is free software; you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation, Inc., 53 Temple Place Ste 330,
+;; Boston MA 02111-1307, USA; either version 2 of the License, or
+;; (at your option) any later version; incorporated herein by reference.
+;;
+;; -----------------------------------------------------------------------
+
+;;
+;; configinit.inc
+;;
+;; Initialize the configuration section
+;;
+
+ section .text
+
+reset_config:
+ call highmemsize
+
+ ; Initialize the .config section
+ xor eax,eax
+ mov si,__config_lma
+ mov di,__config_start
+ mov cx,__config_dwords
+ rep movsd
+
+%ifndef DEPEND
+%if NULLFILE != 0
+ mov al,NULLFILE
+ mov di,FKeyName
+ mov cx,MAX_FKEYS*(1 << FILENAME_MAX_LG2)
+ rep stosb
+%endif
+%endif
+
+ mov si,linuxauto_cmd ; Default command: "linux auto"
+ mov di,default_cmd
+ mov cx,linuxauto_len
+ rep movsb
+
+ mov di,KbdMap ; Default keymap 1:1
+ xor al,al
+ inc ch ; CX <- 256
+mkkeymap: stosb
+ inc al
+ loop mkkeymap
+
+ mov eax,[HighMemSize]
+ mov [VKernelEnd],eax
+
+ ret
+
+ section .data
+linuxauto_cmd db 'linux auto',0
+linuxauto_len equ $-linuxauto_cmd
diff --git a/core/conio.inc b/core/conio.inc
new file mode 100644
index 00000000..47005961
--- /dev/null
+++ b/core/conio.inc
@@ -0,0 +1,391 @@
+;; -----------------------------------------------------------------------
+;;
+;; Copyright 1994-2008 H. Peter Anvin - All Rights Reserved
+;;
+;; This program is free software; you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation, Inc., 53 Temple Place Ste 330,
+;; Boston MA 02111-1307, USA; either version 2 of the License, or
+;; (at your option) any later version; incorporated herein by reference.
+;;
+;; -----------------------------------------------------------------------
+
+;;
+;; conio.inc
+;;
+;; Console I/O code, except:
+;; writechr, writestr - module-dependent
+;; cwritestr, crlf - writestr.inc
+;; writehex* - writehex.inc
+;;
+
+;
+; loadkeys: Load a LILO-style keymap; SI and DX:AX set by searchdir
+;
+ section .text
+
+loadkeys:
+ and dx,dx ; Should be 256 bytes exactly
+ jne loadkeys_ret
+ cmp ax,256
+ jne loadkeys_ret
+
+ mov bx,trackbuf
+ mov cx,1 ; 1 cluster should be >= 256 bytes
+ call getfssec
+
+ mov si,trackbuf
+ mov di,KbdMap
+ mov cx,256 >> 2
+ rep movsd
+
+loadkeys_ret: ret
+
+;
+; get_msg_file: Load a text file and write its contents to the screen,
+; interpreting color codes. Call with the file already
+; on the top of the open/getc stack.
+;
+; Assumes CS == DS == ES.
+;
+get_msg_file:
+ mov byte [TextAttribute],07h ; Default grey on white
+ mov byte [DisplayMask],07h ; Display text in all modes
+ call msg_initvars
+
+print_msg_file:
+.getc:
+ call getc
+ jc .done
+ cmp al,1Ah ; DOS EOF?
+ je .done
+ movzx cx,byte [UsingVGA]
+ and cl,01h
+ inc cx ; CL <- 01h = text mode,
+ ; 02h = graphics mode
+ call [NextCharJump] ; Do what shall be done
+ jmp .getc
+.done:
+ jmp close ; Tailcall!
+
+msg_putchar: ; Normal character
+ cmp al,0Fh ; ^O = color code follows
+ je msg_ctrl_o
+ cmp al,0Dh ; Ignore <CR>
+ je msg_ignore
+ cmp al,0Ah ; <LF> = newline
+ je msg_newline
+ cmp al,0Ch ; <FF> = clear screen
+ je msg_formfeed
+ cmp al,07h ; <BEL> = beep
+ je msg_beep
+ cmp al,19h ; <EM> = return to text mode
+ je msg_novga
+ cmp al,18h ; <CAN> = VGA filename follows
+ je msg_vga
+ jnb .not_modectl
+ cmp al,10h ; 10h to 17h are mode controls
+ jae msg_modectl
+.not_modectl:
+
+msg_normal: call write_serial_displaymask ; Write to serial port
+ test [DisplayMask],cl
+ jz msg_ignore ; Not screen
+ test byte [DisplayCon],01h
+ jz msg_ignore
+ mov bl,[TextAttribute]
+ mov bh,[BIOS_page]
+ mov ah,09h ; Write character/attribute
+ mov cx,1 ; One character only
+ int 10h ; Write to screen
+ mov al,[CursorCol]
+ inc ax
+ cmp al,[VidCols]
+ ja msg_line_wrap ; Screen wraparound
+ mov [CursorCol],al
+
+msg_gotoxy: mov bh,[BIOS_page]
+ mov dx,[CursorDX]
+ mov ah,02h ; Set cursor position
+ int 10h
+msg_ignore: ret
+
+msg_beep: mov ax,0E07h ; Beep
+ xor bx,bx
+ int 10h
+ ret
+
+msg_ctrl_o: ; ^O = color code follows
+ mov word [NextCharJump],msg_setbg
+ ret
+msg_newline: ; Newline char or end of line
+ mov si,crlf_msg
+ call write_serial_str_displaymask
+msg_line_wrap: ; Screen wraparound
+ test [DisplayMask],cl
+ jz msg_ignore
+ mov byte [CursorCol],0
+ mov al,[CursorRow]
+ inc ax
+ cmp al,[VidRows]
+ ja msg_scroll
+ mov [CursorRow],al
+ jmp short msg_gotoxy
+msg_scroll: xor cx,cx ; Upper left hand corner
+ mov dx,[ScreenSize]
+ mov [CursorRow],dh ; New cursor at the bottom
+ mov bh,[ScrollAttribute]
+ mov ax,0601h ; Scroll up one line
+ int 10h
+ jmp short msg_gotoxy
+msg_formfeed: ; Form feed character
+ mov si,crff_msg
+ call write_serial_str_displaymask
+ test [DisplayMask],cl
+ jz msg_ignore
+ xor cx,cx
+ mov [CursorDX],cx ; Upper lefthand corner
+ mov dx,[ScreenSize]
+ mov bh,[TextAttribute]
+ mov ax,0600h ; Clear screen region
+ int 10h
+ jmp msg_gotoxy
+msg_setbg: ; Color background character
+ call unhexchar
+ jc msg_color_bad
+ shl al,4
+ test [DisplayMask],cl
+ jz .dontset
+ mov [TextAttribute],al
+.dontset:
+ mov word [NextCharJump],msg_setfg
+ ret
+msg_setfg: ; Color foreground character
+ call unhexchar
+ jc msg_color_bad
+ test [DisplayMask],cl
+ jz .dontset
+ or [TextAttribute],al ; setbg set foreground to 0
+.dontset:
+ jmp short msg_putcharnext
+msg_vga:
+ mov word [NextCharJump],msg_filename
+ mov di, VGAFileBuf
+ jmp short msg_setvgafileptr
+
+msg_color_bad:
+ mov byte [TextAttribute],07h ; Default attribute
+msg_putcharnext:
+ mov word [NextCharJump],msg_putchar
+ ret
+
+msg_filename: ; Getting VGA filename
+ cmp al,0Ah ; <LF> = end of filename
+ je msg_viewimage
+ cmp al,' '
+ jbe msg_ret ; Ignore space/control char
+ mov di,[VGAFilePtr]
+ cmp di,VGAFileBufEnd
+ jnb msg_ret
+ mov [di],al ; Can't use stosb (DS:)
+ inc di
+msg_setvgafileptr:
+ mov [VGAFilePtr],di
+msg_ret: ret
+
+msg_novga:
+ call vgaclearmode
+ jmp short msg_initvars
+
+msg_viewimage:
+ mov si,[VGAFilePtr]
+ mov byte [si],0 ; Zero-terminate filename
+ mov si,VGAFileBuf
+ mov di,VGAFileMBuf
+ call mangle_name
+ call open
+ jz msg_putcharnext ; Not there
+ call vgadisplayfile
+ ; Fall through
+
+ ; Subroutine to initialize variables, also needed
+ ; after loading a graphics file
+msg_initvars:
+ pusha
+ mov bh,[BIOS_page]
+ mov ah,03h ; Read cursor position
+ int 10h
+ mov [CursorDX],dx
+ popa
+ jmp short msg_putcharnext ; Initialize state machine
+
+msg_modectl:
+ and al,07h
+ mov [DisplayMask],al
+ jmp short msg_putcharnext
+
+;
+; write_serial: If serial output is enabled, write character on serial port
+; write_serial_displaymask: d:o, but ignore if DisplayMask & 04h == 0
+;
+write_serial_displaymask:
+ test byte [DisplayMask], 04h
+ jz write_serial.end
+write_serial:
+ pushfd
+ pushad
+ mov bx,[SerialPort]
+ and bx,bx
+ je .noserial
+ push ax
+ mov ah,[FlowInput]
+.waitspace:
+ ; Wait for space in transmit register
+ lea dx,[bx+5] ; DX -> LSR
+ in al,dx
+ test al,20h
+ jz .waitspace
+
+ ; Wait for input flow control
+ inc dx ; DX -> MSR
+ in al,dx
+ and al,ah
+ cmp al,ah
+ jne .waitspace
+.no_flow:
+
+ xchg dx,bx ; DX -> THR
+ pop ax
+ call slow_out ; Send data
+.noserial: popad
+ popfd
+.end: ret
+
+;
+; write_serial_str: write_serial for strings
+; write_serial_str_displaymask: d:o, but ignore if DisplayMask & 04h == 0
+;
+write_serial_str_displaymask:
+ test byte [DisplayMask], 04h
+ jz write_serial_str.end
+
+write_serial_str:
+.loop lodsb
+ and al,al
+ jz .end
+ call write_serial
+ jmp short .loop
+.end: ret
+
+;
+; pollchar: check if we have an input character pending (ZF = 0)
+;
+pollchar:
+ pushad
+ mov ah,11h ; Poll keyboard
+ int 16h
+ jnz .done ; Keyboard response
+ mov dx,[SerialPort]
+ and dx,dx
+ jz .done ; No serial port -> no input
+ add dx,byte 5 ; DX -> LSR
+ in al,dx
+ test al,1 ; ZF = 0 if data pending
+ jz .done
+ inc dx ; DX -> MSR
+ mov ah,[FlowIgnore] ; Required status bits
+ in al,dx
+ and al,ah
+ cmp al,ah
+ setne al
+ dec al ; Set ZF = 0 if equal
+.done: popad
+ ret
+
+;
+; getchar: Read a character from keyboard or serial port
+;
+getchar:
+ RESET_IDLE
+.again:
+ DO_IDLE
+ mov ah,11h ; Poll keyboard
+ int 16h
+ jnz .kbd ; Keyboard input?
+ mov bx,[SerialPort]
+ and bx,bx
+ jz .again
+ lea dx,[bx+5] ; DX -> LSR
+ in al,dx
+ test al,1
+ jz .again
+ inc dx ; DX -> MSR
+ mov ah,[FlowIgnore]
+ in al,dx
+ and al,ah
+ cmp al,ah
+ jne .again
+.serial: xor ah,ah ; Avoid confusion
+ xchg dx,bx ; Data port
+ in al,dx
+ ret
+.kbd: mov ah,10h ; Get keyboard input
+ int 16h
+ cmp al,0E0h
+ jnz .not_ext
+ xor al,al
+.not_ext:
+ and al,al
+ jz .func_key
+ mov bx,KbdMap ; Convert character sets
+ xlatb
+.func_key: ret
+
+%ifdef DEBUG_TRACERS
+;
+; debug hack to print a character with minimal code impact
+;
+debug_tracer: pushad
+ pushfd
+ mov bp,sp
+ mov bx,[bp+9*4] ; Get return address
+ mov al,[cs:bx] ; Get data byte
+ inc word [bp+9*4] ; Return to after data byte
+ call writechr
+ popfd
+ popad
+ ret
+%endif ; DEBUG_TRACERS
+
+ section .data
+%if IS_ISOLINUX == 0 ; Defined elsewhere for ISOLINUX
+crlf_msg db CR, LF
+null_msg db 0
+%endif
+crff_msg db CR, FF, 0
+
+ section .config
+ ; This is a word to pc_setint16 can set it
+DisplayCon dw 01h ; Console display enabled
+
+ScrollAttribute db 07h ; Grey on white (normal text color)
+
+ section .bss
+ alignb 2
+NextCharJump resw 1 ; Routine to interpret next print char
+CursorDX equ $
+CursorCol resb 1 ; Cursor column for message file
+CursorRow resb 1 ; Cursor row for message file
+ScreenSize equ $
+VidCols resb 1 ; Columns on screen-1
+VidRows resb 1 ; Rows on screen-1
+
+; Serial console stuff...
+BaudDivisor resw 1 ; Baud rate divisor
+FlowControl equ $
+FlowOutput resb 1 ; Outputs to assert for serial flow
+FlowInput resb 1 ; Input bits for serial flow
+FlowIgnore resb 1 ; Ignore input unless these bits set
+
+TextAttribute resb 1 ; Text attribute for message file
+DisplayMask resb 1 ; Display modes mask
diff --git a/core/cpuinit.inc b/core/cpuinit.inc
new file mode 100644
index 00000000..fd62cc77
--- /dev/null
+++ b/core/cpuinit.inc
@@ -0,0 +1,86 @@
+;; -----------------------------------------------------------------------
+;;
+;; Copyright 1994-2008 H. Peter Anvin - All Rights Reserved
+;;
+;; This program is free software; you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation, Inc., 53 Temple Place Ste 330,
+;; Boston MA 02111-1307, USA; either version 2 of the License, or
+;; (at your option) any later version; incorporated herein by reference.
+;;
+;; -----------------------------------------------------------------------
+
+;;
+;; cpuinit.inc
+;;
+;; CPU-dependent initialization and related checks.
+;;
+
+check_escapes:
+ mov ah,02h ; Check keyboard flags
+ int 16h
+ mov [KbdFlags],al ; Save for boot prompt check
+ test al,04h ; Ctrl->skip 386 check
+ jnz skip_checks
+
+;
+; Now check that there is sufficient low (DOS) memory
+;
+; NOTE: Linux doesn't use all of real_mode_seg, but we use the same
+; segment for COMBOOT images, which can use all 64K
+;
+dosram_k equ (real_mode_seg+0x1000) >> 6 ; Minimum DOS memory (K)
+ int 12h
+ cmp ax,dosram_k
+ jae enough_ram
+ mov si,err_noram
+ call writestr
+ jmp kaboom
+enough_ram:
+skip_checks:
+
+;
+; Initialize the bcopy32 code in low memory
+;
+ mov si,__bcopy32_lma
+ mov di,__bcopy32_start
+ mov cx,__bcopy32_dwords
+ rep movsd
+
+;
+; Check if we're 386 (as opposed to 486+); if so we need to blank out
+; the WBINVD instruction
+;
+; We check for 486 by setting EFLAGS.AC
+;
+%if DO_WBINVD
+ pushfd ; Save the good flags
+ pushfd
+ pop eax
+ mov ebx,eax
+ xor eax,(1 << 18) ; AC bit
+ push eax
+ popfd
+ pushfd
+ pop eax
+ popfd ; Restore the original flags
+ xor eax,ebx
+ jnz is_486
+;
+; 386 - Looks like we better blot out the WBINVD instruction
+;
+ mov byte [try_wbinvd],0c3h ; Near RET
+is_486:
+%endif ; DO_WBINVD
+
+ section .data
+err_noram db 'It appears your computer has less than '
+ asciidec dosram_k
+ db 'K of low ("DOS")'
+ db CR, LF
+ db 'RAM. Linux needs at least this amount to boot. If you get'
+ db CR, LF
+ db 'this message in error, hold down the Ctrl key while'
+ db CR, LF
+ db 'booting, and I will take your word for it.', CR, LF, 0
+ section .text
diff --git a/core/dnsresolv.inc b/core/dnsresolv.inc
new file mode 100644
index 00000000..f31c578b
--- /dev/null
+++ b/core/dnsresolv.inc
@@ -0,0 +1,380 @@
+; -*- fundamental -*-
+; -----------------------------------------------------------------------
+;
+; Copyright 2004-2008 H. Peter Anvin - All Rights Reserved
+;
+; This program is free software; you can redistribute it and/or modify
+; it under the terms of the GNU General Public License as published by
+; the Free Software Foundation, Inc., 53 Temple Place Ste 330,
+; Bostom MA 02111-1307, USA; either version 2 of the License, or
+; (at your option) any later version; incorporated herein by reference.
+;
+; -----------------------------------------------------------------------
+
+;
+; dnsresolv.inc
+;
+; Very simple DNS resolver (assumes recursion-enabled DNS server;
+; this should be the normal thing for client-serving DNS servers.)
+;
+
+DNS_PORT equ htons(53) ; Default DNS port
+DNS_MAX_PACKET equ 512 ; Defined by protocol
+; TFTP uses the range 49152-57343
+DNS_LOCAL_PORT equ htons(60053) ; All local DNS queries come from this port #
+DNS_MAX_SERVERS equ 4 ; Max no of DNS servers
+
+ section .text
+
+;
+; Turn a string in DS:SI into a DNS "label set" in ES:DI.
+; On return, DI points to the first byte after the label set,
+; and SI to the terminating byte.
+;
+; On return, DX contains the number of dots encountered.
+;
+dns_mangle:
+ push ax
+ push bx
+ xor dx,dx
+.isdot:
+ inc dx
+ xor al,al
+ mov bx,di
+ stosb
+.getbyte:
+ lodsb
+ and al,al
+ jz .endstring
+ cmp al,':'
+ jz .endstring
+ cmp al,'.'
+ je .isdot
+ inc byte [es:bx]
+ stosb
+ jmp .getbyte
+.endstring:
+ dec si
+ dec dx ; We always counted one high
+ cmp byte [es:bx],0
+ jz .done
+ xor al,al
+ stosb
+.done:
+ pop bx
+ pop ax
+ ret
+
+;
+; Compare two sets of DNS labels, in DS:SI and ES:DI; the one in SI
+; is allowed pointers relative to a packet in DNSRecvBuf.
+;
+; Assumes DS == ES. ZF = 1 if same; no registers changed.
+; (Note: change reference to [di] to [es:di] to remove DS == ES assumption)
+;
+dns_compare:
+ pusha
+%if 0
+
+.label:
+ lodsb
+ cmp al,0C0h
+ jb .noptr
+ and al,03Fh ; Get pointer value
+ mov ah,al ; ... in network byte order!
+ lodsb
+ mov si,DNSRecvBuf
+ add si,ax
+ jmp .label
+.noptr:
+ cmp al,[di]
+ jne .done ; Mismatch
+ inc di
+ movzx cx,al ; End label?
+ and cx,cx ; ZF = 1 if match
+ jz .done
+
+ ; We have a string of bytes that need to match now
+ repe cmpsb
+ je .label
+
+.done:
+%else
+ xor ax,ax
+%endif
+ popa
+ ret
+
+;
+; Skip past a DNS label set in DS:SI.
+;
+dns_skiplabel:
+ push ax
+ xor ax,ax ; AH == 0
+.loop:
+ lodsb
+ cmp al,0C0h ; Pointer?
+ jae .ptr
+ and al,al
+ jz .done
+ add si,ax
+ jmp .loop
+.ptr:
+ inc si ; Pointer is two bytes
+.done:
+ pop ax
+ ret
+
+ ; DNS header format
+ struc dnshdr
+.id: resw 1
+.flags: resw 1
+.qdcount: resw 1
+.ancount: resw 1
+.nscount: resw 1
+.arcount: resw 1
+ endstruc
+
+ ; DNS query
+ struc dnsquery
+.qtype: resw 1
+.qclass: resw 1
+ endstruc
+
+ ; DNS RR
+ struc dnsrr
+.type: resw 1
+.class: resw 1
+.ttl: resd 1
+.rdlength: resw 1
+.rdata: equ $
+ endstruc
+
+ section .bss2
+ alignb 2
+DNSSendBuf resb DNS_MAX_PACKET
+DNSRecvBuf resb DNS_MAX_PACKET
+LocalDomain resb 256 ; Max possible length
+DNSServers resd DNS_MAX_SERVERS
+
+ section .data
+pxe_udp_write_pkt_dns:
+.status: dw 0 ; Status
+.sip: dd 0 ; Server IP
+.gip: dd 0 ; Gateway IP
+.lport: dw DNS_LOCAL_PORT ; Local port
+.rport: dw DNS_PORT ; Remote port
+.buffersize: dw 0 ; Size of packet
+.buffer: dw DNSSendBuf, 0 ; off, seg of buffer
+
+pxe_udp_read_pkt_dns:
+.status: dw 0 ; Status
+.sip: dd 0 ; Source IP
+.dip: dd 0 ; Destination (our) IP
+.rport: dw DNS_PORT ; Remote port
+.lport: dw DNS_LOCAL_PORT ; Local port
+.buffersize: dw DNS_MAX_PACKET ; Max packet size
+.buffer: dw DNSRecvBuf, 0 ; off, seg of buffer
+
+LastDNSServer dw DNSServers
+
+; Actual resolver function
+; Points to a null-terminated or :-terminated string in DS:SI
+; and returns the name in EAX if it exists and can be found.
+; If EAX = 0 on exit, the lookup failed.
+;
+; No segment assumptions permitted.
+;
+ section .text
+dns_resolv:
+ push ds
+ push es
+ push di
+ push cx
+ push dx
+
+ push cs
+ pop es ; ES <- CS
+
+ ; First, build query packet
+ mov di,DNSSendBuf+dnshdr.flags
+ inc word [es:di-2] ; New query ID
+ mov ax,htons(0100h) ; Recursion requested
+ stosw
+ mov ax,htons(1) ; One question
+ stosw
+ xor ax,ax ; No answers, NS or ARs
+ stosw
+ stosw
+ stosw
+
+ call dns_mangle ; Convert name to DNS labels
+
+ push cs ; DS <- CS
+ pop ds
+
+ push si ; Save pointer to after DNS string
+
+ ; Initialize...
+ mov eax,[MyIP]
+ mov [pxe_udp_read_pkt_dns.dip],eax
+
+ and dx,dx
+ jnz .fqdn ; If we have dots, assume it's FQDN
+ dec di ; Remove final null
+ mov si,LocalDomain
+ call strcpy ; Uncompressed DNS label set so it ends in null
+.fqdn:
+
+ mov ax,htons(1)
+ stosw ; QTYPE = 1 = A
+ stosw ; QCLASS = 1 = IN
+
+ sub di,DNSSendBuf
+ mov [pxe_udp_write_pkt_dns.buffersize],di
+
+ ; Now, send it to the nameserver(s)
+ ; Outer loop: exponential backoff
+ ; Inner loop: scan the various DNS servers
+
+ mov dx,PKT_TIMEOUT
+ mov cx,PKT_RETRY
+.backoff:
+ mov si,DNSServers
+.servers:
+ cmp si,[LastDNSServer]
+ jb .moreservers
+
+.nomoreservers:
+ add dx,dx ; Exponential backoff
+ loop .backoff
+
+ xor eax,eax ; Nothing...
+.done:
+ pop si
+ pop dx
+ pop cx
+ pop di
+ pop es
+ pop ds
+ ret
+
+.moreservers:
+ lodsd ; EAX <- next server
+ push si
+ push cx
+ push dx
+
+ mov word [pxe_udp_write_pkt_dns.status],0
+
+ mov [pxe_udp_write_pkt_dns.sip],eax
+ mov [pxe_udp_read_pkt_dns.sip],eax
+ xor eax,[MyIP]
+ and eax,[Netmask]
+ jz .nogw
+ mov eax,[Gateway]
+.nogw:
+ mov [pxe_udp_write_pkt_dns.gip],eax
+
+ mov di,pxe_udp_write_pkt_dns
+ mov bx,PXENV_UDP_WRITE
+ call pxenv
+ jc .timeout ; Treat failed transmit as timeout
+ cmp word [pxe_udp_write_pkt_dns.status],0
+ jne .timeout
+
+ mov cx,[BIOS_timer]
+.waitrecv:
+ mov ax,[BIOS_timer]
+ sub ax,cx
+ cmp ax,dx
+ jae .timeout
+
+ mov word [pxe_udp_read_pkt_dns.status],0
+ mov word [pxe_udp_read_pkt_dns.buffersize],DNS_MAX_PACKET
+ mov di,pxe_udp_read_pkt_dns
+ mov bx,PXENV_UDP_READ
+ call pxenv
+ and ax,ax
+ jnz .waitrecv
+ cmp [pxe_udp_read_pkt_dns.status],ax
+ jnz .waitrecv
+
+ ; Got a packet, deal with it...
+ mov si,DNSRecvBuf
+ lodsw
+ cmp ax,[DNSSendBuf] ; ID
+ jne .waitrecv ; Not ours
+
+ lodsw ; flags
+ xor al,80h ; Query#/Answer bit
+ test ax,htons(0F80Fh)
+ jnz .badness
+
+ lodsw
+ xchg ah,al ; ntohs
+ mov cx,ax ; Questions echoed
+ lodsw
+ xchg ah,al ; ntohs
+ push ax ; Replies
+ lodsw ; NS records
+ lodsw ; Authority records
+
+ jcxz .qskipped
+.skipq:
+ call dns_skiplabel ; Skip name
+ add si,4 ; Skip question trailer
+ loop .skipq
+
+.qskipped:
+ pop cx ; Number of replies
+ jcxz .badness
+
+.parseanswer:
+ mov di,DNSSendBuf+dnshdr_size
+ call dns_compare
+ pushf
+ call dns_skiplabel
+ mov ax,[si+8] ; RDLENGTH
+ xchg ah,al ; ntohs
+ popf
+ jnz .notsame
+ cmp dword [si],htons(1)*0x10001 ; TYPE = A, CLASS = IN?
+ jne .notsame
+ cmp ax,4 ; RDLENGTH = 4?
+ jne .notsame
+ ;
+ ; We hit paydirt here...
+ ;
+ mov eax,[si+10]
+.gotresult:
+ add sp,6 ; Drop timeout information
+ jmp .done
+
+.notsame:
+ add si,10
+ add si,ax
+ loop .parseanswer
+
+.badness:
+ ; We got back no data from this server. Unfortunately, for a recursive,
+ ; non-authoritative query there is no such thing as an NXDOMAIN reply,
+ ; which technically means we can't draw any conclusions. However,
+ ; in practice that means the domain doesn't exist. If this turns out
+ ; to be a problem, we may want to add code to go through all the servers
+ ; before giving up.
+
+ ; If the DNS server wasn't capable of recursion, and isn't capable
+ ; of giving us an authoritative reply (i.e. neither AA or RA set),
+ ; then at least try a different setver...
+ test word [DNSRecvBuf+dnshdr.flags],htons(0480h)
+ jz .timeout
+
+ xor eax,eax
+ jmp .gotresult
+
+.timeout:
+ pop dx
+ pop cx
+ pop si
+ jmp .servers
diff --git a/core/ext2_fs.inc b/core/ext2_fs.inc
new file mode 100644
index 00000000..e84efb14
--- /dev/null
+++ b/core/ext2_fs.inc
@@ -0,0 +1,183 @@
+; -----------------------------------------------------------------------
+;
+; Copyright 1998-2008 H. Peter Anvin - All Rights Reserved
+;
+; This program is free software; you can redistribute it and/or modify
+; it under the terms of the GNU General Public License as published by
+; the Free Software Foundation, Inc., 675 Mass Ave, Cambridge MA 02139,
+; USA; either version 2 of the License, or (at your option) any later
+; version; incorporated herein by reference.
+;
+; -----------------------------------------------------------------------
+
+;
+; ext2_fs.inc
+;
+; NASM include file for ext2fs data structures
+;
+
+%define EXT2_SUPER_MAGIC 0xEF53
+
+%define EXT2_GOOD_OLD_REV 0 ; The good old (original) format
+%define EXT2_DYNAMIC_REV 1 ; V2 format w/ dynamic inode sizes
+%define EXT2_GOOD_OLD_INODE_SIZE 128
+
+; Special inode numbers
+%define EXT2_BAD_INO 1 ; Bad blocks inode
+%define EXT2_ROOT_INO 2 ; Root inode
+%define EXT2_BOOT_LOADER_INO 5 ; Boot loader inode
+%define EXT2_UNDEL_DIR_INO 6 ; Undelete directory inode
+%define EXT3_RESIZE_INO 7 ; Reserved group descriptors inode
+%define EXT3_JOURNAL_INO 8 ; Journal inode
+
+; We're readonly, so we only care about incompat features.
+%define EXT2_FEATURE_INCOMPAT_COMPRESSION 0x0001
+%define EXT2_FEATURE_INCOMPAT_FILETYPE 0x0002
+%define EXT3_FEATURE_INCOMPAT_RECOVER 0x0004
+%define EXT3_FEATURE_INCOMPAT_JOURNAL_DEV 0x0008
+%define EXT2_FEATURE_INCOMPAT_META_BG 0x0010
+%define EXT2_FEATURE_INCOMPAT_ANY 0xffffffff
+
+%define EXT2_NDIR_BLOCKS 12
+%define EXT2_IND_BLOCK EXT2_NDIR_BLOCKS
+%define EXT2_DIND_BLOCK (EXT2_IND_BLOCK+1)
+%define EXT2_TIND_BLOCK (EXT2_DIND_BLOCK+1)
+%define EXT2_N_BLOCKS (EXT2_TIND_BLOCK+1)
+
+;
+; File types and file modes
+;
+%define S_IFDIR 0040000o ; Directory
+%define S_IFCHR 0020000o ; Character device
+%define S_IFBLK 0060000o ; Block device
+%define S_IFREG 0100000o ; Regular file
+%define S_IFIFO 0010000o ; FIFO
+%define S_IFLNK 0120000o ; Symbolic link
+%define S_IFSOCK 0140000o ; Socket
+
+%define S_IFSHIFT 12
+
+%define T_IFDIR (S_IFDIR >> S_IFSHIFT)
+%define T_IFCHR (S_IFCHR >> S_IFSHIFT)
+%define T_IFBLK (S_IFBLK >> S_IFSHIFT)
+%define T_IFREG (S_IFREG >> S_IFSHIFT)
+%define T_IFIFO (S_IFIFO >> S_IFSHIFT)
+%define T_IFLNK (S_IFLNK >> S_IFSHIFT)
+%define T_IFSOCK (S_IFSOCK >> S_IFSHIFT)
+
+;
+; Structure definition for the ext2 superblock
+;
+ struc ext2_super_block
+s_inodes_count resd 1 ; Inodes count
+s_blocks_count resd 1 ; Blocks count
+s_r_blocks_count resd 1 ; Reserved blocks count
+s_free_blocks_count resd 1 ; Free blocks count
+s_free_inodes_count resd 1 ; Free inodes count
+s_first_data_block resd 1 ; First Data Block
+s_log_block_size resd 1 ; Block size
+s_log_frag_size resd 1 ; Fragment size
+s_blocks_per_group resd 1 ; # Blocks per group
+s_frags_per_group resd 1 ; # Fragments per group
+s_inodes_per_group resd 1 ; # Inodes per group
+s_mtime resd 1 ; Mount time
+s_wtime resd 1 ; Write time
+s_mnt_count resw 1 ; Mount count
+s_max_mnt_count resw 1 ; Maximal mount count
+s_magic resw 1 ; Magic signature
+s_state resw 1 ; File system state
+s_errors resw 1 ; Behaviour when detecting errors
+s_minor_rev_level resw 1 ; minor revision level
+s_lastcheck resd 1 ; time of last check
+s_checkinterval resd 1 ; max. time between checks
+s_creator_os resd 1 ; OS
+s_rev_level resd 1 ; Revision level
+s_def_resuid resw 1 ; Default uid for reserved blocks
+s_def_resgid resw 1 ; Default gid for reserved blocks
+s_first_ino resd 1 ; First non-reserved inode
+s_inode_size resw 1 ; size of inode structure
+s_block_group_nr resw 1 ; block group # of this superblock
+s_feature_compat resd 1 ; compatible feature set
+s_feature_incompat resd 1 ; incompatible feature set
+s_feature_ro_compat resd 1 ; readonly-compatible feature set
+s_uuid resb 16 ; 128-bit uuid for volume
+s_volume_name resb 16 ; volume name
+s_last_mounted resb 64 ; directory where last mounted
+s_algorithm_usage_bitmap resd 1 ; For compression
+s_prealloc_blocks resb 1 ; Nr of blocks to try to preallocate
+s_prealloc_dir_blocks resb 1 ; Nr to preallocate for dirs
+s_padding1 resw 1
+s_reserved resd 204 ; Padding to the end of the block
+ endstruc
+
+%ifndef DEPEND
+%if ext2_super_block_size != 1024
+%error "ext2_super_block definition bogus"
+%endif
+%endif
+
+;
+; Structure definition for the ext2 inode
+;
+ struc ext2_inode
+i_mode resw 1 ; File mode
+i_uid resw 1 ; Owner Uid
+i_size resd 1 ; Size in bytes
+i_atime resd 1 ; Access time
+i_ctime resd 1 ; Creation time
+i_mtime resd 1 ; Modification time
+i_dtime resd 1 ; Deletion Time
+i_gid resw 1 ; Group Id
+i_links_count resw 1 ; Links count
+i_blocks resd 1 ; Blocks count
+i_flags resd 1 ; File flags
+l_i_reserved1 resd 1
+i_block resd EXT2_N_BLOCKS ; Pointer to blocks
+i_version resd 1 ; File version (for NFS)
+i_file_acl resd 1 ; File ACL
+i_dir_acl resd 1 ; Directory ACL
+i_faddr resd 1 ; Fragment address
+l_i_frag resb 1 ; Fragment number
+l_i_fsize resb 1 ; Fragment size
+i_pad1 resw 1
+l_i_reserved2 resd 2
+ endstruc
+
+%ifndef DEPEND
+%if ext2_inode_size != 128
+%error "ext2_inode definition bogus"
+%endif
+%endif
+
+;
+; Structure definition for ext2 block group descriptor
+;
+ struc ext2_group_desc
+bg_block_bitmap resd 1 ; Block bitmap block
+bg_inode_bitmap resd 1 ; Inode bitmap block
+bg_inode_table resd 1 ; Inode table block
+bg_free_blocks_count resw 1 ; Free blocks count
+bg_free_inodes_count resw 1 ; Free inodes count
+bg_used_dirs_count resw 1 ; Used inodes count
+bg_pad resw 1
+bg_reserved resd 3
+ endstruc
+
+%ifndef DEPEND
+%if ext2_group_desc_size != 32
+%error "ext2_group_desc definition bogus"
+%endif
+%endif
+
+%define ext2_group_desc_lg2size 5
+
+;
+; Structure definition for ext2 directory entry
+;
+ struc ext2_dir_entry
+d_inode resd 1 ; Inode number
+d_rec_len resw 1 ; Directory entry length
+d_name_len resb 1 ; Name length
+d_file_type resb 1 ; File type
+d_name equ $
+ endstruc
diff --git a/core/extlinux.asm b/core/extlinux.asm
new file mode 100644
index 00000000..6c2946c2
--- /dev/null
+++ b/core/extlinux.asm
@@ -0,0 +1,1588 @@
+; -*- fundamental -*- (asm-mode sucks)
+; ****************************************************************************
+;
+; extlinux.asm
+;
+; A program to boot Linux kernels off an ext2/ext3 filesystem.
+;
+; Copyright 1994-2008 H. Peter Anvin - All Rights Reserved
+;
+; This program is free software; you can redistribute it and/or modify
+; it under the terms of the GNU General Public License as published by
+; the Free Software Foundation, Inc., 53 Temple Place Ste 330,
+; Boston MA 02111-1307, USA; either version 2 of the License, or
+; (at your option) any later version; incorporated herein by reference.
+;
+; ****************************************************************************
+
+%define IS_EXTLINUX 1
+%include "head.inc"
+%include "ext2_fs.inc"
+
+;
+; Some semi-configurable constants... change on your own risk.
+;
+my_id equ extlinux_id
+; NASM 0.98.38 croaks if these are equ's rather than macros...
+FILENAME_MAX_LG2 equ 8 ; log2(Max filename size Including final null)
+FILENAME_MAX equ (1 << FILENAME_MAX_LG2) ; Max mangled filename size
+NULLFILE equ 0 ; Null character == empty filename
+NULLOFFSET equ 0 ; Position in which to look
+retry_count equ 16 ; How patient are we with the disk?
+%assign HIGHMEM_SLOP 0 ; Avoid this much memory near the top
+LDLINUX_MAGIC equ 0x3eb202fe ; A random number to identify ourselves with
+
+MAX_OPEN_LG2 equ 6 ; log2(Max number of open files)
+MAX_OPEN equ (1 << MAX_OPEN_LG2)
+
+SECTOR_SHIFT equ 9
+SECTOR_SIZE equ (1 << SECTOR_SHIFT)
+
+MAX_SYMLINKS equ 64 ; Maximum number of symlinks per lookup
+SYMLINK_SECTORS equ 2 ; Max number of sectors in a symlink
+ ; (should be >= FILENAME_MAX)
+
+;
+; This is what we need to do when idle
+;
+%macro RESET_IDLE 0
+ ; Nothing
+%endmacro
+%macro DO_IDLE 0
+ ; Nothing
+%endmacro
+
+;
+; The following structure is used for "virtual kernels"; i.e. LILO-style
+; option labels. The options we permit here are `kernel' and `append
+; Since there is no room in the bottom 64K for all of these, we
+; stick them in high memory and copy them down before we need them.
+;
+ struc vkernel
+vk_vname: resb FILENAME_MAX ; Virtual name **MUST BE FIRST!**
+vk_rname: resb FILENAME_MAX ; Real name
+vk_appendlen: resw 1
+vk_type: resb 1 ; Type of file
+ alignb 4
+vk_append: resb max_cmd_len+1 ; Command line
+ alignb 4
+vk_end: equ $ ; Should be <= vk_size
+ endstruc
+
+;
+; Segment assignments in the bottom 640K
+; Stick to the low 512K in case we're using something like M-systems flash
+; which load a driver into low RAM (evil!!)
+;
+; 0000h - main code/data segment (and BIOS segment)
+;
+real_mode_seg equ 3000h
+cache_seg equ 2000h ; 64K area for metadata cache
+xfer_buf_seg equ 1000h ; Bounce buffer for I/O to high mem
+comboot_seg equ real_mode_seg ; COMBOOT image loading zone
+
+;
+; File structure. This holds the information for each currently open file.
+;
+ struc open_file_t
+file_bytesleft resd 1 ; Number of bytes left (0 = free)
+file_sector resd 1 ; Next linear sector to read
+file_in_sec resd 1 ; Sector where inode lives
+file_in_off resw 1
+file_mode resw 1
+ endstruc
+
+%ifndef DEPEND
+%if (open_file_t_size & (open_file_t_size-1))
+%error "open_file_t is not a power of 2"
+%endif
+%endif
+
+; ---------------------------------------------------------------------------
+; BEGIN CODE
+; ---------------------------------------------------------------------------
+
+;
+; Memory below this point is reserved for the BIOS and the MBR
+;
+ section .earlybss
+trackbufsize equ 8192
+trackbuf resb trackbufsize ; Track buffer goes here
+ ; ends at 2800h
+
+ section .bss
+SuperBlock resb 1024 ; ext2 superblock
+SuperInfo resq 16 ; DOS superblock expanded
+ClustSize resd 1 ; Bytes/cluster ("block")
+SecPerClust resd 1 ; Sectors/cluster
+ClustMask resd 1 ; Sectors/cluster - 1
+PtrsPerBlock1 resd 1 ; Pointers/cluster
+PtrsPerBlock2 resd 1 ; (Pointers/cluster)^2
+DriveNumber resb 1 ; BIOS drive number
+ClustShift resb 1 ; Shift count for sectors/cluster
+ClustByteShift resb 1 ; Shift count for bytes/cluster
+
+ alignb open_file_t_size
+Files resb MAX_OPEN*open_file_t_size
+
+ section .text
+;
+; Some of the things that have to be saved very early are saved
+; "close" to the initial stack pointer offset, in order to
+; reduce the code size...
+;
+StackBuf equ $-44-32 ; Start the stack here (grow down - 4K)
+PartInfo equ StackBuf ; Saved partition table entry
+FloppyTable equ PartInfo+16 ; Floppy info table (must follow PartInfo)
+OrigFDCTabPtr equ StackBuf-8 ; The 2nd high dword on the stack
+OrigESDI equ StackBuf-4 ; The high dword on the stack
+
+;
+; Primary entry point. Tempting as though it may be, we can't put the
+; initial "cli" here; the jmp opcode in the first byte is part of the
+; "magic number" (using the term very loosely) for the DOS superblock.
+;
+bootsec equ $
+_start: jmp short start ; 2 bytes
+ nop ; 1 byte
+;
+; "Superblock" follows -- it's in the boot sector, so it's already
+; loaded and ready for us
+;
+bsOemName db 'EXTLINUX' ; The SYS command sets this, so...
+;
+; These are the fields we actually care about. We end up expanding them
+; all to dword size early in the code, so generate labels for both
+; the expanded and unexpanded versions.
+;
+%macro superb 1
+bx %+ %1 equ SuperInfo+($-superblock)*8+4
+bs %+ %1 equ $
+ zb 1
+%endmacro
+%macro superw 1
+bx %+ %1 equ SuperInfo+($-superblock)*8
+bs %+ %1 equ $
+ zw 1
+%endmacro
+%macro superd 1
+bx %+ %1 equ $ ; no expansion for dwords
+bs %+ %1 equ $
+ zd 1
+%endmacro
+superblock equ $
+ superw BytesPerSec
+ superb SecPerClust
+ superw ResSectors
+ superb FATs
+ superw RootDirEnts
+ superw Sectors
+ superb Media
+ superw FATsecs
+ superw SecPerTrack
+ superw Heads
+superinfo_size equ ($-superblock)-1 ; How much to expand
+ superd Hidden
+ superd HugeSectors
+ ;
+ ; This is as far as FAT12/16 and FAT32 are consistent
+ ;
+ zb 54 ; FAT12/16 need 26 more bytes,
+ ; FAT32 need 54 more bytes
+superblock_len equ $-superblock
+
+;
+; Note we don't check the constraints above now; we did that at install
+; time (we hope!)
+;
+start:
+ cli ; No interrupts yet, please
+ cld ; Copy upwards
+;
+; Set up the stack
+;
+ xor ax,ax
+ mov ss,ax
+ mov sp,StackBuf ; Just below BSS
+ push es ; Save initial ES:DI -> $PnP pointer
+ push di
+ mov es,ax
+;
+; DS:SI may contain a partition table entry. Preserve it for us.
+;
+ mov cx,8 ; Save partition info
+ mov di,PartInfo
+ rep movsw
+
+ mov ds,ax ; Now we can initialize DS...
+
+;
+; Now sautee the BIOS floppy info block to that it will support decent-
+; size transfers; the floppy block is 11 bytes and is stored in the
+; INT 1Eh vector (brilliant waste of resources, eh?)
+;
+; Of course, if BIOSes had been properly programmed, we wouldn't have
+; had to waste precious space with this code.
+;
+ mov bx,fdctab
+ lfs si,[bx] ; FS:SI -> original fdctab
+ push fs ; Save on stack in case we need to bail
+ push si
+
+ ; Save the old fdctab even if hard disk so the stack layout
+ ; is the same. The instructions above do not change the flags
+ mov [DriveNumber],dl ; Save drive number in DL
+ and dl,dl ; If floppy disk (00-7F), assume no
+ ; partition table
+ js harddisk
+
+floppy:
+ mov cl,6 ; 12 bytes (CX == 0)
+ ; es:di -> FloppyTable already
+ ; This should be safe to do now, interrupts are off...
+ mov [bx],di ; FloppyTable
+ mov [bx+2],ax ; Segment 0
+ fs rep movsw ; Faster to move words
+ mov cl,[bsSecPerTrack] ; Patch the sector count
+ mov [di-8],cl
+ ; AX == 0 here
+ int 13h ; Some BIOSes need this
+
+ jmp short not_harddisk
+;
+; The drive number and possibly partition information was passed to us
+; by the BIOS or previous boot loader (MBR). Current "best practice" is to
+; trust that rather than what the superblock contains.
+;
+; Would it be better to zero out bsHidden if we don't have a partition table?
+;
+; Note: di points to beyond the end of PartInfo
+;
+harddisk:
+ test byte [di-16],7Fh ; Sanity check: "active flag" should
+ jnz no_partition ; be 00 or 80
+ mov eax,[di-8] ; Partition offset (dword)
+ mov [bsHidden],eax
+no_partition:
+;
+; Get disk drive parameters (don't trust the superblock.) Don't do this for
+; floppy drives -- INT 13:08 on floppy drives will (may?) return info about
+; what the *drive* supports, not about the *media*. Fortunately floppy disks
+; tend to have a fixed, well-defined geometry which is stored in the superblock.
+;
+ ; DL == drive # still
+ mov ah,08h
+ int 13h
+ jc no_driveparm
+ and ah,ah
+ jnz no_driveparm
+ shr dx,8
+ inc dx ; Contains # of heads - 1
+ mov [bsHeads],dx
+ and cx,3fh
+ mov [bsSecPerTrack],cx
+no_driveparm:
+not_harddisk:
+;
+; Ready to enable interrupts, captain
+;
+ sti
+
+;
+; Do we have EBIOS (EDD)?
+;
+eddcheck:
+ mov bx,55AAh
+ mov ah,41h ; EDD existence query
+ mov dl,[DriveNumber]
+ int 13h
+ jc .noedd
+ cmp bx,0AA55h
+ jne .noedd
+ test cl,1 ; Extended disk access functionality set
+ jz .noedd
+ ;
+ ; We have EDD support...
+ ;
+ mov byte [getlinsec.jmp+1],(getlinsec_ebios-(getlinsec.jmp+2))
+.noedd:
+
+;
+; Load the first sector of LDLINUX.SYS; this used to be all proper
+; with parsing the superblock and root directory; it doesn't fit
+; together with EBIOS support, unfortunately.
+;
+ mov eax,[FirstSector] ; Sector start
+ mov bx,ldlinux_sys ; Where to load it
+ call getonesec
+
+ ; Some modicum of integrity checking
+ cmp dword [ldlinux_magic+4],LDLINUX_MAGIC^HEXDATE
+ jne kaboom
+
+ ; Go for it...
+ jmp ldlinux_ent
+
+;
+; getonesec: get one disk sector
+;
+getonesec:
+ mov bp,1 ; One sector
+ ; Fall through
+
+;
+; getlinsec: load a sequence of BP floppy sector given by the linear sector
+; number in EAX into the buffer at ES:BX. We try to optimize
+; by loading up to a whole track at a time, but the user
+; is responsible for not crossing a 64K boundary.
+; (Yes, BP is weird for a count, but it was available...)
+;
+; On return, BX points to the first byte after the transferred
+; block.
+;
+; This routine assumes CS == DS, and trashes most registers.
+;
+; Stylistic note: use "xchg" instead of "mov" when the source is a register
+; that is dead from that point; this saves space. However, please keep
+; the order to dst,src to keep things sane.
+;
+getlinsec:
+ add eax,[bsHidden] ; Add partition offset
+ xor edx,edx ; Zero-extend LBA (eventually allow 64 bits)
+
+.jmp: jmp strict short getlinsec_cbios
+
+;
+; getlinsec_ebios:
+;
+; getlinsec implementation for EBIOS (EDD)
+;
+getlinsec_ebios:
+.loop:
+ push bp ; Sectors left
+.retry2:
+ call maxtrans ; Enforce maximum transfer size
+ movzx edi,bp ; Sectors we are about to read
+ mov cx,retry_count
+.retry:
+
+ ; Form DAPA on stack
+ push edx
+ push eax
+ push es
+ push bx
+ push di
+ push word 16
+ mov si,sp
+ pushad
+ mov dl,[DriveNumber]
+ push ds
+ push ss
+ pop ds ; DS <- SS
+ mov ah,42h ; Extended Read
+ int 13h
+ pop ds
+ popad
+ lea sp,[si+16] ; Remove DAPA
+ jc .error
+ pop bp
+ add eax,edi ; Advance sector pointer
+ sub bp,di ; Sectors left
+ shl di,SECTOR_SHIFT ; 512-byte sectors
+ add bx,di ; Advance buffer pointer
+ and bp,bp
+ jnz .loop
+
+ ret
+
+.error:
+ ; Some systems seem to get "stuck" in an error state when
+ ; using EBIOS. Doesn't happen when using CBIOS, which is
+ ; good, since some other systems get timeout failures
+ ; waiting for the floppy disk to spin up.
+
+ pushad ; Try resetting the device
+ xor ax,ax
+ mov dl,[DriveNumber]
+ int 13h
+ popad
+ loop .retry ; CX-- and jump if not zero
+
+ ;shr word [MaxTransfer],1 ; Reduce the transfer size
+ ;jnz .retry2
+
+ ; Total failure. Try falling back to CBIOS.
+ mov byte [getlinsec.jmp+1],(getlinsec_cbios-(getlinsec.jmp+2))
+ ;mov byte [MaxTransfer],63 ; Max possibe CBIOS transfer
+
+ pop bp
+ ; ... fall through ...
+
+;
+; getlinsec_cbios:
+;
+; getlinsec implementation for legacy CBIOS
+;
+getlinsec_cbios:
+.loop:
+ push edx
+ push eax
+ push bp
+ push bx
+
+ movzx esi,word [bsSecPerTrack]
+ movzx edi,word [bsHeads]
+ ;
+ ; Dividing by sectors to get (track,sector): we may have
+ ; up to 2^18 tracks, so we need to use 32-bit arithmetric.
+ ;
+ div esi
+ xor cx,cx
+ xchg cx,dx ; CX <- sector index (0-based)
+ ; EDX <- 0
+ ; eax = track #
+ div edi ; Convert track to head/cyl
+
+ ; We should test this, but it doesn't fit...
+ ; cmp eax,1023
+ ; ja .error
+
+ ;
+ ; Now we have AX = cyl, DX = head, CX = sector (0-based),
+ ; BP = sectors to transfer, SI = bsSecPerTrack,
+ ; ES:BX = data target
+ ;
+
+ call maxtrans ; Enforce maximum transfer size
+
+ ; Must not cross track boundaries, so BP <= SI-CX
+ sub si,cx
+ cmp bp,si
+ jna .bp_ok
+ mov bp,si
+.bp_ok:
+
+ shl ah,6 ; Because IBM was STOOPID
+ ; and thought 8 bits were enough
+ ; then thought 10 bits were enough...
+ inc cx ; Sector numbers are 1-based, sigh
+ or cl,ah
+ mov ch,al
+ mov dh,dl
+ mov dl,[DriveNumber]
+ xchg ax,bp ; Sector to transfer count
+ mov ah,02h ; Read sectors
+ mov bp,retry_count
+.retry:
+ pushad
+ int 13h
+ popad
+ jc .error
+.resume:
+ movzx ecx,al ; ECX <- sectors transferred
+ shl ax,SECTOR_SHIFT ; Convert sectors in AL to bytes in AX
+ pop bx
+ add bx,ax
+ pop bp
+ pop eax
+ pop edx
+ add eax,ecx
+ sub bp,cx
+ jnz .loop
+ ret
+
+.error:
+ dec bp
+ jnz .retry
+
+ xchg ax,bp ; Sectors transferred <- 0
+ shr word [MaxTransfer],1
+ jnz .resume
+ ; Fall through to disk_error
+
+;
+; kaboom: write a message and bail out.
+;
+disk_error:
+kaboom:
+ xor si,si
+ mov ss,si
+ mov sp,StackBuf-4 ; Reset stack
+ mov ds,si ; Reset data segment
+ pop dword [fdctab] ; Restore FDC table
+.patch: ; When we have full code, intercept here
+ mov si,bailmsg
+
+ ; Write error message, this assumes screen page 0
+.loop: lodsb
+ and al,al
+ jz .done
+ mov ah,0Eh ; Write to screen as TTY
+ mov bx,0007h ; Attribute
+ int 10h
+ jmp short .loop
+.done:
+ cbw ; AH <- 0
+.again: int 16h ; Wait for keypress
+ ; NB: replaced by int 18h if
+ ; chosen at install time..
+ int 19h ; And try once more to boot...
+.norge: jmp short .norge ; If int 19h returned; this is the end
+
+;
+; Truncate BP to MaxTransfer
+;
+maxtrans:
+ cmp bp,[MaxTransfer]
+ jna .ok
+ mov bp,[MaxTransfer]
+.ok: ret
+
+;
+; Error message on failure
+;
+bailmsg: db 'Boot error', 0Dh, 0Ah, 0
+
+ ; This fails if the boot sector overflows
+ zb 1F8h-($-$$)
+
+FirstSector dd 0xDEADBEEF ; Location of sector 1
+MaxTransfer dw 0x007F ; Max transfer size
+
+; This field will be filled in 0xAA55 by the installer, but we abuse it
+; to house a pointer to the INT 16h instruction at
+; kaboom.again, which gets patched to INT 18h in RAID mode.
+bootsignature dw kaboom.again-bootsec
+
+;
+; ===========================================================================
+; End of boot sector
+; ===========================================================================
+; Start of LDLINUX.SYS
+; ===========================================================================
+
+ldlinux_sys:
+
+syslinux_banner db 0Dh, 0Ah
+ db 'EXTLINUX '
+ db version_str, ' ', date, ' ', 0
+ db 0Dh, 0Ah, 1Ah ; EOF if we "type" this in DOS
+
+ align 8, db 0
+ldlinux_magic dd LDLINUX_MAGIC
+ dd LDLINUX_MAGIC^HEXDATE
+
+;
+; This area is patched by the installer. It is found by looking for
+; LDLINUX_MAGIC, plus 8 bytes.
+;
+patch_area:
+LDLDwords dw 0 ; Total dwords starting at ldlinux_sys,
+ ; not including ADVs
+LDLSectors dw 0 ; Number of sectors, not including
+ ; bootsec & this sec, but including the two ADVs
+CheckSum dd 0 ; Checksum starting at ldlinux_sys
+ ; value = LDLINUX_MAGIC - [sum of dwords]
+CurrentDir dd 2 ; "Current" directory inode number
+
+; Space for up to 64 sectors, the theoretical maximum
+SectorPtrs times 64 dd 0
+
+ldlinux_ent:
+;
+; Note that some BIOSes are buggy and run the boot sector at 07C0:0000
+; instead of 0000:7C00 and the like. We don't want to add anything
+; more to the boot sector, so it is written to not assume a fixed
+; value in CS, but we don't want to deal with that anymore from now
+; on.
+;
+ jmp 0:.next
+.next:
+
+;
+; Tell the user we got this far
+;
+ mov si,syslinux_banner
+ call writestr
+
+;
+; Tell the user if we're using EBIOS or CBIOS
+;
+print_bios:
+ mov si,cbios_name
+ cmp byte [getlinsec.jmp+1],(getlinsec_ebios-(getlinsec.jmp+2))
+ jne .cbios
+ mov si,ebios_name
+.cbios:
+ mov [BIOSName],si
+ call writestr
+
+ section .bss
+%define HAVE_BIOSNAME 1
+BIOSName resw 1
+
+ section .text
+;
+; Now we read the rest of LDLINUX.SYS. Don't bother loading the first
+; sector again, though.
+;
+load_rest:
+ mov si,SectorPtrs
+ mov bx,7C00h+2*SECTOR_SIZE ; Where we start loading
+ mov cx,[LDLSectors]
+
+.get_chunk:
+ jcxz .done
+ xor bp,bp
+ lodsd ; First sector of this chunk
+
+ mov edx,eax
+
+.make_chunk:
+ inc bp
+ dec cx
+ jz .chunk_ready
+ inc edx ; Next linear sector
+ cmp [si],edx ; Does it match
+ jnz .chunk_ready ; If not, this is it
+ add si,4 ; If so, add sector to chunk
+ jmp short .make_chunk
+
+.chunk_ready:
+ call getlinsecsr
+ shl bp,SECTOR_SHIFT
+ add bx,bp
+ jmp .get_chunk
+
+.done:
+
+;
+; All loaded up, verify that we got what we needed.
+; Note: the checksum field is embedded in the checksum region, so
+; by the time we get to the end it should all cancel out.
+;
+verify_checksum:
+ mov si,ldlinux_sys
+ mov cx,[LDLDwords]
+ mov edx,-LDLINUX_MAGIC
+.checksum:
+ lodsd
+ add edx,eax
+ loop .checksum
+
+ and edx,edx ; Should be zero
+ jz all_read ; We're cool, go for it!
+
+;
+; Uh-oh, something went bad...
+;
+ mov si,checksumerr_msg
+ call writestr
+ jmp kaboom
+
+;
+; -----------------------------------------------------------------------------
+; Subroutines that have to be in the first sector
+; -----------------------------------------------------------------------------
+
+;
+;
+; writestr: write a null-terminated string to the console
+; This assumes we're on page 0. This is only used for early
+; messages, so it should be OK.
+;
+writestr:
+.loop: lodsb
+ and al,al
+ jz .return
+ mov ah,0Eh ; Write to screen as TTY
+ mov bx,0007h ; Attribute
+ int 10h
+ jmp short .loop
+.return: ret
+
+
+; getlinsecsr: save registers, call getlinsec, restore registers
+;
+getlinsecsr: pushad
+ call getlinsec
+ popad
+ ret
+
+;
+; Checksum error message
+;
+checksumerr_msg db ' Load error - ', 0 ; Boot failed appended
+
+;
+; BIOS type string
+;
+cbios_name db 'CBIOS', 0
+ebios_name db 'EBIOS', 0
+
+;
+; Debug routine
+;
+%ifdef debug
+safedumpregs:
+ cmp word [Debug_Magic],0D00Dh
+ jnz nc_return
+ jmp dumpregs
+%endif
+
+rl_checkpt equ $ ; Must be <= 8000h
+
+rl_checkpt_off equ ($-$$)
+%ifndef DEPEND
+%if rl_checkpt_off > 400h
+%error "Sector 1 overflow"
+%endif
+%endif
+
+; ----------------------------------------------------------------------------
+; End of code and data that have to be in the first sector
+; ----------------------------------------------------------------------------
+
+all_read:
+;
+; Let the user (and programmer!) know we got this far. This used to be
+; in Sector 1, but makes a lot more sense here.
+;
+ mov si,copyright_str
+ call writestr
+
+;
+; Insane hack to expand the DOS superblock to dwords
+;
+expand_super:
+ xor eax,eax
+ mov si,superblock
+ mov di,SuperInfo
+ mov cx,superinfo_size
+.loop:
+ lodsw
+ dec si
+ stosd ; Store expanded word
+ xor ah,ah
+ stosd ; Store expanded byte
+ loop .loop
+
+;
+; Load the real (ext2) superblock; 1024 bytes long at offset 1024
+;
+ mov bx,SuperBlock
+ mov eax,1024 >> SECTOR_SHIFT
+ mov bp,ax
+ call getlinsec
+
+;
+; Compute some values...
+;
+ xor edx,edx
+ inc edx
+
+ ; s_log_block_size = log2(blocksize) - 10
+ mov cl,[SuperBlock+s_log_block_size]
+ add cl,10
+ mov [ClustByteShift],cl
+ mov eax,edx
+ shl eax,cl
+ mov [ClustSize],eax
+
+ sub cl,SECTOR_SHIFT
+ mov [ClustShift],cl
+ shr eax,SECTOR_SHIFT
+ mov [SecPerClust],eax
+ dec eax
+ mov [ClustMask],eax
+
+ add cl,SECTOR_SHIFT-2 ; 4 bytes/pointer
+ shl edx,cl
+ mov [PtrsPerBlock1],edx
+ shl edx,cl
+ mov [PtrsPerBlock2],edx
+
+;
+; Common initialization code
+;
+%include "init.inc"
+%include "cpuinit.inc"
+
+;
+; Initialize the metadata cache
+;
+ call initcache
+
+;
+; Now, everything is "up and running"... patch kaboom for more
+; verbosity and using the full screen system
+;
+ ; E9 = JMP NEAR
+ mov dword [kaboom.patch],0e9h+((kaboom2-(kaboom.patch+3)) << 8)
+
+;
+; Now we're all set to start with our *real* business. First load the
+; configuration file (if any) and parse it.
+;
+; In previous versions I avoided using 32-bit registers because of a
+; rumour some BIOSes clobbered the upper half of 32-bit registers at
+; random. I figure, though, that if there are any of those still left
+; they probably won't be trying to install Linux on them...
+;
+; The code is still ripe with 16-bitisms, though. Not worth the hassle
+; to take'm out. In fact, we may want to put them back if we're going
+; to boot ELKS at some point.
+;
+
+;
+; Load configuration file
+;
+load_config:
+ mov si,config_name ; Save config file name
+ mov di,ConfigName
+ call strcpy
+
+ mov di,ConfigName
+ call open
+ jz no_config_file
+
+;
+; Now we have the config file open. Parse the config file and
+; run the user interface.
+;
+%include "ui.inc"
+
+;
+; getlinsec_ext: same as getlinsec, except load any sector from the zero
+; block as all zeros; use to load any data derived
+; from an ext2 block pointer, i.e. anything *except the
+; superblock.*
+;
+getonesec_ext:
+ mov bp,1
+
+getlinsec_ext:
+ cmp eax,[SecPerClust]
+ jae getlinsec ; Nothing fancy
+
+ ; If we get here, at least part of what we want is in the
+ ; zero block. Zero one sector at a time and loop.
+ push eax
+ push cx
+ xchg di,bx
+ xor eax,eax
+ mov cx,SECTOR_SIZE >> 2
+ rep stosd
+ xchg di,bx
+ pop cx
+ pop eax
+ inc eax
+ dec bp
+ jnz getlinsec_ext
+ ret
+
+;
+; allocate_file: Allocate a file structure
+;
+; If successful:
+; ZF set
+; BX = file pointer
+; In unsuccessful:
+; ZF clear
+;
+allocate_file:
+ TRACER 'a'
+ push cx
+ mov bx,Files
+ mov cx,MAX_OPEN
+.check: cmp dword [bx], byte 0
+ je .found
+ add bx,open_file_t_size ; ZF = 0
+ loop .check
+ ; ZF = 0 if we fell out of the loop
+.found: pop cx
+ ret
+;
+; open_inode:
+; Open a file indicated by an inode number in EAX
+;
+; NOTE: This file considers finding a zero-length file an
+; error. This is so we don't have to deal with that special
+; case elsewhere in the program (most loops have the test
+; at the end).
+;
+; If successful:
+; ZF clear
+; SI = file pointer
+; EAX = file length in bytes
+; ThisInode = the first 128 bytes of the inode
+; If unsuccessful
+; ZF set
+;
+; Assumes CS == DS == ES.
+;
+open_inode.allocate_failure:
+ xor eax,eax
+ pop bx
+ pop di
+ ret
+
+open_inode:
+ push di
+ push bx
+ call allocate_file
+ jnz .allocate_failure
+
+ push cx
+ push gs
+ ; First, get the appropriate inode group and index
+ dec eax ; There is no inode 0
+ xor edx,edx
+ mov [bx+file_sector],edx
+ div dword [SuperBlock+s_inodes_per_group]
+ ; EAX = inode group; EDX = inode within group
+ push edx
+
+ ; Now, we need the block group descriptor.
+ ; To get that, we first need the relevant descriptor block.
+
+ shl eax, ext2_group_desc_lg2size ; Get byte offset in desc table
+ xor edx,edx
+ div dword [ClustSize]
+ ; eax = block #, edx = offset in block
+ add eax,dword [SuperBlock+s_first_data_block]
+ inc eax ; s_first_data_block+1
+ mov cl,[ClustShift]
+ shl eax,cl
+ push edx
+ shr edx,SECTOR_SHIFT
+ add eax,edx
+ pop edx
+ and dx,SECTOR_SIZE-1
+ call getcachesector ; Get the group descriptor
+ add si,dx
+ mov esi,[gs:si+bg_inode_table] ; Get inode table block #
+ pop eax ; Get inode within group
+ movzx edx, word [SuperBlock+s_inode_size]
+ mul edx
+ ; edx:eax = byte offset in inode table
+ div dword [ClustSize]
+ ; eax = block # versus inode table, edx = offset in block
+ add eax,esi
+ shl eax,cl ; Turn into sector
+ push dx
+ shr edx,SECTOR_SHIFT
+ add eax,edx
+ mov [bx+file_in_sec],eax
+ pop dx
+ and dx,SECTOR_SIZE-1
+ mov [bx+file_in_off],dx
+
+ call getcachesector
+ add si,dx
+ mov cx,EXT2_GOOD_OLD_INODE_SIZE >> 2
+ mov di,ThisInode
+ gs rep movsd
+
+ mov ax,[ThisInode+i_mode]
+ mov [bx+file_mode],ax
+ mov eax,[ThisInode+i_size]
+ mov [bx+file_bytesleft],eax
+ mov si,bx
+ and eax,eax ; ZF clear unless zero-length file
+ pop gs
+ pop cx
+ pop bx
+ pop di
+ ret
+
+ section .bss
+ alignb 4
+ThisInode resb EXT2_GOOD_OLD_INODE_SIZE ; The most recently opened inode
+
+ section .text
+;
+; close_file:
+; Deallocates a file structure (pointer in SI)
+; Assumes CS == DS.
+;
+close_file:
+ and si,si
+ jz .closed
+ mov dword [si],0 ; First dword == file_bytesleft
+ xor si,si
+.closed: ret
+
+;
+; searchdir:
+; Search the root directory for a pre-mangled filename in DS:DI.
+;
+; NOTE: This file considers finding a zero-length file an
+; error. This is so we don't have to deal with that special
+; case elsewhere in the program (most loops have the test
+; at the end).
+;
+; If successful:
+; ZF clear
+; SI = file pointer
+; DX:AX = EAX = file length in bytes
+; If unsuccessful
+; ZF set
+;
+; Assumes CS == DS == ES; *** IS THIS CORRECT ***?
+;
+searchdir:
+ push bx
+ push cx
+ push bp
+ mov byte [SymlinkCtr],MAX_SYMLINKS
+
+ mov eax,[CurrentDir]
+.begin_path:
+.leadingslash:
+ cmp byte [di],'/' ; Absolute filename?
+ jne .gotdir
+ mov eax,EXT2_ROOT_INO
+ inc di ; Skip slash
+ jmp .leadingslash
+.gotdir:
+
+ ; At this point, EAX contains the directory inode,
+ ; and DS:DI contains a pathname tail.
+.open:
+ push eax ; Save directory inode
+
+ call open_inode
+ jz .missing ; If error, done
+
+ mov cx,[si+file_mode]
+ shr cx,S_IFSHIFT ; Get file type
+
+ cmp cx,T_IFDIR
+ je .directory
+
+ add sp,4 ; Drop directory inode
+
+ cmp cx,T_IFREG
+ je .file
+ cmp cx,T_IFLNK
+ je .symlink
+
+ ; Otherwise, something bad...
+.err:
+ call close_file
+.err_noclose:
+ xor eax,eax
+ xor si,si
+ cwd ; DX <- 0
+
+.done:
+ and eax,eax ; Set/clear ZF
+ pop bp
+ pop cx
+ pop bx
+ ret
+
+.missing:
+ add sp,4 ; Drop directory inode
+ jmp .done
+
+ ;
+ ; It's a file.
+ ;
+.file:
+ cmp byte [di],0 ; End of path?
+ je .done ; If so, done
+ jmp .err ; Otherwise, error
+
+ ;
+ ; It's a directory.
+ ;
+.directory:
+ pop dword [ThisDir] ; Remember what directory we're searching
+
+ cmp byte [di],0 ; More path?
+ je .err ; If not, bad
+
+.skipslash: ; Skip redundant slashes
+ cmp byte [di],'/'
+ jne .readdir
+ inc di
+ jmp .skipslash
+
+.readdir:
+ mov cx,[SecPerClust]
+ push cx
+ shl cx,SECTOR_SHIFT
+ mov bx,trackbuf
+ add cx,bx
+ mov [EndBlock],cx
+ pop cx
+ push bx
+ call getfssec
+ pop bx
+ pushf ; Save EOF flag
+ push si ; Save filesystem pointer
+.getent:
+ cmp bx,[EndBlock]
+ jae .endblock
+
+ push di
+ cmp dword [bx+d_inode],0 ; Zero inode = void entry
+ je .nope
+
+ movzx cx,byte [bx+d_name_len]
+ lea si,[bx+d_name]
+ repe cmpsb
+ je .maybe
+.nope:
+ pop di
+ add bx,[bx+d_rec_len]
+ jmp .getent
+
+.endblock:
+ pop si
+ popf
+ jnc .readdir ; There is more
+ jmp .err ; Otherwise badness...
+
+.maybe:
+ mov eax,[bx+d_inode]
+
+ ; Does this match the end of the requested filename?
+ cmp byte [di],0
+ je .finish
+ cmp byte [di],'/'
+ jne .nope
+
+ ; We found something; now we need to open the file
+.finish:
+ pop bx ; Adjust stack (di)
+ pop si
+ call close_file ; Close directory
+ pop bx ; Adjust stack (flags)
+ jmp .open
+
+ ;
+ ; It's a symlink. We have to determine if it's a fast symlink
+ ; (data stored in the inode) or not (data stored as a regular
+ ; file.) Either which way, we start from the directory
+ ; which we just visited if relative, or from the root directory
+ ; if absolute, and append any remaining part of the path.
+ ;
+.symlink:
+ dec byte [SymlinkCtr]
+ jz .err ; Too many symlink references
+
+ cmp eax,SYMLINK_SECTORS*SECTOR_SIZE
+ jae .err ; Symlink too long
+
+ ; Computation for fast symlink, as defined by ext2/3 spec
+ xor ecx,ecx
+ cmp [ThisInode+i_file_acl],ecx
+ setne cl ; ECX <- i_file_acl ? 1 : 0
+ cmp [ThisInode+i_blocks],ecx
+ jne .slow_symlink
+
+ ; It's a fast symlink
+.fast_symlink:
+ call close_file ; We've got all we need
+ mov si,ThisInode+i_block
+
+ push di
+ mov di,SymlinkTmpBuf
+ mov ecx,eax
+ rep movsb
+ pop si
+
+.symlink_finish:
+ cmp byte [si],0
+ je .no_slash
+ mov al,'/'
+ stosb
+.no_slash:
+ mov bp,SymlinkTmpBufEnd
+ call strecpy
+ jc .err_noclose ; Buffer overflow
+
+ ; Now copy it to the "real" buffer; we need to have
+ ; two buffers so we avoid overwriting the tail on the
+ ; next copy
+ mov si,SymlinkTmpBuf
+ mov di,SymlinkBuf
+ push di
+ call strcpy
+ pop di
+ mov eax,[ThisDir] ; Resume searching previous directory
+ jmp .begin_path
+
+.slow_symlink:
+ mov bx,SymlinkTmpBuf
+ mov cx,SYMLINK_SECTORS
+ call getfssec
+ ; The EOF closed the file
+
+ mov si,di ; SI = filename tail
+ mov di,SymlinkTmpBuf
+ add di,ax ; AX = file length
+ jmp .symlink_finish
+
+
+ section .bss
+ alignb 4
+SymlinkBuf resb SYMLINK_SECTORS*SECTOR_SIZE+64
+SymlinkTmpBuf equ trackbuf
+SymlinkTmpBufEnd equ trackbuf+SYMLINK_SECTORS*SECTOR_SIZE+64
+ThisDir resd 1
+EndBlock resw 1
+SymlinkCtr resb 1
+
+ section .text
+;
+; mangle_name: Mangle a filename pointed to by DS:SI into a buffer pointed
+; to by ES:DI; ends on encountering any whitespace.
+; DI is preserved.
+;
+; This verifies that a filename is < FILENAME_MAX characters,
+; doesn't contain whitespace, zero-pads the output buffer,
+; and removes redundant slashes,
+; so "repe cmpsb" can do a compare, and the
+; path-searching routine gets a bit of an easier job.
+;
+; FIX: we may want to support \-escapes here (and this would
+; be the place.)
+;
+mangle_name:
+ push di
+ push bx
+ xor ax,ax
+ mov cx,FILENAME_MAX-1
+ mov bx,di
+
+.mn_loop:
+ lodsb
+ cmp al,' ' ; If control or space, end
+ jna .mn_end
+ cmp al,ah ; Repeated slash?
+ je .mn_skip
+ xor ah,ah
+ cmp al,'/'
+ jne .mn_ok
+ mov ah,al
+.mn_ok stosb
+.mn_skip: loop .mn_loop
+.mn_end:
+ cmp bx,di ; At the beginning of the buffer?
+ jbe .mn_zero
+ cmp byte [di-1],'/' ; Terminal slash?
+ jne .mn_zero
+.mn_kill: dec di ; If so, remove it
+ inc cx
+ jmp short .mn_end
+.mn_zero:
+ inc cx ; At least one null byte
+ xor ax,ax ; Zero-fill name
+ rep stosb
+ pop bx
+ pop di
+ ret ; Done
+
+;
+; unmangle_name: Does the opposite of mangle_name; converts a DOS-mangled
+; filename to the conventional representation. This is needed
+; for the BOOT_IMAGE= parameter for the kernel.
+;
+; DS:SI -> input mangled file name
+; ES:DI -> output buffer
+;
+; On return, DI points to the first byte after the output name,
+; which is set to a null byte.
+;
+unmangle_name: call strcpy
+ dec di ; Point to final null byte
+ ret
+
+;
+;
+; kaboom2: once everything is loaded, replace the part of kaboom
+; starting with "kaboom.patch" with this part
+
+kaboom2:
+ mov si,err_bootfailed
+ call cwritestr
+ cmp byte [kaboom.again+1],18h ; INT 18h version?
+ je .int18
+ call getchar
+ call vgaclearmode
+ int 19h ; And try once more to boot...
+.norge: jmp short .norge ; If int 19h returned; this is the end
+.int18:
+ call vgaclearmode
+ int 18h
+.noreg: jmp short .noreg ; Nynorsk
+
+
+;
+; linsector: Convert a linear sector index in a file to a linear sector number
+; EAX -> linear sector number
+; DS:SI -> open_file_t
+;
+; Returns next sector number in EAX; CF on EOF (not an error!)
+;
+linsector:
+ push gs
+ push ebx
+ push esi
+ push edi
+ push ecx
+ push edx
+ push ebp
+
+ push eax ; Save sector index
+ mov cl,[ClustShift]
+ shr eax,cl ; Convert to block number
+ push eax
+ mov eax,[si+file_in_sec]
+ mov bx,si
+ call getcachesector ; Get inode
+ add si,[bx+file_in_off] ; Get *our* inode
+ pop eax
+ lea ebx,[i_block+4*eax]
+ cmp eax,EXT2_NDIR_BLOCKS
+ jb .direct
+ mov ebx,i_block+4*EXT2_IND_BLOCK
+ sub eax,EXT2_NDIR_BLOCKS
+ mov ebp,[PtrsPerBlock1]
+ cmp eax,ebp
+ jb .ind1
+ mov ebx,i_block+4*EXT2_DIND_BLOCK
+ sub eax,ebp
+ mov ebp,[PtrsPerBlock2]
+ cmp eax,ebp
+ jb .ind2
+ mov ebx,i_block+4*EXT2_TIND_BLOCK
+ sub eax,ebp
+
+.ind3:
+ ; Triple indirect; eax contains the block no
+ ; with respect to the start of the tind area;
+ ; ebx contains the pointer to the tind block.
+ xor edx,edx
+ div dword [PtrsPerBlock2]
+ ; EAX = which dind block, EDX = pointer within dind block
+ push ax
+ shr eax,SECTOR_SHIFT-2
+ mov ebp,[gs:si+bx]
+ shl ebp,cl
+ add eax,ebp
+ call getcachesector
+ pop bx
+ and bx,(SECTOR_SIZE >> 2)-1
+ shl bx,2
+ mov eax,edx ; The ind2 code wants the remainder...
+
+.ind2:
+ ; Double indirect; eax contains the block no
+ ; with respect to the start of the dind area;
+ ; ebx contains the pointer to the dind block.
+ xor edx,edx
+ div dword [PtrsPerBlock1]
+ ; EAX = which ind block, EDX = pointer within ind block
+ push ax
+ shr eax,SECTOR_SHIFT-2
+ mov ebp,[gs:si+bx]
+ shl ebp,cl
+ add eax,ebp
+ call getcachesector
+ pop bx
+ and bx,(SECTOR_SIZE >> 2)-1
+ shl bx,2
+ mov eax,edx ; The int1 code wants the remainder...
+
+.ind1:
+ ; Single indirect; eax contains the block no
+ ; with respect to the start of the ind area;
+ ; ebx contains the pointer to the ind block.
+ push ax
+ shr eax,SECTOR_SHIFT-2
+ mov ebp,[gs:si+bx]
+ shl ebp,cl
+ add eax,ebp
+ call getcachesector
+ pop bx
+ and bx,(SECTOR_SIZE >> 2)-1
+ shl bx,2
+
+.direct:
+ mov ebx,[gs:bx+si] ; Get the pointer
+
+ pop eax ; Get the sector index again
+ shl ebx,cl ; Convert block number to sector
+ and eax,[ClustMask] ; Add offset within block
+ add eax,ebx
+
+ pop ebp
+ pop edx
+ pop ecx
+ pop edi
+ pop esi
+ pop ebx
+ pop gs
+ ret
+
+;
+; getfssec: Get multiple sectors from a file
+;
+; Same as above, except SI is a pointer to a open_file_t
+;
+; ES:BX -> Buffer
+; DS:SI -> Pointer to open_file_t
+; CX -> Sector count (0FFFFh = until end of file)
+; Must not exceed the ES segment
+; Returns CF=1 on EOF (not necessarily error)
+; On return ECX = number of bytes read
+; All arguments are advanced to reflect data read.
+;
+getfssec:
+ push ebp
+ push eax
+ push edx
+ push edi
+
+ movzx ecx,cx
+ push ecx ; Sectors requested read
+ mov eax,[si+file_bytesleft]
+ add eax,SECTOR_SIZE-1
+ shr eax,SECTOR_SHIFT
+ cmp ecx,eax ; Number of sectors left
+ jbe .lenok
+ mov cx,ax
+.lenok:
+.getfragment:
+ mov eax,[si+file_sector] ; Current start index
+ mov edi,eax
+ call linsector
+ push eax ; Fragment start sector
+ mov edx,eax
+ xor ebp,ebp ; Fragment sector count
+.getseccnt:
+ inc bp
+ dec cx
+ jz .do_read
+ xor eax,eax
+ mov ax,es
+ shl ax,4
+ add ax,bx ; Now DI = how far into 64K block we are
+ not ax ; Bytes left in 64K block
+ inc eax
+ shr eax,SECTOR_SHIFT ; Sectors left in 64K block
+ cmp bp,ax
+ jnb .do_read ; Unless there is at least 1 more sector room...
+ inc edi ; Sector index
+ inc edx ; Linearly next sector
+ mov eax,edi
+ call linsector
+ ; jc .do_read
+ cmp edx,eax
+ je .getseccnt
+.do_read:
+ pop eax ; Linear start sector
+ pushad
+ call getlinsec_ext
+ popad
+ push bp
+ shl bp,9
+ add bx,bp ; Adjust buffer pointer
+ pop bp
+ add [si+file_sector],ebp ; Next sector index
+ jcxz .done
+ jnz .getfragment
+ ; Fall through
+.done:
+ pop ecx ; Sectors requested read
+ shl ecx,SECTOR_SHIFT
+ cmp ecx,[si+file_bytesleft]
+ jb .noteof
+ mov ecx,[si+file_bytesleft]
+.noteof: sub [si+file_bytesleft],ecx
+ ; Did we run out of file?
+ cmp dword [si+file_bytesleft],1
+ ; CF set if [SI] < 1, i.e. == 0
+ pop edi
+ pop edx
+ pop eax
+ pop ebp
+ ret
+
+; -----------------------------------------------------------------------------
+; Common modules
+; -----------------------------------------------------------------------------
+
+%include "getc.inc" ; getc et al
+%include "conio.inc" ; Console I/O
+%include "plaincon.inc" ; writechr
+%include "writestr.inc" ; String output
+%include "configinit.inc" ; Initialize configuration
+%include "parseconfig.inc" ; High-level config file handling
+%include "parsecmd.inc" ; Low-level config file handling
+%include "bcopy32.inc" ; 32-bit bcopy
+%include "loadhigh.inc" ; Load a file into high memory
+%include "font.inc" ; VGA font stuff
+%include "graphics.inc" ; VGA graphics
+%include "highmem.inc" ; High memory sizing
+%include "strcpy.inc" ; strcpy()
+%include "strecpy.inc" ; strcpy with end pointer check
+%include "cache.inc" ; Metadata disk cache
+%include "adv.inc" ; Auxillary Data Vector
+%include "localboot.inc" ; Disk-based local boot
+
+; -----------------------------------------------------------------------------
+; Begin data section
+; -----------------------------------------------------------------------------
+
+ section .data
+copyright_str db ' Copyright (C) 1994-', year, ' H. Peter Anvin'
+ db CR, LF, 0
+err_bootfailed db CR, LF, 'Boot failed: please change disks and press '
+ db 'a key to continue.', CR, LF, 0
+config_name db 'extlinux.conf',0 ; Unmangled form
+
+;
+; Command line options we'd like to take a look at
+;
+; mem= and vga= are handled as normal 32-bit integer values
+initrd_cmd db 'initrd='
+initrd_cmd_len equ 7
+
+;
+; Config file keyword table
+;
+%include "keywords.inc"
+
+;
+; Extensions to search for (in *forward* order).
+;
+ align 4, db 0
+exten_table: db '.cbt' ; COMBOOT (specific)
+ db '.img' ; Disk image
+ db '.bs', 0 ; Boot sector
+ db '.com' ; COMBOOT (same as DOS)
+ db '.c32' ; COM32
+exten_table_end:
+ dd 0, 0 ; Need 8 null bytes here
+
+;
+; Misc initialized (data) variables
+;
+%ifdef debug ; This code for debugging only
+debug_magic dw 0D00Dh ; Debug code sentinel
+%endif
+
+ alignb 4, db 0
+BufSafe dw trackbufsize/SECTOR_SIZE ; Clusters we can load into trackbuf
+BufSafeBytes dw trackbufsize ; = how many bytes?
+%ifndef DEPEND
+%if ( trackbufsize % SECTOR_SIZE ) != 0
+%error trackbufsize must be a multiple of SECTOR_SIZE
+%endif
+%endif
diff --git a/core/font.inc b/core/font.inc
new file mode 100644
index 00000000..be9a365c
--- /dev/null
+++ b/core/font.inc
@@ -0,0 +1,139 @@
+;; -----------------------------------------------------------------------
+;;
+;; Copyright 1994-2008 H. Peter Anvin - All Rights Reserved
+;;
+;; This program is free software; you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation, Inc., 53 Temple Place Ste 330,
+;; Boston MA 02111-1307, USA; either version 2 of the License, or
+;; (at your option) any later version; incorporated herein by reference.
+;;
+;; -----------------------------------------------------------------------
+
+;;
+;; font.inc
+;;
+;; VGA font handling code
+;;
+
+ section .text
+
+;
+; loadfont: Load a .psf font file and install it onto the VGA console
+; (if we're not on a VGA screen then ignore.) It is called with
+; SI and EAX set by routine searchdir
+;
+loadfont:
+ ; XXX: This can be 8K+4 bytes and the trackbuf is only
+ ; guaranteed to be 8K in size...
+ mov bx,trackbuf
+ mov cx,[BufSafe]
+ call getfssec
+
+ mov ax,[trackbuf] ; Magic number
+ cmp ax,0436h
+ jne lf_ret
+
+ mov al,[trackbuf+2] ; File mode
+ cmp al,5 ; Font modes 0-5 supported
+ ja lf_ret
+
+ mov bh,byte [trackbuf+3] ; Height of font
+ cmp bh,2 ; VGA minimum
+ jb lf_ret
+ cmp bh,32 ; VGA maximum
+ ja lf_ret
+
+ ; Copy to font buffer
+ mov si,trackbuf+4 ; Start of font data
+ mov [VGAFontSize],bh
+ mov di,vgafontbuf
+ mov cx,(32*256) >> 2 ; Maximum size
+ rep movsd
+
+ mov [UserFont], byte 1 ; Set font flag
+
+ ; Fall through to use_font
+
+;
+; use_font:
+; This routine activates whatever font happens to be in the
+; vgafontbuf, and updates the adjust_screen data.
+; Must be called with CS = DS = ES
+;
+use_font:
+ test byte [UsingVGA], ~03h ; Nonstandard mode?
+ jz .modeok
+ call vgaclearmode
+
+.modeok:
+ test [UserFont], byte 1 ; Are we using a user-specified font?
+ jz adjust_screen ; If not, just do the normal stuff
+
+ mov bp,vgafontbuf
+ mov bh,[VGAFontSize]
+
+ xor bl,bl ; Needed by both INT 10h calls
+
+ test byte [UsingVGA], 01h ; Are we in graphics mode?
+ jz .text
+
+.graphics:
+ xor cx,cx
+ mov cl,bh ; CX = bytes/character
+ mov ax,[GXPixRows]
+ div cl ; Compute char rows per screen
+ mov dl,al
+ dec ax
+ mov [VidRows],al
+ mov ax,1121h ; Set user character table
+ int 10h
+ mov ax,[GXPixCols]
+ shr ax,3 ; 8 pixels/character
+ dec ax
+ mov [VidCols],al
+.lf_ret: ret ; No need to call adjust_screen
+
+.text:
+ mov cx,256
+ xor dx,dx
+ mov ax,1110h
+ int 10h ; Load into VGA RAM
+
+ xor bl,bl
+ mov ax,1103h ; Select page 0
+ int 10h
+
+lf_ret equ use_font.lf_ret
+
+;
+; adjust_screen: Set the internal variables associated with the screen size.
+; This is a subroutine in case we're loading a custom font.
+;
+adjust_screen:
+ pusha
+ mov al,[BIOS_vidrows]
+ and al,al
+ jnz vidrows_ok
+ mov al,24 ; No vidrows in BIOS, assume 25
+ ; (Remember: vidrows == rows-1)
+vidrows_ok: mov [VidRows],al
+ mov ah,0fh
+ int 10h ; Read video state
+ dec ah ; Store count-1 (same as rows)
+ mov [VidCols],ah
+ popa
+ ret
+
+ section .bss
+vgafontbuf resb 8192
+
+ section .data
+ align 2, db 0
+VGAFontSize dw 16 ; Defaults to 16 byte font
+UserFont db 0 ; Using a user-specified font
+
+ section .bss1
+ alignb 4
+GXPixCols resw 1 ; Graphics mode pixel columns
+GXPixRows resw 1 ; Graphics mode pixel rows
diff --git a/core/genhash.pl b/core/genhash.pl
new file mode 100755
index 00000000..c79139fd
--- /dev/null
+++ b/core/genhash.pl
@@ -0,0 +1,26 @@
+#!/usr/bin/perl
+#
+# Generate hash values for keywords
+#
+
+eval { use bytes; };
+
+while ( defined($keywd = <STDIN>) ) {
+ chomp $keywd;
+
+ ($keywd,$keywdname) = split(/\s+/, $keywd);
+ $keywdname = $keywd unless ( $keywdname );
+
+ $l = length($keywd);
+ $h = 0;
+ for ( $i = 0 ; $i < $l ; $i++ ) {
+ $c = ord(substr($keywd,$i,1)) | 0x20;
+ $h = ((($h << 5)|($h >> 27)) ^ $c) & 0xFFFFFFFF;
+ }
+ if ( $seenhash{$h} ) {
+ printf STDERR "$0: hash collision (0x%08x) %s %s\n",
+ $h, $keywd, $seenhash{$h};
+ }
+ $seenhash{$h} = $keywd;
+ printf("%-23s equ 0x%08x\n", "hash_${keywdname}", $h);
+}
diff --git a/core/getc.inc b/core/getc.inc
new file mode 100644
index 00000000..b36115ca
--- /dev/null
+++ b/core/getc.inc
@@ -0,0 +1,381 @@
+;; -----------------------------------------------------------------------
+;;
+;; Copyright 1994-2008 H. Peter Anvin - All Rights Reserved
+;;
+;; This program is free software; you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation, Inc., 53 Temple Place Ste 330,
+;; Boston MA 02111-1307, USA; either version 2 of the License, or
+;; (at your option) any later version; incorporated herein by reference.
+;;
+;; -----------------------------------------------------------------------
+
+;;
+;; getc.inc
+;;
+;; Simple file handling library (open, getc, ungetc)
+;;
+;; WARNING: This interface uses the real_mode_seg/comboot_seg.
+;;
+
+MAX_GETC_LG2 equ 4 ; Max number of file nesting
+MAX_GETC equ (1 << MAX_GETC_LG2)
+bytes_per_getc_lg2 equ 16-MAX_GETC_LG2
+bytes_per_getc equ (1 << bytes_per_getc_lg2)
+secs_per_getc equ bytes_per_getc/SECTOR_SIZE
+MAX_UNGET equ 9 ; Max bytes that can be pushed back
+
+ struc getc_file
+gc_file resw 1 ; File pointer
+gc_bufbytes resw 1 ; Bytes left in buffer
+gc_bufdata resw 1 ; Pointer to data in buffer
+gc_unget_cnt resb 1 ; Character pushed back count
+gc_unget_buf resb MAX_UNGET ; Character pushed back buffer
+ endstruc
+getc_file_lg2 equ 4 ; Size of getc_file as a power of 2
+
+%ifndef DEPEND
+%if (getc_file_size != (1 << getc_file_lg2))
+%error "getc_file_size != (1 << getc_file_lg2)"
+%endif
+%endif
+
+;
+; open,getc: Load a file a character at a time for parsing in a manner
+; similar to the C library getc routine.
+; Up to MAX_GETC files can be open at the same time,
+; they are accessed in a stack-like fashion.
+;
+; All routines assume CS == DS.
+;
+; open: Input: mangled filename in DS:DI
+; Output: ZF set on file not found or zero length
+;
+; openfd: Input: file handle in SI, file size in EAX
+; Output: ZF set on getc stack overflow
+;
+; getc: Output: CF set on end of file
+; Character loaded in AL
+;
+; close: Output: CF set if nothing open
+;
+open:
+ call searchdir
+ jz openfd.ret
+openfd:
+ push bx
+
+ mov bx,[CurrentGetC]
+ sub bx,getc_file_size
+ cmp bx,GetCStack
+ jb .stack_full ; Excessive nesting
+ mov [CurrentGetC],bx
+
+ mov [bx+gc_file],si ; File pointer
+ xor ax,ax
+ mov [bx+gc_bufbytes],ax ; Buffer empty
+ mov [bx+gc_unget_cnt],al ; ungetc buffer empty
+
+ inc ax ; ZF <- 0
+ pop bx
+.ret: ret
+
+.stack_full:
+ call close_file
+ xor ax,ax ; ZF <- 1
+ pop bx
+ ret
+
+getc:
+ push bx
+ push si
+ push di
+ push es
+
+ mov di,[CurrentGetC]
+ movzx bx,byte [di+gc_unget_cnt]
+ and bx,bx
+ jnz .have_unget
+
+ mov si,real_mode_seg ; Borrow the real_mode_seg
+ mov es,si
+
+.got_data:
+ sub word [di+gc_bufbytes],1
+ jc .get_data ; Was it zero already?
+ mov si,[di+gc_bufdata]
+ mov al,[es:si]
+ inc si
+ mov [di+gc_bufdata],si
+.done:
+ clc
+.ret:
+ pop es
+ pop di
+ pop si
+ pop bx
+ ret
+.have_unget:
+ dec bx
+ mov al,[di+bx+gc_unget_buf]
+ mov [di+gc_unget_cnt],bl
+ jmp .done
+
+.get_data:
+ pushad
+ ; Compute start of buffer
+ mov bx,di
+ sub bx,GetCStack
+ shl bx,bytes_per_getc_lg2-getc_file_lg2
+
+ mov [di+gc_bufdata],bx
+ mov si,[di+gc_file]
+ and si,si
+ mov [di+gc_bufbytes],si ; In case SI == 0
+ jz .empty
+ mov cx,bytes_per_getc >> SECTOR_SHIFT
+ call getfssec
+ mov [di+gc_bufbytes],cx
+ mov [di+gc_file],si
+ jcxz .empty
+ popad
+ TRACER 'd'
+ jmp .got_data
+
+.empty:
+ TRACER 'e'
+ ; [di+gc_bufbytes] is zero already, thus we will continue
+ ; to get EOF on any further attempts to read the file.
+ popad
+ xor al,al ; Return a predictable zero
+ stc
+ jmp .ret
+
+close:
+ push bx
+ push si
+ mov bx,[CurrentGetC]
+ mov si,[bx+gc_file]
+ call close_file
+ add bx,getc_file_size
+ mov [CurrentGetC],bx
+ pop si
+ pop bx
+ ret
+
+;
+; ungetc: Push a character (in AL) back into the getc buffer
+; Note: if more than MAX_UNGET bytes are pushed back, all
+; hell will break loose.
+;
+ungetc:
+ push di
+ push bx
+ mov di,[CurrentGetC]
+ movzx bx,[di+gc_unget_cnt]
+ mov [bx+di+gc_unget_buf],al
+ inc bx
+ mov [di+gc_unget_cnt],bl
+ pop bx
+ pop di
+ ret
+
+;
+; skipspace: Skip leading whitespace using "getc". If we hit end-of-line
+; or end-of-file, return with carry set; ZF = true of EOF
+; ZF = false for EOLN; otherwise CF = ZF = 0.
+;
+; Otherwise AL = first character after whitespace
+;
+skipspace:
+.loop: call getc
+ jc .eof
+ cmp al,1Ah ; DOS EOF
+ je .eof
+ cmp al,0Ah
+ je .eoln
+ cmp al,' '
+ jbe .loop
+ ret ; CF = ZF = 0
+.eof: cmp al,al ; Set ZF
+ stc ; Set CF
+ ret
+.eoln: add al,0FFh ; Set CF, clear ZF
+ ret
+
+;
+; getint: Load an integer from the getc file.
+; Return CF if error; otherwise return integer in EBX
+;
+getint:
+ mov di,NumBuf
+.getnum: cmp di,NumBufEnd ; Last byte in NumBuf
+ jae .loaded
+ push di
+ call getc
+ pop di
+ jc .loaded
+ stosb
+ cmp al,'-'
+ jnb .getnum
+ call ungetc ; Unget non-numeric
+.loaded: mov byte [di],0
+ mov si,NumBuf
+ ; Fall through to parseint
+
+;
+; parseint: Convert an integer to a number in EBX
+; Get characters from string in DS:SI
+; Return CF on error
+; DS:SI points to first character after number
+;
+; Syntaxes accepted: [-]dec, [-]0+oct, [-]0x+hex, val+[KMG]
+;
+parseint:
+ push eax
+ push ecx
+ push bp
+ xor eax,eax ; Current digit (keep eax == al)
+ mov ebx,eax ; Accumulator
+ mov ecx,ebx ; Base
+ xor bp,bp ; Used for negative flag
+.begin: lodsb
+ cmp al,'-'
+ jne .not_minus
+ xor bp,1 ; Set unary minus flag
+ jmp short .begin
+.not_minus:
+ cmp al,'0'
+ jb .err
+ je .octhex
+ cmp al,'9'
+ ja .err
+ mov cl,10 ; Base = decimal
+ jmp short .foundbase
+.octhex:
+ lodsb
+ cmp al,'0'
+ jb .km ; Value is zero
+ or al,20h ; Downcase
+ cmp al,'x'
+ je .ishex
+ cmp al,'7'
+ ja .err
+ mov cl,8 ; Base = octal
+ jmp short .foundbase
+.ishex:
+ mov al,'0' ; No numeric value accrued yet
+ mov cl,16 ; Base = hex
+.foundbase:
+ call unhexchar
+ jc .km ; Not a (hex) digit
+ cmp al,cl
+ jae .km ; Invalid for base
+ imul ebx,ecx ; Multiply accumulated by base
+ add ebx,eax ; Add current digit
+ lodsb
+ jmp short .foundbase
+.km:
+ dec si ; Back up to last non-numeric
+ lodsb
+ or al,20h
+ cmp al,'k'
+ je .isk
+ cmp al,'m'
+ je .ism
+ cmp al,'g'
+ je .isg
+ dec si ; Back up
+.fini: and bp,bp
+ jz .ret ; CF=0!
+ neg ebx ; Value was negative
+.done: clc
+.ret: pop bp
+ pop ecx
+ pop eax
+ ret
+.err: stc
+ jmp short .ret
+.isg: shl ebx,10 ; * 2^30
+.ism: shl ebx,10 ; * 2^20
+.isk: shl ebx,10 ; * 2^10
+ jmp .fini
+
+ section .bss1
+ alignb 4
+NumBuf resb 15 ; Buffer to load number
+NumBufEnd resb 1 ; Last byte in NumBuf
+
+GetCStack resb getc_file_size*MAX_GETC
+.end equ $
+
+ section .data
+CurrentGetC dw GetCStack.end ; GetCStack empty
+
+;
+; unhexchar: Convert a hexadecimal digit in AL to the equivalent number;
+; return CF=1 if not a hex digit
+;
+ section .text
+unhexchar:
+ cmp al,'0'
+ jb .ret ; If failure, CF == 1 already
+ cmp al,'9'
+ ja .notdigit
+ sub al,'0' ; CF <- 0
+ ret
+.notdigit: or al,20h ; upper case -> lower case
+ cmp al,'a'
+ jb .ret ; If failure, CF == 1 already
+ cmp al,'f'
+ ja .err
+ sub al,'a'-10 ; CF <- 0
+ ret
+.err: stc
+.ret: ret
+
+;
+;
+; getline: Get a command line, converting control characters to spaces
+; and collapsing streches to one; a space is appended to the
+; end of the string, unless the line is empty.
+; The line is terminated by ^J, ^Z or EOF and is written
+; to ES:DI. On return, DI points to first char after string.
+; CF is set if we hit EOF.
+;
+getline:
+ call skipspace
+ mov dl,1 ; Empty line -> empty string.
+ jz .eof ; eof
+ jc .eoln ; eoln
+ call ungetc
+.fillloop: push dx
+ push di
+ call getc
+ pop di
+ pop dx
+ jc .ret ; CF set!
+ cmp al,' '
+ jna .ctrl
+ xor dx,dx
+.store: stosb
+ jmp short .fillloop
+.ctrl: cmp al,10
+ je .ret ; CF clear!
+ cmp al,26
+ je .eof
+ and dl,dl
+ jnz .fillloop ; Ignore multiple spaces
+ mov al,' ' ; Ctrl -> space
+ inc dx
+ jmp short .store
+.eoln: clc ; End of line is not end of file
+ jmp short .ret
+.eof: stc
+.ret: pushf ; We want the last char to be space!
+ and dl,dl
+ jnz .xret
+ mov al,' '
+ stosb
+.xret: popf
+ ret
diff --git a/core/graphics.inc b/core/graphics.inc
new file mode 100644
index 00000000..2b8290fc
--- /dev/null
+++ b/core/graphics.inc
@@ -0,0 +1,331 @@
+;; -----------------------------------------------------------------------
+;;
+;; Copyright 1994-2008 H. Peter Anvin - All Rights Reserved
+;;
+;; This program is free software; you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation, Inc., 53 Temple Place Ste 330,
+;; Boston MA 02111-1307, USA; either version 2 of the License, or
+;; (at your option) any later version; incorporated herein by reference.
+;;
+;; -----------------------------------------------------------------------
+
+; ----------------------------------------------------------------------------
+; VGA splash screen code
+; ----------------------------------------------------------------------------
+
+;
+; vgadisplayfile:
+; Display a graphical splash screen.
+; The file is already opened on the top of the getc stack.
+;
+; Assumes CS == DS == ES.
+;
+ section .text
+
+vgadisplayfile:
+ ; This is a cheap and easy way to make sure the screen is
+ ; cleared in case we were in graphics mode already
+ call vgaclearmode
+ call vgasetmode
+ jnz .error_nz
+
+.graphalready:
+ ; Load the header.
+ mov cx,4+2*2+16*3
+ mov di,LSSHeader
+.gethdr:
+ call getc
+ stosb
+ loop .gethdr
+ jc .error
+
+ ; The header WILL be in the first chunk.
+ cmp dword [LSSMagic],0x1413f33d ; Magic number
+.error_nz: jne .error
+
+ mov dx,GraphColorMap ; Color map offset
+ mov ax,1012h ; Set RGB registers
+ xor bx,bx ; First register number
+ mov cx,16 ; 16 registers
+ int 10h
+
+.movecursor:
+ mov ax,[GraphYSize] ; Number of pixel rows
+ mov dx,[VGAFontSize]
+ add ax,dx
+ dec ax
+ div dl
+ xor dx,dx ; Set column to 0
+ cmp al,[VidRows]
+ jb .rowsok
+ mov al,[VidRows]
+ dec al
+.rowsok:
+ mov dh,al
+ mov ah,2
+ xor bx,bx
+ int 10h ; Set cursor below image
+
+ mov cx,[GraphYSize] ; Number of graphics rows
+ mov word [VGAPos],0
+
+.drawpixelrow:
+ push cx
+ mov di,VGARowBuffer
+ ; Pre-clear the row buffer
+ push di
+ mov cx,640/4
+ xor eax,eax
+ rep stosd
+ pop di
+ push di
+ mov cx,[GraphXSize]
+ call rledecode ; Decode one row
+ pop si
+ push es
+ mov di,0A000h ; VGA segment
+ mov es,di
+ mov di,[VGAPos]
+ mov bp,640
+ call packedpixel2vga
+ add word [VGAPos],80
+ pop es
+ pop cx
+ loop .drawpixelrow
+
+.error:
+ jmp close ; Tailcall!
+
+;
+; rledecode:
+; Decode a pixel row in RLE16 format.
+;
+; getc stack -> input
+; CX -> pixel count
+; ES:DI -> output (packed pixel)
+;
+rledecode:
+ xor dx,dx ; DL = last pixel, DH = nybble buffer
+.loop:
+ call .getnybble
+ cmp al,dl
+ je .run ; Start of run sequence
+ stosb
+ mov dl,al
+ dec cx
+ jnz .loop
+.done:
+ ret
+.run:
+ xor bx,bx
+ call .getnybble
+ or bl,al
+ jz .longrun
+.dorun:
+ push cx
+ mov cx,bx
+ mov al,dl
+ rep stosb
+ pop cx
+ sub cx,bx
+ ja .loop
+ jmp short .done
+.longrun:
+ call .getnybble
+ mov bl,al
+ call .getnybble
+ shl al,4
+ or bl,al
+ add bx,16
+ jmp short .dorun
+
+.getnybble:
+ test dh,10h
+ jz .low
+ and dh,0Fh
+ mov al,dh
+ ret
+.low:
+ call getc
+ mov dh,al
+ shr dh,4
+ or dh,10h ; Nybble already read
+ and al,0Fh
+ ret
+
+;
+; packedpixel2vga:
+; Convert packed-pixel to VGA bitplanes
+;
+; DS:SI -> packed pixel string
+; BP -> pixel count (multiple of 8)
+; ES:DI -> output
+;
+packedpixel2vga:
+ mov dx,3C4h ; VGA Sequencer Register select port
+ mov al,2 ; Sequencer mask
+ out dx,al ; Select the sequencer mask
+ inc dx ; VGA Sequencer Register data port
+ mov al,1
+ mov bl,al
+.planeloop:
+ pusha
+ out dx,al
+.loop1:
+ mov cx,8
+.loop2:
+ xchg cx,bx
+ lodsb
+ shr al,cl
+ rcl ch,1 ; VGA is bigendian. Sigh.
+ xchg cx,bx
+ loop .loop2
+ mov al,bh
+ stosb
+ sub bp,byte 8
+ ja .loop1
+ popa
+ inc bl
+ shl al,1
+ cmp bl,4
+ jbe .planeloop
+ ret
+
+;
+; vgasetmode:
+; Enable VGA graphics, if possible; return ZF=1 on success
+; DS must be set to the base segment; ES is set to DS.
+;
+vgasetmode:
+ push ds
+ pop es
+ mov al,[UsingVGA]
+ cmp al,01h
+ je .success ; Nothing to do...
+ test al,04h
+ jz .notvesa
+ ; We're in a VESA mode, which means VGA; use VESA call
+ ; to revert the mode, and then call the conventional
+ ; mode-setting for good measure...
+ mov ax,4F02h
+ mov bx,0012h
+ int 10h
+ jmp .setmode
+.notvesa:
+ mov ax,1A00h ; Get video card and monitor
+ xor bx,bx
+ int 10h
+ sub bl, 7 ; BL=07h and BL=08h OK
+ cmp bl, 1
+ ja .error ; ZF=0
+; mov bx,TextColorReg
+; mov dx,1009h ; Read color registers
+; int 10h
+.setmode:
+ mov ax,0012h ; Set mode = 640x480 VGA 16 colors
+ int 10h
+ mov dx,linear_color
+ mov ax,1002h ; Write color registers
+ int 10h
+ mov [UsingVGA], byte 1
+
+ ; Set GXPixCols and GXPixRows
+ mov dword [GXPixCols],640+(480 << 16)
+
+ call use_font ; Set graphics font/data
+ mov byte [ScrollAttribute], 00h
+
+.success:
+ xor ax,ax ; Set ZF
+.error:
+ ret
+
+;
+; vgaclearmode:
+; Disable VGA graphics. It is not safe to assume any value
+; for DS or ES.
+;
+vgaclearmode:
+ push ds
+ push es
+ pushad
+ mov ax,cs
+ mov ds,ax
+ mov es,ax
+ mov al,[UsingVGA]
+ and al,al ; Already in text mode?
+ jz .done
+ test al,04h
+ jz .notvesa
+ mov ax,4F02h ; VESA return to normal video mode
+ mov bx,0003h
+ int 10h
+.notvesa:
+ mov ax,0003h ; Return to normal video mode
+ int 10h
+; mov dx,TextColorReg ; Restore color registers
+; mov ax,1002h
+; int 10h
+ mov [UsingVGA], byte 0
+
+ mov byte [ScrollAttribute], 07h
+ call use_font ; Restore text font/data
+.done:
+ popad
+ pop es
+ pop ds
+ ret
+
+;
+; vgashowcursor/vgahidecursor:
+; If VGA graphics is enabled, draw a cursor/clear a cursor
+;
+vgashowcursor:
+ pushad
+ mov al,'_'
+ jmp short vgacursorcommon
+vgahidecursor:
+ pushad
+ mov al,' '
+vgacursorcommon:
+ cmp [UsingVGA], byte 1
+ jne .done
+ mov ah,09h
+ mov bx,0007h
+ mov cx,1
+ int 10h
+.done:
+ popad
+ ret
+
+
+ section .data
+ ; Map colors to consecutive DAC registers
+linear_color db 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 0
+
+ ; See comboot.doc, INT 22h AX=0017h for the semantics
+ ; of this byte.
+UsingVGA db 0
+
+ section .bss1
+ alignb 4
+LSSHeader equ $
+LSSMagic resd 1 ; Magic number
+GraphXSize resw 1 ; Width of splash screen file
+GraphYSize resw 1 ; Height of splash screen file
+GraphColorMap resb 3*16
+VGAPos resw 1 ; Pointer into VGA memory
+VGAFilePtr resw 1 ; Pointer into VGAFileBuf
+; TextColorReg resb 17 ; VGA color registers for text mode
+%if IS_SYSLINUX
+VGAFileBuf resb FILENAME_MAX+2 ; Unmangled VGA image name
+%else
+VGAFileBuf resb FILENAME_MAX ; Unmangled VGA image name
+%endif
+VGAFileBufEnd equ $
+VGAFileMBuf resb FILENAME_MAX ; Mangled VGA image name
+
+; We need a buffer of 640+80 bytes. At this point, command_line should
+; not be in use, so use that buffer.
+VGARowBuffer equ command_line
diff --git a/core/head.inc b/core/head.inc
new file mode 100644
index 00000000..37f1b36f
--- /dev/null
+++ b/core/head.inc
@@ -0,0 +1,31 @@
+; -*- fundamental -*- (asm-mode sucks)
+; -----------------------------------------------------------------------
+;
+; Copyright 2006-2008 H. Peter Anvin - All Rights Reserved
+;
+; This program is free software; you can redistribute it and/or modify
+; it under the terms of the GNU General Public License as published by
+; the Free Software Foundation, Inc., 53 Temple Place Ste 330,
+; Boston MA 02111-1307, USA; either version 2 of the License, or
+; (at your option) any later version; incorporated herein by reference.
+;
+; -----------------------------------------------------------------------
+
+;
+; head.inc
+;
+; Common header includes
+;
+
+%ifndef _HEAD_INC
+%define _HEAD_INC
+
+%include "macros.inc"
+%include "config.inc"
+%include "layout.inc"
+%include "kernel.inc"
+%include "bios.inc"
+%include "tracers.inc"
+%include "stack.inc"
+
+%endif ; _HEAD_INC
diff --git a/core/highmem.inc b/core/highmem.inc
new file mode 100644
index 00000000..1cd46dd9
--- /dev/null
+++ b/core/highmem.inc
@@ -0,0 +1,148 @@
+;; -----------------------------------------------------------------------
+;;
+;; Copyright 1994-2008 H. Peter Anvin - All Rights Reserved
+;;
+;; This program is free software; you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation, Inc., 53 Temple Place Ste 330,
+;; Boston MA 02111-1307, USA; either version 2 of the License, or
+;; (at your option) any later version; incorporated herein by reference.
+;;
+;; -----------------------------------------------------------------------
+
+;;
+;; highmem.inc
+;;
+;; Probe for the size of high memory. This can be overridden by a
+;; mem= command on the command line while booting a new kernel.
+;;
+
+ section .text
+
+;
+; This is set up as a subroutine; it will set up the global variable
+; HighMemSize. All registers are preserved.
+;
+highmemsize:
+ push es
+ pushad
+
+ push cs
+ pop es
+
+;
+; First, try INT 15:E820 (get BIOS memory map)
+;
+; Note: we may have to scan this multiple times, because some (daft) BIOSes
+; report main memory as multiple contiguous ranges...
+;
+get_e820:
+ mov dword [E820Max],-(1 << 20) ; Max amount of high memory
+ mov dword [E820Mem],(1 << 20) ; End of detected high memory
+.start_over:
+ xor ebx,ebx ; Start with first record
+ jmp short .do_e820 ; Skip "at end" check first time!
+.int_loop: and ebx,ebx ; If we're back at beginning...
+ jz .e820_done ; ... we're done
+.do_e820: mov eax,0000E820h
+ mov edx,534D4150h ; "SMAP" backwards
+ xor ecx,ecx
+ mov cl,20 ; ECX <- 20
+ mov di,E820Buf
+ int 15h
+ jnc .no_carry
+ ; If carry, ebx == 0 means error, ebx != 0 means we're done
+ and ebx,ebx
+ jnz .e820_done
+ jmp no_e820
+.no_carry:
+ cmp eax,534D4150h
+ jne no_e820
+;
+; Look for a memory block starting at <= 1 MB and continuing upward
+;
+ cmp dword [E820Buf+4], byte 0
+ ja .int_loop ; Start >= 4 GB?
+ mov eax, [E820Buf]
+ cmp dword [E820Buf+16],1
+ je .is_ram ; Is it memory?
+ ;
+ ; Non-memory range. Remember this as a limit; some BIOSes get the length
+ ; of primary RAM incorrect!
+ ;
+ cmp eax, (1 << 20)
+ jb .int_loop ; Starts in lowmem region
+ cmp eax,[E820Max]
+ jae .int_loop ; Already above limit
+ mov [E820Max],eax ; Set limit
+ jmp .int_loop
+
+.is_ram:
+ cmp eax,[E820Mem]
+ ja .int_loop ; Not contiguous with our starting point
+ add eax,[E820Buf+8]
+ jc .overflow
+ cmp dword [E820Buf+12],0
+ je .nooverflow
+.overflow:
+ or eax,-1
+.nooverflow:
+ cmp eax,[E820Mem]
+ jbe .int_loop ; All is below our baseline
+ mov [E820Mem],eax
+ jmp .start_over ; Start over in case we find an adjacent range
+
+.e820_done:
+ mov eax,[E820Mem]
+ cmp eax,[E820Max]
+ jna .not_limited
+ mov eax,[E820Max]
+.not_limited:
+ cmp eax,(1 << 20)
+ ja got_highmem ; Did we actually find memory?
+ ; otherwise fall through
+
+;
+; INT 15:E820 failed. Try INT 15:E801.
+;
+no_e820:
+ mov ax,0e801h ; Query high memory (semi-recent)
+ int 15h
+ jc no_e801
+ cmp ax,3c00h
+ ja no_e801 ; > 3C00h something's wrong with this call
+ jb e801_hole ; If memory hole we can only use low part
+
+ mov ax,bx
+ shl eax,16 ; 64K chunks
+ add eax,(16 << 20) ; Add first 16M
+ jmp short got_highmem
+
+;
+; INT 15:E801 failed. Try INT 15:88.
+;
+no_e801:
+ mov ah,88h ; Query high memory (oldest)
+ int 15h
+ cmp ax,14*1024 ; Don't trust memory >15M
+ jna e801_hole
+ mov ax,14*1024
+e801_hole:
+ and eax,0ffffh
+ shl eax,10 ; Convert from kilobytes
+ add eax,(1 << 20) ; First megabyte
+got_highmem:
+%if HIGHMEM_SLOP != 0
+ sub eax,HIGHMEM_SLOP
+%endif
+ mov [HighMemSize],eax
+ popad
+ pop es
+ ret ; Done!
+
+ section .bss
+ alignb 4
+E820Buf resd 5 ; INT 15:E820 data buffer
+E820Mem resd 1 ; Memory detected by E820
+E820Max resd 1 ; Is E820 memory capped?
+HighMemSize resd 1 ; End of memory pointer (bytes)
diff --git a/core/init.inc b/core/init.inc
new file mode 100644
index 00000000..0b213ace
--- /dev/null
+++ b/core/init.inc
@@ -0,0 +1,47 @@
+; -*- fundamental -*-
+; -----------------------------------------------------------------------
+;
+; Copyright 2004-2008 H. Peter Anvin - All Rights Reserved
+;
+; This program is free software; you can redistribute it and/or modify
+; it under the terms of the GNU General Public License as published by
+; the Free Software Foundation, Inc., 53 Temple Place Ste 330,
+; Boston MA 02111-1307, USA; either version 2 of the License, or
+; (at your option) any later version; incorporated herein by reference.
+;
+; -----------------------------------------------------------------------
+
+;
+; init.inc
+;
+; Common initialization code (inline)
+;
+
+ section .text
+common_init:
+ ; Now set up screen parameters
+ call adjust_screen
+
+;
+; Initialize configuration information
+;
+ call reset_config
+
+;
+; Clear Files structures
+;
+ mov di,Files
+ mov cx,(MAX_OPEN*open_file_t_size)/4
+ xor eax,eax
+ rep stosd
+
+%if IS_PXELINUX
+ mov di,Files+tftp_pktbuf
+ mov cx,MAX_OPEN
+.setbufptr:
+ mov [di],ax
+ add di,open_file_t_size
+ add ax,PKTBUF_SIZE
+ loop .setbufptr
+%endif
+ section .text ; This is an inline file...
diff --git a/core/isolinux-debug.asm b/core/isolinux-debug.asm
new file mode 100644
index 00000000..9c74b7cd
--- /dev/null
+++ b/core/isolinux-debug.asm
@@ -0,0 +1,2 @@
+%define DEBUG_MESSAGES 1
+%include "isolinux.asm"
diff --git a/core/isolinux.asm b/core/isolinux.asm
new file mode 100644
index 00000000..52d426f9
--- /dev/null
+++ b/core/isolinux.asm
@@ -0,0 +1,1549 @@
+; -*- fundamental -*- (asm-mode sucks)
+; ****************************************************************************
+;
+; isolinux.asm
+;
+; A program to boot Linux kernels off a CD-ROM using the El Torito
+; boot standard in "no emulation" mode, making the entire filesystem
+; available. It is based on the SYSLINUX boot loader for MS-DOS
+; floppies.
+;
+; Copyright 1994-2008 H. Peter Anvin - All Rights Reserved
+;
+; This program is free software; you can redistribute it and/or modify
+; it under the terms of the GNU General Public License as published by
+; the Free Software Foundation, Inc., 53 Temple Place Ste 330,
+; Boston MA 02111-1307, USA; either version 2 of the License, or
+; (at your option) any later version; incorporated herein by reference.
+;
+; ****************************************************************************
+
+%define IS_ISOLINUX 1
+%include "head.inc"
+
+;
+; Some semi-configurable constants... change on your own risk.
+;
+my_id equ isolinux_id
+FILENAME_MAX_LG2 equ 8 ; log2(Max filename size Including final null)
+FILENAME_MAX equ (1 << FILENAME_MAX_LG2)
+NULLFILE equ 0 ; Zero byte == null file name
+NULLOFFSET equ 0 ; Position in which to look
+retry_count equ 6 ; How patient are we with the BIOS?
+%assign HIGHMEM_SLOP 128*1024 ; Avoid this much memory near the top
+MAX_OPEN_LG2 equ 6 ; log2(Max number of open files)
+MAX_OPEN equ (1 << MAX_OPEN_LG2)
+SECTOR_SHIFT equ 11 ; 2048 bytes/sector (El Torito requirement)
+SECTOR_SIZE equ (1 << SECTOR_SHIFT)
+
+;
+; This is what we need to do when idle
+;
+%macro RESET_IDLE 0
+ ; Nothing
+%endmacro
+%macro DO_IDLE 0
+ ; Nothing
+%endmacro
+
+;
+; The following structure is used for "virtual kernels"; i.e. LILO-style
+; option labels. The options we permit here are `kernel' and `append
+; Since there is no room in the bottom 64K for all of these, we
+; stick them in high memory and copy them down before we need them.
+;
+ struc vkernel
+vk_vname: resb FILENAME_MAX ; Virtual name **MUST BE FIRST!**
+vk_rname: resb FILENAME_MAX ; Real name
+vk_appendlen: resw 1
+vk_type: resb 1 ; Type of file
+ alignb 4
+vk_append: resb max_cmd_len+1 ; Command line
+ alignb 4
+vk_end: equ $ ; Should be <= vk_size
+ endstruc
+
+;
+; Segment assignments in the bottom 640K
+; 0000h - main code/data segment (and BIOS segment)
+;
+real_mode_seg equ 2000h
+xfer_buf_seg equ 1000h ; Bounce buffer for I/O to high mem
+comboot_seg equ real_mode_seg ; COMBOOT image loading zone
+
+;
+; File structure. This holds the information for each currently open file.
+;
+ struc open_file_t
+file_sector resd 1 ; Sector pointer (0 = structure free)
+file_bytesleft resd 1 ; Number of bytes left
+file_left resd 1 ; Number of sectors left
+ resd 1 ; Unused
+ endstruc
+
+%ifndef DEPEND
+%if (open_file_t_size & (open_file_t_size-1))
+%error "open_file_t is not a power of 2"
+%endif
+%endif
+
+ struc dir_t
+dir_lba resd 1 ; Directory start (LBA)
+dir_len resd 1 ; Length in bytes
+dir_clust resd 1 ; Length in clusters
+ endstruc
+
+; ---------------------------------------------------------------------------
+; BEGIN CODE
+; ---------------------------------------------------------------------------
+
+;
+; Memory below this point is reserved for the BIOS and the MBR
+;
+ section .earlybss
+trackbufsize equ 8192
+trackbuf resb trackbufsize ; Track buffer goes here
+; ends at 2800h
+
+ ; Some of these are touched before the whole image
+ ; is loaded. DO NOT move this to .uibss.
+ section .bss2
+ alignb 4
+ISOFileName resb 64 ; ISO filename canonicalization buffer
+ISOFileNameEnd equ $
+CurDir resb dir_t_size ; Current directory
+RootDir resb dir_t_size ; Root directory
+FirstSecSum resd 1 ; Checksum of bytes 64-2048
+ImageDwords resd 1 ; isolinux.bin size, dwords
+InitStack resd 1 ; Initial stack pointer (SS:SP)
+DiskSys resw 1 ; Last INT 13h call
+ImageSectors resw 1 ; isolinux.bin size, sectors
+DiskError resb 1 ; Error code for disk I/O
+DriveNumber resb 1 ; CD-ROM BIOS drive number
+ISOFlags resb 1 ; Flags for ISO directory search
+RetryCount resb 1 ; Used for disk access retries
+
+_spec_start equ $
+
+;
+; El Torito spec packet
+;
+
+ alignb 8
+spec_packet: resb 1 ; Size of packet
+sp_media: resb 1 ; Media type
+sp_drive: resb 1 ; Drive number
+sp_controller: resb 1 ; Controller index
+sp_lba: resd 1 ; LBA for emulated disk image
+sp_devspec: resw 1 ; IDE/SCSI information
+sp_buffer: resw 1 ; User-provided buffer
+sp_loadseg: resw 1 ; Load segment
+sp_sectors: resw 1 ; Sector count
+sp_chs: resb 3 ; Simulated CHS geometry
+sp_dummy: resb 1 ; Scratch, safe to overwrite
+
+;
+; EBIOS drive parameter packet
+;
+ alignb 8
+drive_params: resw 1 ; Buffer size
+dp_flags: resw 1 ; Information flags
+dp_cyl: resd 1 ; Physical cylinders
+dp_head: resd 1 ; Physical heads
+dp_sec: resd 1 ; Physical sectors/track
+dp_totalsec: resd 2 ; Total sectors
+dp_secsize: resw 1 ; Bytes per sector
+dp_dpte: resd 1 ; Device Parameter Table
+dp_dpi_key: resw 1 ; 0BEDDh if rest valid
+dp_dpi_len: resb 1 ; DPI len
+ resb 1
+ resw 1
+dp_bus: resb 4 ; Host bus type
+dp_interface: resb 8 ; Interface type
+db_i_path: resd 2 ; Interface path
+db_d_path: resd 2 ; Device path
+ resb 1
+db_dpi_csum: resb 1 ; Checksum for DPI info
+
+;
+; EBIOS disk address packet
+;
+ alignb 8
+dapa: resw 1 ; Packet size
+.count: resw 1 ; Block count
+.off: resw 1 ; Offset of buffer
+.seg: resw 1 ; Segment of buffer
+.lba: resd 2 ; LBA (LSW, MSW)
+
+;
+; Spec packet for disk image emulation
+;
+ alignb 8
+dspec_packet: resb 1 ; Size of packet
+dsp_media: resb 1 ; Media type
+dsp_drive: resb 1 ; Drive number
+dsp_controller: resb 1 ; Controller index
+dsp_lba: resd 1 ; LBA for emulated disk image
+dsp_devspec: resw 1 ; IDE/SCSI information
+dsp_buffer: resw 1 ; User-provided buffer
+dsp_loadseg: resw 1 ; Load segment
+dsp_sectors: resw 1 ; Sector count
+dsp_chs: resb 3 ; Simulated CHS geometry
+dsp_dummy: resb 1 ; Scratch, safe to overwrite
+
+ alignb 4
+_spec_end equ $
+_spec_len equ _spec_end - _spec_start
+
+ alignb open_file_t_size
+Files resb MAX_OPEN*open_file_t_size
+
+ section .text
+;;
+;; Primary entry point. Because BIOSes are buggy, we only load the first
+;; CD-ROM sector (2K) of the file, so the number one priority is actually
+;; loading the rest.
+;;
+StackBuf equ $-44 ; 44 bytes needed for
+ ; the bootsector chainloading
+ ; code!
+OrigESDI equ StackBuf-4 ; The high dword on the stack
+
+bootsec equ $
+
+_start: ; Far jump makes sure we canonicalize the address
+ cli
+ jmp 0:_start1
+ times 8-($-$$) nop ; Pad to file offset 8
+
+ ; This table hopefully gets filled in by mkisofs using the
+ ; -boot-info-table option. If not, the values in this
+ ; table are default values that we can use to get us what
+ ; we need, at least under a certain set of assumptions.
+bi_pvd: dd 16 ; LBA of primary volume descriptor
+bi_file: dd 0 ; LBA of boot file
+bi_length: dd 0xdeadbeef ; Length of boot file
+bi_csum: dd 0xdeadbeef ; Checksum of boot file
+bi_reserved: times 10 dd 0xdeadbeef ; Reserved
+
+_start1: mov [cs:InitStack],sp ; Save initial stack pointer
+ mov [cs:InitStack+2],ss
+ xor ax,ax
+ mov ss,ax
+ mov sp,StackBuf ; Set up stack
+ push es ; Save initial ES:DI -> $PnP pointer
+ push di
+ mov ds,ax
+ mov es,ax
+ mov fs,ax
+ mov gs,ax
+ sti
+
+ cld
+ ; Show signs of life
+ mov si,syslinux_banner
+ call writestr
+%ifdef DEBUG_MESSAGES
+ mov si,copyright_str
+ call writestr
+%endif
+
+ ;
+ ; Before modifying any memory, get the checksum of bytes
+ ; 64-2048
+ ;
+initial_csum: xor edi,edi
+ mov si,_start1
+ mov cx,(SECTOR_SIZE-64) >> 2
+.loop: lodsd
+ add edi,eax
+ loop .loop
+ mov [FirstSecSum],edi
+
+ mov [DriveNumber],dl
+%ifdef DEBUG_MESSAGES
+ mov si,startup_msg
+ call writemsg
+ mov al,dl
+ call writehex2
+ call crlf
+%endif
+ ;
+ ; Initialize spec packet buffers
+ ;
+ mov di,_spec_start
+ mov cx,_spec_len >> 2
+ xor eax,eax
+ rep stosd
+
+ ; Initialize length field of the various packets
+ mov byte [spec_packet],13h
+ mov byte [drive_params],30
+ mov byte [dapa],16
+ mov byte [dspec_packet],13h
+
+ ; Other nonzero fields
+ inc word [dsp_sectors]
+
+ ; Now figure out what we're actually doing
+ ; Note: use passed-in DL value rather than 7Fh because
+ ; at least some BIOSes will get the wrong value otherwise
+ mov ax,4B01h ; Get disk emulation status
+ mov dl,[DriveNumber]
+ mov si,spec_packet
+ call int13
+ jc award_hack ; changed for BrokenAwardHack
+ mov dl,[DriveNumber]
+ cmp [sp_drive],dl ; Should contain the drive number
+ jne spec_query_failed
+
+%ifdef DEBUG_MESSAGES
+ mov si,spec_ok_msg
+ call writemsg
+ mov al,byte [sp_drive]
+ call writehex2
+ call crlf
+%endif
+
+found_drive:
+ ; Alright, we have found the drive. Now, try to find the
+ ; boot file itself. If we have a boot info table, life is
+ ; good; if not, we have to make some assumptions, and try
+ ; to figure things out ourselves. In particular, the
+ ; assumptions we have to make are:
+ ; - single session only
+ ; - only one boot entry (no menu or other alternatives)
+
+ cmp dword [bi_file],0 ; Address of code to load
+ jne found_file ; Boot info table present :)
+
+%ifdef DEBUG_MESSAGES
+ mov si,noinfotable_msg
+ call writemsg
+%endif
+
+ ; No such luck. See if the spec packet contained one.
+ mov eax,[sp_lba]
+ and eax,eax
+ jz set_file ; Good enough
+
+%ifdef DEBUG_MESSAGES
+ mov si,noinfoinspec_msg
+ call writemsg
+%endif
+
+ ; No such luck. Get the Boot Record Volume, assuming single
+ ; session disk, and that we're the first entry in the chain
+ mov eax,17 ; Assumed address of BRV
+ mov bx,trackbuf
+ call getonesec
+
+ mov eax,[trackbuf+47h] ; Get boot catalog address
+ mov bx,trackbuf
+ call getonesec ; Get boot catalog
+
+ mov eax,[trackbuf+28h] ; First boot entry
+ ; And hope and pray this is us...
+
+ ; Some BIOSes apparently have limitations on the size
+ ; that may be loaded (despite the El Torito spec being very
+ ; clear on the fact that it must all be loaded.) Therefore,
+ ; we load it ourselves, and *bleep* the BIOS.
+
+set_file:
+ mov [bi_file],eax
+
+found_file:
+ ; Set up boot file sizes
+ mov eax,[bi_length]
+ sub eax,SECTOR_SIZE-3
+ shr eax,2 ; bytes->dwords
+ mov [ImageDwords],eax ; boot file dwords
+ add eax,(2047 >> 2)
+ shr eax,9 ; dwords->sectors
+ mov [ImageSectors],ax ; boot file sectors
+
+ mov eax,[bi_file] ; Address of code to load
+ inc eax ; Don't reload bootstrap code
+%ifdef DEBUG_MESSAGES
+ mov si,offset_msg
+ call writemsg
+ call writehex8
+ call crlf
+%endif
+
+ ; Just in case some BIOSes have problems with
+ ; segment wraparound, use the normalized address
+ mov bx,((7C00h+2048) >> 4)
+ mov es,bx
+ xor bx,bx
+ mov bp,[ImageSectors]
+%ifdef DEBUG_MESSAGES
+ push ax
+ mov si,size_msg
+ call writemsg
+ mov ax,bp
+ call writehex4
+ call crlf
+ pop ax
+%endif
+ call getlinsec
+
+ push ds
+ pop es
+
+%ifdef DEBUG_MESSAGES
+ mov si,loaded_msg
+ call writemsg
+%endif
+
+ ; Verify the checksum on the loaded image.
+verify_image:
+ mov si,7C00h+2048
+ mov bx,es
+ mov ecx,[ImageDwords]
+ mov edi,[FirstSecSum] ; First sector checksum
+.loop es lodsd
+ add edi,eax
+ dec ecx
+ jz .done
+ and si,si
+ jnz .loop
+ ; SI wrapped around, advance ES
+ add bx,1000h
+ mov es,bx
+ jmp short .loop
+.done: mov ax,ds
+ mov es,ax
+ cmp [bi_csum],edi
+ je integrity_ok
+
+ mov si,checkerr_msg
+ call writemsg
+ jmp kaboom
+
+integrity_ok:
+%ifdef DEBUG_MESSAGES
+ mov si,allread_msg
+ call writemsg
+%endif
+ jmp all_read ; Jump to main code
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Start of BrokenAwardHack --- 10-nov-2002 Knut_Petersen@t-online.de
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;;
+;; There is a problem with certain versions of the AWARD BIOS ...
+;; the boot sector will be loaded and executed correctly, but, because the
+;; int 13 vector points to the wrong code in the BIOS, every attempt to
+;; load the spec packet will fail. We scan for the equivalent of
+;;
+;; mov ax,0201h
+;; mov bx,7c00h
+;; mov cx,0006h
+;; mov dx,0180h
+;; pushf
+;; call <direct far>
+;;
+;; and use <direct far> as the new vector for int 13. The code above is
+;; used to load the boot code into ram, and there should be no reason
+;; for anybody to change it now or in the future. There are no opcodes
+;; that use encodings relativ to IP, so scanning is easy. If we find the
+;; code above in the BIOS code we can be pretty sure to run on a machine
+;; with an broken AWARD BIOS ...
+;;
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+ ;;
+%ifdef DEBUG_MESSAGES ;;
+ ;;
+award_notice db "Trying BrokenAwardHack first ...",CR,LF,0 ;;
+award_not_orig db "BAH: Original Int 13 vector : ",0 ;;
+award_not_new db "BAH: Int 13 vector changed to : ",0 ;;
+award_not_succ db "BAH: SUCCESS",CR,LF,0 ;;
+award_not_fail db "BAH: FAILURE" ;;
+award_not_crlf db CR,LF,0 ;;
+ ;;
+%endif ;;
+ ;;
+award_oldint13 dd 0 ;;
+award_string db 0b8h,1,2,0bbh,0,7ch,0b9h,6,0,0bah,80h,1,09ch,09ah ;;
+ ;;
+ ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+award_hack: mov si,spec_err_msg ; Moved to this place from
+ call writemsg ; spec_query_faild
+ ;
+%ifdef DEBUG_MESSAGES ;
+ ;
+ mov si,award_notice ; display our plan
+ call writemsg ;
+ mov si,award_not_orig ; display original int 13
+ call writemsg ; vector
+%endif ;
+ mov eax,[13h*4] ;
+ mov [award_oldint13],eax ;
+ ;
+%ifdef DEBUG_MESSAGES ;
+ ;
+ call writehex8 ;
+ mov si,award_not_crlf ;
+ call writestr ;
+%endif ;
+ push es ; save ES
+ mov ax,0f000h ; ES = BIOS Seg
+ mov es,ax ;
+ cld ;
+ xor di,di ; start at ES:DI = f000:0
+award_loop: push di ; save DI
+ mov si,award_string ; scan for award_string
+ mov cx,7 ; length of award_string = 7dw
+ repz cmpsw ; compare
+ pop di ; restore DI
+ jcxz award_found ; jmp if found
+ inc di ; not found, inc di
+ jno award_loop ;
+ ;
+award_failed: pop es ; No, not this way :-((
+award_fail2: ;
+ ;
+%ifdef DEBUG_MESSAGES ;
+ ;
+ mov si,award_not_fail ; display failure ...
+ call writemsg ;
+%endif ;
+ mov eax,[award_oldint13] ; restore the original int
+ or eax,eax ; 13 vector if there is one
+ jz spec_query_failed ; and try other workarounds
+ mov [13h*4],eax ;
+ jmp spec_query_failed ;
+ ;
+award_found: mov eax,[es:di+0eh] ; load possible int 13 addr
+ pop es ; restore ES
+ ;
+ cmp eax,[award_oldint13] ; give up if this is the
+ jz award_failed ; active int 13 vector,
+ mov [13h*4],eax ; otherwise change 0:13h*4
+ ;
+ ;
+%ifdef DEBUG_MESSAGES ;
+ ;
+ push eax ; display message and
+ mov si,award_not_new ; new vector address
+ call writemsg ;
+ pop eax ;
+ call writehex8 ;
+ mov si,award_not_crlf ;
+ call writestr ;
+%endif ;
+ mov ax,4B01h ; try to read the spec packet
+ mov dl,[DriveNumber] ; now ... it should not fail
+ mov si,spec_packet ; any longer
+ int 13h ;
+ jc award_fail2 ;
+ ;
+%ifdef DEBUG_MESSAGES ;
+ ;
+ mov si,award_not_succ ; display our SUCCESS
+ call writemsg ;
+%endif ;
+ jmp found_drive ; and leave error recovery code
+ ;
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; End of BrokenAwardHack ---- 10-nov-2002 Knut_Petersen@t-online.de
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+
+ ; INT 13h, AX=4B01h, DL=<passed in value> failed.
+ ; Try to scan the entire 80h-FFh from the end.
+
+spec_query_failed:
+
+ ; some code moved to BrokenAwardHack
+
+ mov dl,0FFh
+.test_loop: pusha
+ mov ax,4B01h
+ mov si,spec_packet
+ mov byte [si],13h ; Size of buffer
+ call int13
+ popa
+ jc .still_broken
+
+ mov si,maybe_msg
+ call writemsg
+ mov al,dl
+ call writehex2
+ call crlf
+
+ cmp byte [sp_drive],dl
+ jne .maybe_broken
+
+ ; Okay, good enough...
+ mov si,alright_msg
+ call writemsg
+.found_drive0: mov [DriveNumber],dl
+.found_drive: jmp found_drive
+
+ ; Award BIOS 4.51 apparently passes garbage in sp_drive,
+ ; but if this was the drive number originally passed in
+ ; DL then consider it "good enough"
+.maybe_broken:
+ mov al,[DriveNumber]
+ cmp al,dl
+ je .found_drive
+
+ ; Intel Classic R+ computer with Adaptec 1542CP BIOS 1.02
+ ; passes garbage in sp_drive, and the drive number originally
+ ; passed in DL does not have 80h bit set.
+ or al,80h
+ cmp al,dl
+ je .found_drive0
+
+.still_broken: dec dx
+ cmp dl, 80h
+ jnb .test_loop
+
+ ; No spec packet anywhere. Some particularly pathetic
+ ; BIOSes apparently don't even implement function
+ ; 4B01h, so we can't query a spec packet no matter
+ ; what. If we got a drive number in DL, then try to
+ ; use it, and if it works, then well...
+ mov dl,[DriveNumber]
+ cmp dl,81h ; Should be 81-FF at least
+ jb fatal_error ; If not, it's hopeless
+
+ ; Write a warning to indicate we're on *very* thin ice now
+ mov si,nospec_msg
+ call writemsg
+ mov al,dl
+ call writehex2
+ call crlf
+ mov si,trysbm_msg
+ call writemsg
+ jmp .found_drive ; Pray that this works...
+
+fatal_error:
+ mov si,nothing_msg
+ call writemsg
+
+.norge: jmp short .norge
+
+ ; Information message (DS:SI) output
+ ; Prefix with "isolinux: "
+ ;
+writemsg: push ax
+ push si
+ mov si,isolinux_str
+ call writestr
+ pop si
+ call writestr
+ pop ax
+ ret
+
+;
+; Write a character to the screen. There is a more "sophisticated"
+; version of this in the subsequent code, so we patch the pointer
+; when appropriate.
+;
+
+writechr:
+ jmp near writechr_simple ; 3-byte jump
+
+writechr_simple:
+ pushfd
+ pushad
+ mov ah,0Eh
+ xor bx,bx
+ int 10h
+ popad
+ popfd
+ ret
+
+;
+; int13: save all the segment registers and call INT 13h
+; Some CD-ROM BIOSes have been found to corrupt segment registers.
+;
+int13:
+
+ push ds
+ push es
+ push fs
+ push gs
+ int 13h
+ pop gs
+ pop fs
+ pop es
+ pop ds
+ ret
+
+;
+; Get one sector. Convenience entry point.
+;
+getonesec:
+ mov bp,1
+ ; Fall through to getlinsec
+
+;
+; Get linear sectors - EBIOS LBA addressing, 2048-byte sectors.
+;
+; Note that we can't always do this as a single request, because at least
+; Phoenix BIOSes has a 127-sector limit. To be on the safe side, stick
+; to 32 sectors (64K) per request.
+;
+; Input:
+; EAX - Linear sector number
+; ES:BX - Target buffer
+; BP - Sector count
+;
+getlinsec:
+ mov si,dapa ; Load up the DAPA
+ mov [si+4],bx
+ mov bx,es
+ mov [si+6],bx
+ mov [si+8],eax
+.loop:
+ push bp ; Sectors left
+ cmp bp,[MaxTransfer]
+ jbe .bp_ok
+ mov bp,[MaxTransfer]
+.bp_ok:
+ mov [si+2],bp
+ push si
+ mov dl,[DriveNumber]
+ mov ah,42h ; Extended Read
+ call xint13
+ pop si
+ pop bp
+ movzx eax,word [si+2] ; Sectors we read
+ add [si+8],eax ; Advance sector pointer
+ sub bp,ax ; Sectors left
+ shl ax,SECTOR_SHIFT-4 ; 2048-byte sectors -> segment
+ add [si+6],ax ; Advance buffer pointer
+ and bp,bp
+ jnz .loop
+ mov eax,[si+8] ; Next sector
+ ret
+
+ ; INT 13h with retry
+xint13: mov byte [RetryCount],retry_count
+.try: pushad
+ call int13
+ jc .error
+ add sp,byte 8*4 ; Clean up stack
+ ret
+.error:
+ mov [DiskError],ah ; Save error code
+ popad
+ mov [DiskSys],ax ; Save system call number
+ dec byte [RetryCount]
+ jz .real_error
+ push ax
+ mov al,[RetryCount]
+ mov ah,[dapa+2] ; Sector transfer count
+ cmp al,2 ; Only 2 attempts left
+ ja .nodanger
+ mov ah,1 ; Drop transfer size to 1
+ jmp short .setsize
+.nodanger:
+ cmp al,retry_count-2
+ ja .again ; First time, just try again
+ shr ah,1 ; Otherwise, try to reduce
+ adc ah,0 ; the max transfer size, but not to 0
+.setsize:
+ mov [MaxTransfer],ah
+ mov [dapa+2],ah
+.again:
+ pop ax
+ jmp .try
+
+.real_error: mov si,diskerr_msg
+ call writemsg
+ mov al,[DiskError]
+ call writehex2
+ mov si,oncall_str
+ call writestr
+ mov ax,[DiskSys]
+ call writehex4
+ mov si,ondrive_str
+ call writestr
+ mov al,dl
+ call writehex2
+ call crlf
+ ; Fall through to kaboom
+
+;
+; kaboom: write a message and bail out. Wait for a user keypress,
+; then do a hard reboot.
+;
+kaboom:
+ RESET_STACK_AND_SEGS AX
+ mov si,err_bootfailed
+ call cwritestr
+ call getchar
+ cli
+ mov word [BIOS_magic],0 ; Cold reboot
+ jmp 0F000h:0FFF0h ; Reset vector address
+
+; -----------------------------------------------------------------------------
+; Common modules needed in the first sector
+; -----------------------------------------------------------------------------
+
+%include "writestr.inc" ; String output
+writestr equ cwritestr
+%include "writehex.inc" ; Hexadecimal output
+
+; -----------------------------------------------------------------------------
+; Data that needs to be in the first sector
+; -----------------------------------------------------------------------------
+
+syslinux_banner db CR, LF, 'ISOLINUX ', version_str, ' ', date, ' ', 0
+copyright_str db ' Copyright (C) 1994-', year, ' H. Peter Anvin'
+ db CR, LF, 0
+isolinux_str db 'isolinux: ', 0
+%ifdef DEBUG_MESSAGES
+startup_msg: db 'Starting up, DL = ', 0
+spec_ok_msg: db 'Loaded spec packet OK, drive = ', 0
+secsize_msg: db 'Sector size appears to be ', 0
+offset_msg: db 'Loading main image from LBA = ', 0
+size_msg: db 'Sectors to load = ', 0
+loaded_msg: db 'Loaded boot image, verifying...', CR, LF, 0
+verify_msg: db 'Image checksum verified.', CR, LF, 0
+allread_msg db 'Main image read, jumping to main code...', CR, LF, 0
+%endif
+noinfotable_msg db 'No boot info table, assuming single session disk...', CR, LF, 0
+noinfoinspec_msg db 'Spec packet missing LBA information, trying to wing it...', CR, LF, 0
+spec_err_msg: db 'Loading spec packet failed, trying to wing it...', CR, LF, 0
+maybe_msg: db 'Found something at drive = ', 0
+alright_msg: db 'Looks like it might be right, continuing...', CR, LF, 0
+nospec_msg db 'Extremely broken BIOS detected, last ditch attempt with drive = ', 0
+nothing_msg: db 'Failed to locate CD-ROM device; boot failed.', CR, LF
+trysbm_msg db 'See http://syslinux.zytor.com/sbm for more information.', CR, LF, 0
+diskerr_msg: db 'Disk error ', 0
+oncall_str: db ', AX = ',0
+ondrive_str: db ', drive ', 0
+checkerr_msg: db 'Image checksum error, sorry...', CR, LF, 0
+
+err_bootfailed db CR, LF, 'Boot failed: press a key to retry...'
+bailmsg equ err_bootfailed
+crlf_msg db CR, LF
+null_msg db 0
+
+ alignb 4, db 0
+MaxTransfer dw 32 ; Max sectors per transfer
+
+rl_checkpt equ $ ; Must be <= 800h
+
+rl_checkpt_off equ ($-$$)
+;%ifndef DEPEND
+;%if rl_checkpt_off > 0x800
+;%error "Sector 0 overflow"
+;%endif
+;%endif
+
+; ----------------------------------------------------------------------------
+; End of code and data that have to be in the first sector
+; ----------------------------------------------------------------------------
+
+all_read:
+
+; Test tracers
+ TRACER 'T'
+ TRACER '>'
+
+;
+; Common initialization code
+;
+%include "init.inc"
+%include "cpuinit.inc"
+
+ ; Patch the writechr routine to point to the full code
+ mov word [writechr+1], writechr_full-(writechr+3)
+
+; Tell the user we got this far...
+%ifndef DEBUG_MESSAGES ; Gets messy with debugging on
+ mov si,copyright_str
+ call writestr
+%endif
+
+;
+; Now we're all set to start with our *real* business. First load the
+; configuration file (if any) and parse it.
+;
+; In previous versions I avoided using 32-bit registers because of a
+; rumour some BIOSes clobbered the upper half of 32-bit registers at
+; random. I figure, though, that if there are any of those still left
+; they probably won't be trying to install Linux on them...
+;
+; The code is still ripe with 16-bitisms, though. Not worth the hassle
+; to take'm out. In fact, we may want to put them back if we're going
+; to boot ELKS at some point.
+;
+
+;
+; Now, we need to sniff out the actual filesystem data structures.
+; mkisofs gave us a pointer to the primary volume descriptor
+; (which will be at 16 only for a single-session disk!); from the PVD
+; we should be able to find the rest of what we need to know.
+;
+get_fs_structures:
+ mov eax,[bi_pvd]
+ mov bx,trackbuf
+ call getonesec
+
+ mov eax,[trackbuf+156+2]
+ mov [RootDir+dir_lba],eax
+ mov [CurDir+dir_lba],eax
+%ifdef DEBUG_MESSAGES
+ mov si,dbg_rootdir_msg
+ call writemsg
+ call writehex8
+ call crlf
+%endif
+ mov eax,[trackbuf+156+10]
+ mov [RootDir+dir_len],eax
+ mov [CurDir+dir_len],eax
+ add eax,SECTOR_SIZE-1
+ shr eax,SECTOR_SHIFT
+ mov [RootDir+dir_clust],eax
+ mov [CurDir+dir_clust],eax
+
+ ; Look for an isolinux directory, and if found,
+ ; make it the current directory instead of the root
+ ; directory.
+ mov di,boot_dir ; Search for /boot/isolinux
+ mov al,02h
+ call searchdir_iso
+ jnz .found_dir
+ mov di,isolinux_dir
+ mov al,02h ; Search for /isolinux
+ call searchdir_iso
+ jz .no_isolinux_dir
+.found_dir:
+ mov [CurDir+dir_len],eax
+ mov eax,[si+file_left]
+ mov [CurDir+dir_clust],eax
+ xor eax,eax ; Free this file pointer entry
+ xchg eax,[si+file_sector]
+ mov [CurDir+dir_lba],eax
+%ifdef DEBUG_MESSAGES
+ push si
+ mov si,dbg_isodir_msg
+ call writemsg
+ pop si
+ call writehex8
+ call crlf
+%endif
+.no_isolinux_dir:
+
+;
+; Locate the configuration file
+;
+load_config:
+%ifdef DEBUG_MESSAGES
+ mov si,dbg_config_msg
+ call writemsg
+%endif
+
+ mov si,config_name
+ mov di,ConfigName
+ call strcpy
+
+ mov di,ConfigName
+ call open
+ jz no_config_file ; Not found or empty
+
+%ifdef DEBUG_MESSAGES
+ mov si,dbg_configok_msg
+ call writemsg
+%endif
+
+;
+; Now we have the config file open. Parse the config file and
+; run the user interface.
+;
+%include "ui.inc"
+
+;
+; Enable disk emulation. The kind of disk we emulate is dependent on the
+; size of the file: 1200K, 1440K or 2880K floppy, otherwise harddisk.
+;
+is_disk_image:
+ TRACER CR
+ TRACER LF
+ TRACER 'D'
+ TRACER ':'
+
+ mov edx,eax ; File size
+ mov di,img_table
+ mov cx,img_table_count
+ mov eax,[si+file_sector] ; Starting LBA of file
+ mov [dsp_lba],eax ; Location of file
+ mov byte [dsp_drive], 0 ; 00h floppy, 80h hard disk
+.search_table:
+ TRACER 't'
+ mov eax,[di+4]
+ cmp edx,[di]
+ je .type_found
+ add di,8
+ loop .search_table
+
+ ; Hard disk image. Need to examine the partition table
+ ; in order to deduce the C/H/S geometry. Sigh.
+.hard_disk_image:
+ TRACER 'h'
+ cmp edx,512
+ jb .bad_image
+
+ mov bx,trackbuf
+ mov cx,1 ; Load 1 sector
+ call getfssec
+
+ cmp word [trackbuf+510],0aa55h ; Boot signature
+ jne .bad_image ; Image not bootable
+
+ mov cx,4 ; 4 partition entries
+ mov di,trackbuf+446 ; Start of partition table
+
+ xor ax,ax ; Highest sector(al) head(ah)
+
+.part_scan:
+ cmp byte [di+4], 0
+ jz .part_loop
+ lea si,[di+1]
+ call .hs_check
+ add si,byte 4
+ call .hs_check
+.part_loop:
+ add di,byte 16
+ loop .part_scan
+
+ push eax ; H/S
+ push edx ; File size
+ mov bl,ah
+ xor bh,bh
+ inc bx ; # of heads in BX
+ xor ah,ah ; # of sectors in AX
+ cwde ; EAX[31:16] <- 0
+ mul bx
+ shl eax,9 ; Convert to bytes
+ ; Now eax contains the number of bytes per cylinder
+ pop ebx ; File size
+ xor edx,edx
+ div ebx
+ and edx,edx
+ jz .no_remainder
+ inc eax ; Fractional cylinder...
+ ; Now (e)ax contains the number of cylinders
+.no_remainder: cmp eax,1024
+ jna .ok_cyl
+ mov ax,1024 ; Max possible #
+.ok_cyl: dec ax ; Convert to max cylinder no
+ pop ebx ; S(bl) H(bh)
+ shl ah,6
+ or bl,ah
+ xchg ax,bx
+ shl eax,16
+ mov ah,bl
+ mov al,4 ; Hard disk boot
+ mov byte [dsp_drive], 80h ; Drive 80h = hard disk
+
+.type_found:
+ TRACER 'T'
+ mov bl,[sp_media]
+ and bl,0F0h ; Copy controller info bits
+ or al,bl
+ mov [dsp_media],al ; Emulation type
+ shr eax,8
+ mov [dsp_chs],eax ; C/H/S geometry
+ mov ax,[sp_devspec] ; Copy device spec
+ mov [dsp_devspec],ax
+ mov al,[sp_controller] ; Copy controller index
+ mov [dsp_controller],al
+
+ TRACER 'V'
+ call vgaclearmode ; Reset video
+
+ mov ax,4C00h ; Enable emulation and boot
+ mov si,dspec_packet
+ mov dl,[DriveNumber]
+ lss sp,[InitStack]
+ TRACER 'X'
+
+ call int13
+
+ ; If this returns, we have problems
+.bad_image:
+ mov si,err_disk_image
+ call cwritestr
+ jmp enter_command
+
+;
+; Look for the highest seen H/S geometry
+; We compute cylinders separately
+;
+.hs_check:
+ mov bl,[si] ; Head #
+ cmp bl,ah
+ jna .done_track
+ mov ah,bl ; New highest head #
+.done_track: mov bl,[si+1]
+ and bl,3Fh ; Sector #
+ cmp bl,al
+ jna .done_sector
+ mov al,bl
+.done_sector: ret
+
+;
+; close_file:
+; Deallocates a file structure (pointer in SI)
+; Assumes CS == DS.
+;
+close_file:
+ and si,si
+ jz .closed
+ mov dword [si],0 ; First dword == file_left
+ xor si,si
+.closed: ret
+
+;
+; searchdir:
+;
+; Open a file
+;
+; On entry:
+; DS:DI = filename
+; If successful:
+; ZF clear
+; SI = file pointer
+; EAX = file length in bytes
+; If unsuccessful
+; ZF set
+;
+; Assumes CS == DS == ES, and trashes BX and CX.
+;
+; searchdir_iso is a special entry point for ISOLINUX only. In addition
+; to the above, searchdir_iso passes a file flag mask in AL. This is useful
+; for searching for directories.
+;
+alloc_failure:
+ xor ax,ax ; ZF <- 1
+ ret
+
+searchdir:
+ xor al,al
+searchdir_iso:
+ mov [ISOFlags],al
+ TRACER 'S'
+ call allocate_file ; Temporary file structure for directory
+ jnz alloc_failure
+ push es
+ push ds
+ pop es ; ES = DS
+ mov si,CurDir
+ cmp byte [di],'/' ; If filename begins with slash
+ jne .not_rooted
+ inc di ; Skip leading slash
+ mov si,RootDir ; Reference root directory instead
+.not_rooted:
+ mov eax,[si+dir_clust]
+ mov [bx+file_left],eax
+ mov eax,[si+dir_lba]
+ mov [bx+file_sector],eax
+ mov edx,[si+dir_len]
+
+.look_for_slash:
+ mov ax,di
+.scan:
+ mov cl,[di]
+ inc di
+ and cl,cl
+ jz .isfile
+ cmp cl,'/'
+ jne .scan
+ mov [di-1],byte 0 ; Terminate at directory name
+ mov cl,02h ; Search for directory
+ xchg cl,[ISOFlags]
+
+ push di ; Save these...
+ push cx
+
+ ; Create recursion stack frame...
+ push word .resume ; Where to "return" to
+ push es
+.isfile: xchg ax,di
+
+.getsome:
+ ; Get a chunk of the directory
+ ; This relies on the fact that ISOLINUX doesn't change SI
+ mov si,trackbuf
+ TRACER 'g'
+ pushad
+ xchg bx,si
+ mov cx,[BufSafe]
+ call getfssec
+ popad
+
+.compare:
+ movzx eax,byte [si] ; Length of directory entry
+ cmp al,33
+ jb .next_sector
+ TRACER 'c'
+ mov cl,[si+25]
+ xor cl,[ISOFlags]
+ test cl, byte 8Eh ; Unwanted file attributes!
+ jnz .not_file
+ pusha
+ movzx cx,byte [si+32] ; File identifier length
+ add si,byte 33 ; File identifier offset
+ TRACER 'i'
+ call iso_compare_names
+ popa
+ je .success
+.not_file:
+ sub edx,eax ; Decrease bytes left
+ jbe .failure
+ add si,ax ; Advance pointer
+
+.check_overrun:
+ ; Did we finish the buffer?
+ cmp si,trackbuf+trackbufsize
+ jb .compare ; No, keep going
+
+ jmp short .getsome ; Get some more directory
+
+.next_sector:
+ ; Advance to the beginning of next sector
+ lea ax,[si+SECTOR_SIZE-1]
+ and ax,~(SECTOR_SIZE-1)
+ sub ax,si
+ jmp short .not_file ; We still need to do length checks
+
+.failure: xor eax,eax ; ZF = 1
+ mov [bx+file_sector],eax
+ pop es
+ ret
+
+.success:
+ mov eax,[si+2] ; Location of extent
+ mov [bx+file_sector],eax
+ mov eax,[si+10] ; Data length
+ mov [bx+file_bytesleft],eax
+ push eax
+ add eax,SECTOR_SIZE-1
+ shr eax,SECTOR_SHIFT
+ mov [bx+file_left],eax
+ pop eax
+ and bx,bx ; ZF = 0
+ mov si,bx
+ pop es
+ ret
+
+.resume: ; We get here if we were only doing part of a lookup
+ ; This relies on the fact that .success returns bx == si
+ xchg edx,eax ; Directory length in edx
+ pop cx ; Old ISOFlags
+ pop di ; Next filename pointer
+ mov byte [di-1], '/' ; Restore slash
+ mov [ISOFlags],cl ; Restore the flags
+ jz .failure ; Did we fail? If so fail for real!
+ jmp .look_for_slash ; Otherwise, next level
+
+;
+; allocate_file: Allocate a file structure
+;
+; If successful:
+; ZF set
+; BX = file pointer
+; In unsuccessful:
+; ZF clear
+;
+allocate_file:
+ TRACER 'a'
+ push cx
+ mov bx,Files
+ mov cx,MAX_OPEN
+.check: cmp dword [bx], byte 0
+ je .found
+ add bx,open_file_t_size ; ZF = 0
+ loop .check
+ ; ZF = 0 if we fell out of the loop
+.found: pop cx
+ ret
+
+;
+; iso_compare_names:
+; Compare the names DS:SI and DS:DI and report if they are
+; equal from an ISO 9660 perspective. SI is the name from
+; the filesystem; CX indicates its length, and ';' terminates.
+; DI is expected to end with a null.
+;
+; Note: clobbers AX, CX, SI, DI; assumes DS == ES == base segment
+;
+
+iso_compare_names:
+ ; First, terminate and canonicalize input filename
+ push di
+ mov di,ISOFileName
+.canon_loop: jcxz .canon_end
+ lodsb
+ dec cx
+ cmp al,';'
+ je .canon_end
+ and al,al
+ je .canon_end
+ stosb
+ cmp di,ISOFileNameEnd-1 ; Guard against buffer overrun
+ jb .canon_loop
+.canon_end:
+ cmp di,ISOFileName
+ jbe .canon_done
+ cmp byte [di-1],'.' ; Remove terminal dots
+ jne .canon_done
+ dec di
+ jmp short .canon_end
+.canon_done:
+ mov [di],byte 0 ; Null-terminate string
+ pop di
+ mov si,ISOFileName
+.compare:
+ lodsb
+ mov ah,[di]
+ inc di
+ and ax,ax
+ jz .success ; End of string for both
+ and al,al ; Is either one end of string?
+ jz .failure ; If so, failure
+ and ah,ah
+ jz .failure
+ or ax,2020h ; Convert to lower case
+ cmp al,ah
+ je .compare
+.failure: and ax,ax ; ZF = 0 (at least one will be nonzero)
+.success: ret
+
+;
+; mangle_name: Mangle a filename pointed to by DS:SI into a buffer pointed
+; to by ES:DI; ends on encountering any whitespace.
+; DI is preserved.
+;
+; This verifies that a filename is < FILENAME_MAX characters,
+; doesn't contain whitespace, zero-pads the output buffer,
+; and removes trailing dots and redundant slashes,
+; so "repe cmpsb" can do a compare, and the
+; path-searching routine gets a bit of an easier job.
+;
+mangle_name:
+ push di
+ push bx
+ xor ax,ax
+ mov cx,FILENAME_MAX-1
+ mov bx,di
+
+.mn_loop:
+ lodsb
+ cmp al,' ' ; If control or space, end
+ jna .mn_end
+ cmp al,ah ; Repeated slash?
+ je .mn_skip
+ xor ah,ah
+ cmp al,'/'
+ jne .mn_ok
+ mov ah,al
+.mn_ok stosb
+.mn_skip: loop .mn_loop
+.mn_end:
+ cmp bx,di ; At the beginning of the buffer?
+ jbe .mn_zero
+ cmp byte [es:di-1],'.' ; Terminal dot?
+ je .mn_kill
+ cmp byte [es:di-1],'/' ; Terminal slash?
+ jne .mn_zero
+.mn_kill: dec di ; If so, remove it
+ inc cx
+ jmp short .mn_end
+.mn_zero:
+ inc cx ; At least one null byte
+ xor ax,ax ; Zero-fill name
+ rep stosb
+ pop bx
+ pop di
+ ret ; Done
+
+;
+; unmangle_name: Does the opposite of mangle_name; converts a DOS-mangled
+; filename to the conventional representation. This is needed
+; for the BOOT_IMAGE= parameter for the kernel.
+;
+; DS:SI -> input mangled file name
+; ES:DI -> output buffer
+;
+; On return, DI points to the first byte after the output name,
+; which is set to a null byte.
+;
+unmangle_name: call strcpy
+ dec di ; Point to final null byte
+ ret
+
+;
+; getfssec: Get multiple clusters from a file, given the file pointer.
+;
+; On entry:
+; ES:BX -> Buffer
+; SI -> File pointer
+; CX -> Cluster count
+; On exit:
+; SI -> File pointer (or 0 on EOF)
+; CF = 1 -> Hit EOF
+; ECX -> Bytes actually read
+;
+getfssec:
+ TRACER 'F'
+
+ push ds
+ push cs
+ pop ds ; DS <- CS
+
+ movzx ecx,cx
+ cmp ecx,[si+file_left]
+ jna .ok_size
+ mov ecx,[si+file_left]
+
+.ok_size:
+ mov bp,cx
+ push cx
+ push si
+ mov eax,[si+file_sector]
+ TRACER 'l'
+ call getlinsec
+ xor ecx,ecx
+ pop si
+ pop cx
+
+ add [si+file_sector],ecx
+ sub [si+file_left],ecx
+ ja .not_eof ; CF = 0
+ stc
+
+.not_eof:
+ pushf
+ shl ecx,SECTOR_SHIFT ; Convert to bytes
+ cmp ecx,[si+file_bytesleft]
+ jb .not_all
+ mov ecx,[si+file_bytesleft]
+.not_all: sub [si+file_bytesleft],ecx
+ popf
+ jnc .ret
+ push eax
+ xor eax,eax
+ mov [si+file_sector],eax ; Unused
+ mov si,ax
+ pop eax
+ stc
+.ret:
+ pop ds
+ TRACER 'f'
+ ret
+
+; -----------------------------------------------------------------------------
+; Common modules
+; -----------------------------------------------------------------------------
+
+%include "getc.inc" ; getc et al
+%include "conio.inc" ; Console I/O
+%include "configinit.inc" ; Initialize configuration
+%include "parseconfig.inc" ; High-level config file handling
+%include "parsecmd.inc" ; Low-level config file handling
+%include "bcopy32.inc" ; 32-bit bcopy
+%include "loadhigh.inc" ; Load a file into high memory
+%include "font.inc" ; VGA font stuff
+%include "graphics.inc" ; VGA graphics
+%include "highmem.inc" ; High memory sizing
+%include "strcpy.inc" ; strcpy()
+%include "rawcon.inc" ; Console I/O w/o using the console functions
+%include "adv.inc" ; Auxillary Data Vector
+%include "localboot.inc" ; Disk-based local boot
+
+; -----------------------------------------------------------------------------
+; Begin data section
+; -----------------------------------------------------------------------------
+
+ section .data
+
+default_str db 'default', 0
+default_len equ ($-default_str)
+boot_dir db '/boot' ; /boot/isolinux
+isolinux_dir db '/isolinux', 0
+config_name db 'isolinux.cfg', 0
+err_disk_image db 'Cannot load disk image (invalid file)?', CR, LF, 0
+
+%ifdef DEBUG_MESSAGES
+dbg_rootdir_msg db 'Root directory at LBA = ', 0
+dbg_isodir_msg db 'isolinux directory at LBA = ', 0
+dbg_config_msg db 'About to load config file...', CR, LF, 0
+dbg_configok_msg db 'Configuration file opened...', CR, LF, 0
+%endif
+;
+; Command line options we'd like to take a look at
+;
+; mem= and vga= are handled as normal 32-bit integer values
+initrd_cmd db 'initrd='
+initrd_cmd_len equ 7
+
+;
+; Config file keyword table
+;
+%include "keywords.inc"
+
+;
+; Extensions to search for (in *forward* order).
+;
+ align 4, db 0
+exten_table: db '.cbt' ; COMBOOT (specific)
+ db '.img' ; Disk image
+ db '.bin' ; CD boot sector
+ db '.com' ; COMBOOT (same as DOS)
+ db '.c32' ; COM32
+exten_table_end:
+ dd 0, 0 ; Need 8 null bytes here
+
+;
+; Floppy image table
+;
+ align 4, db 0
+img_table_count equ 3
+img_table:
+ dd 1200*1024 ; 1200K floppy
+ db 1 ; Emulation type
+ db 80-1 ; Max cylinder
+ db 15 ; Max sector
+ db 2-1 ; Max head
+
+ dd 1440*1024 ; 1440K floppy
+ db 2 ; Emulation type
+ db 80-1 ; Max cylinder
+ db 18 ; Max sector
+ db 2-1 ; Max head
+
+ dd 2880*1024 ; 2880K floppy
+ db 3 ; Emulation type
+ db 80-1 ; Max cylinder
+ db 36 ; Max sector
+ db 2-1 ; Max head
+
+;
+; Misc initialized (data) variables
+;
+
+;
+; Variables that are uninitialized in SYSLINUX but initialized here
+;
+; **** ISOLINUX:: We may have to make this flexible, based on what the
+; **** BIOS expects our "sector size" to be.
+;
+ alignb 4, db 0
+BufSafe dw trackbufsize/SECTOR_SIZE ; Clusters we can load into trackbuf
+BufSafeBytes dw trackbufsize ; = how many bytes?
+%ifndef DEPEND
+%if ( trackbufsize % SECTOR_SIZE ) != 0
+%error trackbufsize must be a multiple of SECTOR_SIZE
+%endif
+%endif
diff --git a/core/kernel.inc b/core/kernel.inc
new file mode 100644
index 00000000..0da03638
--- /dev/null
+++ b/core/kernel.inc
@@ -0,0 +1,112 @@
+;; -----------------------------------------------------------------------
+;;
+;; Copyright 1994-2008 H. Peter Anvin - All Rights Reserved
+;;
+;; This program is free software; you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation, Inc., 53 Temple Place Ste 330,
+;; Boston MA 02111-1307, USA; either version 2 of the License, or
+;; (at your option) any later version; incorporated herein by reference.
+;;
+;; -----------------------------------------------------------------------
+
+;;
+;; kernel.inc
+;;
+;; Header file for the kernel interface definitions
+;;
+
+%ifndef _KERNEL_INC
+%define _KERNEL_INC
+
+;;
+;; Structure of the real_mode_seg
+;;
+
+ struc real_mode_seg_t
+ resb 20h-($-$$) ; org 20h
+kern_cmd_magic resw 1 ; 0020 Magic # for command line
+kern_cmd_offset resw 1 ; 0022 Offset for kernel command line
+ resb 497-($-$$) ; org 497d
+bs_setupsecs resb 1 ; 01F1 Sectors for setup code (0 -> 4)
+bs_rootflags resw 1 ; 01F2 Root readonly flag
+bs_syssize resw 1 ; 01F4
+bs_swapdev resw 1 ; 01F6 Swap device (obsolete)
+bs_ramsize resw 1 ; 01F8 Ramdisk flags, formerly ramdisk size
+bs_vidmode resw 1 ; 01FA Video mode
+bs_rootdev resw 1 ; 01FC Root device
+bs_bootsign resw 1 ; 01FE Boot sector signature (0AA55h)
+su_jump resb 1 ; 0200 0EBh
+su_jump2 resb 1 ; 0201 Size of following header
+su_header resd 1 ; 0202 New setup code: header
+su_version resw 1 ; 0206 See linux/arch/i386/boot/setup.S
+su_switch resw 1 ; 0208
+su_setupseg resw 1 ; 020A
+su_startsys resw 1 ; 020C
+su_kver resw 1 ; 020E Kernel version pointer
+su_loader resb 1 ; 0210 Loader ID
+su_loadflags resb 1 ; 0211 Load high flag
+su_movesize resw 1 ; 0212
+su_code32start resd 1 ; 0214 Start of code loaded high
+su_ramdiskat resd 1 ; 0218 Start of initial ramdisk
+su_ramdisklen resd 1 ; 021C Length of initial ramdisk
+su_bsklugeoffs resw 1 ; 0220
+su_bsklugeseg resw 1 ; 0222
+su_heapend resw 1 ; 0224
+su_pad1 resw 1 ; 0226
+su_cmd_line_ptr resd 1 ; 0228
+su_ramdisk_max resd 1 ; 022C
+ resb (0f800h-12)-($-$$)
+linux_stack equ $ ; F7F4
+linux_fdctab resb 12
+cmd_line_here equ $ ; F800 Should be out of the way
+ endstruc
+
+;
+; Old kernel command line signature
+;
+CMD_MAGIC equ 0A33Fh ; Command line magic
+
+;
+; If we're loading the command line old-style, we need a smaller
+; heap.
+;
+old_cmd_line_here equ 9800h
+old_max_cmd_len equ 2047
+old_linux_fdctab equ old_cmd_line_here-12
+old_linux_stack equ old_linux_fdctab
+
+;
+; Magic number of su_header field
+;
+HEADER_ID equ 'HdrS' ; HdrS (in littleendian hex)
+
+;
+; Flags for the su_loadflags field
+;
+LOAD_HIGH equ 01h ; Large kernel, load high
+CAN_USE_HEAP equ 80h ; Boot loader reports heap size
+
+;
+; ID codes for various modules
+;
+syslinux_id equ 031h ; 3 = SYSLINUX family; 1 = SYSLINUX
+pxelinux_id equ 032h ; 3 = SYSLINUX family; 2 = PXELINUX
+isolinux_id equ 033h ; 3 = SYSLINUX family; 3 = ISOLINUX
+extlinux_id equ 034h ; 3 = SYSLINUX family; 4 = EXTLINUX
+
+;
+; Types of vkernels
+;
+VK_KERNEL equ 0 ; Choose by filename
+VK_LINUX equ 1 ; Linux kernel image
+VK_BOOT equ 2 ; Boot sector
+VK_BSS equ 3 ; BSS boot sector
+VK_PXE equ 4 ; PXE NBP
+VK_FDIMAGE equ 5 ; Floppy disk image
+VK_COMBOOT equ 6 ; COMBOOT image
+VK_COM32 equ 7 ; COM32 image
+VK_CONFIG equ 8 ; Configuration file
+VK_TYPES equ 9 ; Number of VK types
+
+%endif ; _KERNEL_INC
diff --git a/core/keywords b/core/keywords
new file mode 100644
index 00000000..d7d8fa65
--- /dev/null
+++ b/core/keywords
@@ -0,0 +1,44 @@
+menu
+text
+include
+append
+config
+default
+display
+font
+implicit
+ipappend
+kbdmap
+kernel
+linux
+boot
+bss
+pxe
+fdimage
+comboot
+com32
+label
+localboot
+prompt
+say
+serial
+console
+timeout
+totaltimeout
+allowoptions
+ontimeout
+onerror
+noescape
+f0
+f1
+f2
+f3
+f4
+f5
+f6
+f7
+f8
+f9
+f10
+f11
+f12
diff --git a/core/keywords.inc b/core/keywords.inc
new file mode 100644
index 00000000..b6a701bb
--- /dev/null
+++ b/core/keywords.inc
@@ -0,0 +1,97 @@
+;; -----------------------------------------------------------------------
+;;
+;; Copyright 1994-2008 H. Peter Anvin - All Rights Reserved
+;;
+;; This program is free software; you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation, Inc., 53 Temple Place Ste 330,
+;; Boston MA 02111-1307, USA; either version 2 of the License, or
+;; (at your option) any later version; incorporated herein by reference.
+;;
+;; -----------------------------------------------------------------------
+
+;;
+;; keywords.inc
+;;
+;; Common header file for the handling of keyword hash and macros
+;;
+
+%ifndef DEPEND ; Generated file
+%include "kwdhash.gen"
+%endif
+
+%macro keyword 2
+ dd hash_%1 ; Hash value
+ dw 0 ; No argument
+ dw %2 ; Entrypoint
+%endmacro
+
+%macro keyword 3
+ dd hash_%1 ; Hash value
+ dw %3 ; 16-bit argument
+ dw %2 ; Entrypoint
+%endmacro
+
+%macro keyword 4
+ dd hash_%1 ; Hash value
+ db %3, %4 ; 2 8-bit arguments
+ dw %2 ; Entrypoint
+%endmacro
+
+keywd_size equ 8 ; Bytes per keyword
+
+ align 4, db 0
+
+%define FKeyN(n) (FKeyName+(((n)-1) << FILENAME_MAX_LG2))
+
+keywd_table:
+ keyword menu, pc_comment
+ keyword text, pc_text
+ keyword include, pc_opencmd, pc_include
+ keyword append, pc_append
+ keyword default, pc_default
+ keyword display, pc_opencmd, get_msg_file
+ keyword font, pc_filecmd, loadfont
+ keyword implicit, pc_setint16, AllowImplicit
+ keyword kbdmap, pc_filecmd, loadkeys
+ keyword kernel, pc_kernel, VK_KERNEL
+ keyword linux, pc_kernel, VK_LINUX
+ keyword boot, pc_kernel, VK_BOOT
+ keyword bss, pc_kernel, VK_BSS
+ keyword pxe, pc_kernel, VK_PXE
+ keyword fdimage, pc_kernel, VK_FDIMAGE
+ keyword comboot, pc_kernel, VK_COMBOOT
+ keyword com32, pc_kernel, VK_COM32
+ keyword config, pc_kernel, VK_CONFIG
+ keyword label, pc_label
+ keyword prompt, pc_setint16, ForcePrompt
+ keyword say, pc_say
+ keyword serial, pc_serial
+ keyword console, pc_setint16, DisplayCon
+ keyword timeout, pc_timeout, KbdTimeout
+ keyword totaltimeout, pc_timeout, TotalTimeout
+ keyword ontimeout, pc_ontimeout
+ keyword onerror, pc_onerror
+ keyword allowoptions, pc_setint16, AllowOptions
+ keyword noescape, pc_setint16, NoEscape
+ keyword f1, pc_fkey, FKeyN(1)
+ keyword f2, pc_fkey, FKeyN(2)
+ keyword f3, pc_fkey, FKeyN(3)
+ keyword f4, pc_fkey, FKeyN(4)
+ keyword f5, pc_fkey, FKeyN(5)
+ keyword f6, pc_fkey, FKeyN(6)
+ keyword f7, pc_fkey, FKeyN(7)
+ keyword f8, pc_fkey, FKeyN(8)
+ keyword f9, pc_fkey, FKeyN(9)
+ keyword f10, pc_fkey, FKeyN(10)
+ keyword f0, pc_fkey, FKeyN(10)
+ keyword f11, pc_fkey, FKeyN(11)
+ keyword f12, pc_fkey, FKeyN(12)
+%if IS_PXELINUX
+ keyword ipappend, pc_ipappend
+%endif
+%if HAS_LOCALBOOT
+ keyword localboot, pc_localboot
+%endif
+
+keywd_count equ ($-keywd_table)/keywd_size
diff --git a/core/layout.inc b/core/layout.inc
new file mode 100644
index 00000000..51460e14
--- /dev/null
+++ b/core/layout.inc
@@ -0,0 +1,92 @@
+; -----------------------------------------------------------------------
+;
+; Copyright 1994-2008 H. Peter Anvin - All Rights Reserved
+;
+; This program is free software; you can redistribute it and/or modify
+; it under the terms of the GNU General Public License as published by
+; the Free Software Foundation, Inc., 53 Temple Place Ste 330,
+; Bostom MA 02111-1307, USA; either version 2 of the License, or
+; (at your option) any later version; incorporated herein by reference.
+;
+; -----------------------------------------------------------------------
+
+;
+; layout.inc
+;
+; Memory layout of segments
+;
+
+ ; Default to 16-bit code
+ bits 16
+
+; Memory below 0800h is reserved for the BIOS and the MBR.
+BSS_START equ 0800h
+
+; Text starts at the load address of 07C00h.
+TEXT_START equ 7C00h
+
+; The secondary BSS section, above the text; we really wish we could
+; just make it follow .bcopy32 or hang off the end,
+; but it doesn't seem to work that way.
+LATEBSS_START equ 0B800h
+
+%ifdef MAP
+ [map all MAP]
+%endif
+
+;
+; The various sections and their relationship
+;
+ ; Use .earlybss for things that MUST be in low memory.
+ section .earlybss nobits start=BSS_START
+ section .bcopy32 exec nowrite progbits align=4
+ section .config write progbits align=4
+ section .config.end write nobits align=4
+
+ ; Use .bss for things that doesn't have to be in low memory;
+ ; with .bss1 and .bss2 to offload. .earlybss should be used
+ ; for things that absolutely have to be below 0x7c00.
+ section .bss write nobits align=16
+
+ ; Warning here: RBFG build 22 randomly overwrites
+ ; memory location [0x5680,0x576c), possibly more. It
+ ; seems that it gets confused and screws up the
+ ; pointer to its own internal packet buffer and starts
+ ; writing a received ARP packet into low memory.
+%if IS_PXELINUX
+ section .rbfg write nobits
+RBFG_brainfuck: resb 2048 ; Bigger than an Ethernet packet...
+%endif
+
+ ; For section following .rbfg
+%if IS_PXELINUX
+ section .bss2 write nobits align=16
+%else
+ section .bss2 write nobits align=16
+%endif
+
+ section .text exec write progbits align=16
+ section .data write progbits align=16
+
+ section .adv write nobits align=512
+
+ ; .uibss contains bss data which is guaranteed to be
+ ; safe to clobber during the loading of the image. This
+ ; is because while loading the primary image we will clobber
+ ; the spillover from the last fractional sector load.
+ section .uibss write nobits align=16
+
+ ; Normal bss...
+ section .bss1 write nobits align=16
+
+ ; Symbols from linker script
+%macro SECINFO 1
+ extern __%1_start, __%1_lma, __%1_end
+ extern __%1_len, __%1_dwords
+%endmacro
+ SECINFO bcopy32
+ SECINFO config
+
+ global _start
+
+ section .text
diff --git a/core/ldlinux.asm b/core/ldlinux.asm
new file mode 100644
index 00000000..86de4588
--- /dev/null
+++ b/core/ldlinux.asm
@@ -0,0 +1,1605 @@
+; -*- fundamental -*- (asm-mode sucks)
+; ****************************************************************************
+;
+; ldlinux.asm
+;
+; A program to boot Linux kernels off an MS-DOS formatted floppy disk. This
+; functionality is good to have for installation floppies, where it may
+; be hard to find a functional Linux system to run LILO off.
+;
+; This program allows manipulation of the disk to take place entirely
+; from MS-LOSS, and can be especially useful in conjunction with the
+; umsdos filesystem.
+;
+; Copyright 1994-2008 H. Peter Anvin - All Rights Reserved
+;
+; This program is free software; you can redistribute it and/or modify
+; it under the terms of the GNU General Public License as published by
+; the Free Software Foundation, Inc., 53 Temple Place Ste 330,
+; Boston MA 02111-1307, USA; either version 2 of the License, or
+; (at your option) any later version; incorporated herein by reference.
+;
+; ****************************************************************************
+
+%ifndef IS_MDSLINUX
+%define IS_SYSLINUX 1
+%endif
+%include "head.inc"
+
+;
+; Some semi-configurable constants... change on your own risk.
+;
+my_id equ syslinux_id
+FILENAME_MAX_LG2 equ 6 ; log2(Max filename size Including final null)
+FILENAME_MAX equ (1<<FILENAME_MAX_LG2) ; Max mangled filename size
+NULLFILE equ 0 ; First char space == null filename
+NULLOFFSET equ 0 ; Position in which to look
+retry_count equ 16 ; How patient are we with the disk?
+%assign HIGHMEM_SLOP 0 ; Avoid this much memory near the top
+LDLINUX_MAGIC equ 0x3eb202fe ; A random number to identify ourselves with
+
+MAX_OPEN_LG2 equ 6 ; log2(Max number of open files)
+MAX_OPEN equ (1 << MAX_OPEN_LG2)
+
+SECTOR_SHIFT equ 9
+SECTOR_SIZE equ (1 << SECTOR_SHIFT)
+
+;
+; This is what we need to do when idle
+;
+%macro RESET_IDLE 0
+ ; Nothing
+%endmacro
+%macro DO_IDLE 0
+ ; Nothing
+%endmacro
+
+;
+; The following structure is used for "virtual kernels"; i.e. LILO-style
+; option labels. The options we permit here are `kernel' and `append
+; Since there is no room in the bottom 64K for all of these, we
+; stick them in high memory and copy them down before we need them.
+;
+ struc vkernel
+vk_vname: resb FILENAME_MAX ; Virtual name **MUST BE FIRST!**
+vk_rname: resb FILENAME_MAX ; Real name
+vk_appendlen: resw 1
+vk_type: resb 1 ; Type of file
+ alignb 4
+vk_append: resb max_cmd_len+1 ; Command line
+ alignb 4
+vk_end: equ $ ; Should be <= vk_size
+ endstruc
+
+;
+; Segment assignments in the bottom 640K
+; Stick to the low 512K in case we're using something like M-systems flash
+; which load a driver into low RAM (evil!!)
+;
+; 0000h - main code/data segment (and BIOS segment)
+;
+real_mode_seg equ 3000h
+cache_seg equ 2000h ; 64K area for metadata cache
+xfer_buf_seg equ 1000h ; Bounce buffer for I/O to high mem
+comboot_seg equ real_mode_seg ; COMBOOT image loading zone
+
+;
+; File structure. This holds the information for each currently open file.
+;
+ struc open_file_t
+file_sector resd 1 ; Sector pointer (0 = structure free)
+file_bytesleft resd 1 ; Number of bytes left
+file_left resd 1 ; Number of sectors left
+ resd 1 ; Unused
+ endstruc
+
+%ifndef DEPEND
+%if (open_file_t_size & (open_file_t_size-1))
+%error "open_file_t is not a power of 2"
+%endif
+%endif
+
+; ---------------------------------------------------------------------------
+; BEGIN CODE
+; ---------------------------------------------------------------------------
+
+;
+; Memory below this point is reserved for the BIOS and the MBR
+;
+ section .earlybss
+trackbufsize equ 8192
+trackbuf resb trackbufsize ; Track buffer goes here
+ ; ends at 2800h
+
+ section .bss
+ alignb 8
+
+ ; Expanded superblock
+SuperInfo equ $
+ resq 16 ; The first 16 bytes expanded 8 times
+FAT resd 1 ; Location of (first) FAT
+RootDirArea resd 1 ; Location of root directory area
+RootDir resd 1 ; Location of root directory proper
+DataArea resd 1 ; Location of data area
+RootDirSize resd 1 ; Root dir size in sectors
+TotalSectors resd 1 ; Total number of sectors
+ClustSize resd 1 ; Bytes/cluster
+ClustMask resd 1 ; Sectors/cluster - 1
+CopySuper resb 1 ; Distinguish .bs versus .bss
+DriveNumber resb 1 ; BIOS drive number
+ClustShift resb 1 ; Shift count for sectors/cluster
+ClustByteShift resb 1 ; Shift count for bytes/cluster
+
+ alignb open_file_t_size
+Files resb MAX_OPEN*open_file_t_size
+
+ section .text
+;
+; Some of the things that have to be saved very early are saved
+; "close" to the initial stack pointer offset, in order to
+; reduce the code size...
+;
+StackBuf equ $-44-32 ; Start the stack here (grow down - 4K)
+PartInfo equ StackBuf ; Saved partition table entry
+FloppyTable equ PartInfo+16 ; Floppy info table (must follow PartInfo)
+OrigFDCTabPtr equ StackBuf-8 ; The 2nd high dword on the stack
+OrigESDI equ StackBuf-4 ; The high dword on the stack
+
+;
+; Primary entry point. Tempting as though it may be, we can't put the
+; initial "cli" here; the jmp opcode in the first byte is part of the
+; "magic number" (using the term very loosely) for the DOS superblock.
+;
+bootsec equ $
+_start: jmp short start ; 2 bytes
+ nop ; 1 byte
+;
+; "Superblock" follows -- it's in the boot sector, so it's already
+; loaded and ready for us
+;
+bsOemName db 'SYSLINUX' ; The SYS command sets this, so...
+;
+; These are the fields we actually care about. We end up expanding them
+; all to dword size early in the code, so generate labels for both
+; the expanded and unexpanded versions.
+;
+%macro superb 1
+bx %+ %1 equ SuperInfo+($-superblock)*8+4
+bs %+ %1 equ $
+ zb 1
+%endmacro
+%macro superw 1
+bx %+ %1 equ SuperInfo+($-superblock)*8
+bs %+ %1 equ $
+ zw 1
+%endmacro
+%macro superd 1
+bx %+ %1 equ $ ; no expansion for dwords
+bs %+ %1 equ $
+ zd 1
+%endmacro
+superblock equ $
+ superw BytesPerSec
+ superb SecPerClust
+ superw ResSectors
+ superb FATs
+ superw RootDirEnts
+ superw Sectors
+ superb Media
+ superw FATsecs
+ superw SecPerTrack
+ superw Heads
+superinfo_size equ ($-superblock)-1 ; How much to expand
+ superd Hidden
+ superd HugeSectors
+ ;
+ ; This is as far as FAT12/16 and FAT32 are consistent
+ ;
+ zb 54 ; FAT12/16 need 26 more bytes,
+ ; FAT32 need 54 more bytes
+superblock_len equ $-superblock
+
+SecPerClust equ bxSecPerClust
+;
+; Note we don't check the constraints above now; we did that at install
+; time (we hope!)
+;
+start:
+ cli ; No interrupts yet, please
+ cld ; Copy upwards
+;
+; Set up the stack
+;
+ xor ax,ax
+ mov ss,ax
+ mov sp,StackBuf ; Just below BSS
+ push es ; Save initial ES:DI -> $PnP pointer
+ push di
+ mov es,ax
+;
+; DS:SI may contain a partition table entry. Preserve it for us.
+;
+ mov cx,8 ; Save partition info
+ mov di,PartInfo
+ rep movsw
+
+ mov ds,ax ; Now we can initialize DS...
+
+;
+; Now sautee the BIOS floppy info block to that it will support decent-
+; size transfers; the floppy block is 11 bytes and is stored in the
+; INT 1Eh vector (brilliant waste of resources, eh?)
+;
+; Of course, if BIOSes had been properly programmed, we wouldn't have
+; had to waste precious space with this code.
+;
+ mov bx,fdctab
+ lfs si,[bx] ; FS:SI -> original fdctab
+ push fs ; Save on stack in case we need to bail
+ push si
+
+ ; Save the old fdctab even if hard disk so the stack layout
+ ; is the same. The instructions above do not change the flags
+ mov [DriveNumber],dl ; Save drive number in DL
+ and dl,dl ; If floppy disk (00-7F), assume no
+ ; partition table
+ js harddisk
+
+floppy:
+ mov cl,6 ; 12 bytes (CX == 0)
+ ; es:di -> FloppyTable already
+ ; This should be safe to do now, interrupts are off...
+ mov [bx],di ; FloppyTable
+ mov [bx+2],ax ; Segment 0
+ fs rep movsw ; Faster to move words
+ mov cl,[bsSecPerTrack] ; Patch the sector count
+ mov [di-8],cl
+ ; AX == 0 here
+ int 13h ; Some BIOSes need this
+
+ jmp short not_harddisk
+;
+; The drive number and possibly partition information was passed to us
+; by the BIOS or previous boot loader (MBR). Current "best practice" is to
+; trust that rather than what the superblock contains.
+;
+; Would it be better to zero out bsHidden if we don't have a partition table?
+;
+; Note: di points to beyond the end of PartInfo
+;
+harddisk:
+ test byte [di-16],7Fh ; Sanity check: "active flag" should
+ jnz no_partition ; be 00 or 80
+ mov eax,[di-8] ; Partition offset (dword)
+ mov [bsHidden],eax
+no_partition:
+;
+; Get disk drive parameters (don't trust the superblock.) Don't do this for
+; floppy drives -- INT 13:08 on floppy drives will (may?) return info about
+; what the *drive* supports, not about the *media*. Fortunately floppy disks
+; tend to have a fixed, well-defined geometry which is stored in the superblock.
+;
+ ; DL == drive # still
+ mov ah,08h
+ int 13h
+ jc no_driveparm
+ and ah,ah
+ jnz no_driveparm
+ shr dx,8
+ inc dx ; Contains # of heads - 1
+ mov [bsHeads],dx
+ and cx,3fh
+ mov [bsSecPerTrack],cx
+no_driveparm:
+not_harddisk:
+;
+; Ready to enable interrupts, captain
+;
+ sti
+
+;
+; Do we have EBIOS (EDD)?
+;
+eddcheck:
+ mov bx,55AAh
+ mov ah,41h ; EDD existence query
+ mov dl,[DriveNumber]
+ int 13h
+ jc .noedd
+ cmp bx,0AA55h
+ jne .noedd
+ test cl,1 ; Extended disk access functionality set
+ jz .noedd
+ ;
+ ; We have EDD support...
+ ;
+ mov byte [getlinsec.jmp+1],(getlinsec_ebios-(getlinsec.jmp+2))
+.noedd:
+
+;
+; Load the first sector of LDLINUX.SYS; this used to be all proper
+; with parsing the superblock and root directory; it doesn't fit
+; together with EBIOS support, unfortunately.
+;
+ mov eax,[FirstSector] ; Sector start
+ mov bx,ldlinux_sys ; Where to load it
+ call getonesec
+
+ ; Some modicum of integrity checking
+ cmp dword [ldlinux_magic+4],LDLINUX_MAGIC^HEXDATE
+ jne kaboom
+
+ ; Go for it...
+ jmp ldlinux_ent
+
+;
+; getonesec: get one disk sector
+;
+getonesec:
+ mov bp,1 ; One sector
+ ; Fall through
+
+;
+; getlinsec: load a sequence of BP floppy sector given by the linear sector
+; number in EAX into the buffer at ES:BX. We try to optimize
+; by loading up to a whole track at a time, but the user
+; is responsible for not crossing a 64K boundary.
+; (Yes, BP is weird for a count, but it was available...)
+;
+; On return, BX points to the first byte after the transferred
+; block.
+;
+; This routine assumes CS == DS, and trashes most registers.
+;
+; Stylistic note: use "xchg" instead of "mov" when the source is a register
+; that is dead from that point; this saves space. However, please keep
+; the order to dst,src to keep things sane.
+;
+getlinsec:
+ add eax,[bsHidden] ; Add partition offset
+ xor edx,edx ; Zero-extend LBA (eventually allow 64 bits)
+
+.jmp: jmp strict short getlinsec_cbios
+
+;
+; getlinsec_ebios:
+;
+; getlinsec implementation for EBIOS (EDD)
+;
+getlinsec_ebios:
+.loop:
+ push bp ; Sectors left
+.retry2:
+ call maxtrans ; Enforce maximum transfer size
+ movzx edi,bp ; Sectors we are about to read
+ mov cx,retry_count
+.retry:
+
+ ; Form DAPA on stack
+ push edx
+ push eax
+ push es
+ push bx
+ push di
+ push word 16
+ mov si,sp
+ pushad
+ mov dl,[DriveNumber]
+ push ds
+ push ss
+ pop ds ; DS <- SS
+ mov ah,42h ; Extended Read
+ int 13h
+ pop ds
+ popad
+ lea sp,[si+16] ; Remove DAPA
+ jc .error
+ pop bp
+ add eax,edi ; Advance sector pointer
+ sub bp,di ; Sectors left
+ shl di,SECTOR_SHIFT ; 512-byte sectors
+ add bx,di ; Advance buffer pointer
+ and bp,bp
+ jnz .loop
+
+ ret
+
+.error:
+ ; Some systems seem to get "stuck" in an error state when
+ ; using EBIOS. Doesn't happen when using CBIOS, which is
+ ; good, since some other systems get timeout failures
+ ; waiting for the floppy disk to spin up.
+
+ pushad ; Try resetting the device
+ xor ax,ax
+ mov dl,[DriveNumber]
+ int 13h
+ popad
+ loop .retry ; CX-- and jump if not zero
+
+ ;shr word [MaxTransfer],1 ; Reduce the transfer size
+ ;jnz .retry2
+
+ ; Total failure. Try falling back to CBIOS.
+ mov byte [getlinsec.jmp+1],(getlinsec_cbios-(getlinsec.jmp+2))
+ ;mov byte [MaxTransfer],63 ; Max possibe CBIOS transfer
+
+ pop bp
+ ; ... fall through ...
+
+;
+; getlinsec_cbios:
+;
+; getlinsec implementation for legacy CBIOS
+;
+getlinsec_cbios:
+.loop:
+ push edx
+ push eax
+ push bp
+ push bx
+
+ movzx esi,word [bsSecPerTrack]
+ movzx edi,word [bsHeads]
+ ;
+ ; Dividing by sectors to get (track,sector): we may have
+ ; up to 2^18 tracks, so we need to use 32-bit arithmetric.
+ ;
+ div esi
+ xor cx,cx
+ xchg cx,dx ; CX <- sector index (0-based)
+ ; EDX <- 0
+ ; eax = track #
+ div edi ; Convert track to head/cyl
+
+ ; We should test this, but it doesn't fit...
+ ; cmp eax,1023
+ ; ja .error
+
+ ;
+ ; Now we have AX = cyl, DX = head, CX = sector (0-based),
+ ; BP = sectors to transfer, SI = bsSecPerTrack,
+ ; ES:BX = data target
+ ;
+
+ call maxtrans ; Enforce maximum transfer size
+
+ ; Must not cross track boundaries, so BP <= SI-CX
+ sub si,cx
+ cmp bp,si
+ jna .bp_ok
+ mov bp,si
+.bp_ok:
+
+ shl ah,6 ; Because IBM was STOOPID
+ ; and thought 8 bits were enough
+ ; then thought 10 bits were enough...
+ inc cx ; Sector numbers are 1-based, sigh
+ or cl,ah
+ mov ch,al
+ mov dh,dl
+ mov dl,[DriveNumber]
+ xchg ax,bp ; Sector to transfer count
+ mov ah,02h ; Read sectors
+ mov bp,retry_count
+.retry:
+ pushad
+ int 13h
+ popad
+ jc .error
+.resume:
+ movzx ecx,al ; ECX <- sectors transferred
+ shl ax,SECTOR_SHIFT ; Convert sectors in AL to bytes in AX
+ pop bx
+ add bx,ax
+ pop bp
+ pop eax
+ pop edx
+ add eax,ecx
+ sub bp,cx
+ jnz .loop
+ ret
+
+.error:
+ dec bp
+ jnz .retry
+
+ xchg ax,bp ; Sectors transferred <- 0
+ shr word [MaxTransfer],1
+ jnz .resume
+ ; Fall through to disk_error
+
+;
+; kaboom: write a message and bail out.
+;
+disk_error:
+kaboom:
+ xor si,si
+ mov ss,si
+ mov sp,StackBuf-4 ; Reset stack
+ mov ds,si ; Reset data segment
+ pop dword [fdctab] ; Restore FDC table
+.patch: ; When we have full code, intercept here
+ mov si,bailmsg
+
+ ; Write error message, this assumes screen page 0
+.loop: lodsb
+ and al,al
+ jz .done
+ mov ah,0Eh ; Write to screen as TTY
+ mov bx,0007h ; Attribute
+ int 10h
+ jmp short .loop
+.done:
+ cbw ; AH <- 0
+.again: int 16h ; Wait for keypress
+ ; NB: replaced by int 18h if
+ ; chosen at install time..
+ int 19h ; And try once more to boot...
+.norge: jmp short .norge ; If int 19h returned; this is the end
+
+;
+; Truncate BP to MaxTransfer
+;
+maxtrans:
+ cmp bp,[MaxTransfer]
+ jna .ok
+ mov bp,[MaxTransfer]
+.ok: ret
+
+;
+; Error message on failure
+;
+bailmsg: db 'Boot error', 0Dh, 0Ah, 0
+
+ ; This fails if the boot sector overflows
+ zb 1F8h-($-$$)
+
+FirstSector dd 0xDEADBEEF ; Location of sector 1
+MaxTransfer dw 0x007F ; Max transfer size
+
+; This field will be filled in 0xAA55 by the installer, but we abuse it
+; to house a pointer to the INT 16h instruction at
+; kaboom.again, which gets patched to INT 18h in RAID mode.
+bootsignature dw kaboom.again-bootsec
+
+;
+; ===========================================================================
+; End of boot sector
+; ===========================================================================
+; Start of LDLINUX.SYS
+; ===========================================================================
+
+ldlinux_sys:
+
+syslinux_banner db 0Dh, 0Ah
+%if IS_MDSLINUX
+ db 'MDSLINUX '
+%else
+ db 'SYSLINUX '
+%endif
+ db version_str, ' ', date, ' ', 0
+ db 0Dh, 0Ah, 1Ah ; EOF if we "type" this in DOS
+
+ align 8, db 0
+ldlinux_magic dd LDLINUX_MAGIC
+ dd LDLINUX_MAGIC^HEXDATE
+
+;
+; This area is patched by the installer. It is found by looking for
+; LDLINUX_MAGIC, plus 8 bytes.
+;
+patch_area:
+LDLDwords dw 0 ; Total dwords starting at ldlinux_sys
+LDLSectors dw 0 ; Number of sectors - (bootsec+this sec)
+CheckSum dd 0 ; Checksum starting at ldlinux_sys
+ ; value = LDLINUX_MAGIC - [sum of dwords]
+
+; Space for up to 64 sectors, the theoretical maximum
+SectorPtrs times 64 dd 0
+
+ldlinux_ent:
+;
+; Note that some BIOSes are buggy and run the boot sector at 07C0:0000
+; instead of 0000:7C00 and the like. We don't want to add anything
+; more to the boot sector, so it is written to not assume a fixed
+; value in CS, but we don't want to deal with that anymore from now
+; on.
+;
+ jmp 0:.next
+.next:
+
+;
+; Tell the user we got this far
+;
+ mov si,syslinux_banner
+ call writestr
+
+;
+; Tell the user if we're using EBIOS or CBIOS
+;
+print_bios:
+ mov si,cbios_name
+ cmp byte [getlinsec.jmp+1],(getlinsec_ebios-(getlinsec.jmp+2))
+ jne .cbios
+ mov si,ebios_name
+.cbios:
+ mov [BIOSName],si
+ call writestr
+
+ section .bss
+%define HAVE_BIOSNAME 1
+BIOSName resw 1
+
+ section .text
+;
+; Now we read the rest of LDLINUX.SYS. Don't bother loading the first
+; sector again, though.
+;
+load_rest:
+ mov si,SectorPtrs
+ mov bx,7C00h+2*SECTOR_SIZE ; Where we start loading
+ mov cx,[LDLSectors]
+
+.get_chunk:
+ jcxz .done
+ xor bp,bp
+ lodsd ; First sector of this chunk
+
+ mov edx,eax
+
+.make_chunk:
+ inc bp
+ dec cx
+ jz .chunk_ready
+ inc edx ; Next linear sector
+ cmp [si],edx ; Does it match
+ jnz .chunk_ready ; If not, this is it
+ add si,4 ; If so, add sector to chunk
+ jmp short .make_chunk
+
+.chunk_ready:
+ call getlinsecsr
+ shl bp,SECTOR_SHIFT
+ add bx,bp
+ jmp .get_chunk
+
+.done:
+
+;
+; All loaded up, verify that we got what we needed.
+; Note: the checksum field is embedded in the checksum region, so
+; by the time we get to the end it should all cancel out.
+;
+verify_checksum:
+ mov si,ldlinux_sys
+ mov cx,[LDLDwords]
+ mov edx,-LDLINUX_MAGIC
+.checksum:
+ lodsd
+ add edx,eax
+ loop .checksum
+
+ and edx,edx ; Should be zero
+ jz all_read ; We're cool, go for it!
+
+;
+; Uh-oh, something went bad...
+;
+ mov si,checksumerr_msg
+ call writestr
+ jmp kaboom
+
+;
+; -----------------------------------------------------------------------------
+; Subroutines that have to be in the first sector
+; -----------------------------------------------------------------------------
+
+;
+;
+; writestr: write a null-terminated string to the console
+; This assumes we're on page 0. This is only used for early
+; messages, so it should be OK.
+;
+writestr:
+.loop: lodsb
+ and al,al
+ jz .return
+ mov ah,0Eh ; Write to screen as TTY
+ mov bx,0007h ; Attribute
+ int 10h
+ jmp short .loop
+.return: ret
+
+
+; getlinsecsr: save registers, call getlinsec, restore registers
+;
+getlinsecsr: pushad
+ call getlinsec
+ popad
+ ret
+
+;
+; Checksum error message
+;
+checksumerr_msg db ' Load error - ', 0 ; Boot failed appended
+
+;
+; BIOS type string
+;
+cbios_name db 'CBIOS', 0
+ebios_name db 'EBIOS', 0
+
+;
+; Debug routine
+;
+%ifdef debug
+safedumpregs:
+ cmp word [Debug_Magic],0D00Dh
+ jnz nc_return
+ jmp dumpregs
+%endif
+
+rl_checkpt equ $ ; Must be <= 8000h
+
+rl_checkpt_off equ ($-$$)
+%ifndef DEPEND
+%if rl_checkpt_off > 400h
+%error "Sector 1 overflow"
+%endif
+%endif
+
+; ----------------------------------------------------------------------------
+; End of code and data that have to be in the first sector
+; ----------------------------------------------------------------------------
+
+all_read:
+;
+; Let the user (and programmer!) know we got this far. This used to be
+; in Sector 1, but makes a lot more sense here.
+;
+ mov si,copyright_str
+ call writestr
+
+
+;
+; Insane hack to expand the superblock to dwords
+;
+expand_super:
+ xor eax,eax
+ mov si,superblock
+ mov di,SuperInfo
+ mov cx,superinfo_size
+.loop:
+ lodsw
+ dec si
+ stosd ; Store expanded word
+ xor ah,ah
+ stosd ; Store expanded byte
+ loop .loop
+
+;
+; Compute some information about this filesystem.
+;
+
+; First, generate the map of regions
+genfatinfo:
+ mov edx,[bxSectors]
+ and dx,dx
+ jnz .have_secs
+ mov edx,[bsHugeSectors]
+.have_secs:
+ mov [TotalSectors],edx
+
+ mov eax,[bxResSectors]
+ mov [FAT],eax ; Beginning of FAT
+ mov edx,[bxFATsecs]
+ and dx,dx
+ jnz .have_fatsecs
+ mov edx,[bootsec+36] ; FAT32 BPB_FATsz32
+.have_fatsecs:
+ imul edx,[bxFATs]
+ add eax,edx
+ mov [RootDirArea],eax ; Beginning of root directory
+ mov [RootDir],eax ; For FAT12/16 == root dir location
+
+ mov edx,[bxRootDirEnts]
+ add dx,SECTOR_SIZE/32-1
+ shr dx,SECTOR_SHIFT-5
+ mov [RootDirSize],edx
+ add eax,edx
+ mov [DataArea],eax ; Beginning of data area
+
+; Next, generate a cluster size shift count and mask
+ mov eax,[bxSecPerClust]
+ bsr cx,ax
+ mov [ClustShift],cl
+ push cx
+ add cl,SECTOR_SHIFT
+ mov [ClustByteShift],cl
+ pop cx
+ dec ax
+ mov [ClustMask],eax
+ inc ax
+ shl eax,SECTOR_SHIFT
+ mov [ClustSize],eax
+
+;
+; FAT12, FAT16 or FAT28^H^H32? This computation is fscking ridiculous.
+;
+getfattype:
+ mov eax,[TotalSectors]
+ sub eax,[DataArea]
+ shr eax,cl ; cl == ClustShift
+ mov cl,nextcluster_fat12-(nextcluster+2)
+ cmp eax,4085 ; FAT12 limit
+ jb .setsize
+ mov cl,nextcluster_fat16-(nextcluster+2)
+ cmp eax,65525 ; FAT16 limit
+ jb .setsize
+ ;
+ ; FAT32, root directory is a cluster chain
+ ;
+ mov cl,[ClustShift]
+ mov eax,[bootsec+44] ; Root directory cluster
+ sub eax,2
+ shl eax,cl
+ add eax,[DataArea]
+ mov [RootDir],eax
+ mov cl,nextcluster_fat28-(nextcluster+2)
+.setsize:
+ mov byte [nextcluster+1],cl
+
+;
+; Common initialization code
+;
+%include "cpuinit.inc"
+%include "init.inc"
+
+;
+; Initialize the metadata cache
+;
+ call initcache
+
+;
+; Now, everything is "up and running"... patch kaboom for more
+; verbosity and using the full screen system
+;
+ ; E9 = JMP NEAR
+ mov dword [kaboom.patch],0e9h+((kaboom2-(kaboom.patch+3)) << 8)
+
+;
+; Now we're all set to start with our *real* business. First load the
+; configuration file (if any) and parse it.
+;
+; In previous versions I avoided using 32-bit registers because of a
+; rumour some BIOSes clobbered the upper half of 32-bit registers at
+; random. I figure, though, that if there are any of those still left
+; they probably won't be trying to install Linux on them...
+;
+; The code is still ripe with 16-bitisms, though. Not worth the hassle
+; to take'm out. In fact, we may want to put them back if we're going
+; to boot ELKS at some point.
+;
+
+;
+; Load configuration file
+;
+ mov si,config_name ; Save configuration file name
+ mov di,ConfigName
+ call strcpy
+
+ mov di,syslinux_cfg1
+ call open
+ jnz .config_open
+ mov di,syslinux_cfg2
+ call open
+ jnz .config_open
+ mov di,syslinux_cfg3
+ call open
+ jz no_config_file
+.config_open:
+ mov eax,[PrevDir] ; Make the directory with syslinux.cfg ...
+ mov [CurrentDir],eax ; ... the current directory
+
+;
+; Now we have the config file open. Parse the config file and
+; run the user interface.
+;
+%include "ui.inc"
+
+;
+; allocate_file: Allocate a file structure
+;
+; If successful:
+; ZF set
+; BX = file pointer
+; In unsuccessful:
+; ZF clear
+;
+allocate_file:
+ TRACER 'a'
+ push cx
+ mov bx,Files
+ mov cx,MAX_OPEN
+.check: cmp dword [bx], byte 0
+ je .found
+ add bx,open_file_t_size ; ZF = 0
+ loop .check
+ ; ZF = 0 if we fell out of the loop
+.found: pop cx
+ ret
+
+;
+; search_dos_dir:
+; Search a specific directory for a pre-mangled filename in
+; MangledBuf, in the directory starting in sector EAX.
+;
+; NOTE: This file considers finding a zero-length file an
+; error. This is so we don't have to deal with that special
+; case elsewhere in the program (most loops have the test
+; at the end).
+;
+; Assumes DS == ES == CS.
+;
+; If successful:
+; ZF clear
+; SI = file pointer
+; EAX = file length (MAY BE ZERO!)
+; DL = file attributes
+; If unsuccessful
+; ZF set
+;
+
+search_dos_dir:
+ push bx
+ call allocate_file
+ jnz .alloc_failure
+
+ push cx
+ push gs
+ push es
+ push ds
+ pop es ; ES = DS
+
+.scansector:
+ ; EAX <- directory sector to scan
+ call getcachesector
+ ; GS:SI now points to this sector
+
+ mov cx,SECTOR_SIZE/32 ; 32 == directory entry size
+.scanentry:
+ cmp byte [gs:si],0
+ jz .failure ; Hit directory high water mark
+ test byte [gs:si+11],8 ; Ignore volume labels and
+ ; VFAT long filename entries
+ jnz .nomatch
+ push cx
+ push si
+ push di
+ mov di,MangledBuf
+ mov cx,11
+ gs repe cmpsb
+ pop di
+ pop si
+ pop cx
+ jz .found
+.nomatch:
+ add si,32
+ loop .scanentry
+
+ call nextsector
+ jnc .scansector ; CF is set if we're at end
+
+ ; If we get here, we failed
+.failure:
+ pop es
+ pop gs
+ pop cx
+.alloc_failure:
+ pop bx
+ xor eax,eax ; ZF <- 1
+ ret
+.found:
+ mov eax,[gs:si+28] ; File size
+ add eax,SECTOR_SIZE-1
+ shr eax,SECTOR_SHIFT
+ mov [bx+4],eax ; Sector count
+
+ mov cl,[ClustShift]
+ mov dx,[gs:si+20] ; High cluster word
+ shl edx,16
+ mov dx,[gs:si+26] ; Low cluster word
+ sub edx,2
+ shl edx,cl
+ add edx,[DataArea]
+ mov [bx],edx ; Starting sector
+
+ mov eax,[gs:si+28] ; File length again
+ mov dl,[gs:si+11] ; File attribute
+ mov si,bx ; File pointer...
+ and si,si ; ZF <- 0
+
+ pop es
+ pop gs
+ pop cx
+ pop bx
+ ret
+
+;
+; close_file:
+; Deallocates a file structure (pointer in SI)
+; Assumes CS == DS.
+;
+close_file:
+ and si,si
+ jz .closed
+ mov dword [si],0 ; First dword == file_sector
+ xor si,si
+.closed: ret
+
+;
+; searchdir:
+;
+; Open a file
+;
+; On entry:
+; DS:DI = filename
+; If successful:
+; ZF clear
+; SI = file pointer
+; EAX = file length in bytes
+; If unsuccessful
+; ZF set
+;
+; Assumes CS == DS == ES, and trashes BX and CX.
+;
+searchdir:
+ mov eax,[CurrentDir]
+ cmp byte [di],'/' ; Root directory?
+ jne .notroot
+ mov eax,[RootDir]
+ inc di
+.notroot:
+
+.pathwalk:
+ push eax ; <A> Current directory sector
+ mov si,di
+.findend:
+ lodsb
+ cmp al,' '
+ jbe .endpath
+ cmp al,'/'
+ jne .findend
+.endpath:
+ xchg si,di
+ pop eax ; <A> Current directory sector
+
+ mov [PrevDir],eax ; Remember last directory searched
+
+ push di
+ call mangle_dos_name ; MangledBuf <- component
+ call search_dos_dir
+ pop di
+ jz .notfound ; Pathname component missing
+
+ cmp byte [di-1],'/' ; Do we expect a directory
+ je .isdir
+
+ ; Otherwise, it should be a file
+.isfile:
+ test dl,18h ; Subdirectory|Volume Label
+ jnz .badfile ; If not a file, it's a bad thing
+
+ ; SI and EAX are already set
+ mov [si+file_bytesleft],eax
+ push eax
+ add eax,SECTOR_SIZE-1
+ shr eax,SECTOR_SHIFT
+ mov [si+file_left],eax ; Sectors left
+ pop eax
+ and eax,eax ; EAX != 0
+ jz .badfile
+ ret ; Done!
+
+ ; If we expected a directory, it better be one...
+.isdir:
+ test dl,10h ; Subdirectory
+ jz .badfile
+
+ xor eax,eax
+ xchg eax,[si+file_sector] ; Get sector number and free file structure
+ jmp .pathwalk ; Walk the next bit of the path
+
+.badfile:
+ xor eax,eax
+ mov [si],eax ; Free file structure
+
+.notfound:
+ xor eax,eax
+ ret
+
+ section .bss
+ alignb 4
+CurrentDir resd 1 ; Current directory
+PrevDir resd 1 ; Last scanned directory
+
+ section .text
+
+;
+;
+; kaboom2: once everything is loaded, replace the part of kaboom
+; starting with "kaboom.patch" with this part
+
+kaboom2:
+ mov si,err_bootfailed
+ call cwritestr
+ cmp byte [kaboom.again+1],18h ; INT 18h version?
+ je .int18
+ call getchar
+ call vgaclearmode
+ int 19h ; And try once more to boot...
+.norge: jmp short .norge ; If int 19h returned; this is the end
+.int18:
+ call vgaclearmode
+ int 18h
+.noreg: jmp short .noreg ; Nynorsk
+
+;
+; mangle_name: Mangle a filename pointed to by DS:SI into a buffer pointed
+; to by ES:DI; ends on encountering any whitespace.
+; DI is preserved.
+;
+; This verifies that a filename is < FILENAME_MAX characters,
+; doesn't contain whitespace, zero-pads the output buffer,
+; and removes trailing dots and redundant slashes, plus changes
+; backslashes to forward slashes,
+; so "repe cmpsb" can do a compare, and the path-searching routine
+; gets a bit of an easier job.
+;
+;
+mangle_name:
+ push di
+ push bx
+ xor ax,ax
+ mov cx,FILENAME_MAX-1
+ mov bx,di
+
+.mn_loop:
+ lodsb
+ cmp al,' ' ; If control or space, end
+ jna .mn_end
+ cmp al,'\' ; Backslash?
+ jne .mn_not_bs
+ mov al,'/' ; Change to forward slash
+.mn_not_bs:
+ cmp al,ah ; Repeated slash?
+ je .mn_skip
+ xor ah,ah
+ cmp al,'/'
+ jne .mn_ok
+ mov ah,al
+.mn_ok stosb
+.mn_skip: loop .mn_loop
+.mn_end:
+ cmp bx,di ; At the beginning of the buffer?
+ jbe .mn_zero
+ cmp byte [es:di-1],'.' ; Terminal dot?
+ je .mn_kill
+ cmp byte [es:di-1],'/' ; Terminal slash?
+ jne .mn_zero
+.mn_kill: dec di ; If so, remove it
+ inc cx
+ jmp short .mn_end
+.mn_zero:
+ inc cx ; At least one null byte
+ xor ax,ax ; Zero-fill name
+ rep stosb
+ pop bx
+ pop di
+ ret ; Done
+
+;
+; unmangle_name: Does the opposite of mangle_name; converts a DOS-mangled
+; filename to the conventional representation. This is needed
+; for the BOOT_IMAGE= parameter for the kernel.
+; NOTE: A 13-byte buffer is mandatory, even if the string is
+; known to be shorter.
+;
+; DS:SI -> input mangled file name
+; ES:DI -> output buffer
+;
+; On return, DI points to the first byte after the output name,
+; which is set to a null byte.
+;
+unmangle_name: call strcpy
+ dec di ; Point to final null byte
+ ret
+
+;
+; mangle_dos_name:
+; Mangle a DOS filename component pointed to by DS:SI
+; into [MangledBuf]; ends on encountering any whitespace or slash.
+; Assumes CS == DS == ES.
+;
+
+mangle_dos_name:
+ pusha
+ mov di,MangledBuf
+
+ mov cx,11 ; # of bytes to write
+.loop:
+ lodsb
+ cmp al,' ' ; If control or space, end
+ jna .end
+ cmp al,'/' ; Slash, too
+ je .end
+ cmp al,'.' ; Period -> space-fill
+ je .is_period
+ cmp al,'a'
+ jb .not_lower
+ cmp al,'z'
+ ja .not_uslower
+ sub al,020h
+ jmp short .not_lower
+.is_period: mov al,' ' ; We need to space-fill
+.period_loop: cmp cx,3 ; If <= 3 characters left
+ jbe .loop ; Just ignore it
+ stosb ; Otherwise, write a period
+ loop .period_loop ; Dec CX and (always) jump
+.not_uslower: cmp al,ucase_low
+ jb .not_lower
+ cmp al,ucase_high
+ ja .not_lower
+ mov bx,ucase_tab-ucase_low
+ xlatb
+.not_lower: stosb
+ loop .loop ; Don't continue if too long
+.end:
+ mov al,' ' ; Space-fill name
+ rep stosb ; Doesn't do anything if CX=0
+ popa
+ ret ; Done
+
+ section .bss
+MangledBuf resb 11
+
+ section .text
+;
+; Case tables for extended characters; this is technically code page 865,
+; but code page 437 users will probably not miss not being able to use the
+; cent sign in kernel images too much :-)
+;
+; The table only covers the range 129 to 164; the rest we can deal with.
+;
+ section .data
+
+ucase_low equ 129
+ucase_high equ 164
+ucase_tab db 154, 144, 'A', 142, 'A', 143, 128, 'EEEIII'
+ db 142, 143, 144, 146, 146, 'O', 153, 'OUUY', 153, 154
+ db 157, 156, 157, 158, 159, 'AIOU', 165
+
+ section .text
+;
+; getfssec_edx: Get multiple sectors from a file
+;
+; This routine makes sure the subtransfers do not cross a 64K boundary,
+; and will correct the situation if it does, UNLESS *sectors* cross
+; 64K boundaries.
+;
+; ES:BX -> Buffer
+; EDX -> Current sector number
+; CX -> Sector count (0FFFFh = until end of file)
+; Must not exceed the ES segment
+; Returns EDX=0, CF=1 on EOF (not necessarily error)
+; All arguments are advanced to reflect data read.
+;
+getfssec_edx:
+ push ebp
+ push eax
+.getfragment:
+ xor ebp,ebp ; Fragment sector count
+ push edx ; Starting sector pointer
+.getseccnt:
+ inc bp
+ dec cx
+ jz .do_read
+ xor eax,eax
+ mov ax,es
+ shl ax,4
+ add ax,bx ; Now AX = how far into 64K block we are
+ not ax ; Bytes left in 64K block
+ inc eax
+ shr eax,SECTOR_SHIFT ; Sectors left in 64K block
+ cmp bp,ax
+ jnb .do_read ; Unless there is at least 1 more sector room...
+ mov eax,edx ; Current sector
+ inc edx ; Predict it's the linearly next sector
+ call nextsector
+ jc .do_read
+ cmp edx,eax ; Did it match?
+ jz .getseccnt
+.do_read:
+ pop eax ; Starting sector pointer
+ call getlinsecsr
+ lea eax,[eax+ebp-1] ; This is the last sector actually read
+ shl bp,9
+ add bx,bp ; Adjust buffer pointer
+ call nextsector
+ jc .eof
+ mov edx,eax
+ and cx,cx
+ jnz .getfragment
+.done:
+ pop eax
+ pop ebp
+ ret
+.eof:
+ xor edx,edx
+ stc
+ jmp .done
+
+;
+; getfssec: Get multiple sectors from a file
+;
+; Same as above, except SI is a pointer to a open_file_t
+;
+; ES:BX -> Buffer
+; DS:SI -> Pointer to open_file_t
+; CX -> Sector count (0FFFFh = until end of file)
+; Must not exceed the ES segment
+; Returns CF=1 on EOF (not necessarily error)
+; ECX returns number of bytes read.
+; All arguments are advanced to reflect data read.
+;
+getfssec:
+ push edx
+ movzx edx,cx
+ push edx ; Zero-extended CX
+ cmp edx,[si+file_left]
+ jbe .sizeok
+ mov edx,[si+file_left]
+ mov cx,dx
+.sizeok:
+ sub [si+file_left],edx
+ mov edx,[si+file_sector]
+ call getfssec_edx
+ mov [si+file_sector],edx
+ pop ecx ; Sectors requested read
+ shl ecx,SECTOR_SHIFT
+ cmp ecx,[si+file_bytesleft]
+ ja .eof
+.noteof:
+ sub [si+file_bytesleft],ecx ; CF <- 0
+ pop edx
+ ret
+.eof:
+ mov ecx,[si+file_bytesleft]
+ call close_file
+ pop edx
+ stc
+ ret
+
+;
+; nextcluster: Advance a cluster pointer in EDI to the next cluster
+; pointed at in the FAT tables. CF=0 on return if end of file.
+;
+nextcluster:
+ jmp strict short nextcluster_fat28 ; This gets patched
+
+nextcluster_fat12:
+ push eax
+ push edx
+ push bx
+ push cx
+ push si
+ mov edx,edi
+ shr edi,1
+ pushf ; Save the shifted-out LSB (=CF)
+ add edx,edi
+ mov eax,edx
+ shr eax,9
+ call getfatsector
+ mov bx,dx
+ and bx,1FFh
+ mov cl,[gs:si+bx]
+ inc edx
+ mov eax,edx
+ shr eax,9
+ call getfatsector
+ mov bx,dx
+ and bx,1FFh
+ mov ch,[gs:si+bx]
+ popf
+ jnc .even
+ shr cx,4
+.even: and cx,0FFFh
+ movzx edi,cx
+ cmp di,0FF0h
+ pop si
+ pop cx
+ pop bx
+ pop edx
+ pop eax
+ ret
+
+;
+; FAT16 decoding routine.
+;
+nextcluster_fat16:
+ push eax
+ push si
+ push bx
+ mov eax,edi
+ shr eax,SECTOR_SHIFT-1
+ call getfatsector
+ mov bx,di
+ add bx,bx
+ and bx,1FEh
+ movzx edi,word [gs:si+bx]
+ cmp di,0FFF0h
+ pop bx
+ pop si
+ pop eax
+ ret
+;
+; FAT28 ("FAT32") decoding routine.
+;
+nextcluster_fat28:
+ push eax
+ push si
+ push bx
+ mov eax,edi
+ shr eax,SECTOR_SHIFT-2
+ call getfatsector
+ mov bx,di
+ add bx,bx
+ add bx,bx
+ and bx,1FCh
+ mov edi,dword [gs:si+bx]
+ and edi,0FFFFFFFh ; 28 bits only
+ cmp edi,0FFFFFF0h
+ pop bx
+ pop si
+ pop eax
+ ret
+
+;
+; nextsector: Given a sector in EAX on input, return the next sector
+; of the same filesystem object, which may be the root
+; directory or a cluster chain. Returns EOF.
+;
+; Assumes CS == DS.
+;
+nextsector:
+ push edi
+ push edx
+ mov edx,[DataArea]
+ mov edi,eax
+ sub edi,edx
+ jae .isdata
+
+ ; Root directory
+ inc eax
+ cmp eax,edx
+ cmc
+ jmp .done
+
+.isdata:
+ not edi
+ test edi,[ClustMask]
+ jz .endcluster
+
+ ; It's not the final sector in a cluster
+ inc eax
+ jmp .done
+
+.endcluster:
+ push gs ; nextcluster trashes gs
+ push cx
+ not edi
+ mov cl,[ClustShift]
+ shr edi,cl
+ add edi,2
+
+ ; Now EDI contains the cluster number
+ call nextcluster
+ cmc
+ jc .exit ; There isn't anything else...
+
+ ; New cluster number now in EDI
+ sub edi,2
+ shl edi,cl ; CF <- 0, unless something is very wrong
+ lea eax,[edi+edx]
+.exit:
+ pop cx
+ pop gs
+.done:
+ pop edx
+ pop edi
+ ret
+
+;
+; getfatsector: Check for a particular sector (in EAX) in the FAT cache,
+; and return a pointer in GS:SI, loading it if needed.
+;
+; Assumes CS == DS.
+;
+getfatsector:
+ add eax,[FAT] ; FAT starting address
+ jmp getcachesector
+
+; -----------------------------------------------------------------------------
+; Common modules
+; -----------------------------------------------------------------------------
+
+%include "getc.inc" ; getc et al
+%include "conio.inc" ; Console I/O
+%include "plaincon.inc" ; writechr
+%include "writestr.inc" ; String output
+%include "configinit.inc" ; Initialize configuration
+%include "parseconfig.inc" ; High-level config file handling
+%include "parsecmd.inc" ; Low-level config file handling
+%include "bcopy32.inc" ; 32-bit bcopy
+%include "loadhigh.inc" ; Load a file into high memory
+%include "font.inc" ; VGA font stuff
+%include "graphics.inc" ; VGA graphics
+%include "highmem.inc" ; High memory sizing
+%include "strcpy.inc" ; strcpy()
+%include "cache.inc" ; Metadata disk cache
+%include "adv.inc" ; Auxillary Data Vector
+%include "localboot.inc" ; Disk-based local boot
+
+; -----------------------------------------------------------------------------
+; Begin data section
+; -----------------------------------------------------------------------------
+
+ section .data
+copyright_str db ' Copyright (C) 1994-', year, ' H. Peter Anvin'
+ db CR, LF, 0
+err_bootfailed db CR, LF, 'Boot failed: please change disks and press '
+ db 'a key to continue.', CR, LF, 0
+syslinux_cfg1 db '/boot' ; /boot/syslinux/syslinux.cfg
+syslinux_cfg2 db '/syslinux' ; /syslinux/syslinux.cfg
+syslinux_cfg3 db '/' ; /syslinux.cfg
+config_name db 'syslinux.cfg', 0 ; syslinux.cfg
+
+;
+; Command line options we'd like to take a look at
+;
+; mem= and vga= are handled as normal 32-bit integer values
+initrd_cmd db 'initrd='
+initrd_cmd_len equ 7
+
+;
+; Config file keyword table
+;
+%include "keywords.inc"
+
+;
+; Extensions to search for (in *forward* order).
+;
+exten_table: db '.cbt' ; COMBOOT (specific)
+ db '.bss' ; Boot Sector (add superblock)
+ db '.bs', 0 ; Boot Sector
+ db '.com' ; COMBOOT (same as DOS)
+ db '.c32' ; COM32
+exten_table_end:
+ dd 0, 0 ; Need 8 null bytes here
+
+;
+; Misc initialized (data) variables
+;
+%ifdef debug ; This code for debugging only
+debug_magic dw 0D00Dh ; Debug code sentinel
+%endif
+
+ alignb 4, db 0
+BufSafe dw trackbufsize/SECTOR_SIZE ; Clusters we can load into trackbuf
+BufSafeBytes dw trackbufsize ; = how many bytes?
+%ifndef DEPEND
+%if ( trackbufsize % SECTOR_SIZE ) != 0
+%error trackbufsize must be a multiple of SECTOR_SIZE
+%endif
+%endif
diff --git a/core/loadhigh.inc b/core/loadhigh.inc
new file mode 100644
index 00000000..63041483
--- /dev/null
+++ b/core/loadhigh.inc
@@ -0,0 +1,120 @@
+;; -----------------------------------------------------------------------
+;;
+;; Copyright 1994-2008 H. Peter Anvin - All Rights Reserved
+;;
+;; This program is free software; you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation, Inc., 53 Temple Place Ste 330,
+;; Boston MA 02111-1307, USA; either version 2 of the License, or
+;; (at your option) any later version; incorporated herein by reference.
+;;
+;; -----------------------------------------------------------------------
+
+;;
+;; loadhigh.inc
+;;
+;; Load a file into high memory
+;;
+
+ section .text
+
+;
+; load_high: loads (the remainder of) a file into high memory.
+; This routine prints dots for each 64K transferred, and
+; calls abort_check periodically.
+;
+; The xfer_buf_seg is used as a bounce buffer.
+;
+; Assumes CS == DS.
+;
+; The input address (EDI) should be dword aligned, and the final
+; stretch is padded with zeroes if necessary.
+;
+; Inputs: SI = file handle/cluster pointer
+; EDI = target address in high memory
+; EAX = maximum number of bytes to load
+; DX = zero-padding mask (e.g. 0003h for pad to dword)
+; BX = subroutine to call at the top of each loop
+; (to print status and check for abort)
+; MyHighMemSize = maximum load address
+;
+; Outputs: SI = file handle/cluster pointer
+; EBX = first untouched address (not including padding)
+; EDI = first untouched address (including padding)
+; CF = reached EOF
+;
+load_high:
+ push es ; <AAA> ES
+
+ mov cx,xfer_buf_seg
+ mov es,cx
+ mov [PauseBird],bx
+
+.read_loop:
+ and si,si ; If SI == 0 then we have end of file
+ jz .eof
+ call [PauseBird]
+
+ push eax ; <A> Total bytes to transfer
+ cmp eax,(1 << 16) ; Max 64K in one transfer
+ jna .size_ok
+ mov eax,(1 << 16)
+.size_ok:
+ push eax ; <B> Bytes transferred this chunk
+ add eax,SECTOR_SIZE-1
+ shr eax,SECTOR_SHIFT ; Convert to sectors
+
+ ; Now (e)ax contains the number of sectors to get
+ push edi ; <C> Target buffer
+ mov cx,ax
+ xor bx,bx ; ES:0
+ call getfssec ; Load the data into xfer_buf_seg
+ pop edi ; <C> Target buffer
+ pushf ; <C> EOF status
+ lea ebx,[edi+ecx] ; End of data
+.fix_slop:
+ test cx,dx
+ jz .noslop
+ ; The last dword fractional - pad with zeroes
+ ; Zero-padding is critical for multi-file initramfs.
+ mov byte [es:ecx],0
+ inc cx
+ jmp short .fix_slop
+.noslop:
+ lea eax,[edi+ecx]
+ cmp eax,[MyHighMemSize]
+ ja .overflow
+
+ push esi ; <D> File handle/cluster pointer
+ mov esi,(xfer_buf_seg << 4) ; Source address
+ call bcopy ; Copy to high memory
+ pop esi ; <D> File handle/cluster pointer
+ popf ; <C> EOF status
+ pop ecx ; <B> Byte count this round
+ pop eax ; <A> Total bytes to transfer
+ jc .eof
+ sub eax,ecx
+ jnz .read_loop ; More to read... (if ZF=1 then CF=0)
+.eof:
+ pop es ; <AAA> ES
+ ret
+
+.overflow: mov si,err_nohighmem
+ jmp abort_load
+
+dot_pause:
+ push ax
+ mov al,'.'
+ call writechr
+ pop ax
+ jmp abort_check ; Handles the return
+
+ section .data
+err_nohighmem db CR, LF
+ db 'Not enough memory to load specified image.', CR, LF, 0
+
+ section .bss
+ alignb 2
+PauseBird resw 1
+
+ section .text
diff --git a/core/localboot.inc b/core/localboot.inc
new file mode 100644
index 00000000..b6b31deb
--- /dev/null
+++ b/core/localboot.inc
@@ -0,0 +1,75 @@
+; -----------------------------------------------------------------------
+;
+; Copyright 1999-2008 H. Peter Anvin - All Rights Reserved
+;
+; This program is free software; you can redistribute it and/or modify
+; it under the terms of the GNU General Public License as published by
+; the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+; Boston MA 02110-1301, USA; either version 2 of the License, or
+; (at your option) any later version; incorporated herein by reference.
+;
+; -----------------------------------------------------------------------
+
+;
+; localboot.inc
+;
+; Boot from a local disk, or invoke INT 18h.
+;
+
+%if HAS_LOCALBOOT
+
+;
+; Boot a specified local disk. AX specifies the BIOS disk number; or
+; -1 in case we should execute INT 18h ("next device.")
+;
+ section .text
+
+local_boot:
+ call vgaclearmode
+ RESET_STACK_AND_SEGS dx ; dx <- 0
+ mov fs,dx
+ mov gs,dx
+ mov si,localboot_msg
+ call writestr
+ cmp ax,-1
+ je .int18
+
+ ; Load boot sector from the specified BIOS device and jump to it.
+ mov dl,al
+ xor dh,dh
+ push dx
+ xor ax,ax ; Reset drive
+ int 13h
+ mov ax,0201h ; Read one sector
+ mov cx,0001h ; C/H/S = 0/0/1 (first sector)
+ mov bx,trackbuf
+ mov bp,retry_count
+.again:
+ pusha
+ int 13h
+ popa
+ jnc .ok
+ dec bp
+ jnz .again
+ jmp kaboom ; Failure...
+.ok:
+ pop dx
+ cli ; Abandon hope, ye who enter here
+ mov si,trackbuf
+ mov di,07C00h
+ mov cx,512 ; Probably overkill, but should be safe
+ rep movsd
+ mov ss,cx ; SS <- 0
+ mov sp,7C00h
+ jmp 0:07C00h ; Jump to new boot sector
+
+.int18:
+ int 18h ; Hope this does the right thing...
+ jmp kaboom ; If we returned, oh boy...
+
+ section .data
+localboot_msg db 'Booting from local disk...', CR, LF, 0
+
+ section .text
+
+%endif ; HAS_LOCALBOOT
diff --git a/core/lstadjust.pl b/core/lstadjust.pl
new file mode 100755
index 00000000..cec54029
--- /dev/null
+++ b/core/lstadjust.pl
@@ -0,0 +1,57 @@
+#!/usr/bin/perl
+#
+# Take a NASM list and map file and make the offsets in the list file
+# absolute. This makes debugging a lot easier.
+#
+# Usage:
+#
+# lstadjust.pl listfile mapfile outfile
+#
+
+($listfile, $mapfile, $outfile) = @ARGV;
+
+open(LST, "< $listfile\0")
+ or die "$0: cannot open: $listfile: $!\n";
+open(MAP, "< $mapfile\0")
+ or die "$0: cannot open: $mapfile: $!\n";
+open(OUT, "> $outfile\0")
+ or die "$0: cannot create: $outfile: $!\n";
+
+%vstart = ();
+
+while (defined($line = <MAP>)) {
+ if ($line =~ /^\s*([0-9]+)\s+(\S+)\s+([0-9a-f]+)\s+([0-9a-f]+)\s+([0-9a-f]+)\s+([0-9a-f]+)\s+2\*\*([0-9]+)/i) {
+ $vstart{$2} = hex $4;
+ }
+}
+close(MAP);
+
+$offset = 0;
+@ostack = ();
+
+while (defined($line = <LST>)) {
+ chomp $line;
+
+ $source = substr($line, 40);
+ if ($source =~ /^([^;]*);/) {
+ $source = $1;
+ }
+
+ ($label, $op, $arg, $tail) = split(/\s+/, $source);
+ if ($op =~ /^(|\[)section$/i) {
+ $offset = $vstart{$arg};
+ } elsif ($op =~ /^(absolute|\[absolute)$/i) {
+ $offset = 0;
+ } elsif ($op =~ /^struc$/i) {
+ push(@ostack, $offset);
+ $offset = 0;
+ } elsif ($op =~ /^endstruc$/i) {
+ $offset = pop(@ostack);
+ }
+
+ if ($line =~ /^(\s*[0-9]+ )([0-9A-F]{8})(\s.*)$/) {
+ $line = sprintf("%s%08X%s", $1, (hex $2)+$offset, $3);
+ }
+
+ print OUT $line, "\n";
+}
diff --git a/core/macros.inc b/core/macros.inc
new file mode 100644
index 00000000..f5e2c924
--- /dev/null
+++ b/core/macros.inc
@@ -0,0 +1,110 @@
+;; -----------------------------------------------------------------------
+;;
+;; Copyright 1994-2008 H. Peter Anvin - All Rights Reserved
+;;
+;; This program is free software; you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation, Inc., 53 Temple Place Ste 330,
+;; Boston MA 02111-1307, USA; either version 2 of the License, or
+;; (at your option) any later version; incorporated herein by reference.
+;;
+;; -----------------------------------------------------------------------
+
+;;
+;; macros.inc
+;;
+;; Convenient macros
+;;
+
+%ifndef _MACROS_INC
+%define _MACROS_INC
+
+;
+; Identify the module we're compiling; the "correct" should be defined
+; in the module itself to 1
+;
+%ifndef IS_SYSLINUX
+%define IS_SYSLINUX 0
+%endif
+%ifndef IS_MDSLINUX
+%define IS_MDSLINUX 0
+%endif
+%ifndef IS_PXELINUX
+%define IS_PXELINUX 0
+%endif
+%ifndef IS_ISOLINUX
+%define IS_ISOLINUX 0
+%endif
+%ifndef IS_EXTLINUX
+%define IS_EXTLINUX 0
+%endif
+
+;
+; Macros similar to res[bwd], but which works in the code segment (after
+; section .text) or the data segment (section .data)
+;
+%macro zb 1.nolist
+ times %1 db 0
+%endmacro
+
+%macro zw 1.nolist
+ times %1 dw 0
+%endmacro
+
+%macro zd 1.nolist
+ times %1 dd 0
+%endmacro
+
+;
+; Macro to emit an unsigned decimal number as a string
+;
+%macro asciidec 1.nolist
+%ifndef DEPEND ; Not safe for "depend"
+%if %1 >= 1000000000
+ db ((%1/1000000000) % 10) + '0'
+%endif
+%if %1 >= 100000000
+ db ((%1/100000000) % 10) + '0'
+%endif
+%if %1 >= 10000000
+ db ((%1/10000000) % 10) + '0'
+%endif
+%if %1 >= 1000000
+ db ((%1/1000000) % 10) + '0'
+%endif
+%if %1 >= 100000
+ db ((%1/100000) % 10) + '0'
+%endif
+%if %1 >= 10000
+ db ((%1/10000) % 10) + '0'
+%endif
+%if %1 >= 1000
+ db ((%1/1000) % 10) + '0'
+%endif
+%if %1 >= 100
+ db ((%1/100) % 10) + '0'
+%endif
+%if %1 >= 10
+ db ((%1/10) % 10) + '0'
+%endif
+ db (%1 % 10) + '0'
+%endif
+%endmacro
+
+;
+; Macros for network byte order of constants
+;
+%define htons(x) ( ( ((x) & 0FFh) << 8 ) + ( ((x) & 0FF00h) >> 8 ) )
+%define ntohs(x) htons(x)
+%define htonl(x) ( ( ((x) & 0FFh) << 24) + ( ((x) & 0FF00h) << 8 ) + ( ((x) & 0FF0000h) >> 8 ) + ( ((x) & 0FF000000h) >> 24) )
+%define ntohl(x) htonl(x)
+
+;
+; ASCII
+;
+CR equ 13 ; Carriage Return
+LF equ 10 ; Line Feed
+FF equ 12 ; Form Feed
+BS equ 8 ; Backspace
+
+%endif ; _MACROS_INC
diff --git a/core/now.pl b/core/now.pl
new file mode 100644
index 00000000..a3b5a798
--- /dev/null
+++ b/core/now.pl
@@ -0,0 +1,21 @@
+#!/usr/bin/perl
+#
+# Print the time (possibly the mtime of a file) as a hexadecimal integer
+# If more than one file, print the mtime of the *newest* file.
+#
+
+undef $now;
+
+foreach $file ( @ARGV ) {
+ ($dev,$ino,$mode,$nlink,$uid,$gid,$rdev,$size,$atime,$mtime,
+ $ctime,$blksize,$blocks) = stat($file);
+ if ( !defined($now) || $now < $mtime ) {
+ $now = $mtime;
+ }
+}
+
+if ( !defined($now) ) {
+ $now = time;
+}
+
+printf "0x%08x\n", $now;
diff --git a/core/parsecmd.inc b/core/parsecmd.inc
new file mode 100644
index 00000000..8e648699
--- /dev/null
+++ b/core/parsecmd.inc
@@ -0,0 +1,120 @@
+;; -----------------------------------------------------------------------
+;;
+;; Copyright 1994-2008 H. Peter Anvin - All Rights Reserved
+;;
+;; This program is free software; you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation, Inc., 53 Temple Place Ste 330,
+;; Boston MA 02111-1307, USA; either version 2 of the License, or
+;; (at your option) any later version; incorporated herein by reference.
+;;
+;; -----------------------------------------------------------------------
+
+;;
+;; parsecmd.inc
+;;
+;; Command line parser code
+;;
+
+ section .text
+
+; -------------------------------------------------------------------------
+; getcommand: Get a keyword from the current "getc" file and match it
+; against a list of keywords (keywd_table). Each entry in
+; that table should have the following form:
+; <32 bit hash value> <16 bit handler offset>
+;
+; The handler is called, and upon return this function
+; returns with CF = 0. On EOF, this function returns
+; with CF = 1.
+; -------------------------------------------------------------------------
+
+getcommand:
+.find:
+ call skipspace ; Skip leading whitespace
+ jz .eof ; End of file
+ jc .find ; End of line: try again
+
+ ; Do this explicitly so #foo is treated as a comment
+ cmp al,'#' ; Leading hash mark -> comment
+ je .skipline
+
+ or al,20h ; Convert to lower case
+ movzx ebx,al ; Hash for a one-char keyword
+.read_loop:
+ push ebx
+ call getc
+ pop ebx
+ jc .eof
+ cmp al,' ' ; Whitespace
+ jbe .done
+ or al,20h
+ rol ebx,5
+ xor bl,al
+ jmp short .read_loop
+.done: call ungetc
+ call skipspace
+ jz .eof
+ jc .noparm
+ call ungetc ; Return nonwhitespace char to buf
+ mov si,keywd_table
+ mov cx,keywd_count
+.table_search:
+ lodsd
+ cmp ebx,eax
+ je .found_keywd
+ lodsd ; Skip entrypoint/argument
+ loop .table_search
+
+ ; Otherwise unrecognized keyword
+ mov si,err_badcfg
+ jmp short .error
+
+ ; No parameter
+.noparm:
+ mov si,err_noparm
+ mov al,10 ; Already at EOL
+.error:
+ call cwritestr
+ jmp short .skipline
+
+.found_keywd: lodsw ; Load argument into ax
+ call [si]
+ clc
+ ret
+
+.eof: stc
+ ret
+
+.skipline: cmp al,10 ; Search for LF
+ je .find
+ call getc
+ jc .eof
+ jmp short .skipline
+
+ section .data
+err_badcfg db 'Unknown keyword in configuration file.', CR, LF, 0
+err_noparm db 'Missing parameter in configuration file.', CR, LF, 0
+
+ section .uibss
+ alignb 4
+vk_size equ (vk_end + 3) & ~3
+VKernelBuf: resb vk_size ; "Current" vkernel
+AppendBuf resb max_cmd_len+1 ; append=
+Ontimeout resb max_cmd_len+1 ; ontimeout
+Onerror resb max_cmd_len+1 ; onerror
+KbdMap resb 256 ; Keyboard map
+FKeyName resb MAX_FKEYS*FILENAME_MAX ; File names for F-key help
+KernelCNameLen resw 1 ; Length of unmangled kernel name
+InitRDCNameLen resw 1 ; Length of unmangled initrd name
+%if IS_SYSLINUX
+KernelName resb FILENAME_MAX+1 ; Mangled name for kernel
+KernelCName resb FILENAME_MAX+2 ; Unmangled kernel name
+InitRDCName resb FILENAME_MAX+2 ; Unmangled initrd name
+%else
+KernelName resb FILENAME_MAX ; Mangled name for kernel
+KernelCName resb FILENAME_MAX ; Unmangled kernel name
+InitRDCName resb FILENAME_MAX ; Unmangled initrd name
+%endif
+MNameBuf resb FILENAME_MAX
+InitRD resb FILENAME_MAX
diff --git a/core/parseconfig.inc b/core/parseconfig.inc
new file mode 100644
index 00000000..2ef9c3a2
--- /dev/null
+++ b/core/parseconfig.inc
@@ -0,0 +1,454 @@
+;; -----------------------------------------------------------------------
+;;
+;; Copyright 1994-2008 H. Peter Anvin - All Rights Reserved
+;;
+;; This program is free software; you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation, Inc., 53 Temple Place Ste 330,
+;; Boston MA 02111-1307, USA; either version 2 of the License, or
+;; (at your option) any later version; incorporated herein by reference.
+;;
+;; -----------------------------------------------------------------------
+
+;;
+;; parseconfig.inc
+;;
+;; Configuration file operations
+;;
+
+ section .text
+;
+; "default" command
+;
+pc_default: mov di,default_cmd
+ call getline
+ mov byte [di-1],0 ; null-terminate
+ ret
+
+;
+; "ontimeout" command
+;
+pc_ontimeout: mov di,Ontimeout
+ call getline
+ sub di,Ontimeout+1 ; Don't need final space
+ mov [OntimeoutLen],di
+ ret
+
+;
+; "onerror" command
+;
+pc_onerror: mov di,Onerror
+ call getline
+ sub di,Onerror
+ mov [OnerrorLen],di
+ ret
+
+;
+; "append" command
+;
+pc_append: cmp byte [VKernel],0
+ ja .vk
+ mov di,AppendBuf
+ call getline
+ sub di,AppendBuf
+.app1: mov [AppendLen],di
+ ret
+.vk: mov di,VKernelBuf+vk_append ; "append" command (vkernel)
+ call getline
+ sub di,VKernelBuf+vk_append
+ cmp di,byte 2
+ jne .app2
+ cmp byte [VKernelBuf+vk_append],'-'
+ jne .app2
+ xor di,di ; If "append -" -> null string
+.app2: mov [VKernelBuf+vk_appendlen],di
+ ret
+
+;
+; "ipappend" command (PXELINUX only)
+;
+%if IS_PXELINUX
+pc_ipappend: call getint
+ jc .err
+ cmp byte [VKernel],0
+ jne .vk
+ mov [IPAppend],bl
+.err: ret
+.vk: mov [VKernelBuf+vk_ipappend],bl
+ ret
+%endif
+
+;
+; "localboot" command (PXELINUX, ISOLINUX)
+;
+%if HAS_LOCALBOOT
+
+pc_localboot: call getint
+ cmp byte [VKernel],0 ; ("label" section only)
+ je .err
+ mov di,VKernelBuf+vk_rname
+ xor ax,ax
+ mov cx,FILENAME_MAX
+ rep stosb ; Null kernel name
+%if IS_PXELINUX
+ ; PXELINUX uses the first 4 bytes of vk_rname for the
+ ; mangled IP address
+ mov [VKernelBuf+vk_rname+5], bx ; Return type
+%else
+ mov [VKernelBuf+vk_rname+1], bx ; Return type
+%endif
+.err: ret
+
+%endif ; HAS_LOCALBOOT
+
+;
+; "kernel", "config", ... command
+;
+pc_kernel: cmp byte [VKernel],0
+ je .err ; ("label" section only)
+ mov [VKernelBuf+vk_type],al
+ call pc_getline
+ mov di,VKernelBuf+vk_rname
+ call mangle_name
+.err: ret
+
+;
+; "timeout", "totaltimeout" command
+;
+; N.B. 1/10 s ~ 1.D2162AABh clock ticks
+;
+pc_timeout: push ax
+ call getint
+ pop si
+ jc .err
+ mov eax,0D2162AABh
+ mul ebx ; clock ticks per 1/10 s
+ add ebx,edx
+ mov [si],ebx
+.err: ret
+
+
+;
+; "totaltimeout" command
+;
+pc_totaltimeout:
+
+;
+; Generic integer variable setting commands:
+; "prompt", "implicit"
+;
+pc_setint16:
+ push ax
+ call getint
+ pop si
+ jc .err
+ mov [si],bx
+.err: ret
+
+;
+; Generic file-processing commands:
+; "font", "kbdmap",
+;
+pc_filecmd: push ax ; Function to tailcall
+ call pc_getline
+ mov di,MNameBuf
+ call mangle_name
+ call searchdir
+ jnz .ok
+ pop ax ; Drop the successor function
+.ok: ret ; Tailcall if OK, error return
+
+;
+; Commands that expect the file to be opened on top of the getc stack.
+; "display", "include"
+;
+pc_opencmd: push ax ; Function to tailcall
+ call pc_getline
+ mov di,MNameBuf
+ call mangle_name
+ call open
+ jnz .ok
+ pop ax ; Drop the successor function
+.ok: ret ; Tailcall if OK, error return
+
+;
+; "include" command (invoked from pc_opencmd)
+;
+pc_include: inc word [IncludeLevel]
+.err: ret
+
+;
+; "serial" command
+;
+pc_serial: call getint
+ jc .err
+ push bx ; Serial port #
+ call skipspace
+ jnc .ok
+ pop bx
+.err: ret
+.ok:
+ call ungetc
+ call getint
+ mov [FlowControl], word 0 ; Default to no flow control
+ jc .nobaud
+.valid_baud:
+ push ebx
+ call skipspace
+ jc .no_flow
+ call ungetc
+ call getint ; Hardware flow control?
+ jnc .valid_flow
+.no_flow:
+ xor bx,bx ; Default -> no flow control
+.valid_flow:
+ and bh,0Fh ; FlowIgnore
+ shl bh,4
+ mov [FlowIgnore],bh
+ mov bh,bl
+ and bx,0F003h ; Valid bits
+ mov [FlowControl],bx
+ pop ebx ; Baud rate
+ jmp short .parse_baud
+.nobaud:
+ mov ebx,DEFAULT_BAUD ; No baud rate given
+.parse_baud:
+ pop di ; Serial port #
+ cmp ebx,byte 75
+ jb .err ; < 75 baud == bogus
+ mov eax,BAUD_DIVISOR
+ cdq
+ div ebx
+ mov [BaudDivisor],ax
+ push ax ; Baud rate divisor
+ cmp di,3
+ ja .port_is_io ; If port > 3 then port is I/O addr
+ shl di,1
+ mov di,[di+serial_base] ; Get the I/O port from the BIOS
+.port_is_io:
+ mov [SerialPort],di
+
+ ;
+ ; Begin code to actually set up the serial port
+ ;
+ lea dx,[di+3] ; DX -> LCR
+ mov al,83h ; Enable DLAB
+ call slow_out
+
+ pop ax ; Divisor
+ mov dx,di ; DX -> LS
+ call slow_out
+
+ inc dx ; DX -> MS
+ mov al,ah
+ call slow_out
+
+ mov al,03h ; Disable DLAB
+ inc dx ; DX -> LCR
+ inc dx
+ call slow_out
+
+ in al,dx ; Read back LCR (detect missing hw)
+ cmp al,03h ; If nothing here we'll read 00 or FF
+ jne .serial_port_bad ; Assume serial port busted
+ dec dx
+ dec dx ; DX -> IER
+ xor al,al ; IRQ disable
+ call slow_out
+
+ inc dx ; DX -> FCR/IIR
+ mov al,01h
+ call slow_out ; Enable FIFOs if present
+ in al,dx
+ cmp al,0C0h ; FIFOs enabled and usable?
+ jae .fifo_ok
+ xor ax,ax ; Disable FIFO if unusable
+ call slow_out
+.fifo_ok:
+
+ inc dx
+ inc dx ; DX -> MCR
+ in al,dx
+ or al,[FlowOutput] ; Assert bits
+ call slow_out
+
+ ; Show some life
+ cmp byte [SerialNotice],0
+ je .notfirst
+ mov byte [SerialNotice],0
+
+ mov si,syslinux_banner
+ call write_serial_str
+ mov si,copyright_str
+ call write_serial_str
+.notfirst:
+ ret
+
+.serial_port_bad:
+ mov [SerialPort], word 0
+ ret
+
+;
+; "F"-key command
+;
+pc_fkey: push ax
+ call pc_getline
+ pop di
+ call mangle_name ; Mangle file name
+ ret
+
+;
+; "label" command
+;
+pc_label: call commit_vk ; Commit any current vkernel
+ mov di,VKernelBuf ; Erase the vkernelbuf for better compression
+ mov cx,(vk_size >> 1)
+ xor ax,ax
+ rep stosw
+ call pc_getline
+ mov di,VKernelBuf+vk_vname
+ call mangle_name ; Mangle virtual name
+ mov byte [VKernel],1 ; We've seen a "label" statement
+ mov si,VKernelBuf+vk_vname ; By default, rname == vname
+ ; mov di,VKernelBuf+vk_rname ; -- already set
+ mov cx,FILENAME_MAX
+ rep movsb
+ mov si,AppendBuf ; Default append==global append
+ mov di,VKernelBuf+vk_append
+ mov cx,[AppendLen]
+ mov [VKernelBuf+vk_appendlen],cx
+ rep movsb
+%if IS_PXELINUX ; PXELINUX only
+ mov al,[IPAppend] ; Default ipappend==global ipappend
+ mov [VKernelBuf+vk_ipappend],al
+%endif
+ ret
+
+;
+; "say" command
+;
+pc_say: call pc_getline ; "say" command
+ call writestr
+ jmp crlf ; tailcall
+
+;
+; "text" command; ignore everything until we get an "endtext" line
+;
+pc_text: call pc_getline ; Ignore rest of line
+.loop:
+ call pc_getline
+ jc .eof
+
+ ; Leading spaces are already removed...
+ lodsd
+ and eax,0xdfdfdfdf ; Upper case
+ cmp eax,'ENDT'
+ jne .loop
+ lodsd
+ and eax,0x00dfdfdf ; Upper case and mask
+ cmp eax,'EXT'
+ jne .loop
+ ; If we get here we hit ENDTEXT
+.eof:
+ ret
+
+;
+; Comment line
+;
+pc_comment: ; Fall into pc_getline
+
+;
+; Common subroutine: load line into trackbuf; returns with SI -> trackbuf
+; CF is set on EOF.
+;
+pc_getline: mov di,trackbuf
+ push di
+ call getline
+ mov byte [di],0 ; Null-terminate
+ pop si
+ ret
+
+;
+; Main loop for configuration file parsing
+;
+parse_config:
+ mov di,VKernelBuf ; Clear VKernelBuf at start
+ xor ax,ax
+ mov cx,vk_size
+ rep stosb
+
+.again:
+ call getcommand ; Parse one command
+ jnc .again ; If not EOF...
+ call close
+ dec word [IncludeLevel] ; Still parsing?
+ jnz .again
+
+ ;
+ ; The fall through to commit_vk to commit any final
+ ; VKernel being read
+ ;
+;
+; commit_vk: Store the current VKernelBuf into buffer segment
+;
+commit_vk:
+ ; For better compression, clean up the append field
+ mov ax,[VKernelBuf+vk_appendlen]
+ mov di,VKernelBuf+vk_append
+ add di,ax
+ mov cx,max_cmd_len+1
+ sub cx,ax
+ xor ax,ax
+ rep stosb
+
+ ; Pack into high memory
+ mov si,VKernelBuf
+ mov edi,[VKernelEnd]
+ mov cx,vk_size
+ call rllpack
+ mov [VKernelEnd],edi
+ ret
+.overflow:
+ mov si,vk_overflow_msg
+ call writestr
+ ret
+
+ section .data
+vk_overflow_msg db 'Out of memory parsing config file', CR, LF, 0
+SerialNotice db 1 ; Only print this once
+
+ section .bss
+ alignb 4
+VKernelEnd resd 1 ; Lowest high memory address used
+
+ ; This symbol should be used by loaders to indicate
+ ; the highest address *they* are allowed to use.
+HighMemRsvd equ VKernelEnd
+ ; by vkernels
+ section .config
+ align 4, db 0
+KbdTimeout dd 0 ; Keyboard timeout (if any)
+TotalTimeout dd 0 ; Total timeout (if any)
+AppendLen dw 0 ; Bytes in append= command
+OntimeoutLen dw 0 ; Bytes in ontimeout command
+OnerrorLen dw 0 ; Bytes in onerror command
+CmdLinePtr dw cmd_line_here ; Command line advancing pointer
+ForcePrompt dw 0 ; Force prompt
+NoEscape dw 0 ; No escape
+AllowImplicit dw 1 ; Allow implicit kernels
+AllowOptions dw 1 ; User-specified options allowed
+IncludeLevel dw 1 ; Nesting level
+SerialPort dw 0 ; Serial port base (or 0 for no serial port)
+VKernel db 0 ; Have we seen any "label" statements?
+
+%if IS_PXELINUX
+IPAppend db 0 ; Default IPAPPEND option
+%endif
+
+ section .uibss
+ alignb 4 ; For the good of REP MOVSD
+command_line resb max_cmd_len+2 ; Command line buffer
+ alignb 4
+default_cmd resb max_cmd_len+1 ; "default" command line
+
+%include "rllpack.inc"
diff --git a/core/plaincon.inc b/core/plaincon.inc
new file mode 100644
index 00000000..59b2cbb5
--- /dev/null
+++ b/core/plaincon.inc
@@ -0,0 +1,24 @@
+;
+; writechr: Write a single character in AL to the console without
+; mangling any registers; handle video pages correctly.
+;
+ section .text
+
+writechr:
+ call write_serial ; write to serial port if needed
+ pushfd
+ test byte [cs:UsingVGA], 08h
+ jz .videook
+ call vgaclearmode
+.videook:
+ test byte [cs:DisplayCon], 01h
+ jz .nothing
+ pushad
+ mov ah,0Eh
+ mov bl,07h ; attribute
+ mov bh,[cs:BIOS_page] ; current page
+ int 10h
+ popad
+.nothing:
+ popfd
+ ret
diff --git a/core/pxe.inc b/core/pxe.inc
new file mode 100644
index 00000000..7471c4f0
--- /dev/null
+++ b/core/pxe.inc
@@ -0,0 +1,154 @@
+;; -----------------------------------------------------------------------
+;;
+;; Copyright 1999-2008 H. Peter Anvin - All Rights Reserved
+;;
+;; This program is free software; you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation, Inc., 53 Temple Place Ste 330,
+;; Boston MA 02111-1307, USA; either version 2 of the License, or
+;; (at your option) any later version; incorporated herein by reference.
+;;
+;; -----------------------------------------------------------------------
+
+;;
+;; pxe.inc
+;;
+;; PXE opcodes
+;;
+
+%ifndef _PXE_INC
+%define _PXE_INC 1
+
+%define PXENV_TFTP_OPEN 0x0020
+%define PXENV_TFTP_CLOSE 0x0021
+%define PXENV_TFTP_READ 0x0022
+%define PXENV_TFTP_READ_FILE 0x0023
+%define PXENV_TFTP_READ_FILE_PMODE 0x0024
+%define PXENV_TFTP_GET_FSIZE 0x0025
+
+%define PXENV_UDP_OPEN 0x0030
+%define PXENV_UDP_CLOSE 0x0031
+%define PXENV_UDP_READ 0x0032
+%define PXENV_UDP_WRITE 0x0033
+
+%define PXENV_START_UNDI 0x0000
+%define PXENV_UNDI_STARTUP 0x0001
+%define PXENV_UNDI_CLEANUP 0x0002
+%define PXENV_UNDI_INITIALIZE 0x0003
+%define PXENV_UNDI_RESET_NIC 0x0004
+%define PXENV_UNDI_SHUTDOWN 0x0005
+%define PXENV_UNDI_OPEN 0x0006
+%define PXENV_UNDI_CLOSE 0x0007
+%define PXENV_UNDI_TRANSMIT 0x0008
+%define PXENV_UNDI_SET_MCAST_ADDR 0x0009
+%define PXENV_UNDI_SET_STATION_ADDR 0x000A
+%define PXENV_UNDI_SET_PACKET_FILTER 0x000B
+%define PXENV_UNDI_GET_INFORMATION 0x000C
+%define PXENV_UNDI_GET_STATISTICS 0x000D
+%define PXENV_UNDI_CLEAR_STATISTICS 0x000E
+%define PXENV_UNDI_INITIATE_DIAGS 0x000F
+%define PXENV_UNDI_FORCE_INTERRUPT 0x0010
+%define PXENV_UNDI_GET_MCAST_ADDR 0x0011
+%define PXENV_UNDI_GET_NIC_TYPE 0x0012
+%define PXENV_UNDI_GET_IFACE_INFO 0x0013
+%define PXENV_UNDI_ISR 0x0014
+%define PXENV_STOP_UNDI 0x0015 ; Overlap...?
+%define PXENV_UNDI_GET_STATE 0x0015 ; Overlap...?
+
+%define PXENV_UNLOAD_STACK 0x0070
+%define PXENV_GET_CACHED_INFO 0x0071
+%define PXENV_RESTART_DHCP 0x0072
+%define PXENV_RESTART_TFTP 0x0073
+%define PXENV_MODE_SWITCH 0x0074
+%define PXENV_START_BASE 0x0075
+%define PXENV_STOP_BASE 0x0076
+
+; gPXE extensions...
+%define PXENV_FILE_OPEN 0x00e0
+%define PXENV_FILE_CLOSE 0x00e1
+%define PXENV_FILE_SELECT 0x00e2
+%define PXENV_FILE_READ 0x00e3
+%define PXENV_GET_FILE_SIZE 0x00e4
+%define PXENV_FILE_EXEC 0x00e5
+%define PXENV_FILE_API_CHECK 0x00e6
+
+; Exit codes
+%define PXENV_EXIT_SUCCESS 0x0000
+%define PXENV_EXIT_FAILURE 0x0001
+
+; Status codes
+%define PXENV_STATUS_SUCCESS 0x00
+%define PXENV_STATUS_FAILURE 0x01
+%define PXENV_STATUS_BAD_FUNC 0x02
+%define PXENV_STATUS_UNSUPPORTED 0x03
+%define PXENV_STATUS_KEEP_UNDI 0x04
+%define PXENV_STATUS_KEEP_ALL 0x05
+%define PXENV_STATUS_OUT_OF_RESOURCES 0x06
+%define PXENV_STATUS_ARP_TIMEOUT 0x11
+%define PXENV_STATUS_UDP_CLOSED 0x18
+%define PXENV_STATUS_UDP_OPEN 0x19
+%define PXENV_STATUS_TFTP_CLOSED 0x1a
+%define PXENV_STATUS_TFTP_OPEN 0x1b
+%define PXENV_STATUS_MCOPY_PROBLEM 0x20
+%define PXENV_STATUS_BIS_INTEGRITY_FAILURE 0x21
+%define PXENV_STATUS_BIS_VALIDATE_FAILURE 0x22
+%define PXENV_STATUS_BIS_INIT_FAILURE 0x23
+%define PXENV_STATUS_BIS_SHUTDOWN_FAILURE 0x24
+%define PXENV_STATUS_BIS_GBOA_FAILURE 0x25
+%define PXENV_STATUS_BIS_FREE_FAILURE 0x26
+%define PXENV_STATUS_BIS_GSI_FAILURE 0x27
+%define PXENV_STATUS_BIS_BAD_CKSUM 0x28
+%define PXENV_STATUS_TFTP_CANNOT_ARP_ADDRESS 0x30
+%define PXENV_STATUS_TFTP_OPEN_TIMEOUT 0x32
+
+%define PXENV_STATUS_TFTP_UNKNOWN_OPCODE 0x33
+%define PXENV_STATUS_TFTP_READ_TIMEOUT 0x35
+%define PXENV_STATUS_TFTP_ERROR_OPCODE 0x36
+%define PXENV_STATUS_TFTP_CANNOT_OPEN_CONNECTION 0x38
+%define PXENV_STATUS_TFTP_CANNOT_READ_FROM_CONNECTION 0x39
+%define PXENV_STATUS_TFTP_TOO_MANY_PACKAGES 0x3a
+%define PXENV_STATUS_TFTP_FILE_NOT_FOUND 0x3b
+%define PXENV_STATUS_TFTP_ACCESS_VIOLATION 0x3c
+%define PXENV_STATUS_TFTP_NO_MCAST_ADDRESS 0x3d
+%define PXENV_STATUS_TFTP_NO_FILESIZE 0x3e
+%define PXENV_STATUS_TFTP_INVALID_PACKET_SIZE 0x3f
+%define PXENV_STATUS_DHCP_TIMEOUT 0x51
+%define PXENV_STATUS_DHCP_NO_IP_ADDRESS 0x52
+%define PXENV_STATUS_DHCP_NO_BOOTFILE_NAME 0x53
+%define PXENV_STATUS_DHCP_BAD_IP_ADDRESS 0x54
+%define PXENV_STATUS_UNDI_INVALID_FUNCTION 0x60
+%define PXENV_STATUS_UNDI_MEDIATEST_FAILED 0x61
+%define PXENV_STATUS_UNDI_CANNOT_INIT_NIC_FOR_MCAST 0x62
+%define PXENV_STATUS_UNDI_CANNOT_INITIALIZE_NIC 0x63
+%define PXENV_STATUS_UNDI_CANNOT_INITIALIZE_PHY 0x64
+%define PXENV_STATUS_UNDI_CANNOT_READ_CONFIG_DATA 0x65
+%define PXENV_STATUS_UNDI_CANNOT_READ_INIT_DATA 0x66
+%define PXENV_STATUS_UNDI_BAD_MAC_ADDRESS 0x67
+%define PXENV_STATUS_UNDI_BAD_EEPROM_CHECKSUM 0x68
+%define PXENV_STATUS_UNDI_ERROR_SETTING_ISR 0x69
+%define PXENV_STATUS_UNDI_INVALID_STATE 0x6a
+%define PXENV_STATUS_UNDI_TRANSMIT_ERROR 0x6b
+%define PXENV_STATUS_UNDI_INVALID_PARAMETER 0x6c
+%define PXENV_STATUS_BSTRAP_PROMPT_MENU 0x74
+%define PXENV_STATUS_BSTRAP_MCAST_ADDR 0x76
+%define PXENV_STATUS_BSTRAP_MISSING_LIST 0x77
+%define PXENV_STATUS_BSTRAP_NO_RESPONSE 0x78
+%define PXENV_STATUS_BSTRAP_FILE_TOO_BIG 0x79
+%define PXENV_STATUS_BINL_CANCELED_BY_KEYSTROKE 0xa0
+%define PXENV_STATUS_BINL_NO_PXE_SERVER 0xa1
+%define PXENV_STATUS_NOT_AVAILABLE_IN_PMODE 0xa2
+%define PXENV_STATUS_NOT_AVAILABLE_IN_RMODE 0xa3
+%define PXENV_STATUS_BUSD_DEVICE_NOT_SUPPORTED 0xb0
+%define PXENV_STATUS_LOADER_NO_FREE_BASE_MEMORY 0xc0
+%define PXENV_STATUS_LOADER_NO_BC_ROMID 0xc1
+%define PXENV_STATUS_LOADER_BAD_BC_ROMID 0xc2
+%define PXENV_STATUS_LOADER_BAD_BC_RUNTIME_IMAGE 0xc3
+%define PXENV_STATUS_LOADER_NO_UNDI_ROMID 0xc4
+%define PXENV_STATUS_LOADER_BAD_UNDI_ROMID 0xc5
+%define PXENV_STATUS_LOADER_BAD_UNDI_DRIVER_IMAGE 0xc6
+%define PXENV_STATUS_LOADER_NO_PXE_STRUCT 0xc8
+%define PXENV_STATUS_LOADER_NO_PXENV_STRUCT 0xc9
+%define PXENV_STATUS_LOADER_UNDI_START 0xca
+%define PXENV_STATUS_LOADER_BC_START 0xcb
+
+%endif ; _PXE_INC
diff --git a/core/pxelinux.asm b/core/pxelinux.asm
new file mode 100644
index 00000000..ce3250bc
--- /dev/null
+++ b/core/pxelinux.asm
@@ -0,0 +1,2897 @@
+; -*- fundamental -*- (asm-mode sucks)
+; ****************************************************************************
+;
+; pxelinux.asm
+;
+; A program to boot Linux kernels off a TFTP server using the Intel PXE
+; network booting API. It is based on the SYSLINUX boot loader for
+; MS-DOS floppies.
+;
+; Copyright 1994-2008 H. Peter Anvin - All Rights Reserved
+;
+; This program is free software; you can redistribute it and/or modify
+; it under the terms of the GNU General Public License as published by
+; the Free Software Foundation, Inc., 53 Temple Place Ste 330,
+; Boston MA 02111-1307, USA; either version 2 of the License, or
+; (at your option) any later version; incorporated herein by reference.
+;
+; ****************************************************************************
+
+%define IS_PXELINUX 1
+%include "head.inc"
+%include "pxe.inc"
+
+; gPXE extensions support
+%define GPXE 1
+
+;
+; Some semi-configurable constants... change on your own risk.
+;
+my_id equ pxelinux_id
+FILENAME_MAX_LG2 equ 7 ; log2(Max filename size Including final null)
+FILENAME_MAX equ (1 << FILENAME_MAX_LG2)
+NULLFILE equ 0 ; Zero byte == null file name
+NULLOFFSET equ 4 ; Position in which to look
+REBOOT_TIME equ 5*60 ; If failure, time until full reset
+%assign HIGHMEM_SLOP 128*1024 ; Avoid this much memory near the top
+MAX_OPEN_LG2 equ 5 ; log2(Max number of open sockets)
+MAX_OPEN equ (1 << MAX_OPEN_LG2)
+PKTBUF_SIZE equ (65536/MAX_OPEN) ; Per-socket packet buffer size
+TFTP_PORT equ htons(69) ; Default TFTP port
+PKT_RETRY equ 6 ; Packet transmit retry count
+PKT_TIMEOUT equ 12 ; Initial timeout, timer ticks @ 55 ms
+; Desired TFTP block size
+; For Ethernet MTU is normally 1500. Unfortunately there seems to
+; be a fair number of networks with "substandard" MTUs which break.
+; The code assumes TFTP_LARGEBLK <= 2K.
+TFTP_MTU equ 1440
+TFTP_LARGEBLK equ (TFTP_MTU-20-8-4) ; MTU - IP hdr - UDP hdr - TFTP hdr
+; Standard TFTP block size
+TFTP_BLOCKSIZE_LG2 equ 9 ; log2(bytes/block)
+TFTP_BLOCKSIZE equ (1 << TFTP_BLOCKSIZE_LG2)
+%assign USE_PXE_PROVIDED_STACK 1 ; Use stack provided by PXE?
+
+SECTOR_SHIFT equ TFTP_BLOCKSIZE_LG2
+SECTOR_SIZE equ TFTP_BLOCKSIZE
+
+;
+; This is what we need to do when idle
+; *** This is disabled because some PXE stacks wait for unacceptably
+; *** long if there are no packets receivable.
+
+%define HAVE_IDLE 0 ; idle is not a noop
+
+%if HAVE_IDLE
+%macro RESET_IDLE 0
+ call reset_idle
+%endmacro
+%macro DO_IDLE 0
+ call check_for_arp
+%endmacro
+%else
+%macro RESET_IDLE 0
+ ; Nothing
+%endmacro
+%macro DO_IDLE 0
+ ; Nothing
+%endmacro
+%endif
+
+;
+; TFTP operation codes
+;
+TFTP_RRQ equ htons(1) ; Read request
+TFTP_WRQ equ htons(2) ; Write request
+TFTP_DATA equ htons(3) ; Data packet
+TFTP_ACK equ htons(4) ; ACK packet
+TFTP_ERROR equ htons(5) ; ERROR packet
+TFTP_OACK equ htons(6) ; OACK packet
+
+;
+; TFTP error codes
+;
+TFTP_EUNDEF equ htons(0) ; Unspecified error
+TFTP_ENOTFOUND equ htons(1) ; File not found
+TFTP_EACCESS equ htons(2) ; Access violation
+TFTP_ENOSPACE equ htons(3) ; Disk full
+TFTP_EBADOP equ htons(4) ; Invalid TFTP operation
+TFTP_EBADID equ htons(5) ; Unknown transfer
+TFTP_EEXISTS equ htons(6) ; File exists
+TFTP_ENOUSER equ htons(7) ; No such user
+TFTP_EOPTNEG equ htons(8) ; Option negotiation failure
+
+;
+; The following structure is used for "virtual kernels"; i.e. LILO-style
+; option labels. The options we permit here are `kernel' and `append
+; Since there is no room in the bottom 64K for all of these, we
+; stick them in high memory and copy them down before we need them.
+;
+ struc vkernel
+vk_vname: resb FILENAME_MAX ; Virtual name **MUST BE FIRST!**
+vk_rname: resb FILENAME_MAX ; Real name
+vk_ipappend: resb 1 ; "IPAPPEND" flag
+vk_type: resb 1 ; Type of file
+vk_appendlen: resw 1
+ alignb 4
+vk_append: resb max_cmd_len+1 ; Command line
+ alignb 4
+vk_end: equ $ ; Should be <= vk_size
+ endstruc
+
+;
+; Segment assignments in the bottom 640K
+; 0000h - main code/data segment (and BIOS segment)
+;
+real_mode_seg equ 3000h
+pktbuf_seg equ 2000h ; Packet buffers segments
+xfer_buf_seg equ 1000h ; Bounce buffer for I/O to high mem
+comboot_seg equ real_mode_seg ; COMBOOT image loading zone
+
+;
+; BOOTP/DHCP packet pattern
+;
+ struc bootp_t
+bootp:
+.opcode resb 1 ; BOOTP/DHCP "opcode"
+.hardware resb 1 ; ARP hardware type
+.hardlen resb 1 ; Hardware address length
+.gatehops resb 1 ; Used by forwarders
+.ident resd 1 ; Transaction ID
+.seconds resw 1 ; Seconds elapsed
+.flags resw 1 ; Broadcast flags
+.cip resd 1 ; Client IP
+.yip resd 1 ; "Your" IP
+.sip resd 1 ; Next server IP
+.gip resd 1 ; Relay agent IP
+.macaddr resb 16 ; Client MAC address
+.sname resb 64 ; Server name (optional)
+.bootfile resb 128 ; Boot file name
+.option_magic resd 1 ; Vendor option magic cookie
+.options resb 1260 ; Vendor options
+ endstruc
+
+BOOTP_OPTION_MAGIC equ htonl(0x63825363) ; See RFC 2132
+
+;
+; TFTP connection data structure. Each one of these corresponds to a local
+; UDP port. The size of this structure must be a power of 2.
+; HBO = host byte order; NBO = network byte order
+; (*) = written by options negotiation code, must be dword sized
+;
+; For a gPXE connection, we set the local port number to -1 and the
+; remote port number contains the gPXE file handle.
+;
+ struc open_file_t
+tftp_localport resw 1 ; Local port number (0 = not in use)
+tftp_remoteport resw 1 ; Remote port number
+tftp_remoteip resd 1 ; Remote IP address
+tftp_filepos resd 1 ; Bytes downloaded (including buffer)
+tftp_filesize resd 1 ; Total file size(*)
+tftp_blksize resd 1 ; Block size for this connection(*)
+tftp_bytesleft resw 1 ; Unclaimed data bytes
+tftp_lastpkt resw 1 ; Sequence number of last packet (NBO)
+tftp_dataptr resw 1 ; Pointer to available data
+tftp_goteof resb 1 ; 1 if the EOF packet received
+ resb 3 ; Currently unusued
+ ; At end since it should not be zeroed on socked close
+tftp_pktbuf resw 1 ; Packet buffer offset
+ endstruc
+%ifndef DEPEND
+%if (open_file_t_size & (open_file_t_size-1))
+%error "open_file_t is not a power of 2"
+%endif
+%endif
+
+; ---------------------------------------------------------------------------
+; BEGIN CODE
+; ---------------------------------------------------------------------------
+
+;
+; Memory below this point is reserved for the BIOS and the MBR
+;
+ section .earlybss
+trackbufsize equ 8192
+trackbuf resb trackbufsize ; Track buffer goes here
+ ; ends at 2800h
+
+ alignb open_file_t_size
+Files resb MAX_OPEN*open_file_t_size
+
+ alignb FILENAME_MAX
+BootFile resb 256 ; Boot file from DHCP packet
+PathPrefix resb 256 ; Path prefix derived from boot file
+DotQuadBuf resb 16 ; Buffer for dotted-quad IP address
+IPOption resb 80 ; ip= option buffer
+InitStack resd 1 ; Pointer to reset stack (SS:SP)
+PXEStack resd 1 ; Saved stack during PXE call
+
+ section .bss
+ alignb 4
+RebootTime resd 1 ; Reboot timeout, if set by option
+StrucPtr resd 1 ; Pointer to PXENV+ or !PXE structure
+APIVer resw 1 ; PXE API version found
+IPOptionLen resw 1 ; Length of IPOption
+IdleTimer resw 1 ; Time to check for ARP?
+LocalBootType resw 1 ; Local boot return code
+PktTimeout resw 1 ; Timeout for current packet
+RealBaseMem resw 1 ; Amount of DOS memory after freeing
+OverLoad resb 1 ; Set if DHCP packet uses "overloading"
+DHCPMagic resb 1 ; PXELINUX magic flags
+
+; The relative position of these fields matter!
+MAC_MAX equ 32 ; Handle hardware addresses this long
+MACLen resb 1 ; MAC address len
+MACType resb 1 ; MAC address type
+MAC resb MAC_MAX+1 ; Actual MAC address
+BOOTIFStr resb 7 ; Space for "BOOTIF="
+MACStr resb 3*(MAC_MAX+1) ; MAC address as a string
+
+; The relative position of these fields matter!
+UUIDType resb 1 ; Type byte from DHCP option
+UUID resb 16 ; UUID, from the PXE stack
+UUIDNull resb 1 ; dhcp_copyoption zero-terminates
+
+;
+; PXE packets which don't need static initialization
+;
+ alignb 4
+pxe_unload_stack_pkt:
+.status: resw 1 ; Status
+.reserved: resw 10 ; Reserved
+pxe_unload_stack_pkt_len equ $-pxe_unload_stack_pkt
+
+ alignb 16
+ ; BOOTP/DHCP packet buffer
+
+ section .bss2
+ alignb 16
+packet_buf resb 2048 ; Transfer packet
+packet_buf_size equ $-packet_buf
+
+ section .text
+ ;
+ ; PXELINUX needs more BSS than the other derivatives;
+ ; therefore we relocate it from 7C00h on startup.
+ ;
+StackBuf equ $ ; Base of stack if we use our own
+
+;
+; Primary entry point.
+;
+bootsec equ $
+_start:
+ pushfd ; Paranoia... in case of return to PXE
+ pushad ; ... save as much state as possible
+ push ds
+ push es
+ push fs
+ push gs
+
+ xor ax,ax
+ mov ds,ax
+ mov es,ax
+
+ jmp 0:_start1 ; Canonicalize address
+_start1:
+ mov bp,sp
+ les bx,[bp+48] ; ES:BX -> !PXE or PXENV+ structure
+
+ ; That is all pushed onto the PXE stack. Save the pointer
+ ; to it and switch to an internal stack.
+ mov [InitStack],sp
+ mov [InitStack+2],ss
+
+%if USE_PXE_PROVIDED_STACK
+ ; Apparently some platforms go bonkers if we
+ ; set up our own stack...
+ mov [BaseStack],sp
+ mov [BaseStack+4],ss
+%endif
+
+ cli ; Paranoia
+ lss esp,[BaseStack]
+
+ sti ; Stack set up and ready
+ cld ; Copy upwards
+
+;
+; Initialize screen (if we're using one)
+;
+ push es ; Save ES -> PXE entry structure
+ push ds
+ pop es ; ES <- DS
+%include "init.inc"
+ pop es ; Restore ES -> PXE entry structure
+;
+; Tell the user we got this far
+;
+ mov si,syslinux_banner
+ call writestr
+
+ mov si,copyright_str
+ call writestr
+
+;
+; Assume API version 2.1, in case we find the !PXE structure without
+; finding the PXENV+ structure. This should really look at the Base
+; Code ROM ID structure in have_pxe, but this is adequate for now --
+; if we have !PXE, we have to be 2.1 or higher, and we don't care
+; about higher versions than that.
+;
+ mov word [APIVer],0201h
+
+;
+; Now we need to find the !PXE structure. It's *supposed* to be pointed
+; to by SS:[SP+4], but support INT 1Ah, AX=5650h method as well.
+; FIX: ES:BX should point to the PXENV+ structure on entry as well.
+; We should make that the second test, and not trash ES:BX...
+;
+ cmp dword [es:bx], '!PXE'
+ je have_pxe
+
+ ; Uh-oh, not there... try plan B
+ mov ax, 5650h
+%if USE_PXE_PROVIDED_STACK == 0
+ lss sp,[InitStack]
+%endif
+ int 1Ah ; May trash regs
+%if USE_PXE_PROVIDED_STACK == 0
+ lss esp,[BaseStack]
+%endif
+
+ jc no_pxe
+ cmp ax,564Eh
+ jne no_pxe
+
+ ; Okay, that gave us the PXENV+ structure, find !PXE
+ ; structure from that (if available)
+ cmp dword [es:bx], 'PXEN'
+ jne no_pxe
+ cmp word [es:bx+4], 'V+'
+ je have_pxenv
+
+ ; Nothing there either. Last-ditch: scan memory
+ call memory_scan_for_pxe_struct ; !PXE scan
+ jnc have_pxe
+ call memory_scan_for_pxenv_struct ; PXENV+ scan
+ jnc have_pxenv
+
+no_pxe: mov si,err_nopxe
+ call writestr
+ jmp kaboom
+
+have_pxenv:
+ mov [StrucPtr],bx
+ mov [StrucPtr+2],es
+
+ mov si,found_pxenv
+ call writestr
+
+ mov si,apiver_str
+ call writestr
+ mov ax,[es:bx+6]
+ mov [APIVer],ax
+ call writehex4
+ call crlf
+
+ cmp ax,0201h ; API version 2.1 or higher
+ jb old_api
+ mov si,bx
+ mov ax,es
+ les bx,[es:bx+28h] ; !PXE structure pointer
+ cmp dword [es:bx],'!PXE'
+ je have_pxe
+
+ ; Nope, !PXE structure missing despite API 2.1+, or at least
+ ; the pointer is missing. Do a last-ditch attempt to find it.
+ call memory_scan_for_pxe_struct
+ jnc have_pxe
+
+ ; Otherwise, no dice, use PXENV+ structure
+ mov bx,si
+ mov es,ax
+
+old_api: ; Need to use a PXENV+ structure
+ mov si,using_pxenv_msg
+ call writestr
+
+ mov eax,[es:bx+0Ah] ; PXE RM API
+ mov [PXEEntry],eax
+
+ mov si,undi_data_msg
+ call writestr
+ mov ax,[es:bx+20h]
+ call writehex4
+ call crlf
+ mov si,undi_data_len_msg
+ call writestr
+ mov ax,[es:bx+22h]
+ call writehex4
+ call crlf
+ mov si,undi_code_msg
+ call writestr
+ mov ax,[es:bx+24h]
+ call writehex4
+ call crlf
+ mov si,undi_code_len_msg
+ call writestr
+ mov ax,[es:bx+26h]
+ call writehex4
+ call crlf
+
+ ; Compute base memory size from PXENV+ structure
+ xor esi,esi
+ movzx eax,word [es:bx+20h] ; UNDI data seg
+ cmp ax,[es:bx+24h] ; UNDI code seg
+ ja .use_data
+ mov ax,[es:bx+24h]
+ mov si,[es:bx+26h]
+ jmp short .combine
+.use_data:
+ mov si,[es:bx+22h]
+.combine:
+ shl eax,4
+ add eax,esi
+ shr eax,10 ; Convert to kilobytes
+ mov [RealBaseMem],ax
+
+ mov si,pxenventry_msg
+ call writestr
+ mov ax,[PXEEntry+2]
+ call writehex4
+ mov al,':'
+ call writechr
+ mov ax,[PXEEntry]
+ call writehex4
+ call crlf
+ jmp have_entrypoint
+
+have_pxe:
+ mov [StrucPtr],bx
+ mov [StrucPtr+2],es
+
+ mov eax,[es:bx+10h]
+ mov [PXEEntry],eax
+
+ mov si,undi_data_msg
+ call writestr
+ mov eax,[es:bx+2Ah]
+ call writehex8
+ call crlf
+ mov si,undi_data_len_msg
+ call writestr
+ mov ax,[es:bx+2Eh]
+ call writehex4
+ call crlf
+ mov si,undi_code_msg
+ call writestr
+ mov ax,[es:bx+32h]
+ call writehex8
+ call crlf
+ mov si,undi_code_len_msg
+ call writestr
+ mov ax,[es:bx+36h]
+ call writehex4
+ call crlf
+
+ ; Compute base memory size from !PXE structure
+ xor esi,esi
+ mov eax,[es:bx+2Ah]
+ cmp eax,[es:bx+32h]
+ ja .use_data
+ mov eax,[es:bx+32h]
+ mov si,[es:bx+36h]
+ jmp short .combine
+.use_data:
+ mov si,[es:bx+2Eh]
+.combine:
+ add eax,esi
+ shr eax,10
+ mov [RealBaseMem],ax
+
+ mov si,pxeentry_msg
+ call writestr
+ mov ax,[PXEEntry+2]
+ call writehex4
+ mov al,':'
+ call writechr
+ mov ax,[PXEEntry]
+ call writehex4
+ call crlf
+
+have_entrypoint:
+ push cs
+ pop es ; Restore CS == DS == ES
+
+;
+; Network-specific initialization
+;
+ xor ax,ax
+ mov [LocalDomain],al ; No LocalDomain received
+
+;
+; The DHCP client identifiers are best gotten from the DHCPREQUEST
+; packet (query info 1).
+;
+query_bootp_1:
+ mov dl,1
+ call pxe_get_cached_info
+ call parse_dhcp
+
+ ; We don't use flags from the request packet, so
+ ; this is a good time to initialize DHCPMagic...
+ ; Initialize it to 1 meaning we will accept options found;
+ ; in earlier versions of PXELINUX bit 0 was used to indicate
+ ; we have found option 208 with the appropriate magic number;
+ ; we no longer require that, but MAY want to re-introduce
+ ; it in the future for vendor encapsulated options.
+ mov byte [DHCPMagic],1
+
+;
+; Now attempt to get the BOOTP/DHCP packet that brought us life (and an IP
+; address). This lives in the DHCPACK packet (query info 2).
+;
+query_bootp_2:
+ mov dl,2
+ call pxe_get_cached_info
+ call parse_dhcp ; Parse DHCP packet
+;
+; Save away MAC address (assume this is in query info 2. If this
+; turns out to be problematic it might be better getting it from
+; the query info 1 packet.)
+;
+.save_mac:
+ movzx cx,byte [trackbuf+bootp.hardlen]
+ cmp cx,16
+ jna .mac_ok
+ xor cx,cx ; Bad hardware address length
+.mac_ok:
+ mov [MACLen],cl
+ mov al,[trackbuf+bootp.hardware]
+ mov [MACType],al
+ mov si,trackbuf+bootp.macaddr
+ mov di,MAC
+ rep movsb
+
+; Enable this if we really need to zero-pad this field...
+; mov cx,MAC+MAC_MAX+1
+; sub cx,di
+; xor ax,ax
+; rep stosb
+
+;
+; Now, get the boot file and other info. This lives in the CACHED_REPLY
+; packet (query info 3).
+;
+ mov dl,3
+ call pxe_get_cached_info
+ call parse_dhcp ; Parse DHCP packet
+
+;
+; Generate the bootif string, and the hardware-based config string.
+;
+make_bootif_string:
+ mov si,bootif_str
+ mov di,BOOTIFStr
+ mov cx,bootif_str_len
+ rep movsb
+
+ movzx cx,byte [MACLen]
+ mov si,MACType
+ inc cx
+.hexify_mac:
+ push cx
+ mov cl,1 ; CH == 0 already
+ call lchexbytes
+ mov al,'-'
+ stosb
+ pop cx
+ loop .hexify_mac
+ mov [di-1],cl ; Null-terminate and strip final dash
+;
+; Generate ip= option
+;
+ call genipopt
+
+;
+; Print IP address
+;
+ mov eax,[MyIP]
+ mov di,DotQuadBuf
+ push di
+ call gendotquad ; This takes network byte order input
+
+ xchg ah,al ; Convert to host byte order
+ ror eax,16 ; (BSWAP doesn't work on 386)
+ xchg ah,al
+
+ mov si,myipaddr_msg
+ call writestr
+ call writehex8
+ mov al,' '
+ call writechr
+ pop si ; DotQuadBuf
+ call writestr
+ call crlf
+
+ mov si,IPOption
+ call writestr
+ call crlf
+
+;
+; Check to see if we got any PXELINUX-specific DHCP options; in particular,
+; if we didn't get the magic enable, do not recognize any other options.
+;
+check_dhcp_magic:
+ test byte [DHCPMagic], 1 ; If we didn't get the magic enable...
+ jnz .got_magic
+ mov byte [DHCPMagic], 0 ; If not, kill all other options
+.got_magic:
+
+
+;
+; Initialize UDP stack
+;
+udp_init:
+ mov eax,[MyIP]
+ mov [pxe_udp_open_pkt.sip],eax
+ mov di,pxe_udp_open_pkt
+ mov bx,PXENV_UDP_OPEN
+ call pxenv
+ jc .failed
+ cmp word [pxe_udp_open_pkt.status], byte 0
+ je .success
+.failed: mov si,err_udpinit
+ call writestr
+ jmp kaboom
+.success:
+
+;
+; Common initialization code
+;
+%include "cpuinit.inc"
+
+;
+; Now we're all set to start with our *real* business. First load the
+; configuration file (if any) and parse it.
+;
+; In previous versions I avoided using 32-bit registers because of a
+; rumour some BIOSes clobbered the upper half of 32-bit registers at
+; random. I figure, though, that if there are any of those still left
+; they probably won't be trying to install Linux on them...
+;
+; The code is still ripe with 16-bitisms, though. Not worth the hassle
+; to take'm out. In fact, we may want to put them back if we're going
+; to boot ELKS at some point.
+;
+
+;
+; Store standard filename prefix
+;
+prefix: test byte [DHCPMagic], 04h ; Did we get a path prefix option
+ jnz .got_prefix
+ mov si,BootFile
+ mov di,PathPrefix
+ cld
+ call strcpy
+ mov cx,di
+ sub cx,PathPrefix+1
+ std
+ lea si,[di-2] ; Skip final null!
+.find_alnum: lodsb
+ or al,20h
+ cmp al,'.' ; Count . or - as alphanum
+ je .alnum
+ cmp al,'-'
+ je .alnum
+ cmp al,'0'
+ jb .notalnum
+ cmp al,'9'
+ jbe .alnum
+ cmp al,'a'
+ jb .notalnum
+ cmp al,'z'
+ ja .notalnum
+.alnum: loop .find_alnum
+ dec si
+.notalnum: mov byte [si+2],0 ; Zero-terminate after delimiter
+ cld
+.got_prefix:
+ mov si,tftpprefix_msg
+ call writestr
+ mov si,PathPrefix
+ call writestr
+ call crlf
+
+;
+; Load configuration file
+;
+find_config:
+
+;
+; Begin looking for configuration file
+;
+config_scan:
+ test byte [DHCPMagic], 02h
+ jz .no_option
+
+ ; We got a DHCP option, try it first
+ call .try
+ jnz .success
+
+.no_option:
+ mov di,ConfigName
+ mov si,cfgprefix
+ mov cx,cfgprefix_len
+ rep movsb
+
+ ; Have to guess config file name...
+
+ ; Try loading by UUID.
+ cmp byte [HaveUUID],0
+ je .no_uuid
+
+ push di
+ mov bx,uuid_dashes
+ mov si,UUID
+.gen_uuid:
+ movzx cx,byte [bx]
+ jcxz .done_uuid
+ inc bx
+ call lchexbytes
+ mov al,'-'
+ stosb
+ jmp .gen_uuid
+.done_uuid:
+ mov [di-1],cl ; Remove last dash and zero-terminate
+ pop di
+ call .try
+ jnz .success
+.no_uuid:
+
+ ; Try loading by MAC address
+ push di
+ mov si,MACStr
+ call strcpy
+ pop di
+ call .try
+ jnz .success
+
+ ; Nope, try hexadecimal IP prefixes...
+.scan_ip:
+ mov cx,4
+ mov si,MyIP
+ call uchexbytes ; Convert to hex string
+
+ mov cx,8 ; Up to 8 attempts
+.tryagain:
+ mov byte [di],0 ; Zero-terminate string
+ call .try
+ jnz .success
+ dec di ; Drop one character
+ loop .tryagain
+
+ ; Final attempt: "default" string
+ mov si,default_str ; "default" string
+ call strcpy
+ call .try
+ jnz .success
+
+ mov si,err_noconfig
+ call writestr
+ jmp kaboom
+
+.try:
+ pusha
+ mov si,trying_msg
+ call writestr
+ mov di,ConfigName
+ mov si,di
+ call writestr
+ call crlf
+ mov si,di
+ mov di,KernelName ; Borrow this buffer for mangled name
+ call mangle_name
+ call open
+ popa
+ ret
+
+
+.success:
+
+;
+; Linux kernel loading code is common. However, we need to define
+; a couple of helper macros...
+;
+
+; Handle "ipappend" option
+%define HAVE_SPECIAL_APPEND
+%macro SPECIAL_APPEND 0
+ test byte [IPAppend],01h ; ip=
+ jz .noipappend1
+ mov si,IPOption
+ mov cx,[IPOptionLen]
+ rep movsb
+ mov al,' '
+ stosb
+.noipappend1:
+ test byte [IPAppend],02h
+ jz .noipappend2
+ mov si,BOOTIFStr
+ call strcpy
+ mov byte [es:di-1],' ' ; Replace null with space
+.noipappend2:
+%endmacro
+
+; Unload PXE stack
+%define HAVE_UNLOAD_PREP
+%macro UNLOAD_PREP 0
+ call unload_pxe
+%endmacro
+
+;
+; Now we have the config file open. Parse the config file and
+; run the user interface.
+;
+%include "ui.inc"
+
+;
+; Boot to the local disk by returning the appropriate PXE magic.
+; AX contains the appropriate return code.
+;
+%if HAS_LOCALBOOT
+
+local_boot:
+ push cs
+ pop ds
+ mov [LocalBootType],ax
+ call vgaclearmode
+ mov si,localboot_msg
+ call writestr
+ ; Restore the environment we were called with
+ lss sp,[InitStack]
+ pop gs
+ pop fs
+ pop es
+ pop ds
+ popad
+ mov ax,[cs:LocalBootType]
+ popfd
+ retf ; Return to PXE
+
+%endif
+
+;
+; kaboom: write a message and bail out. Wait for quite a while,
+; or a user keypress, then do a hard reboot.
+;
+kaboom:
+ RESET_STACK_AND_SEGS AX
+.patch: mov si,bailmsg
+ call writestr ; Returns with AL = 0
+.drain: call pollchar
+ jz .drained
+ call getchar
+ jmp short .drain
+.drained:
+ mov edi,[RebootTime]
+ mov al,[DHCPMagic]
+ and al,09h ; Magic+Timeout
+ cmp al,09h
+ je .time_set
+ mov edi,REBOOT_TIME
+.time_set:
+ mov cx,18
+.wait1: push cx
+ mov ecx,edi
+.wait2: mov dx,[BIOS_timer]
+.wait3: call pollchar
+ jnz .keypress
+ cmp dx,[BIOS_timer]
+ je .wait3
+ loop .wait2,ecx
+ mov al,'.'
+ call writechr
+ pop cx
+ loop .wait1
+.keypress:
+ call crlf
+ mov word [BIOS_magic],0 ; Cold reboot
+ jmp 0F000h:0FFF0h ; Reset vector address
+
+;
+; memory_scan_for_pxe_struct:
+;
+; If none of the standard methods find the !PXE structure, look for it
+; by scanning memory.
+;
+; On exit, if found:
+; CF = 0, ES:BX -> !PXE structure
+; Otherwise CF = 1, all registers saved
+;
+memory_scan_for_pxe_struct:
+ push ds
+ pusha
+ mov ax,cs
+ mov ds,ax
+ mov si,trymempxe_msg
+ call writestr
+ mov ax,[BIOS_fbm] ; Starting segment
+ shl ax,(10-4) ; Kilobytes -> paragraphs
+; mov ax,01000h ; Start to look here
+ dec ax ; To skip inc ax
+.mismatch:
+ inc ax
+ cmp ax,0A000h ; End of memory
+ jae .not_found
+ call writehex4
+ mov si,fourbs_msg
+ call writestr
+ mov es,ax
+ mov edx,[es:0]
+ cmp edx,'!PXE'
+ jne .mismatch
+ movzx cx,byte [es:4] ; Length of structure
+ cmp cl,08h ; Minimum length
+ jb .mismatch
+ push ax
+ xor ax,ax
+ xor si,si
+.checksum: es lodsb
+ add ah,al
+ loop .checksum
+ pop ax
+ jnz .mismatch ; Checksum must == 0
+.found: mov bp,sp
+ xor bx,bx
+ mov [bp+8],bx ; Save BX into stack frame (will be == 0)
+ mov ax,es
+ call writehex4
+ call crlf
+ popa
+ pop ds
+ clc
+ ret
+.not_found: mov si,notfound_msg
+ call writestr
+ popa
+ pop ds
+ stc
+ ret
+
+;
+; memory_scan_for_pxenv_struct:
+;
+; If none of the standard methods find the PXENV+ structure, look for it
+; by scanning memory.
+;
+; On exit, if found:
+; CF = 0, ES:BX -> PXENV+ structure
+; Otherwise CF = 1, all registers saved
+;
+memory_scan_for_pxenv_struct:
+ pusha
+ mov si,trymempxenv_msg
+ call writestr
+; mov ax,[BIOS_fbm] ; Starting segment
+; shl ax,(10-4) ; Kilobytes -> paragraphs
+ mov ax,01000h ; Start to look here
+ dec ax ; To skip inc ax
+.mismatch:
+ inc ax
+ cmp ax,0A000h ; End of memory
+ jae .not_found
+ mov es,ax
+ mov edx,[es:0]
+ cmp edx,'PXEN'
+ jne .mismatch
+ mov dx,[es:4]
+ cmp dx,'V+'
+ jne .mismatch
+ movzx cx,byte [es:8] ; Length of structure
+ cmp cl,26h ; Minimum length
+ jb .mismatch
+ xor ax,ax
+ xor si,si
+.checksum: es lodsb
+ add ah,al
+ loop .checksum
+ and ah,ah
+ jnz .mismatch ; Checksum must == 0
+.found: mov bp,sp
+ mov [bp+8],bx ; Save BX into stack frame
+ mov ax,bx
+ call writehex4
+ call crlf
+ clc
+ ret
+.not_found: mov si,notfound_msg
+ call writestr
+ popad
+ stc
+ ret
+
+;
+; close_file:
+; Deallocates a file structure (pointer in SI)
+; Assumes CS == DS.
+;
+; XXX: We should check to see if this file is still open on the server
+; side and send a courtesy ERROR packet to the server.
+;
+close_file:
+ and si,si
+ jz .closed
+ mov word [si],0 ; Not in use
+.closed: ret
+
+;
+; searchdir:
+;
+; Open a TFTP connection to the server
+;
+; On entry:
+; DS:DI = mangled filename
+; If successful:
+; ZF clear
+; SI = socket pointer
+; EAX = file length in bytes, or -1 if unknown
+; If unsuccessful
+; ZF set
+;
+
+searchdir:
+ push es
+ push bx
+ push cx
+ mov ax,ds
+ mov es,ax
+ mov si,di
+ push bp
+ mov bp,sp
+
+ call allocate_socket
+ jz .ret
+
+ mov ax,PKT_RETRY ; Retry counter
+ mov word [PktTimeout],PKT_TIMEOUT ; Initial timeout
+
+.sendreq: push ax ; [bp-2] - Retry counter
+ push si ; [bp-4] - File name
+
+ mov di,packet_buf
+ mov [pxe_udp_write_pkt.buffer],di
+
+ mov ax,TFTP_RRQ ; TFTP opcode
+ stosw
+
+ lodsd ; EAX <- server override (if any)
+ and eax,eax
+ jnz .noprefix ; No prefix, and we have the server
+
+ push si ; Add common prefix
+ mov si,PathPrefix
+ call strcpy
+ dec di
+ pop si
+
+ mov eax,[ServerIP] ; Get default server
+
+.noprefix:
+ call strcpy ; Filename
+%if GPXE
+ mov si,packet_buf+2
+ call is_gpxe
+ jnc .gpxe
+%endif
+
+ mov [bx+tftp_remoteip],eax
+
+ push bx ; [bp-6] - TFTP block
+ mov bx,[bx]
+ push bx ; [bp-8] - TID (local port no)
+
+ mov [pxe_udp_write_pkt.status],byte 0
+ mov [pxe_udp_write_pkt.sip],eax
+ ; Now figure out the gateway
+ xor eax,[MyIP]
+ and eax,[Netmask]
+ jz .nogwneeded
+ mov eax,[Gateway]
+.nogwneeded:
+ mov [pxe_udp_write_pkt.gip],eax
+ mov [pxe_udp_write_pkt.lport],bx
+ mov ax,[ServerPort]
+ mov [pxe_udp_write_pkt.rport],ax
+ mov si,tftp_tail
+ mov cx,tftp_tail_len
+ rep movsb
+ sub di,packet_buf ; Get packet size
+ mov [pxe_udp_write_pkt.buffersize],di
+
+ mov di,pxe_udp_write_pkt
+ mov bx,PXENV_UDP_WRITE
+ call pxenv
+ jc .failure
+ cmp word [pxe_udp_write_pkt.status],byte 0
+ jne .failure
+
+ ;
+ ; Danger, Will Robinson! We need to support timeout
+ ; and retry lest we just lost a packet...
+ ;
+
+ ; Packet transmitted OK, now we need to receive
+.getpacket: push word [PktTimeout] ; [bp-10]
+ push word [BIOS_timer] ; [bp-12]
+
+.pkt_loop: mov bx,[bp-8] ; TID
+ mov di,packet_buf
+ mov word [pxe_udp_read_pkt.status],0
+ mov [pxe_udp_read_pkt.buffer],di
+ mov [pxe_udp_read_pkt.buffer+2],ds
+ mov word [pxe_udp_read_pkt.buffersize],packet_buf_size
+ mov eax,[MyIP]
+ mov [pxe_udp_read_pkt.dip],eax
+ mov [pxe_udp_read_pkt.lport],bx
+ mov di,pxe_udp_read_pkt
+ mov bx,PXENV_UDP_READ
+ call pxenv
+ jnc .got_packet ; Wait for packet
+.no_packet:
+ mov dx,[BIOS_timer]
+ cmp dx,[bp-12]
+ je .pkt_loop
+ mov [bp-12],dx
+ dec word [bp-10] ; Timeout
+ jnz .pkt_loop
+ pop ax ; Adjust stack
+ pop ax
+ shl word [PktTimeout],1 ; Exponential backoff
+ jmp .failure
+
+.got_packet:
+ mov si,[bp-6] ; TFTP pointer
+ mov bx,[bp-8] ; TID
+
+ ; Make sure the packet actually came from the server
+ ; This is technically not to the TFTP spec?
+ mov eax,[si+tftp_remoteip]
+ cmp [pxe_udp_read_pkt.sip],eax
+ jne .no_packet
+
+ ; Got packet - reset timeout
+ mov word [PktTimeout],PKT_TIMEOUT
+
+ pop ax ; Adjust stack
+ pop ax
+
+ mov ax,[pxe_udp_read_pkt.rport]
+ mov [si+tftp_remoteport],ax
+
+ ; filesize <- -1 == unknown
+ mov dword [si+tftp_filesize], -1
+ ; Default blksize unless blksize option negotiated
+ mov word [si+tftp_blksize], TFTP_BLOCKSIZE
+
+ movzx ecx,word [pxe_udp_read_pkt.buffersize]
+ sub cx,2 ; CX <- bytes after opcode
+ jb .failure ; Garbled reply
+
+ mov si,packet_buf
+ lodsw
+
+ cmp ax, TFTP_ERROR
+ je .bailnow ; ERROR reply: don't try again
+
+ ; If the server doesn't support any options, we'll get
+ ; a DATA reply instead of OACK. Stash the data in
+ ; the file buffer and go with the default value for
+ ; all options...
+ cmp ax, TFTP_DATA
+ je .no_oack
+
+ cmp ax, TFTP_OACK
+ jne .err_reply ; Unknown packet type
+
+ ; Now we need to parse the OACK packet to get the transfer
+ ; and packet sizes.
+ ; SI -> first byte of options; [E]CX -> byte count
+.parse_oack:
+ jcxz .done_pkt ; No options acked
+.get_opt_name:
+ mov di,si
+ mov bx,si
+.opt_name_loop: lodsb
+ and al,al
+ jz .got_opt_name
+ or al,20h ; Convert to lowercase
+ stosb
+ loop .opt_name_loop
+ ; We ran out, and no final null
+ jmp .err_reply
+.got_opt_name: ; si -> option value
+ dec cx ; bytes left in pkt
+ jz .err_reply ; Option w/o value
+
+ ; Parse option pointed to by bx; guaranteed to be
+ ; null-terminated.
+ push cx
+ push si
+ mov si,bx ; -> option name
+ mov bx,tftp_opt_table
+ mov cx,tftp_opts
+.opt_loop:
+ push cx
+ push si
+ mov di,[bx] ; Option pointer
+ mov cx,[bx+2] ; Option len
+ repe cmpsb
+ pop si
+ pop cx
+ je .get_value ; OK, known option
+ add bx,6
+ loop .opt_loop
+
+ pop si
+ pop cx
+ jmp .err_reply ; Non-negotiated option returned
+
+.get_value: pop si ; si -> option value
+ pop cx ; cx -> bytes left in pkt
+ mov bx,[bx+4] ; Pointer to data target
+ add bx,[bp-6] ; TFTP socket pointer
+ xor eax,eax
+ xor edx,edx
+.value_loop: lodsb
+ and al,al
+ jz .got_value
+ sub al,'0'
+ cmp al, 9
+ ja .err_reply ; Not a decimal digit
+ imul edx,10
+ add edx,eax
+ mov [bx],edx
+ loop .value_loop
+ ; Ran out before final null, accept anyway
+ jmp short .done_pkt
+
+.got_value:
+ dec cx
+ jnz .get_opt_name ; Not end of packet
+
+ ; ZF == 1
+
+ ; Success, done!
+.done_pkt:
+ pop si ; Junk
+ pop si ; We want the packet ptr in SI
+
+ mov eax,[si+tftp_filesize]
+.got_file: ; SI->socket structure, EAX = size
+ and eax,eax ; Set ZF depending on file size
+ jz .error_si ; ZF = 1 need to free the socket
+.ret:
+ leave ; SP <- BP, POP BP
+ pop cx
+ pop bx
+ pop es
+ ret
+
+
+.no_oack: ; We got a DATA packet, meaning no options are
+ ; suported. Save the data away and consider the length
+ ; undefined, *unless* this is the only data packet...
+ mov bx,[bp-6] ; File pointer
+ sub cx,2 ; Too short?
+ jb .failure
+ lodsw ; Block number
+ cmp ax,htons(1)
+ jne .failure
+ mov [bx+tftp_lastpkt],ax
+ cmp cx,TFTP_BLOCKSIZE
+ ja .err_reply ; Corrupt...
+ je .not_eof
+ ; This was the final EOF packet, already...
+ ; We know the filesize, but we also want to ack the
+ ; packet and set the EOF flag.
+ mov [bx+tftp_filesize],ecx
+ mov byte [bx+tftp_goteof],1
+ push si
+ mov si,bx
+ ; AX = htons(1) already
+ call ack_packet
+ pop si
+.not_eof:
+ mov [bx+tftp_bytesleft],cx
+ mov ax,pktbuf_seg
+ push es
+ mov es,ax
+ mov di,tftp_pktbuf
+ mov [bx+tftp_dataptr],di
+ add cx,3
+ shr cx,2
+ rep movsd
+ pop es
+ jmp .done_pkt
+
+.err_reply: ; Option negotiation error. Send ERROR reply.
+ ; ServerIP and gateway are already programmed in
+ mov si,[bp-6]
+ mov ax,[si+tftp_remoteport]
+ mov word [pxe_udp_write_pkt.rport],ax
+ mov word [pxe_udp_write_pkt.buffer],tftp_opt_err
+ mov word [pxe_udp_write_pkt.buffersize],tftp_opt_err_len
+ mov di,pxe_udp_write_pkt
+ mov bx,PXENV_UDP_WRITE
+ call pxenv
+
+ ; Write an error message and explode
+ mov si,err_damage
+ call writestr
+ jmp kaboom
+
+.bailnow: mov word [bp-2],1 ; Immediate error - no retry
+
+.failure: pop bx ; Junk
+ pop bx
+ pop si
+ pop ax
+ dec ax ; Retry counter
+ jnz .sendreq ; Try again
+
+.error: mov si,bx ; Socket pointer
+.error_si: ; Socket pointer already in SI
+ call free_socket ; ZF <- 1, SI <- 0
+ jmp .ret
+
+
+%if GPXE
+.gpxe:
+ push bx ; Socket pointer
+ mov di,gpxe_file_open
+ mov word [di],2 ; PXENV_STATUS_BAD_FUNC
+ mov word [di+4],packet_buf+2 ; Completed URL
+ mov [di+6],ds
+ mov bx,PXENV_FILE_OPEN
+ call pxenv
+ pop si ; Socket pointer in SI
+ jc .error_si
+
+ mov ax,[di+2]
+ mov word [si+tftp_localport],-1 ; gPXE URL
+ mov [si+tftp_remoteport],ax
+ mov di,gpxe_get_file_size
+ mov [di+2],ax
+
+%if 0
+ ; Disable this for now since gPXE doesn't always
+ ; return valid information in PXENV_GET_FILE_SIZE
+ mov bx,PXENV_GET_FILE_SIZE
+ call pxenv
+ mov eax,[di+4] ; File size
+ jnc .oksize
+%endif
+ or eax,-1 ; Size unknown
+.oksize:
+ mov [si+tftp_filesize],eax
+ jmp .got_file
+%endif ; GPXE
+
+;
+; allocate_socket: Allocate a local UDP port structure
+;
+; If successful:
+; ZF set
+; BX = socket pointer
+; If unsuccessful:
+; ZF clear
+;
+allocate_socket:
+ push cx
+ mov bx,Files
+ mov cx,MAX_OPEN
+.check: cmp word [bx], byte 0
+ je .found
+ add bx,open_file_t_size
+ loop .check
+ xor cx,cx ; ZF = 1
+ pop cx
+ ret
+ ; Allocate a socket number. Socket numbers are made
+ ; guaranteed unique by including the socket slot number
+ ; (inverted, because we use the loop counter cx); add a
+ ; counter value to keep the numbers from being likely to
+ ; get immediately reused.
+ ;
+ ; The NextSocket variable also contains the top two bits
+ ; set. This generates a value in the range 49152 to
+ ; 57343.
+.found:
+ dec cx
+ push ax
+ mov ax,[NextSocket]
+ inc ax
+ and ax,((1 << (13-MAX_OPEN_LG2))-1) | 0xC000
+ mov [NextSocket],ax
+ shl cx,13-MAX_OPEN_LG2
+ add cx,ax ; ZF = 0
+ xchg ch,cl ; Convert to network byte order
+ mov [bx],cx ; Socket in use
+ pop ax
+ pop cx
+ ret
+
+;
+; Free socket: socket in SI; return SI = 0, ZF = 1 for convenience
+;
+free_socket:
+ push es
+ pusha
+ xor ax,ax
+ mov es,ax
+ mov di,si
+ mov cx,tftp_pktbuf >> 1 ; tftp_pktbuf is not cleared
+ rep stosw
+ popa
+ pop es
+ xor si,si
+ ret
+
+;
+; parse_dotquad:
+; Read a dot-quad pathname in DS:SI and output an IP
+; address in EAX, with SI pointing to the first
+; nonmatching character.
+;
+; Return CF=1 on error.
+;
+; No segment assumptions permitted.
+;
+parse_dotquad:
+ push cx
+ mov cx,4
+ xor eax,eax
+.parseloop:
+ mov ch,ah
+ mov ah,al
+ lodsb
+ sub al,'0'
+ jb .notnumeric
+ cmp al,9
+ ja .notnumeric
+ aad ; AL += 10 * AH; AH = 0;
+ xchg ah,ch
+ jmp .parseloop
+.notnumeric:
+ cmp al,'.'-'0'
+ pushf
+ mov al,ah
+ mov ah,ch
+ xor ch,ch
+ ror eax,8
+ popf
+ jne .error
+ loop .parseloop
+ jmp .done
+.error:
+ loop .realerror ; If CX := 1 then we're done
+ clc
+ jmp .done
+.realerror:
+ stc
+.done:
+ dec si ; CF unchanged!
+ pop cx
+ ret
+
+;
+; is_url: Return CF=0 if and only if the buffer pointed to by
+; DS:SI is a URL (contains ://). No registers modified.
+;
+%if GPXE
+is_url:
+ push si
+ push eax
+.loop:
+ mov eax,[si]
+ inc si
+ and al,al
+ jz .not_url
+ and eax,0FFFFFFh
+ cmp eax,'://'
+ jne .loop
+.done:
+ ; CF=0 here
+ pop eax
+ pop si
+ ret
+.not_url:
+ stc
+ jmp .done
+
+;
+; is_gpxe: Return CF=0 if and only if the buffer pointed to by
+; DS:SI is a URL (contains ://) *and* the gPXE extensions
+; API is available. No registers modified.
+;
+is_gpxe:
+ call is_url
+ jc .ret ; Not a URL, don't bother
+.again:
+ cmp byte [HasGPXE],1
+ ja .unknown
+ ; CF=1 if not available (0),
+ ; CF=0 if known available (1).
+.ret: ret
+
+.unknown:
+ ; If we get here, the gPXE status is unknown.
+ push es
+ pushad
+ push ds
+ pop es
+ mov di,gpxe_file_api_check
+ mov bx,PXENV_FILE_API_CHECK ; BH = 0
+ call pxenv
+ jc .nogood
+ cmp dword [di+4],0xe9c17b20
+ jne .nogood
+ mov ax,[di+12] ; Don't care about the upper half...
+ not ax ; Set bits of *missing* functions...
+ and ax,01001011b ; The functions we care about
+ setz bh
+ jz .done
+.nogood:
+ mov si,gpxe_warning_msg
+ call writestr
+.done:
+ mov [HasGPXE],bh
+ popad
+ pop es
+ jmp .again
+
+ section .data
+gpxe_warning_msg
+ db 'URL syntax, but gPXE extensions not detected, '
+ db 'trying plain TFTP...', CR, LF, 0
+HasGPXE db -1 ; Unknown
+ section .text
+
+%endif
+
+;
+; mangle_name: Mangle a filename pointed to by DS:SI into a buffer pointed
+; to by ES:DI; ends on encountering any whitespace.
+; DI is preserved.
+;
+; This verifies that a filename is < FILENAME_MAX characters
+; and doesn't contain whitespace, and zero-pads the output buffer,
+; so "repe cmpsb" can do a compare.
+;
+; The first four bytes of the manged name is the IP address of
+; the download host, 0 for no host, or -1 for a gPXE URL.
+;
+; No segment assumptions permitted.
+;
+mangle_name:
+ push di
+%if GPXE
+ call is_url
+ jc .not_url
+ or eax,-1 ; It's a URL
+ jmp .prefix_done
+.not_url:
+%endif ; GPXE
+ push si
+ mov eax,[cs:ServerIP]
+ cmp byte [si],0
+ je .noip ; Null filename?!?!
+ cmp word [si],'::' ; Leading ::?
+ je .gotprefix
+
+.more:
+ inc si
+ cmp byte [si],0
+ je .noip
+ cmp word [si],'::'
+ jne .more
+
+ ; We have a :: prefix of some sort, it could be either
+ ; a DNS name or a dot-quad IP address. Try the dot-quad
+ ; first...
+.here:
+ pop si
+ push si
+ call parse_dotquad
+ jc .notdq
+ cmp word [si],'::'
+ je .gotprefix
+.notdq:
+ pop si
+ push si
+ call dns_resolv
+ cmp word [si],'::'
+ jne .noip
+ and eax,eax
+ jnz .gotprefix
+
+.noip:
+ pop si
+ xor eax,eax
+ jmp .prefix_done
+
+.gotprefix:
+ pop cx ; Adjust stack
+ inc si ; Skip double colon
+ inc si
+
+.prefix_done:
+ stosd ; Save IP address prefix
+ mov cx,FILENAME_MAX-5
+
+.mn_loop:
+ lodsb
+ cmp al,' ' ; If control or space, end
+ jna .mn_end
+ stosb
+ loop .mn_loop
+.mn_end:
+ inc cx ; At least one null byte
+ xor ax,ax ; Zero-fill name
+ rep stosb ; Doesn't do anything if CX=0
+ pop di
+ ret ; Done
+
+;
+; unmangle_name: Does the opposite of mangle_name; converts a DOS-mangled
+; filename to the conventional representation. This is needed
+; for the BOOT_IMAGE= parameter for the kernel.
+;
+; NOTE: The output buffer needs to be able to hold an
+; expanded IP address.
+;
+; DS:SI -> input mangled file name
+; ES:DI -> output buffer
+;
+; On return, DI points to the first byte after the output name,
+; which is set to a null byte.
+;
+unmangle_name:
+ push eax
+ lodsd
+ and eax,eax
+ jz .noip
+ cmp eax,-1
+ jz .noip ; URL
+ call gendotquad
+ mov ax,'::'
+ stosw
+.noip:
+ call strcpy
+ dec di ; Point to final null byte
+ pop eax
+ ret
+
+;
+; pxenv
+;
+; This is the main PXENV+/!PXE entry point, using the PXENV+
+; calling convention. This is a separate local routine so
+; we can hook special things from it if necessary. In particular,
+; some PXE stacks seem to not like being invoked from anything but
+; the initial stack, so humour it.
+;
+; While we're at it, save and restore all registers.
+;
+pxenv:
+ pushad
+%if USE_PXE_PROVIDED_STACK == 0
+ mov [cs:PXEStack],sp
+ mov [cs:PXEStack+2],ss
+ lss sp,[cs:InitStack]
+%endif
+ ; This works either for the PXENV+ or the !PXE calling
+ ; convention, as long as we ignore CF (which is redundant
+ ; with AX anyway.)
+ push es
+ push di
+ push bx
+.jump: call 0:0
+ add sp,6
+ mov [cs:PXEStatus],ax
+ add ax,-1 ; Set CF unless AX was 0
+
+%if USE_PXE_PROVIDED_STACK == 0
+ lss sp,[cs:PXEStack]
+%endif
+
+ ; This clobbers the AX return, but we don't use it
+ ; except for testing it against zero (and setting CF),
+ ; which we did above. For anything else,
+ ; use the Status field in the reply.
+ ; For the COMBOOT function, the value is saved in
+ ; the PXEStatus variable.
+ popad
+ cld ; Make sure DF <- 0
+ ret
+
+; Must be after function def due to NASM bug
+PXEEntry equ pxenv.jump+1
+
+ section .bss
+ alignb 2
+PXEStatus resb 2
+
+ section .text
+
+;
+; getfssec: Get multiple clusters from a file, given the starting cluster.
+;
+; In this case, get multiple blocks from a specific TCP connection.
+;
+; On entry:
+; ES:BX -> Buffer
+; SI -> TFTP socket pointer
+; CX -> 512-byte block count; 0FFFFh = until end of file
+; On exit:
+; SI -> TFTP socket pointer (or 0 on EOF)
+; CF = 1 -> Hit EOF
+; ECX -> number of bytes actually read
+;
+getfssec:
+ push eax
+ push edi
+ push bx
+ push si
+ push fs
+ mov di,bx
+ mov ax,pktbuf_seg
+ mov fs,ax
+
+ xor eax,eax
+ movzx ecx,cx
+ shl ecx,TFTP_BLOCKSIZE_LG2 ; Convert to bytes
+ push ecx ; Initial request size
+ jz .hit_eof ; Nothing to do?
+
+.need_more:
+ call fill_buffer
+ movzx eax,word [si+tftp_bytesleft]
+ and ax,ax
+ jz .hit_eof
+
+ push ecx
+ cmp ecx,eax
+ jna .ok_size
+ mov ecx,eax
+.ok_size:
+ mov ax,cx ; EAX<31:16> == ECX<31:16> == 0
+ mov bx,[si+tftp_dataptr]
+ sub [si+tftp_bytesleft],cx
+ xchg si,bx
+ fs rep movsb ; Copy from packet buffer
+ xchg si,bx
+ mov [si+tftp_dataptr],bx
+
+ pop ecx
+ sub ecx,eax
+ jnz .need_more
+
+.hit_eof:
+ call fill_buffer
+
+ pop eax ; Initial request amount
+ xchg eax,ecx
+ sub ecx,eax ; ... minus anything not gotten
+
+ pop fs
+ pop si
+
+ ; Is there anything left of this?
+ mov eax,[si+tftp_filesize]
+ sub eax,[si+tftp_filepos]
+ jnz .bytes_left
+
+ cmp [si+tftp_bytesleft],ax ; AX == 0
+ jne .bytes_left
+
+ cmp byte [si+tftp_goteof],0
+ je .done
+ ; I'm 99% sure this can't happen, but...
+ call fill_buffer ; Receive/ACK the EOF packet
+.done:
+ ; The socket is closed and the buffer drained
+ ; Close socket structure and re-init for next user
+ call free_socket
+ stc
+ jmp .ret
+.bytes_left:
+ clc
+.ret:
+ pop bx
+ pop edi
+ pop eax
+ ret
+
+;
+; Get a fresh packet if the buffer is drained, and we haven't hit
+; EOF yet. The buffer should be filled immediately after draining!
+;
+; expects fs -> pktbuf_seg and ds:si -> socket structure
+;
+fill_buffer:
+ cmp word [si+tftp_bytesleft],0
+ je .empty
+ ret ; Otherwise, nothing to do
+
+.empty:
+ push es
+ pushad
+ mov ax,ds
+ mov es,ax
+
+ ; Note: getting the EOF packet is not the same thing
+ ; as tftp_filepos == tftp_filesize; if the EOF packet
+ ; is empty the latter condition can be true without
+ ; having gotten the official EOF.
+ cmp byte [si+tftp_goteof],0
+ jne .ret ; Already EOF
+
+%if GPXE
+ cmp word [si+tftp_localport], -1
+ jne .get_packet_tftp
+ call get_packet_gpxe
+ jmp .ret
+.get_packet_tftp:
+%endif ; GPXE
+
+ ; TFTP code...
+.packet_loop:
+ ; Start by ACKing the previous packet; this should cause the
+ ; next packet to be sent.
+ mov cx,PKT_RETRY
+ mov word [PktTimeout],PKT_TIMEOUT
+
+.send_ack: push cx ; <D> Retry count
+
+ mov ax,[si+tftp_lastpkt]
+ call ack_packet ; Send ACK
+
+ ; We used to test the error code here, but sometimes
+ ; PXE would return negative status even though we really
+ ; did send the ACK. Now, just treat a failed send as
+ ; a normally lost packet, and let it time out in due
+ ; course of events.
+
+.send_ok: ; Now wait for packet.
+ mov dx,[BIOS_timer] ; Get current time
+
+ mov cx,[PktTimeout]
+.wait_data: push cx ; <E> Timeout
+ push dx ; <F> Old time
+
+ mov bx,[si+tftp_pktbuf]
+ mov [pxe_udp_read_pkt.buffer],bx
+ mov [pxe_udp_read_pkt.buffer+2],fs
+ mov [pxe_udp_read_pkt.buffersize],word PKTBUF_SIZE
+ mov eax,[si+tftp_remoteip]
+ mov [pxe_udp_read_pkt.sip],eax
+ mov eax,[MyIP]
+ mov [pxe_udp_read_pkt.dip],eax
+ mov ax,[si+tftp_remoteport]
+ mov [pxe_udp_read_pkt.rport],ax
+ mov ax,[si+tftp_localport]
+ mov [pxe_udp_read_pkt.lport],ax
+ mov di,pxe_udp_read_pkt
+ mov bx,PXENV_UDP_READ
+ call pxenv
+ jnc .recv_ok
+
+ ; No packet, or receive failure
+ mov dx,[BIOS_timer]
+ pop ax ; <F> Old time
+ pop cx ; <E> Timeout
+ cmp ax,dx ; Same time -> don't advance timeout
+ je .wait_data ; Same clock tick
+ loop .wait_data ; Decrease timeout
+
+ pop cx ; <D> Didn't get any, send another ACK
+ shl word [PktTimeout],1 ; Exponential backoff
+ loop .send_ack
+ jmp kaboom ; Forget it...
+
+.recv_ok: pop dx ; <F>
+ pop cx ; <E>
+
+ cmp word [pxe_udp_read_pkt.buffersize],byte 4
+ jb .wait_data ; Bad size for a DATA packet
+
+ mov bx,[si+tftp_pktbuf]
+ cmp word [fs:bx],TFTP_DATA ; Not a data packet?
+ jne .wait_data ; Then wait for something else
+
+ mov ax,[si+tftp_lastpkt]
+ xchg ah,al ; Host byte order
+ inc ax ; Which packet are we waiting for?
+ xchg ah,al ; Network byte order
+ cmp [fs:bx+2],ax
+ je .right_packet
+
+ ; Wrong packet, ACK the packet and then try again
+ ; This is presumably because the ACK got lost,
+ ; so the server just resent the previous packet
+ mov ax,[fs:bx+2]
+ call ack_packet
+ jmp .send_ok ; Reset timeout
+
+.right_packet: ; It's the packet we want. We're also EOF if the
+ ; size < blocksize
+
+ pop cx ; <D> Don't need the retry count anymore
+
+ mov [si+tftp_lastpkt],ax ; Update last packet number
+
+ movzx ecx,word [pxe_udp_read_pkt.buffersize]
+ sub cx,byte 4 ; Skip TFTP header
+
+ ; Set pointer to data block
+ lea ax,[bx+4] ; Data past TFTP header
+ mov [si+tftp_dataptr],ax
+
+ add [si+tftp_filepos],ecx
+ mov [si+tftp_bytesleft],cx
+
+ cmp cx,[si+tftp_blksize] ; Is it a full block?
+ jb .last_block ; If not, it's EOF
+
+.ret:
+ popad
+ pop es
+ ret
+
+
+.last_block: ; Last block - ACK packet immediately
+ mov ax,[fs:bx+2]
+ call ack_packet
+
+ ; Make sure we know we are at end of file
+ mov eax,[si+tftp_filepos]
+ mov [si+tftp_filesize],eax
+ mov byte [si+tftp_goteof],1
+
+ jmp .ret
+
+;
+; ack_packet:
+;
+; Send ACK packet. This is a common operation and so is worth canning.
+;
+; Entry:
+; SI = TFTP block
+; AX = Packet # to ack (network byte order)
+; Exit:
+; All registers preserved
+;
+; This function uses the pxe_udp_write_pkt but not the packet_buf.
+;
+ack_packet:
+ pushad
+ mov [ack_packet_buf+2],ax ; Packet number to ack
+ mov ax,[si]
+ mov [pxe_udp_write_pkt.lport],ax
+ mov ax,[si+tftp_remoteport]
+ mov [pxe_udp_write_pkt.rport],ax
+ mov eax,[si+tftp_remoteip]
+ mov [pxe_udp_write_pkt.sip],eax
+ xor eax,[MyIP]
+ and eax,[Netmask]
+ jz .nogw
+ mov eax,[Gateway]
+.nogw:
+ mov [pxe_udp_write_pkt.gip],eax
+ mov [pxe_udp_write_pkt.buffer],word ack_packet_buf
+ mov [pxe_udp_write_pkt.buffersize], word 4
+ mov di,pxe_udp_write_pkt
+ mov bx,PXENV_UDP_WRITE
+ call pxenv
+ popad
+ ret
+
+%if GPXE
+;
+; Get a fresh packet from a gPXE socket; expects fs -> pktbuf_seg
+; and ds:si -> socket structure
+;
+; Assumes CS == DS == ES.
+;
+get_packet_gpxe:
+ mov di,gpxe_file_read
+
+ mov ax,[si+tftp_remoteport] ; gPXE filehandle
+ mov [di+2],ax
+ mov ax,[si+tftp_pktbuf]
+ mov [di+6],ax
+ mov [si+tftp_dataptr],ax
+ mov [di+8],fs
+
+.again:
+ mov word [di+4],PKTBUF_SIZE
+ mov bx,PXENV_FILE_READ
+ call pxenv
+ jnc .ok ; Got data or EOF
+ cmp word [di],PXENV_STATUS_TFTP_OPEN ; == EWOULDBLOCK
+ je .again
+ jmp kaboom ; Otherwise error...
+
+.ok:
+ movzx eax,word [di+4] ; Bytes read
+ mov [si+tftp_bytesleft],ax ; Bytes in buffer
+ add [si+tftp_filepos],eax ; Position in file
+
+ and ax,ax ; EOF?
+ mov eax,[si+tftp_filepos]
+
+ jnz .got_stuff
+
+ ; We got EOF here, make sure the upper layers know
+ mov [si+tftp_filesize],eax
+
+.got_stuff:
+ ; If we're done here, close the file
+ cmp [si+tftp_filesize],eax
+ ja .done ; Not EOF, there is still data...
+
+ ; Reuse the previous [es:di] structure since the
+ ; relevant fields are all the same
+ mov byte [si+tftp_goteof],1
+
+ mov bx,PXENV_FILE_CLOSE
+ call pxenv
+ ; Ignore return...
+.done:
+ ret
+%endif ; GPXE
+
+;
+; unload_pxe:
+;
+; This function unloads the PXE and UNDI stacks and unclaims
+; the memory.
+;
+unload_pxe:
+ test byte [KeepPXE],01h ; Should we keep PXE around?
+ jnz reset_pxe
+
+ push ds
+ push es
+
+ mov ax,cs
+ mov ds,ax
+ mov es,ax
+
+ mov si,new_api_unload
+ cmp byte [APIVer+1],2 ; Major API version >= 2?
+ jae .new_api
+ mov si,old_api_unload
+.new_api:
+
+.call_loop: xor ax,ax
+ lodsb
+ and ax,ax
+ jz .call_done
+ xchg bx,ax
+ mov di,pxe_unload_stack_pkt
+ push di
+ xor ax,ax
+ mov cx,pxe_unload_stack_pkt_len >> 1
+ rep stosw
+ pop di
+ call pxenv
+ jc .cant_free
+ mov ax,word [pxe_unload_stack_pkt.status]
+ cmp ax,PXENV_STATUS_SUCCESS
+ jne .cant_free
+ jmp .call_loop
+
+.call_done:
+ mov bx,0FF00h
+
+ mov dx,[RealBaseMem]
+ cmp dx,[BIOS_fbm] ; Sanity check
+ jna .cant_free
+ inc bx
+
+ ; Check that PXE actually unhooked the INT 1Ah chain
+ movzx eax,word [4*0x1a]
+ movzx ecx,word [4*0x1a+2]
+ shl ecx,4
+ add eax,ecx
+ shr eax,10
+ cmp ax,dx ; Not in range
+ jae .ok
+ cmp ax,[BIOS_fbm]
+ jae .cant_free
+ ; inc bx
+
+.ok:
+ mov [BIOS_fbm],dx
+.pop_ret:
+ pop es
+ pop ds
+ ret
+
+.cant_free:
+ mov si,cant_free_msg
+ call writestr
+ push ax
+ xchg bx,ax
+ call writehex4
+ mov al,'-'
+ call writechr
+ pop ax
+ call writehex4
+ mov al,'-'
+ call writechr
+ mov eax,[4*0x1a]
+ call writehex8
+ call crlf
+ jmp .pop_ret
+
+ ; We want to keep PXE around, but still we should reset
+ ; it to the standard bootup configuration
+reset_pxe:
+ push es
+ push cs
+ pop es
+ mov bx,PXENV_UDP_CLOSE
+ mov di,pxe_udp_close_pkt
+ call pxenv
+ pop es
+ ret
+
+;
+; gendotquad
+;
+; Take an IP address (in network byte order) in EAX and
+; output a dotted quad string to ES:DI.
+; DI points to terminal null at end of string on exit.
+;
+gendotquad:
+ push eax
+ push cx
+ mov cx,4
+.genchar:
+ push eax
+ cmp al,10 ; < 10?
+ jb .lt10 ; If so, skip first 2 digits
+
+ cmp al,100 ; < 100
+ jb .lt100 ; If so, skip first digit
+
+ aam 100
+ ; Now AH = 100-digit; AL = remainder
+ add ah,'0'
+ mov [es:di],ah
+ inc di
+
+.lt100:
+ aam 10
+ ; Now AH = 10-digit; AL = remainder
+ add ah,'0'
+ mov [es:di],ah
+ inc di
+
+.lt10:
+ add al,'0'
+ stosb
+ mov al,'.'
+ stosb
+ pop eax
+ ror eax,8 ; Move next char into LSB
+ loop .genchar
+ dec di
+ mov [es:di], byte 0
+ pop cx
+ pop eax
+ ret
+;
+; uchexbytes/lchexbytes
+;
+; Take a number of bytes in memory and convert to upper/lower-case
+; hexadecimal
+;
+; Input:
+; DS:SI = input bytes
+; ES:DI = output buffer
+; CX = number of bytes
+; Output:
+; DS:SI = first byte after
+; ES:DI = first byte after
+; CX = 0
+;
+; Trashes AX, DX
+;
+
+lchexbytes:
+ mov dl,'a'-'9'-1
+ jmp xchexbytes
+uchexbytes:
+ mov dl,'A'-'9'-1
+xchexbytes:
+.loop:
+ lodsb
+ mov ah,al
+ shr al,4
+ call .outchar
+ mov al,ah
+ call .outchar
+ loop .loop
+ ret
+.outchar:
+ and al,0Fh
+ add al,'0'
+ cmp al,'9'
+ jna .done
+ add al,dl
+.done:
+ stosb
+ ret
+
+;
+; pxe_get_cached_info
+;
+; Get a DHCP packet from the PXE stack into the trackbuf.
+;
+; Input:
+; DL = packet type
+; Output:
+; CX = buffer size
+;
+; Assumes CS == DS == ES.
+;
+pxe_get_cached_info:
+ pushad
+ mov si,get_packet_msg
+ call writestr
+ mov al,dl
+ call writehex2
+ call crlf
+ mov di,pxe_bootp_query_pkt
+ push di
+ xor ax,ax
+ stosw ; Status
+ movzx ax,dl
+ stosw ; Packet type
+ mov ax,trackbufsize
+ stosw ; Buffer size
+ mov ax,trackbuf
+ stosw ; Buffer offset
+ xor ax,ax
+ stosw ; Buffer segment
+
+ pop di ; DI -> parameter set
+ mov bx,PXENV_GET_CACHED_INFO
+ call pxenv
+ jc .err
+ and ax,ax
+ jnz .err
+
+ popad
+ mov cx,[pxe_bootp_query_pkt.buffersize]
+ ret
+
+.err:
+ mov si,err_pxefailed
+ call writestr
+ call writehex4
+ call crlf
+ jmp kaboom
+
+ section .data
+get_packet_msg db 'Getting cached packet ', 0
+
+ section .text
+;
+; ip_ok
+;
+; Tests an IP address in EAX for validity; return with ZF=1 for bad.
+; We used to refuse class E, but class E addresses are likely to become
+; assignable unicast addresses in the near future.
+;
+ip_ok:
+ push ax
+ cmp eax,-1 ; Refuse the all-ones address
+ jz .out
+ and al,al ; Refuse network zero
+ jz .out
+ cmp al,127 ; Refuse loopback
+ jz .out
+ and al,0F0h
+ cmp al,224 ; Refuse class D
+.out:
+ pop ax
+ ret
+
+;
+; parse_dhcp
+;
+; Parse a DHCP packet. This includes dealing with "overloaded"
+; option fields (see RFC 2132, section 9.3)
+;
+; This should fill in the following global variables, if the
+; information is present:
+;
+; MyIP - client IP address
+; ServerIP - boot server IP address
+; Netmask - network mask
+; Gateway - default gateway router IP
+; BootFile - boot file name
+; DNSServers - DNS server IPs
+; LocalDomain - Local domain name
+; MACLen, MAC - Client identifier, if MACLen == 0
+;
+; This assumes the DHCP packet is in "trackbuf" and the length
+; of the packet in in CX on entry.
+;
+
+parse_dhcp:
+ mov byte [OverLoad],0 ; Assume no overload
+ mov eax, [trackbuf+bootp.yip]
+ call ip_ok
+ jz .noyip
+ mov [MyIP], eax
+.noyip:
+ mov eax, [trackbuf+bootp.sip]
+ and eax, eax
+ call ip_ok
+ jz .nosip
+ mov [ServerIP], eax
+.nosip:
+ sub cx, bootp.options
+ jbe .nooptions
+ mov si, trackbuf+bootp.option_magic
+ lodsd
+ cmp eax, BOOTP_OPTION_MAGIC
+ jne .nooptions
+ call parse_dhcp_options
+.nooptions:
+ mov si, trackbuf+bootp.bootfile
+ test byte [OverLoad],1
+ jz .nofileoverload
+ mov cx,128
+ call parse_dhcp_options
+ jmp short .parsed_file
+.nofileoverload:
+ cmp byte [si], 0
+ jz .parsed_file ; No bootfile name
+ mov di,BootFile
+ mov cx,32
+ rep movsd
+ xor al,al
+ stosb ; Null-terminate
+.parsed_file:
+ mov si, trackbuf+bootp.sname
+ test byte [OverLoad],2
+ jz .nosnameoverload
+ mov cx,64
+ call parse_dhcp_options
+.nosnameoverload:
+ ret
+
+;
+; Parse a sequence of DHCP options, pointed to by DS:SI; the field
+; size is CX -- some DHCP servers leave option fields unterminated
+; in violation of the spec.
+;
+; For parse_some_dhcp_options, DH contains the minimum value for
+; the option to recognize -- this is used to restrict parsing to
+; PXELINUX-specific options only.
+;
+parse_dhcp_options:
+ xor dx,dx
+
+parse_some_dhcp_options:
+.loop:
+ and cx,cx
+ jz .done
+
+ lodsb
+ dec cx
+ jz .done ; Last byte; must be PAD, END or malformed
+ cmp al, 0 ; PAD option
+ je .loop
+ cmp al,255 ; END option
+ je .done
+
+ ; Anything else will have a length field
+ mov dl,al ; DL <- option number
+ xor ax,ax
+ lodsb ; AX <- option length
+ dec cx
+ sub cx,ax ; Decrement bytes left counter
+ jb .done ; Malformed option: length > field size
+
+ cmp dl,dh ; Is the option value valid?
+ jb .opt_done
+
+ mov bx,dhcp_option_list
+.find_option:
+ cmp bx,dhcp_option_list_end
+ jae .opt_done
+ cmp dl,[bx]
+ je .found_option
+ add bx,3
+ jmp .find_option
+.found_option:
+ pushad
+ call [bx+1]
+ popad
+
+; Fall through
+ ; Unknown option. Skip to the next one.
+.opt_done:
+ add si,ax
+ jmp .loop
+.done:
+ ret
+
+ section .data
+dhcp_option_list:
+ section .text
+
+%macro dopt 2
+ section .data
+ db %1
+ dw dopt_%2
+ section .text
+dopt_%2:
+%endmacro
+
+;
+; Parse individual DHCP options. SI points to the option data and
+; AX to the option length. DL contains the option number.
+; All registers are saved around the routine.
+;
+ dopt 1, subnet_mask
+ mov ebx,[si]
+ mov [Netmask],ebx
+ ret
+
+ dopt 3, router
+ mov ebx,[si]
+ mov [Gateway],ebx
+ ret
+
+ dopt 6, dns_servers
+ mov cx,ax
+ shr cx,2
+ cmp cl,DNS_MAX_SERVERS
+ jna .oklen
+ mov cl,DNS_MAX_SERVERS
+.oklen:
+ mov di,DNSServers
+ rep movsd
+ mov [LastDNSServer],di
+ ret
+
+ dopt 16, local_domain
+ mov bx,si
+ add bx,ax
+ xor ax,ax
+ xchg [bx],al ; Zero-terminate option
+ mov di,LocalDomain
+ call dns_mangle ; Convert to DNS label set
+ mov [bx],al ; Restore ending byte
+ ret
+
+ dopt 43, vendor_encaps
+ mov dh,208 ; Only recognize PXELINUX options
+ mov cx,ax ; Length of option = max bytes to parse
+ call parse_some_dhcp_options ; Parse recursive structure
+ ret
+
+ dopt 52, option_overload
+ mov bl,[si]
+ mov [OverLoad],bl
+ ret
+
+ dopt 54, server
+ mov eax,[si]
+ cmp dword [ServerIP],0
+ jne .skip ; Already have a next server IP
+ call ip_ok
+ jz .skip
+ mov [ServerIP],eax
+.skip: ret
+
+ dopt 61, client_identifier
+ cmp ax,MAC_MAX ; Too long?
+ ja .skip
+ cmp ax,2 ; Too short?
+ jb .skip
+ cmp [MACLen],ah ; Only do this if MACLen == 0
+ jne .skip
+ push ax
+ lodsb ; Client identifier type
+ cmp al,[MACType]
+ pop ax
+ jne .skip ; Client identifier is not a MAC
+ dec ax
+ mov [MACLen],al
+ mov di,MAC
+ jmp dhcp_copyoption
+.skip: ret
+
+ dopt 67, bootfile_name
+ mov di,BootFile
+ jmp dhcp_copyoption
+
+ dopt 97, uuid_client_identifier
+ cmp ax,17 ; type byte + 16 bytes UUID
+ jne .skip
+ mov dl,[si] ; Must have type 0 == UUID
+ or dl,[HaveUUID] ; Capture only the first instance
+ jnz .skip
+ mov byte [HaveUUID],1 ; Got UUID
+ mov di,UUIDType
+ jmp dhcp_copyoption
+.skip: ret
+
+ dopt 209, pxelinux_configfile
+ mov di,ConfigName
+ or byte [DHCPMagic],2 ; Got config file
+ jmp dhcp_copyoption
+
+ dopt 210, pxelinux_pathprefix
+ mov di,PathPrefix
+ or byte [DHCPMagic],4 ; Got path prefix
+ jmp dhcp_copyoption
+
+ dopt 211, pxelinux_reboottime
+ cmp al,4
+ jne .done
+ mov ebx,[si]
+ xchg bl,bh ; Convert to host byte order
+ rol ebx,16
+ xchg bl,bh
+ mov [RebootTime],ebx
+ or byte [DHCPMagic],8 ; Got RebootTime
+.done: ret
+
+ ; Common code for copying an option verbatim
+ ; Copies the option into ES:DI and null-terminates it.
+ ; Returns with AX=0 and SI past the option.
+dhcp_copyoption:
+ xchg cx,ax ; CX <- option length
+ rep movsb
+ xchg cx,ax ; AX <- 0
+ stosb ; Null-terminate
+ ret
+
+ section .data
+dhcp_option_list_end:
+ section .text
+
+ section .data
+HaveUUID db 0
+uuid_dashes db 4,2,2,2,6,0 ; Bytes per UUID dashed section
+ section .text
+
+;
+; genipopt
+;
+; Generate an ip=<client-ip>:<boot-server-ip>:<gw-ip>:<netmask>
+; option into IPOption based on a DHCP packet in trackbuf.
+; Assumes CS == DS == ES.
+;
+genipopt:
+ pushad
+ mov di,IPOption
+ mov eax,'ip='
+ stosd
+ dec di
+ mov eax,[MyIP]
+ call gendotquad
+ mov al,':'
+ stosb
+ mov eax,[ServerIP]
+ call gendotquad
+ mov al,':'
+ stosb
+ mov eax,[Gateway]
+ call gendotquad
+ mov al,':'
+ stosb
+ mov eax,[Netmask]
+ call gendotquad ; Zero-terminates its output
+ sub di,IPOption
+ mov [IPOptionLen],di
+ popad
+ ret
+
+;
+; Call the receive loop while idle. This is done mostly so we can respond to
+; ARP messages, but perhaps in the future this can be used to do network
+; console.
+;
+; hpa sez: people using automatic control on the serial port get very
+; unhappy if we poll for ARP too often (the PXE stack is pretty slow,
+; typically.) Therefore, only poll if at least 4 BIOS timer ticks have
+; passed since the last poll, and reset this when a character is
+; received (RESET_IDLE).
+;
+%if HAVE_IDLE
+
+reset_idle:
+ push ax
+ mov ax,[cs:BIOS_timer]
+ mov [cs:IdleTimer],ax
+ pop ax
+ ret
+
+check_for_arp:
+ push ax
+ mov ax,[cs:BIOS_timer]
+ sub ax,[cs:IdleTimer]
+ cmp ax,4
+ pop ax
+ jae .need_poll
+ ret
+.need_poll: pushad
+ push ds
+ push es
+ mov ax,cs
+ mov ds,ax
+ mov es,ax
+ mov di,packet_buf
+ mov [pxe_udp_read_pkt.status],al ; 0
+ mov [pxe_udp_read_pkt.buffer],di
+ mov [pxe_udp_read_pkt.buffer+2],ds
+ mov word [pxe_udp_read_pkt.buffersize],packet_buf_size
+ mov eax,[MyIP]
+ mov [pxe_udp_read_pkt.dip],eax
+ mov word [pxe_udp_read_pkt.lport],htons(9) ; discard port
+ mov di,pxe_udp_read_pkt
+ mov bx,PXENV_UDP_READ
+ call pxenv
+ ; Ignore result...
+ pop es
+ pop ds
+ popad
+ RESET_IDLE
+ ret
+
+%endif ; HAVE_IDLE
+
+; -----------------------------------------------------------------------------
+; Common modules
+; -----------------------------------------------------------------------------
+
+%include "getc.inc" ; getc et al
+%include "conio.inc" ; Console I/O
+%include "writestr.inc" ; String output
+writestr equ cwritestr
+%include "writehex.inc" ; Hexadecimal output
+%include "configinit.inc" ; Initialize configuration
+%include "parseconfig.inc" ; High-level config file handling
+%include "parsecmd.inc" ; Low-level config file handling
+%include "bcopy32.inc" ; 32-bit bcopy
+%include "loadhigh.inc" ; Load a file into high memory
+%include "font.inc" ; VGA font stuff
+%include "graphics.inc" ; VGA graphics
+%include "highmem.inc" ; High memory sizing
+%include "strcpy.inc" ; strcpy()
+%include "rawcon.inc" ; Console I/O w/o using the console functions
+%include "dnsresolv.inc" ; DNS resolver
+%include "adv.inc" ; Auxillary Data Vector
+
+; -----------------------------------------------------------------------------
+; Begin data section
+; -----------------------------------------------------------------------------
+
+ section .data
+
+copyright_str db ' Copyright (C) 1994-', year, ' H. Peter Anvin'
+ db CR, LF, 0
+err_bootfailed db CR, LF, 'Boot failed: press a key to retry, or wait for reset...', CR, LF, 0
+bailmsg equ err_bootfailed
+err_nopxe db "No !PXE or PXENV+ API found; we're dead...", CR, LF, 0
+err_pxefailed db 'PXE API call failed, error ', 0
+err_udpinit db 'Failed to initialize UDP stack', CR, LF, 0
+err_noconfig db 'Unable to locate configuration file', CR, LF, 0
+err_damage db 'TFTP server sent an incomprehesible reply', CR, LF, 0
+found_pxenv db 'Found PXENV+ structure', CR, LF, 0
+using_pxenv_msg db 'Old PXE API detected, using PXENV+ structure', CR, LF, 0
+apiver_str db 'PXE API version is ',0
+pxeentry_msg db 'PXE entry point found (we hope) at ', 0
+pxenventry_msg db 'PXENV entry point found (we hope) at ', 0
+trymempxe_msg db 'Scanning memory for !PXE structure... ', 0
+trymempxenv_msg db 'Scanning memory for PXENV+ structure... ', 0
+undi_data_msg db 'UNDI data segment at: ',0
+undi_data_len_msg db 'UNDI data segment size: ',0
+undi_code_msg db 'UNDI code segment at: ',0
+undi_code_len_msg db 'UNDI code segment size: ',0
+cant_free_msg db 'Failed to free base memory, error ', 0
+notfound_msg db 'not found', CR, LF, 0
+myipaddr_msg db 'My IP address seems to be ',0
+tftpprefix_msg db 'TFTP prefix: ', 0
+localboot_msg db 'Booting from local disk...', CR, LF, 0
+trying_msg db 'Trying to load: ', 0
+fourbs_msg db BS, BS, BS, BS, 0
+default_str db 'default', 0
+syslinux_banner db CR, LF, 'PXELINUX ', version_str, ' ', date, ' ', 0
+cfgprefix db 'pxelinux.cfg/' ; No final null!
+cfgprefix_len equ ($-cfgprefix)
+
+;
+; Command line options we'd like to take a look at
+;
+; mem= and vga= are handled as normal 32-bit integer values
+initrd_cmd db 'initrd='
+initrd_cmd_len equ $-initrd_cmd
+
+; This one we make ourselves
+bootif_str db 'BOOTIF='
+bootif_str_len equ $-bootif_str
+;
+; Config file keyword table
+;
+%include "keywords.inc"
+
+;
+; Extensions to search for (in *forward* order).
+; (.bs and .bss are disabled for PXELINUX, since they are not supported)
+;
+ align 4, db 0
+exten_table: db '.cbt' ; COMBOOT (specific)
+ db '.0', 0, 0 ; PXE bootstrap program
+ db '.com' ; COMBOOT (same as DOS)
+ db '.c32' ; COM32
+exten_table_end:
+ dd 0, 0 ; Need 8 null bytes here
+
+;
+; PXE unload sequences
+;
+new_api_unload:
+ db PXENV_UDP_CLOSE
+ db PXENV_UNDI_SHUTDOWN
+ db PXENV_UNLOAD_STACK
+ db PXENV_STOP_UNDI
+ db 0
+old_api_unload:
+ db PXENV_UDP_CLOSE
+ db PXENV_UNDI_SHUTDOWN
+ db PXENV_UNLOAD_STACK
+ db PXENV_UNDI_CLEANUP
+ db 0
+
+;
+; PXE query packets partially filled in
+;
+ section .bss
+pxe_bootp_query_pkt:
+.status: resw 1 ; Status
+.packettype: resw 1 ; Boot server packet type
+.buffersize: resw 1 ; Packet size
+.buffer: resw 2 ; seg:off of buffer
+.bufferlimit: resw 1 ; Unused
+
+ section .data
+pxe_udp_open_pkt:
+.status: dw 0 ; Status
+.sip: dd 0 ; Source (our) IP
+
+pxe_udp_close_pkt:
+.status: dw 0 ; Status
+
+pxe_udp_write_pkt:
+.status: dw 0 ; Status
+.sip: dd 0 ; Server IP
+.gip: dd 0 ; Gateway IP
+.lport: dw 0 ; Local port
+.rport: dw 0 ; Remote port
+.buffersize: dw 0 ; Size of packet
+.buffer: dw 0, 0 ; seg:off of buffer
+
+pxe_udp_read_pkt:
+.status: dw 0 ; Status
+.sip: dd 0 ; Source IP
+.dip: dd 0 ; Destination (our) IP
+.rport: dw 0 ; Remote port
+.lport: dw 0 ; Local port
+.buffersize: dw 0 ; Max packet size
+.buffer: dw 0, 0 ; seg:off of buffer
+
+%if GPXE
+
+gpxe_file_api_check:
+.status: dw 0 ; Status
+.size: dw 20 ; Size in bytes
+.magic: dd 0x91d447b2 ; Magic number
+.provider: dd 0
+.apimask: dd 0
+.flags: dd 0
+
+gpxe_file_open:
+.status: dw 0 ; Status
+.filehandle: dw 0 ; FileHandle
+.filename: dd 0 ; seg:off of FileName
+.reserved: dd 0
+
+gpxe_get_file_size:
+.status: dw 0 ; Status
+.filehandle: dw 0 ; FileHandle
+.filesize: dd 0 ; FileSize
+
+gpxe_file_read:
+.status: dw 0 ; Status
+.filehandle: dw 0 ; FileHandle
+.buffersize: dw 0 ; BufferSize
+.buffer: dd 0 ; seg:off of buffer
+
+%endif ; GPXE
+
+;
+; Misc initialized (data) variables
+;
+ alignb 4, db 0
+BaseStack dd StackBuf ; ESP of base stack
+ dw 0 ; SS of base stack
+NextSocket dw 49152 ; Counter for allocating socket numbers
+KeepPXE db 0 ; Should PXE be kept around?
+
+;
+; TFTP commands
+;
+tftp_tail db 'octet', 0 ; Octet mode
+tsize_str db 'tsize' ,0 ; Request size
+tsize_len equ ($-tsize_str)
+ db '0', 0
+blksize_str db 'blksize', 0 ; Request large blocks
+blksize_len equ ($-blksize_str)
+ asciidec TFTP_LARGEBLK
+ db 0
+tftp_tail_len equ ($-tftp_tail)
+
+ alignb 2, db 0
+;
+; Options negotiation parsing table (string pointer, string len, offset
+; into socket structure)
+;
+tftp_opt_table:
+ dw tsize_str, tsize_len, tftp_filesize
+ dw blksize_str, blksize_len, tftp_blksize
+tftp_opts equ ($-tftp_opt_table)/6
+
+;
+; Error packet to return on options negotiation error
+;
+tftp_opt_err dw TFTP_ERROR ; ERROR packet
+ dw TFTP_EOPTNEG ; ERROR 8: bad options
+ db 'tsize option required', 0 ; Error message
+tftp_opt_err_len equ ($-tftp_opt_err)
+
+ alignb 4, db 0
+ack_packet_buf: dw TFTP_ACK, 0 ; TFTP ACK packet
+
+;
+; IP information (initialized to "unknown" values)
+MyIP dd 0 ; My IP address
+ServerIP dd 0 ; IP address of boot server
+Netmask dd 0 ; Netmask of this subnet
+Gateway dd 0 ; Default router
+ServerPort dw TFTP_PORT ; TFTP server port
+
+;
+; Variables that are uninitialized in SYSLINUX but initialized here
+;
+ alignb 4, db 0
+BufSafe dw trackbufsize/TFTP_BLOCKSIZE ; Clusters we can load into trackbuf
+BufSafeBytes dw trackbufsize ; = how many bytes?
+%ifndef DEPEND
+%if ( trackbufsize % TFTP_BLOCKSIZE ) != 0
+%error trackbufsize must be a multiple of TFTP_BLOCKSIZE
+%endif
+%endif
diff --git a/core/rawcon.inc b/core/rawcon.inc
new file mode 100644
index 00000000..10d7a764
--- /dev/null
+++ b/core/rawcon.inc
@@ -0,0 +1,75 @@
+;
+; writechr: Write a single character in AL to the console without
+; mangling any registers. This does raw console writes,
+; since some PXE BIOSes seem to interfere regular console I/O.
+;
+%if IS_ISOLINUX
+writechr_full:
+%else
+writechr:
+%endif
+ push ds
+ push cs
+ pop ds
+ test byte [UsingVGA], 08h
+ jz .videook
+ call vgaclearmode
+.videook:
+ call write_serial ; write to serial port if needed
+ pushfd
+ test byte [DisplayCon],01h ; Write to screen?
+ jz .nothing
+
+ pushad
+ mov bh,[BIOS_page]
+ push ax
+ mov ah,03h ; Read cursor position
+ int 10h
+ pop ax
+ cmp al,8
+ je .bs
+ cmp al,13
+ je .cr
+ cmp al,10
+ je .lf
+ push dx
+ mov bh,[BIOS_page]
+ mov bl,07h ; White on black
+ mov cx,1 ; One only
+ mov ah,09h ; Write char and attribute
+ int 10h
+ pop dx
+ inc dl
+ cmp dl,[VidCols]
+ jna .curxyok
+ xor dl,dl
+.lf: inc dh
+ cmp dh,[VidRows]
+ ja .scroll
+.curxyok: mov bh,[BIOS_page]
+ mov ah,02h ; Set cursor position
+ int 10h
+.ret: popad
+.nothing:
+ popfd
+ pop ds
+ ret
+.scroll: dec dh
+ mov bh,[BIOS_page]
+ mov ah,02h
+ int 10h
+ mov ax,0601h ; Scroll up one line
+ mov bh,[ScrollAttribute]
+ xor cx,cx
+ mov dx,[ScreenSize] ; The whole screen
+ int 10h
+ jmp short .ret
+.cr: xor dl,dl
+ jmp short .curxyok
+.bs: sub dl,1
+ jnc .curxyok
+ mov dl,[VidCols]
+ sub dh,1
+ jnc .curxyok
+ xor dh,dh
+ jmp short .curxyok
diff --git a/core/regdump.inc b/core/regdump.inc
new file mode 100644
index 00000000..59a48c09
--- /dev/null
+++ b/core/regdump.inc
@@ -0,0 +1,108 @@
+;; -----------------------------------------------------------------------
+;;
+;; Copyright 2003-2008 H. Peter Anvin - All Rights Reserved
+;;
+;; This program is free software; you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation, Inc., 53 Temple Place Ste 330,
+;; Boston MA 02111-1307, USA; either version 2 of the License, or
+;; (at your option) any later version; incorporated herein by reference.
+;;
+;; -----------------------------------------------------------------------
+
+;;
+;; regdump.inc
+;;
+;; Dump as much as possible of the register state; for debugging
+;;
+
+disk_dumpregs:
+ mov ah,02h
+ call dumpregs
+ int 13h
+ ret
+
+dumpregs:
+ push gs
+ push fs
+ push es
+ push ds
+ push ss
+ push cs
+ pushad
+ pushfd
+
+ push cs
+ pop ds
+
+ mov bp,sp
+ mov di,regnames
+
+ mov cx,9 ; 9 32-bit registers
+.reg8:
+ mov si,[di]
+ inc di
+ inc di
+ call cwritestr
+ mov eax,[bp]
+ add bp,4
+ call writehex8
+ loop .reg8
+
+ mov cx,7 ; 6 16-bit registers
+.reg4:
+ mov si,[di]
+ inc di
+ inc di
+ call cwritestr
+ mov eax,[bp]
+ inc bp
+ inc bp
+ call writehex4
+ loop .reg4
+
+ call crlf
+
+ popfd
+ popad
+ add sp,4 ; Skip CS, SS
+ pop ds
+ pop es
+ pop fs
+ pop gs
+ ret
+
+regnames:
+ dw .eflags
+ dw .edi
+ dw .esi
+ dw .ebp
+ dw .esp
+ dw .ebx
+ dw .edx
+ dw .ecx
+ dw .eax
+ dw .cs
+ dw .ss
+ dw .ds
+ dw .es
+ dw .fs
+ dw .gs
+ dw .ip
+
+.eflags db 'EFL: ', 0
+.edi db 13,10,'EDI: ', 0
+.esi db ' ESI: ', 0
+.ebp db ' EBP: ', 0
+.esp db ' ESP: ', 0
+.ebx db 13,10,'EBX: ', 0
+.edx db ' EDX: ', 0
+.ecx db ' ECX: ', 0
+.eax db ' EAX: ', 0
+.cs db 13,10,'CS: ',0
+.ss db ' SS: ',0
+.ds db ' DS: ',0
+.es db ' ES: ',0
+.fs db ' FS: ',0
+.gs db ' GS: ',0
+.ip db ' IP: ',0
diff --git a/core/rllpack.inc b/core/rllpack.inc
new file mode 100644
index 00000000..a556e00a
--- /dev/null
+++ b/core/rllpack.inc
@@ -0,0 +1,156 @@
+; -*- fundamental -*- ---------------------------------------------------
+;
+; Copyright 2004-2008 H. Peter Anvin - All Rights Reserved
+;
+; This program is free software; you can redistribute it and/or modify
+; it under the terms of the GNU General Public License as published by
+; the Free Software Foundation, Inc., 53 Temple Place Ste 330,
+; Boston MA 02111-1307, USA; either version 2 of the License, or
+; (at your option) any later version; incorporated herein by reference.
+;
+; -----------------------------------------------------------------------
+
+;
+; rllpack.inc
+;
+; Very simple RLL compressor/decompressor, used to pack binary structures
+; together.
+;
+; Format of leading byte
+; 1-128 = x verbatim bytes follow
+; 129-223 = (x-126) times subsequent byte
+; 224-255 = (x-224)*256+(next byte) times the following byte
+; 0 = end of data
+;
+; These structures are stored *in reverse order* in high memory.
+; High memory pointers point to one byte beyond the end.
+;
+
+ section .text
+
+;
+; rllpack:
+; Pack CX bytes from SI into EDI.
+; Returns updated SI and EDI.
+;
+rllpack:
+ push word .pmentry
+ call simple_pm_call
+ ret
+
+.pmentry:
+ push cx
+ push ebx
+ push edx
+.startseq:
+ xor ax,ax ; Zero byte
+ xor ebx,ebx ; Run length zero
+ dec edi
+ mov edx,edi ; Pointer to header byte
+ mov [edi],al ; Create header byte
+ jcxz .done ; If done, this was the terminator
+.stdbyte:
+ lodsb
+ dec edi
+ mov [edi],al
+ dec cx
+ cmp ah,al
+ je .same
+.diff:
+ mov ah,al
+ xor bx,bx
+.plainbyte:
+ inc bx
+ inc byte [edx]
+ jcxz .startseq
+ jns .stdbyte
+ jmp .startseq
+.same:
+ cmp bl,2
+ jb .plainbyte
+ ; 3 bytes or more in a row, time to convert sequence
+ sub [edx],bl
+ jnz .normal
+ inc edi ; We killed a whole stretch,
+ ; drop start byte
+.normal:
+ inc bx
+ add edi,ebx ; Remove the stored run bytes
+.getrun:
+ jcxz .nomatch
+ lodsb
+ cmp al,ah
+ jne .nomatch
+ cmp bx,(256-224)*256-1 ; Maximum run size
+ jae .nomatch
+ inc bx
+ dec cx
+ jmp .getrun
+.nomatch:
+ cmp bx,224-126
+ jae .twobyte
+.onebyte:
+ add bl,126
+ dec edi
+ mov [edi],bl
+ jmp .storebyte
+.twobyte:
+ add bh,224
+ sub edi,2
+ mov [edi],bx
+.storebyte:
+ dec edi
+ mov [edi],ah
+ dec si ; Reload subsequent byte
+ jmp .startseq
+.done:
+ pop edx
+ pop ebx
+ pop cx
+ ret
+;
+; rllunpack:
+; Unpack bytes from ESI into DI
+; On return ESI, DI are updated and CX contains number of bytes output.
+;
+rllunpack:
+ push word .pmentry
+ call simple_pm_call
+ ret
+
+.pmentry:
+ push di
+ xor cx,cx
+.header:
+ dec esi
+ mov cl,[esi]
+ jcxz .done
+ cmp cl,129
+ jae .isrun
+ ; Not a run
+.copy:
+ dec esi
+ mov al,[esi]
+ stosb
+ loop .copy
+ jmp .header
+.isrun:
+ cmp cl,224
+ jae .longrun
+ sub cl,126
+.dorun:
+ dec esi
+ mov al,[esi]
+ rep stosb
+ jmp .header
+.longrun:
+ sub cl,224
+ mov ch,cl
+ dec esi
+ mov cl,[esi]
+ jmp .dorun
+.done:
+ pop cx
+ sub cx,di
+ neg cx
+ ret
diff --git a/core/runkernel.inc b/core/runkernel.inc
new file mode 100644
index 00000000..abd23782
--- /dev/null
+++ b/core/runkernel.inc
@@ -0,0 +1,634 @@
+;; -----------------------------------------------------------------------
+;;
+;; Copyright 1994-2008 H. Peter Anvin - All Rights Reserved
+;;
+;; This program is free software; you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation, Inc., 53 Temple Place Ste 330,
+;; Boston MA 02111-1307, USA; either version 2 of the License, or
+;; (at your option) any later version; incorporated herein by reference.
+;;
+;; -----------------------------------------------------------------------
+
+;;
+;; runkernel.inc
+;;
+;; Common code for running a Linux kernel
+;;
+
+;
+; Hook macros, that may or may not be defined
+;
+%ifndef HAVE_SPECIAL_APPEND
+%macro SPECIAL_APPEND 0
+%endmacro
+%endif
+
+%ifndef HAVE_UNLOAD_PREP
+%macro UNLOAD_PREP 0
+%endmacro
+%endif
+
+;
+; A Linux kernel consists of three parts: boot sector, setup code, and
+; kernel code. The boot sector is never executed when using an external
+; booting utility, but it contains some status bytes that are necessary.
+;
+; First check that our kernel is at least 1K, or else it isn't long
+; enough to have the appropriate headers.
+;
+; We used to require the kernel to be 64K or larger, but it has gotten
+; popular to use the Linux kernel format for other things, which may
+; not be so large.
+;
+; Additionally, we used to have a test for 8 MB or smaller. Equally
+; obsolete.
+;
+is_linux_kernel:
+ push si ; <A> file pointer
+ mov si,loading_msg
+ call cwritestr
+ mov si,KernelCName ; Print kernel name part of
+ call cwritestr ; "Loading" message
+
+
+;
+; Now start transferring the kernel
+;
+ push word real_mode_seg
+ pop es
+
+;
+; Start by loading the bootsector/setup code, to see if we need to
+; do something funky. It should fit in the first 32K (loading 64K won't
+; work since we might have funny stuff up near the end of memory).
+;
+ call dot_pause ; Check for abort key
+ mov cx,8000h >> SECTOR_SHIFT ; Half a moby (32K)
+ xor bx,bx
+ pop si ; <A> file pointer
+ call getfssec
+ cmp cx,1024
+ jb kernel_corrupt
+ cmp word [es:bs_bootsign],0AA55h
+ jne kernel_corrupt ; Boot sec signature missing
+
+;
+; Save the file pointer for later...
+;
+ push si ; <A> file pointer
+
+;
+; Construct the command line (append options have already been copied)
+;
+construct_cmdline:
+ mov di,[CmdLinePtr]
+ mov si,boot_image ; BOOT_IMAGE=
+ mov cx,boot_image_len
+ rep movsb
+ mov si,KernelCName ; Unmangled kernel name
+ mov cx,[KernelCNameLen]
+ rep movsb
+ mov al,' ' ; Space
+ stosb
+
+ SPECIAL_APPEND ; Module-specific hook
+
+ mov si,[CmdOptPtr] ; Options from user input
+ call strcpy
+
+;
+; Scan through the command line for anything that looks like we might be
+; interested in. The original version of this code automatically assumed
+; the first option was BOOT_IMAGE=, but that is no longer certain.
+;
+ mov si,cmd_line_here
+ xor ax,ax
+ mov [InitRDPtr],ax ; No initrd= option (yet)
+ push es ; Set DS <- real_mode_seg
+ pop ds
+get_next_opt: lodsb
+ and al,al
+ jz cmdline_end
+ cmp al,' '
+ jbe get_next_opt
+ dec si
+ mov eax,[si]
+ cmp eax,'vga='
+ je is_vga_cmd
+ cmp eax,'mem='
+ je is_mem_cmd
+%if IS_PXELINUX
+ cmp eax,'keep' ; Is it "keeppxe"?
+ jne .notkeep
+ cmp dword [si+3],'ppxe'
+ jne .notkeep
+ cmp byte [si+7],' ' ; Must be whitespace or EOS
+ ja .notkeep
+ or byte [cs:KeepPXE],1
+.notkeep:
+%endif
+ push es ; <B> ES -> real_mode_seg
+ push cs
+ pop es ; Set ES <- normal DS
+ mov di,initrd_cmd
+ mov cx,initrd_cmd_len
+ repe cmpsb
+ jne .not_initrd
+
+ cmp al,' '
+ jbe .noramdisk
+ mov [cs:InitRDPtr],si
+ jmp .not_initrd
+.noramdisk:
+ xor ax,ax
+ mov [cs:InitRDPtr],ax
+.not_initrd: pop es ; <B> ES -> real_mode_seg
+skip_this_opt: lodsb ; Load from command line
+ cmp al,' '
+ ja skip_this_opt
+ dec si
+ jmp short get_next_opt
+is_vga_cmd:
+ add si,4
+ mov eax,[si-1]
+ mov bx,-1
+ cmp eax,'=nor' ; vga=normal
+ je vc0
+ dec bx ; bx <- -2
+ cmp eax,'=ext' ; vga=ext
+ je vc0
+ dec bx ; bx <- -3
+ cmp eax,'=ask' ; vga=ask
+ je vc0
+ call parseint ; vga=<number>
+ jc skip_this_opt ; Not an integer
+vc0: mov [bs_vidmode],bx ; Set video mode
+ jmp short skip_this_opt
+is_mem_cmd:
+ add si,4
+ call parseint
+ jc skip_this_opt ; Not an integer
+%if HIGHMEM_SLOP != 0
+ sub ebx,HIGHMEM_SLOP
+%endif
+ mov [cs:MyHighMemSize],ebx
+ jmp short skip_this_opt
+cmdline_end:
+ push cs ; Restore standard DS
+ pop ds
+ sub si,cmd_line_here
+ mov [CmdLineLen],si ; Length including final null
+;
+; Now check if we have a large kernel, which needs to be loaded high
+;
+prepare_header:
+ mov dword [RamdiskMax], HIGHMEM_MAX ; Default initrd limit
+ cmp dword [es:su_header],HEADER_ID ; New setup code ID
+ jne old_kernel ; Old kernel, load low
+ mov ax,[es:su_version]
+ mov [KernelVersion],ax
+ cmp ax,0200h ; Setup code version 2.0
+ jb old_kernel ; Old kernel, load low
+ cmp ax,0201h ; Version 2.01+?
+ jb new_kernel ; If 2.00, skip this step
+ ; Set up the heap (assuming loading high for now)
+ mov word [es:su_heapend],linux_stack-512
+ or byte [es:su_loadflags],80h ; Let the kernel know we care
+ cmp ax,0203h ; Version 2.03+?
+ jb new_kernel ; Not 2.03+
+ mov eax,[es:su_ramdisk_max]
+ mov [RamdiskMax],eax ; Set the ramdisk limit
+
+;
+; We definitely have a new-style kernel. Let the kernel know who we are,
+; and that we are clueful
+;
+new_kernel:
+ mov byte [es:su_loader],my_id ; Show some ID
+ xor eax,eax
+ mov [es:su_ramdisklen],eax ; No initrd loaded yet
+
+;
+; About to load the kernel. This is a modern kernel, so use the boot flags
+; we were provided.
+;
+ mov al,[es:su_loadflags]
+ mov [LoadFlags],al
+
+ ; Cap the ramdisk memory range if appropriate
+ mov eax,[RamdiskMax]
+ cmp eax,[MyHighMemSize]
+ ja .ok
+ mov [MyHighMemSize],eax
+.ok:
+
+any_kernel:
+
+;
+; Load the kernel. We always load it at 100000h even if we're supposed to
+; load it "low"; for a "low" load we copy it down to low memory right before
+; jumping to it.
+;
+read_kernel:
+ movzx ax,byte [es:bs_setupsecs] ; Setup sectors
+ and ax,ax
+ jnz .sects_ok
+ mov al,4 ; 0 = 4 setup sectors
+.sects_ok:
+ inc ax ; Including the boot sector
+ mov [SetupSecs],ax
+
+ call dot_pause
+
+;
+; Move the stuff beyond the setup code to high memory at 100000h
+;
+ movzx esi,word [SetupSecs] ; Setup sectors
+ shl si,9 ; Convert to bytes
+ mov ecx,8000h ; 32K
+ sub ecx,esi ; Number of bytes to copy
+ add esi,(real_mode_seg << 4) ; Pointer to source
+ mov edi,100000h ; Copy to address 100000h
+
+ call bcopy ; Transfer to high memory
+
+ pop si ; <A> File pointer
+ and si,si ; EOF already?
+ jz high_load_done
+
+ ; On exit EDI -> where to load the rest
+
+ mov bx,dot_pause
+ or eax,-1 ; Load the whole file
+ mov dx,3 ; Pad to dword
+ call load_high
+
+high_load_done:
+ mov [KernelEnd],edi
+ mov ax,real_mode_seg ; Set to real mode seg
+ mov es,ax
+
+ mov si,dot_msg
+ call cwritestr
+;
+; Some older kernels (1.2 era) would have more than 4 setup sectors, but
+; would not rely on the boot protocol to manage that. These kernels fail
+; if they see protected-mode kernel data after the setup sectors, so
+; clear that memory.
+;
+ mov di,[SetupSecs]
+ shl di,9
+ xor eax,eax
+ mov cx,cmd_line_here
+ sub cx,di
+ shr cx,2
+ rep stosd
+
+;
+; Now see if we have an initial RAMdisk; if so, do requisite computation
+; We know we have a new kernel; the old_kernel code already will have objected
+; if we tried to load initrd using an old kernel
+;
+load_initrd:
+ xor eax,eax
+ cmp [InitRDPtr],ax
+ jz .noinitrd
+ call parse_load_initrd
+.noinitrd:
+
+;
+; Abandon hope, ye that enter here! We do no longer permit aborts.
+;
+ call abort_check ; Last chance!!
+
+ mov si,ready_msg
+ call cwritestr
+
+ UNLOAD_PREP ; Module-specific hook
+
+;
+; Now, if we were supposed to load "low", copy the kernel down to 10000h
+; and the real mode stuff to 90000h. We assume that all bzImage kernels are
+; capable of starting their setup from a different address.
+;
+ mov ax,real_mode_seg
+ mov es,ax
+ mov fs,ax
+
+;
+; If the default root device is set to FLOPPY (0000h), change to
+; /dev/fd0 (0200h)
+;
+ cmp word [es:bs_rootdev],byte 0
+ jne root_not_floppy
+ mov word [es:bs_rootdev],0200h
+root_not_floppy:
+
+;
+; Copy command line. Unfortunately, the old kernel boot protocol requires
+; the command line to exist in the 9xxxxh range even if the rest of the
+; setup doesn't.
+;
+setup_command_line:
+ mov dx,[KernelVersion]
+ test byte [LoadFlags],LOAD_HIGH
+ jz .need_high_cmdline
+ cmp dx,0202h ; Support new cmdline protocol?
+ jb .need_high_cmdline
+ ; New cmdline protocol
+ ; Store 32-bit (flat) pointer to command line
+ ; This is the "high" location, since we have bzImage
+ mov dword [fs:su_cmd_line_ptr],(real_mode_seg << 4)+cmd_line_here
+ mov word [HeapEnd],linux_stack
+ mov word [fs:su_heapend],linux_stack-512
+ jmp .setup_done
+
+.need_high_cmdline:
+;
+; Copy command line down to fit in high conventional memory
+; -- this happens if we have a zImage kernel or the protocol
+; is less than 2.02.
+;
+ mov si,cmd_line_here
+ mov di,old_cmd_line_here
+ mov [fs:kern_cmd_magic],word CMD_MAGIC ; Store magic
+ mov [fs:kern_cmd_offset],di ; Store pointer
+ mov word [HeapEnd],old_linux_stack
+ mov ax,255 ; Max cmdline limit
+ cmp dx,0201h
+ jb .adjusted
+ ; Protocol 2.01+
+ mov word [fs:su_heapend],old_linux_stack-512
+ jbe .adjusted
+ ; Protocol 2.02+
+ ; Note that the only reason we would end up here is
+ ; because we have a zImage, so we anticipate the move
+ ; to 90000h already...
+ mov dword [fs:su_cmd_line_ptr],0x90000+old_cmd_line_here
+ mov ax,old_max_cmd_len ; 2.02+ allow a higher limit
+.adjusted:
+
+ mov cx,[CmdLineLen]
+ cmp cx,ax
+ jna .len_ok
+ mov cx,ax ; Truncate the command line
+.len_ok:
+ fs rep movsb
+ stosb ; Final null, note AL=0 already
+ mov [CmdLineEnd],di
+ cmp dx,0200h
+ jb .nomovesize
+ mov [es:su_movesize],di ; Tell the kernel what to move
+.nomovesize:
+.setup_done:
+
+;
+; Time to start setting up move descriptors
+;
+setup_move:
+ mov di,trackbuf
+ xor cx,cx ; Number of descriptors
+
+ mov bx,es ; real_mode_seg
+ mov fs,bx
+ push ds ; We need DS == ES == CS here
+ pop es
+
+ test byte [LoadFlags],LOAD_HIGH
+ jnz .loading_high
+
+; Loading low: move real_mode stuff to 90000h, then move the kernel down
+ mov eax,90000h
+ stosd
+ mov eax,real_mode_seg << 4
+ stosd
+ movzx eax,word [CmdLineEnd]
+ stosd
+ inc cx
+
+ mov eax,10000h ; Target address of low kernel
+ stosd
+ mov eax,100000h ; Where currently loaded
+ stosd
+ neg eax
+ add eax,[KernelEnd]
+ stosd
+ inc cx
+
+ mov bx,9000h ; Revised real mode segment
+
+.loading_high:
+
+ cmp word [InitRDPtr],0 ; Did we have an initrd?
+ je .no_initrd
+
+ mov eax,[fs:su_ramdiskat]
+ stosd
+ mov eax,[InitRDStart]
+ stosd
+ mov eax,[fs:su_ramdisklen]
+ stosd
+ inc cx
+
+.no_initrd:
+ push cx ; Length of descriptor list
+ push word trackbuf
+
+ mov dword [EntryPoint],run_linux_kernel
+ ; BX points to the final real mode segment, and will be loaded
+ ; into DS.
+ jmp replace_bootstrap
+
+
+run_linux_kernel:
+;
+; Set up segment registers and the Linux real-mode stack
+; Note: ds == the real mode segment
+;
+ cli
+ mov ax,ds
+ mov ss,ax
+ mov sp,strict word linux_stack
+ ; Point HeapEnd to the immediate of the instruction above
+HeapEnd equ $-2 ; Self-modifying code! Fun!
+ mov es,ax
+ mov fs,ax
+ mov gs,ax
+
+;
+; We're done... now RUN THAT KERNEL!!!!
+; Setup segment == real mode segment + 020h; we need to jump to offset
+; zero in the real mode segment.
+;
+ add ax,020h
+ push ax
+ push word 0h
+ retf
+
+;
+; Load an older kernel. Older kernels always have 4 setup sectors, can't have
+; initrd, and are always loaded low.
+;
+old_kernel:
+ xor ax,ax
+ cmp word [InitRDPtr],ax ; Old kernel can't have initrd
+ je .load
+ mov si,err_oldkernel
+ jmp abort_load
+.load:
+ mov byte [LoadFlags],al ; Always low
+ mov word [KernelVersion],ax ; Version 0.00
+ jmp any_kernel
+
+;
+; parse_load_initrd
+;
+; Parse an initrd= option and load the initrds. This sets
+; InitRDStart and InitRDEnd with dword padding between; we then
+; do a global memory shuffle to move it to the end of memory.
+;
+; On entry, EDI points to where to start loading.
+;
+parse_load_initrd:
+ push es
+ push ds
+ mov ax,real_mode_seg
+ mov ds,ax
+ push cs
+ pop es ; DS == real_mode_seg, ES == CS
+
+ mov [cs:InitRDStart],edi
+ mov [cs:InitRDEnd],edi
+
+ mov si,[cs:InitRDPtr]
+
+.get_chunk:
+ ; DS:SI points to the start of a name
+
+ mov bx,si
+.find_end:
+ lodsb
+ cmp al,','
+ je .got_end
+ cmp al,' '
+ jbe .got_end
+ jmp .find_end
+
+.got_end:
+ push ax ; Terminating character
+ push si ; Next filename (if any)
+ mov byte [si-1],0 ; Zero-terminate
+ mov si,bx ; Current filename
+
+ push di
+ mov di,InitRD ; Target buffer for mangled name
+ call mangle_name
+ pop di
+ call loadinitrd
+
+ pop si
+ pop ax
+ mov [si-1],al ; Restore ending byte
+
+ cmp al,','
+ je .get_chunk
+
+ ; Compute the initrd target location
+ mov edx,[cs:InitRDEnd]
+ sub edx,[cs:InitRDStart]
+ mov [su_ramdisklen],edx
+ mov eax,[cs:MyHighMemSize]
+ sub eax,edx
+ and ax,0F000h ; Round to a page boundary
+ mov [su_ramdiskat],eax
+
+ pop ds
+ pop es
+ ret
+
+;
+; Load RAM disk into high memory
+;
+; Input: InitRD - set to the mangled name of the initrd
+; EDI - location to load
+; Output: EDI - location for next initrd
+; InitRDEnd - updated
+;
+loadinitrd:
+ push ds
+ push es
+ mov ax,cs ; CS == DS == ES
+ mov ds,ax
+ mov es,ax
+ push edi
+ mov si,InitRD
+ mov di,InitRDCName
+ call unmangle_name ; Create human-readable name
+ sub di,InitRDCName
+ mov [InitRDCNameLen],di
+ mov di,InitRD
+ call searchdir ; Look for it in directory
+ pop edi
+ jz .notthere
+
+ push si
+ mov si,crlfloading_msg ; Write "Loading "
+ call cwritestr
+ mov si,InitRDCName ; Write ramdisk name
+ call cwritestr
+ mov si,dotdot_msg ; Write dots
+ call cwritestr
+ pop si
+
+ mov dx,3
+ mov bx,dot_pause
+ call load_high
+ mov [InitRDEnd],ebx
+
+ pop es
+ pop ds
+ ret
+
+.notthere:
+ mov si,err_noinitrd
+ call cwritestr
+ mov si,InitRDCName
+ call cwritestr
+ mov si,crlf_msg
+ jmp abort_load
+
+no_high_mem: ; Error routine
+ mov si,err_nohighmem
+ jmp abort_load
+
+ ret
+
+ section .data
+crlfloading_msg db CR, LF
+loading_msg db 'Loading ', 0
+dotdot_msg db '.'
+dot_msg db '.', 0
+ready_msg db 'ready.', CR, LF, 0
+err_oldkernel db 'Cannot load a ramdisk with an old kernel image.'
+ db CR, LF, 0
+err_noinitrd db CR, LF, 'Could not find ramdisk image: ', 0
+
+boot_image db 'BOOT_IMAGE='
+boot_image_len equ $-boot_image
+
+ section .bss
+ alignb 4
+MyHighMemSize resd 1 ; Possibly adjusted highmem size
+RamdiskMax resd 1 ; Highest address for ramdisk
+KernelSize resd 1 ; Size of kernel in bytes
+KernelSects resd 1 ; Size of kernel in sectors
+KernelEnd resd 1 ; Ending address of the kernel image
+InitRDStart resd 1 ; Start of initrd (pre-relocation)
+InitRDEnd resd 1 ; End of initrd (pre-relocation)
+CmdLineLen resw 1 ; Length of command line including null
+CmdLineEnd resw 1 ; End of the command line in real_mode_seg
+SetupSecs resw 1 ; Number of setup sectors (+bootsect)
+InitRDPtr resw 1 ; Pointer to initrd= option in command line
+KernelVersion resw 1 ; Kernel protocol version
+LoadFlags resb 1 ; Loadflags from kernel
diff --git a/core/stack.inc b/core/stack.inc
new file mode 100644
index 00000000..f670dec0
--- /dev/null
+++ b/core/stack.inc
@@ -0,0 +1,47 @@
+; -----------------------------------------------------------------------
+;
+; Copyright 2005-2008 H. Peter Anvin - All Rights Reserved
+;
+; This program is free software; you can redistribute it and/or modify
+; it under the terms of the GNU General Public License as published by
+; the Free Software Foundation, Inc., 53 Temple Place Ste 330,
+; Boston MA 02111-1307, USA; either version 2 of the License, or
+; (at your option) any later version; incorporated herein by reference.
+;
+; -----------------------------------------------------------------------
+
+;
+; stack.inc
+;
+; How to reset the stack pointer
+;
+
+%ifndef _STACK_INC
+%define _STACK_INC
+
+;
+; This macro resets the stack pointer (including SS), and sets
+; DS == ES == 0, interrupts on, DF = 0.
+;
+; It takes a 16-bit register that can be safely clobbered as parameter.
+;
+%macro RESET_STACK_AND_SEGS 1
+ xor %1,%1
+ mov ds,%1
+ mov es,%1
+%if IS_SYSLINUX || IS_EXTLINUX
+ mov ss,%1 ; Just in case...
+ mov sp,StackBuf-2*5 ; Reset stack
+%elif IS_PXELINUX
+ lss esp,[BaseStack]
+%elif IS_ISOLINUX
+ mov ss,%1
+ mov sp,StackBuf-2*2
+%else
+ NEED TO KNOW HOW TO RESET STACK
+%endif
+ sti
+ cld
+%endmacro
+
+%endif ; _STACK_INC
diff --git a/core/strcpy.inc b/core/strcpy.inc
new file mode 100644
index 00000000..2bafa7fe
--- /dev/null
+++ b/core/strcpy.inc
@@ -0,0 +1,13 @@
+;
+; strcpy: Copy DS:SI -> ES:DI up to and including a null byte;
+; on exit SI and DI point to the byte *after* the null byte
+;
+ section .text
+
+strcpy: push ax
+.loop: lodsb
+ stosb
+ and al,al
+ jnz .loop
+ pop ax
+ ret
diff --git a/core/strecpy.inc b/core/strecpy.inc
new file mode 100644
index 00000000..1fc53e96
--- /dev/null
+++ b/core/strecpy.inc
@@ -0,0 +1,28 @@
+;
+; strecpy: Copy DS:SI -> ES:DI up to and including a null byte;
+; on exit SI and DI point to the byte *after* the null byte.
+; BP holds a pointer to the first byte beyond the end of the
+; target buffer; return with CF=1 if target buffer overflows;
+; the output is still zero-terminated.
+;
+ section .text
+
+strecpy:
+ push ax
+ push bp
+ dec bp
+ dec bp
+.loop: lodsb
+ stosb
+ and al,al ; CF=0
+ jz .done
+ cmp bp,di ; CF set if BP < DI
+ jnc .loop
+
+ ; Zero-terminate overflow string
+ mov al,0 ; Avoid changing flags
+ stosb
+.done:
+ pop bp
+ pop ax
+ ret
diff --git a/core/syslinux.ld b/core/syslinux.ld
new file mode 100644
index 00000000..3c55820f
--- /dev/null
+++ b/core/syslinux.ld
@@ -0,0 +1,119 @@
+/*
+ * Linker script for the SYSLINUX core
+ */
+
+OUTPUT_FORMAT("elf32-i386", "elf32-i386", "elf32-i386")
+OUTPUT_ARCH(i386)
+EXTERN(_start)
+ENTRY(_start)
+
+STACK_LEN = 4096;
+
+SECTIONS
+{
+ /* "Early" sections (before the load) */
+ . = 0x0800;
+
+ .earlybss : {
+ __earlybss_start = .;
+ *(.earlybss)
+ __earlybss_end = .;
+ }
+ __earlybss_len = __earlybss_end - __earlybss_start;
+ __earlybss_dwords = (__earlybss_len + 3) >> 2;
+
+ .bcopy32 : AT (__bcopy32_lma) {
+ FILL(0x90909090)
+ __bcopy32_start = .;
+ *(.bcopy32)
+ __bcopy32_end = .;
+ }
+ __bcopy32_len = __bcopy32_end - __bcopy32_start;
+ __bcopy32_dwords = (__bcopy32_len + 3) >> 2;
+
+ .config : AT (__config_lma) {
+ __config_start = .;
+ *(.config)
+ __config_end = .;
+ }
+ __config_len = __config_end - __config_start;
+ __config_dwords = (__config_len + 3) >> 2;
+
+ .bss : AT(__bss_start) {
+ __bss_start = .;
+ *(.bss)
+ *(.bss2)
+ __bss_end = .;
+ }
+ __bss_len = __bss_end - __bss_start;
+ __bss_dwords = (__bss_len + 3) >> 2;
+
+ /* Stack */
+
+ . = 0x7c00 - STACK_LEN;
+ .stack : {
+ __stack_start = .;
+ . += STACK_LEN;
+ __stack_end = .;
+ }
+ __stack_len = __stack_end - __stack_start;
+ __stack_dwords = (__stack_len + 3) >> 2;
+
+ /* Initialized sections */
+
+ . = 0x7c00;
+ .text : {
+ FILL(0x90909090)
+ __text_start = .;
+ *(.text)
+ __text_end = .;
+ }
+ __text_len = __text_end - __text_start;
+ __text_dwords = (__text_len + 3) >> 2;
+
+ . = ALIGN(4);
+ __bcopy32_lma = .;
+ . += SIZEOF(.bcopy32);
+
+ . = ALIGN(4);
+ .data : {
+ __data_start = .;
+ *(.data)
+ __data_end = .;
+ }
+ __data_len = __data_end - __data_start;
+ __data_dwords = (__data_len + 3) >> 2;
+
+ . = ALIGN(4);
+ __config_lma = .;
+ . += SIZEOF(.config);
+
+ /* ADV, must be the last intialized section */
+
+ . = ALIGN(512);
+ .adv : {
+ __adv_start = .;
+ *(.adv)
+ __adv_end = .;
+ }
+ __adv_len = __adv_end - __adv_start;
+ __adv_dwords = (__adv_len + 3) >> 2;
+
+ /* Late uninitialized sections */
+
+ .uibss : {
+ __uibss_start = .;
+ *(.uibss)
+ __uibss_end = .;
+ }
+ __uibss_len = __uibss_end - __uibss_start;
+ __uibss_dwords = (__uibss_len + 3) >> 2;
+
+ .bss1 : {
+ __bss1_start = .;
+ *(.bss1)
+ __bss1_end = .;
+ }
+ __bss1_len = __bss1_end - __bss1_start;
+ __bss1_dwords = (__bss1_len + 3) >> 2;
+}
diff --git a/core/tracers.inc b/core/tracers.inc
new file mode 100644
index 00000000..a51209fa
--- /dev/null
+++ b/core/tracers.inc
@@ -0,0 +1,40 @@
+;; -----------------------------------------------------------------------
+;;
+;; Copyright 1994-2008 H. Peter Anvin - All Rights Reserved
+;;
+;; This program is free software; you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation, Inc., 53 Temple Place Ste 330,
+;; Boston MA 02111-1307, USA; either version 2 of the License, or
+;; (at your option) any later version; incorporated herein by reference.
+;;
+;; -----------------------------------------------------------------------
+
+;;
+;; tracers.inc
+;;
+;; Debugging tracers
+;;
+
+%ifndef _TRACERS_INC
+%define _TRACERS_INC
+
+; Note: The Makefile builds one version with DEBUG_MESSAGES automatically.
+; %define DEBUG_TRACERS 1 ; Uncomment to get debugging tracers
+; %define DEBUG_MESSAGES ; Uncomment to get debugging messages
+
+%ifdef DEBUG_TRACERS
+
+%macro TRACER 1
+ call debug_tracer
+ db %1
+%endmacro
+
+%else ; DEBUG_TRACERS
+
+%macro TRACER 1
+%endmacro
+
+%endif ; DEBUG_TRACERS
+
+%endif ; _TRACERS_INC
diff --git a/core/ui.inc b/core/ui.inc
new file mode 100644
index 00000000..3fc3e639
--- /dev/null
+++ b/core/ui.inc
@@ -0,0 +1,683 @@
+;; -----------------------------------------------------------------------
+;;
+;; Copyright 1994-2008 H. Peter Anvin - All Rights Reserved
+;;
+;; This program is free software; you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation, Inc., 53 Temple Place Ste 330,
+;; Boston MA 02111-1307, USA; either version 2 of the License, or
+;; (at your option) any later version; incorporated herein by reference.
+;;
+;; -----------------------------------------------------------------------
+
+;
+; This file should be entered with the config file open (for getc)
+;
+load_config_file:
+ call parse_config ; Parse configuration file
+no_config_file:
+
+ call adv_init
+;
+; Check for an ADV boot-once entry
+;
+ mov dl,ADV_BOOTONCE
+ call adv_get
+ jcxz .no_bootonce
+
+.have_bootone:
+ ; We apparently have a boot-once set; clear it and
+ ; then execute the boot-once...
+
+ ; Save the boot-once data; SI = data, CX = length
+ mov di,command_line
+ rep movsb
+ xor ax,ax
+ stosb
+
+ ; Clear the boot-once data from the ADV
+ xor cx,cx ; Set to zero = delete
+ call adv_set
+ jc .err
+ call adv_write
+.err: jmp load_kernel
+
+.no_bootonce:
+
+;
+; Check whether or not we are supposed to display the boot prompt.
+;
+check_for_key:
+ cmp word [ForcePrompt],0 ; Force prompt?
+ jnz enter_command
+ test byte [KbdFlags],5Bh ; Shift Alt Caps Scroll
+ jz auto_boot ; If neither, default boot
+
+enter_command:
+ cmp word [NoEscape],0 ; If NOESCAPE, no prompt,
+ jne auto_boot ; always run default cmd
+
+ mov si,boot_prompt
+ call cwritestr
+
+ mov byte [FuncFlag],0 ; <Ctrl-F> not pressed
+ mov di,command_line
+
+;
+; get the very first character -- we can either time
+; out, or receive a character press at this time. Some dorky BIOSes stuff
+; a return in the buffer on bootup, so wipe the keyboard buffer first.
+;
+clear_buffer: mov ah,11h ; Check for pending char
+ int 16h
+ jz get_char_time
+ mov ah,10h ; Get char
+ int 16h
+ jmp short clear_buffer
+
+ ; For the first character, both KbdTimeout and
+ ; TotalTimeout apply; after that, only TotalTimeout.
+
+get_char_time:
+ mov eax,[TotalTimeout]
+ mov [ThisTotalTo],eax
+ mov eax,[KbdTimeout]
+ mov [ThisKbdTo],eax
+
+get_char:
+ call getchar_timeout
+ and dword [ThisKbdTo],0 ; For the next time...
+
+ and al,al
+ jz func_key
+
+got_ascii: cmp al,7Fh ; <DEL> == <BS>
+ je backspace
+ cmp al,' ' ; ASCII?
+ jb not_ascii
+ ja enter_char
+ cmp di,command_line ; Space must not be first
+ je short get_char
+enter_char: test byte [FuncFlag],1
+ jnz ctrl_f ; Keystroke after <Ctrl-F>
+ cmp di,max_cmd_len+command_line ; Check there's space
+ jnb short get_char
+ stosb ; Save it
+ call writechr ; Echo to screen
+ jmp short get_char
+not_ascii:
+ cmp al,0Dh ; Enter
+ je command_done
+ cmp al,'F' & 1Fh ; <Ctrl-F>
+ je set_func_flag
+%if IS_PXELINUX
+ cmp al,'N' & 1Fh ; <Ctrl-N>
+ je show_network_info
+%endif
+ cmp al,'U' & 1Fh ; <Ctrl-U>
+ je kill_command ; Kill input line
+ cmp al,'V' & 1Fh ; <Ctrl-V>
+ je print_version
+ cmp al,'X' & 1Fh ; <Ctrl-X>
+ je force_text_mode
+ cmp al,08h ; Backspace
+ jne get_char
+backspace: cmp di,command_line ; Make sure there is anything
+ je get_char ; to erase
+ dec di ; Unstore one character
+ mov si,wipe_char ; and erase it from the screen
+ call cwritestr
+get_char_2:
+ jmp short get_char
+
+kill_command:
+ call crlf
+ jmp enter_command
+
+force_text_mode:
+ call vgaclearmode
+ jmp enter_command
+
+set_func_flag:
+ mov byte [FuncFlag],1
+ jmp short get_char_2
+
+ctrl_f:
+ xor ah,ah
+ mov [FuncFlag],ah
+ cmp al,'0'
+ jb get_char_2
+ je .zero ; <Ctrl-F>0 = F10
+ or al,20h ; Lower case
+ cmp al,'9'
+ jna .digit
+ cmp al,'a' ; F10-F12 = <Ctrl-F>A, B, C
+ jb get_char_2
+ cmp al,'c'
+ ja get_char_2
+ sub al,'a'-10
+ jmp show_help
+.zero:
+ mov al,10
+ jmp show_help
+.digit:
+ sub al,'1'
+ jmp show_help
+
+func_key:
+ ; AL = 0 if we get here
+ xchg al,ah
+ cmp al,44h ; F10
+ ja .f11_f12
+ sub al,3Bh ; F1
+ jb get_char_2
+ jmp show_help
+.f11_f12:
+ cmp al,85h ; F11
+ jb get_char_2
+ cmp al,86h ; F12
+ ja get_char_2
+ sub al,85h-10
+
+show_help: ; AX = func key # (0 = F1, 9 = F10, 11 = F12)
+ push di ; Save end-of-cmdline pointer
+ shl ax,FILENAME_MAX_LG2 ; Convert to pointer
+ add ax,FKeyName
+ xchg di,ax
+ cmp byte [di+NULLOFFSET],NULLFILE
+ je short fk_nofile ; Undefined F-key
+ call open
+ jz short fk_nofile ; File not found
+ call crlf
+ call get_msg_file
+ jmp short fk_wrcmd
+
+print_version:
+ push di ; Command line write pointer
+ mov si,syslinux_banner
+ call cwritestr
+%ifdef HAVE_BIOSNAME
+ mov si,[BIOSName]
+ call cwritestr
+%endif
+ mov si,copyright_str
+ call cwritestr
+
+ ; ... fall through ...
+
+ ; Write the boot prompt and command line again and
+ ; wait for input. Note that this expects the cursor
+ ; to already have been CRLF'd, and that the old value
+ ; of DI (the command line write pointer) is on the stack.
+fk_wrcmd:
+ mov si,boot_prompt
+ call cwritestr
+ pop di ; Command line write pointer
+ push di
+ mov byte [di],0 ; Null-terminate command line
+ mov si,command_line
+ call cwritestr ; Write command line so far
+fk_nofile: pop di
+ jmp get_char
+
+;
+; Show network info (in the form of the ipappend strings)
+;
+%if IS_PXELINUX
+show_network_info:
+ push di ; Command line write pointer
+ call crlf
+ mov si,IPAppends ; See comboot.doc
+ mov cx,numIPAppends
+.loop:
+ lodsw
+ push si
+ mov si,ax
+ call cwritestr
+ call crlf
+ pop si
+ loop .loop
+ jmp fk_wrcmd
+%endif
+
+;
+; Jump here to run the default command line
+;
+auto_boot:
+ mov si,default_cmd
+ mov di,command_line
+ mov cx,(max_cmd_len+4) >> 2
+ rep movsd
+ jmp short load_kernel
+
+;
+; Jump here when the command line is completed
+;
+command_done:
+ call crlf
+ cmp di,command_line ; Did we just hit return?
+ je auto_boot
+ xor al,al ; Store a final null
+ stosb
+
+load_kernel: ; Load the kernel now
+;
+; First we need to mangle the kernel name the way DOS would...
+;
+ mov si,command_line
+ mov di,KernelName
+ push si
+ call mangle_name
+ pop si
+;
+; Fast-forward to first option (we start over from the beginning, since
+; mangle_name doesn't necessarily return a consistent ending state.)
+;
+clin_non_wsp: lodsb
+ cmp al,' '
+ ja clin_non_wsp
+clin_is_wsp: and al,al
+ jz clin_opt_ptr
+ lodsb
+ cmp al,' '
+ jbe clin_is_wsp
+clin_opt_ptr: dec si ; Point to first nonblank
+ mov [CmdOptPtr],si ; Save ptr to first option
+;
+; If "allowoptions 0", put a null character here in order to ignore any
+; user-specified options.
+;
+ mov ax,[AllowOptions]
+ and ax,ax
+ jnz clin_opt_ok
+ mov [si],al
+clin_opt_ok:
+
+;
+; Now check if it is a "virtual kernel"
+;
+vk_check:
+ mov esi,[HighMemSize] ; Start from top of memory
+.scan:
+ cmp esi,[VKernelEnd]
+ jbe .not_vk
+
+ mov di,VKernelBuf
+ call rllunpack
+ ; ESI updated on return
+
+ sub di,cx ; Return to beginning of buf
+ push si
+ mov si,KernelName
+ mov cx,FILENAME_MAX
+ es repe cmpsb
+ pop si
+ je .found
+ jmp .scan
+
+;
+; We *are* using a "virtual kernel"
+;
+.found:
+ push es
+ push word real_mode_seg
+ pop es
+ mov di,cmd_line_here
+ mov si,VKernelBuf+vk_append
+ mov cx,[VKernelBuf+vk_appendlen]
+ rep movsb
+ mov [CmdLinePtr],di ; Where to add rest of cmd
+ pop es
+ mov di,KernelName
+ push di
+ mov si,VKernelBuf+vk_rname
+ mov cx,FILENAME_MAX ; We need ECX == CX later
+ rep movsb
+ pop di
+%if IS_PXELINUX
+ mov al,[VKernelBuf+vk_ipappend]
+ mov [IPAppend],al
+%endif
+ xor bx,bx ; Try only one version
+
+ mov al, [VKernelBuf+vk_type]
+ mov [KernelType], al
+
+%if HAS_LOCALBOOT
+ ; Is this a "localboot" pseudo-kernel?
+%if IS_PXELINUX
+ cmp byte [VKernelBuf+vk_rname+4], 0
+%else
+ cmp byte [VKernelBuf+vk_rname], 0
+%endif
+ jne get_kernel ; No, it's real, go get it
+
+ mov ax, [VKernelBuf+vk_rname+1]
+ jmp local_boot
+%else
+ jmp get_kernel
+%endif
+
+.not_vk:
+
+;
+; Not a "virtual kernel" - check that's OK and construct the command line
+;
+ cmp word [AllowImplicit],byte 0
+ je bad_implicit
+ push es
+ push si
+ push di
+ mov di,real_mode_seg
+ mov es,di
+ mov si,AppendBuf
+ mov di,cmd_line_here
+ mov cx,[AppendLen]
+ rep movsb
+ mov [CmdLinePtr],di
+ pop di
+ pop si
+ pop es
+
+ mov [KernelType], cl ; CL == 0 here
+
+;
+; Find the kernel on disk
+;
+get_kernel: mov byte [KernelName+FILENAME_MAX],0 ; Zero-terminate filename/extension
+ mov di,KernelName+4*IS_PXELINUX
+ xor al,al
+ mov cx,FILENAME_MAX-5 ; Need 4 chars + null
+ repne scasb ; Scan for final null
+ jne .no_skip
+ dec di ; Point to final null
+.no_skip: mov [KernelExtPtr],di
+ mov bx,exten_table
+.search_loop: push bx
+ mov di,KernelName ; Search on disk
+ call searchdir
+ pop bx
+ jnz kernel_good
+ mov eax,[bx] ; Try a different extension
+ mov si,[KernelExtPtr]
+ mov [si],eax
+ mov byte [si+4],0
+ add bx,byte 4
+ cmp bx,exten_table_end
+ jna .search_loop ; allow == case (final case)
+ ; Fall into bad_kernel
+;
+; bad_kernel: Kernel image not found
+; bad_implicit: The user entered a nonvirtual kernel name, with "implicit 0"
+;
+bad_implicit:
+bad_kernel:
+ mov cx,[OnerrorLen]
+ and cx,cx
+ jnz on_error
+.really:
+ mov si,KernelName
+ mov di,KernelCName
+ push di
+ call unmangle_name ; Get human form
+ mov si,err_notfound ; Complain about missing kernel
+ call cwritestr
+ pop si ; KernelCName
+ call cwritestr
+ mov si,crlf_msg
+ jmp abort_load ; Ask user for clue
+
+;
+; on_error: bad kernel, but we have onerror set; CX = OnerrorLen
+;
+on_error:
+ mov si,Onerror
+ mov di,command_line
+ push si ; <A>
+ push di ; <B>
+ push cx ; <C>
+ push cx ; <D>
+ push di ; <E>
+ repe cmpsb
+ pop di ; <E> di == command_line
+ pop bx ; <D> bx == [OnerrorLen]
+ je bad_kernel.really ; Onerror matches command_line already
+ neg bx ; bx == -[OnerrorLen]
+ lea cx,[max_cmd_len+bx]
+ ; CX == max_cmd_len-[OnerrorLen]
+ mov di,command_line+max_cmd_len-1
+ mov byte [di+1],0 ; Enforce null-termination
+ lea si,[di+bx]
+ std
+ rep movsb ; Make space in command_line
+ cld
+ pop cx ; <C> cx == [OnerrorLen]
+ pop di ; <B> di == command_line
+ pop si ; <A> si == Onerror
+ rep movsb
+ jmp load_kernel
+
+;
+; kernel_corrupt: Called if the kernel file does not seem healthy
+;
+kernel_corrupt: mov si,err_notkernel
+ jmp abort_load
+
+;
+; Get a key, observing ThisKbdTO and ThisTotalTO -- those are timeouts
+; which can be adjusted by the caller based on the corresponding
+; master variables; on return they're updated.
+;
+; This cheats. If we say "no timeout" we actually get a timeout of
+; 7.5 years.
+;
+getchar_timeout:
+ call vgashowcursor
+ RESET_IDLE
+
+.loop:
+ push word [BIOS_timer]
+ call pollchar
+ jnz .got_char
+ pop ax
+ cmp ax,[BIOS_timer] ; Has the timer advanced?
+ je .loop
+ DO_IDLE
+
+ dec dword [ThisKbdTo]
+ jz .timeout
+ dec dword [ThisTotalTo]
+ jnz .loop
+
+.timeout:
+ ; Timeout!!!!
+ pop cx ; Discard return address
+ call vgahidecursor
+ mov si,Ontimeout ; Copy ontimeout command
+ mov di,command_line
+ mov cx,[OntimeoutLen] ; if we have one...
+ rep movsb
+ jmp command_done
+
+.got_char:
+ pop cx ; Discard
+ call getchar
+ call vgahidecursor
+ ret
+
+;
+; This is it! We have a name (and location on the disk)... let's load
+; that sucker!! First we have to decide what kind of file this is; base
+; that decision on the file extension. The following extensions are
+; recognized; case insensitive:
+;
+; .com - COMBOOT image
+; .cbt - COMBOOT image
+; .c32 - COM32 image
+; .bs - Boot sector
+; .0 - PXE bootstrap program (PXELINUX only)
+; .bin - Boot sector
+; .bss - Boot sector, but transfer over DOS superblock (SYSLINUX only)
+; .img - Floppy image (ISOLINUX only)
+;
+; Anything else is assumed to be a Linux kernel.
+;
+ section .bss
+ alignb 4
+Kernel_EAX resd 1
+Kernel_SI resw 1
+
+ section .text
+kernel_good_saved:
+ ; Alternate entry point for which the return from
+ ; searchdir is stored in memory. This is used for
+ ; COMBOOT function INT 22h, AX=0016h.
+ mov si,[Kernel_SI]
+ mov eax,[Kernel_EAX]
+
+kernel_good:
+ pushad
+
+ mov si,KernelName
+ mov di,KernelCName
+ call unmangle_name
+ sub di,KernelCName
+ mov [KernelCNameLen],di
+
+ ; Default memory limit, can be overridden by image loaders
+ mov eax,[HighMemRsvd]
+ mov [MyHighMemSize],eax
+
+ popad
+
+ push di
+ push ax
+ mov di,KernelName+4*IS_PXELINUX
+ xor al,al
+ mov cx,FILENAME_MAX
+ repne scasb
+ jne .one_step
+ dec di
+.one_step: mov ecx,[di-4] ; 4 bytes before end
+ pop ax
+ pop di
+
+;
+; At this point, EAX contains the size of the kernel, SI contains
+; the file handle/cluster pointer, and ECX contains the extension (if any.)
+;
+ movzx di,byte [KernelType]
+ add di,di
+ jmp [kerneltype_table+di]
+
+is_unknown_filetype:
+ or ecx,20202000h ; Force lower case (except dot)
+
+ cmp ecx,'.com'
+ je is_comboot_image
+ cmp ecx,'.cbt'
+ je is_comboot_image
+ cmp ecx,'.c32'
+ je is_com32_image
+%if IS_ISOLINUX
+ cmp ecx,'.img'
+ je is_disk_image
+%endif
+ cmp ecx,'.bss'
+ je is_bss_sector
+ cmp ecx,'.bin'
+ je is_bootsector
+ shr ecx,8
+ cmp ecx,'.bs'
+ je is_bootsector
+ shr ecx,8
+ cmp cx,'.0'
+ je is_bootsector
+
+ ; Otherwise Linux kernel
+ jmp is_linux_kernel
+
+is_config_file:
+ pusha
+ mov si,KernelCName ; Save the config file name, for posterity
+ mov di,ConfigName
+ call strcpy
+ popa
+ call openfd
+ call reset_config
+ jmp load_config_file
+
+; This is an image type we can't deal with
+is_bad_image:
+ mov si,err_badimage
+ call cwritestr
+ jmp enter_command
+
+%if IS_SYSLINUX || IS_MDSLINUX
+ ; ok
+%else
+is_bss_sector equ is_bad_image
+%endif
+%if IS_ISOLINUX
+ ; ok
+%else
+is_disk_image equ is_bad_image
+%endif
+
+ section .data
+boot_prompt db 'boot: ', 0
+wipe_char db BS, ' ', BS, 0
+err_badimage db 'Invalid image type for this media type!', CR, LF, 0
+err_notfound db 'Could not find kernel image: ',0
+err_notkernel db CR, LF, 'Invalid or corrupt kernel image.', CR, LF, 0
+
+
+ align 2, db 0
+kerneltype_table:
+ dw is_unknown_filetype ; VK_KERNEL
+ dw is_linux_kernel ; VK_LINUX
+ dw is_bootsector ; VK_BOOT
+ dw is_bss_sector ; VK_BSS
+ dw is_bootsector ; VK_PXE
+ dw is_disk_image ; VK_FDIMAGE
+ dw is_comboot_image ; VK_COMBOOT
+ dw is_com32_image ; VK_COM32
+ dw is_config_file ; VK_CONFIG
+
+ section .bss
+ alignb 4
+ThisKbdTo resd 1 ; Temporary holder for KbdTimeout
+ThisTotalTo resd 1 ; Temporary holder for TotalTimeout
+KernelExtPtr resw 1 ; During search, final null pointer
+CmdOptPtr resw 1 ; Pointer to first option on cmd line
+KbdFlags resb 1 ; Check for keyboard escapes
+FuncFlag resb 1 ; Escape sequences received from keyboard
+KernelType resb 1 ; Kernel type, from vkernel, if known
+
+ section .text
+;
+; Linux kernel loading code is common.
+;
+%include "runkernel.inc"
+
+;
+; COMBOOT-loading code
+;
+%include "comboot.inc"
+%include "com32.inc"
+%include "cmdline.inc"
+
+;
+; Boot sector loading code
+;
+%include "bootsect.inc"
+
+;
+; Abort loading code
+;
+%include "abort.inc"
+
+;
+; Hardware cleanup common code
+;
+%include "cleanup.inc"
diff --git a/core/writehex.inc b/core/writehex.inc
new file mode 100644
index 00000000..1dbe4ab3
--- /dev/null
+++ b/core/writehex.inc
@@ -0,0 +1,53 @@
+;; -----------------------------------------------------------------------
+;;
+;; Copyright 1994-2008 H. Peter Anvin - All Rights Reserved
+;;
+;; This program is free software; you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation, Inc., 53 Temple Place Ste 330,
+;; Boston MA 02111-1307, USA; either version 2 of the License, or
+;; (at your option) any later version; incorporated herein by reference.
+;;
+;; -----------------------------------------------------------------------
+
+;;
+;; writehex.inc
+;;
+;; Write hexadecimal numbers to the console
+;;
+
+ section .text
+;
+; writehex[248]: Write a hex number in (AL, AX, EAX) to the console
+;
+writehex2:
+ pushfd
+ pushad
+ rol eax,24
+ mov cx,2
+ jmp short writehex_common
+writehex4:
+ pushfd
+ pushad
+ rol eax,16
+ mov cx,4
+ jmp short writehex_common
+writehex8:
+ pushfd
+ pushad
+ mov cx,8
+writehex_common:
+.loop: rol eax,4
+ push eax
+ and al,0Fh
+ cmp al,10
+ jae .high
+.low: add al,'0'
+ jmp short .ischar
+.high: add al,'A'-10
+.ischar: call writechr
+ pop eax
+ loop .loop
+ popad
+ popfd
+ ret
diff --git a/core/writestr.inc b/core/writestr.inc
new file mode 100644
index 00000000..04ad67a6
--- /dev/null
+++ b/core/writestr.inc
@@ -0,0 +1,47 @@
+;; -----------------------------------------------------------------------
+;;
+;; Copyright 1994-2008 H. Peter Anvin - All Rights Reserved
+;;
+;; This program is free software; you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation, Inc., 53 Temple Place Ste 330,
+;; Boston MA 02111-1307, USA; either version 2 of the License, or
+;; (at your option) any later version; incorporated herein by reference.
+;;
+;; -----------------------------------------------------------------------
+
+;;
+;; writestr.inc
+;;
+;; Code to write a simple string.
+;;
+
+ section .text
+;
+; crlf: Print a newline
+;
+crlf: push ax
+ mov al,CR
+ call writechr
+ mov al,LF
+ call writechr
+ pop ax
+ ret
+
+;
+; cwritestr: write a null-terminated string to the console, saving
+; registers on entry.
+;
+; Note: writestr and cwritestr are distinct in SYSLINUX (only)
+;
+cwritestr:
+ pushfd
+ pushad
+.top: lodsb
+ and al,al
+ jz .end
+ call writechr
+ jmp short .top
+.end: popad
+ popfd
+ ret