summaryrefslogtreecommitdiffstats
path: root/src/arch/i386
diff options
context:
space:
mode:
authorMichael Brown2005-03-08 19:53:11 +0100
committerMichael Brown2005-03-08 19:53:11 +0100
commit3d6123e69ab879c72ff489afc5bf93ef0b7a94ce (patch)
tree9f3277569153a550fa8d81ebd61bd88f266eb8da /src/arch/i386
downloadipxe-3d6123e69ab879c72ff489afc5bf93ef0b7a94ce.tar.gz
ipxe-3d6123e69ab879c72ff489afc5bf93ef0b7a94ce.tar.xz
ipxe-3d6123e69ab879c72ff489afc5bf93ef0b7a94ce.zip
Initial revision
Diffstat (limited to 'src/arch/i386')
-rw-r--r--src/arch/i386/Config131
-rw-r--r--src/arch/i386/Makefile373
-rw-r--r--src/arch/i386/core/aout_loader.c144
-rw-r--r--src/arch/i386/core/callbacks.c107
-rw-r--r--src/arch/i386/core/cpu.c86
-rw-r--r--src/arch/i386/core/elf.c135
-rw-r--r--src/arch/i386/core/etherboot.lds90
-rw-r--r--src/arch/i386/core/etherboot.prefix.lds100
-rw-r--r--src/arch/i386/core/freebsd_loader.c377
-rw-r--r--src/arch/i386/core/hooks.c35
-rw-r--r--src/arch/i386/core/i386_timer.c191
-rw-r--r--src/arch/i386/core/init.S305
-rw-r--r--src/arch/i386/core/multiboot_loader.c143
-rw-r--r--src/arch/i386/core/pci_io.c352
-rw-r--r--src/arch/i386/core/pic8259.c331
-rw-r--r--src/arch/i386/core/prefixudata.lds8
-rw-r--r--src/arch/i386/core/prefixzdata.lds8
-rw-r--r--src/arch/i386/core/pxe_callbacks.c364
-rw-r--r--src/arch/i386/core/pxe_loader.c94
-rw-r--r--src/arch/i386/core/realmode.c148
-rw-r--r--src/arch/i386/core/realmode_asm.S695
-rw-r--r--src/arch/i386/core/start16.S285
-rw-r--r--src/arch/i386/core/start16.lds8
-rw-r--r--src/arch/i386/core/start16z.lds65
-rw-r--r--src/arch/i386/core/start32.S767
-rw-r--r--src/arch/i386/core/tagged_loader.c201
-rw-r--r--src/arch/i386/core/video_subr.c94
-rw-r--r--src/arch/i386/core/wince_loader.c273
-rw-r--r--src/arch/i386/drivers/net/undi.c1458
-rw-r--r--src/arch/i386/drivers/net/undi.h178
-rw-r--r--src/arch/i386/firmware/pcbios/basemem.c317
-rw-r--r--src/arch/i386/firmware/pcbios/bios.c155
-rw-r--r--src/arch/i386/firmware/pcbios/console.c85
-rw-r--r--src/arch/i386/firmware/pcbios/e820mangler.S296
-rw-r--r--src/arch/i386/firmware/pcbios/hidemem.c94
-rw-r--r--src/arch/i386/firmware/pcbios/memsizes.c201
-rw-r--r--src/arch/i386/include/bits/byteswap.h45
-rw-r--r--src/arch/i386/include/bits/cpu.h243
-rw-r--r--src/arch/i386/include/bits/elf.h91
-rw-r--r--src/arch/i386/include/bits/elf_x.h5
-rw-r--r--src/arch/i386/include/bits/eltorito.h3
-rw-r--r--src/arch/i386/include/bits/endian.h9
-rw-r--r--src/arch/i386/include/bits/string.h99
-rw-r--r--src/arch/i386/include/callbacks_arch.h243
-rw-r--r--src/arch/i386/include/hidemem.h21
-rw-r--r--src/arch/i386/include/hooks.h9
-rw-r--r--src/arch/i386/include/io.h246
-rw-r--r--src/arch/i386/include/latch.h10
-rw-r--r--src/arch/i386/include/limits.h59
-rw-r--r--src/arch/i386/include/pic8259.h96
-rw-r--r--src/arch/i386/include/pxe_callbacks.h33
-rw-r--r--src/arch/i386/include/pxe_types.h35
-rw-r--r--src/arch/i386/include/realmode.h124
-rw-r--r--src/arch/i386/include/segoff.h41
-rw-r--r--src/arch/i386/include/setjmp.h12
-rw-r--r--src/arch/i386/include/stdint.h16
-rw-r--r--src/arch/i386/include/vga.h228
-rw-r--r--src/arch/i386/prefix/bImageprefix.S614
-rw-r--r--src/arch/i386/prefix/boot1a.s410
-rw-r--r--src/arch/i386/prefix/comprefix.S49
-rw-r--r--src/arch/i386/prefix/elf_dprefix.S96
-rw-r--r--src/arch/i386/prefix/elfprefix.S96
-rwxr-xr-xsrc/arch/i386/prefix/exeprefix.S44
-rw-r--r--src/arch/i386/prefix/floppyprefix.S359
-rw-r--r--src/arch/i386/prefix/huf.lds6
-rw-r--r--src/arch/i386/prefix/img.lds6
-rw-r--r--src/arch/i386/prefix/liloprefix.S144
-rw-r--r--src/arch/i386/prefix/lmelf_dprefix.S163
-rw-r--r--src/arch/i386/prefix/lmelf_prefix.S163
-rw-r--r--src/arch/i386/prefix/nullprefix.S16
-rw-r--r--src/arch/i386/prefix/pxeprefix.S398
-rw-r--r--src/arch/i386/prefix/romprefix.S416
-rw-r--r--src/arch/i386/prefix/unhuf.S400
-rw-r--r--src/arch/i386/prefix/unhuf.lds33
-rw-r--r--src/arch/i386/prefix/unnrv2b.S129
75 files changed, 13904 insertions, 0 deletions
diff --git a/src/arch/i386/Config b/src/arch/i386/Config
new file mode 100644
index 00000000..e8ac91b8
--- /dev/null
+++ b/src/arch/i386/Config
@@ -0,0 +1,131 @@
+# Config for i386 Etherboot
+#
+# Do not delete the tag OptionDescription and /OptionDescription
+# It is used to automatically generate the documentation.
+#
+# @OptionDescrition@
+#
+# BIOS interface options:
+#
+# -DPCBIOS
+# Compile in support for the normal pcbios
+# -DLINUXBIOS
+# Compile in support for LinuxBIOS
+# -DBBS_BUT_NOT_PNP_COMPLIANT
+# Some BIOSes claim to be PNP but they don't conform
+# to the BBS spec which specifies that ES:DI must
+# point to the string $PnP on entry. This option
+# works around those. This option must be added to
+# LCONFIG.
+# -DNO_DELAYED_INT
+# Take control as soon as BIOS detects the ROM.
+# Normally hooks onto INT18H or INT19H. Use only if you
+# have a very non-conformant BIOS as it bypasses
+# BIOS initialisation of devices. This only works for
+# legacy ROMs, i.e. PCI_PNP_HEADER not defined.
+# This option was formerly called NOINT19H.
+# -DBOOT_INT18H
+# Etherboot normally hooks onto INT19H for legacy ROMs.
+# You can choose to hook onto INT18H (BASIC interpreter
+# entry point) instead. This entry point is used when
+# all boot devices have been exhausted. This option must
+# be added to LCONFIG.
+# -DCONFIG_PCI_DIRECT
+# Define this for PCI BIOSes that do not implement
+# BIOS32 or not correctly. Normally not needed.
+# Only works for BIOSes of a certain era.
+# -DCONFIG_TSC_CURRTICKS
+# Uses the processor time stamp counter instead of reading
+# the BIOS time counter. This allows Etherboot to work
+# even without a BIOS. This only works on late model
+# 486s and above.
+# -DCONFIG_NO_TIMER2
+# Some systems do not have timer2 implemented.
+# If you have a RTC this will allow you to roughly calibrate
+# it using outb instructions.
+# -DIBM_L40
+# This option uses the 0x92 method of controlling
+# A20 instead of the traditional method of using the
+# keyboard controller. An explanation of A20 is here:
+# http://www.win.tue.nl/~aeb/linux/kbd/A20.html
+# This occurs on MCA, EISA and some embedded boards,
+# and sometimes with the Fast Gate A20 option on some
+# BIOSes.
+# Enable this only if you are sure of what you are doing.
+#
+# Extended cpu options
+
+# -DCONFIG_X86_64
+# Compile in support for booting x86_64 64bit binaries.
+#
+# PXE loader options:
+#
+# -DPXELOADER_KEEP_ALL
+# Prevent PXE loader (prefix) from unloading the
+# PXE stack. You will want to use this if, for
+# example, you are booting via PXE-on-floppy.
+# You may want to use it under certain
+# circumstances when using the Etherboot UNDI
+# driver; these are complex and best practice is
+# not yet established.
+#
+# Obscure options you probably don't need to touch:
+#
+# -DIGNORE_E820_MAP
+# Ignore the memory map returned by the E820 BIOS
+# call. May be necessary on some buggy BIOSes.
+# -DT503_AUI
+# Use AUI by default on 3c503 cards.
+# -DFLATTEN_REAL_MODE
+# Use 4GB segment limits when calling out to or
+# returning to real-mode code. This is necessary to
+# work around some buggy code (e.g. OpenBSD's pxeboot)
+# that uses flat real-mode without being sufficiently
+# paranoid about the volatility of its segment limits.
+
+#
+# @/OptionDescription@
+
+# BIOS select don't change unless you know what you are doing
+CFLAGS+= -DPCBIOS
+
+# Compile in k8/hammer support
+# CFLAGS+= -DCONFIG_X86_64
+
+# Options to make a version of Etherboot that will work under linuxBIOS.
+# CFLAGS+= -DLINUXBIOS -DCONFIG_TSC_CURRTICKS -DCONSOLE_SERIAL -DCOMCONSOLE=0x3f8 -DCOMPRESERVE -DCONFIG_PCI_DIRECT -DELF_IMAGE
+
+# These options affect the loader that is prepended to the Etherboot image
+# LCONFIG+= -DBBS_BUT_NOT_PNP_COMPLIANT
+# LCONFIG+= -DBOOT_INT18H
+
+# Produce code that will work inside the Bochs emulator. The pnic
+# driver is probably the best one to try.
+# CFLAGS+= -DCONFIG_PCI_DIRECT
+
+# Produce code that will work with OpenBSD's pxeboot
+# CFLAGS+= -DFLATTEN_REAL_MODE
+
+CFLAGS+= -fstrength-reduce -fomit-frame-pointer -march=i386
+# Squeeze the code in as little space as possible.
+# gcc3 needs a different syntax to gcc2 if you want to avoid spurious warnings.
+GCC_VERSION = $(subst ., ,$(shell $(CC) -dumpversion))
+GCC_MAJORVERSION = $(firstword $(GCC_VERSION))
+ifeq ($(GCC_MAJORVERSION),2)
+CFLAGS+= -malign-jumps=1 -malign-loops=1 -malign-functions=1
+else
+CFLAGS+= -falign-jumps=1 -falign-loops=1 -falign-functions=1
+endif
+GCC_MINORVERSION = $(word 2, $(GCC_VERSION))
+ifneq ($(GCC_MINORVERSION),4)
+CFLAGS+= -mcpu=i386
+endif
+
+LDFLAGS+= -N
+
+ifeq "$(shell uname -s)" "FreeBSD"
+CFLAGS+= -DIMAGE_FREEBSD -DELF_IMAGE -DAOUT_IMAGE
+endif
+
+# An alternate location for isolinux.bin can be set here
+# ISOLINUX_BIN=/path/to/isolinux.bin
diff --git a/src/arch/i386/Makefile b/src/arch/i386/Makefile
new file mode 100644
index 00000000..cded46c3
--- /dev/null
+++ b/src/arch/i386/Makefile
@@ -0,0 +1,373 @@
+ARCH_FORMAT= elf32-i386
+
+# For debugging, don't delete intermediates
+#.SECONDARY:
+
+LDSCRIPT= arch/i386/core/etherboot.lds
+PLDSCRIPT= arch/i386/core/etherboot.prefix.lds
+
+LCONFIG+= -Ui386
+
+ROMLIMIT= 524288
+CHECKSIZE= { read d1; read d1 d2 d3 size d4; [ $$size -gt $(ROMLIMIT) ] &&\
+ { $(RM) $@; echo "ERROR: code size exceeds limit!"; exit 1; }; exit 0; }
+
+START= $(BIN)/start32.o $(BIN)/linuxbios.o \
+ $(BIN)/bios.o $(BIN)/console.o $(BIN)/memsizes.o $(BIN)/basemem.o \
+ $(BIN)/hidemem.o $(BIN)/e820mangler.o \
+ $(BIN)/realmode.o $(BIN)/realmode_asm.o \
+ $(BIN)/callbacks.o $(BIN)/pxe_callbacks.o
+
+SRCS+= arch/i386/prefix/floppyprefix.S
+SRCS+= arch/i386/prefix/unhuf.S
+SRCS+= arch/i386/prefix/unnrv2b.S
+SRCS+= arch/i386/firmware/pcbios/bios.c
+SRCS+= arch/i386/firmware/pcbios/console.c
+SRCS+= arch/i386/firmware/pcbios/memsizes.c
+SRCS+= arch/i386/firmware/pcbios/basemem.c
+SRCS+= arch/i386/firmware/pcbios/hidemem.c
+SRCS+= arch/i386/firmware/pcbios/e820mangler.S
+SRCS+= arch/i386/prefix/liloprefix.S
+SRCS+= arch/i386/prefix/elfprefix.S
+SRCS+= arch/i386/prefix/lmelf_prefix.S
+SRCS+= arch/i386/prefix/elf_dprefix.S
+SRCS+= arch/i386/prefix/lmelf_dprefix.S
+SRCS+= arch/i386/prefix/comprefix.S
+SRCS+= arch/i386/prefix/exeprefix.S
+SRCS+= arch/i386/prefix/pxeprefix.S
+SRCS+= arch/i386/prefix/romprefix.S
+
+SRCS+= arch/i386/core/init.S
+SRCS+= arch/i386/core/start32.S
+SRCS+= arch/i386/core/pci_io.c
+SRCS+= arch/i386/core/i386_timer.c
+SRCS+= arch/i386/core/elf.c
+SRCS+= arch/i386/core/cpu.c
+SRCS+= arch/i386/core/video_subr.c
+SRCS+= arch/i386/core/pic8259.c
+SRCS+= arch/i386/core/hooks.c
+SRCS+= arch/i386/core/callbacks.c
+SRCS+= arch/i386/core/realmode.c
+SRCS+= arch/i386/core/realmode_asm.S
+SRCS+= arch/i386/core/pxe_callbacks.c
+
+# ROM loaders: ISA and PCI versions
+ISAPREFIX= $(BIN)/isaprefix.o
+ISAENTRY= $(BIN)/isaprefix.entry.o
+ISAEXIT= $(BIN)/isaprefix.exit.o
+PCIPREFIX= $(BIN)/pciprefix.o
+PCIENTRY= $(BIN)/pciprefix.entry.o
+PCIEXIT= $(BIN)/pciprefix.exit.o
+# Variables xxx_ROMTYPE are defined by genrules.pl. ROMENTRY and
+# ROMEXIT will evaluate to give the correct objects to use.
+TARGETBASE=$(patsubst $(BIN)/%,%,$(firstword $(subst ., ,$@)))
+ROMCARD=$(firstword $(subst --, ,$(TARGETBASE)))
+ROMTYPE=$(firstword $(ROMTYPE_$(ROMCARD)) ISA)
+romENTRY=$($(ROMTYPE)ENTRY)
+romEXIT=$($(ROMTYPE)EXIT)
+
+# Target type for generic prf rules
+TARGETTYPE=$(patsubst .%,%, $(suffix $(basename $@)))
+TARGETENTRY=$($(TARGETTYPE)ENTRY)
+TARGETEXIT=$($(TARGETTYPE)EXIT)
+
+# Other real-mode entry loaders
+dskPREFIX= $(BIN)/floppyprefix.o
+dskENTRY= $(BIN)/floppyprefix.entry.o
+dskEXIT= $(BIN)/floppyprefix.exit.o
+comPREFIX= $(BIN)/comprefix.o
+comENTRY= $(BIN)/comprefix.entry.o
+comEXIT= $(BIN)/comprefix.exit.o
+exePREFIX= $(BIN)/exeprefix.o
+exeENTRY= $(BIN)/exeprefix.entry.o
+exeEXIT= $(BIN)/exeprefix.exit.o
+liloPREFIX= $(BIN)/liloprefix.o
+liloENTRY= $(BIN)/liloprefix.entry.o
+liloEXIT= $(BIN)/liloprefix.exit.o
+bImagePREFIX= $(BIN)/bImageprefix.o
+bImageENTRY= $(BIN)/bImageprefix.entry.o
+bImageEXIT= $(BIN)/bImageprefix.exit.o
+pxePREFIX= $(BIN)/pxeprefix.o
+pxeENTRY= $(BIN)/pxeprefix.entry.o
+pxeEXIT= $(BIN)/pxeprefix.exit.o
+rawPREFIX= $(BIN)/nullprefix.o
+rawENTRY= $(BIN)/nullprefix.entry.o
+rawEXIT= $(BIN)/nullprefix.exit.o
+
+# Protected mode entry loaders
+elfPREFIX= $(BIN)/elfprefix.o
+elfENTRY= $(BIN)/elfprefix.entry.o
+elfEXIT= $(BIN)/elfprefix.exit.o
+lmelfPREFIX= $(BIN)/lmelf_prefix.o
+lmelfENTRY= $(BIN)/lmelf_prefix.entry.o
+lmelfEXIT= $(BIN)/lmelf_prefix.exit.o
+elfdPREFIX= $(BIN)/elf_dprefix.o
+elfdENTRY= $(BIN)/elf_dprefix.entry.o
+elfdEXIT= $(BIN)/elf_dprefix.exit.o
+lmelfdPREFIX= $(BIN)/lmelf_dprefix.o
+lmelfdENTRY= $(BIN)/lmelf_dprefix.entry.o
+lmelfdEXIT= $(BIN)/lmelf_dprefix.exit.o
+
+include $(BIN)/Roms
+
+all: $(ROMS)
+allroms: $(ROMS)
+allzroms: $(ROMS)
+alldsks: $(EB_DSKS)
+allzdsks: $(EB_ZDSKS)
+alllilos: $(EB_LILOS)
+allzlilos: $(EB_ZLILOS)
+allbImages: $(EB_BIMAGES)
+allbzImages: $(EB_BZIMAGES)
+allpxes: $(EB_PXES)
+allzpxes: $(EB_ZPXES)
+allelfs: $(EB_ELFS)
+allzelfs: $(EB_ZELFS)
+alllmelfs: $(EB_LMELFS)
+allzlmelfs: $(EB_ZLMELFS)
+allelfds: $(EB_ELFDS)
+allzelfds: $(EB_ZELFDS)
+alllmelfds: $(EB_LMELFDS)
+allzlmelfds: $(EB_ZLMELFDS)
+allcoms: $(EB_COMS)
+allexes: $(EB_EXES)
+allisos: $(EB_ISOS)
+alllisos: $(EB_LISOS)
+
+BOBJS+= $(BIN)/pci_io.o $(BIN)/i386_timer.o
+BOBJS+= $(BIN)/elf.o $(BIN)/cpu.o $(BIN)/video_subr.o
+BOBJS+= $(BIN)/pic8259.o $(BIN)/hooks.o
+
+# ROM loaders
+
+$(ISAPREFIX): arch/i386/prefix/romprefix.S $(MAKEDEPS)
+ $(CPP) $(CFLAGS) $(LCONFIG) -Ui386 -D ASSEMBLY $< \
+ | $(AS) $(ASFLAGS) -o $@
+
+$(PCIPREFIX): arch/i386/prefix/romprefix.S $(MAKEDEPS)
+ $(CPP) -DPCI_PNP_HEADER $(CFLAGS) $(LCONFIG) -Ui386 -D ASSEMBLY $< \
+ | $(AS) $(ASFLAGS) -o $@
+
+# Prefix splitters
+$(BIN)/%prefix.entry.o: $(BIN)/%prefix.o $(MAKEDEPS)
+ $(OBJCOPY) -R .text16 $< $@
+
+$(BIN)/%prefix.exit.o: $(BIN)/%prefix.o $(MAKEDEPS)
+ $(OBJCOPY) -R .prefix $< $@
+
+# Generic prefix objects
+PREFIXOBJS = $(BIN)/init.o
+ZPREFIXOBJS = $(BIN)/init.o $(BIN)/unnrv2b.o
+
+# Utilities
+$(BIN)/nrv2b: util/nrv2b.c
+ $(HOST_CC) -O2 -DENCODE -DDECODE -DMAIN -DVERBOSE -DNDEBUG -DBITSIZE=32 -DENDIAN=0 -o $@ $<
+
+ZFILELEN = perl util/zfilelen.pl
+
+# Pattern Rules
+
+# General for compiling/assembly source files
+
+$(BIN)/%.o: arch/i386/core/%.c $(MAKEDEPS)
+ $(CC) $(CFLAGS) -o $@ -c $<
+
+$(BIN)/%.o: arch/i386/core/%.S $(MAKEDEPS)
+ $(CPP) $(CFLAGS) -Ui386 -D ASSEMBLY $< | $(AS) $(ASFLAGS) -o $@
+
+$(BIN)/%.o: arch/i386/firmware/pcbios/%.c $(MAKEDEPS)
+ $(CC) $(CFLAGS) -o $@ -c $<
+
+$(BIN)/%.o: arch/i386/firmware/pcbios/%.S $(MAKEDEPS)
+ $(CPP) $(CFLAGS) -Ui386 -D ASSEMBLY $< | $(AS) $(ASFLAGS) -o $@
+
+$(BIN)/%.o: arch/i386/prefix/%.S $(MAKEDEPS)
+ $(CPP) $(CFLAGS) -Ui386 -D ASSEMBLY $< | $(AS) $(ASFLAGS) -o $@
+
+# general rule for 16bit .o, may be overridden
+$(BIN)/%.o: $(BIN)/%.s
+ $(AS) $(ASFLAGS) -o $@ $<
+
+# general rule for .bin (plain binary loader code), may be overridden
+$(BIN)/%.bin: $(BIN)/%.o
+ $(OBJCOPY) -O binary $< $@
+
+# general rule for .z (compressed binary code), may be overridden
+# rule for .z is in top level Makefile
+# Give the directory name, e.g. use $(BIN)/rtl8139.com as the target.
+
+$(BIN)/%.zo: $(BIN)/%.zbin arch/i386/core/prefixzdata.lds $(MAKEDEPS)
+ $(LD) -T arch/i386/core/prefixzdata.lds -b binary $< -o $@
+
+$(BIN)/%.uo: $(BIN)/%.bin arch/i386/core/prefixudata.lds $(MAKEDEPS)
+ $(LD) -T arch/i386/core/prefixudata.lds -b binary $< -o $@
+
+# Intermediate prf rules
+
+%.prf: %.rt $(PREFIXOBJS) %.rt1.uo %.rt2.uo $(MAKEDEPS)
+ $(MAKE) $(TARGETENTRY)
+ $(LD) $(LDFLAGS) -T $(PLDSCRIPT) $(TARGETENTRY) -R $(subst $(MAKEDEPS),,$^) -o $@
+
+%.zprf: %.rt $(ZPREFIXOBJS) %.rt1.uo %.rt2.zo $(MAKEDEPS)
+ $(MAKE) $(TARGETENTRY)
+ $(LD) $(LDFLAGS) -T $(PLDSCRIPT) $(TARGETENTRY) -R $(subst $(MAKEDEPS),,$^) -o $@
+
+# general rules for normal/compressed ROM images, may be overridden
+SUFFIXES += rom zrom
+
+$(BIN)/%.rom.rt: $(BIN)/%.rt.o $(ISAENTRY) $(PCIENTRY) $(ISAEXIT) $(PCIEXIT) $(LDSCRIPT) $(MAKEDEPS)
+ $(LD) $(LDFLAGS) -T $(LDSCRIPT) -o $@ $(romEXIT) $<
+ @$(SIZE) $@ | $(CHECKSIZE)
+
+$(BIN)/%.rom: $(BIN)/%.rom.prf
+ $(OBJCOPY) -O binary $< $@
+ $(MAKEROM) $(MAKEROM_FLAGS) $(MAKEROM_$(ROMCARD)) $(MAKEROM_ID_$(ROMCARD)) -i$(IDENT) $@
+
+$(BIN)/%.zrom: $(BIN)/%.rom.zprf
+ $(OBJCOPY) -O binary $< $@
+ $(MAKEROM) $(MAKEROM_FLAGS) $(MAKEROM_$(ROMCARD)) $(MAKEROM_ID_$(ROMCARD)) -i$(IDENT) $@
+
+# general rules for ELF images
+SUFFIXES += elf zelf
+$(BIN)/%.elf.rt: $(BIN)/%.rt.o $(elfENTRY) $(elfEXIT) $(LDSCRIPT) $(MAKEDEPS)
+ $(LD) $(LDFLAGS) -T $(LDSCRIPT) -o $@ $(elfEXIT) $<
+
+$(BIN)/%.elf: $(BIN)/%.elf.prf
+ $(OBJCOPY) -O binary $< $@
+
+$(BIN)/%.zelf: $(BIN)/%.elf.zprf
+ $(OBJCOPY) -O binary $< $@
+
+# general rules for Long Mode ELF images
+SUFFIXES += lmelf zlmelf
+$(BIN)/%.lmelf.rt: $(BIN)/%.rt.o $(lmelfENTRY) $(lmelfEXIT) $(LDSCRIPT) $(MAKEDEPS)
+ $(LD) $(LDFLAGS) -T $(LDSCRIPT) -o $@ $(lmelfEXIT) $<
+
+$(BIN)/%.lmelf: $(BIN)/%.lmelf.prf
+ $(OBJCOPY) -O binary $< $@
+
+$(BIN)/%.zlmelf: $(BIN)/%.lmelf.zprf
+ $(OBJCOPY) -O binary $< $@
+
+# general rules for ELF dynamic images
+SUFFIXES += elfd zelfd
+$(BIN)/%.elfd.rt: $(BIN)/%.rt.o $(elfdENTRY) $(elfdEXIT) $(LDSCRIPT) $(MAKEDEPS)
+ $(LD) $(LDFLAGS) -T $(LDSCRIPT) -o $@ $(elfdEXIT) $<
+
+$(BIN)/%.elfd: $(BIN)/%.elfd.prf
+ $(OBJCOPY) -O binary $< $@
+
+$(BIN)/%.zelfd: $(BIN)/%.elfd.zprf
+ $(OBJCOPY) -O binary $< $@
+
+# general rules for Long Mode ELF dynamic images
+SUFFIXES += lmelfd zlmelfd
+$(BIN)/%.lmelfd.rt: $(BIN)/%.rt.o $(lmelfdENTRY) $(lmelfdEXIT) $(LDSCRIPT) $(MAKEDEPS)
+ $(LD) $(LDFLAGS) -T $(LDSCRIPT) -o $@ $(lmelfdEXIT) $<
+
+$(BIN)/%.lmelfd: $(BIN)/%.lmelfd.prf
+ $(OBJCOPY) -O binary $< $@
+
+$(BIN)/%.zlmelfd: $(BIN)/%.lmelfd.zprf
+ $(OBJCOPY) -O binary $< $@
+
+# rules to generate a DOS loadable .com executable
+SUFFIXES += com
+$(BIN)/%.com.rt: $(BIN)/%.rt.o $(comENTRY) $(comEXIT) $(LDSCRIPT) $(MAKEDEPS)
+ $(LD) $(LDFLAGS) -T $(LDSCRIPT) -o $@ $< $(comEXIT)
+
+$(BIN)/%.com: $(BIN)/%.com.zprf
+ $(OBJCOPY) -O binary $< $@
+
+# rules to generate a DOS loadable .exe executable
+SUFFIXES += exe
+$(BIN)/%.exe.rt: $(BIN)/%.rt.o $(exeENTRY) $(exeEXIT) $(LDSCRIPT) $(MAKEDEPS)
+ $(LD) $(LDFLAGS) -T $(LDSCRIPT) -o $@ $< $(exeEXIT)
+ @$(SIZE) $@ | $(CHECKSIZE)
+
+$(BIN)/%.exe: $(BIN)/%.exe.prf
+ $(OBJCOPY) -O binary $< $@
+
+# rules to make a LILO loadable image
+SUFFIXES += lilo zlilo
+
+$(BIN)/%.lilo.rt: $(BIN)/%.rt.o $(liloENTRY) $(liloEXIT) $(LDSCRIPT) $(MAKEDEPS)
+ $(LD) $(LDFLAGS) -T $(LDSCRIPT) -o $@ $< $(liloEXIT)
+ @$(SIZE) $@ | $(CHECKSIZE)
+
+$(BIN)/%.lilo: $(BIN)/%.lilo.prf
+ $(OBJCOPY) -O binary $< $@
+
+$(BIN)/%.zlilo: $(BIN)/%.lilo.zprf
+ $(OBJCOPY) -O binary $< $@
+
+# rules to make big linux boot protocol image
+SUFFIXES += bImage bzImage
+
+$(BIN)/%.bImage.rt: $(BIN)/%.rt.o $(bImageENTRY) $(bImageEXIT) $(LDSCRIPT) $(MAKEDEPS)
+ $(LD) $(LDFLAGS) -T $(LDSCRIPT) -o $@ $< $(bImageEXIT)
+
+$(BIN)/%.bImage: $(BIN)/%.bImage.prf
+ $(OBJCOPY) -O binary $< $@
+
+$(BIN)/%.bzImage: $(BIN)/%.bImage.zprf
+ $(OBJCOPY) -O binary $< $@
+
+
+# rules to generate a PXE loadable image
+SUFFIXES += pxe zpxe
+
+$(BIN)/%.pxe.rt: $(BIN)/%.rt.o $(pxeENTRY) $(pxeEXIT) $(LDSCRIPT) $(MAKEDEPS)
+ $(LD) $(LDFLAGS) -T $(LDSCRIPT) -o $@ $< $(pxeEXIT)
+ @$(SIZE) $@ | $(CHECKSIZE)
+
+$(BIN)/%.pxe: $(BIN)/%.pxe.prf
+ $(OBJCOPY) -O binary $< $@
+
+$(BIN)/%.zpxe: $(BIN)/%.pxe.zprf
+ $(OBJCOPY) -O binary $< $@
+
+# rules to generate the .dsk/.zdsk floppy images
+SUFFIXES += dsk zdsk
+
+$(BIN)/%.dsk.rt: $(BIN)/%.rt.o $(dskENTRY) $(dskEXIT) $(LDSCRIPT) $(MAKEDEPS)
+ $(LD) $(LDFLAGS) -T $(LDSCRIPT) -o $@ $< $(dskEXIT)
+ @$(SIZE) $@ | $(CHECKSIZE)
+
+$(BIN)/%.dsk: $(BIN)/%.dsk.prf
+ $(OBJCOPY) -O binary $< $@
+
+$(BIN)/%.zdsk: $(BIN)/%.dsk.zprf
+ $(OBJCOPY) -O binary $< $@
+
+# rules to write the .dsk/.zdsk image onto a blank floppy
+SUFFIXES += fd0 zfd0
+%.fd0: %.dsk
+ dd if=$< bs=512 conv=sync of=/dev/fd0
+ sync
+
+%.zfd0: %.zdsk
+ dd if=$< bs=512 conv=sync of=/dev/fd0
+ sync
+
+# rules to create raw executable images
+SUFFIXES += raw zraw
+$(BIN)/%.raw.rt: $(BIN)/%.rt.o $(rawENTRY) $(rawEXIT) $(LDSCRIPT) $(MAKEDEPS)
+ $(LD) $(LDFLAGS) -T $(LDSCRIPT) -o $@ $< $(rawEXIT)
+
+$(BIN)/%.raw: $(BIN)/%.raw.prf
+ $(OBJCOPY) -O binary $< $@
+
+$(BIN)/%.zraw: $(BIN)/%.raw.zprf
+ $(OBJCOPY) -O binary $< $@
+
+# rule to make a non-emulation ISO boot image
+SUFFIXES += iso
+%.iso: util/geniso %.zlilo
+ ISOLINUX_BIN=${ISOLINUX_BIN} bash util/geniso $*.iso $*.zlilo
+
+# rule to make a floppy emulation ISO boot image
+SUFFIXES += liso
+%.liso: util/genliso %.zlilo
+ bash util/genliso $*.liso $*.zlilo
+
diff --git a/src/arch/i386/core/aout_loader.c b/src/arch/i386/core/aout_loader.c
new file mode 100644
index 00000000..f85620e9
--- /dev/null
+++ b/src/arch/i386/core/aout_loader.c
@@ -0,0 +1,144 @@
+/* a.out */
+struct exec {
+ unsigned long a_midmag; /* flags<<26 | mid<<16 | magic */
+ unsigned long a_text; /* text segment size */
+ unsigned long a_data; /* initialized data size */
+ unsigned long a_bss; /* uninitialized data size */
+ unsigned long a_syms; /* symbol table size */
+ unsigned long a_entry; /* entry point */
+ unsigned long a_trsize; /* text relocation size */
+ unsigned long a_drsize; /* data relocation size */
+};
+
+struct aout_state {
+ struct exec head;
+ unsigned long curaddr;
+ int segment; /* current segment number, -1 for none */
+ unsigned long loc; /* start offset of current block */
+ unsigned long skip; /* padding to be skipped to current segment */
+ unsigned long toread; /* remaining data to be read in the segment */
+};
+
+static struct aout_state astate;
+
+static sector_t aout_download(unsigned char *data, unsigned int len, int eof);
+static inline os_download_t aout_probe(unsigned char *data, unsigned int len)
+{
+ unsigned long start, mid, end, istart, iend;
+ if (len < sizeof(astate.head)) {
+ return 0;
+ }
+ memcpy(&astate.head, data, sizeof(astate.head));
+ if ((astate.head.a_midmag & 0xffff) != 0x010BL) {
+ return 0;
+ }
+
+ printf("(a.out");
+ aout_freebsd_probe();
+ printf(")... ");
+ /* Check the aout image */
+ start = astate.head.a_entry;
+ mid = (((start + astate.head.a_text) + 4095) & ~4095) + astate.head.a_data;
+ end = ((mid + 4095) & ~4095) + astate.head.a_bss;
+ istart = 4096;
+ iend = istart + (mid - start);
+ if (!prep_segment(start, mid, end, istart, iend))
+ return dead_download;
+ astate.segment = -1;
+ astate.loc = 0;
+ astate.skip = 0;
+ astate.toread = 0;
+ return aout_download;
+}
+
+static sector_t aout_download(unsigned char *data, unsigned int len, int eof)
+{
+ unsigned int offset; /* working offset in the current data block */
+
+ offset = 0;
+
+#ifdef AOUT_LYNX_KDI
+ astate.segment++;
+ if (astate.segment == 0) {
+ astate.curaddr = 0x100000;
+ astate.head.a_entry = astate.curaddr + 0x20;
+ }
+ memcpy(phys_to_virt(astate.curaddr), data, len);
+ astate.curaddr += len;
+ return 0;
+#endif
+
+ do {
+ if (astate.segment != -1) {
+ if (astate.skip) {
+ if (astate.skip >= len - offset) {
+ astate.skip -= len - offset;
+ break;
+ }
+ offset += astate.skip;
+ astate.skip = 0;
+ }
+
+ if (astate.toread) {
+ if (astate.toread >= len - offset) {
+ memcpy(phys_to_virt(astate.curaddr), data+offset,
+ len - offset);
+ astate.curaddr += len - offset;
+ astate.toread -= len - offset;
+ break;
+ }
+ memcpy(phys_to_virt(astate.curaddr), data+offset, astate.toread);
+ offset += astate.toread;
+ astate.toread = 0;
+ }
+ }
+
+ /* Data left, but current segment finished - look for the next
+ * segment. This is quite simple for a.out files. */
+ astate.segment++;
+ switch (astate.segment) {
+ case 0:
+ /* read text */
+ astate.curaddr = astate.head.a_entry;
+ astate.skip = 4096;
+ astate.toread = astate.head.a_text;
+ break;
+ case 1:
+ /* read data */
+ /* skip and curaddr may be wrong, but I couldn't find
+ * examples where this failed. There is no reasonable
+ * documentation for a.out available. */
+ astate.skip = ((astate.curaddr + 4095) & ~4095) - astate.curaddr;
+ astate.curaddr = (astate.curaddr + 4095) & ~4095;
+ astate.toread = astate.head.a_data;
+ break;
+ case 2:
+ /* initialize bss and start kernel */
+ astate.curaddr = (astate.curaddr + 4095) & ~4095;
+ astate.skip = 0;
+ astate.toread = 0;
+ memset(phys_to_virt(astate.curaddr), '\0', astate.head.a_bss);
+ goto aout_startkernel;
+ default:
+ break;
+ }
+ } while (offset < len);
+
+ astate.loc += len;
+
+ if (eof) {
+ unsigned long entry;
+
+aout_startkernel:
+ entry = astate.head.a_entry;
+ done(1);
+
+ aout_freebsd_boot();
+#ifdef AOUT_LYNX_KDI
+ xstart32(entry);
+#endif
+ printf("unexpected a.out variant\n");
+ longjmp(restart_etherboot, -2);
+ }
+ return 0;
+}
diff --git a/src/arch/i386/core/callbacks.c b/src/arch/i386/core/callbacks.c
new file mode 100644
index 00000000..d45e63e2
--- /dev/null
+++ b/src/arch/i386/core/callbacks.c
@@ -0,0 +1,107 @@
+/* Callout/callback interface for Etherboot
+ *
+ * This file provides the mechanisms for making calls from Etherboot
+ * to external programs and vice-versa.
+ *
+ * Initial version by Michael Brown <mbrown@fensystems.co.uk>, January 2004.
+ */
+
+#include "etherboot.h"
+#include "callbacks.h"
+#include "realmode.h"
+#include "segoff.h"
+#include <stdarg.h>
+
+/* Maximum amount of stack data that prefix may request to be passed
+ * to its exit routine
+ */
+#define MAX_PREFIX_STACK_DATA 16
+
+/* Prefix exit routine is defined in prefix object */
+extern void prefix_exit ( void );
+extern void prefix_exit_end ( void );
+
+/*****************************************************************************
+ *
+ * IN_CALL INTERFACE
+ *
+ *****************************************************************************
+ */
+
+/* in_call(): entry point for calls in to Etherboot from external code.
+ *
+ * Parameters: some set up by assembly code _in_call(), others as
+ * passed from external code.
+ */
+uint32_t i386_in_call ( va_list ap, i386_pm_in_call_data_t pm_data,
+ uint32_t opcode ) {
+ uint32_t ret;
+ i386_rm_in_call_data_t rm_data;
+ in_call_data_t in_call_data = { &pm_data, NULL };
+ struct {
+ int data[MAX_PREFIX_STACK_DATA/4];
+ } in_stack;
+
+ /* Fill out rm_data if we were called from real mode */
+ if ( opcode & EB_CALL_FROM_REAL_MODE ) {
+ in_call_data.rm = &rm_data;
+ rm_data = va_arg ( ap, typeof(rm_data) );
+ /* Null return address indicates to use the special
+ * prefix exit mechanism, and that there are
+ * parameters on the stack that the prefix wants
+ * handed to its exit routine.
+ */
+ if ( rm_data.ret_addr.offset == 0 ) {
+ int n = va_arg ( ap, int ) / 4;
+ int i;
+ for ( i = 0; i < n; i++ ) {
+ in_stack.data[i] = va_arg ( ap, int );
+ }
+ }
+ }
+
+ /* Hand off to main in_call() routine */
+ ret = in_call ( &in_call_data, opcode, ap );
+
+ /* If real-mode return address is null, it means that we
+ * should exit via the prefix's exit path, which is part of
+ * our image. (This arrangement is necessary since the prefix
+ * code itself may have been vapourised by the time we want to
+ * return.)
+ */
+ if ( ( opcode & EB_CALL_FROM_REAL_MODE ) &&
+ ( rm_data.ret_addr.offset == 0 ) ) {
+ real_call ( prefix_exit, &in_stack, NULL );
+ /* Should never return */
+ }
+
+ return ret;
+}
+
+#ifdef CODE16
+
+/* install_rm_callback_interface(): install real-mode callback
+ * interface at specified address.
+ *
+ * Real-mode code may then call to this address (or lcall to this
+ * address plus RM_IN_CALL_FAR) in order to make an in_call() to
+ * Etherboot.
+ *
+ * Returns the size of the installed code, or 0 if the code could not
+ * be installed.
+ */
+int install_rm_callback_interface ( void *address, size_t available ) {
+ if ( available &&
+ ( available < rm_callback_interface_size ) ) return 0;
+
+ /* Inform RM code where to find Etherboot */
+ rm_etherboot_location = virt_to_phys(_text);
+
+ /* Install callback interface */
+ memcpy ( address, &rm_callback_interface,
+ rm_callback_interface_size );
+
+ return rm_callback_interface_size;
+}
+
+#endif /* CODE16 */
diff --git a/src/arch/i386/core/cpu.c b/src/arch/i386/core/cpu.c
new file mode 100644
index 00000000..8a0f7333
--- /dev/null
+++ b/src/arch/i386/core/cpu.c
@@ -0,0 +1,86 @@
+#ifdef CONFIG_X86_64
+#include "stdint.h"
+#include "string.h"
+#include "bits/cpu.h"
+
+
+/* Standard macro to see if a specific flag is changeable */
+static inline int flag_is_changeable_p(uint32_t flag)
+{
+ uint32_t f1, f2;
+
+ asm("pushfl\n\t"
+ "pushfl\n\t"
+ "popl %0\n\t"
+ "movl %0,%1\n\t"
+ "xorl %2,%0\n\t"
+ "pushl %0\n\t"
+ "popfl\n\t"
+ "pushfl\n\t"
+ "popl %0\n\t"
+ "popfl\n\t"
+ : "=&r" (f1), "=&r" (f2)
+ : "ir" (flag));
+
+ return ((f1^f2) & flag) != 0;
+}
+
+
+/* Probe for the CPUID instruction */
+static inline int have_cpuid_p(void)
+{
+ return flag_is_changeable_p(X86_EFLAGS_ID);
+}
+
+static void identify_cpu(struct cpuinfo_x86 *c)
+{
+ unsigned xlvl;
+
+ c->cpuid_level = -1; /* CPUID not detected */
+ c->x86_model = c->x86_mask = 0; /* So far unknown... */
+ c->x86_vendor_id[0] = '\0'; /* Unset */
+ memset(&c->x86_capability, 0, sizeof c->x86_capability);
+
+ if (!have_cpuid_p()) {
+ /* CPU doesn'thave CPUID */
+
+ /* If there are any capabilities, they'r vendor-specific */
+ /* enable_cpuid() would have set c->x86 for us. */
+ }
+ else {
+ /* CPU does have CPUID */
+
+ /* Get vendor name */
+ cpuid(0x00000000, &c->cpuid_level,
+ (int *)&c->x86_vendor_id[0],
+ (int *)&c->x86_vendor_id[8],
+ (int *)&c->x86_vendor_id[4]);
+
+ /* Initialize the standard set of capabilities */
+ /* Note that the vendor-specific code below might override */
+
+ /* Intel-defined flags: level 0x00000001 */
+ if ( c->cpuid_level >= 0x00000001 ) {
+ unsigned tfms, junk;
+ cpuid(0x00000001, &tfms, &junk, &junk,
+ &c->x86_capability[0]);
+ c->x86 = (tfms >> 8) & 15;
+ c->x86_model = (tfms >> 4) & 15;
+ c->x86_mask = tfms & 15;
+ }
+
+ /* AMD-defined flags: level 0x80000001 */
+ xlvl = cpuid_eax(0x80000000);
+ if ( (xlvl & 0xffff0000) == 0x80000000 ) {
+ if ( xlvl >= 0x80000001 )
+ c->x86_capability[1] = cpuid_edx(0x80000001);
+ }
+ }
+}
+
+struct cpuinfo_x86 cpu_info;
+void cpu_setup(void)
+{
+ identify_cpu(&cpu_info);
+}
+#endif /* CONFIG_X86_64 */
diff --git a/src/arch/i386/core/elf.c b/src/arch/i386/core/elf.c
new file mode 100644
index 00000000..6ef78c71
--- /dev/null
+++ b/src/arch/i386/core/elf.c
@@ -0,0 +1,135 @@
+#include "etherboot.h"
+#include "elf.h"
+
+
+#define NAME "Etherboot"
+
+#if defined(PCBIOS)
+#define FIRMWARE "PCBIOS"
+#endif
+#if defined(LINUXBIOS)
+#define FIRMWARE "LinuxBIOS"
+#endif
+#if !defined(FIRMWARE)
+#error "No BIOS selected"
+#endif
+
+#define SZ(X) ((sizeof(X)+3) & ~3)
+#define CP(D,S) (memcpy(&(D), &(S), sizeof(S)))
+
+struct elf_notes {
+ /* The note header */
+ struct Elf_Bhdr hdr;
+
+ /* First the Fixed sized entries that must be well aligned */
+
+ /* Pointer to bootp data */
+ Elf_Nhdr nf1;
+ char nf1_name[SZ(EB_PARAM_NOTE)];
+ uint32_t nf1_bootp_data;
+
+ /* Pointer to ELF header */
+ Elf_Nhdr nf2;
+ char nf2_name[SZ(EB_PARAM_NOTE)];
+ uint32_t nf2_header;
+
+ /* A copy of the i386 memory map */
+ Elf_Nhdr nf3;
+ char nf3_name[SZ(EB_PARAM_NOTE)];
+ struct meminfo nf3_meminfo;
+
+ /* Then the variable sized data string data where alignment does not matter */
+
+ /* The bootloader name */
+ Elf_Nhdr nv1;
+ char nv1_desc[SZ(NAME)];
+ /* The bootloader version */
+ Elf_Nhdr nv2;
+ char nv2_desc[SZ(VERSION)];
+ /* The firmware type */
+ Elf_Nhdr nv3;
+ char nv3_desc[SZ(FIRMWARE)];
+ /* Name of the loaded image */
+ Elf_Nhdr nv4;
+ char nv4_loaded_image[128];
+ /* An empty command line */
+ Elf_Nhdr nv5;
+ char nv5_cmdline[SZ("")];
+};
+
+#define ELF_NOTE_COUNT (3 + 5)
+
+static struct elf_notes notes;
+struct Elf_Bhdr *prepare_boot_params(void *header)
+{
+ memset(&notes, 0, sizeof(notes));
+ notes.hdr.b_signature = ELF_BHDR_MAGIC;
+ notes.hdr.b_size = sizeof(notes);
+ notes.hdr.b_checksum = 0;
+ notes.hdr.b_records = ELF_NOTE_COUNT;
+
+ /* Initialize the fixed length entries. */
+ notes.nf1.n_namesz = sizeof(EB_PARAM_NOTE);
+ notes.nf1.n_descsz = sizeof(notes.nf1_bootp_data);
+ notes.nf1.n_type = EB_BOOTP_DATA;
+ CP(notes.nf1_name, EB_PARAM_NOTE);
+ notes.nf1_bootp_data = virt_to_phys(BOOTP_DATA_ADDR);
+
+ notes.nf2.n_namesz = sizeof(EB_PARAM_NOTE);
+ notes.nf2.n_descsz = sizeof(notes.nf2_header);
+ notes.nf2.n_type = EB_HEADER;
+ CP(notes.nf2_name, EB_PARAM_NOTE);
+ notes.nf2_header = virt_to_phys(header);
+
+ notes.nf3.n_namesz = sizeof(EB_PARAM_NOTE);
+ notes.nf3.n_descsz = sizeof(notes.nf3_meminfo);
+ notes.nf3.n_type = EB_I386_MEMMAP;
+ CP(notes.nf3_name, EB_PARAM_NOTE);
+ memcpy(&notes.nf3_meminfo, &meminfo, sizeof(meminfo));
+
+ /* Initialize the variable length entries */
+ notes.nv1.n_namesz = 0;
+ notes.nv1.n_descsz = sizeof(NAME);
+ notes.nv1.n_type = EBN_BOOTLOADER_NAME;
+ CP(notes.nv1_desc, NAME);
+
+ notes.nv2.n_namesz = 0;
+ notes.nv2.n_descsz = sizeof(VERSION);
+ notes.nv2.n_type = EBN_BOOTLOADER_VERSION;
+ CP(notes.nv2_desc, VERSION);
+
+ notes.nv3.n_namesz = 0;
+ notes.nv3.n_descsz = sizeof(FIRMWARE);
+ notes.nv3.n_type = EBN_FIRMWARE_TYPE;
+ CP(notes.nv3_desc, FIRMWARE);
+
+ /* Attempt to pass the name of the loaded image */
+ notes.nv4.n_namesz = 0;
+ notes.nv4.n_descsz = sizeof(notes.nv4_loaded_image);
+ notes.nv4.n_type = EBN_LOADED_IMAGE;
+ memcpy(&notes.nv4_loaded_image, KERNEL_BUF, sizeof(notes.nv4_loaded_image));
+
+ /* Pass an empty command line for now */
+ notes.nv5.n_namesz = 0;
+ notes.nv5.n_descsz = sizeof("");
+ notes.nv5.n_type = EBN_COMMAND_LINE;
+ CP(notes.nv5_cmdline, "");
+
+
+ notes.hdr.b_checksum = ipchksum(&notes, sizeof(notes));
+ /* Like UDP invert a 0 checksum to show that a checksum is present */
+ if (notes.hdr.b_checksum == 0) {
+ notes.hdr.b_checksum = 0xffff;
+ }
+ return &notes.hdr;
+}
+
+int elf_start(unsigned long machine __unused_i386, unsigned long entry, unsigned long params)
+{
+#if defined(CONFIG_X86_64)
+ if (machine == EM_X86_64) {
+ return xstart_lm(entry, params);
+ }
+#endif
+ return xstart32(entry, params);
+}
diff --git a/src/arch/i386/core/etherboot.lds b/src/arch/i386/core/etherboot.lds
new file mode 100644
index 00000000..6f406329
--- /dev/null
+++ b/src/arch/i386/core/etherboot.lds
@@ -0,0 +1,90 @@
+OUTPUT_FORMAT("elf32-i386", "elf32-i386", "elf32-i386")
+OUTPUT_ARCH(i386)
+
+ENTRY(_text)
+SECTIONS {
+ . = ALIGN(16);
+ /* Start address of Etherboot in the virtual address space */
+ _virt_start = 0;
+ _text = . ;
+ .text.nocompress : {
+ *(.text*.nocompress)
+ . = ALIGN(16);
+ } = 0x9090
+
+ .text16 : {
+ _text16 = .;
+ *(.text16)
+ *(.text16.*)
+ _etext16 = . ;
+ }
+ .text.compress : {
+ *(.text)
+ *(.text.*)
+ } = 0x9090
+ .rodata : {
+ . = ALIGN(4);
+ *(.rodata)
+ *(.rodata.*)
+ }
+ . = ALIGN(4);
+ .drivers.pci : {
+ pci_drivers = . ;
+ *(.drivers.pci);
+ pci_drivers_end = . ;
+ }
+ . = ALIGN(4);
+ .drivers.isa : {
+ isa_drivers = . ;
+ *(.drivers.isa);
+ isa_drivers_end = .;
+ }
+ _etext = . ;
+ _data = . ;
+ .data : {
+ *(.data)
+ *(.data.*)
+ }
+ _edata = . ;
+ _uncompressed_verbatim_end = . ;
+ . = ALIGN(16);
+ .bss.preserve : {
+ *(.bss.preserve)
+ *(.bss.preserve.*)
+ }
+ _bss = . ;
+ .bss : {
+ *(.bss)
+ *(.bss.*)
+ }
+ . = ALIGN(16);
+ _ebss = .;
+ _stack = . ;
+ .stack : {
+ _stack_start = . ;
+ *(.stack)
+ *(.stack.*)
+ _stack_end = . ;
+ }
+ _bss_size = _ebss - _bss;
+ _stack_offset = _stack - _text ;
+ _stack_offset_pgh = _stack_offset / 16 ;
+ _stack_size = _stack_end - _stack_start ;
+ . = ALIGN(16);
+ _end = . ;
+
+ /DISCARD/ : {
+ *(.comment)
+ *(.note)
+ }
+
+ /* PXE-specific symbol calculations. The results of these are
+ * needed in romprefix.S, which is why they must be calculated
+ * here.
+ */
+ _pxe_stack_size = _pxe_stack_t_size
+ + _pxe_callback_interface_size
+ + _rm_callback_interface_size
+ + _e820mangler_size + 15 ;
+
+}
diff --git a/src/arch/i386/core/etherboot.prefix.lds b/src/arch/i386/core/etherboot.prefix.lds
new file mode 100644
index 00000000..3550a2a3
--- /dev/null
+++ b/src/arch/i386/core/etherboot.prefix.lds
@@ -0,0 +1,100 @@
+OUTPUT_FORMAT("elf32-i386", "elf32-i386", "elf32-i386")
+OUTPUT_ARCH(i386)
+
+ENTRY(_prefix_start)
+SECTIONS {
+ /* Prefix */
+ .prefix : {
+ _verbatim_start = . ;
+ _prefix_start = . ;
+ *(.prefix)
+ . = ALIGN(16);
+ _prefix_end = . ;
+ } = 0x9090
+ _prefix_size = _prefix_end - _prefix_start;
+
+ .text.nocompress : {
+ *(.prefix.udata)
+ } = 0x9090
+
+ decompress_to = . ;
+ .prefix.zdata : {
+ _compressed = . ;
+ *(.prefix.zdata)
+ _compressed_end = . ;
+ }
+ _compressed_size = _compressed_end - _compressed;
+
+ . = ALIGN(16);
+ _verbatim_end = . ;
+
+
+ /* Size of the core of etherboot in memory */
+ _base_size = _end - _text;
+
+ /* _prefix_size is the length of the non-core etherboot prefix */
+ _prefix_size = _prefix_end - _prefix_start;
+
+ /* _verbatim_size is the actual amount that has to be copied to base memory */
+ _verbatim_size = _verbatim_end - _verbatim_start;
+
+ /* _image_size is the amount of base memory needed to run */
+ _image_size = _base_size + _prefix_size;
+
+ /* Standard sizes rounded up to paragraphs */
+ _prefix_size_pgh = (_prefix_size + 15) / 16;
+ _verbatim_size_pgh = (_verbatim_size + 15) / 16;
+ _image_size_pgh = (_image_size + 15) / 16 ;
+
+ /* Standard sizes in sectors */
+ _prefix_size_sct = (_prefix_size + 511) / 512;
+ _verbatim_size_sct = (_verbatim_size + 511) / 512;
+ _image_size_sct = (_image_size + 511) / 512;
+
+ /* Symbol offsets and sizes for the exe prefix */
+ _exe_hdr_size = 32;
+ _exe_size = _verbatim_size; /* Should this be - 32 to exclude the header? */
+ _exe_size_tail = (_exe_size) % 512;
+ _exe_size_pages = ((_exe_size) + 511) / 512;
+ _exe_bss_size = ((_image_size - _verbatim_size) + 15) / 16;
+ _exe_ss_offset = (_stack_offset + _prefix_size - _exe_hdr_size + 15) / 16 ;
+
+ /* This is where we copy the compressed image before decompression.
+ * Prepare to decompress in place. The end mark is about 8.25 bytes long,
+ * and the worst case symbol is about 16.5 bytes long. Therefore
+ * We need to reserve at least 25 bytes of slack here.
+ * Currently I reserve 2048 bytes of just slack to be safe :)
+ * 2048 bytes easily falls within the BSS (the defualt stack is 4096 bytes)
+ * so we really are decompressing in place.
+ *
+ * Hmm. I missed a trick. In the very worst case (no compression)
+ * the encoded data is 9/8 the size as it started out so to be completely
+ * safe I need to be 1/8 of the uncompressed code size past the end.
+ * This will still fit compfortably into our bss in any conceivable scenario.
+ */
+ _compressed_copy = _edata + _prefix_size - _compressed_size +
+ /* The amount to overflow _edata */
+ MAX( ((_edata - _text + 7) / 8) , 2016 ) + 32;
+ _assert = ASSERT( ( _compressed_copy - _prefix_size ) < _ebss , "Cannot decompress in place" ) ;
+
+ decompress = DEFINED(decompress) ? decompress : 0;
+ /DISCARD/ : {
+ *(.comment)
+ *(.note)
+ }
+
+ /* Symbols used by the prefixes whose addresses are inconvinient
+ * to compute, at runtime in the code.
+ */
+ image_basemem_size = DEFINED(image_basemem_size)? image_basemem_size : 65536;
+ image_basemem = DEFINED(image_basemem)? image_basemem : 65536;
+ _prefix_real_to_prot = _real_to_prot + _prefix_size ;
+ _prefix_prot_to_real = _prot_to_real + _prefix_size ;
+ _prefix_image_basemem_size = image_basemem_size + _prefix_size ;
+ _prefix_image_basemem = image_basemem + _prefix_size ;
+ _prefix_rm_in_call = _rm_in_call + _prefix_size ;
+ _prefix_in_call = _in_call + _prefix_size ;
+ _prefix_rom = rom + _prefix_size ;
+ _prefix_rm_etherboot_location = rm_etherboot_location + _prefix_size ;
+ _prefix_stack_end = _stack_end + _prefix_size ;
+}
diff --git a/src/arch/i386/core/freebsd_loader.c b/src/arch/i386/core/freebsd_loader.c
new file mode 100644
index 00000000..4e820e8e
--- /dev/null
+++ b/src/arch/i386/core/freebsd_loader.c
@@ -0,0 +1,377 @@
+/* bootinfo */
+#define BOOTINFO_VERSION 1
+#define NODEV (-1) /* non-existent device */
+#define PAGE_SHIFT 12 /* LOG2(PAGE_SIZE) */
+#define PAGE_SIZE (1<<PAGE_SHIFT) /* bytes/page */
+#define PAGE_MASK (PAGE_SIZE-1)
+#define N_BIOS_GEOM 8
+
+struct bootinfo {
+ unsigned int bi_version;
+ const unsigned char *bi_kernelname;
+ struct nfs_diskless *bi_nfs_diskless;
+ /* End of fields that are always present. */
+#define bi_endcommon bi_n_bios_used
+ unsigned int bi_n_bios_used;
+ unsigned long bi_bios_geom[N_BIOS_GEOM];
+ unsigned int bi_size;
+ unsigned char bi_memsizes_valid;
+ unsigned char bi_pad[3];
+ unsigned long bi_basemem;
+ unsigned long bi_extmem;
+ unsigned long bi_symtab;
+ unsigned long bi_esymtab;
+ /* Note that these are in the FreeBSD headers but were not here... */
+ unsigned long bi_kernend; /* end of kernel space */
+ unsigned long bi_envp; /* environment */
+ unsigned long bi_modulep; /* preloaded modules */
+};
+
+static struct bootinfo bsdinfo;
+
+#ifdef ELF_IMAGE
+static Elf32_Shdr *shdr; /* To support the FreeBSD kludge! */
+static Address symtab_load;
+static Address symstr_load;
+static int symtabindex;
+static int symstrindex;
+#endif
+
+static enum {
+ Unknown, Tagged, Aout, Elf, Aout_FreeBSD, Elf_FreeBSD,
+} image_type = Unknown;
+
+static unsigned int off;
+
+
+#ifdef ELF_IMAGE
+static void elf_freebsd_probe(void)
+{
+ image_type = Elf;
+ if ( (estate.e.elf32.e_entry & 0xf0000000) &&
+ (estate.e.elf32.e_type == ET_EXEC))
+ {
+ image_type = Elf_FreeBSD;
+ printf("/FreeBSD");
+ off = -(estate.e.elf32.e_entry & 0xff000000);
+ estate.e.elf32.e_entry += off;
+ }
+ /* Make sure we have a null to start with... */
+ shdr = 0;
+
+ /* Clear the symbol index values... */
+ symtabindex = -1;
+ symstrindex = -1;
+
+ /* ...and the load addresses of the symbols */
+ symtab_load = 0;
+ symstr_load = 0;
+}
+
+static void elf_freebsd_fixup_segment(void)
+{
+ if (image_type == Elf_FreeBSD) {
+ estate.p.phdr32[estate.segment].p_paddr += off;
+ }
+}
+
+static void elf_freebsd_find_segment_end(void)
+{
+ /* Count the bytes read even for the last block
+ * as we will need to know where the last block
+ * ends in order to load the symbols correctly.
+ * (plus it could be useful elsewhere...)
+ * Note that we need to count the actual size,
+ * not just the end of the disk image size.
+ */
+ estate.curaddr +=
+ (estate.p.phdr32[estate.segment].p_memsz -
+ estate.p.phdr32[estate.segment].p_filesz);
+}
+
+static int elf_freebsd_debug_loader(unsigned int offset)
+{
+ /* No more segments to be loaded - time to start the
+ * nasty state machine to support the loading of
+ * FreeBSD debug symbols due to the fact that FreeBSD
+ * uses/exports the kernel's debug symbols in order
+ * to make much of the system work! Amazing (arg!)
+ *
+ * We depend on the fact that for the FreeBSD kernel,
+ * there is only one section of debug symbols and that
+ * the section is after all of the loaded sections in
+ * the file. This assumes a lot but is somewhat required
+ * to make this code not be too annoying. (Where do you
+ * load symbols when the code has not loaded yet?)
+ * Since this function is actually just a callback from
+ * the network data transfer code, we need to be able to
+ * work with the data as it comes in. There is no chance
+ * for doing a seek other than forwards.
+ *
+ * The process we use is to first load the section
+ * headers. Once they are loaded (shdr != 0) we then
+ * look for where the symbol table and symbol table
+ * strings are and setup some state that we found
+ * them and fall into processing the first one (which
+ * is the symbol table) and after that has been loaded,
+ * we try the symbol strings. Note that the order is
+ * actually required as the memory image depends on
+ * the symbol strings being loaded starting at the
+ * end of the symbol table. The kernel assumes this
+ * layout of the image.
+ *
+ * At any point, if we get to the end of the load file
+ * or the section requested is earlier in the file than
+ * the current file pointer, we just end up falling
+ * out of this and booting the kernel without this
+ * information.
+ */
+
+ /* Make sure that the next address is long aligned... */
+ /* Assumes size of long is a power of 2... */
+ estate.curaddr = (estate.curaddr + sizeof(long) - 1) & ~(sizeof(long) - 1);
+
+ /* If we have not yet gotten the shdr loaded, try that */
+ if (shdr == 0)
+ {
+ estate.toread = estate.e.elf32.e_shnum * estate.e.elf32.e_shentsize;
+ estate.skip = estate.e.elf32.e_shoff - (estate.loc + offset);
+ if (estate.toread)
+ {
+#if ELF_DEBUG
+ printf("shdr *, size %lX, curaddr %lX\n",
+ estate.toread, estate.curaddr);
+#endif
+
+ /* Start reading at the curaddr and make that the shdr */
+ shdr = (Elf32_Shdr *)phys_to_virt(estate.curaddr);
+
+ /* Start to read... */
+ return 1;
+ }
+ }
+ else
+ {
+ /* We have the shdr loaded, check if we have found
+ * the indexs where the symbols are supposed to be */
+ if ((symtabindex == -1) && (symstrindex == -1))
+ {
+ int i;
+ /* Make sure that the address is page aligned... */
+ /* Symbols need to start in their own page(s)... */
+ estate.curaddr = (estate.curaddr + 4095) & ~4095;
+
+ /* Need to make new indexes... */
+ for (i=0; i < estate.e.elf32.e_shnum; i++)
+ {
+ if (shdr[i].sh_type == SHT_SYMTAB)
+ {
+ int j;
+ for (j=0; j < estate.e.elf32.e_phnum; j++)
+ {
+ /* Check only for loaded sections */
+ if ((estate.p.phdr32[j].p_type | 0x80) == (PT_LOAD | 0x80))
+ {
+ /* Only the extra symbols */
+ if ((shdr[i].sh_offset >= estate.p.phdr32[j].p_offset) &&
+ ((shdr[i].sh_offset + shdr[i].sh_size) <=
+ (estate.p.phdr32[j].p_offset + estate.p.phdr32[j].p_filesz)))
+ {
+ shdr[i].sh_offset=0;
+ shdr[i].sh_size=0;
+ break;
+ }
+ }
+ }
+ if ((shdr[i].sh_offset != 0) && (shdr[i].sh_size != 0))
+ {
+ symtabindex = i;
+ symstrindex = shdr[i].sh_link;
+ }
+ }
+ }
+ }
+
+ /* Check if we have a symbol table index and have not loaded it */
+ if ((symtab_load == 0) && (symtabindex >= 0))
+ {
+ /* No symbol table yet? Load it first... */
+
+ /* This happens to work out in a strange way.
+ * If we are past the point in the file already,
+ * we will skip a *large* number of bytes which
+ * ends up bringing us to the end of the file and
+ * an old (default) boot. Less code and lets
+ * the state machine work in a cleaner way but this
+ * is a nasty side-effect trick... */
+ estate.skip = shdr[symtabindex].sh_offset - (estate.loc + offset);
+
+ /* And we need to read this many bytes... */
+ estate.toread = shdr[symtabindex].sh_size;
+
+ if (estate.toread)
+ {
+#if ELF_DEBUG
+ printf("db sym, size %lX, curaddr %lX\n",
+ estate.toread, estate.curaddr);
+#endif
+ /* Save where we are loading this... */
+ symtab_load = phys_to_virt(estate.curaddr);
+
+ *((long *)phys_to_virt(estate.curaddr)) = estate.toread;
+ estate.curaddr += sizeof(long);
+
+ /* Start to read... */
+ return 1;
+ }
+ }
+ else if ((symstr_load == 0) && (symstrindex >= 0))
+ {
+ /* We have already loaded the symbol table, so
+ * now on to the symbol strings... */
+
+
+ /* Same nasty trick as above... */
+ estate.skip = shdr[symstrindex].sh_offset - (estate.loc + offset);
+
+ /* And we need to read this many bytes... */
+ estate.toread = shdr[symstrindex].sh_size;
+
+ if (estate.toread)
+ {
+#if ELF_DEBUG
+ printf("db str, size %lX, curaddr %lX\n",
+ estate.toread, estate.curaddr);
+#endif
+ /* Save where we are loading this... */
+ symstr_load = phys_to_virt(estate.curaddr);
+
+ *((long *)phys_to_virt(estate.curaddr)) = estate.toread;
+ estate.curaddr += sizeof(long);
+
+ /* Start to read... */
+ return 1;
+ }
+ }
+ }
+ /* all done */
+ return 0;
+}
+
+static void elf_freebsd_boot(unsigned long entry)
+{
+ if (image_type != Elf_FreeBSD)
+ return;
+
+ memset(&bsdinfo, 0, sizeof(bsdinfo));
+ bsdinfo.bi_basemem = meminfo.basememsize;
+ bsdinfo.bi_extmem = meminfo.memsize;
+ bsdinfo.bi_memsizes_valid = 1;
+ bsdinfo.bi_version = BOOTINFO_VERSION;
+ bsdinfo.bi_kernelname = virt_to_phys(KERNEL_BUF);
+ bsdinfo.bi_nfs_diskless = NULL;
+ bsdinfo.bi_size = sizeof(bsdinfo);
+#define RB_BOOTINFO 0x80000000 /* have `struct bootinfo *' arg */
+ if(freebsd_kernel_env[0] != '\0'){
+ freebsd_howto |= RB_BOOTINFO;
+ bsdinfo.bi_envp = (unsigned long)freebsd_kernel_env;
+ }
+
+ /* Check if we have symbols loaded, and if so,
+ * made the meta_data needed to pass those to
+ * the kernel. */
+ if ((symtab_load !=0) && (symstr_load != 0))
+ {
+ unsigned long *t;
+
+ bsdinfo.bi_symtab = symtab_load;
+
+ /* End of symbols (long aligned...) */
+ /* Assumes size of long is a power of 2... */
+ bsdinfo.bi_esymtab = (symstr_load +
+ sizeof(long) +
+ *((long *)symstr_load) +
+ sizeof(long) - 1) & ~(sizeof(long) - 1);
+
+ /* Where we will build the meta data... */
+ t = phys_to_virt(bsdinfo.bi_esymtab);
+
+#if ELF_DEBUG
+ printf("Metadata at %lX\n",t);
+#endif
+
+ /* Set up the pointer to the memory... */
+ bsdinfo.bi_modulep = virt_to_phys(t);
+
+ /* The metadata structure is an array of 32-bit
+ * words where we store some information about the
+ * system. This is critical, as FreeBSD now looks
+ * only for the metadata for the extended symbol
+ * information rather than in the bootinfo.
+ */
+ /* First, do the kernel name and the kernel type */
+ /* Note that this assumed x86 byte order... */
+
+ /* 'kernel\0\0' */
+ *t++=MODINFO_NAME; *t++= 7; *t++=0x6E72656B; *t++=0x00006C65;
+
+ /* 'elf kernel\0\0' */
+ *t++=MODINFO_TYPE; *t++=11; *t++=0x20666C65; *t++=0x6E72656B; *t++ = 0x00006C65;
+
+ /* Now the symbol start/end - note that they are
+ * here in local/physical address - the Kernel
+ * boot process will relocate the addresses. */
+ *t++=MODINFOMD_SSYM | MODINFO_METADATA; *t++=sizeof(*t); *t++=bsdinfo.bi_symtab;
+ *t++=MODINFOMD_ESYM | MODINFO_METADATA; *t++=sizeof(*t); *t++=bsdinfo.bi_esymtab;
+
+ *t++=MODINFO_END; *t++=0; /* end of metadata */
+
+ /* Since we have symbols we need to make
+ * sure that the kernel knows its own end
+ * of memory... It is not _end but after
+ * the symbols and the metadata... */
+ bsdinfo.bi_kernend = virt_to_phys(t);
+
+ /* Signal locore.s that we have a valid bootinfo
+ * structure that was completely filled in. */
+ freebsd_howto |= 0x80000000;
+ }
+
+ xstart32(entry, freebsd_howto, NODEV, 0, 0, 0,
+ virt_to_phys(&bsdinfo), 0, 0, 0);
+ longjmp(restart_etherboot, -2);
+}
+#endif
+
+#ifdef AOUT_IMAGE
+static void aout_freebsd_probe(void)
+{
+ image_type = Aout;
+ if (((astate.head.a_midmag >> 16) & 0xffff) == 0) {
+ /* Some other a.out variants have a different
+ * value, and use other alignments (e.g. 1K),
+ * not the 4K used by FreeBSD. */
+ image_type = Aout_FreeBSD;
+ printf("/FreeBSD");
+ off = -(astate.head.a_entry & 0xff000000);
+ astate.head.a_entry += off;
+ }
+}
+
+static void aout_freebsd_boot(void)
+{
+ if (image_type == Aout_FreeBSD) {
+ memset(&bsdinfo, 0, sizeof(bsdinfo));
+ bsdinfo.bi_basemem = meminfo.basememsize;
+ bsdinfo.bi_extmem = meminfo.memsize;
+ bsdinfo.bi_memsizes_valid = 1;
+ bsdinfo.bi_version = BOOTINFO_VERSION;
+ bsdinfo.bi_kernelname = virt_to_phys(KERNEL_BUF);
+ bsdinfo.bi_nfs_diskless = NULL;
+ bsdinfo.bi_size = sizeof(bsdinfo);
+ xstart32(astate.head.a_entry, freebsd_howto, NODEV, 0, 0, 0,
+ virt_to_phys(&bsdinfo), 0, 0, 0);
+ longjmp(restart_etherboot, -2);
+ }
+}
+#endif
diff --git a/src/arch/i386/core/hooks.c b/src/arch/i386/core/hooks.c
new file mode 100644
index 00000000..9ca6f480
--- /dev/null
+++ b/src/arch/i386/core/hooks.c
@@ -0,0 +1,35 @@
+#include "etherboot.h"
+#include "callbacks.h"
+#include <stdarg.h>
+
+void arch_main ( in_call_data_t *data __unused, va_list params __unused )
+{
+#ifdef PCBIOS
+ /* Deallocate base memory used for the prefix, if applicable
+ */
+ forget_prefix_base_memory();
+#endif
+
+}
+
+void arch_relocated_from (unsigned long old_addr )
+{
+
+#ifdef PCBIOS
+ /* Deallocate base memory used for the Etherboot runtime,
+ * if applicable
+ */
+ forget_runtime_base_memory( old_addr );
+#endif
+
+}
+
+void arch_on_exit ( int exit_status __unused )
+{
+#ifdef PCBIOS
+ /* Deallocate the real-mode stack now. We will reallocate
+ * the stack if are going to use it after this point.
+ */
+ forget_real_mode_stack();
+#endif
+}
diff --git a/src/arch/i386/core/i386_timer.c b/src/arch/i386/core/i386_timer.c
new file mode 100644
index 00000000..531183d4
--- /dev/null
+++ b/src/arch/i386/core/i386_timer.c
@@ -0,0 +1,191 @@
+/* A couple of routines to implement a low-overhead timer for drivers */
+
+ /*
+ * 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; either version 2, or (at
+ * your option) any later version.
+ */
+
+#include "etherboot.h"
+#include "timer.h"
+#include "latch.h"
+
+void __load_timer2(unsigned int ticks)
+{
+ /*
+ * Now let's take care of PPC channel 2
+ *
+ * Set the Gate high, program PPC channel 2 for mode 0,
+ * (interrupt on terminal count mode), binary count,
+ * load 5 * LATCH count, (LSB and MSB) to begin countdown.
+ *
+ * Note some implementations have a bug where the high bits byte
+ * of channel 2 is ignored.
+ */
+ /* Set up the timer gate, turn off the speaker */
+ /* Set the Gate high, disable speaker */
+ outb((inb(PPC_PORTB) & ~PPCB_SPKR) | PPCB_T2GATE, PPC_PORTB);
+ /* binary, mode 0, LSB/MSB, Ch 2 */
+ outb(TIMER2_SEL|WORD_ACCESS|MODE0|BINARY_COUNT, TIMER_MODE_PORT);
+ /* LSB of ticks */
+ outb(ticks & 0xFF, TIMER2_PORT);
+ /* MSB of ticks */
+ outb(ticks >> 8, TIMER2_PORT);
+}
+
+static int __timer2_running(void)
+{
+ return ((inb(PPC_PORTB) & PPCB_T2OUT) == 0);
+}
+
+#if !defined(CONFIG_TSC_CURRTICKS)
+void setup_timers(void)
+{
+ return;
+}
+
+void load_timer2(unsigned int ticks)
+{
+ return __load_timer2(ticks);
+}
+
+int timer2_running(void)
+{
+ return __timer2_running();
+}
+
+void ndelay(unsigned int nsecs)
+{
+ waiton_timer2((nsecs * CLOCK_TICK_RATE)/1000000000);
+}
+void udelay(unsigned int usecs)
+{
+ waiton_timer2((usecs * TICKS_PER_MS)/1000);
+}
+#endif /* !defined(CONFIG_TSC_CURRTICKS) */
+
+#if defined(CONFIG_TSC_CURRTICKS)
+
+#define rdtsc(low,high) \
+ __asm__ __volatile__("rdtsc" : "=a" (low), "=d" (high))
+
+#define rdtscll(val) \
+ __asm__ __volatile__ ("rdtsc" : "=A" (val))
+
+
+/* Number of clock ticks to time with the rtc */
+#define LATCH 0xFF
+
+#define LATCHES_PER_SEC ((CLOCK_TICK_RATE + (LATCH/2))/LATCH)
+#define TICKS_PER_LATCH ((LATCHES_PER_SEC + (TICKS_PER_SEC/2))/TICKS_PER_SEC)
+
+static void sleep_latch(void)
+{
+ __load_timer2(LATCH);
+ while(__timer2_running());
+}
+
+/* ------ Calibrate the TSC -------
+ * Time how long it takes to excute a loop that runs in known time.
+ * And find the convertion needed to get to CLOCK_TICK_RATE
+ */
+
+
+static unsigned long long calibrate_tsc(void)
+{
+ unsigned long startlow, starthigh;
+ unsigned long endlow, endhigh;
+
+ rdtsc(startlow,starthigh);
+ sleep_latch();
+ rdtsc(endlow,endhigh);
+
+ /* 64-bit subtract - gcc just messes up with long longs */
+ __asm__("subl %2,%0\n\t"
+ "sbbl %3,%1"
+ :"=a" (endlow), "=d" (endhigh)
+ :"g" (startlow), "g" (starthigh),
+ "0" (endlow), "1" (endhigh));
+
+ /* Error: ECPUTOOFAST */
+ if (endhigh)
+ goto bad_ctc;
+
+ endlow *= TICKS_PER_LATCH;
+ return endlow;
+
+ /*
+ * The CTC wasn't reliable: we got a hit on the very first read,
+ * or the CPU was so fast/slow that the quotient wouldn't fit in
+ * 32 bits..
+ */
+bad_ctc:
+ printf("bad_ctc\n");
+ return 0;
+}
+
+static unsigned long clocks_per_tick;
+void setup_timers(void)
+{
+ if (!clocks_per_tick) {
+ clocks_per_tick = calibrate_tsc();
+ /* Display the CPU Mhz to easily test if the calibration was bad */
+ printf("CPU %ld Mhz\n", (clocks_per_tick/1000 * TICKS_PER_SEC)/1000);
+ }
+}
+
+unsigned long currticks(void)
+{
+ unsigned long clocks_high, clocks_low;
+ unsigned long currticks;
+ /* Read the Time Stamp Counter */
+ rdtsc(clocks_low, clocks_high);
+
+ /* currticks = clocks / clocks_per_tick; */
+ __asm__("divl %1"
+ :"=a" (currticks)
+ :"r" (clocks_per_tick), "0" (clocks_low), "d" (clocks_high));
+
+
+ return currticks;
+}
+
+static unsigned long long timer_timeout;
+static int __timer_running(void)
+{
+ unsigned long long now;
+ rdtscll(now);
+ return now < timer_timeout;
+}
+
+void udelay(unsigned int usecs)
+{
+ unsigned long long now;
+ rdtscll(now);
+ timer_timeout = now + usecs * ((clocks_per_tick * TICKS_PER_SEC)/(1000*1000));
+ while(__timer_running());
+}
+void ndelay(unsigned int nsecs)
+{
+ unsigned long long now;
+ rdtscll(now);
+ timer_timeout = now + nsecs * ((clocks_per_tick * TICKS_PER_SEC)/(1000*1000*1000));
+ while(__timer_running());
+}
+
+void load_timer2(unsigned int timer2_ticks)
+{
+ unsigned long long now;
+ unsigned long clocks;
+ rdtscll(now);
+ clocks = timer2_ticks * ((clocks_per_tick * TICKS_PER_SEC)/CLOCK_TICK_RATE);
+ timer_timeout = now + clocks;
+}
+
+int timer2_running(void)
+{
+ return __timer_running();
+}
+
+#endif /* RTC_CURRTICKS */
diff --git a/src/arch/i386/core/init.S b/src/arch/i386/core/init.S
new file mode 100644
index 00000000..71717c25
--- /dev/null
+++ b/src/arch/i386/core/init.S
@@ -0,0 +1,305 @@
+#include "callbacks.h"
+ .equ CR0_PE, 1
+
+ .text
+ .arch i386
+ .section ".prefix", "ax", @progbits
+
+#undef CODE16
+#if defined(PCBIOS)
+#define CODE16
+#endif
+
+/* We have two entry points: "conventional" (at the start of the file)
+ * and "callback" (at _entry, 2 bytes in). The "callback" entry
+ * should be used if the caller wishes to provide a specific opcode.
+ * It is equivalent to a call to in_call. Using the "conventional"
+ * entry point is equivalent to using the "callback" entry point with
+ * an opcode of EB_OPCODE_MAIN.
+ *
+ * Both entry points can be called in either 16-bit real or 32-bit
+ * protected mode with flat physical addresses. We detect which mode
+ * the processor is in and call either in_call or rm_in_call as
+ * appropriate. Note that the mode detection code must therefore be
+ * capable of doing the same thing in either mode, even though the
+ * machine code instructions will be interpreted differently.
+ *
+ * The decompressor will be invoked if necessary to decompress
+ * Etherboot before attempting to jump to it.
+ */
+
+/******************************************************************************
+ * Entry points and mode detection code
+ ******************************************************************************
+ */
+
+ .code32
+/* "Conventional" entry point: caller provides no opcode */
+ .globl _start
+_start:
+ /* Set flag to indicate conventional entry point used */
+ pushl $0 /* "pushw $0" in 16-bit code */
+ /* Fall through to "callback" entry point */
+
+/* "Callback" entry point */
+ .globl _entry
+_entry:
+
+#ifdef CODE16
+ /* CPU mode detection code */
+ pushl %eax /* "pushw %ax" in 16-bit code */
+ pushw %ax /* "pushl %eax" in 16-bit code */
+ movl %cr0, %eax /* Test protected mode bit */
+ testb $CR0_PE, %al
+ popw %ax /* "popl %eax" in 16-bit code */
+ popl %eax /* "popw %eax" in 16-bit code */
+ jz rmode
+#endif /* CODE16 */
+
+/******************************************************************************
+ * Entered in protected mode
+ ******************************************************************************
+ */
+
+ .code32
+pmode:
+ cmpl $0, 0(%esp) /* Conventional entry point used? */
+ jne 1f
+ /* Entered via conventional entry point: set up stack */
+ xchgl %eax, 4(%esp) /* %eax = return addr, store %eax */
+ movl %eax, 0(%esp) /* 0(%esp) = return address */
+ movl $(EB_OPCODE_MAIN|EB_USE_INTERNAL_STACK|EB_SKIP_OPCODE), %eax
+ xchgl %eax, 4(%esp) /* 4(%esp) = opcode, restore %eax */
+1:
+ /* Run decompressor if necessary */
+ pushl %eax
+ movl $decompress, %eax
+ testl %eax, %eax
+ jz 1f
+ call decompress
+1: popl %eax
+
+ /* Make in_call to Etherboot */
+ jmp _prefix_in_call
+
+/******************************************************************************
+ * Entered in real mode
+ ******************************************************************************
+ */
+
+#ifdef CODE16
+ .code16
+rmode:
+ pushw %ax /* Padding */
+ pushw %bp
+ movw %sp, %bp
+ cmpw $0, 6(%bp) /* Conventional entry point used? */
+ jne 1f
+ /* Entered via conventional entry point: set up stack */
+ pushw %ax
+ movw 6(%bp), %ax
+ movw %ax, 2(%bp) /* Move return address down */
+ movl $(EB_OPCODE_MAIN|EB_USE_INTERNAL_STACK|EB_SKIP_OPCODE), 4(%bp)
+ popw %ax
+ popw %bp
+ jmp 2f
+1: /* Entered via callback entry point: do nothing */
+ popw %bp
+ popw %ax
+2:
+ /* Preserve registers */
+ pushw %ds
+ pushl %eax
+
+ /* Run decompressor if necessary. Decompressor is 32-bit
+ * code, so we must switch to pmode first. Save and restore
+ * GDT over transition to pmode.
+ */
+ movl $decompress, %eax
+ testl %eax, %eax
+ jz 1f
+ pushw %ds
+ pushw %es
+ pushw %fs
+ pushw %gs
+ subw $8, %sp
+ pushw %bp
+ movw %sp, %bp
+ sgdt 2(%bp)
+ pushw %ss /* Store params for _prot_to_real */
+ pushw %cs
+ call _prefix_real_to_prot
+ .code32
+ call decompress
+ call _prefix_prot_to_real
+ .code16
+ popw %ax /* skip */
+ popw %ax /* skip */
+ lgdt 2(%bp)
+ popw %bp
+ addw $8, %sp
+ popw %gs
+ popw %fs
+ popw %es
+ popw %ds
+1:
+
+ /* Set rm_etherboot_location */
+ xorl %eax, %eax
+ movw %cs, %ax
+ movw %ax, %ds
+ shll $4, %eax
+ addl $_prefix_size, %eax
+ movl %eax, _prefix_rm_etherboot_location
+
+ /* Restore registers */
+ popl %eax
+ popw %ds
+
+ /* Make real-mode in_call to Etherboot */
+ jmp _prefix_rm_in_call
+#endif /* CODE16 */
+
+/******************************************************************************
+ * Utility routines that can be called by the "prefix".
+ ******************************************************************************
+ */
+
+#ifdef CODE16
+
+/* Prelocate code: either to an area at the top of free base memory.
+ * Switch stacks to use the stack within the resulting
+ * Etherboot image.
+ *
+ * On entry, %cs:0000 must be the start of the prefix: this is used to
+ * locate the code to be copied.
+ *
+ * This routine takes a single word parameter: the number of bytes to
+ * be transferred from the old stack to the new stack (excluding the
+ * return address and this parameter itself, which will always be
+ * copied). If this value is negative, the stacks will not be
+ * switched.
+ *
+ * Control will "return" to the appropriate point in the relocated
+ * image.
+ */
+
+#define PRELOC_PRESERVE ( 20 )
+#define PRELOC_OFFSET_RETADDR ( PRELOC_PRESERVE )
+#define PRELOC_OFFSET_RETADDR_E ( PRELOC_OFFSET_RETADDR + 4 )
+#define PRELOC_OFFSET_COPY ( PRELOC_OFFSET_RETADDR_E )
+#define PRELOC_OFFSET_COPY_E ( PRELOC_OFFSET_COPY + 2 )
+
+#define PRELOC_ALWAYS_COPY ( PRELOC_OFFSET_COPY_E )
+
+ .code16
+ .globl prelocate
+prelocate:
+ /* Pad to allow for expansion of return address */
+ pushw %ax
+
+ /* Preserve registers */
+ pushaw
+ pushw %ds
+ pushw %es
+
+ /* Claim an area of base memory from the BIOS and put the
+ * payload there.
+ */
+ movw $0x40, %bx
+ movw %bx, %es
+ movw %es:(0x13), %bx /* FBMS in kb to %ax */
+ shlw $6, %bx /* ... in paragraphs */
+ subw $_image_size_pgh, %bx /* Subtract space for image */
+ shrw $6, %bx /* Round down to nearest kb */
+ movw %bx, %es:(0x13) /* ...and claim memory from BIOS */
+ shlw $6, %bx
+
+ /* At this point %bx contains the segment address for the
+ * start of the image (image = prefix + runtime).
+ */
+
+ /* Switch stacks */
+ movw %ss, %ax
+ movw %ax, %ds
+ movw %sp, %si /* %ds:si = current %ss:sp */
+ movw %ss:PRELOC_OFFSET_COPY(%si), %cx
+ testw %cx, %cx
+ js 1f
+ leaw _stack_offset_pgh(%bx), %ax /* %ax = new %ss */
+ movw %ax, %es
+ movw $_stack_size, %di
+ addw $PRELOC_ALWAYS_COPY, %cx
+ subw %cx, %di /* %es:di = new %ss:sp */
+ movw %ax, %ss /* Set new %ss:sp */
+ movw %di, %sp
+ cld
+ rep movsb /* Copy stack contents */
+1:
+
+ /* Do the image copy backwards, since if there's overlap with
+ * a forward copy then it means we're going to get trashed
+ * during the copy anyway...
+ */
+ pushal /* Preserve 32-bit registers */
+ movw %bx, %es /* Destination base for copy */
+ pushw %cs
+ popw %ds /* Source base for copy */
+ movl $_verbatim_size-1, %ecx /* Offset to last byte */
+ movl %ecx, %esi
+ movl %ecx, %edi
+ incl %ecx /* Length */
+ std /* Backwards copy of binary */
+ ADDR32 rep movsb
+ cld
+ popal /* Restore 32-bit registers */
+
+ /* Store (%bx<<4) as image_basemem to be picked up by
+ * basemem.c. Also store image_size, since there's no other
+ * way that we can later know how much memory we allocated.
+ * (_zfile_size is unavailable when rt2 is linked).
+ */
+ pushl %eax
+ xorl %eax, %eax
+ movw %bx, %ax
+ shll $4, %eax
+ movl %eax, %es:_prefix_image_basemem
+ movl $_image_size, %es:_prefix_image_basemem_size
+ popl %eax
+
+ /* Expand original near return address into far return to new
+ * code location.
+ */
+ movw %sp, %bp
+ xchgw %bx, (PRELOC_OFFSET_RETADDR+2)(%bp)
+ movw %bx, (PRELOC_OFFSET_RETADDR+0)(%bp)
+
+ /* Restore registers and return */
+ popw %es
+ popw %ds
+ popaw
+ lret /* Jump to relocated code */
+
+ /* Utility routine to free base memory allocated by prelocate.
+ * Ensure that said memory is not in use (e.g. for the CPU
+ * stack) before calling this routine.
+ */
+ .globl deprelocate
+deprelocate:
+ /* Claim an area of base memory from the BIOS and put the
+ * payload there.
+ */
+ pushw %ax
+ pushw %es
+ movw $0x40, %ax
+ movw %ax, %es
+ movw %es:(0x13), %ax /* FBMS in kb to %ax */
+ shlw $6, %ax /* ... in paragraphs */
+ addw $_image_size_pgh+0x40-1, %ax /* Add space for image and... */
+ shrw $6, %ax /* ...round up to nearest kb */
+ movw %ax, %es:(0x13) /* Give memory back to BIOS */
+ popw %es
+ popw %ax
+ ret
+
+#endif /* CODE16 */
diff --git a/src/arch/i386/core/multiboot_loader.c b/src/arch/i386/core/multiboot_loader.c
new file mode 100644
index 00000000..e9785f1c
--- /dev/null
+++ b/src/arch/i386/core/multiboot_loader.c
@@ -0,0 +1,143 @@
+/* Multiboot support
+ *
+ * 2003-07-02 mmap fix and header probe by SONE Takeshi
+ */
+
+struct multiboot_mods {
+ unsigned mod_start;
+ unsigned mod_end;
+ unsigned char *string;
+ unsigned reserved;
+};
+
+struct multiboot_mmap {
+ unsigned int size;
+ unsigned int base_addr_low;
+ unsigned int base_addr_high;
+ unsigned int length_low;
+ unsigned int length_high;
+ unsigned int type;
+};
+
+/* The structure of a Multiboot 0.6 parameter block. */
+struct multiboot_info {
+ unsigned int flags;
+#define MULTIBOOT_MEM_VALID 0x01
+#define MULTIBOOT_BOOT_DEV_VALID 0x02
+#define MULTIBOOT_CMDLINE_VALID 0x04
+#define MULTIBOOT_MODS_VALID 0x08
+#define MULTIBOOT_AOUT_SYMS_VALID 0x10
+#define MULTIBOOT_ELF_SYMS_VALID 0x20
+#define MULTIBOOT_MMAP_VALID 0x40
+ unsigned int memlower;
+ unsigned int memupper;
+ unsigned int bootdev;
+ unsigned int cmdline; /* physical address of the command line */
+ unsigned mods_count;
+ struct multiboot_mods *mods_addr;
+ unsigned syms_num;
+ unsigned syms_size;
+ unsigned syms_addr;
+ unsigned syms_shndx;
+ unsigned mmap_length;
+ unsigned mmap_addr;
+ /* The structure actually ends here, so I might as well put
+ * the ugly e820 parameters here...
+ */
+ struct multiboot_mmap mmap[E820MAX];
+};
+
+/* Multiboot image header (minimal part) */
+struct multiboot_header {
+ unsigned int magic;
+#define MULTIBOOT_HEADER_MAGIC 0x1BADB002
+ unsigned int flags;
+ unsigned int checksum;
+};
+
+static struct multiboot_header *mbheader;
+
+static struct multiboot_info mbinfo;
+
+static void multiboot_probe(unsigned char *data, int len)
+{
+ int offset;
+ struct multiboot_header *h;
+
+ /* Multiboot spec requires the header to be in first 8KB of the image */
+ if (len > 8192)
+ len = 8192;
+
+ for (offset = 0; offset < len; offset += 4) {
+ h = (struct multiboot_header *) (data + offset);
+ if (h->magic == MULTIBOOT_HEADER_MAGIC
+ && h->magic + h->flags + h->checksum == 0) {
+ printf("/Multiboot");
+ mbheader = h;
+ return;
+ }
+ }
+ mbheader = 0;
+}
+
+static inline void multiboot_boot(unsigned long entry)
+{
+ unsigned char cmdline[512], *c;
+ int i;
+ if (!mbheader)
+ return;
+ /* Etherboot limits the command line to the kernel name,
+ * default parameters and user prompted parameters. All of
+ * them are shorter than 256 bytes. As the kernel name and
+ * the default parameters come from the same BOOTP/DHCP entry
+ * (or if they don't, the parameters are empty), only two
+ * strings of the maximum size are possible. Note this buffer
+ * can overrun if a stupid file name is chosen. Oh well. */
+ c = cmdline;
+ for (i = 0; KERNEL_BUF[i] != 0; i++) {
+ switch (KERNEL_BUF[i]) {
+ case ' ':
+ case '\\':
+ case '"':
+ *c++ = '\\';
+ break;
+ default:
+ break;
+ }
+ *c++ = KERNEL_BUF[i];
+ }
+ (void)sprintf(c, " -retaddr %#lX", virt_to_phys(xend32));
+
+ mbinfo.flags = MULTIBOOT_MMAP_VALID | MULTIBOOT_MEM_VALID |MULTIBOOT_CMDLINE_VALID;
+ mbinfo.memlower = meminfo.basememsize;
+ mbinfo.memupper = meminfo.memsize;
+ mbinfo.bootdev = 0; /* not booted from disk */
+ mbinfo.cmdline = virt_to_phys(cmdline);
+ for (i = 0; i < (int) meminfo.map_count; i++) {
+ mbinfo.mmap[i].size = sizeof(struct multiboot_mmap)
+ - sizeof(unsigned int);
+ mbinfo.mmap[i].base_addr_low =
+ (unsigned int) meminfo.map[i].addr;
+ mbinfo.mmap[i].base_addr_high =
+ (unsigned int) (meminfo.map[i].addr >> 32);
+ mbinfo.mmap[i].length_low =
+ (unsigned int) meminfo.map[i].size;
+ mbinfo.mmap[i].length_high =
+ (unsigned int) (meminfo.map[i].size >> 32);
+ mbinfo.mmap[i].type = meminfo.map[i].type;
+ }
+ mbinfo.mmap_length = meminfo.map_count * sizeof(struct multiboot_mmap);
+ mbinfo.mmap_addr = virt_to_phys(mbinfo.mmap);
+
+ /* The Multiboot 0.6 spec requires all segment registers to be
+ * loaded with an unrestricted, writeable segment.
+ * xstart32 does this for us.
+ */
+
+ /* Start the kernel, passing the Multiboot information record
+ * and the magic number. */
+ os_regs.eax = 0x2BADB002;
+ os_regs.ebx = virt_to_phys(&mbinfo);
+ xstart32(entry);
+ longjmp(restart_etherboot, -2);
+}
diff --git a/src/arch/i386/core/pci_io.c b/src/arch/i386/core/pci_io.c
new file mode 100644
index 00000000..e89561e4
--- /dev/null
+++ b/src/arch/i386/core/pci_io.c
@@ -0,0 +1,352 @@
+/*
+** Support for NE2000 PCI clones added David Monro June 1997
+** Generalised to other NICs by Ken Yap July 1997
+**
+** Most of this is taken from:
+**
+** /usr/src/linux/drivers/pci/pci.c
+** /usr/src/linux/include/linux/pci.h
+** /usr/src/linux/arch/i386/bios32.c
+** /usr/src/linux/include/linux/bios32.h
+** /usr/src/linux/drivers/net/ne.c
+*/
+#ifdef CONFIG_PCI
+#include "etherboot.h"
+#include "pci.h"
+
+#ifdef CONFIG_PCI_DIRECT
+#define PCIBIOS_SUCCESSFUL 0x00
+
+#define DEBUG 0
+
+/*
+ * Functions for accessing PCI configuration space with type 1 accesses
+ */
+
+#define CONFIG_CMD(bus, device_fn, where) (0x80000000 | (bus << 16) | (device_fn << 8) | (where & ~3))
+
+int pcibios_read_config_byte(unsigned int bus, unsigned int device_fn,
+ unsigned int where, uint8_t *value)
+{
+ outl(CONFIG_CMD(bus,device_fn,where), 0xCF8);
+ *value = inb(0xCFC + (where&3));
+ return PCIBIOS_SUCCESSFUL;
+}
+
+int pcibios_read_config_word (unsigned int bus,
+ unsigned int device_fn, unsigned int where, uint16_t *value)
+{
+ outl(CONFIG_CMD(bus,device_fn,where), 0xCF8);
+ *value = inw(0xCFC + (where&2));
+ return PCIBIOS_SUCCESSFUL;
+}
+
+int pcibios_read_config_dword (unsigned int bus, unsigned int device_fn,
+ unsigned int where, uint32_t *value)
+{
+ outl(CONFIG_CMD(bus,device_fn,where), 0xCF8);
+ *value = inl(0xCFC);
+ return PCIBIOS_SUCCESSFUL;
+}
+
+int pcibios_write_config_byte (unsigned int bus, unsigned int device_fn,
+ unsigned int where, uint8_t value)
+{
+ outl(CONFIG_CMD(bus,device_fn,where), 0xCF8);
+ outb(value, 0xCFC + (where&3));
+ return PCIBIOS_SUCCESSFUL;
+}
+
+int pcibios_write_config_word (unsigned int bus, unsigned int device_fn,
+ unsigned int where, uint16_t value)
+{
+ outl(CONFIG_CMD(bus,device_fn,where), 0xCF8);
+ outw(value, 0xCFC + (where&2));
+ return PCIBIOS_SUCCESSFUL;
+}
+
+int pcibios_write_config_dword (unsigned int bus, unsigned int device_fn, unsigned int where, uint32_t value)
+{
+ outl(CONFIG_CMD(bus,device_fn,where), 0xCF8);
+ outl(value, 0xCFC);
+ return PCIBIOS_SUCCESSFUL;
+}
+
+#undef CONFIG_CMD
+
+#else /* CONFIG_PCI_DIRECT not defined */
+
+#if !defined(PCBIOS)
+#error "The pcibios can only be used when the PCBIOS support is compiled in"
+#endif
+
+/* Macro for calling the BIOS32 service. This replaces the old
+ * bios32_call function. Use in a statement such as
+ * __asm__ ( BIOS32_CALL,
+ * : <output registers>
+ * : "S" ( bios32_entry ), <other input registers> );
+ */
+#define BIOS32_CALL "call _virt_to_phys\n\t" \
+ "pushl %%cs\n\t" \
+ "call *%%esi\n\t" \
+ "cli\n\t" \
+ "cld\n\t" \
+ "call _phys_to_virt\n\t"
+
+static unsigned long bios32_entry;
+static unsigned long pcibios_entry;
+
+static unsigned long bios32_service(unsigned long service)
+{
+ unsigned char return_code; /* %al */
+ unsigned long address; /* %ebx */
+ unsigned long length; /* %ecx */
+ unsigned long entry; /* %edx */
+
+ __asm__(BIOS32_CALL
+ : "=a" (return_code),
+ "=b" (address),
+ "=c" (length),
+ "=d" (entry)
+ : "0" (service),
+ "1" (0),
+ "S" (bios32_entry));
+
+ switch (return_code) {
+ case 0:
+ return address + entry;
+ case 0x80: /* Not present */
+ printf("bios32_service(%d) : not present\n", service);
+ return 0;
+ default: /* Shouldn't happen */
+ printf("bios32_service(%d) : returned %#X????\n",
+ service, return_code);
+ return 0;
+ }
+}
+
+int pcibios_read_config_byte(unsigned int bus,
+ unsigned int device_fn, unsigned int where, uint8_t *value)
+{
+ unsigned long ret;
+ unsigned long bx = (bus << 8) | device_fn;
+
+ __asm__(BIOS32_CALL
+ "jc 1f\n\t"
+ "xor %%ah, %%ah\n"
+ "1:"
+ : "=c" (*value),
+ "=a" (ret)
+ : "1" (PCIBIOS_READ_CONFIG_BYTE),
+ "b" (bx),
+ "D" ((long) where),
+ "S" (pcibios_entry));
+ return (int) (ret & 0xff00) >> 8;
+}
+
+int pcibios_read_config_word(unsigned int bus,
+ unsigned int device_fn, unsigned int where, uint16_t *value)
+{
+ unsigned long ret;
+ unsigned long bx = (bus << 8) | device_fn;
+
+ __asm__(BIOS32_CALL
+ "jc 1f\n\t"
+ "xor %%ah, %%ah\n"
+ "1:"
+ : "=c" (*value),
+ "=a" (ret)
+ : "1" (PCIBIOS_READ_CONFIG_WORD),
+ "b" (bx),
+ "D" ((long) where),
+ "S" (pcibios_entry));
+ return (int) (ret & 0xff00) >> 8;
+}
+
+int pcibios_read_config_dword(unsigned int bus,
+ unsigned int device_fn, unsigned int where, uint32_t *value)
+{
+ unsigned long ret;
+ unsigned long bx = (bus << 8) | device_fn;
+
+ __asm__(BIOS32_CALL
+ "jc 1f\n\t"
+ "xor %%ah, %%ah\n"
+ "1:"
+ : "=c" (*value),
+ "=a" (ret)
+ : "1" (PCIBIOS_READ_CONFIG_DWORD),
+ "b" (bx),
+ "D" ((long) where),
+ "S" (pcibios_entry));
+ return (int) (ret & 0xff00) >> 8;
+}
+
+int pcibios_write_config_byte (unsigned int bus,
+ unsigned int device_fn, unsigned int where, uint8_t value)
+{
+ unsigned long ret;
+ unsigned long bx = (bus << 8) | device_fn;
+
+ __asm__(BIOS32_CALL
+ "jc 1f\n\t"
+ "xor %%ah, %%ah\n"
+ "1:"
+ : "=a" (ret)
+ : "0" (PCIBIOS_WRITE_CONFIG_BYTE),
+ "c" (value),
+ "b" (bx),
+ "D" ((long) where),
+ "S" (pcibios_entry));
+ return (int) (ret & 0xff00) >> 8;
+}
+
+int pcibios_write_config_word (unsigned int bus,
+ unsigned int device_fn, unsigned int where, uint16_t value)
+{
+ unsigned long ret;
+ unsigned long bx = (bus << 8) | device_fn;
+
+ __asm__(BIOS32_CALL
+ "jc 1f\n\t"
+ "xor %%ah, %%ah\n"
+ "1:"
+ : "=a" (ret)
+ : "0" (PCIBIOS_WRITE_CONFIG_WORD),
+ "c" (value),
+ "b" (bx),
+ "D" ((long) where),
+ "S" (pcibios_entry));
+ return (int) (ret & 0xff00) >> 8;
+}
+
+int pcibios_write_config_dword (unsigned int bus,
+ unsigned int device_fn, unsigned int where, uint32_t value)
+{
+ unsigned long ret;
+ unsigned long bx = (bus << 8) | device_fn;
+
+ __asm__(BIOS32_CALL
+ "jc 1f\n\t"
+ "xor %%ah, %%ah\n"
+ "1:"
+ : "=a" (ret)
+ : "0" (PCIBIOS_WRITE_CONFIG_DWORD),
+ "c" (value),
+ "b" (bx),
+ "D" ((long) where),
+ "S" (pcibios_entry));
+ return (int) (ret & 0xff00) >> 8;
+}
+
+static void check_pcibios(void)
+{
+ unsigned long signature;
+ unsigned char present_status;
+ unsigned char major_revision;
+ unsigned char minor_revision;
+ int pack;
+
+ if ((pcibios_entry = bios32_service(PCI_SERVICE))) {
+ __asm__(BIOS32_CALL
+ "jc 1f\n\t"
+ "xor %%ah, %%ah\n"
+ "1:\tshl $8, %%eax\n\t"
+ "movw %%bx, %%ax"
+ : "=d" (signature),
+ "=a" (pack)
+ : "1" (PCIBIOS_PCI_BIOS_PRESENT),
+ "S" (pcibios_entry)
+ : "bx", "cx");
+
+ present_status = (pack >> 16) & 0xff;
+ major_revision = (pack >> 8) & 0xff;
+ minor_revision = pack & 0xff;
+ if (present_status || (signature != PCI_SIGNATURE)) {
+ printf("ERROR: BIOS32 says PCI BIOS, but no PCI "
+ "BIOS????\n");
+ pcibios_entry = 0;
+ }
+#if DEBUG
+ if (pcibios_entry) {
+ printf ("pcibios_init : PCI BIOS revision %hhX.%hhX"
+ " entry at %#X\n", major_revision,
+ minor_revision, pcibios_entry);
+ }
+#endif
+ }
+}
+
+static void pcibios_init(void)
+{
+ union bios32 *check;
+ unsigned char sum;
+ int i, length;
+ bios32_entry = 0;
+
+ /*
+ * Follow the standard procedure for locating the BIOS32 Service
+ * directory by scanning the permissible address range from
+ * 0xe0000 through 0xfffff for a valid BIOS32 structure.
+ *
+ */
+
+ for (check = phys_to_virt(0xe0000); (void *)check <= phys_to_virt(0xffff0); ++check) {
+ if (check->fields.signature != BIOS32_SIGNATURE)
+ continue;
+ length = check->fields.length * 16;
+ if (!length)
+ continue;
+ sum = 0;
+ for (i = 0; i < length ; ++i)
+ sum += check->chars[i];
+ if (sum != 0)
+ continue;
+ if (check->fields.revision != 0) {
+ printf("pcibios_init : unsupported revision %d at %#X, mail drew@colorado.edu\n",
+ check->fields.revision, check);
+ continue;
+ }
+#if DEBUG
+ printf("pcibios_init : BIOS32 Service Directory "
+ "structure at %#X\n", check);
+#endif
+ if (!bios32_entry) {
+ if (check->fields.entry >= 0x100000) {
+ printf("pcibios_init: entry in high "
+ "memory, giving up\n");
+ return;
+ } else {
+ bios32_entry = check->fields.entry;
+#if DEBUG
+ printf("pcibios_init : BIOS32 Service Directory"
+ " entry at %#X\n", bios32_entry);
+#endif
+ }
+ }
+ }
+ if (bios32_entry)
+ check_pcibios();
+}
+#endif /* CONFIG_PCI_DIRECT not defined*/
+
+unsigned long pcibios_bus_base(unsigned int bus __unused)
+{
+ /* architecturally this must be 0 */
+ return 0;
+}
+
+void find_pci(int type, struct pci_device *dev)
+{
+#ifndef CONFIG_PCI_DIRECT
+ if (!pcibios_entry) {
+ pcibios_init();
+ }
+ if (!pcibios_entry) {
+ printf("pci_init: no BIOS32 detected\n");
+ return;
+ }
+#endif
+ return scan_pci_bus(type, dev);
+}
+#endif /* CONFIG_PCI */
diff --git a/src/arch/i386/core/pic8259.c b/src/arch/i386/core/pic8259.c
new file mode 100644
index 00000000..cc2071fc
--- /dev/null
+++ b/src/arch/i386/core/pic8259.c
@@ -0,0 +1,331 @@
+/*
+ * Basic support for controlling the 8259 Programmable Interrupt Controllers.
+ *
+ * Initially written by Michael Brown (mcb30).
+ */
+
+#include <etherboot.h>
+#include "pic8259.h"
+#include "realmode.h"
+
+#ifdef DEBUG_IRQ
+#define DBG(...) printf ( __VA_ARGS__ )
+#else
+#define DBG(...)
+#endif
+
+/* State of trivial IRQ handler */
+irq_t trivial_irq_installed_on = IRQ_NONE;
+static uint16_t trivial_irq_previous_trigger_count = 0;
+
+/* The actual trivial IRQ handler
+ *
+ * Note: we depend on the C compiler not realising that we're putting
+ * variables in the ".text16" section and therefore not forcing them
+ * back to the ".data" section. I don't see any reason to expect this
+ * behaviour to change.
+ *
+ * These must *not* be the first variables to appear in this file; the
+ * first variable to appear gets the ".data" directive.
+ */
+RM_FRAGMENT(_trivial_irq_handler,
+ "pushw %bx\n\t"
+ "call 1f\n1:\tpopw %bx\n\t" /* PIC access to variables */
+ "incw %cs:(_trivial_irq_trigger_count-1b)(%bx)\n\t"
+ "popw %bx\n\t"
+ "iret\n\t"
+ "\n\t"
+ ".globl _trivial_irq_trigger_count\n\t"
+ "_trivial_irq_trigger_count: .short 0\n\t"
+ "\n\t"
+ ".globl _trivial_irq_chain_to\n\t"
+ "_trivial_irq_chain_to: .short 0,0\n\t"
+ "\n\t"
+ ".globl _trivial_irq_chain\n\t"
+ "_trivial_irq_chain: .byte 0\n\t"
+ );
+extern volatile uint16_t _trivial_irq_trigger_count;
+extern segoff_t _trivial_irq_chain_to;
+extern int8_t _trivial_irq_chain;
+
+/* Current locations of trivial IRQ handler. These will change at
+ * runtime when relocation is used; the handler needs to be copied to
+ * base memory before being installed.
+ */
+void (*trivial_irq_handler)P((void)) = _trivial_irq_handler;
+uint16_t volatile *trivial_irq_trigger_count = &_trivial_irq_trigger_count;
+segoff_t *trivial_irq_chain_to = &_trivial_irq_chain_to;
+uint8_t *trivial_irq_chain = &_trivial_irq_chain;
+
+/* Install a handler for the specified IRQ. Address of previous
+ * handler will be stored in previous_handler. Enabled/disabled state
+ * of IRQ will be preserved across call, therefore if the handler does
+ * chaining, ensure that either (a) IRQ is disabled before call, or
+ * (b) previous_handler points directly to the place that the handler
+ * picks up its chain-to address.
+ */
+
+int install_irq_handler ( irq_t irq, segoff_t *handler,
+ uint8_t *previously_enabled,
+ segoff_t *previous_handler ) {
+ segoff_t *irq_vector = IRQ_VECTOR ( irq );
+ *previously_enabled = irq_enabled ( irq );
+
+ if ( irq > IRQ_MAX ) {
+ DBG ( "Invalid IRQ number %d\n" );
+ return 0;
+ }
+
+ previous_handler->segment = irq_vector->segment;
+ previous_handler->offset = irq_vector->offset;
+ if ( *previously_enabled ) disable_irq ( irq );
+ DBG ( "Installing handler at %hx:%hx for IRQ %d (vector 0000:%hx),"
+ " leaving %s\n",
+ handler->segment, handler->offset, irq, virt_to_phys(irq_vector),
+ ( *previously_enabled ? "enabled" : "disabled" ) );
+ DBG ( "...(previous handler at %hx:%hx)\n",
+ previous_handler->segment, previous_handler->offset );
+ irq_vector->segment = handler->segment;
+ irq_vector->offset = handler->offset;
+ if ( *previously_enabled ) enable_irq ( irq );
+ return 1;
+}
+
+/* Remove handler for the specified IRQ. Routine checks that another
+ * handler has not been installed that chains to handler before
+ * uninstalling handler. Enabled/disabled state of the IRQ will be
+ * restored to that specified by previously_enabled.
+ */
+
+int remove_irq_handler ( irq_t irq, segoff_t *handler,
+ uint8_t *previously_enabled,
+ segoff_t *previous_handler ) {
+ segoff_t *irq_vector = IRQ_VECTOR ( irq );
+
+ if ( irq > IRQ_MAX ) {
+ DBG ( "Invalid IRQ number %d\n" );
+ return 0;
+ }
+ if ( ( irq_vector->segment != handler->segment ) ||
+ ( irq_vector->offset != handler->offset ) ) {
+ DBG ( "Cannot remove handler for IRQ %d\n" );
+ return 0;
+ }
+
+ DBG ( "Removing handler for IRQ %d\n", irq );
+ disable_irq ( irq );
+ irq_vector->segment = previous_handler->segment;
+ irq_vector->offset = previous_handler->offset;
+ if ( *previously_enabled ) enable_irq ( irq );
+ return 1;
+}
+
+/* Install the trivial IRQ handler. This routine installs the
+ * handler, tests it and enables the IRQ.
+ */
+
+int install_trivial_irq_handler ( irq_t irq ) {
+ segoff_t trivial_irq_handler_segoff = SEGOFF(trivial_irq_handler);
+
+ if ( trivial_irq_installed_on != IRQ_NONE ) {
+ DBG ( "Can install trivial IRQ handler only once\n" );
+ return 0;
+ }
+ if ( SEGMENT(trivial_irq_handler) > 0xffff ) {
+ DBG ( "Trivial IRQ handler not in base memory\n" );
+ return 0;
+ }
+
+ DBG ( "Installing trivial IRQ handler on IRQ %d\n", irq );
+ if ( ! install_irq_handler ( irq, &trivial_irq_handler_segoff,
+ trivial_irq_chain,
+ trivial_irq_chain_to ) )
+ return 0;
+ trivial_irq_installed_on = irq;
+
+ DBG ( "Testing trivial IRQ handler\n" );
+ disable_irq ( irq );
+ *trivial_irq_trigger_count = 0;
+ trivial_irq_previous_trigger_count = 0;
+ fake_irq ( irq );
+ if ( ! trivial_irq_triggered ( irq ) ) {
+ DBG ( "Installation of trivial IRQ handler failed\n" );
+ remove_trivial_irq_handler ( irq );
+ return 0;
+ }
+ /* Send EOI just in case there was a leftover interrupt */
+ send_specific_eoi ( irq );
+ DBG ( "Trivial IRQ handler installed successfully\n" );
+ enable_irq ( irq );
+ return 1;
+}
+
+/* Remove the trivial IRQ handler.
+ */
+
+int remove_trivial_irq_handler ( irq_t irq ) {
+ segoff_t trivial_irq_handler_segoff = SEGOFF(trivial_irq_handler);
+
+ if ( trivial_irq_installed_on == IRQ_NONE ) return 1;
+ if ( irq != trivial_irq_installed_on ) {
+ DBG ( "Cannot uninstall trivial IRQ handler from IRQ %d; "
+ "is installed on IRQ %d\n", irq,
+ trivial_irq_installed_on );
+ return 0;
+ }
+
+ if ( ! remove_irq_handler ( irq, &trivial_irq_handler_segoff,
+ trivial_irq_chain,
+ trivial_irq_chain_to ) )
+ return 0;
+
+ if ( trivial_irq_triggered ( trivial_irq_installed_on ) ) {
+ DBG ( "Sending EOI for unwanted trivial IRQ\n" );
+ send_specific_eoi ( trivial_irq_installed_on );
+ }
+
+ trivial_irq_installed_on = IRQ_NONE;
+ return 1;
+}
+
+/* Safe method to detect whether or not trivial IRQ has been
+ * triggered. Using this call avoids potential race conditions. This
+ * call will return success only once per trigger.
+ */
+
+int trivial_irq_triggered ( irq_t irq ) {
+ uint16_t trivial_irq_this_trigger_count = *trivial_irq_trigger_count;
+ int triggered = ( trivial_irq_this_trigger_count -
+ trivial_irq_previous_trigger_count );
+
+ /* irq is not used at present, but we have it in the API for
+ * future-proofing; in case we want the facility to have
+ * multiple trivial IRQ handlers installed simultaneously.
+ *
+ * Avoid compiler warning about unused variable.
+ */
+ if ( irq == IRQ_NONE ) {};
+
+ trivial_irq_previous_trigger_count = trivial_irq_this_trigger_count;
+ return triggered ? 1 : 0;
+}
+
+/* Copy trivial IRQ handler to a new location. Typically used to copy
+ * the handler into base memory; when relocation is being used we need
+ * to do this before installing the handler.
+ *
+ * Call with target=NULL in order to restore the handler to its
+ * original location.
+ */
+
+int copy_trivial_irq_handler ( void *target, size_t target_size ) {
+ irq_t currently_installed_on = trivial_irq_installed_on;
+ uint32_t offset = ( target == NULL ? 0 :
+ target - (void*)_trivial_irq_handler );
+
+ if (( target != NULL ) && ( target_size < TRIVIAL_IRQ_HANDLER_SIZE )) {
+ DBG ( "Insufficient space to copy trivial IRQ handler\n" );
+ return 0;
+ }
+
+ if ( currently_installed_on != IRQ_NONE ) {
+ DBG ("WARNING: relocating trivial IRQ handler while in use\n");
+ if ( ! remove_trivial_irq_handler ( currently_installed_on ) )
+ return 0;
+ }
+
+ /* Do the actual copy */
+ if ( target != NULL ) {
+ DBG ( "Copying trivial IRQ handler to %hx:%hx\n",
+ SEGMENT(target), OFFSET(target) );
+ memcpy ( target, _trivial_irq_handler,
+ TRIVIAL_IRQ_HANDLER_SIZE );
+ } else {
+ DBG ( "Restoring trivial IRQ handler to original location\n" );
+ }
+ /* Update all the pointers to structures within the handler */
+ trivial_irq_handler = ( void (*)P((void)) )
+ ( (void*)_trivial_irq_handler + offset );
+ trivial_irq_trigger_count = (uint16_t*)
+ ( (void*)&_trivial_irq_trigger_count + offset );
+ trivial_irq_chain_to = (segoff_t*)
+ ( (void*)&_trivial_irq_chain_to + offset );
+ trivial_irq_chain = (uint8_t*)
+ ( (void*)&_trivial_irq_chain + offset );
+
+ if ( currently_installed_on != IRQ_NONE ) {
+ if ( ! install_trivial_irq_handler ( currently_installed_on ) )
+ return 0;
+ }
+ return 1;
+}
+
+/* Send non-specific EOI(s). This seems to be inherently unsafe.
+ */
+
+void send_nonspecific_eoi ( irq_t irq ) {
+ DBG ( "Sending non-specific EOI for IRQ %d\n", irq );
+ if ( irq >= IRQ_PIC_CUTOFF ) {
+ outb ( ICR_EOI_NON_SPECIFIC, PIC2_ICR );
+ }
+ outb ( ICR_EOI_NON_SPECIFIC, PIC1_ICR );
+}
+
+/* Send specific EOI(s).
+ */
+
+void send_specific_eoi ( irq_t irq ) {
+ DBG ( "Sending specific EOI for IRQ %d\n", irq );
+ outb ( ICR_EOI_SPECIFIC | ICR_VALUE(irq), ICR_REG(irq) );
+ if ( irq >= IRQ_PIC_CUTOFF ) {
+ outb ( ICR_EOI_SPECIFIC | ICR_VALUE(CHAINED_IRQ),
+ ICR_REG(CHAINED_IRQ) );
+ }
+}
+
+/* Fake an IRQ
+ */
+
+void fake_irq ( irq_t irq ) {
+ struct {
+ uint16_t int_number;
+ } PACKED in_stack;
+
+ /* Convert IRQ to INT number:
+ *
+ * subb $0x08,%cl Invert bit 3, set bits 4-7 iff irq < 8
+ * xorb $0x70,%cl Invert bits 4-6
+ * andb $0x7f,%cl Clear bit 7
+ *
+ * No, it's not the most intuitive method, but I was proud to
+ * get it down to three lines of assembler when this routine
+ * was originally implemented in pcbios.S.
+ */
+ in_stack.int_number = ( ( irq - 8 ) ^ 0x70 ) & 0x7f;
+
+ RM_FRAGMENT(rm_fake_irq,
+ "popw %ax\n\t" /* %ax = INT number */
+ "call 1f\n1:\tpop %bx\n\t"
+ "movb %al, %cs:(2f-1b+1)(%bx)\n\t" /* Overwrite INT number..*/
+ "\n2:\tint $0x00\n\t" /* ..in this instruction */
+ );
+
+ real_call ( rm_fake_irq, &in_stack, NULL );
+}
+
+/* Dump current 8259 status: enabled IRQs and handler addresses.
+ */
+
+#ifdef DEBUG_IRQ
+void dump_irq_status ( void ) {
+ int irq = 0;
+
+ for ( irq = 0; irq < 16; irq++ ) {
+ if ( irq_enabled ( irq ) ) {
+ printf ( "IRQ%d enabled, ISR at %hx:%hx\n", irq,
+ IRQ_VECTOR(irq)->segment,
+ IRQ_VECTOR(irq)->offset );
+ }
+ }
+}
+#endif
diff --git a/src/arch/i386/core/prefixudata.lds b/src/arch/i386/core/prefixudata.lds
new file mode 100644
index 00000000..1c76128e
--- /dev/null
+++ b/src/arch/i386/core/prefixudata.lds
@@ -0,0 +1,8 @@
+OUTPUT_FORMAT("elf32-i386", "elf32-i386", "elf32-i386")
+OUTPUT_ARCH(i386)
+
+SECTIONS {
+ .prefix.udata : {
+ *(*)
+ }
+}
diff --git a/src/arch/i386/core/prefixzdata.lds b/src/arch/i386/core/prefixzdata.lds
new file mode 100644
index 00000000..bf6ea977
--- /dev/null
+++ b/src/arch/i386/core/prefixzdata.lds
@@ -0,0 +1,8 @@
+OUTPUT_FORMAT("elf32-i386", "elf32-i386", "elf32-i386")
+OUTPUT_ARCH(i386)
+
+SECTIONS {
+ .prefix.zdata : {
+ *(*)
+ }
+}
diff --git a/src/arch/i386/core/pxe_callbacks.c b/src/arch/i386/core/pxe_callbacks.c
new file mode 100644
index 00000000..344d34fe
--- /dev/null
+++ b/src/arch/i386/core/pxe_callbacks.c
@@ -0,0 +1,364 @@
+/* PXE callback mechanisms. This file contains only the portions
+ * specific to i386: i.e. the low-level mechanisms for calling in from
+ * an NBP to the PXE stack and for starting an NBP from the PXE stack.
+ */
+
+#ifdef PXE_EXPORT
+
+#include "etherboot.h"
+#include "callbacks.h"
+#include "realmode.h"
+#include "pxe.h"
+#include "pxe_callbacks.h"
+#include "pxe_export.h"
+#include "hidemem.h"
+#include <stdarg.h>
+
+#define INSTALLED(x) ( (typeof(&x)) ( (void*)(&x) \
+ - &pxe_callback_interface \
+ + (void*)&pxe_stack->arch_data ) )
+#define pxe_intercept_int1a INSTALLED(_pxe_intercept_int1a)
+#define pxe_intercepted_int1a INSTALLED(_pxe_intercepted_int1a)
+#define pxe_pxenv_location INSTALLED(_pxe_pxenv_location)
+#define INT1A_VECTOR ( (segoff_t*) ( phys_to_virt( 4 * 0x1a ) ) )
+
+/* The overall size of the PXE stack is ( sizeof(pxe_stack_t) +
+ * pxe_callback_interface_size + rm_callback_interface_size ).
+ * Unfortunately, this isn't a compile-time constant, since
+ * {pxe,rm}_callback_interface_size depend on the length of the
+ * assembly code in these interfaces.
+ *
+ * We used to have a function pxe_stack_size() which returned this
+ * value. However, it actually needs to be a link-time constant, so
+ * that it can appear in the UNDIROMID structure in romprefix.S. We
+ * therefore export the three component sizes as absolute linker
+ * symbols, get the linker to add them together and generate a new
+ * absolute symbol _pxe_stack_size. We then import this value into a
+ * C variable pxe_stack_size, for access from C code.
+ */
+
+/* gcc won't let us use extended asm outside a function (compiler
+ * bug), ao we have to put these asm statements inside a dummy
+ * function.
+ */
+static void work_around_gcc_bug ( void ) __attribute__ ((used));
+static void work_around_gcc_bug ( void ) {
+ /* Export sizeof(pxe_stack_t) as absolute linker symbol */
+ __asm__ ( ".globl _pxe_stack_t_size" );
+ __asm__ ( ".equ _pxe_stack_t_size, %c0"
+ : : "i" (sizeof(pxe_stack_t)) );
+}
+/* Import _pxe_stack_size absolute linker symbol into C variable */
+extern int pxe_stack_size;
+__asm__ ( "pxe_stack_size: .long _pxe_stack_size" );
+
+/* Utility routine: byte checksum
+ */
+uint8_t byte_checksum ( void *address, size_t size ) {
+ unsigned int i, sum = 0;
+
+ for ( i = 0; i < size; i++ ) {
+ sum += ((uint8_t*)address)[i];
+ }
+ return (uint8_t)sum;
+}
+
+/* install_pxe_stack(): install PXE stack.
+ *
+ * Use base = NULL for auto-allocation of base memory
+ *
+ * IMPORTANT: no further allocation of base memory should take place
+ * before the PXE stack is removed. This is to work around a small
+ * but important deficiency in the PXE specification.
+ */
+pxe_stack_t * install_pxe_stack ( void *base ) {
+ pxe_t *pxe;
+ pxenv_t *pxenv;
+ void *pxe_callback_code;
+ void (*pxe_in_call_far)(void);
+ void (*pxenv_in_call_far)(void);
+ void *rm_callback_code;
+ void *e820mangler_code;
+ void *end;
+
+ /* If already installed, just return */
+ if ( pxe_stack != NULL ) return pxe_stack;
+
+ /* Allocate base memory if requested to do so
+ */
+ if ( base == NULL ) {
+ base = allot_base_memory ( pxe_stack_size );
+ if ( base == NULL ) return NULL;
+ }
+
+ /* Round address up to 16-byte physical alignment */
+ pxe_stack = (pxe_stack_t *)
+ ( phys_to_virt ( ( virt_to_phys(base) + 0xf ) & ~0xf ) );
+ /* Zero out allocated stack */
+ memset ( pxe_stack, 0, sizeof(*pxe_stack) );
+
+ /* Calculate addresses for portions of the stack */
+ pxe = &(pxe_stack->pxe);
+ pxenv = &(pxe_stack->pxenv);
+ pxe_callback_code = &(pxe_stack->arch_data);
+ pxe_in_call_far = _pxe_in_call_far +
+ ( pxe_callback_code - &pxe_callback_interface );
+ pxenv_in_call_far = _pxenv_in_call_far +
+ ( pxe_callback_code - &pxe_callback_interface );
+ rm_callback_code = pxe_callback_code + pxe_callback_interface_size;
+
+ e820mangler_code = (void*)(((int)rm_callback_code +
+ rm_callback_interface_size + 0xf ) & ~0xf);
+ end = e820mangler_code + e820mangler_size;
+
+ /* Initialise !PXE data structures */
+ memcpy ( pxe->Signature, "!PXE", 4 );
+ pxe->StructLength = sizeof(*pxe);
+ pxe->StructRev = 0;
+ pxe->reserved_1 = 0;
+ /* We don't yet have an UNDI ROM ID structure */
+ pxe->UNDIROMID.segment = 0;
+ pxe->UNDIROMID.offset = 0;
+ /* or a BC ROM ID structure */
+ pxe->BaseROMID.segment = 0;
+ pxe->BaseROMID.offset = 0;
+ pxe->EntryPointSP.segment = SEGMENT(pxe_stack);
+ pxe->EntryPointSP.offset = (void*)pxe_in_call_far - (void*)pxe_stack;
+ /* No %esp-compatible entry point yet */
+ pxe->EntryPointESP.segment = 0;
+ pxe->EntryPointESP.offset = 0;
+ pxe->StatusCallout.segment = -1;
+ pxe->StatusCallout.offset = -1;
+ pxe->reserved_2 = 0;
+ pxe->SegDescCn = 7;
+ pxe->FirstSelector = 0;
+ /* PXE specification doesn't say anything about when the stack
+ * space should get freed. We work around this by claiming it
+ * as our data segment as well.
+ */
+ pxe->Stack.Seg_Addr = pxe->UNDIData.Seg_Addr = real_mode_stack >> 4;
+ pxe->Stack.Phy_Addr = pxe->UNDIData.Phy_Addr = real_mode_stack;
+ pxe->Stack.Seg_Size = pxe->UNDIData.Seg_Size = real_mode_stack_size;
+ /* Code segment has to be the one containing the data structures... */
+ pxe->UNDICode.Seg_Addr = SEGMENT(pxe_stack);
+ pxe->UNDICode.Phy_Addr = virt_to_phys(pxe_stack);
+ pxe->UNDICode.Seg_Size = end - (void*)pxe_stack;
+ /* No base code loaded */
+ pxe->BC_Data.Seg_Addr = 0;
+ pxe->BC_Data.Phy_Addr = 0;
+ pxe->BC_Data.Seg_Size = 0;
+ pxe->BC_Code.Seg_Addr = 0;
+ pxe->BC_Code.Phy_Addr = 0;
+ pxe->BC_Code.Seg_Size = 0;
+ pxe->BC_CodeWrite.Seg_Addr = 0;
+ pxe->BC_CodeWrite.Phy_Addr = 0;
+ pxe->BC_CodeWrite.Seg_Size = 0;
+ pxe->StructCksum -= byte_checksum ( pxe, sizeof(*pxe) );
+
+ /* Initialise PXENV+ data structures */
+ memcpy ( pxenv->Signature, "PXENV+", 6 );
+ pxenv->Version = 0x201;
+ pxenv->Length = sizeof(*pxenv);
+ pxenv->RMEntry.segment = SEGMENT(pxe_stack);
+ pxenv->RMEntry.offset = (void*)pxenv_in_call_far - (void*)pxe_stack;
+ pxenv->PMOffset = 0; /* "Do not use" says the PXE spec */
+ pxenv->PMSelector = 0; /* "Do not use" says the PXE spec */
+ pxenv->StackSeg = pxenv->UNDIDataSeg = real_mode_stack >> 4;
+ pxenv->StackSize = pxenv->UNDIDataSize = real_mode_stack_size;
+ pxenv->BC_CodeSeg = 0;
+ pxenv->BC_CodeSize = 0;
+ pxenv->BC_DataSeg = 0;
+ pxenv->BC_DataSize = 0;
+ /* UNDIData{Seg,Size} set above */
+ pxenv->UNDICodeSeg = SEGMENT(pxe_stack);
+ pxenv->UNDICodeSize = end - (void*)pxe_stack;
+ pxenv->PXEPtr.segment = SEGMENT(pxe);
+ pxenv->PXEPtr.offset = OFFSET(pxe);
+ pxenv->Checksum -= byte_checksum ( pxenv, sizeof(*pxenv) );
+
+ /* Mark stack as inactive */
+ pxe_stack->state = CAN_UNLOAD;
+
+ /* Install PXE and RM callback code and E820 mangler */
+ memcpy ( pxe_callback_code, &pxe_callback_interface,
+ pxe_callback_interface_size );
+ install_rm_callback_interface ( rm_callback_code, 0 );
+ install_e820mangler ( e820mangler_code );
+
+ return pxe_stack;
+}
+
+/* Use the UNDI data segment as our real-mode stack. This is for when
+ * we have been loaded via the UNDI loader
+ */
+void use_undi_ds_for_rm_stack ( uint16_t ds ) {
+ forget_real_mode_stack();
+ real_mode_stack = virt_to_phys ( VIRTUAL ( ds, 0 ) );
+ lock_real_mode_stack = 1;
+}
+
+/* Activate PXE stack (i.e. hook interrupt vectors). The PXE stack
+ * *can* be used before it is activated, but it really shoudln't.
+ */
+int hook_pxe_stack ( void ) {
+ if ( pxe_stack == NULL ) return 0;
+ if ( pxe_stack->state >= MIDWAY ) return 1;
+
+ /* Hook INT15 handler */
+ hide_etherboot();
+
+ /* Hook INT1A handler */
+ *pxe_intercepted_int1a = *INT1A_VECTOR;
+ pxe_pxenv_location->segment = SEGMENT(pxe_stack);
+ pxe_pxenv_location->offset = (void*)&pxe_stack->pxenv
+ - (void*)pxe_stack;
+ INT1A_VECTOR->segment = SEGMENT(&pxe_stack->arch_data);
+ INT1A_VECTOR->offset = (void*)pxe_intercept_int1a
+ - (void*)&pxe_stack->arch_data;
+
+ /* Mark stack as active */
+ pxe_stack->state = MIDWAY;
+ return 1;
+}
+
+/* Deactivate the PXE stack (i.e. unhook interrupt vectors).
+ */
+int unhook_pxe_stack ( void ) {
+ if ( pxe_stack == NULL ) return 0;
+ if ( pxe_stack->state <= CAN_UNLOAD ) return 1;
+
+ /* Restore original INT15 and INT1A handlers */
+ *INT1A_VECTOR = *pxe_intercepted_int1a;
+ if ( !unhide_etherboot() ) {
+ /* Cannot unhook INT15. We're up the creek without
+ * even a suitable log out of which to fashion a
+ * paddle. There are some very badly behaved NBPs
+ * that will ignore plaintive pleas such as
+ * PXENV_KEEP_UNDI and just zero out our code anyway.
+ * This means they end up vapourising an active INT15
+ * handler, which is generally not a good thing to do.
+ */
+ return 0;
+ }
+
+ /* Mark stack as inactive */
+ pxe_stack->state = CAN_UNLOAD;
+ return 1;
+}
+
+/* remove_pxe_stack(): remove PXE stack installed by install_pxe_stack()
+ */
+void remove_pxe_stack ( void ) {
+ /* Ensure stack is deactivated, then free up the memory */
+ if ( ensure_pxe_state ( CAN_UNLOAD ) ) {
+ forget_base_memory ( pxe_stack, pxe_stack_size );
+ pxe_stack = NULL;
+ } else {
+ printf ( "Cannot remove PXE stack!\n" );
+ }
+}
+
+/* xstartpxe(): start up a PXE image
+ */
+int xstartpxe ( void ) {
+ int nbp_exit;
+ struct {
+ reg16_t bx;
+ reg16_t es;
+ segoff_t pxe;
+ } PACKED in_stack;
+
+ /* Set up registers and stack parameters to pass to PXE NBP */
+ in_stack.es.word = SEGMENT(&(pxe_stack->pxenv));
+ in_stack.bx.word = OFFSET(&(pxe_stack->pxenv));
+ in_stack.pxe.segment = SEGMENT(&(pxe_stack->pxe));
+ in_stack.pxe.offset = OFFSET(&(pxe_stack->pxe));
+
+ /* Real-mode trampoline fragment used to jump to PXE NBP
+ */
+ RM_FRAGMENT(jump_to_pxe_nbp,
+ "popw %bx\n\t"
+ "popw %es\n\t"
+ "lcall $" RM_STR(PXE_LOAD_SEGMENT) ", $" RM_STR(PXE_LOAD_OFFSET) "\n\t"
+ );
+
+ /* Call to PXE image */
+ gateA20_unset();
+ nbp_exit = real_call ( jump_to_pxe_nbp, &in_stack, NULL );
+ gateA20_set();
+
+ return nbp_exit;
+}
+
+int pxe_in_call ( in_call_data_t *in_call_data, va_list params ) {
+ /* i386 calling conventions; the only two defined by Intel's
+ * PXE spec.
+ *
+ * Assembly code must pass a long containing the PXE version
+ * code (i.e. 0x201 for !PXE, 0x200 for PXENV+) as the first
+ * parameter after the in_call opcode. This is used to decide
+ * whether to take parameters from the stack (!PXE) or from
+ * registers (PXENV+).
+ */
+ uint32_t api_version = va_arg ( params, typeof(api_version) );
+ uint16_t opcode;
+ segoff_t segoff;
+ t_PXENV_ANY *structure;
+
+ if ( api_version >= 0x201 ) {
+ /* !PXE calling convention */
+ pxe_call_params_t pxe_params
+ = va_arg ( params, typeof(pxe_params) );
+ opcode = pxe_params.opcode;
+ segoff = pxe_params.segoff;
+ } else {
+ /* PXENV+ calling convention */
+ opcode = in_call_data->pm->regs.bx;
+ segoff.segment = in_call_data->rm->seg_regs.es;
+ segoff.offset = in_call_data->pm->regs.di;
+ }
+ structure = VIRTUAL ( segoff.segment, segoff.offset );
+ return pxe_api_call ( opcode, structure );
+}
+
+#ifdef TEST_EXCLUDE_ALGORITHM
+/* This code retained because it's a difficult algorithm to tweak with
+ * confidence
+ */
+int ___test_exclude ( int start, int len, int estart, int elen, int fixbase );
+void __test_exclude ( int start, int len, int estart, int elen, int fixbase ) {
+ int newrange = ___test_exclude ( start, len, estart, elen, fixbase );
+ int newstart = ( newrange >> 16 ) & 0xffff;
+ int newlen = ( newrange & 0xffff );
+
+ printf ( "[%x,%x): excluding [%x,%x) %s gives [%x,%x)\n",
+ start, start + len,
+ estart, estart + elen,
+ ( fixbase == 0 ) ? " " : "fb",
+ newstart, newstart + newlen );
+}
+void _test_exclude ( int start, int len, int estart, int elen ) {
+ __test_exclude ( start, len, estart, elen, 0 );
+ __test_exclude ( start, len, estart, elen, 1 );
+}
+void test_exclude ( void ) {
+ _test_exclude ( 0x8000, 0x1000, 0x0400, 0x200 ); /* before */
+ _test_exclude ( 0x8000, 0x1000, 0x9000, 0x200 ); /* after */
+ _test_exclude ( 0x8000, 0x1000, 0x7f00, 0x200 ); /* before overlap */
+ _test_exclude ( 0x8000, 0x1000, 0x8f00, 0x200 ); /* after overlap */
+ _test_exclude ( 0x8000, 0x1000, 0x8000, 0x200 ); /* align start */
+ _test_exclude ( 0x8000, 0x1000, 0x8e00, 0x200 ); /* align end */
+ _test_exclude ( 0x8000, 0x1000, 0x8100, 0x200 ); /* early overlap */
+ _test_exclude ( 0x8000, 0x1000, 0x8d00, 0x200 ); /* late overlap */
+ _test_exclude ( 0x8000, 0x1000, 0x7000, 0x3000 ); /* total overlap */
+ _test_exclude ( 0x8000, 0x1000, 0x8000, 0x1000 ); /* exact overlap */
+}
+#endif /* TEST_EXCLUDE_ALGORITHM */
+
+#else /* PXE_EXPORT */
+
+/* Define symbols used by the linker scripts, to prevent link errors */
+__asm__ ( ".globl _pxe_stack_t_size" );
+__asm__ ( ".equ _pxe_stack_t_size, 0" );
+
+#endif /* PXE_EXPORT */
diff --git a/src/arch/i386/core/pxe_loader.c b/src/arch/i386/core/pxe_loader.c
new file mode 100644
index 00000000..1b611891
--- /dev/null
+++ b/src/arch/i386/core/pxe_loader.c
@@ -0,0 +1,94 @@
+/*
+ * PXE image loader for Etherboot.
+ *
+ * Note: There is no signature check for PXE images because there is
+ * no signature. Well done, Intel! Consequently, pxe_probe() must be
+ * called last of all the image_probe() routines, because it will
+ * *always* claim the image.
+ */
+
+#ifndef PXE_EXPORT
+#error PXE_IMAGE requires PXE_EXPORT
+#endif
+
+#include "etherboot.h"
+#include "pxe_callbacks.h"
+#include "pxe_export.h"
+#include "pxe.h"
+
+unsigned long pxe_load_offset;
+
+static sector_t pxe_download ( unsigned char *data,
+ unsigned int len, int eof );
+
+static inline os_download_t pxe_probe ( unsigned char *data __unused,
+ unsigned int len __unused ) {
+ printf("(PXE)");
+ pxe_load_offset = 0;
+ return pxe_download;
+}
+
+static sector_t pxe_download ( unsigned char *data,
+ unsigned int len, int eof ) {
+ unsigned long block_address = PXE_LOAD_ADDRESS + pxe_load_offset;
+ PXENV_STATUS_t nbp_exit;
+
+ /* Check segment will fit. We can't do this in probe()
+ * because there's nothing in the non-existent header to tell
+ * us how long the image is.
+ */
+ if ( ! prep_segment ( block_address, block_address + len,
+ block_address + len,
+ pxe_load_offset, pxe_load_offset + len ) ) {
+ longjmp ( restart_etherboot, -2 );
+ }
+
+ /* Load block into memory, continue loading until eof */
+ memcpy ( phys_to_virt ( block_address ), data, len );
+ pxe_load_offset += len;
+ if ( ! eof ) {
+ return 0;
+ }
+
+ /* Start up PXE NBP */
+ done ( 0 );
+
+ /* Install and activate a PXE stack */
+ pxe_stack = install_pxe_stack ( NULL );
+ if ( ensure_pxe_state ( READY ) ) {
+ /* Invoke the NBP */
+ nbp_exit = xstartpxe();
+ } else {
+ /* Fake success so we tear down the stack */
+ nbp_exit = PXENV_STATUS_SUCCESS;
+ }
+
+ /* NBP has three exit codes:
+ * PXENV_STATUS_KEEP_UNDI : keep UNDI and boot next device
+ * PXENV_STATUS_KEEP_ALL : keep all and boot next device
+ * anything else : remove all and boot next device
+ *
+ * Strictly, we're meant to hand back to the BIOS, but this
+ * would prevent the useful combination of "PXE NBP fails, so
+ * let Etherboot try to boot its next device". We therefore
+ * take liberties.
+ */
+ if ( nbp_exit != PXENV_STATUS_KEEP_UNDI &&
+ nbp_exit != PXENV_STATUS_KEEP_ALL ) {
+ /* Tear down PXE stack */
+ remove_pxe_stack();
+ }
+
+ /* Boot next device. Under strict PXE compliance, exit back
+ * to the BIOS, otherwise let Etherboot move to the next
+ * device.
+ */
+#ifdef PXE_STRICT
+ longjmp ( restart_etherboot, 255 );
+#else
+ longjmp ( restart_etherboot, 4 );
+#endif
+
+ /* Never reached; avoid compiler warning */
+ return ( 0 );
+}
diff --git a/src/arch/i386/core/realmode.c b/src/arch/i386/core/realmode.c
new file mode 100644
index 00000000..ef4ede86
--- /dev/null
+++ b/src/arch/i386/core/realmode.c
@@ -0,0 +1,148 @@
+/* Real-mode interface: C portions.
+ *
+ * Initial version by Michael Brown <mbrown@fensystems.co.uk>, January 2004.
+ */
+
+#include "etherboot.h"
+#include "realmode.h"
+#include "segoff.h"
+
+#define RM_STACK_SIZE ( 0x1000 )
+
+/* gcc won't let us use extended asm outside a function (compiler
+ * bug), ao we have to put these asm statements inside a dummy
+ * function.
+ */
+static void work_around_gcc_bug ( void ) __attribute__ ((used));
+static void work_around_gcc_bug ( void ) {
+ /* Export _real_mode_stack_size as absolute linker symbol */
+ __asm__ ( ".globl _real_mode_stack_size" );
+ __asm__ ( ".equ _real_mode_stack_size, %c0" : : "i" (RM_STACK_SIZE) );
+}
+
+/* While Etherboot remains in base memory the real-mode stack is
+ * placed in the Etherboot main stack. The first allocation or
+ * deallocation of base memory will cause a 'proper' real-mode stack
+ * to be allocated. This will happen before Etherboot is relocated to
+ * high memory.
+ */
+uint32_t real_mode_stack = 0;
+size_t real_mode_stack_size = RM_STACK_SIZE;
+int lock_real_mode_stack = 0; /* Set to make stack immobile */
+
+/* Make a call to a real-mode code block.
+ */
+
+/* These is the structure that exists on the stack as the paramters
+ * passed in to _real_call. We pass a pointer to this struct to
+ * prepare_real_call(), to save stack space.
+ */
+typedef struct {
+ void *fragment;
+ int fragment_len;
+ void *in_stack;
+ int in_stack_len;
+ void *out_stack;
+ int out_stack_len;
+} real_call_params_t;
+
+uint32_t prepare_real_call ( real_call_params_t *p,
+ int local_stack_len, char *local_stack ) {
+ char *stack_base;
+ char *stack_end;
+ char *stack;
+ char *s;
+ prot_to_real_params_t *p2r_params;
+ real_to_prot_params_t *r2p_params;
+
+ /* Work out where we're putting the stack */
+ if ( virt_to_phys(local_stack) < 0xa0000 ) {
+ /* Current stack is in base memory. We can therefore
+ * use it directly, with a constraint on the size that
+ * we don't know; assume that we can use up to
+ * real_mode_stack_size. (Not a valid assumption, but
+ * it will do).
+ */
+ stack_end = local_stack + local_stack_len;
+ stack_base = stack_end - real_mode_stack_size;
+ } else {
+ if (!real_mode_stack) {
+ allot_real_mode_stack();
+ }
+ /* Use the allocated real-mode stack in base memory.
+ * This has fixed start and end points.
+ */
+ stack_base = phys_to_virt(real_mode_stack);
+ stack_end = stack_base + real_mode_stack_size;
+ }
+ s = stack = stack_end - local_stack_len;
+
+ /* Compile input stack and trampoline code to stack */
+ if ( p->in_stack_len ) {
+ memcpy ( s, p->in_stack, p->in_stack_len );
+ s += p->in_stack_len;
+ }
+ memcpy ( s, _prot_to_real_prefix, prot_to_real_prefix_size );
+ s += prot_to_real_prefix_size;
+ p2r_params = (prot_to_real_params_t*) ( s - sizeof(*p2r_params) );
+ memcpy ( s, p->fragment, p->fragment_len );
+ s += p->fragment_len;
+ memcpy ( s, _real_to_prot_suffix, real_to_prot_suffix_size );
+ s += real_to_prot_suffix_size;
+ r2p_params = (real_to_prot_params_t*) ( s - sizeof(*r2p_params) );
+
+ /* Set parameters within compiled stack */
+ p2r_params->ss = p2r_params->cs = SEGMENT ( stack_base );
+ p2r_params->esp = virt_to_phys ( stack );
+ p2r_params->r2p_params = virt_to_phys ( r2p_params );
+ r2p_params->out_stack = ( p->out_stack == NULL ) ?
+ 0 : virt_to_phys ( p->out_stack );
+ r2p_params->out_stack_len = p->out_stack_len;
+
+ return virt_to_phys ( stack + p->in_stack_len );
+}
+
+
+/* Parameters are not genuinely unused; they are passed to
+ * prepare_real_call() as part of a real_call_params_t struct.
+ */
+uint16_t _real_call ( void *fragment, int fragment_len,
+ void *in_stack __unused, int in_stack_len,
+ void *out_stack __unused, int out_stack_len __unused ) {
+ uint16_t retval;
+
+ /* This code is basically equivalent to
+ *
+ * uint32_t trampoline;
+ * char local_stack[ in_stack_len + prot_to_real_prefix_size +
+ * fragment_len + real_to_prot_suffix_size ];
+ * trampoline = prepare_real_call ( &fragment, local_stack );
+ * __asm__ ( "call _virt_to_phys\n\t"
+ * "call %%eax\n\t"
+ * "call _phys_to_virt\n\t"
+ * : "=a" (retval) : "0" (trampoline) );
+ *
+ * but implemented in assembly to avoid problems with not
+ * being certain exactly how gcc handles %esp.
+ */
+
+ __asm__ ( "pushl %%ebp\n\t"
+ "movl %%esp, %%ebp\n\t" /* %esp preserved via %ebp */
+ "subl %%ecx, %%esp\n\t" /* space for inline RM stack */
+ "pushl %%esp\n\t" /* set up RM stack */
+ "pushl %%ecx\n\t"
+ "pushl %%eax\n\t"
+ "call prepare_real_call\n\t" /* %eax = trampoline addr */
+ "addl $12, %%esp\n\t"
+ "call _virt_to_phys\n\t" /* switch to phys addr */
+ "call *%%eax\n\t" /* call to trampoline */
+ "call _phys_to_virt\n\t" /* switch to virt addr */
+ "movl %%ebp, %%esp\n\t" /* restore %esp & %ebp */
+ "popl %%ebp\n\t"
+ : "=a" ( retval )
+ : "0" ( &fragment )
+ , "c" ( ( ( in_stack_len + prot_to_real_prefix_size +
+ fragment_len + real_to_prot_suffix_size )
+ + 0x3 ) & ~0x3 ) );
+ return retval;
+}
diff --git a/src/arch/i386/core/realmode_asm.S b/src/arch/i386/core/realmode_asm.S
new file mode 100644
index 00000000..28a5bfed
--- /dev/null
+++ b/src/arch/i386/core/realmode_asm.S
@@ -0,0 +1,695 @@
+/* Real-mode interface: assembly-language portions.
+ *
+ * Initial version by Michael Brown <mbrown@fensystems.co.uk>, January 2004.
+ */
+
+#include "realmode.h"
+#include "callbacks.h"
+
+#if 1 /* CODE16 */
+
+#define BOCHSBP xchgw %bx,%bx
+
+#define NUM_PUSHA_REGS (8)
+#define NUM_SEG_REGS (6)
+
+ .text
+ .arch i386
+ .section ".text16.nocompress", "ax", @progbits
+ .code16
+
+ .equ CR0_PE,1
+
+#ifdef GAS291
+#define DATA32 data32;
+#define ADDR32 addr32;
+#define LJMPI(x) ljmp x
+#else
+#define DATA32 data32
+#define ADDR32 addr32
+/* newer GAS295 require #define LJMPI(x) ljmp *x */
+#define LJMPI(x) ljmp x
+#endif
+
+/****************************************************************************
+ * REAL-MODE CALLBACK INTERFACE
+ *
+ * This must be copied down to base memory in order for external
+ * programs to be able to make calls in to Etherboot. Store the
+ * current physical address of Etherboot (i.e. virt_to_phys(_text)) in
+ * (uint32_t)rm_etherboot_location, then copy
+ * (uint16_t)rm_callback_interface_size bytes starting at
+ * &((void)rm_callback_interface).
+ *
+ * There are two defined entry points:
+ * Offset RM_IN_CALL = 0 Near call entry point
+ * Offset RM_IN_CALL_FAR = 2 Far call entry point
+ *
+ * Note that the routines _prot_to_real and _real_to_prot double as
+ * trampoline fragments for external calls (calls from Etherboot to
+ * real-mode code). _prot_to_real does not automatically re-enable
+ * interrupts; this is to allow for the potential of using Etherboot
+ * code as an ISR. _real_to_prot does automatically disable
+ * interrupts, since we don't have a protected-mode IDT.
+ ****************************************************************************
+ */
+
+ .globl rm_callback_interface
+ .code16
+rm_callback_interface:
+ .globl _rm_in_call
+_rm_in_call:
+ jmp _real_in_call
+ .globl _rm_in_call_far
+_rm_in_call_far:
+ jmp _real_in_call_far
+
+/****************************************************************************
+ * _real_in_call
+ *
+ * Parameters:
+ * 16-bit real-mode near/far return address (implicit from [l]call
+ * to routine) Other parameters as for _in_call_far().
+ *
+ * This routine will convert the 16-bit real-mode far return address
+ * to a 32-bit real-mode far return address, switch to protected mode
+ * using _real_to_prot and call in to _in_call_far.
+ ****************************************************************************
+ */
+
+#define RIC_PRESERVE ( 8 )
+#define RIC_OFFSET_CALLADDR ( RIC_PRESERVE )
+#define RIC_OFFSET_CALLADDR_E ( RIC_OFFSET_CALLADDR + 4 )
+#define RIC_OFFSET_CONTADDR ( RIC_OFFSET_CALLADDR_E )
+#define RIC_OFFSET_CONTADDR_E ( RIC_OFFSET_CONTADDR + 4 )
+#define RIC_OFFSET_OPCODE ( RIC_OFFSET_CONTADDR_E )
+#define RIC_OFFSET_OPCODE_E ( RIC_OFFSET_OPCODE + 4 )
+#define RIC_OFFSET_SEG_REGS ( RIC_OFFSET_OPCODE_E )
+#define RIC_OFFSET_SEG_REGS_E ( RIC_OFFSET_SEG_REGS + ( NUM_SEG_REGS * 2 ) )
+#define RIC_OFFSET_PAD ( RIC_OFFSET_SEG_REGS_E )
+#define RIC_OFFSET_PAD_E ( RIC_OFFSET_PAD + 2 )
+#define RIC_OFFSET_FLAGS ( RIC_OFFSET_PAD_E )
+#define RIC_OFFSET_FLAGS_E ( RIC_OFFSET_FLAGS + 2 )
+#define RIC_OFFSET_RETADDR ( RIC_OFFSET_FLAGS_E )
+#define RIC_OFFSET_RETADDR_E ( RIC_OFFSET_RETADDR + 4 )
+#define RIC_OFFSET_ORIG_OPCODE ( RIC_OFFSET_RETADDR_E )
+#define RIC_INSERT_LENGTH ( RIC_OFFSET_OPCODE_E - RIC_OFFSET_CALLADDR )
+
+ .code16
+_real_in_call:
+ /* Expand near return address to far return address
+ */
+ pushw %ax /* Extend stack, store %ax */
+ pushfw
+ pushw %bp
+ movw %sp, %bp
+ movw %cs, %ax
+ xchgw %ax, 6(%bp)
+ xchgw %ax, 4(%bp) /* also restores %ax */
+ popw %bp
+ popfw
+ /* Fall through to _real_in_call_far */
+
+_real_in_call_far:
+ /* Store flags and pad */
+ pushfw
+ pushw %ax
+
+ /* Store segment registers. Order matches that of seg_regs_t */
+ pushw %gs
+ pushw %fs
+ pushw %es
+ pushw %ds
+ pushw %ss
+ pushw %cs
+
+ /* Switch to protected mode */
+ call _real_to_prot
+ .code32
+
+ /* Allow space for expanded stack */
+ subl $RIC_INSERT_LENGTH, %esp
+
+ /* Store temporary registers */
+ pushl %ebp
+ pushl %eax
+
+ /* Copy opcode, set EB_CALL_FROM_REAL_MODE and EP_SKIP_OPCODE.
+ * Copy it because _in_call() and i386_in_call() expect it at
+ * a fixed position, not as part of the va_list.
+ */
+ movl RIC_OFFSET_ORIG_OPCODE(%esp), %eax
+ orl $(EB_CALL_FROM_REAL_MODE|EB_SKIP_OPCODE), %eax
+ movl %eax, RIC_OFFSET_OPCODE(%esp)
+
+ /* Set up call and return addresses */
+ call 1f
+1: popl %ebp
+ subl $1b, %ebp /* %ebp = offset */
+ movl rm_etherboot_location(%ebp), %eax /* Etherboot phys addr */
+ subl $_text, %eax
+ addl $_in_call, %eax /* _in_call phys addr */
+ movl %eax, RIC_OFFSET_CALLADDR(%esp)
+ leal 2f(%ebp), %eax /* continuation address */
+ movl %eax, RIC_OFFSET_CONTADDR(%esp)
+
+ /* Restore temporary registers */
+ popl %eax
+ popl %ebp
+
+ /* Call to _in_call */
+ ret
+ /* opcode will be popped automatically thanks to EB_SKIP_OPCODE */
+
+2: /* Continuation point */
+ call _prot_to_real /* Return to real mode */
+ /* Note: the first two words of our segment register store
+ * happens to be exactly what we need to pass as parameters to
+ * _prot_to_real.
+ */
+ .code16
+ popw %ds /* Restore segment registers */
+ popw %ds /* (skip cs&ss since these */
+ popw %ds /* have already been set by */
+ popw %es /* _prot_to_real */
+ popw %fs
+ popw %gs
+ addw $2, %sp /* skip pad */
+
+ /* Check for EB_SKIP_OPCODE */
+ pushw %bp
+ movw %sp, %bp
+ testl $EB_SKIP_OPCODE, 6(%bp)
+ popw %bp
+ jnz 1f
+ /* Normal return */
+ popfw /* Restore interrupt status */
+ lret /* Back to caller */
+1: /* Return and skip opcode */
+ popfw
+ lret $4
+
+/****************************************************************************
+ * rm_etherboot_location: the current physical location of Etherboot.
+ * Needed so that real-mode callback routines can locate Etherboot.
+ ****************************************************************************
+ */
+ .globl rm_etherboot_location
+rm_etherboot_location: .long 0
+
+/****************************************************************************
+ * _prot_to_real_prefix
+ *
+ * Trampoline fragment. Switch from 32-bit protected mode with flat
+ * physical addresses to 16-bit real mode. Store registers in the
+ * trampoline for restoration by _real_to_prot_suffix. Switch to
+ * stack in base memory.
+ ****************************************************************************
+ */
+
+ .globl _prot_to_real_prefix
+ .code32
+_prot_to_real_prefix:
+ /* Registers to preserve */
+ pushl %ebx
+ pushl %esi
+ pushl %edi
+ pushl %ebp
+
+ /* Calculate offset */
+ call 1f
+1: popl %ebp
+ subl $1b, %ebp /* %ebp = offset for labels in p2r*/
+
+ /* Preserve registers and return address in r2p_params */
+ movl p2r_r2p_params(%ebp), %ebx
+ subl $r2p_params, %ebx /* %ebx = offset for labels in r2p */
+ popl r2p_ebp(%ebx)
+ popl r2p_edi(%ebx)
+ popl r2p_esi(%ebx)
+ popl r2p_ebx(%ebx)
+ popl r2p_ret_addr(%ebx)
+ movl %esp, r2p_esp(%ebx)
+
+ /* Switch stacks */
+ movl p2r_esp(%ebp), %esp
+
+ /* Switch to real mode */
+ pushl p2r_segments(%ebp)
+ call _prot_to_real
+ .code16
+ addw $4, %sp
+
+ /* Fall through to next trampoline fragment */
+ jmp _prot_to_real_prefix_end
+
+/****************************************************************************
+ * _prot_to_real
+ *
+ * Switch from 32-bit protected mode with flat physical addresses to
+ * 16-bit real mode. Stack and code must be in base memory when
+ * called. %cs, %ss, %eip, %esp are changed to real-mode values,
+ * other segment registers are destroyed, all other registers are
+ * preserved. Interrupts are *not* enabled.
+ *
+ * Parameters:
+ * %cs Real-mode code segment (word)
+ * %ss Real-mode stack segment (word)
+ ****************************************************************************
+ */
+
+#define P2R_PRESERVE ( 12 )
+#define P2R_OFFSET_RETADDR ( P2R_PRESERVE )
+#define P2R_OFFSET_RETADDR_E ( P2R_OFFSET_RETADDR + 4 )
+#define P2R_OFFSET_CS ( P2R_OFFSET_RETADDR_E )
+#define P2R_OFFSET_CS_E ( P2R_OFFSET_CS + 2 )
+#define P2R_OFFSET_SS ( P2R_OFFSET_CS_E )
+#define P2R_OFFSET_SS_E ( P2R_OFFSET_SS + 2 )
+
+ .globl _prot_to_real
+ .code32
+_prot_to_real:
+ /* Preserve registers */
+ pushl %ebp
+ pushl %ebx
+ pushl %eax
+
+ /* Calculate offset */
+ call 1f
+1: popl %ebp
+ subl $1b, %ebp /* %ebp = offset for labels in p2r*/
+
+ /* Set up GDT with real-mode limits and appropriate bases for
+ * real-mode %cs and %ss. Set up protected-mode continuation
+ * point on stack.
+ */
+ /* Fixup GDT */
+ leal p2r_gdt(%ebp), %eax
+ movl %eax, p2r_gdt_addr(%ebp)
+
+ /* Calculate CS base address: set GDT code segment, adjust
+ * return address, set up continuation address on stack.
+ */
+ movzwl P2R_OFFSET_CS(%esp), %eax
+ shll $4, %eax
+ /* Change return address to real-mode far address */
+ subl %eax, P2R_OFFSET_RETADDR(%esp)
+ movl %eax, %ebx
+ shrl $4, %ebx
+ movw %bx, (P2R_OFFSET_RETADDR+2)(%esp)
+ /* First real mode address */
+ movl %eax, %ebx
+ shrl $4, %ebx
+ pushw %bx
+ leal 3f(%ebp), %ebx
+ subl %eax, %ebx
+ pushw %bx
+ /* Continuation address */
+ pushl $(p2r_rmcs - p2r_gdt)
+ leal 2f(%ebp), %ebx
+ subl %eax, %ebx
+ pushl %ebx
+ /* Code segment in GDT */
+ movw %ax, (p2r_rmcs+2)(%ebp)
+ shrl $16, %eax /* Remainder of cs base addr */
+ movb %al, (p2r_rmcs+4)(%ebp)
+ movb %ah, (p2r_rmcs+7)(%ebp)
+
+ /* Calculate SS base address: set GDT data segment, retain to
+ * use for adjusting %esp.
+ */
+ movzwl (12+P2R_OFFSET_SS)(%esp), %eax /* Set ss base address */
+ shll $4, %eax
+ movw %ax, (p2r_rmds+2)(%ebp)
+ movl %eax, %ebx
+ shrl $16, %ebx
+ movb %bl, (p2r_rmds+4)(%ebp)
+ movb %bh, (p2r_rmds+7)(%ebp)
+
+ /* Load GDT */
+ lgdt p2r_gdt(%ebp)
+ /* Reload all segment registers and adjust %esp */
+ movw $(p2r_rmds - p2r_gdt), %bx /* Pmode DS */
+ movw %bx, %ss
+ subl %eax, %esp /* %esp now less than 0x10000 */
+ movw %bx, %ds
+ movw %bx, %es
+ movw %bx, %fs
+ movw %bx, %gs
+ lret /* %cs:eip */
+2: /* Segment registers now have 16-bit limits. */
+ .code16
+
+ /* Switch to real mode */
+ movl %cr0, %ebx
+ andb $0!CR0_PE, %bl
+ movl %ebx, %cr0
+
+ /* Make intersegment jmp to flush the processor pipeline
+ * and reload %cs:%eip (to clear upper 16 bits of %eip).
+ */
+ lret
+3:
+
+ /* Load real-mode segment value to %ss. %sp already OK */
+ shrl $4, %eax
+ movw %ax, %ss
+
+ /* Restore registers */
+ popl %eax
+ popl %ebx
+ popl %ebp
+
+ /* Return to caller in real-mode */
+ lret
+
+#ifdef FLATTEN_REAL_MODE
+#define RM_LIMIT_16_19__AVL__SIZE__GRANULARITY 0x8f
+#else
+#define RM_LIMIT_16_19__AVL__SIZE__GRANULARITY 0x00
+#endif
+
+p2r_gdt:
+p2r_gdtarg:
+p2r_gdt_limit: .word p2r_gdt_end - p2r_gdt - 1
+p2r_gdt_addr: .long 0
+p2r_gdt_padding: .word 0
+p2r_rmcs:
+ /* 16 bit real mode code segment */
+ .word 0xffff,(0&0xffff)
+ .byte (0>>16),0x9b,RM_LIMIT_16_19__AVL__SIZE__GRANULARITY,(0>>24)
+p2r_rmds:
+ /* 16 bit real mode data segment */
+ .word 0xffff,(0&0xffff)
+ .byte (0>>16),0x93,RM_LIMIT_16_19__AVL__SIZE__GRANULARITY,(0>>24)
+p2r_gdt_end:
+
+ /* This is the end of the trampoline prefix code. When used
+ * as a prefix, fall through to the following code in the
+ * trampoline.
+ */
+p2r_params: /* Structure must match prot_to_real_params_t in realmode.h */
+p2r_esp: .long 0
+p2r_segments:
+p2r_cs: .word 0
+p2r_ss: .word 0
+p2r_r2p_params: .long 0
+ .globl _prot_to_real_prefix_end
+_prot_to_real_prefix_end:
+
+ .globl _prot_to_real_prefix_size
+ .equ _prot_to_real_prefix_size, _prot_to_real_prefix_end - _prot_to_real_prefix
+ .globl prot_to_real_prefix_size
+prot_to_real_prefix_size:
+ .word _prot_to_real_prefix_size
+
+/****************************************************************************
+ * _real_to_prot_suffix
+ *
+ * Trampoline fragment. Switch from 16-bit real-mode to 32-bit
+ * protected mode with flat physical addresses. Copy returned stack
+ * parameters to output_stack. Restore registers preserved by
+ * _prot_to_real_prefix. Restore stack to previous location.
+ ****************************************************************************
+ */
+
+ .globl _real_to_prot_suffix
+ .code16
+_real_to_prot_suffix:
+
+ /* Switch to protected mode */
+ call _real_to_prot
+ .code32
+
+ /* Calculate offset */
+ call 1f
+1: popl %ebp
+ subl $1b, %ebp /* %ebp = offset for labels in r2p */
+
+ /* Copy stack to out_stack */
+ movl r2p_out_stack(%ebp), %edi
+ movl r2p_out_stack_len(%ebp), %ecx
+ movl %esp, %esi
+ cld
+ rep movsb
+
+ /* Switch back to original stack */
+ movl r2p_esp(%ebp), %esp
+
+ /* Restore registers and return */
+ pushl r2p_ret_addr(%ebp) /* Set up return address on stack */
+ movl r2p_ebx(%ebp), %ebx
+ movl r2p_esi(%ebp), %esi
+ movl r2p_edi(%ebp), %edi
+ movl r2p_ebp(%ebp), %ebp
+ ret
+
+/****************************************************************************
+ * _real_to_prot
+ *
+ * Switch from 16-bit real-mode to 32-bit protected mode with flat
+ * physical addresses. All segment registers are destroyed, %eip and
+ * %esp are changed to flat physical values, all other registers are
+ * preserved. Interrupts are disabled.
+ *
+ * Parameters: none
+ ****************************************************************************
+ */
+
+#define R2P_PRESERVE ( 12 )
+#define R2P_OFFSET_RETADDR ( R2P_PRESERVE )
+#define R2P_OFFSET_ORIG_RETADDR ( R2P_OFFSET_RETADDR + 2 )
+
+ .globl _real_to_prot
+ .code16
+_real_to_prot:
+ /* Disable interrupts */
+ cli
+ /* zero extend the return address */
+ pushw $0
+
+ /* Preserve registers */
+ pushl %ebp
+ pushl %ebx
+ pushl %eax
+
+ /* Convert 16-bit real-mode near return address to
+ * 32-bit pmode physical near return address
+ */
+ movw %sp, %bp
+ xorl %ebx, %ebx
+ push %cs
+ popw %bx
+ movw %bx, %ds
+ shll $4, %ebx
+ movzwl %ss:R2P_OFFSET_ORIG_RETADDR(%bp), %eax
+ addl %ebx, %eax
+ movl %eax, %ss:(R2P_OFFSET_RETADDR)(%bp)
+
+ /* Store the code segment physical base address in %ebp */
+ movl %ebx, %ebp
+
+ /* Find the offset within the code segment that I am running at */
+ xorl %ebx, %ebx
+ call 1f
+1: popw %bx
+
+ /* Set up GDT */
+ leal (r2p_gdt-1b)(%bx), %eax /* %ds:ebx = %ds:bx = &(r2p_gdt) */
+ addl %ebp, %eax /* %eax = &r2p_gdt (physical) */
+ movl %eax, %ds:(r2p_gdt-1b+2)(%bx) /* Set phys. addr. in r2p_gdt */
+
+ /* Compute the first protected mode physical address */
+ leal (2f-1b)(%bx), %eax
+ addl %ebp, %eax
+ movl %eax, %ds:(r2p_paddr-1b)(%bx)
+
+ /* Calculate new %esp */
+ xorl %eax, %eax
+ push %ss
+ popw %ax
+ shll $4, %eax
+ movzwl %sp, %ebp
+ addl %eax, %ebp /* %ebp = new %esp */
+
+ /* Load GDT */
+ DATA32 lgdt %ds:(r2p_gdt-1b)(%bx) /* Load GDT */
+
+ /* Switch to protected mode */
+ movl %cr0, %eax
+ orb $CR0_PE, %al
+ movl %eax, %cr0
+
+ /* flush prefetch queue, and reload %cs:%eip */
+ DATA32 ljmp %ds:(r2p_paddr-1b)(%bx)
+ .code32
+2:
+
+ /* Load segment registers, adjust %esp */
+ movw $(r2p_pmds-r2p_gdt), %ax
+ movw %ax, %ss
+ movl %ebp, %esp
+ movw %ax, %ds
+ movw %ax, %es
+ movw %ax, %fs
+ movw %ax, %gs
+
+ /* Restore registers */
+ popl %eax
+ popl %ebx
+ popl %ebp
+
+ /* return to caller */
+ ret
+
+r2p_gdt:
+ .word r2p_gdt_end - r2p_gdt - 1 /* limit */
+ .long 0 /* addr */
+ .word 0
+r2p_pmcs:
+ /* 32 bit protected mode code segment, physical addresses */
+ .word 0xffff, 0
+ .byte 0, 0x9f, 0xcf, 0
+r2p_pmds:
+ /* 32 bit protected mode data segment, physical addresses */
+ .word 0xffff,0
+ .byte 0,0x93,0xcf,0
+r2p_gdt_end:
+
+r2p_paddr:
+ .long 2b
+ .word r2p_pmcs - r2p_gdt, 0
+
+
+ /* This is the end of the trampoline suffix code.
+ */
+r2p_params: /* Structure must match real_to_prot_params_t in realmode.h */
+r2p_ret_addr: .long 0
+r2p_esp: .long 0
+r2p_ebx: .long 0
+r2p_esi: .long 0
+r2p_edi: .long 0
+r2p_ebp: .long 0
+r2p_out_stack: .long 0
+r2p_out_stack_len: .long 0
+ .globl _real_to_prot_suffix_end
+_real_to_prot_suffix_end:
+
+ .globl _real_to_prot_suffix_size
+ .equ _real_to_prot_suffix_size, _real_to_prot_suffix_end - _real_to_prot_suffix
+ .globl real_to_prot_suffix_size
+real_to_prot_suffix_size:
+ .word _real_to_prot_suffix_size
+
+rm_callback_interface_end:
+
+ .globl _rm_callback_interface_size
+ .equ _rm_callback_interface_size, rm_callback_interface_end - rm_callback_interface
+ .globl rm_callback_interface_size
+rm_callback_interface_size:
+ .word _rm_callback_interface_size
+
+/****************************************************************************
+ * END OF REAL-MODE CALLBACK INTERFACE
+ ****************************************************************************
+ */
+
+
+#ifdef PXE_EXPORT
+/****************************************************************************
+ * PXE CALLBACK INTERFACE
+ *
+ * Prepend this to rm_callback_interface to create a real-mode PXE
+ * callback interface.
+ ****************************************************************************
+ */
+ .section ".text16", "ax", @progbits
+ .globl pxe_callback_interface
+ .code16
+pxe_callback_interface:
+
+/* Macro to calculate offset of labels within code segment in
+ * installed copy of code.
+ */
+#define INSTALLED(x) ( (x) - pxe_callback_interface )
+
+/****************************************************************************
+ * PXE entry points (!PXE and PXENV+ APIs)
+ ****************************************************************************
+ */
+ /* in_call mechanism for !PXE API calls */
+ .globl _pxe_in_call_far
+_pxe_in_call_far:
+ /* Prepend "PXE API call" and "API version 0x201" to stack */
+ pushl $0x201
+ jmp 1f
+ /* in_call mechanism for PXENV+ API calls */
+ .globl _pxenv_in_call_far
+_pxenv_in_call_far:
+ /* Prepend "PXE API call" and "API version 0x200" to stack */
+ pushl $0x200
+1: pushl $EB_OPCODE_PXE
+ /* Perform real-mode in_call */
+ call pxe_rm_in_call
+ /* Return */
+ addw $8, %sp
+ lret
+
+/****************************************************************************
+ * PXE installation check (INT 1A) code
+ ****************************************************************************
+ */
+ .globl _pxe_intercept_int1a
+_pxe_intercept_int1a:
+ pushfw
+ cmpw $0x5650, %ax
+ jne 2f
+1: /* INT 1A,5650 - Intercept */
+ popfw
+ /* Set up return values according to PXE spec: */
+ movw $0x564e, %ax /* AX := 564Eh (VN) */
+ pushw %cs:INSTALLED(_pxe_pxenv_segment)
+ popw %es /* ES:BX := &(PXENV+ structure) */
+ movw %cs:INSTALLED(_pxe_pxenv_offset), %bx
+ clc /* CF is cleared */
+ lret $2 /* 'iret' without reloading flags */
+2: /* INT 1A,other - Do not intercept */
+ popfw
+ ljmp %cs:*INSTALLED(_pxe_intercepted_int1a)
+
+ .globl _pxe_intercepted_int1a
+_pxe_intercepted_int1a: .word 0,0
+ .globl _pxe_pxenv_location
+_pxe_pxenv_location:
+_pxe_pxenv_offset: .word 0
+_pxe_pxenv_segment: .word 0
+
+pxe_rm_in_call:
+pxe_attach_rm:
+ /* rm_callback_interface must be appended here */
+
+pxe_callback_interface_end:
+
+ .globl _pxe_callback_interface_size
+ .equ _pxe_callback_interface_size, pxe_callback_interface_end - pxe_callback_interface
+ .globl pxe_callback_interface_size
+pxe_callback_interface_size:
+ .word _pxe_callback_interface_size
+
+#else /* PXE_EXPORT */
+
+/* Define symbols used by the linker scripts, to prevent link errors */
+ .globl _pxe_callback_interface_size
+ .equ _pxe_callback_interface_size, 0
+
+#endif /* PXE_EXPORT */
+
+#else /* CODE16 */
+
+/* Define symbols used by the linker scripts, to prevent link errors */
+ .globl _rm_callback_interface_size
+ .equ _rm_callback_interface_size, 0
+ .globl _pxe_callback_interface_size
+ .equ _pxe_callback_interface_size, 0
+
+#endif /* CODE16 */
diff --git a/src/arch/i386/core/start16.S b/src/arch/i386/core/start16.S
new file mode 100644
index 00000000..72fcfbfb
--- /dev/null
+++ b/src/arch/i386/core/start16.S
@@ -0,0 +1,285 @@
+/*****************************************************************************
+ *
+ * THIS FILE IS NOW OBSOLETE.
+ *
+ * The functions of this file are now placed in init.S.
+ *
+ *****************************************************************************
+ */
+
+#ifndef PCBIOS
+#error "16bit code is only supported with the PCBIOS"
+#endif
+
+#define CODE_SEG 0x08
+#define DATA_SEG 0x10
+
+#define EXEC_IN_SITU_MAGIC 0x45524548 /* 'HERE' */
+
+ .equ CR0_PE, 1
+
+#ifdef GAS291
+#define DATA32 data32;
+#define ADDR32 addr32;
+#define LJMPI(x) ljmp x
+#else
+#define DATA32 data32
+#define ADDR32 addr32
+/* newer GAS295 require #define LJMPI(x) ljmp *x */
+#define LJMPI(x) ljmp x
+#endif
+
+/*****************************************************************************
+ *
+ * start16 : move payload to desired area of memory, set up for exit
+ * back to prefix, set up for 32-bit code.
+ *
+ * Enter (from prefix) with es:di = 0x4552:0x4548 if you want to
+ * prevent start16 from moving the payload. There are three
+ * motivations for moving the payload:
+ *
+ * 1. It may be in ROM, in which case we need to move it to RAM.
+ * 2. Whatever loaded us probably didn't know about our memory usage
+ * beyond the end of the image file. We should claim this memory
+ * before using it.
+ *
+ * Unless the prefix instructs us otherwise we will move the payload to:
+ *
+ * An area of memory claimed from the BIOS via 40:13.
+ *
+ * We use the main Etherboot stack (within the image target) as our
+ * stack; we don't rely on the prefix passing us a stack usable for
+ * anything other than the prefix's return address. The (first 512
+ * bytes of the) prefix code segment is copied to a safe archive
+ * location.
+ *
+ * When we return to the prefix (from start32), we copy this code back
+ * to a new area of memory, restore the prefix's ss:sp and ljmp back
+ * to the copy of the prefix. The prefix will see a return from
+ * start16 *but* may be executing at a new location. Code following
+ * the lcall to start16 must therefore be position-independent and
+ * must also be within [cs:0000,cs:01ff]. We make absolutely no
+ * guarantees about the stack contents when the prefix regains
+ * control.
+ *
+ * Trashes just about all registers, including all the segment
+ * registers.
+ *
+ *****************************************************************************
+ */
+
+ .text
+ .code16
+ .arch i386
+ .org 0
+ .globl _start16
+_start16:
+
+/*****************************************************************************
+ * Work out where we are going to place our image (image = optional
+ * decompressor + runtime). Exit this stage with %ax containing the
+ * runtime target address divided by 16 (i.e. a real-mode segment
+ * address).
+ *****************************************************************************
+ */
+ movw %es, %ax
+ cmpw $(EXEC_IN_SITU_MAGIC >> 16), %ax
+ jne exec_moved
+ cmpw $(EXEC_IN_SITU_MAGIC & 0xffff), %di
+ jne exec_moved
+exec_in_situ:
+ /* Prefix has warned us not to move the payload. Simply
+ * calculate where the image is going to end up, so we can
+ * work out where to put our stack.
+ */
+ movw %cs, %ax
+ addw $((payload-_start16)/16), %ax
+ jmp 99f
+exec_moved:
+ /* Claim an area of base memory from the BIOS and put the
+ * payload there. arch_relocated_to() will deal with freeing
+ * up this memory once we've relocated to high memory.
+ */
+ movw $0x40, %ax
+ movw %ax, %es
+ movw %es:(0x13), %ax /* FBMS in kb to %ax */
+ shlw $6, %ax /* ... in paragraphs */
+ subw $__image_size_pgh, %ax /* Subtract space for image */
+ shrw $6, %ax /* Round down to nearest kb */
+ movw %ax, %es:(0x13) /* ...and claim memory from BIOS */
+ shlw $6, %ax
+99:
+ /* At this point %ax contains the segment address for the
+ * start of the image (image = optional decompressor + runtime).
+ */
+
+/*****************************************************************************
+ * Set up stack in start32's stack space within the place we're going
+ * to copy Etherboot to, reserve space for GDT, copy return address
+ * from prefix stack, store prefix stack address
+ *****************************************************************************
+ */
+ popl %esi /* Return address */
+ mov %ss, %bx /* %es:di = prefix stack address */
+ mov %bx, %es /* (*after* pop of return address) */
+ movw %sp, %di
+ movw $__offset_stack_pgh, %bx /* Set up Etherboot stack */
+ addw %ax, %bx
+ movw %bx, %ss
+ movw $__stack_size, %sp
+ subw $(_gdt_end - _gdt), %sp /* Reserve space for GDT */
+ movw %sp, %bp /* Record GDT location */
+ /* Set up i386_rm_in_call_data_t structure on stack. This is
+ * the same structure as is set up by rm_in_call.
+ */
+ pushl $0 /* Dummy opcode */
+ pushl %esi /* Prefix return address */
+ pushfw /* Flags */
+ pushw %di /* Prefix %sp */
+ pushw %gs /* Segment registers */
+ pushw %fs
+ pushw %es
+ pushw %ds
+ pushw %es /* Prefix %ss */
+ pushw %cs
+ /* Stack is now 32-bit aligned */
+
+ /* %ax still contains image target segment address */
+
+/*****************************************************************************
+ * Calculate image target and prefix code physical addresses, store on stack
+ * for use in copy routine.
+ *****************************************************************************
+ */
+ movzwl %es:-2(%di), %ebx /* Prefix code segment */
+ shll $4, %ebx
+ pushl %ebx /* Prefix code physical address */
+ movzwl %ax, %edi /* Image target segment */
+ shll $4, %edi
+ pushl %edi /* Image target physical address */
+
+/*****************************************************************************
+ * Transition to 32-bit protected mode. Set up all segment
+ * descriptors to use flat physical addresses.
+ *****************************************************************************
+ */
+ /* Copy gdt to area reserved on stack
+ */
+ push %cs /* GDT source location -> %ds:%si */
+ pop %ds
+ mov $(_gdt - _start16), %si
+ push %ss /* GDT target location -> %es:%di */
+ pop %es
+ mov %bp, %di
+ mov $(_gdt_end - _gdt), %cx
+ cld
+ rep movsb /* Copy GDT to stack */
+ movl %ss, %eax
+ shll $4, %eax
+ movzwl %bp, %ebx
+ addl %eax, %ebx /* Physical addr of GDT copy -> %ebx */
+ movl %ebx, 2(%bp) /* Fill in addr field in GDT */
+
+ /* Compute the offset I am running at.
+ */
+ movl %cs, %ebx
+ shll $4, %ebx /* %ebx = offset for start16 symbols */
+
+ /* Switch to 32bit protected mode.
+ */
+ cli /* Disable interrupts */
+ lgdt (%bp) /* Load GDT from stack */
+ movl %cr0, %eax /* Set protected mode bit */
+ orb $CR0_PE, %al
+ movl %eax, %cr0
+ movl %ss, %eax /* Convert stack pointer to 32bit */
+ shll $4, %eax
+ movzwl %sp, %esp
+ addl %eax, %esp
+ movl $DATA_SEG, %eax /* Reload the segment registers */
+ movl %eax, %ds
+ movl %eax, %es
+ movl %eax, %ss
+ movl %eax, %fs
+ movl %eax, %gs
+ /* Flush prefetch queue, and reload %cs:%eip by effectively ljmping
+ * to code32_start. Do the jump via pushl and lret because the text
+ * may not be writable/
+ */
+ pushl $CODE_SEG
+ ADDR32 leal (code32_start-_start16)(%ebx), %eax
+ pushl %eax
+ DATA32 lret /* DATA32 needed, because we're still in 16-bit mode */
+
+_gdt:
+gdtarg:
+ .word _gdt_end - _gdt - 1 /* limit */
+ .long 0 /* addr */
+ .word 0
+_pmcs:
+ /* 32 bit protected mode code segment */
+ .word 0xffff, 0
+ .byte 0, 0x9f, 0xcf, 0
+_pmds:
+ /* 32 bit protected mode data segment */
+ .word 0xffff,0
+ .byte 0,0x93,0xcf,0
+_gdt_end:
+
+ .code32
+code32_start:
+
+/*****************************************************************************
+ * Copy payload to target location. Do the copy backwards, since if
+ * there's overlap with a forward copy then it means start16 is going
+ * to get trashed during the copy anyway...
+ *****************************************************************************
+ */
+ popl %edi /* Image target physical address */
+ pushl %edi
+ leal (payload-_start16)(%ebx), %esi /* Image source physical addr */
+ movl $__payload_size, %ecx /* Payload size (not image size) */
+ addl %ecx, %edi /* Start at last byte (length - 1) */
+ decl %edi
+ addl %ecx, %esi
+ decl %esi
+ std /* Backward copy of image */
+ rep movsb
+ cld
+ popl %edi /* Restore image target physical address */
+ leal __decompressor_uncompressed(%edi), %ebx
+ subl $_text, %ebx /* %ebx = offset for runtime symbols */
+
+/*****************************************************************************
+ * Copy prefix to storage area within Etherboot image.
+ *****************************************************************************
+ */
+ popl %esi /* Prefix source physical address */
+ pushl %edi
+ leal _prefix_copy(%ebx), %edi /* Prefix copy phys. addr. */
+ leal _eprefix_copy(%ebx), %ecx
+ subl %edi, %ecx /* Prefix copy size */
+ rep movsb /* Forward copy of prefix */
+ popl %edi /* Restore image target physical address */
+
+/*****************************************************************************
+ * Record base memory used by Etherboot image
+ *****************************************************************************
+ */
+ movl %edi, _prefix_image_basemem (%ebx)
+
+/*****************************************************************************
+ * Jump to start of the image (i.e. the decompressor, or start32 if
+ * non-compressed).
+ *****************************************************************************
+ */
+ pushl $0 /* Inform start32 that exit path is 16-bit */
+ jmpl *%edi /* Jump to image */
+
+ .balign 16
+ /* Etherboot needs to be 16byte aligned or data that
+ * is virtually aligned is no longer physically aligned
+ * which is just nasty in general. 16byte alignment
+ * should be sufficient though.
+ */
+payload:
diff --git a/src/arch/i386/core/start16.lds b/src/arch/i386/core/start16.lds
new file mode 100644
index 00000000..544fc78f
--- /dev/null
+++ b/src/arch/i386/core/start16.lds
@@ -0,0 +1,8 @@
+/* When linking with an uncompressed image, these symbols are not
+ * defined so we provide them here.
+ */
+
+__decompressor_uncompressed = 0 ;
+__decompressor__start = 0 ;
+
+INCLUDE arch/i386/core/start16z.lds
diff --git a/src/arch/i386/core/start16z.lds b/src/arch/i386/core/start16z.lds
new file mode 100644
index 00000000..711bcf7b
--- /dev/null
+++ b/src/arch/i386/core/start16z.lds
@@ -0,0 +1,65 @@
+OUTPUT_FORMAT("elf32-i386", "elf32-i386", "elf32-i386")
+OUTPUT_ARCH(i386)
+
+/* Linker-generated symbols are prefixed with a double underscore.
+ * Decompressor symbols are prefixed with __decompressor_. All other
+ * symbols are the same as in the original object file, i.e. the
+ * runtime addresses.
+ */
+
+ENTRY(_start16)
+
+SECTIONS {
+ .text : {
+ *(.text)
+ }
+ .payload : {
+ __payload_start = .;
+ *(.data)
+ __payload_end = .;
+ }
+
+ /* _payload_size is the size of the binary image appended to
+ * start16, in bytes.
+ */
+ __payload_size = __payload_end - __payload_start ;
+
+ /* _size is the size of the runtime image
+ * (start32 + the C code), in bytes.
+ */
+ __size = _end - _start ;
+
+ /* _decompressor_size is the size of the decompressor, in
+ * bytes. For a non-compressed image, start16.lds sets
+ * _decompressor_uncompressed = _decompressor__start = 0.
+ */
+ __decompressor_size = __decompressor_uncompressed - __decompressor__start ;
+
+ /* image__size is the total size of the image, after
+ * decompression and including the decompressor if applicable.
+ * It is therefore the amount of memory that start16's payload
+ * needs in order to execute, in bytes.
+ */
+ __image_size = __size + __decompressor_size ;
+
+ /* Amount to add to runtime symbols to obtain the offset of
+ * that symbol within the image.
+ */
+ __offset_adjust = __decompressor_size - _start ;
+
+ /* Calculations for the stack
+ */
+ __stack_size = _estack - _stack ;
+ __offset_stack = _stack + __offset_adjust ;
+
+ /* Some symbols will be larger than 16 bits but guaranteed to
+ * be multiples of 16. We calculate them in paragraphs and
+ * export these symbols which can be used in 16-bit code
+ * without risk of overflow.
+ */
+ __image_size_pgh = ( __image_size / 16 );
+ __start_pgh = ( _start / 16 );
+ __decompressor_size_pgh = ( __decompressor_size / 16 );
+ __offset_stack_pgh = ( __offset_stack / 16 );
+}
+
diff --git a/src/arch/i386/core/start32.S b/src/arch/i386/core/start32.S
new file mode 100644
index 00000000..6dc3f203
--- /dev/null
+++ b/src/arch/i386/core/start32.S
@@ -0,0 +1,767 @@
+/* #defines because ljmp wants a number, probably gas bug */
+/* .equ KERN_CODE_SEG,_pmcs-_gdt */
+#define KERN_CODE_SEG 0x08
+ .equ KERN_DATA_SEG,_pmds-_gdt
+/* .equ REAL_CODE_SEG,_rmcs-_gdt */
+#define REAL_CODE_SEG 0x18
+ .equ REAL_DATA_SEG,_rmds-_gdt
+ .equ FLAT_CODE_SEG,_pmcs2-_gdt
+ .equ FLAT_DATA_SEG,_pmds2-_gdt
+ .equ CR0_PE,1
+#ifdef CONFIG_X86_64
+ .equ LM_CODE_SEG, _lmcs-_gdt
+ .equ LM_DATA_SEG, _lmds-_gdt
+#endif
+
+ .equ MSR_K6_EFER, 0xC0000080
+ .equ EFER_LME, 0x00000100
+ .equ X86_CR4_PAE, 0x00000020
+ .equ CR0_PG, 0x80000000
+
+#ifdef GAS291
+#define DATA32 data32;
+#define ADDR32 addr32;
+#define LJMPI(x) ljmp x
+#else
+#define DATA32 data32
+#define ADDR32 addr32
+/* newer GAS295 require #define LJMPI(x) ljmp *x */
+#define LJMPI(x) ljmp x
+#endif
+
+#define BOCHSBP xchgw %bx, %bx
+
+#include "callbacks.h"
+#define NUM_PUSHA_REGS (8)
+#define NUM_SEG_REGS (6)
+
+/*
+ * NOTE: if you write a subroutine that is called from C code (gcc/egcs),
+ * then you only have to take care of %ebx, %esi, %edi and %ebp. These
+ * registers must not be altered under any circumstance. All other registers
+ * may be clobbered without any negative side effects. If you don't follow
+ * this rule then you'll run into strange effects that only occur on some
+ * gcc versions (because the register allocator may use different registers).
+ *
+ * All the data32 prefixes for the ljmp instructions are necessary, because
+ * the assembler emits code with a relocation address of 0. This means that
+ * all destinations are initially negative, which the assembler doesn't grok,
+ * because for some reason negative numbers don't fit into 16 bits. The addr32
+ * prefixes are there for the same reasons, because otherwise the memory
+ * references are only 16 bit wide. Theoretically they are all superfluous.
+ * One last note about prefixes: the data32 prefixes on all call _real_to_prot
+ * instructions could be removed if the _real_to_prot function is changed to
+ * deal correctly with 16 bit return addresses. I tried it, but failed.
+ */
+
+/**************************************************************************
+ * START
+ *
+ * This file is no longer enterered from the top. init.S will jump to
+ * either _in_call or _rm_in_call, depending on the processor mode
+ * when init.S was entered.
+ **************************************************************************/
+ .text
+ .arch i386
+ .code32
+
+/**************************************************************************
+_IN_CALL - make a call in to Etherboot.
+**************************************************************************/
+
+/* There are two 32-bit entry points: _in_call and _in_call_far, for
+ * near calls and far calls respectively. Both should be called with
+ * flat physical addresses. They will result in a call to the C
+ * routine in_call(); see there for API details.
+ *
+ * Note that this routine makes fairly heavy use of the stack and no
+ * use of fixed data areas. This is because it must be re-entrant;
+ * there may be more than one concurrent call in to Etherboot.
+ */
+
+#define IC_OFFSET_VA_LIST_PTR ( 0 )
+#define IC_OFFSET_VA_LIST_PTR_E ( IC_OFFSET_VA_LIST_PTR + 4 )
+#define IC_OFFSET_REGISTERS ( IC_OFFSET_VA_LIST_PTR_E )
+#define IC_OFFSET_REGISTERS_E ( IC_OFFSET_REGISTERS + ( NUM_PUSHA_REGS * 4 ) )
+#define IC_OFFSET_SEG_REGS ( IC_OFFSET_REGISTERS_E )
+#define IC_OFFSET_SEG_REGS_E ( IC_OFFSET_SEG_REGS + ( NUM_SEG_REGS * 2 ) )
+#define IC_OFFSET_GDT ( IC_OFFSET_SEG_REGS_E )
+#define IC_OFFSET_GDT_E ( IC_OFFSET_GDT + 8 )
+#define IC_OFFSET_FLAGS ( IC_OFFSET_GDT_E )
+#define IC_OFFSET_FLAGS_E ( IC_OFFSET_FLAGS + 4 )
+#define IC_OFFSET_RETADDR ( IC_OFFSET_FLAGS_E )
+#define IC_OFFSET_RETADDR_E ( IC_OFFSET_RETADDR + 8 )
+#define IC_OFFSET_ORIG_STACK ( IC_OFFSET_RETADDR )
+#define IC_OFFSET_OPCODE ( IC_OFFSET_ORIG_STACK + 8 )
+#define IC_OFFSET_OPCODE_E ( IC_OFFSET_OPCODE + 4 )
+#define IC_OFFSET_VA_LIST ( IC_OFFSET_OPCODE_E )
+
+ .code32
+ .globl _in_call
+ .globl _in_call_far
+_in_call:
+ /* Expand to far return address */
+ pushl %eax /* Store %eax */
+ xorl %eax, %eax
+ movw %cs, %ax
+ xchgl %eax, 4(%esp) /* 4(%esp) = %cs, %eax = ret addr */
+ xchgl %eax, 0(%esp) /* 0(%esp) = ret addr, restore %eax */
+_in_call_far:
+ /* Store flags */
+ pushfl
+ /* Store the GDT */
+ subl $8, %esp
+ sgdt 0(%esp)
+ /* Store segment register values */
+ pushw %gs
+ pushw %fs
+ pushw %es
+ pushw %ds
+ pushw %ss
+ pushw %cs
+ /* Store general-purpose register values */
+ pushal
+ /* Replace %esp in store with physical %esp value on entry */
+ leal (IC_OFFSET_ORIG_STACK - IC_OFFSET_REGISTERS)(%esp), %eax
+ movl %eax, (IC_OFFSET_REGISTERS - IC_OFFSET_REGISTERS + 12)(%esp)
+ /* Store va_list pointer (physical address) */
+ leal (IC_OFFSET_VA_LIST - IC_OFFSET_VA_LIST_PTR_E)(%esp), %eax
+ pushl %eax
+ /* IC_OFFSET_*(%esp) are now valid */
+
+ /* Switch to virtual addresses */
+ call _phys_to_virt
+
+ /* Fixup the va_list pointer */
+ movl virt_offset, %ebp
+ subl %ebp, IC_OFFSET_VA_LIST_PTR(%esp)
+
+ /* Check opcode for EB_USE_INTERNAL_STACK flag */
+ movl IC_OFFSET_OPCODE(%esp), %eax
+ testl $EB_USE_INTERNAL_STACK, %eax
+ je 2f
+ /* Use internal stack flag set */
+ /* Check %esp is not already in internal stack range */
+ leal _stack, %esi /* %esi = bottom of internal stack */
+ leal _estack, %edi /* %edi = top of internal stack */
+ cmpl %esi, %esp
+ jb 1f
+ cmpl %edi, %esp
+ jbe 2f
+1: /* %esp not currently in internal stack range */
+ movl %esp, %esi /* %esi = original stack */
+ movl $IC_OFFSET_OPCODE_E, %ecx /* %ecx = length to transfer */
+ subl %ecx, %edi /* %edi = internal stack pos */
+ movl %edi, %esp /* = new %esp */
+ rep movsb /* Copy data to internal stack */
+2:
+
+ /* Call to C code */
+ call i386_in_call
+
+ /* Set %eax (return code from C) in registers structure on
+ * stack, so that we return it to the caller.
+ */
+ movl %eax, (IC_OFFSET_REGISTERS + 28)(%esp)
+
+ /* Calculate physical continuation address */
+ movl virt_offset, %ebp
+ movzwl (IC_OFFSET_SEG_REGS + 0)(%esp), %eax /* %cs */
+ movzwl (IC_OFFSET_SEG_REGS + 2)(%esp), %ebx /* %ss */
+ pushl %eax /* Continuation segment */
+ leal 1f(%ebp), %eax
+ pushl %eax /* Continuation offset */
+
+ /* Restore caller's GDT */
+ cli /* Temporarily disable interrupts */
+ lgdt (8+IC_OFFSET_GDT)(%esp)
+ /* Reset %ss and adjust %esp */
+ movw %bx, %ss
+ addl %ebp, %esp
+ lret /* Reload %cs:eip, flush prefetch */
+1:
+
+ /* Skip va_list ptr */
+ popl %eax
+ /* Reload general-purpose registers to be returned */
+ popal
+ /* Reload segment registers as passed in from caller */
+ popw %gs
+ popw %fs
+ popw %es
+ popw %ds
+ addl $(4+8), %esp /* Skip %cs, %ss and GDT (already reloaded) */
+ /* Restore flags (including revert of interrupt status) */
+ popfl
+
+ /* Restore physical %esp from entry. It will only be
+ * different if EB_USE_INTERNAL_STACK was specified.
+ */
+ movl ( 12 + IC_OFFSET_REGISTERS - IC_OFFSET_RETADDR )(%esp), %esp
+
+ /* Check for EB_SKIP_OPCODE */
+ pushfl
+ testl $EB_SKIP_OPCODE, 12(%esp)
+ jnz 1f
+ /* Normal return */
+ popfl
+ lret
+1: /* Return and skip opcode */
+ popfl
+ lret $4
+
+/**************************************************************************
+RELOCATE_TO - relocate etherboot to the specified address
+**************************************************************************/
+ .globl relocate_to
+relocate_to:
+ /* Save the callee save registers */
+ pushl %ebp
+ pushl %esi
+ pushl %edi
+
+ /* Compute the virtual destination address */
+ movl 16(%esp), %edi # dest
+ subl virt_offset, %edi
+
+
+ /* Compute the new value of virt_offset */
+ movl 16(%esp), %ebp # virt_offset
+ subl $_text, %ebp
+
+ /* Fixup the gdt */
+ pushl $_pmcs
+ pushl %ebp # virt_offset
+ call set_seg_base
+ addl $8, %esp
+
+ /* Fixup gdtarg */
+ leal _gdt(%ebp), %eax
+ movl %eax, gdtarg +2
+
+ /* Fixup virt_offset */
+ movl %ebp, virt_offset
+
+ /* Load the move parameters */
+ movl $_text, %esi
+ movl $_end, %ecx
+ subl %esi, %ecx
+
+ /* Move etherboot uses %esi, %edi, %ecx */
+ rep
+ movsb
+
+ /* Reload the gdt */
+ cs
+ lgdt gdtarg
+
+ /* Reload %cs */
+ ljmp $KERN_CODE_SEG, $1f
+1:
+ /* reload other segment registers */
+ movl $KERN_DATA_SEG, %eax
+ movl %eax,%ds
+ movl %eax,%es
+ movl %eax,%ss
+ movl %eax,%fs
+ movl %eax,%gs
+
+ /* Restore the callee save registers */
+ popl %edi
+ popl %esi
+ popl %ebp
+
+ /* return */
+ ret
+
+/**************************************************************************
+XSTART32 - Transfer control to the kernel just loaded
+**************************************************************************/
+ .globl xstart32
+xstart32:
+ /* Save the callee save registers */
+ movl %ebp, os_regs + 32
+ movl %esi, os_regs + 36
+ movl %edi, os_regs + 40
+ movl %ebx, os_regs + 44
+
+ /* save the return address */
+ popl %eax
+ movl %eax, os_regs + 48
+
+ /* save the stack pointer */
+ movl %esp, os_regs + 52
+
+ /* Get the new destination address */
+ popl %ecx
+
+ /* Store the physical address of xend on the stack */
+ movl $xend32, %ebx
+ addl virt_offset, %ebx
+ pushl %ebx
+
+ /* Store the destination address on the stack */
+ pushl $FLAT_CODE_SEG
+ pushl %ecx
+
+ /* Cache virt_offset */
+ movl virt_offset, %ebp
+
+ /* Switch to using physical addresses */
+ call _virt_to_phys
+
+ /* Save the target stack pointer */
+ movl %esp, os_regs + 12(%ebp)
+ leal os_regs(%ebp), %esp
+
+ /* Store the pointer to os_regs */
+ movl %esp, os_regs_ptr(%ebp)
+
+ /* Load my new registers */
+ popal
+ movl (-32 + 12)(%esp), %esp
+
+ /* Jump to the new kernel
+ * The lret switches to a flat code segment
+ */
+ lret
+
+ .balign 4
+ .globl xend32
+xend32:
+ /* Fixup %eflags */
+ nop
+ cli
+ cld
+
+ /* Load %esp with &os_regs + virt_offset */
+ .byte 0xbc /* movl $0, %esp */
+os_regs_ptr:
+ .long 0
+
+ /* Save the result registers */
+ addl $32, %esp
+ pushal
+
+ /* Compute virt_offset */
+ movl %esp, %ebp
+ subl $os_regs, %ebp
+
+ /* Load the stack pointer */
+ movl 52(%esp), %esp
+
+ /* Enable the virtual addresses */
+ leal _phys_to_virt(%ebp), %eax
+ call *%eax
+
+ /* Restore the callee save registers */
+ movl os_regs + 32, %ebp
+ movl os_regs + 36, %esi
+ movl os_regs + 40, %edi
+ movl os_regs + 44, %ebx
+ movl os_regs + 48, %edx
+ movl os_regs + 52, %esp
+
+ /* Get the C return value */
+ movl os_regs + 28, %eax
+
+ jmpl *%edx
+
+#ifdef CONFIG_X86_64
+ .arch sledgehammer
+/**************************************************************************
+XSTART_lm - Transfer control to the kernel just loaded in long mode
+**************************************************************************/
+ .globl xstart_lm
+xstart_lm:
+ /* Save the callee save registers */
+ pushl %ebp
+ pushl %esi
+ pushl %edi
+ pushl %ebx
+
+ /* Cache virt_offset && (virt_offset & 0xfffff000) */
+ movl virt_offset, %ebp
+ movl %ebp, %ebx
+ andl $0xfffff000, %ebx
+
+ /* Switch to using physical addresses */
+ call _virt_to_phys
+
+ /* Initialize the page tables */
+ /* Level 4 */
+ leal 0x23 + pgt_level3(%ebx), %eax
+ leal pgt_level4(%ebx), %edi
+ movl %eax, (%edi)
+
+ /* Level 3 */
+ leal 0x23 + pgt_level2(%ebx), %eax
+ leal pgt_level3(%ebx), %edi
+ movl %eax, 0x00(%edi)
+ addl $4096, %eax
+ movl %eax, 0x08(%edi)
+ addl $4096, %eax
+ movl %eax, 0x10(%edi)
+ addl $4096, %eax
+ movl %eax, 0x18(%edi)
+
+ /* Level 2 */
+ movl $0xe3, %eax
+ leal pgt_level2(%ebx), %edi
+ leal 16384(%edi), %esi
+pgt_level2_loop:
+ movl %eax, (%edi)
+ addl $8, %edi
+ addl $0x200000, %eax
+ cmp %esi, %edi
+ jne pgt_level2_loop
+
+ /* Point at the x86_64 page tables */
+ leal pgt_level4(%ebx), %edi
+ movl %edi, %cr3
+
+
+ /* Setup for the return from 64bit mode */
+ /* 64bit align the stack */
+ movl %esp, %ebx /* original stack pointer + 16 */
+ andl $0xfffffff8, %esp
+
+ /* Save original stack pointer + 16 */
+ pushl %ebx
+
+ /* Save virt_offset */
+ pushl %ebp
+
+ /* Setup for the jmp to 64bit long mode */
+ leal start_lm(%ebp), %eax
+ movl %eax, 0x00 + start_lm_addr(%ebp)
+ movl $LM_CODE_SEG, %eax
+ movl %eax, 0x04 + start_lm_addr(%ebp)
+
+ /* Setup for the jump out of 64bit long mode */
+ leal end_lm(%ebp), %eax
+ movl %eax, 0x00 + end_lm_addr(%ebp)
+ movl $FLAT_CODE_SEG, %eax
+ movl %eax, 0x04 + end_lm_addr(%ebp)
+
+ /* Enable PAE mode */
+ movl %cr4, %eax
+ orl $X86_CR4_PAE, %eax
+ movl %eax, %cr4
+
+ /* Enable long mode */
+ movl $MSR_K6_EFER, %ecx
+ rdmsr
+ orl $EFER_LME, %eax
+ wrmsr
+
+ /* Start paging, entering 32bit compatiblity mode */
+ movl %cr0, %eax
+ orl $CR0_PG, %eax
+ movl %eax, %cr0
+
+ /* Enter 64bit long mode */
+ ljmp *start_lm_addr(%ebp)
+ .code64
+start_lm:
+ /* Load 64bit data segments */
+ movl $LM_DATA_SEG, %eax
+ movl %eax, %ds
+ movl %eax, %es
+ movl %eax, %ss
+
+ andq $0xffffffff, %rbx
+ /* Get the address to jump to */
+ movl 20(%rbx), %edx
+ andq $0xffffffff, %rdx
+
+ /* Get the argument pointer */
+ movl 24(%rbx), %ebx
+ andq $0xffffffff, %rbx
+
+ /* Jump to the 64bit code */
+ call *%rdx
+
+ /* Preserve the result */
+ movl %eax, %edx
+
+ /* Fixup %eflags */
+ cli
+ cld
+
+ /* Switch to 32bit compatibility mode */
+ ljmp *end_lm_addr(%rip)
+
+ .code32
+end_lm:
+ /* Disable paging */
+ movl %cr0, %eax
+ andl $~CR0_PG, %eax
+ movl %eax, %cr0
+
+ /* Disable long mode */
+ movl $MSR_K6_EFER, %ecx
+ rdmsr
+ andl $~EFER_LME, %eax
+ wrmsr
+
+ /* Disable PAE */
+ movl %cr4, %eax
+ andl $~X86_CR4_PAE, %eax
+ movl %eax, %cr4
+
+ /* Compute virt_offset */
+ popl %ebp
+
+ /* Compute the original stack pointer + 16 */
+ popl %ebx
+ movl %ebx, %esp
+
+ /* Enable the virtual addresses */
+ leal _phys_to_virt(%ebp), %eax
+ call *%eax
+
+ /* Restore the callee save registers */
+ popl %ebx
+ popl %esi
+ popl %edi
+ popl %ebp
+
+ /* Get the C return value */
+ movl %edx, %eax
+
+ /* Return */
+ ret
+
+ .arch i386
+#endif /* CONFIG_X86_64 */
+
+/**************************************************************************
+SETJMP - Save stack context for non-local goto
+**************************************************************************/
+ .globl setjmp
+setjmp:
+ movl 4(%esp),%ecx /* jmpbuf */
+ movl 0(%esp),%edx /* return address */
+ movl %edx,0(%ecx)
+ movl %ebx,4(%ecx)
+ movl %esp,8(%ecx)
+ movl %ebp,12(%ecx)
+ movl %esi,16(%ecx)
+ movl %edi,20(%ecx)
+ movl $0,%eax
+ ret
+
+/**************************************************************************
+LONGJMP - Non-local jump to a saved stack context
+**************************************************************************/
+ .globl longjmp
+longjmp:
+ movl 4(%esp),%edx /* jumpbuf */
+ movl 8(%esp),%eax /* result */
+ movl 0(%edx),%ecx
+ movl 4(%edx),%ebx
+ movl 8(%edx),%esp
+ movl 12(%edx),%ebp
+ movl 16(%edx),%esi
+ movl 20(%edx),%edi
+ cmpl $0,%eax
+ jne 1f
+ movl $1,%eax
+1: movl %ecx,0(%esp)
+ ret
+
+/**************************************************************************
+_VIRT_TO_PHYS - Transition from virtual to physical addresses
+ Preserves all preservable registers and flags
+**************************************************************************/
+ .globl _virt_to_phys
+_virt_to_phys:
+ pushfl
+ pushl %ebp
+ pushl %eax
+ movl virt_offset, %ebp /* Load virt_offset */
+ addl %ebp, 12(%esp) /* Adjust the return address */
+
+ /* reload the code segment */
+ pushl $FLAT_CODE_SEG
+ leal 1f(%ebp), %eax
+ pushl %eax
+ lret
+
+1:
+ /* reload other segment registers */
+ movl $FLAT_DATA_SEG, %eax
+ movl %eax, %ds
+ movl %eax, %es
+ movl %eax, %ss
+ addl %ebp, %esp /* Adjust the stack pointer */
+ movl %eax, %fs
+ movl %eax, %gs
+
+ popl %eax
+ popl %ebp
+ popfl
+ ret
+
+
+/**************************************************************************
+_PHYS_TO_VIRT - Transition from using physical to virtual addresses
+ Preserves all preservable registers and flags
+**************************************************************************/
+ .globl _phys_to_virt
+_phys_to_virt:
+ pushfl
+ pushl %ebp
+ pushl %eax
+
+ call 1f
+1: popl %ebp
+ subl $1b, %ebp
+ movl %ebp, virt_offset(%ebp)
+
+ /* Fixup the gdt */
+ leal _pmcs(%ebp), %eax
+ pushl %eax
+ pushl %ebp
+ call set_seg_base
+ addl $8, %esp
+
+ /* Fixup gdtarg */
+ leal _gdt(%ebp), %eax
+ movl %eax, (gdtarg+2)(%ebp)
+
+ /* Load the global descriptor table */
+ cli
+ lgdt %cs:gdtarg(%ebp)
+ ljmp $KERN_CODE_SEG, $1f
+1:
+ /* reload other segment regsters */
+ movl $KERN_DATA_SEG, %eax
+ movl %eax, %ds
+ movl %eax, %es
+ movl %eax, %ss
+ subl %ebp, %esp /* Adjust the stack pointer */
+ movl %eax, %fs
+ movl %eax, %gs
+
+ subl %ebp, 12(%esp) /* Adjust the return address */
+ popl %eax
+ popl %ebp
+ popfl
+ ret
+
+
+/**************************************************************************
+SET_SEG_BASE - Set the base address of a segment register
+**************************************************************************/
+ .globl set_seg_base
+set_seg_base:
+ pushl %eax
+ pushl %ebx
+ movl 12(%esp), %eax /* %eax = base address */
+ movl 16(%esp), %ebx /* %ebx = &code_descriptor */
+ movw %ax, (0+2)(%ebx) /* CS base bits 0-15 */
+ movw %ax, (8+2)(%ebx) /* DS base bits 0-15 */
+ shrl $16, %eax
+ movb %al, (0+4)(%ebx) /* CS base bits 16-23 */
+ movb %al, (8+4)(%ebx) /* DS base bits 16-23 */
+ movb %ah, (0+7)(%ebx) /* CS base bits 24-31 */
+ movb %ah, (8+7)(%ebx) /* DS base bits 24-31 */
+ popl %ebx
+ popl %eax
+ ret
+
+/**************************************************************************
+GLOBAL DESCRIPTOR TABLE
+**************************************************************************/
+ .data
+ .align 4
+
+ .globl _gdt
+ .globl gdtarg
+_gdt:
+gdtarg:
+ .word _gdt_end - _gdt - 1 /* limit */
+ .long _gdt /* addr */
+ .word 0
+
+ .globl _pmcs
+_pmcs:
+ /* 32 bit protected mode code segment */
+ .word 0xffff,0
+ .byte 0,0x9f,0xcf,0
+
+_pmds:
+ /* 32 bit protected mode data segment */
+ .word 0xffff,0
+ .byte 0,0x93,0xcf,0
+
+_rmcs:
+ /* 16 bit real mode code segment */
+ .word 0xffff,(0&0xffff)
+ .byte (0>>16),0x9b,0x00,(0>>24)
+
+_rmds:
+ /* 16 bit real mode data segment */
+ .word 0xffff,(0&0xffff)
+ .byte (0>>16),0x93,0x00,(0>>24)
+
+_pmcs2:
+ /* 32 bit protected mode code segment, base 0 */
+ .word 0xffff,0
+ .byte 0,0x9f,0xcf,0
+
+_pmds2:
+ /* 32 bit protected mode data segment, base 0 */
+ .word 0xffff,0
+ .byte 0,0x93,0xcf,0
+
+#ifdef CONFIG_X86_64
+_lmcs:
+ /* 64bit long mode code segment, base 0 */
+ .word 0xffff, 0
+ .byte 0x00, 0x9f, 0xaf , 0x00
+_lmds:
+ /* 64bit long mode data segment, base 0 */
+ .word 0xffff, 0
+ .byte 0x00, 0x93, 0xcf, 0x00
+#endif
+_gdt_end:
+
+ /* The initial register contents */
+ .balign 4
+ .globl initial_regs
+initial_regs:
+ .fill 8, 4, 0
+
+ /* The virtual address offset */
+ .globl virt_offset
+virt_offset:
+ .long 0
+
+ .section ".stack"
+ .p2align 3
+ /* allocate a 4K stack in the stack segment */
+ .globl _stack
+_stack:
+ .space 4096
+ .globl _estack
+_estack:
+#ifdef CONFIG_X86_64
+ .section ".bss"
+ .p2align 12
+ /* Include a dummy space in case we are loaded badly aligned */
+ .space 4096
+ /* Reserve enough space for a page table convering 4GB with 2MB pages */
+pgt_level4:
+ .space 4096
+pgt_level3:
+ .space 4096
+pgt_level2:
+ .space 16384
+start_lm_addr:
+ .space 8
+end_lm_addr:
+ .space 8
+#endif
diff --git a/src/arch/i386/core/tagged_loader.c b/src/arch/i386/core/tagged_loader.c
new file mode 100644
index 00000000..9c6d6c25
--- /dev/null
+++ b/src/arch/i386/core/tagged_loader.c
@@ -0,0 +1,201 @@
+#include "realmode.h"
+#include "segoff.h"
+
+struct segheader
+{
+ unsigned char length;
+ unsigned char vendortag;
+ unsigned char reserved;
+ unsigned char flags;
+ unsigned long loadaddr;
+ unsigned long imglength;
+ unsigned long memlength;
+};
+
+struct imgheader
+{
+ unsigned long magic;
+ unsigned long length; /* and flags */
+ union
+ {
+ segoff_t segoff;
+ unsigned long location;
+ } u;
+ unsigned long execaddr;
+};
+
+/* Keep all context about loaded image in one place */
+static struct tagged_context
+{
+ struct imgheader img; /* copy of header */
+ unsigned long linlocation; /* addr of header */
+ unsigned long last0, last1; /* of prev segment */
+ unsigned long segaddr, seglen; /* of current segment */
+ unsigned char segflags;
+ unsigned char first;
+ unsigned long curaddr;
+} tctx;
+
+#define TAGGED_PROGRAM_RETURNS (tctx.img.length & 0x00000100) /* bit 8 */
+#define LINEAR_EXEC_ADDR (tctx.img.length & 0x80000000) /* bit 31 */
+
+static sector_t tagged_download(unsigned char *data, unsigned int len, int eof);
+void xstart16 (unsigned long execaddr, segoff_t location,
+ void *bootp);
+
+static inline os_download_t tagged_probe(unsigned char *data, unsigned int len)
+{
+ struct segheader *sh;
+ unsigned long loc;
+ if (*((uint32_t *)data) != 0x1B031336L) {
+ return 0;
+ }
+ printf("(NBI)");
+ /* If we don't have enough data give up */
+ if (len < 512)
+ return dead_download;
+ /* Zero all context info */
+ memset(&tctx, 0, sizeof(tctx));
+ /* Copy first 4 longwords */
+ memcpy(&tctx.img, data, sizeof(tctx.img));
+ /* Memory location where we are supposed to save it */
+ tctx.segaddr = tctx.linlocation =
+ ((tctx.img.u.segoff.segment) << 4) + tctx.img.u.segoff.offset;
+ if (!prep_segment(tctx.segaddr, tctx.segaddr + 512, tctx.segaddr + 512,
+ 0, 512)) {
+ return dead_download;
+ }
+ /* Now verify the segments we are about to load */
+ loc = 512;
+ for(sh = (struct segheader *)(data
+ + ((tctx.img.length & 0x0F) << 2)
+ + ((tctx.img.length & 0xF0) >> 2) );
+ (sh->length > 0) && ((unsigned char *)sh < data + 512);
+ sh = (struct segheader *)((unsigned char *)sh
+ + ((sh->length & 0x0f) << 2) + ((sh->length & 0xf0) >> 2)) ) {
+ if (!prep_segment(
+ sh->loadaddr,
+ sh->loadaddr + sh->imglength,
+ sh->loadaddr + sh->imglength,
+ loc, loc + sh->imglength)) {
+ return dead_download;
+ }
+ loc = loc + sh->imglength;
+ if (sh->flags & 0x04)
+ break;
+ }
+ if (!(sh->flags & 0x04))
+ return dead_download;
+ /* Grab a copy */
+ memcpy(phys_to_virt(tctx.segaddr), data, 512);
+ /* Advance to first segment descriptor */
+ tctx.segaddr += ((tctx.img.length & 0x0F) << 2)
+ + ((tctx.img.length & 0xF0) >> 2);
+ /* Remember to skip the first 512 data bytes */
+ tctx.first = 1;
+
+ return tagged_download;
+
+}
+static sector_t tagged_download(unsigned char *data, unsigned int len, int eof)
+{
+ int i;
+
+ if (tctx.first) {
+ tctx.first = 0;
+ if (len > 512) {
+ len -= 512;
+ data += 512;
+ /* and fall through to deal with rest of block */
+ } else
+ return 0;
+ }
+ for (;;) {
+ if (len == 0) /* Detect truncated files */
+ eof = 0;
+ while (tctx.seglen == 0) {
+ struct segheader sh;
+ if (tctx.segflags & 0x04) {
+ done(1);
+ if (LINEAR_EXEC_ADDR) {
+ int result;
+ /* no gateA20_unset for PM call */
+ result = xstart32(tctx.img.execaddr,
+ virt_to_phys(&loaderinfo),
+ tctx.linlocation,
+ virt_to_phys(BOOTP_DATA_ADDR));
+ printf("Secondary program returned %d\n",
+ result);
+ if (!TAGGED_PROGRAM_RETURNS) {
+ /* We shouldn't have returned */
+ result = -2;
+ }
+ if (result == 0)
+ result = -2;
+ longjmp(restart_etherboot, result);
+
+ } else {
+ gateA20_unset();
+ xstart16(tctx.img.execaddr,
+ tctx.img.u.segoff,
+ BOOTP_DATA_ADDR);
+ longjmp(restart_etherboot, -2);
+ }
+ }
+ sh = *((struct segheader *)phys_to_virt(tctx.segaddr));
+ tctx.seglen = sh.imglength;
+ if ((tctx.segflags = sh.flags & 0x03) == 0)
+ tctx.curaddr = sh.loadaddr;
+ else if (tctx.segflags == 0x01)
+ tctx.curaddr = tctx.last1 + sh.loadaddr;
+ else if (tctx.segflags == 0x02)
+ tctx.curaddr = (Address)(meminfo.memsize * 1024L
+ + 0x100000L)
+ - sh.loadaddr;
+ else
+ tctx.curaddr = tctx.last0 - sh.loadaddr;
+ tctx.last1 = (tctx.last0 = tctx.curaddr) + sh.memlength;
+ tctx.segflags = sh.flags;
+ tctx.segaddr += ((sh.length & 0x0F) << 2)
+ + ((sh.length & 0xF0) >> 2);
+ /* Avoid lock-up */
+ if ( sh.length == 0 ) longjmp(restart_etherboot, -2);
+ }
+ if ((len <= 0) && !eof)
+ break;
+ i = (tctx.seglen > len) ? len : tctx.seglen;
+ memcpy(phys_to_virt(tctx.curaddr), data, i);
+ tctx.seglen -= i;
+ tctx.curaddr += i;
+ len -= i;
+ data += i;
+ }
+ return 0;
+}
+
+void xstart16 (unsigned long execaddr, segoff_t location,
+ void *bootp) {
+ struct {
+ segoff_t execaddr;
+ segoff_t location;
+ segoff_t bootp;
+ } PACKED in_stack;
+
+ /* AFAICT, execaddr is actually already a segment:offset */
+ *((unsigned long *)&in_stack.execaddr) = execaddr;
+ in_stack.location = location;
+ in_stack.bootp.segment = SEGMENT(bootp);
+ in_stack.bootp.offset = OFFSET(bootp);
+
+ RM_FRAGMENT(rm_xstart16,
+ "popl %eax\n\t" /* Calculated lcall */
+ "pushw %cs\n\t"
+ "call 1f\n1:\tpopw %bp\n\t"
+ "leaw (2f-1b)(%bp), %bx\n\t"
+ "pushw %bx\n\t"
+ "pushl %eax\n\t"
+ "lret\n2:\n\t"
+ );
+
+ real_call ( rm_xstart16, &in_stack, NULL );
+}
diff --git a/src/arch/i386/core/video_subr.c b/src/arch/i386/core/video_subr.c
new file mode 100644
index 00000000..dccdd97c
--- /dev/null
+++ b/src/arch/i386/core/video_subr.c
@@ -0,0 +1,94 @@
+/*
+ *
+ * modified from linuxbios code
+ * by Cai Qiang <rimy2000@hotmail.com>
+ *
+ */
+
+#ifdef CONSOLE_DIRECT_VGA
+
+#include <etherboot.h>
+#include <vga.h>
+
+static char *vidmem; /* The video buffer */
+static int video_line, video_col;
+
+#define VIDBUFFER 0xB8000
+
+static void memsetw(void *s, int c, unsigned int n)
+{
+ int i;
+ u16 *ss = (u16 *) s;
+
+ for (i = 0; i < n; i++) {
+ ss[i] = ( u16 ) c;
+ }
+}
+
+void video_init(void)
+{
+ static int inited=0;
+
+ vidmem = (unsigned char *)phys_to_virt(VIDBUFFER);
+
+ if (!inited) {
+ video_line = 0;
+ video_col = 0;
+
+ memsetw(vidmem, VGA_ATTR_CLR_WHT, 2*1024); //
+
+ inited=1;
+ }
+}
+
+static void video_scroll(void)
+{
+ int i;
+
+ memcpy(vidmem, vidmem + COLS * 2, (LINES - 1) * COLS * 2);
+ for (i = (LINES - 1) * COLS * 2; i < LINES * COLS * 2; i += 2)
+ vidmem[i] = ' ';
+}
+
+void vga_putc(unsigned char byte)
+{
+ if (byte == '\n') {
+ video_line++;
+ video_col = 0;
+
+ } else if (byte == '\r') {
+ video_col = 0;
+
+ } else if (byte == '\b') {
+ video_col--;
+
+ } else if (byte == '\t') {
+ video_col += 4;
+
+ } else if (byte == '\a') {
+ //beep
+ //beep(500);
+
+ } else {
+ vidmem[((video_col + (video_line *COLS)) * 2)] = byte;
+ vidmem[((video_col + (video_line *COLS)) * 2) +1] = VGA_ATTR_CLR_WHT;
+ video_col++;
+ }
+ if (video_col < 0) {
+ video_col = 0;
+ }
+ if (video_col >= COLS) {
+ video_line++;
+ video_col = 0;
+ }
+ if (video_line >= LINES) {
+ video_scroll();
+ video_line--;
+ }
+ // move the cursor
+ write_crtc((video_col + (video_line *COLS)) >> 8, CRTC_CURSOR_HI);
+ write_crtc((video_col + (video_line *COLS)) & 0x0ff, CRTC_CURSOR_LO);
+}
+
+#endif
+
diff --git a/src/arch/i386/core/wince_loader.c b/src/arch/i386/core/wince_loader.c
new file mode 100644
index 00000000..f452b659
--- /dev/null
+++ b/src/arch/i386/core/wince_loader.c
@@ -0,0 +1,273 @@
+#define LOAD_DEBUG 0
+
+static int get_x_header(unsigned char *data, unsigned long now);
+static void jump_2ep();
+static unsigned char ce_signature[] = {'B', '0', '0', '0', 'F', 'F', '\n',};
+static char ** ep;
+
+#define BOOT_ARG_PTR_LOCATION 0x001FFFFC
+
+typedef struct _BOOT_ARGS{
+ unsigned char ucVideoMode;
+ unsigned char ucComPort;
+ unsigned char ucBaudDivisor;
+ unsigned char ucPCIConfigType;
+
+ unsigned long dwSig;
+ #define BOOTARG_SIG 0x544F4F42
+ unsigned long dwLen;
+
+ unsigned char ucLoaderFlags;
+ unsigned char ucEshellFlags;
+ unsigned char ucEdbgAdapterType;
+ unsigned char ucEdbgIRQ;
+
+ unsigned long dwEdbgBaseAddr;
+ unsigned long dwEdbgDebugZone;
+ unsigned long dwDHCPLeaseTime;
+ unsigned long dwEdbgFlags;
+
+ unsigned long dwEBootFlag;
+ unsigned long dwEBootAddr;
+ unsigned long dwLaunchAddr;
+
+ unsigned long pvFlatFrameBuffer;
+ unsigned short vesaMode;
+ unsigned short cxDisplayScreen;
+ unsigned short cyDisplayScreen;
+ unsigned short cxPhysicalScreen;
+ unsigned short cyPhysicalScreen;
+ unsigned short cbScanLineLength;
+ unsigned short bppScreen;
+
+ unsigned char RedMaskSize;
+ unsigned char REdMaskPosition;
+ unsigned char GreenMaskSize;
+ unsigned char GreenMaskPosition;
+ unsigned char BlueMaskSize;
+ unsigned char BlueMaskPosition;
+} BOOT_ARGS;
+
+BOOT_ARGS BootArgs;
+
+static struct segment_info{
+ unsigned long addr; // Section Address
+ unsigned long size; // Section Size
+ unsigned long checksum; // Section CheckSum
+} X;
+
+#define PSIZE (1500) //Max Packet Size
+#define DSIZE (PSIZE+12)
+static unsigned long dbuffer_available =0;
+static unsigned long not_loadin =0;
+static unsigned long d_now =0;
+
+unsigned long entry;
+static unsigned long ce_curaddr;
+
+
+static sector_t ce_loader(unsigned char *data, unsigned int len, int eof);
+static os_download_t wince_probe(unsigned char *data, unsigned int len)
+{
+ if (strncmp(ce_signature, data, sizeof(ce_signature)) != 0) {
+ return 0;
+ }
+ printf("(WINCE)");
+ return ce_loader;
+}
+
+static sector_t ce_loader(unsigned char *data, unsigned int len, int eof)
+{
+ static unsigned char dbuffer[DSIZE];
+ int this_write = 0;
+ static int firsttime = 1;
+
+ /*
+ * new packet in, we have to
+ * [1] copy data to dbuffer,
+ *
+ * update...
+ * [2] dbuffer_available
+ */
+ memcpy( (dbuffer+dbuffer_available), data, len); //[1]
+ dbuffer_available += len; // [2]
+ len = 0;
+
+ d_now = 0;
+
+#if 0
+ printf("dbuffer_available =%ld \n", dbuffer_available);
+#endif
+
+ if (firsttime)
+ {
+ d_now = sizeof(ce_signature);
+ printf("String Physical Address = %lx \n",
+ *(unsigned long *)(dbuffer+d_now));
+
+ d_now += sizeof(unsigned long);
+ printf("Image Size = %ld [%lx]\n",
+ *(unsigned long *)(dbuffer+d_now),
+ *(unsigned long *)(dbuffer+d_now));
+
+ d_now += sizeof(unsigned long);
+ dbuffer_available -= d_now;
+
+ d_now = (unsigned long)get_x_header(dbuffer, d_now);
+ firsttime = 0;
+ }
+
+ if (not_loadin == 0)
+ {
+ d_now = get_x_header(dbuffer, d_now);
+ }
+
+ while ( not_loadin > 0 )
+ {
+ /* dbuffer do not have enough data to loading, copy all */
+#if LOAD_DEBUG
+ printf("[0] not_loadin = [%ld], dbuffer_available = [%ld] \n",
+ not_loadin, dbuffer_available);
+ printf("[0] d_now = [%ld] \n", d_now);
+#endif
+
+ if( dbuffer_available <= not_loadin)
+ {
+ this_write = dbuffer_available ;
+ memcpy(phys_to_virt(ce_curaddr), (dbuffer+d_now), this_write );
+ ce_curaddr += this_write;
+ not_loadin -= this_write;
+
+ /* reset index and available in the dbuffer */
+ dbuffer_available = 0;
+ d_now = 0;
+#if LOAD_DEBUG
+ printf("[1] not_loadin = [%ld], dbuffer_available = [%ld] \n",
+ not_loadin, dbuffer_available);
+ printf("[1] d_now = [%ld], this_write = [%d] \n",
+ d_now, this_write);
+#endif
+
+ // get the next packet...
+ return (0);
+ }
+
+ /* dbuffer have more data then loading ... , copy partital.... */
+ else
+ {
+ this_write = not_loadin;
+ memcpy(phys_to_virt(ce_curaddr), (dbuffer+d_now), this_write);
+ ce_curaddr += this_write;
+ not_loadin = 0;
+
+ /* reset index and available in the dbuffer */
+ dbuffer_available -= this_write;
+ d_now += this_write;
+#if LOAD_DEBUG
+ printf("[2] not_loadin = [%ld], dbuffer_available = [%ld] \n",
+ not_loadin, dbuffer_available);
+ printf("[2] d_now = [%ld], this_write = [%d] \n\n",
+ d_now, this_write);
+#endif
+
+ /* dbuffer not empty, proceed processing... */
+
+ // don't have enough data to get_x_header..
+ if ( dbuffer_available < (sizeof(unsigned long) * 3) )
+ {
+// printf("we don't have enough data remaining to call get_x. \n");
+ memcpy( (dbuffer+0), (dbuffer+d_now), dbuffer_available);
+ return (0);
+ }
+ else
+ {
+#if LOAD_DEBUG
+ printf("with remaining data to call get_x \n");
+ printf("dbuffer available = %ld , d_now = %ld\n",
+ dbuffer_available, d_now);
+#endif
+ d_now = get_x_header(dbuffer, d_now);
+ }
+ }
+ }
+ return (0);
+}
+
+static int get_x_header(unsigned char *dbuffer, unsigned long now)
+{
+ X.addr = *(unsigned long *)(dbuffer + now);
+ X.size = *(unsigned long *)(dbuffer + now + sizeof(unsigned long));
+ X.checksum = *(unsigned long *)(dbuffer + now + sizeof(unsigned long)*2);
+
+ if (X.addr == 0)
+ {
+ entry = X.size;
+ done(1);
+ printf("Entry Point Address = [%lx] \n", entry);
+ jump_2ep();
+ }
+
+ if (!prep_segment(X.addr, X.addr + X.size, X.addr + X.size, 0, 0)) {
+ longjmp(restart_etherboot, -2);
+ }
+
+ ce_curaddr = X.addr;
+ now += sizeof(unsigned long)*3;
+
+ /* re-calculate dbuffer available... */
+ dbuffer_available -= sizeof(unsigned long)*3;
+
+ /* reset index of this section */
+ not_loadin = X.size;
+
+#if 1
+ printf("\n");
+ printf("\t Section Address = [%lx] \n", X.addr);
+ printf("\t Size = %d [%lx]\n", X.size, X.size);
+ printf("\t Checksum = %ld [%lx]\n", X.checksum, X.checksum);
+#endif
+#if LOAD_DEBUG
+ printf("____________________________________________\n");
+ printf("\t dbuffer_now = %ld \n", now);
+ printf("\t dbuffer available = %ld \n", dbuffer_available);
+ printf("\t not_loadin = %ld \n", not_loadin);
+#endif
+
+ return now;
+}
+
+static void jump_2ep()
+{
+ BootArgs.ucVideoMode = 1;
+ BootArgs.ucComPort = 1;
+ BootArgs.ucBaudDivisor = 1;
+ BootArgs.ucPCIConfigType = 1; // do not fill with 0
+
+ BootArgs.dwSig = BOOTARG_SIG;
+ BootArgs.dwLen = sizeof(BootArgs);
+
+ if(BootArgs.ucVideoMode == 0)
+ {
+ BootArgs.cxDisplayScreen = 640;
+ BootArgs.cyDisplayScreen = 480;
+ BootArgs.cxPhysicalScreen = 640;
+ BootArgs.cyPhysicalScreen = 480;
+ BootArgs.bppScreen = 16;
+ BootArgs.cbScanLineLength = 1024;
+ BootArgs.pvFlatFrameBuffer = 0x800a0000; // ollie say 0x98000000
+ }
+ else if(BootArgs.ucVideoMode != 0xFF)
+ {
+ BootArgs.cxDisplayScreen = 0;
+ BootArgs.cyDisplayScreen = 0;
+ BootArgs.cxPhysicalScreen = 0;
+ BootArgs.cyPhysicalScreen = 0;
+ BootArgs.bppScreen = 0;
+ BootArgs.cbScanLineLength = 0;
+ BootArgs.pvFlatFrameBuffer = 0;
+ }
+
+ ep = phys_to_virt(BOOT_ARG_PTR_LOCATION);
+ *ep= virt_to_phys(&BootArgs);
+ xstart32(entry);
+}
diff --git a/src/arch/i386/drivers/net/undi.c b/src/arch/i386/drivers/net/undi.c
new file mode 100644
index 00000000..084fc18a
--- /dev/null
+++ b/src/arch/i386/drivers/net/undi.c
@@ -0,0 +1,1458 @@
+/**************************************************************************
+Etherboot - BOOTP/TFTP Bootstrap Program
+UNDI NIC driver for Etherboot
+
+This file Copyright (C) 2003 Michael Brown <mbrown@fensystems.co.uk>
+of Fen Systems Ltd. (http://www.fensystems.co.uk/). All rights
+reserved.
+
+$Id$
+***************************************************************************/
+
+/*
+ * 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; either version 2, or (at
+ * your option) any later version.
+ */
+
+#ifdef PCBIOS
+
+/* to get some global routines like printf */
+#include "etherboot.h"
+/* to get the interface to the body of the program */
+#include "nic.h"
+/* to get the PCI support functions, if this is a PCI NIC */
+#include "pci.h"
+/* UNDI and PXE defines. Includes pxe.h. */
+#include "undi.h"
+/* 8259 PIC defines */
+#include "pic8259.h"
+/* Real-mode calls */
+#include "realmode.h"
+/* E820 map mangler */
+#include "hidemem.h"
+
+/* NIC specific static variables go here */
+static undi_t undi = {
+ .pnp_bios = NULL,
+ .rom = NULL,
+ .undi_rom_id = NULL,
+ .pxe = NULL,
+ .pxs = NULL,
+ .xmit_data = NULL,
+ .base_mem_data = NULL,
+ .driver_code = NULL,
+ .driver_code_size = 0,
+ .driver_data = NULL,
+ .driver_data_size = 0,
+ .xmit_buffer = NULL,
+ .prestarted = 0,
+ .started = 0,
+ .initialized = 0,
+ .opened = 0,
+ .pci = { 0, 0, 0, NULL, 0, 0, 0, 0, 0, 0, 0, NULL },
+ .irq = IRQ_NONE
+};
+
+/* Function prototypes */
+static int allocate_base_mem_data ( void );
+static int free_base_mem_data ( void );
+static int eb_pxenv_undi_shutdown ( void );
+static int eb_pxenv_stop_undi ( void );
+static int undi_unload_base_code ( void );
+static int undi_full_shutdown ( void );
+
+/* Trivial/nontrivial IRQ handler selection */
+#ifdef UNDI_NONTRIVIAL_IRQ
+static void nontrivial_irq_handler ( void );
+static void nontrivial_irq_handler_end ( void );
+static int install_nontrivial_irq_handler ( irq_t irq );
+static int remove_nontrivial_irq_handler ( irq_t irq );
+static int nontrivial_irq_triggered ( irq_t irq );
+static int copy_nontrivial_irq_handler ( void *target, size_t target_size );
+#define NONTRIVIAL_IRQ_HANDLER_SIZE FRAGMENT_SIZE(nontrivial_irq_handler)
+#define install_undi_irq_handler(irq) install_nontrivial_irq_handler(irq)
+#define remove_undi_irq_handler(irq) remove_nontrivial_irq_handler(irq)
+#define undi_irq_triggered(irq) nontrivial_irq_triggered(irq)
+#define UNDI_IRQ_HANDLER_SIZE NONTRIVIAL_IRQ_HANDLER_SIZE
+#define copy_undi_irq_handler(dest,size) copy_nontrivial_irq_handler(dest,size)
+#else
+#define install_undi_irq_handler(irq) install_trivial_irq_handler(irq)
+#define remove_undi_irq_handler(irq) remove_trivial_irq_handler(irq)
+#define undi_irq_triggered(irq) trivial_irq_triggered(irq)
+#define UNDI_IRQ_HANDLER_SIZE TRIVIAL_IRQ_HANDLER_SIZE
+#define copy_undi_irq_handler(dest,size) copy_trivial_irq_handler(dest,size)
+#endif /* UNDI_NONTRIVIAL_IRQ */
+
+/* Size of variable-length data in base_mem_data */
+#define BASE_MEM_VARDATA_SIZE ( UNDI_IRQ_HANDLER_SIZE > e820mangler_size ? \
+ UNDI_IRQ_HANDLER_SIZE : e820mangler_size )
+
+/**************************************************************************
+ * Utility functions
+ **************************************************************************/
+
+/* Checksum a block.
+ */
+
+static uint8_t checksum ( void *block, size_t size ) {
+ uint8_t sum = 0;
+ uint16_t i = 0;
+ for ( i = 0; i < size; i++ ) {
+ sum += ( ( uint8_t * ) block )[i];
+ }
+ return sum;
+}
+
+/* Print the status of a !PXE structure
+ */
+
+static void pxe_dump ( void ) {
+ printf ( "API %hx:%hx St %hx:%hx UD %hx:%hx UC %hx:%hx "
+ "BD %hx:%hx BC %hx:%hx\n",
+ undi.pxe->EntryPointSP.segment, undi.pxe->EntryPointSP.offset,
+ undi.pxe->Stack.Seg_Addr, undi.pxe->Stack.Seg_Size,
+ undi.pxe->UNDIData.Seg_Addr, undi.pxe->UNDIData.Seg_Size,
+ undi.pxe->UNDICode.Seg_Addr, undi.pxe->UNDICode.Seg_Size,
+ undi.pxe->BC_Data.Seg_Addr, undi.pxe->BC_Data.Seg_Size,
+ undi.pxe->BC_Code.Seg_Addr, undi.pxe->BC_Code.Seg_Size );
+}
+
+/* Allocate/free space for structures that must reside in base memory
+ */
+
+static int allocate_base_mem_data ( void ) {
+ /* Allocate space in base memory.
+ * Initialise pointers to base memory structures.
+ */
+ if ( undi.base_mem_data == NULL ) {
+ undi.base_mem_data =
+ allot_base_memory ( sizeof(undi_base_mem_data_t) +
+ BASE_MEM_VARDATA_SIZE );
+ if ( undi.base_mem_data == NULL ) {
+ printf ( "Failed to allocate base memory\n" );
+ free_base_mem_data();
+ return 0;
+ }
+ memset ( undi.base_mem_data, 0, sizeof(undi_base_mem_data_t) );
+ undi.pxs = &undi.base_mem_data->pxs;
+ undi.xmit_data = &undi.base_mem_data->xmit_data;
+ undi.xmit_buffer = undi.base_mem_data->xmit_buffer;
+ }
+ return 1;
+}
+
+static int free_base_mem_data ( void ) {
+ if ( undi.base_mem_data != NULL ) {
+ forget_base_memory ( undi.base_mem_data,
+ sizeof(undi_base_mem_data_t) +
+ BASE_MEM_VARDATA_SIZE );
+ undi.base_mem_data = NULL;
+ undi.pxs = NULL;
+ undi.xmit_data = NULL;
+ undi.xmit_buffer = NULL;
+ copy_undi_irq_handler ( NULL, 0 );
+ }
+ return 1;
+}
+
+static void assemble_firing_squad ( firing_squad_lineup_t *lineup,
+ void *start, size_t size,
+ firing_squad_shoot_t shoot ) {
+ int target;
+ int index;
+ int bit;
+ int start_kb = virt_to_phys(start) >> 10;
+ int end_kb = ( virt_to_phys(start+size) + (1<<10) - 1 ) >> 10;
+
+ for ( target = start_kb; target <= end_kb; target++ ) {
+ index = FIRING_SQUAD_TARGET_INDEX ( target );
+ bit = FIRING_SQUAD_TARGET_BIT ( target );
+ lineup->targets[index] = ( shoot << bit ) |
+ ( lineup->targets[index] & ~( 1 << bit ) );
+ }
+}
+
+static void shoot_targets ( firing_squad_lineup_t *lineup ) {
+ int shoot_this_target = 0;
+ int shoot_last_target = 0;
+ int start_target = 0;
+ int target;
+
+ for ( target = 0; target <= 640; target++ ) {
+ shoot_this_target = ( target == 640 ? 0 :
+ ( 1 << FIRING_SQUAD_TARGET_BIT(target) ) &
+ lineup->targets[FIRING_SQUAD_TARGET_INDEX(target)] );
+ if ( shoot_this_target && !shoot_last_target ) {
+ start_target = target;
+ } else if ( shoot_last_target && !shoot_this_target ) {
+ size_t range_size = ( target - start_target ) << 10;
+ forget_base_memory ( phys_to_virt( start_target<<10 ),
+ range_size );
+ }
+ shoot_last_target = shoot_this_target;
+ }
+}
+
+/* Debug macros
+ */
+
+#ifdef TRACE_UNDI
+#define DBG(...) printf ( __VA_ARGS__ )
+#else
+#define DBG(...)
+#endif
+
+#define UNDI_STATUS(pxs) ( (pxs)->Status == PXENV_EXIT_SUCCESS ? \
+ "SUCCESS" : \
+ ( (pxs)->Status == PXENV_EXIT_FAILURE ? \
+ "FAILURE" : "UNKNOWN" ) )
+
+/**************************************************************************
+ * Base memory scanning functions
+ **************************************************************************/
+
+/* Locate the $PnP structure indicating a PnP BIOS.
+ */
+
+static int hunt_pnp_bios ( void ) {
+ uint32_t off = 0x10000;
+
+ printf ( "Hunting for PnP BIOS..." );
+ while ( off > 0 ) {
+ off -= 16;
+ undi.pnp_bios = (pnp_bios_t *) phys_to_virt ( 0xf0000 + off );
+ if ( undi.pnp_bios->signature == PNP_BIOS_SIGNATURE ) {
+ printf ( "found $PnP at f000:%hx...", off );
+ if ( checksum(undi.pnp_bios,sizeof(pnp_bios_t)) !=0) {
+ printf ( "invalid checksum\n..." );
+ continue;
+ }
+ printf ( "ok\n" );
+ return 1;
+ }
+ }
+ printf ( "none found\n" );
+ undi.pnp_bios = NULL;
+ return 0;
+}
+
+/* Locate the !PXE structure indicating a loaded UNDI driver.
+ */
+
+static int hunt_pixie ( void ) {
+ static uint32_t ptr = 0;
+ pxe_t *pxe = NULL;
+
+ printf ( "Hunting for pixies..." );
+ if ( ptr == 0 ) ptr = 0xa0000;
+ while ( ptr > 0x10000 ) {
+ ptr -= 16;
+ pxe = (pxe_t *) phys_to_virt ( ptr );
+ if ( memcmp ( pxe->Signature, "!PXE", 4 ) == 0 ) {
+ printf ( "found !PXE at %x...", ptr );
+ if ( checksum ( pxe, sizeof(pxe_t) ) != 0 ) {
+ printf ( "invalid checksum\n..." );
+ continue;
+ }
+ if ( ptr < get_free_base_memory() ) {
+ printf ( "in free base memory!\n\n"
+ "WARNING: a valid !PXE structure was "
+ "found in an area of memory marked "
+ "as free!\n\n" );
+ undi.pxe = pxe;
+ pxe_dump();
+ undi.pxe = NULL;
+ printf ( "\nIgnoring and continuing, but this "
+ "may cause problems later!\n\n" );
+ continue;
+ }
+ printf ( "ok\n" );
+ undi.pxe = pxe;
+ pxe_dump();
+ printf ( "Resetting pixie...\n" );
+ undi_unload_base_code();
+ eb_pxenv_stop_undi();
+ pxe_dump();
+ return 1;
+ }
+ }
+ printf ( "none found\n" );
+ ptr = 0;
+ return 0;
+}
+
+/* Locate PCI PnP ROMs.
+ */
+
+static int hunt_rom ( void ) {
+ static uint32_t ptr = 0;
+
+ /* If we are not a PCI device, we cannot search for a ROM that
+ * matches us (?)
+ */
+ if ( ! undi.pci.vendor )
+ return 0;
+
+ printf ( "Hunting for ROMs..." );
+ if ( ptr == 0 ) ptr = 0x100000;
+ while ( ptr > 0x0c0000 ) {
+ ptr -= 0x800;
+ undi.rom = ( rom_t * ) phys_to_virt ( ptr );
+ if ( undi.rom->signature == ROM_SIGNATURE ) {
+ pcir_header_t *pcir_header = NULL;
+ pnp_header_t *pnp_header = NULL;
+
+ printf ( "found 55AA at %x...", ptr );
+ if ( undi.rom->pcir_off == 0 ) {
+ printf ( "not a PCI ROM\n..." );
+ continue;
+ }
+ pcir_header = (pcir_header_t*)( ( void * ) undi.rom +
+ undi.rom->pcir_off );
+ if ( pcir_header->signature != PCIR_SIGNATURE ) {
+ printf ( "invalid PCI signature\n..." );
+ continue;
+ }
+ printf ( "PCI:%hx:%hx...", pcir_header->vendor_id,
+ pcir_header->device_id );
+ if ( ( pcir_header->vendor_id != undi.pci.vendor ) ||
+ ( pcir_header->device_id != undi.pci.dev_id ) ) {
+ printf ( "not me (%hx:%hx)\n...",
+ undi.pci.vendor,
+ undi.pci.dev_id );
+ continue;
+ }
+ if ( undi.rom->pnp_off == 0 ) {
+ printf ( "not a PnP ROM\n..." );
+ continue;
+ }
+ pnp_header = (pnp_header_t*)( ( void * ) undi.rom +
+ undi.rom->pnp_off );
+ if ( pnp_header->signature != PNP_SIGNATURE ) {
+ printf ( "invalid $PnP signature\n..." );
+ continue;
+ }
+ if ( checksum(pnp_header,sizeof(pnp_header_t)) != 0 ) {
+ printf ( "invalid PnP checksum\n..." );
+ continue;
+ }
+ printf ( "ok\nROM contains %s by %s\n",
+ pnp_header->product_str_off==0 ? "(unknown)" :
+ (void*)undi.rom+pnp_header->product_str_off,
+ pnp_header->manuf_str_off==0 ? "(unknown)" :
+ (void*)undi.rom+pnp_header->manuf_str_off );
+ return 1;
+ }
+ }
+ printf ( "none found\n" );
+ ptr = 0;
+ undi.rom = NULL;
+ return 0;
+}
+
+/* Locate ROMs containing UNDI drivers.
+ */
+
+static int hunt_undi_rom ( void ) {
+ while ( hunt_rom() ) {
+ if ( undi.rom->undi_rom_id_off == 0 ) {
+ printf ( "Not a PXE ROM\n" );
+ continue;
+ }
+ undi.undi_rom_id = (undi_rom_id_t *)
+ ( (void *)undi.rom + undi.rom->undi_rom_id_off );
+ if ( undi.undi_rom_id->signature != UNDI_SIGNATURE ) {
+ printf ( "Invalid UNDI signature\n" );
+ continue;
+ }
+ if ( checksum ( undi.undi_rom_id,
+ undi.undi_rom_id->struct_length ) != 0 ) {
+ printf ( "Invalid checksum\n" );
+ continue;
+ }
+ printf ( "Located UNDI ROM supporting revision %d.%d.%d\n",
+ undi.undi_rom_id->undi_rev[2],
+ undi.undi_rom_id->undi_rev[1],
+ undi.undi_rom_id->undi_rev[0] );
+ return 1;
+ }
+ return 0;
+}
+
+/**************************************************************************
+ * Low-level UNDI API call wrappers
+ **************************************************************************/
+
+/* Make a real-mode UNDI API call to the UNDI routine at
+ * routine_seg:routine_off, passing in three uint16 parameters on the
+ * real-mode stack.
+ */
+
+static PXENV_EXIT_t _undi_call ( uint16_t routine_seg,
+ uint16_t routine_off, uint16_t st0,
+ uint16_t st1, uint16_t st2 ) {
+ PXENV_EXIT_t ret = PXENV_EXIT_FAILURE;
+ struct {
+ segoff_t routine;
+ uint16_t st0;
+ uint16_t st1;
+ uint16_t st2;
+ } PACKED in_stack = {
+ { routine_off, routine_seg }, st0, st1, st2
+ };
+
+ RM_FRAGMENT(rm_undi_call,
+ "popw %di\n\t" /* %es:di = routine */
+ "popw %es\n\t"
+ "pushw %cs\n\t" /* set up return address */
+ "call 1f\n\t1:popw %bx\n\t"
+ "leaw (2f-1b)(%bx), %ax\n\t"
+ "pushw %ax\n\t"
+ "pushw %es\n\t" /* routine address to stack */
+ "pushw %di\n\t"
+ "lret\n\t" /* calculated lcall */
+ "\n2:\n\t" /* continuation point */
+ );
+
+ /* Parameters are left on stack: set out_stack = in_stack */
+ ret = real_call ( rm_undi_call, &in_stack, &in_stack );
+
+ /* UNDI API calls may rudely change the status of A20 and not
+ * bother to restore it afterwards. Intel is known to be
+ * guilty of this.
+ *
+ * Note that we will return to this point even if A20 gets
+ * screwed up by the UNDI driver, because Etherboot always
+ * resides in an even megabyte of RAM.
+ */
+ gateA20_set();
+
+ return ret;
+}
+
+/* Make a real-mode call to the UNDI loader routine at
+ * routine_seg:routine_off, passing in the seg:off address of a
+ * pxenv_structure on the real-mode stack.
+ */
+
+static int undi_call_loader ( void ) {
+ PXENV_EXIT_t pxenv_exit = PXENV_EXIT_FAILURE;
+
+ /* Hide Etherboot around the loader, so that the PXE stack
+ * doesn't trash our memory areas
+ */
+ install_e820mangler ( undi.base_mem_data->e820mangler );
+ hide_etherboot();
+ pxenv_exit = _undi_call ( SEGMENT( undi.rom ),
+ undi.undi_rom_id->undi_loader_off,
+ OFFSET( undi.pxs ),
+ SEGMENT( undi.pxs ),
+ 0 /* Unused for UNDI loader API */ );
+ if ( !unhide_etherboot() ) {
+ printf ( "FATAL: corrupt INT15\n" );
+ return 0;
+ }
+
+ /* Return 1 for success, to be consistent with other routines */
+ if ( pxenv_exit == PXENV_EXIT_SUCCESS ) return 1;
+ printf ( "UNDI loader call failed with status %#hx\n",
+ undi.pxs->Status );
+ return 0;
+}
+
+/* Make a real-mode UNDI API call, passing in the opcode and the
+ * seg:off address of a pxenv_structure on the real-mode stack.
+ *
+ * Two versions: undi_call() will automatically report any failure
+ * codes, undi_call_silent() will not.
+ */
+
+static int undi_call_silent ( uint16_t opcode ) {
+ PXENV_EXIT_t pxenv_exit = PXENV_EXIT_FAILURE;
+
+ pxenv_exit = _undi_call ( undi.pxe->EntryPointSP.segment,
+ undi.pxe->EntryPointSP.offset,
+ opcode,
+ OFFSET( undi.pxs ),
+ SEGMENT( undi.pxs ) );
+ /* Return 1 for success, to be consistent with other routines */
+ return pxenv_exit == PXENV_EXIT_SUCCESS ? 1 : 0;
+}
+
+static int undi_call ( uint16_t opcode ) {
+ if ( undi_call_silent ( opcode ) ) return 1;
+ printf ( "UNDI API call %#hx failed with status %#hx\n",
+ opcode, undi.pxs->Status );
+ return 0;
+}
+
+#ifdef UNDI_NONTRIVIAL_IRQ
+/* IRQ handler that actually calls PXENV_UNDI_ISR. It's probably
+ * better to use the trivial IRQ handler, since this seems to work for
+ * just about all known NICs and doesn't involve making a PXE API call
+ * in interrupt context.
+ *
+ * This routine is mainly used for testing the Etherboot PXE stack's
+ * ability to be called in interrupt context. It is not compiled in
+ * by default.
+ *
+ * This code has fewer safety checks than those in the
+ * trivial_irq_handler routines. These are omitted because this code
+ * is not intended for mainstream use.
+ */
+
+uint16_t nontrivial_irq_previous_trigger_count = 0;
+
+static int copy_nontrivial_irq_handler ( void *target,
+ size_t target_size __unused ) {
+ RM_FRAGMENT(nontrivial_irq_handler,
+ /* Will be installed on a paragraph boundary, so access variables
+ * using %cs:(xxx-irqstart)
+ */
+ "\n\t"
+ "irqstart:\n\t"
+ /* Fields here must match those in undi_irq_handler_t */
+ "chain_to:\t.word 0,0\n\t"
+ "irq_chain:\t.byte 0,0,0,0\n\t"
+ "entry:\t.word 0,0\n\t"
+ "count_all:\t.word 0\n\t"
+ "count_ours:\t.word 0\n\t"
+ "undi_isr:\n\t"
+ "undi_isr_Status:\t.word 0\n\t"
+ "undi_isr_FuncFlag:\t.word 0\n\t"
+ "undi_isr_others:\t.word 0,0,0,0,0,0\n\t"
+ "handler:\n\t"
+ /* Assume that PXE stack will corrupt everything */
+ "pushal\n\t"
+ "push %ds\n\t"
+ "push %es\n\t"
+ "push %fs\n\t"
+ "push %gs\n\t"
+ /* Set DS == CS */
+ "pushw %cs\n\t"
+ "popw %ds\n\t"
+ /* Set up parameters for call */
+ "movw $" RM_STR(PXENV_UNDI_ISR_IN_START) ", %ds:(undi_isr_FuncFlag-irqstart)\n\t"
+ "pushw %cs\n\t"
+ "popw %es\n\t"
+ "movw $(undi_isr-irqstart), %di\n\t"
+ "movw $" RM_STR(PXENV_UNDI_ISR) ", %bx\n\t"
+ "pushw %es\n\t" /* Registers for PXENV+, stack for !PXE */
+ "pushw %di\n\t"
+ "pushw %bx\n\t"
+ /* Make PXE API call */
+ "lcall *%ds:(entry-irqstart)\n\t"
+ "addw $6, %sp\n\t"
+ /* Set DS == CS */
+ "pushw %cs\n\t"
+ "popw %ds\n\t"
+ /* Check return status to see if it's one of our interrupts */
+ "cmpw $" RM_STR(PXENV_STATUS_SUCCESS) ", %cs:(undi_isr_Status-irqstart)\n\t"
+ "jne 1f\n\t"
+ "cmpw $" RM_STR(PXENV_UNDI_ISR_OUT_OURS) ", %cs:(undi_isr_FuncFlag-irqstart)\n\t"
+ "jne 1f\n\t"
+ /* Increment count_ours if so */
+ "incw %ds:(count_ours-irqstart)\n\t"
+ "1:\n\t"
+ /* Increment count_all anyway */
+ "incw %ds:(count_all-irqstart)\n\t"
+ /* Restore registers and return */
+ "popw %gs\n\t"
+ "popw %fs\n\t"
+ "popw %es\n\t"
+ "popw %ds\n\t"
+ "popal\n\t"
+ "\n\t"
+ /* Chain to acknowledge the interrupt */
+ "cmpb $0, %cs:(irq_chain-irqstart)\n\t"
+ "jz 2f\n\t"
+ "ljmp %cs:(chain_to-irqstart)\n\t"
+ "2:\n\t"
+ "\n\t"
+ "iret\n\t"
+ "\n\t"
+ );
+
+ /* Copy handler */
+ memcpy ( target, nontrivial_irq_handler, NONTRIVIAL_IRQ_HANDLER_SIZE );
+
+ return 1;
+}
+
+static int install_nontrivial_irq_handler ( irq_t irq ) {
+ undi_irq_handler_t *handler =
+ &undi.base_mem_data->nontrivial_irq_handler;
+ segoff_t isr_segoff;
+
+ printf ( "WARNING: using non-trivial IRQ handler [EXPERIMENTAL]\n" );
+ /*
+ * This code is deliberately quick and dirty. The whole
+ * nontrivial IRQ stuff is only present in order to test out
+ * calling our PXE stack in interrupt context. Do NOT use
+ * this in production code.
+ */
+
+ disable_irq ( irq );
+ handler->count_all = 0;
+ handler->count_ours = 0;
+ handler->entry = undi.pxe->EntryPointSP;
+ nontrivial_irq_previous_trigger_count = 0;
+ isr_segoff.segment = SEGMENT(handler);
+ isr_segoff.offset = (void*)&handler->code - (void*)handler;
+ install_irq_handler( irq, &isr_segoff,
+ &handler->irq_chain, &handler->chain_to);
+ enable_irq ( irq );
+
+ return 1;
+}
+
+static int remove_nontrivial_irq_handler ( irq_t irq ) {
+ undi_irq_handler_t *handler =
+ &undi.base_mem_data->nontrivial_irq_handler;
+ segoff_t isr_segoff;
+
+ isr_segoff.segment = SEGMENT(handler);
+ isr_segoff.offset = (char*)&handler->code - (char*)handler;
+ remove_irq_handler( irq, &isr_segoff,
+ &handler->irq_chain, &handler->chain_to);
+ return 1;
+}
+
+static int nontrivial_irq_triggered ( irq_t irq __unused ) {
+ undi_irq_handler_t *handler =
+ &undi.base_mem_data->nontrivial_irq_handler;
+ uint16_t nontrivial_irq_this_trigger_count = handler->count_ours;
+ int triggered = ( nontrivial_irq_this_trigger_count -
+ nontrivial_irq_previous_trigger_count );
+
+ nontrivial_irq_previous_trigger_count =
+ nontrivial_irq_this_trigger_count;
+ return triggered ? 1 : 0;
+}
+
+static void nontrivial_irq_debug ( irq_t irq ) {
+ undi_irq_handler_t *handler =
+ &undi.base_mem_data->nontrivial_irq_handler;
+
+ printf ( "IRQ %d triggered %d times (%d of which were ours)\n",
+ irq, handler->count_all, handler->count_ours );
+}
+#endif /* UNDI_NONTRIVIAL_IRQ */
+
+/**************************************************************************
+ * High-level UNDI API call wrappers
+ **************************************************************************/
+
+/* Install the UNDI driver from a located UNDI ROM.
+ */
+
+static int undi_loader ( void ) {
+ pxe_t *pxe = NULL;
+
+ if ( ! undi.pci.vendor ) {
+ printf ( "ERROR: attempted to call loader of an ISA ROM?\n" );
+ return 0;
+ }
+
+ /* AX contains PCI bus:devfn (PCI specification) */
+ undi.pxs->loader.ax = ( undi.pci.bus << 8 ) | undi.pci.devfn;
+ /* BX and DX set to 0xffff for non-ISAPnP devices
+ * (BIOS boot specification)
+ */
+ undi.pxs->loader.bx = 0xffff;
+ undi.pxs->loader.dx = 0xffff;
+ /* ES:DI points to PnP BIOS' $PnP structure
+ * (BIOS boot specification)
+ */
+ if ( undi.pnp_bios ) {
+ undi.pxs->loader.es = 0xf000;
+ undi.pxs->loader.di = virt_to_phys ( undi.pnp_bios ) - 0xf0000;
+ } else {
+ /* Set to a NULL pointer and hope that we don't need it */
+ undi.pxs->loader.es = 0x0000;
+ undi.pxs->loader.di = 0x0000;
+ }
+
+ /* Allocate space for UNDI driver's code and data segments */
+ undi.driver_code_size = undi.undi_rom_id->code_size;
+ undi.driver_code = allot_base_memory ( undi.driver_code_size );
+ if ( undi.driver_code == NULL ) {
+ printf ( "Could not allocate %d bytes for UNDI code segment\n",
+ undi.driver_code_size );
+ return 0;
+ }
+ undi.pxs->loader.undi_cs = SEGMENT( undi.driver_code );
+
+ undi.driver_data_size = undi.undi_rom_id->data_size;
+ undi.driver_data = allot_base_memory ( undi.driver_data_size );
+ if ( undi.driver_data == NULL ) {
+ printf ( "Could not allocate %d bytes for UNDI code segment\n",
+ undi.driver_data_size );
+ return 0;
+ }
+ undi.pxs->loader.undi_ds = SEGMENT( undi.driver_data );
+
+ printf ( "Installing UNDI driver code to %hx:0000, data at %hx:0000\n",
+ undi.pxs->loader.undi_cs, undi.pxs->loader.undi_ds );
+
+ /* Do the API call to install the loader */
+ if ( ! undi_call_loader () ) return 0;
+
+ pxe = VIRTUAL( undi.pxs->loader.undi_cs,
+ undi.pxs->loader.pxe_ptr.offset );
+ printf ( "UNDI driver created a pixie at %hx:%hx...",
+ undi.pxs->loader.undi_cs, undi.pxs->loader.pxe_ptr.offset );
+ if ( memcmp ( pxe->Signature, "!PXE", 4 ) != 0 ) {
+ printf ( "invalid signature\n" );
+ return 0;
+ }
+ if ( checksum ( pxe, sizeof(pxe_t) ) != 0 ) {
+ printf ( "invalid checksum\n" );
+ return 0;
+ }
+ printf ( "ok\n" );
+ undi.pxe = pxe;
+ pxe_dump();
+ return 1;
+}
+
+/* Start the UNDI driver.
+ */
+
+static int eb_pxenv_start_undi ( void ) {
+ int success = 0;
+
+ /* AX contains PCI bus:devfn (PCI specification) */
+ undi.pxs->start_undi.ax = ( undi.pci.bus << 8 ) | undi.pci.devfn;
+
+ /* BX and DX set to 0xffff for non-ISAPnP devices
+ * (BIOS boot specification)
+ */
+ undi.pxs->start_undi.bx = 0xffff;
+ undi.pxs->start_undi.dx = 0xffff;
+ /* ES:DI points to PnP BIOS' $PnP structure
+ * (BIOS boot specification)
+ */
+ if ( undi.pnp_bios ) {
+ undi.pxs->start_undi.es = 0xf000;
+ undi.pxs->start_undi.di =
+ virt_to_phys ( undi.pnp_bios ) - 0xf0000;
+ } else {
+ /* Set to a NULL pointer and hope that we don't need it */
+ undi.pxs->start_undi.es = 0x0000;
+ undi.pxs->start_undi.di = 0x0000;
+ }
+
+ DBG ( "PXENV_START_UNDI => AX=%hx BX=%hx DX=%hx ES:DI=%hx:%hx\n",
+ undi.pxs->start_undi.ax,
+ undi.pxs->start_undi.bx, undi.pxs->start_undi.dx,
+ undi.pxs->start_undi.es, undi.pxs->start_undi.di );
+ success = undi_call ( PXENV_START_UNDI );
+ DBG ( "PXENV_START_UNDI <= Status=%s\n", UNDI_STATUS(undi.pxs) );
+ if ( success ) undi.prestarted = 1;
+ return success;
+}
+
+static int eb_pxenv_undi_startup ( void ) {
+ int success = 0;
+
+ DBG ( "PXENV_UNDI_STARTUP => (void)\n" );
+ success = undi_call ( PXENV_UNDI_STARTUP );
+ DBG ( "PXENV_UNDI_STARTUP <= Status=%s\n", UNDI_STATUS(undi.pxs) );
+ if ( success ) undi.started = 1;
+ return success;
+}
+
+static int eb_pxenv_undi_cleanup ( void ) {
+ int success = 0;
+
+ DBG ( "PXENV_UNDI_CLEANUP => (void)\n" );
+ success = undi_call ( PXENV_UNDI_CLEANUP );
+ DBG ( "PXENV_UNDI_CLEANUP <= Status=%s\n", UNDI_STATUS(undi.pxs) );
+ return success;
+}
+
+static int eb_pxenv_undi_initialize ( void ) {
+ int success = 0;
+
+ undi.pxs->undi_initialize.ProtocolIni = 0;
+ memset ( &undi.pxs->undi_initialize.reserved, 0,
+ sizeof ( undi.pxs->undi_initialize.reserved ) );
+ DBG ( "PXENV_UNDI_INITIALIZE => ProtocolIni=%x\n" );
+ success = undi_call ( PXENV_UNDI_INITIALIZE );
+ DBG ( "PXENV_UNDI_INITIALIZE <= Status=%s\n", UNDI_STATUS(undi.pxs) );
+ if ( success ) undi.initialized = 1;
+ return success;
+}
+
+static int eb_pxenv_undi_shutdown ( void ) {
+ int success = 0;
+
+ DBG ( "PXENV_UNDI_SHUTDOWN => (void)\n" );
+ success = undi_call ( PXENV_UNDI_SHUTDOWN );
+ DBG ( "PXENV_UNDI_SHUTDOWN <= Status=%s\n", UNDI_STATUS(undi.pxs) );
+ if ( success ) {
+ undi.initialized = 0;
+ undi.started = 0;
+ }
+ return success;
+}
+
+static int eb_pxenv_undi_open ( void ) {
+ int success = 0;
+
+ undi.pxs->undi_open.OpenFlag = 0;
+ undi.pxs->undi_open.PktFilter = FLTR_DIRECTED | FLTR_BRDCST;
+
+ /* Multicast support not yet implemented */
+ undi.pxs->undi_open.R_Mcast_Buf.MCastAddrCount = 0;
+ DBG ( "PXENV_UNDI_OPEN => OpenFlag=%hx PktFilter=%hx "
+ "MCastAddrCount=%hx\n",
+ undi.pxs->undi_open.OpenFlag, undi.pxs->undi_open.PktFilter,
+ undi.pxs->undi_open.R_Mcast_Buf.MCastAddrCount );
+ success = undi_call ( PXENV_UNDI_OPEN );
+ DBG ( "PXENV_UNDI_OPEN <= Status=%s\n", UNDI_STATUS(undi.pxs) );
+ if ( success ) undi.opened = 1;
+ return success;
+}
+
+static int eb_pxenv_undi_close ( void ) {
+ int success = 0;
+
+ DBG ( "PXENV_UNDI_CLOSE => (void)\n" );
+ success = undi_call ( PXENV_UNDI_CLOSE );
+ DBG ( "PXENV_UNDI_CLOSE <= Status=%s\n", UNDI_STATUS(undi.pxs) );
+ if ( success ) undi.opened = 0;
+ return success;
+}
+
+static int eb_pxenv_undi_transmit_packet ( void ) {
+ int success = 0;
+ static const uint8_t broadcast[] = { 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF };
+
+ /* XMitFlag selects unicast / broadcast */
+ if ( memcmp ( undi.xmit_data->destaddr, broadcast,
+ sizeof(broadcast) ) == 0 ) {
+ undi.pxs->undi_transmit.XmitFlag = XMT_BROADCAST;
+ } else {
+ undi.pxs->undi_transmit.XmitFlag = XMT_DESTADDR;
+ }
+
+ /* Zero reserved dwords */
+ undi.pxs->undi_transmit.Reserved[0] = 0;
+ undi.pxs->undi_transmit.Reserved[1] = 0;
+
+ /* Segment:offset pointer to DestAddr in base memory */
+ undi.pxs->undi_transmit.DestAddr.segment =
+ SEGMENT( undi.xmit_data->destaddr );
+ undi.pxs->undi_transmit.DestAddr.offset =
+ OFFSET( undi.xmit_data->destaddr );
+
+ /* Segment:offset pointer to TBD in base memory */
+ undi.pxs->undi_transmit.TBD.segment = SEGMENT( &undi.xmit_data->tbd );
+ undi.pxs->undi_transmit.TBD.offset = OFFSET( &undi.xmit_data->tbd );
+
+ /* Use only the "immediate" part of the TBD */
+ undi.xmit_data->tbd.DataBlkCount = 0;
+
+ DBG ( "PXENV_UNDI_TRANSMIT_PACKET => Protocol=%hx XmitFlag=%hx ...\n"
+ "... DestAddr=%hx:%hx TBD=%hx:%hx ...\n",
+ undi.pxs->undi_transmit.Protocol,
+ undi.pxs->undi_transmit.XmitFlag,
+ undi.pxs->undi_transmit.DestAddr.segment,
+ undi.pxs->undi_transmit.DestAddr.offset,
+ undi.pxs->undi_transmit.TBD.segment,
+ undi.pxs->undi_transmit.TBD.offset );
+ DBG ( "... TBD { ImmedLength=%hx Xmit=%hx:%hx DataBlkCount=%hx }\n",
+ undi.xmit_data->tbd.ImmedLength,
+ undi.xmit_data->tbd.Xmit.segment,
+ undi.xmit_data->tbd.Xmit.offset,
+ undi.xmit_data->tbd.DataBlkCount );
+ success = undi_call ( PXENV_UNDI_TRANSMIT );
+ DBG ( "PXENV_UNDI_TRANSMIT_PACKET <= Status=%s\n",
+ UNDI_STATUS(undi.pxs) );
+ return success;
+}
+
+static int eb_pxenv_undi_set_station_address ( void ) {
+ /* This will spuriously fail on some cards. Ignore failures.
+ * We only ever use it to set the MAC address to the card's
+ * permanent value anyway, so it's a useless call (although we
+ * make it because PXE spec says we should).
+ */
+ DBG ( "PXENV_UNDI_SET_STATION_ADDRESS => "
+ "StationAddress=%!\n",
+ undi.pxs->undi_set_station_address.StationAddress );
+ undi_call_silent ( PXENV_UNDI_SET_STATION_ADDRESS );
+ DBG ( "PXENV_UNDI_SET_STATION_ADDRESS <= Status=%s\n",
+ UNDI_STATUS(undi.pxs) );
+ return 1;
+}
+
+static int eb_pxenv_undi_get_information ( void ) {
+ int success = 0;
+ memset ( undi.pxs, 0, sizeof ( undi.pxs ) );
+ DBG ( "PXENV_UNDI_GET_INFORMATION => (void)\n" );
+ success = undi_call ( PXENV_UNDI_GET_INFORMATION );
+ DBG ( "PXENV_UNDI_GET_INFORMATION <= Status=%s "
+ "BaseIO=%hx IntNumber=%hx ...\n"
+ "... MaxTranUnit=%hx HwType=%hx HwAddrlen=%hx ...\n"
+ "... CurrentNodeAddress=%! PermNodeAddress=%! ...\n"
+ "... ROMAddress=%hx RxBufCt=%hx TxBufCt=%hx\n",
+ UNDI_STATUS(undi.pxs),
+ undi.pxs->undi_get_information.BaseIo,
+ undi.pxs->undi_get_information.IntNumber,
+ undi.pxs->undi_get_information.MaxTranUnit,
+ undi.pxs->undi_get_information.HwType,
+ undi.pxs->undi_get_information.HwAddrLen,
+ undi.pxs->undi_get_information.CurrentNodeAddress,
+ undi.pxs->undi_get_information.PermNodeAddress,
+ undi.pxs->undi_get_information.ROMAddress,
+ undi.pxs->undi_get_information.RxBufCt,
+ undi.pxs->undi_get_information.TxBufCt );
+ return success;
+}
+
+static int eb_pxenv_undi_get_iface_info ( void ) {
+ int success = 0;
+
+ DBG ( "PXENV_UNDI_GET_IFACE_INFO => (void)\n" );
+ success = undi_call ( PXENV_UNDI_GET_IFACE_INFO );
+ DBG ( "PXENV_UNDI_GET_IFACE_INFO <= Status=%s IfaceType=%s ...\n"
+ "... LinkSpeed=%x ServiceFlags=%x\n",
+ UNDI_STATUS(undi.pxs),
+ undi.pxs->undi_get_iface_info.IfaceType,
+ undi.pxs->undi_get_iface_info.LinkSpeed,
+ undi.pxs->undi_get_iface_info.ServiceFlags );
+ return success;
+}
+
+static int eb_pxenv_undi_isr ( void ) {
+ int success = 0;
+
+ DBG ( "PXENV_UNDI_ISR => FuncFlag=%hx\n",
+ undi.pxs->undi_isr.FuncFlag );
+ success = undi_call ( PXENV_UNDI_ISR );
+ DBG ( "PXENV_UNDI_ISR <= Status=%s FuncFlag=%hx BufferLength=%hx ...\n"
+ "... FrameLength=%hx FrameHeaderLength=%hx Frame=%hx:%hx "
+ "ProtType=%hhx ...\n... PktType=%hhx\n",
+ UNDI_STATUS(undi.pxs), undi.pxs->undi_isr.FuncFlag,
+ undi.pxs->undi_isr.BufferLength,
+ undi.pxs->undi_isr.FrameLength,
+ undi.pxs->undi_isr.FrameHeaderLength,
+ undi.pxs->undi_isr.Frame.segment,
+ undi.pxs->undi_isr.Frame.offset,
+ undi.pxs->undi_isr.ProtType,
+ undi.pxs->undi_isr.PktType );
+ return success;
+}
+
+static int eb_pxenv_stop_undi ( void ) {
+ int success = 0;
+
+ DBG ( "PXENV_STOP_UNDI => (void)\n" );
+ success = undi_call ( PXENV_STOP_UNDI );
+ DBG ( "PXENV_STOP_UNDI <= Status=%s\n", UNDI_STATUS(undi.pxs) );
+ if ( success ) undi.prestarted = 0;
+ return success;
+}
+
+static int eb_pxenv_unload_stack ( void ) {
+ int success = 0;
+
+ memset ( undi.pxs, 0, sizeof ( undi.pxs ) );
+ DBG ( "PXENV_UNLOAD_STACK => (void)\n" );
+ success = undi_call_silent ( PXENV_UNLOAD_STACK );
+ DBG ( "PXENV_UNLOAD_STACK <= Status=%s ...\n... (%s)\n",
+ UNDI_STATUS(undi.pxs),
+ ( undi.pxs->Status == PXENV_STATUS_SUCCESS ?
+ "base-code is ready to be removed" :
+ ( undi.pxs->Status == PXENV_STATUS_FAILURE ?
+ "the size of free base memory has been changed" :
+ ( undi.pxs->Status == PXENV_STATUS_KEEP_ALL ?
+ "the NIC interrupt vector has been changed" :
+ "UNEXPECTED STATUS CODE" ) ) ) );
+ return success;
+}
+
+static int eb_pxenv_stop_base ( void ) {
+ int success = 0;
+
+ DBG ( "PXENV_STOP_BASE => (void)\n" );
+ success = undi_call ( PXENV_STOP_BASE );
+ DBG ( "PXENV_STOP_BASE <= Status=%s\n", UNDI_STATUS(undi.pxs) );
+ return success;
+}
+
+/* Unload UNDI base code (if any present) and free memory.
+ */
+static int undi_unload_base_code ( void ) {
+ void *bc_code = VIRTUAL( undi.pxe->BC_Code.Seg_Addr, 0 );
+ size_t bc_code_size = undi.pxe->BC_Code.Seg_Size;
+ void *bc_data = VIRTUAL( undi.pxe->BC_Data.Seg_Addr, 0 );
+ size_t bc_data_size = undi.pxe->BC_Data.Seg_Size;
+ void *bc_stck = VIRTUAL( undi.pxe->Stack.Seg_Addr, 0 );
+ size_t bc_stck_size = undi.pxe->Stack.Seg_Size;
+ firing_squad_lineup_t lineup;
+
+ /* Since we never start the base code, the only time we should
+ * reach this is if we were loaded via PXE. There are many
+ * different and conflicting versions of the "correct" way to
+ * unload the PXE base code, several of which appear within
+ * the PXE specification itself. This one seems to work for
+ * our purposes.
+ *
+ * We always call PXENV_STOP_BASE and PXENV_UNLOAD_STACK even
+ * if the !PXE structure indicates that no base code is
+ * present. We do this for the case that there is a
+ * base-code-less UNDI driver loaded that has hooked some
+ * interrupts. If the base code really is absent, then these
+ * calls will fail, we will ignore the failure, and our
+ * subsequent memory-freeing code is robust enough to handle
+ * whatever's thrown at it.
+ */
+ eb_pxenv_stop_base();
+ eb_pxenv_unload_stack();
+ if ( ( undi.pxs->unload_stack.Status != PXENV_STATUS_SUCCESS ) &&
+ ( undi.pxs->unload_stack.Status != PXENV_STATUS_FAILURE ) &&
+ ( undi.pxe->BC_Code.Seg_Addr != 0 ) )
+ {
+ printf ( "Could not free memory allocated to PXE base code: "
+ "possible memory leak\n" );
+ return 0;
+ }
+ /* Free data structures. Forget what the PXE specification
+ * says about how to calculate the new size of base memory;
+ * basemem.c takes care of all that for us. Note that we also
+ * have to free the stack (even though PXE spec doesn't say
+ * anything about it) because nothing else is going to do so.
+ *
+ * Structures will almost certainly not be kB-aligned and
+ * there's a reasonable chance that the UNDI code or data
+ * portions will lie in the same kB as the base code. Since
+ * forget_base_memory works only in 1kB increments, this means
+ * we have to do some arcane trickery.
+ */
+ memset ( &lineup, 0, sizeof(lineup) );
+ if ( SEGMENT(bc_code) != 0 )
+ assemble_firing_squad( &lineup, bc_code, bc_code_size, SHOOT );
+ if ( SEGMENT(bc_data) != 0 )
+ assemble_firing_squad( &lineup, bc_data, bc_data_size, SHOOT );
+ if ( SEGMENT(bc_stck) != 0 )
+ assemble_firing_squad( &lineup, bc_stck, bc_stck_size, SHOOT );
+ /* Don't shoot any bits of the UNDI driver code or data */
+ assemble_firing_squad ( &lineup,
+ VIRTUAL(undi.pxe->UNDICode.Seg_Addr, 0),
+ undi.pxe->UNDICode.Seg_Size, DONTSHOOT );
+ assemble_firing_squad ( &lineup,
+ VIRTUAL(undi.pxe->UNDIData.Seg_Addr, 0),
+ undi.pxe->UNDIData.Seg_Size, DONTSHOOT );
+ shoot_targets ( &lineup );
+ undi.pxe->BC_Code.Seg_Addr = 0;
+ undi.pxe->BC_Data.Seg_Addr = 0;
+ undi.pxe->Stack.Seg_Addr = 0;
+
+ /* Free and reallocate our own base memory data structures, to
+ * allow the freed base-code blocks to be fully released.
+ */
+ free_base_mem_data();
+ if ( ! allocate_base_mem_data() ) {
+ printf ( "FATAL: memory unaccountably lost\n" );
+ while ( 1 ) {};
+ }
+
+ return 1;
+}
+
+/* UNDI full initialization
+ *
+ * This calls all the various UNDI initialization routines in sequence.
+ */
+
+static int undi_full_startup ( void ) {
+ if ( ! eb_pxenv_start_undi() ) return 0;
+ if ( ! eb_pxenv_undi_startup() ) return 0;
+ if ( ! eb_pxenv_undi_initialize() ) return 0;
+ if ( ! eb_pxenv_undi_get_information() ) return 0;
+ undi.irq = undi.pxs->undi_get_information.IntNumber;
+ copy_undi_irq_handler ( undi.base_mem_data->irq_handler,
+ UNDI_IRQ_HANDLER_SIZE );
+ if ( ! install_undi_irq_handler ( undi.irq ) ) {
+ undi.irq = IRQ_NONE;
+ return 0;
+ }
+ memmove ( &undi.pxs->undi_set_station_address.StationAddress,
+ &undi.pxs->undi_get_information.PermNodeAddress,
+ sizeof (undi.pxs->undi_set_station_address.StationAddress) );
+ if ( ! eb_pxenv_undi_set_station_address() ) return 0;
+ if ( ! eb_pxenv_undi_open() ) return 0;
+ return 1;
+}
+
+/* UNDI full shutdown
+ *
+ * This calls all the various UNDI shutdown routines in sequence and
+ * also frees any memory that it can.
+ */
+
+static int undi_full_shutdown ( void ) {
+ if ( undi.pxe != NULL ) {
+ /* In case we didn't allocate the driver's memory in the first
+ * place, try to grab the code and data segments and sizes
+ * from the !PXE structure.
+ */
+ if ( undi.driver_code == NULL ) {
+ undi.driver_code = VIRTUAL(undi.pxe->UNDICode.Seg_Addr,
+ 0 );
+ undi.driver_code_size = undi.pxe->UNDICode.Seg_Size;
+ }
+ if ( undi.driver_data == NULL ) {
+ undi.driver_data = VIRTUAL(undi.pxe->UNDIData.Seg_Addr,
+ 0 );
+ undi.driver_data_size = undi.pxe->UNDIData.Seg_Size;
+ }
+
+ /* Ignore errors and continue in the hope of shutting
+ * down anyway
+ */
+ if ( undi.opened ) eb_pxenv_undi_close();
+ if ( undi.started ) {
+ eb_pxenv_undi_cleanup();
+ /* We may get spurious UNDI API errors at this
+ * point. If startup() succeeded but
+ * initialize() failed then according to the
+ * spec, we should call shutdown(). However,
+ * some NICS will fail with a status code
+ * 0x006a (INVALID_STATE).
+ */
+ eb_pxenv_undi_shutdown();
+ }
+ if ( undi.irq != IRQ_NONE ) {
+ remove_undi_irq_handler ( undi.irq );
+ undi.irq = IRQ_NONE;
+ }
+ undi_unload_base_code();
+ if ( undi.prestarted ) {
+ eb_pxenv_stop_undi();
+ /* Success OR Failure indicates that memory
+ * can be freed. Any other status code means
+ * that it can't.
+ */
+ if (( undi.pxs->Status == PXENV_STATUS_KEEP_UNDI ) ||
+ ( undi.pxs->Status == PXENV_STATUS_KEEP_ALL ) ) {
+ printf ("Could not free memory allocated to "
+ "UNDI driver: possible memory leak\n");
+ return 0;
+ }
+ }
+ }
+ /* Free memory allocated to UNDI driver */
+ if ( undi.driver_code != NULL ) {
+ /* Clear contents in order to eliminate !PXE and PXENV
+ * signatures to prevent spurious detection via base
+ * memory scan.
+ */
+ memset ( undi.driver_code, 0, undi.driver_code_size );
+ forget_base_memory ( undi.driver_code, undi.driver_code_size );
+ undi.driver_code = NULL;
+ undi.driver_code_size = 0;
+ }
+ if ( undi.driver_data != NULL ) {
+ forget_base_memory ( undi.driver_data, undi.driver_data_size );
+ undi.driver_data = NULL;
+ undi.driver_data_size = 0;
+ }
+ /* !PXE structure now gone; memory freed */
+ undi.pxe = NULL;
+ return 1;
+}
+
+/**************************************************************************
+POLL - Wait for a frame
+***************************************************************************/
+static int undi_poll(struct nic *nic, int retrieve)
+{
+ /* Fun, fun, fun. UNDI drivers don't use polling; they use
+ * interrupts. We therefore cheat and pretend that an
+ * interrupt has occurred every time undi_poll() is called.
+ * This isn't too much of a hack; PCI devices share IRQs and
+ * so the first thing that a proper ISR should do is call
+ * PXENV_UNDI_ISR to determine whether or not the UNDI NIC
+ * generated the interrupt; there is no harm done by spurious
+ * calls to PXENV_UNDI_ISR. Similarly, we wouldn't be
+ * handling them any more rapidly than the usual rate of
+ * undi_poll() being called even if we did implement a full
+ * ISR. So it should work. Ha!
+ *
+ * Addendum (21/10/03). Some cards don't play nicely with
+ * this trick, so instead of doing it the easy way we have to
+ * go to all the hassle of installing a genuine interrupt
+ * service routine and dealing with the wonderful 8259
+ * Programmable Interrupt Controller. Joy.
+ */
+
+ /* See if a hardware interrupt has occurred since the last poll().
+ */
+ if ( ! undi_irq_triggered ( undi.irq ) ) return 0;
+
+ /* Given the frailty of PXE stacks, it's probably not safe to
+ * risk calling PXENV_UNDI_ISR with
+ * FuncFlag=PXENV_UNDI_ISR_START twice for the same interrupt,
+ * so we cheat slightly and assume that there is something
+ * ready to retrieve as long as an interrupt has occurred.
+ */
+ if ( ! retrieve ) return 1;
+
+#ifdef UNDI_NONTRIVIAL_IRQ
+ /* With the nontrivial IRQ handler, we have already called
+ * PXENV_UNDI_ISR with PXENV_UNDI_ISR_IN_START and determined
+ * that it is one of ours.
+ */
+#else
+ /* Ask the UNDI driver if this is "our" interrupt.
+ */
+ undi.pxs->undi_isr.FuncFlag = PXENV_UNDI_ISR_IN_START;
+ if ( ! eb_pxenv_undi_isr() ) return 0;
+ if ( undi.pxs->undi_isr.FuncFlag == PXENV_UNDI_ISR_OUT_NOT_OURS ) {
+ /* "Not our interrupt" translates to "no packet ready
+ * to read".
+ */
+ /* FIXME: Technically, we shouldn't be the one sending
+ * EOI. However, since our IRQ handlers don't yet
+ * support chaining, nothing else gets the chance to.
+ * One nice side-effect of doing this is that it means
+ * we can cheat and claim the timer interrupt as our
+ * NIC interrupt; it will be inefficient but will
+ * work.
+ */
+ send_specific_eoi ( undi.irq );
+ return 0;
+ }
+#endif
+
+ /* At this stage, the device should have cleared its interrupt
+ * line so we can send EOI to the 8259.
+ */
+ send_specific_eoi ( undi.irq );
+
+ /* We might have received a packet, or this might be a
+ * "transmit completed" interrupt. Zero nic->packetlen,
+ * increment whenever we receive a bit of a packet, test
+ * nic->packetlen when we're done to see whether or not we
+ * actually received anything.
+ */
+ nic->packetlen = 0;
+ undi.pxs->undi_isr.FuncFlag = PXENV_UNDI_ISR_IN_PROCESS;
+ if ( ! eb_pxenv_undi_isr() ) return 0;
+ while ( undi.pxs->undi_isr.FuncFlag != PXENV_UNDI_ISR_OUT_DONE ) {
+ switch ( undi.pxs->undi_isr.FuncFlag ) {
+ case PXENV_UNDI_ISR_OUT_TRANSMIT:
+ /* We really don't care about transmission complete
+ * interrupts.
+ */
+ break;
+ case PXENV_UNDI_ISR_OUT_BUSY:
+ /* This should never happen.
+ */
+ printf ( "UNDI ISR thinks it's being re-entered!\n"
+ "Aborting receive\n" );
+ return 0;
+ case PXENV_UNDI_ISR_OUT_RECEIVE:
+ /* Copy data to receive buffer */
+ memcpy ( nic->packet + nic->packetlen,
+ VIRTUAL( undi.pxs->undi_isr.Frame.segment,
+ undi.pxs->undi_isr.Frame.offset ),
+ undi.pxs->undi_isr.BufferLength );
+ nic->packetlen += undi.pxs->undi_isr.BufferLength;
+ break;
+ default:
+ printf ( "UNDI ISR returned bizzare status code %d\n",
+ undi.pxs->undi_isr.FuncFlag );
+ }
+ undi.pxs->undi_isr.FuncFlag = PXENV_UNDI_ISR_IN_GET_NEXT;
+ if ( ! eb_pxenv_undi_isr() ) return 0;
+ }
+ return nic->packetlen > 0 ? 1 : 0;
+}
+
+/**************************************************************************
+TRANSMIT - Transmit a frame
+***************************************************************************/
+static void undi_transmit(
+ struct nic *nic __unused,
+ const char *d, /* Destination */
+ unsigned int t, /* Type */
+ unsigned int s, /* size */
+ const char *p) /* Packet */
+{
+ /* Copy destination to buffer in base memory */
+ memcpy ( undi.xmit_data->destaddr, d, sizeof(MAC_ADDR) );
+
+ /* Translate packet type to UNDI packet type */
+ switch ( t ) {
+ case IP : undi.pxs->undi_transmit.Protocol = P_IP; break;
+ case ARP: undi.pxs->undi_transmit.Protocol = P_ARP; break;
+ case RARP: undi.pxs->undi_transmit.Protocol = P_RARP; break;
+ default: printf ( "Unknown packet type %hx\n", t );
+ return;
+ }
+
+ /* Store packet length in TBD */
+ undi.xmit_data->tbd.ImmedLength = s;
+
+ /* Check to see if data to be transmitted is currently in base
+ * memory. If not, allocate temporary storage in base memory
+ * and copy it there.
+ */
+ if ( SEGMENT( p ) <= 0xffff ) {
+ undi.xmit_data->tbd.Xmit.segment = SEGMENT( p );
+ undi.xmit_data->tbd.Xmit.offset = OFFSET( p );
+ } else {
+ memcpy ( undi.xmit_buffer, p, s );
+ undi.xmit_data->tbd.Xmit.segment = SEGMENT( undi.xmit_buffer );
+ undi.xmit_data->tbd.Xmit.offset = OFFSET( undi.xmit_buffer );
+ }
+
+ eb_pxenv_undi_transmit_packet();
+}
+
+/**************************************************************************
+DISABLE - Turn off ethernet interface
+***************************************************************************/
+static void undi_disable ( struct dev *dev __unused ) {
+ undi_full_shutdown();
+ free_base_mem_data();
+}
+
+/**************************************************************************
+PROBE - Look for an adapter, this routine's visible to the outside
+***************************************************************************/
+
+/* Locate an UNDI driver by first scanning through base memory for an
+ * installed driver and then by scanning for UNDI ROMs and attempting
+ * to install their drivers.
+ */
+
+static int hunt_pixies_and_undi_roms ( void ) {
+ static uint8_t hunt_type = HUNT_FOR_PIXIES;
+
+ if ( hunt_type == HUNT_FOR_PIXIES ) {
+ if ( hunt_pixie() ) {
+ return 1;
+ }
+ }
+ hunt_type = HUNT_FOR_UNDI_ROMS;
+ while ( hunt_undi_rom() ) {
+ if ( undi_loader() ) {
+ return 1;
+ }
+ undi_full_shutdown(); /* Free any allocated memory */
+ }
+ hunt_type = HUNT_FOR_PIXIES;
+ return 0;
+}
+
+/* The actual Etherboot probe routine.
+ */
+
+static int undi_probe(struct dev *dev, struct pci_device *pci)
+{
+ struct nic *nic = (struct nic *)dev;
+
+ /* Zero out global undi structure */
+ memset ( &undi, 0, sizeof(undi) );
+
+ /* Store PCI parameters; we will need them to initialize the
+ * UNDI driver later. If not a PCI device, leave as 0.
+ */
+ if ( pci ) {
+ memcpy ( &undi.pci, pci, sizeof(undi.pci) );
+ }
+
+ /* Find the BIOS' $PnP structure */
+ if ( ! hunt_pnp_bios() ) {
+ /* Not all PXE stacks actually insist on a PnP BIOS.
+ * In particular, an Etherboot PXE stack will work
+ * just fine without one.
+ *
+ * We used to make this a fatal error, but now we just
+ * warn and continue. Note that this is necessary in
+ * order to be able to debug the Etherboot PXE stack
+ * under Bochs, since Bochs' BIOS is non-PnP.
+ */
+ printf ( "WARNING: No PnP BIOS found\n" );
+ }
+
+ /* Allocate base memory data structures */
+ if ( ! allocate_base_mem_data() ) return 0;
+
+ /* Search thoroughly for UNDI drivers */
+ for ( ; hunt_pixies_and_undi_roms(); undi_full_shutdown() ) {
+ /* Try to initialise UNDI driver */
+ printf ( "Initializing UNDI driver. Please wait...\n" );
+ if ( ! undi_full_startup() ) {
+ if ( undi.pxs->Status ==
+ PXENV_STATUS_UNDI_MEDIATEST_FAILED ) {
+ printf ( "Cable not connected (code %#hx)\n",
+ PXENV_STATUS_UNDI_MEDIATEST_FAILED );
+ }
+ continue;
+ }
+ /* Basic information: MAC, IO addr, IRQ */
+ if ( ! eb_pxenv_undi_get_information() ) continue;
+ printf ( "Initialized UNDI NIC with IO %#hx, IRQ %d, MAC %!\n",
+ undi.pxs->undi_get_information.BaseIo,
+ undi.pxs->undi_get_information.IntNumber,
+ undi.pxs->undi_get_information.CurrentNodeAddress );
+ /* Fill out MAC address in nic structure */
+ memcpy ( nic->node_addr,
+ undi.pxs->undi_get_information.CurrentNodeAddress,
+ ETH_ALEN );
+ /* More diagnostic information including link speed */
+ if ( ! eb_pxenv_undi_get_iface_info() ) continue;
+ printf ( "NDIS type %s interface at %d Mbps\n",
+ undi.pxs->undi_get_iface_info.IfaceType,
+ undi.pxs->undi_get_iface_info.LinkSpeed / 1000000 );
+ dev->disable = undi_disable;
+ nic->poll = undi_poll;
+ nic->transmit = undi_transmit;
+ return 1;
+ }
+ undi_disable ( dev ); /* To free base memory structures */
+ return 0;
+}
+
+static int undi_isa_probe ( struct dev *dev,
+ unsigned short *probe_addrs __unused ) {
+ return undi_probe ( dev, NULL );
+}
+
+
+/* UNDI driver states that it is suitable for any PCI NIC (i.e. any
+ * PCI device of class PCI_CLASS_NETWORK_ETHERNET). If there are any
+ * obscure UNDI NICs that have the incorrect PCI class, add them to
+ * this list.
+ */
+static struct pci_id undi_nics[] = {
+ /* PCI_ROM(0x0000, 0x0000, "undi", "UNDI adaptor"), */
+};
+
+static struct pci_driver undi_driver __pci_driver = {
+ .type = NIC_DRIVER,
+ .name = "UNDI",
+ .probe = undi_probe,
+ .ids = undi_nics,
+ .id_count = sizeof(undi_nics)/sizeof(undi_nics[0]),
+ .class = PCI_CLASS_NETWORK_ETHERNET,
+};
+
+static struct isa_driver undi_isa_driver __isa_driver = {
+ .type = NIC_DRIVER,
+ .name = "UNDI",
+ .probe = undi_isa_probe,
+ .ioaddrs = 0,
+};
+
+#endif /* PCBIOS */
diff --git a/src/arch/i386/drivers/net/undi.h b/src/arch/i386/drivers/net/undi.h
new file mode 100644
index 00000000..3ba76c35
--- /dev/null
+++ b/src/arch/i386/drivers/net/undi.h
@@ -0,0 +1,178 @@
+/**************************************************************************
+Etherboot - BOOTP/TFTP Bootstrap Program
+UNDI NIC driver for Etherboot - header file
+
+This file Copyright (C) 2003 Michael Brown <mbrown@fensystems.co.uk>
+of Fen Systems Ltd. (http://www.fensystems.co.uk/). All rights
+reserved.
+
+$Id$
+***************************************************************************/
+
+/*
+ * 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; either version 2, or (at
+ * your option) any later version.
+ */
+
+#include "pxe.h"
+#include "pic8259.h"
+
+/* A union that can function as the parameter block for any UNDI API call.
+ */
+typedef t_PXENV_ANY pxenv_structure_t;
+
+/* BIOS PnP parameter block. We scan for this so that we can pass it
+ * to the UNDI driver.
+ */
+
+#define PNP_BIOS_SIGNATURE ( ('$'<<0) + ('P'<<8) + ('n'<<16) + ('P'<<24) )
+typedef struct pnp_bios {
+ uint32_t signature;
+ uint8_t version;
+ uint8_t length;
+ uint16_t control;
+ uint8_t checksum;
+ uint8_t dontcare[24];
+} PACKED pnp_bios_t;
+
+/* Structures within the PXE ROM.
+ */
+
+#define ROM_SIGNATURE 0xaa55
+typedef struct rom {
+ uint16_t signature;
+ uint8_t unused[0x14];
+ uint16_t undi_rom_id_off;
+ uint16_t pcir_off;
+ uint16_t pnp_off;
+} PACKED rom_t;
+
+#define PCIR_SIGNATURE ( ('P'<<0) + ('C'<<8) + ('I'<<16) + ('R'<<24) )
+typedef struct pcir_header {
+ uint32_t signature;
+ uint16_t vendor_id;
+ uint16_t device_id;
+} PACKED pcir_header_t;
+
+#define PNP_SIGNATURE ( ('$'<<0) + ('P'<<8) + ('n'<<16) + ('P'<<24) )
+typedef struct pnp_header {
+ uint32_t signature;
+ uint8_t struct_revision;
+ uint8_t length;
+ uint16_t next;
+ uint8_t reserved;
+ uint8_t checksum;
+ uint16_t id[2];
+ uint16_t manuf_str_off;
+ uint16_t product_str_off;
+ uint8_t base_type;
+ uint8_t sub_type;
+ uint8_t interface_type;
+ uint8_t indicator;
+ uint16_t boot_connect_off;
+ uint16_t disconnect_off;
+ uint16_t initialise_off;
+ uint16_t reserved2;
+ uint16_t info;
+} PACKED pnp_header_t;
+
+#define UNDI_SIGNATURE ( ('U'<<0) + ('N'<<8) + ('D'<<16) + ('I'<<24) )
+typedef struct undi_rom_id {
+ uint32_t signature;
+ uint8_t struct_length;
+ uint8_t struct_cksum;
+ uint8_t struct_rev;
+ uint8_t undi_rev[3];
+ uint16_t undi_loader_off;
+ uint16_t stack_size;
+ uint16_t data_size;
+ uint16_t code_size;
+} PACKED undi_rom_id_t;
+
+/* Nontrivial IRQ handler structure */
+typedef struct {
+ segoff_t chain_to;
+ uint8_t irq_chain, pad1, pad2, pad3;
+ segoff_t entry;
+ uint16_t count_all;
+ uint16_t count_ours;
+ t_PXENV_UNDI_ISR undi_isr;
+ char code[0];
+} PACKED undi_irq_handler_t ;
+
+/* Storage buffers that we need in base memory. We collect these into
+ * a single structure to make allocation simpler.
+ */
+
+typedef struct undi_base_mem_xmit_data {
+ MAC_ADDR destaddr;
+ t_PXENV_UNDI_TBD tbd;
+} undi_base_mem_xmit_data_t;
+
+typedef struct undi_base_mem_data {
+ pxenv_structure_t pxs;
+ undi_base_mem_xmit_data_t xmit_data;
+ char xmit_buffer[ETH_FRAME_LEN];
+ /* Must be last in structure and paragraph-aligned */
+ union {
+ char e820mangler[0];
+ char irq_handler[0];
+ undi_irq_handler_t nontrivial_irq_handler;
+ } __attribute__ ((aligned(16)));
+} undi_base_mem_data_t;
+
+/* Macros and data structures used when freeing bits of base memory
+ * used by the UNDI driver.
+ */
+
+#define FIRING_SQUAD_TARGET_SIZE 8
+#define FIRING_SQUAD_TARGET_INDEX(x) ( (x) / FIRING_SQUAD_TARGET_SIZE )
+#define FIRING_SQUAD_TARGET_BIT(x) ( (x) % FIRING_SQUAD_TARGET_SIZE )
+typedef struct firing_squad_lineup {
+ uint8_t targets[ 640 / FIRING_SQUAD_TARGET_SIZE ];
+} firing_squad_lineup_t;
+typedef enum firing_squad_shoot {
+ DONTSHOOT = 0,
+ SHOOT = 1
+} firing_squad_shoot_t;
+
+/* Driver private data structure.
+ */
+
+typedef struct undi {
+ /* Pointers to various data structures */
+ pnp_bios_t *pnp_bios;
+ rom_t *rom;
+ undi_rom_id_t *undi_rom_id;
+ pxe_t *pxe;
+ pxenv_structure_t *pxs;
+ undi_base_mem_xmit_data_t *xmit_data;
+ /* Pointers and sizes to keep track of allocated base memory */
+ undi_base_mem_data_t *base_mem_data;
+ void *driver_code;
+ size_t driver_code_size;
+ void *driver_data;
+ size_t driver_data_size;
+ char *xmit_buffer;
+ /* Flags. We keep our own instead of trusting the UNDI driver
+ * to have implemented PXENV_UNDI_GET_STATE correctly. Plus
+ * there's the small issue of PXENV_UNDI_GET_STATE being the
+ * same API call as PXENV_STOP_UNDI...
+ */
+ uint8_t prestarted; /* pxenv_start_undi() has been called */
+ uint8_t started; /* pxenv_undi_startup() has been called */
+ uint8_t initialized; /* pxenv_undi_initialize() has been called */
+ uint8_t opened; /* pxenv_undi_open() has been called */
+ /* Parameters that we need to store for future reference
+ */
+ struct pci_device pci;
+ irq_t irq;
+} undi_t;
+
+/* Constants
+ */
+
+#define HUNT_FOR_PIXIES 0
+#define HUNT_FOR_UNDI_ROMS 1
diff --git a/src/arch/i386/firmware/pcbios/basemem.c b/src/arch/i386/firmware/pcbios/basemem.c
new file mode 100644
index 00000000..c93b19e9
--- /dev/null
+++ b/src/arch/i386/firmware/pcbios/basemem.c
@@ -0,0 +1,317 @@
+#ifdef PCBIOS
+
+#include "etherboot.h"
+#include "realmode.h" /* for real_mode_stack */
+
+/* Routines to allocate base memory in a BIOS-compatible way, by
+ * updating the Free Base Memory Size counter at 40:13h.
+ *
+ * Michael Brown <mbrown@fensystems.co.uk> (mcb30)
+ * $Id$
+ */
+
+#define fbms ( ( uint16_t * ) phys_to_virt ( 0x413 ) )
+#define BASE_MEMORY_MAX ( 640 )
+#define FREE_BLOCK_MAGIC ( ('!'<<0) + ('F'<<8) + ('R'<<16) + ('E'<<24) )
+#define FREE_BASE_MEMORY ( (uint32_t) ( *fbms << 10 ) )
+
+/* Prototypes */
+void * _allot_base_memory ( size_t size );
+void _forget_base_memory ( void *ptr, size_t size );
+
+typedef struct free_base_memory_block {
+ uint32_t magic;
+ uint16_t size_kb;
+} free_base_memory_block_t;
+
+/* Return amount of free base memory in bytes
+ */
+
+uint32_t get_free_base_memory ( void ) {
+ return FREE_BASE_MEMORY;
+}
+
+/* Start of our image in base memory.
+ */
+#define __text16_nocompress __attribute__ ((section (".text16.nocompress")))
+uint32_t image_basemem __text16_nocompress = 0;
+uint32_t image_basemem_size __text16_nocompress = 0;
+
+/* Allot/free the real-mode stack
+ */
+
+void allot_real_mode_stack ( void )
+{
+ void *new_real_mode_stack;
+
+ if ( lock_real_mode_stack )
+ return;
+
+ /* This is evil hack.
+ * Until we have a real_mode stack use 0x7c00.
+ * Except for 0 - 0x600 membory below 0x7c00 is hardly every used.
+ * This stack should never be used unless the stack allocation fails,
+ * or if someone has placed a print statement in a dangerous location.
+ */
+ if (!real_mode_stack) {
+ real_mode_stack = 0x7c00;
+ }
+ new_real_mode_stack = _allot_base_memory ( real_mode_stack_size );
+ if ( ! new_real_mode_stack ) {
+ printf ( "FATAL: No real-mode stack\n" );
+ while ( 1 ) {};
+ }
+ real_mode_stack = virt_to_phys ( new_real_mode_stack );
+ get_memsizes();
+}
+
+void forget_real_mode_stack ( void )
+{
+ if ( lock_real_mode_stack )
+ return;
+
+ if ( real_mode_stack) {
+ _forget_base_memory ( phys_to_virt(real_mode_stack),
+ real_mode_stack_size );
+ /* get_memsizes() uses the real_mode stack we just freed
+ * for it's BIOS calls.
+ */
+ get_memsizes();
+ real_mode_stack = 0;
+ }
+}
+
+/* Allocate N bytes of base memory. Amount allocated will be rounded
+ * up to the nearest kB, since that's the granularity of the BIOS FBMS
+ * counter. Returns NULL if memory cannot be allocated.
+ */
+
+static void * _allot_base_memory ( size_t size )
+{
+ uint16_t size_kb = ( size + 1023 ) >> 10;
+ void *ptr = NULL;
+
+#ifdef DEBUG_BASEMEM
+ printf ( "Trying to allocate %d kB of base memory from %d kB free\n",
+ size_kb, *fbms );
+#endif
+
+ /* Free up any unused memory before we start */
+ free_unused_base_memory();
+
+ /* Check available base memory */
+ if ( size_kb > *fbms ) { return NULL; }
+
+ /* Reduce available base memory */
+ *fbms -= size_kb;
+
+ /* Calculate address of memory allocated */
+ ptr = phys_to_virt ( FREE_BASE_MEMORY );
+
+ /* Zero out memory. We do this so that allocation of
+ * already-used space will show up in the form of a crash as
+ * soon as possible.
+ *
+ * Update: there's another reason for doing this. If we don't
+ * zero the contents, then they could still retain our "free
+ * block" markers and be liable to being freed whenever a
+ * base-memory allocation routine is next called.
+ */
+ memset ( ptr, 0, size_kb << 10 );
+
+#ifdef DEBUG_BASEMEM
+ printf ( "Allocated %d kB at [%x,%x)\n", size_kb,
+ virt_to_phys ( ptr ),
+ virt_to_phys ( ptr ) + size_kb * 1024 );
+#endif
+
+ return ptr;
+}
+
+void * allot_base_memory ( size_t size )
+{
+ void *ptr;
+
+ /* Free real-mode stack, allocate memory, reallocate real-mode
+ * stack.
+ */
+ forget_real_mode_stack();
+ ptr = _allot_base_memory ( size );
+ get_memsizes();
+ return ptr;
+}
+
+/* Free base memory allocated by allot_base_memory. The BIOS provides
+ * nothing better than a LIFO mechanism for freeing memory (i.e. it
+ * just has the single "total free memory" counter), but we improve
+ * upon this slightly; as long as you free all the allotted blocks, it
+ * doesn't matter what order you free them in. (This will only work
+ * for blocks that are freed via forget_base_memory()).
+ *
+ * Yes, it's annoying that you have to remember the size of the blocks
+ * you've allotted. However, since our granularity of allocation is
+ * 1K, the alternative is to risk wasting the occasional kB of base
+ * memory, which is a Bad Thing. Really, you should be using as
+ * little base memory as possible, so consider the awkwardness of the
+ * API to be a feature! :-)
+ */
+
+static void _forget_base_memory ( void *ptr, size_t size )
+{
+ uint16_t remainder = virt_to_phys(ptr) & 1023;
+ uint16_t size_kb = ( size + remainder + 1023 ) >> 10;
+ free_base_memory_block_t *free_block =
+ ( free_base_memory_block_t * ) ( ptr - remainder );
+
+ if ( ( ptr == NULL ) || ( size == 0 ) ) {
+ return;
+ }
+
+#ifdef DEBUG_BASEMEM
+ printf ( "Trying to free %d bytes base memory at 0x%x\n",
+ size, virt_to_phys ( ptr ) );
+ if ( remainder > 0 ) {
+ printf ( "WARNING: destructively expanding free block "
+ "downwards to 0x%x\n",
+ virt_to_phys ( ptr - remainder ) );
+ }
+#endif
+
+ /* Mark every kilobyte within this block as free. This is
+ * overkill for normal purposes, but helps when something has
+ * allocated base memory with a granularity finer than the
+ * BIOS granularity of 1kB. PXE ROMs tend to do this when
+ * they allocate their own memory. This method allows us to
+ * free their blocks (admittedly in a rather dangerous,
+ * tread-on-anything-either-side sort of way, but there's no
+ * other way to do it).
+ *
+ * Since we're marking every kB as free, there's actually no
+ * need for recording the size of the blocks. However, we
+ * keep this in so that debug messages are friendlier. It
+ * probably adds around 8 bytes to the overall code size.
+ */
+ while ( size_kb > 0 ) {
+ /* Mark this block as unused */
+ free_block->magic = FREE_BLOCK_MAGIC;
+ free_block->size_kb = size_kb;
+ /* Move up by 1 kB */
+ free_block = (void *)(((char *)free_block) + (1 << 10));
+ size_kb--;
+ }
+
+ /* Free up unused base memory */
+ free_unused_base_memory();
+}
+
+void forget_base_memory ( void *ptr, size_t size )
+{
+ /* Free memory, free real-mode stack, re-allocate real-mode
+ * stack. Do this so that we don't end up wasting a huge
+ * block of memory trapped behind the real-mode stack.
+ */
+ _forget_base_memory ( ptr, size );
+ forget_real_mode_stack();
+ get_memsizes();
+}
+
+/* Do the actual freeing of memory. This is split out from
+ * forget_base_memory() so that it may be called separately. It
+ * should be called whenever base memory is deallocated by an external
+ * entity (if we can detect that it has done so) so that we get the
+ * chance to free up our own blocks.
+ */
+static void free_unused_base_memory ( void ) {
+ free_base_memory_block_t *free_block = NULL;
+
+ /* Try to release memory back to the BIOS. Free all
+ * consecutive blocks marked as free.
+ */
+ while ( 1 ) {
+ /* Calculate address of next potential free block */
+ free_block = ( free_base_memory_block_t * )
+ phys_to_virt ( FREE_BASE_MEMORY );
+
+ /* Stop processing if we're all the way up to 640K or
+ * if this is not a free block
+ */
+ if ( ( *fbms == BASE_MEMORY_MAX ) ||
+ ( free_block->magic != FREE_BLOCK_MAGIC ) ) {
+ break;
+ }
+
+ /* Return memory to BIOS */
+ *fbms += free_block->size_kb;
+
+#ifdef DEBUG_BASEMEM
+ printf ( "Freed %d kB base memory, %d kB now free\n",
+ free_block->size_kb, *fbms );
+#endif
+
+ /* Zero out freed block. We do this in case
+ * the block contained any structures that
+ * might be located by scanning through
+ * memory.
+ */
+ memset ( free_block, 0, free_block->size_kb << 10 );
+ }
+}
+
+/* Free base memory used by the prefix. Called once at start of
+ * Etherboot by arch_main().
+ */
+void forget_prefix_base_memory ( void )
+{
+ /* runtime_start_kb is _text rounded down to a physical kB boundary */
+ uint32_t runtime_start_kb = virt_to_phys(_text) & ~0x3ff;
+ /* prefix_size_kb is the prefix size excluding any portion
+ * that overlaps into the first kB used by the runtime image
+ */
+ uint32_t prefix_size_kb = runtime_start_kb - image_basemem;
+
+#ifdef DEBUG_BASEMEM
+ printf ( "Attempting to free base memory used by prefix\n" );
+#endif
+
+ /* If the decompressor is in allocated base memory
+ * *and* the Etherboot text is in base
+ * memory, then free the decompressor.
+ */
+ if ( ( image_basemem >= FREE_BASE_MEMORY ) &&
+ ( runtime_start_kb >= FREE_BASE_MEMORY ) &&
+ ( runtime_start_kb <= ( BASE_MEMORY_MAX << 10 ) ) )
+ {
+ forget_base_memory ( phys_to_virt ( image_basemem ),
+ prefix_size_kb );
+ /* Update image_basemem and image_basemem_size to
+ * indicate that our allocation now starts with _text
+ */
+ image_basemem = runtime_start_kb;
+ image_basemem_size -= prefix_size_kb;
+ }
+}
+
+/* Free base memory used by the runtime image. Called after
+ * relocation by arch_relocated_from().
+ */
+void forget_runtime_base_memory ( unsigned long old_addr )
+{
+ /* text_start_kb is old _text rounded down to a physical KB boundary */
+ uint32_t old_text_start_kb = old_addr & ~0x3ff;
+
+#ifdef DEBUG_BASEMEM
+ printf ( "Attempting to free base memory used by runtime image\n" );
+#endif
+
+ if ( ( image_basemem >= FREE_BASE_MEMORY ) &&
+ ( image_basemem == old_text_start_kb ) )
+ {
+ forget_base_memory ( phys_to_virt ( image_basemem ),
+ image_basemem_size );
+ /* Update image_basemem to show no longer in use */
+ image_basemem = 0;
+ image_basemem_size = 0;
+ }
+}
+
+#endif /* PCBIOS */
diff --git a/src/arch/i386/firmware/pcbios/bios.c b/src/arch/i386/firmware/pcbios/bios.c
new file mode 100644
index 00000000..70f26703
--- /dev/null
+++ b/src/arch/i386/firmware/pcbios/bios.c
@@ -0,0 +1,155 @@
+/* Etherboot routines for PCBIOS firmware.
+ *
+ * Body of routines taken from old pcbios.S
+ */
+
+#ifdef PCBIOS
+
+#include "etherboot.h"
+#include "realmode.h"
+#include "segoff.h"
+
+#define CF ( 1 << 0 )
+
+/**************************************************************************
+CURRTICKS - Get Time
+Use direct memory access to BIOS variables, longword 0040:006C (ticks
+today) and byte 0040:0070 (midnight crossover flag) instead of calling
+timeofday BIOS interrupt.
+**************************************************************************/
+#if defined(CONFIG_TSC_CURRTICKS)
+#undef CONFIG_BIOS_CURRTICKS
+#else
+#define CONFIG_BIOS_CURRTICKS 1
+#endif
+#if defined(CONFIG_BIOS_CURRTICKS)
+unsigned long currticks (void)
+{
+ static uint32_t days = 0;
+ uint32_t *ticks = VIRTUAL(0x0040,0x006c);
+ uint8_t *midnight = VIRTUAL(0x0040,0x0070);
+
+ /* Re-enable interrupts so that the timer interrupt can occur
+ */
+ RM_FRAGMENT(rm_currticks,
+ "sti\n\t"
+ "nop\n\t"
+ "nop\n\t"
+ "cli\n\t"
+ );
+
+ real_call ( rm_currticks, NULL, NULL );
+
+ if ( *midnight ) {
+ *midnight = 0;
+ days += 0x1800b0;
+ }
+ return ( days + *ticks );
+}
+#endif /* CONFIG_BIOS_CURRTICKS */
+
+/**************************************************************************
+INT15 - Call Interrupt 0x15
+**************************************************************************/
+int int15 ( int ax )
+{
+ struct {
+ reg16_t ax;
+ } PACKED in_stack;
+ struct {
+ reg16_t flags;
+ } PACKED out_stack;
+ reg16_t ret_ax;
+
+ RM_FRAGMENT(rm_int15,
+ "sti\n\t"
+ "popw %ax\n\t"
+ "stc\n\t"
+ "int $0x15\n\t"
+ "pushf\n\t"
+ "cli\n\t"
+ );
+
+ in_stack.ax.word = ax;
+ ret_ax.word = real_call ( rm_int15, &in_stack, &out_stack );
+
+ /* Carry flag clear indicates function not supported */
+ if ( ! ( out_stack.flags.word & CF ) ) return 0;
+ return ret_ax.h;
+}
+
+#ifdef POWERSAVE
+/**************************************************************************
+CPU_NAP - Save power by halting the CPU until the next interrupt
+**************************************************************************/
+void cpu_nap ( void )
+{
+ RM_FRAGMENT(rm_cpu_nap,
+ "sti\n\t"
+ "hlt\n\t"
+ "cli\n\t"
+ );
+
+ real_call ( rm_cpu_nap, NULL, NULL );
+}
+#endif /* POWERSAVE */
+
+#if (TRY_FLOPPY_FIRST > 0)
+/**************************************************************************
+DISK_INIT - Initialize the disk system
+**************************************************************************/
+void disk_init ( void )
+{
+ RM_FRAGMENT(rm_disk_init,
+ "sti\n\t"
+ "xorw %ax,%ax\n\t"
+ "movb $0x80,%dl\n\t"
+ "int $0x13\n\t"
+ "cli\n\t"
+ );
+
+ real_call ( rm_disk_init, NULL, NULL );
+}
+
+/**************************************************************************
+DISK_READ - Read a sector from disk
+**************************************************************************/
+unsigned int pcbios_disk_read ( int drive, int cylinder, int head, int sector,
+ char *buf ) {
+ struct {
+ reg16_t ax;
+ reg16_t cx;
+ reg16_t dx;
+ segoff_t buffer;
+ } PACKED in_stack;
+ struct {
+ reg16_t flags;
+ } PACKED out_stack;
+ reg16_t ret_ax;
+
+ RM_FRAGMENT(rm_pcbios_disk_read,
+ "sti\n\t"
+ "popw %ax\n\t"
+ "popw %cx\n\t"
+ "popw %dx\n\t"
+ "popw %bx\n\t"
+ "popw %es\n\t"
+ "int $0x13\n\t"
+ "pushfw\n\t"
+ "cli\n\t"
+ );
+
+ in_stack.ax.h = 2; /* INT 13,2 - Read disk sector */
+ in_stack.ax.l = 1; /* Read one sector */
+ in_stack.cx.h = cylinder & 0xff;
+ in_stack.cx.l = ( ( cylinder >> 8 ) & 0x3 ) | sector;
+ in_stack.dx.h = head;
+ in_stack.dx.l = drive;
+ in_stack.buffer.segment = SEGMENT ( buf );
+ in_stack.buffer.offset = OFFSET ( buf );
+ ret_ax.word = real_call ( rm_pcbios_disk_read, &in_stack, &out_stack );
+ return ( out_stack.flags.word & CF ) ? ret_ax.word : 0;
+}
+#endif /* TRY_FLOPPY_FIRST */
+
+#endif /* PCBIOS */
diff --git a/src/arch/i386/firmware/pcbios/console.c b/src/arch/i386/firmware/pcbios/console.c
new file mode 100644
index 00000000..f994f06e
--- /dev/null
+++ b/src/arch/i386/firmware/pcbios/console.c
@@ -0,0 +1,85 @@
+/* Etherboot routines for PCBIOS firmware.
+ *
+ * Body of routines taken from old pcbios.S
+ */
+
+#ifdef PCBIOS
+
+#include "etherboot.h"
+#include "realmode.h"
+#include "segoff.h"
+
+#define ZF ( 1 << 6 )
+
+/**************************************************************************
+CONSOLE_PUTC - Print a character on console
+**************************************************************************/
+void console_putc ( int character )
+{
+ struct {
+ reg16_t ax;
+ } PACKED in_stack;
+
+ RM_FRAGMENT(rm_console_putc,
+ "sti\n\t"
+ "popw %ax\n\t"
+ "movb $0x0e, %ah\n\t"
+ "movl $1, %ebx\n\t"
+ "int $0x10\n\t"
+ "cli\n\t"
+ );
+
+ in_stack.ax.l = character;
+ real_call ( rm_console_putc, &in_stack, NULL );
+}
+
+/**************************************************************************
+CONSOLE_GETC - Get a character from console
+**************************************************************************/
+int console_getc ( void )
+{
+ RM_FRAGMENT(rm_console_getc,
+ "sti\n\t"
+ "xorw %ax, %ax\n\t"
+ "int $0x16\n\t"
+ "xorb %ah, %ah\n\t"
+ "cli\n\t"
+ );
+
+ return real_call ( rm_console_getc, NULL, NULL );
+}
+
+/**************************************************************************
+CONSOLE_ISCHAR - Check for keyboard interrupt
+**************************************************************************/
+int console_ischar ( void )
+{
+ RM_FRAGMENT(rm_console_ischar,
+ "sti\n\t"
+ "movb $1, %ah\n\t"
+ "int $0x16\n\t"
+ "pushfw\n\t"
+ "popw %ax\n\t"
+ "cli\n\t"
+ );
+
+ return ( ( real_call ( rm_console_ischar, NULL, NULL ) & ZF ) == 0 );
+}
+
+/**************************************************************************
+GETSHIFT - Get keyboard shift state
+**************************************************************************/
+int getshift ( void )
+{
+ RM_FRAGMENT(rm_getshift,
+ "sti\n\t"
+ "movb $2, %ah\n\t"
+ "int $0x16\n\t"
+ "andw $0x3, %ax\n\t"
+ "cli\n\t"
+ );
+
+ return real_call ( rm_getshift, NULL, NULL );
+}
+
+#endif /* PCBIOS */
diff --git a/src/arch/i386/firmware/pcbios/e820mangler.S b/src/arch/i386/firmware/pcbios/e820mangler.S
new file mode 100644
index 00000000..9349cf2b
--- /dev/null
+++ b/src/arch/i386/firmware/pcbios/e820mangler.S
@@ -0,0 +1,296 @@
+#undef CODE16
+#if defined(PCBIOS)
+#define CODE16
+#endif
+
+#ifdef CODE16
+
+#define BOCHSBP xchgw %bx,%bx
+
+ .text
+ .arch i386
+ .section ".text16", "ax", @progbits
+ .code16
+
+/****************************************************************************
+ * Memory map mangling code
+ ****************************************************************************
+ */
+
+ .globl e820mangler
+e820mangler:
+
+/* Macro to calculate offset of labels within code segment in
+ * installed copy of code.
+ */
+#define INSTALLED(x) ( (x) - e820mangler )
+
+/****************************************************************************
+ * Intercept INT 15 memory calls and remove the hidden memory ranges
+ * from the resulting memory map.
+ ****************************************************************************
+ */
+ .globl _intercept_int15
+_intercept_int15:
+ /* Preserve registers */
+ pushw %bp
+ /* Store %ax for future reference */
+ pushw %ax
+ /* Make INT-style call to old INT15 routine */
+ pushfw
+ lcall %cs:*INSTALLED(_intercepted_int15)
+ /* Preserve flags returned by original E820 routine */
+ pushfw
+ /* Check for valid INT15 routine */
+ jc intercept_int15_exit
+ /* Check for a routine we want to intercept */
+ movw %sp, %bp
+ cmpw $0xe820, 2(%bp)
+ je intercept_e820
+ cmpw $0xe801, 2(%bp)
+ je intercept_e801
+ cmpb $0x88, 3(%bp)
+ je intercept_88
+intercept_int15_exit:
+ /* Restore registers and return */
+ popfw
+ popw %bp /* discard original %ax */
+ popw %bp
+ lret $2 /* 'iret' - flags already loaded */
+
+ .globl _intercepted_int15
+_intercepted_int15: .word 0,0
+
+/****************************************************************************
+ * Exclude an address range from a potentially overlapping address range
+ *
+ * Note: this *can* be called even if the range doesn't overlap; it
+ * will simply return the range unaltered. It copes with all the
+ * possible cases of overlap, including total overlap (which will
+ * modify the range to length zero). If the to-be-excluded range is
+ * in the middle of the target range, then the larger remaining
+ * portion will be returned. If %di is nonzero on entry then the
+ * range will only be truncated from the high end, i.e. the base
+ * address will never be altered. All this in less than 30
+ * instructions. :)
+ *
+ * Parameters:
+ * %eax Base address of memory range
+ * %ecx Length of memory range
+ * %ebx Base address of memory range to exclude
+ * %edx Length of memory range to exclude
+ * %di 0 => truncate either end, 1 => truncate high end only
+ * Returns:
+ * %eax Updated base address of range
+ * %ecx Updated length of range
+ * %ebx,%edx Undefined
+ * All other registers (including %di) preserved
+ *
+ * Note: "ja" is used rather than "jg" because we are comparing
+ * unsigned ints
+ ****************************************************************************
+ */
+#ifdef TEST_EXCLUDE_ALGORITHM
+ .code32
+#endif /* TEST_EXCLUDE_ALGORITHM */
+exclude_memory_range:
+ /* Convert (start,length) to (start,end) */
+ addl %eax, %ecx
+ addl %ebx, %edx
+ /* Calculate "prefix" length */
+ subl %eax, %ebx /* %ebx = "prefix" length */
+ ja 1f
+ xorl %ebx, %ebx /* Truncate to zero if negative */
+1: /* %di == 0 => truncate either end
+ * %di != 0 => truncate only high end
+ */
+ testw %di, %di
+ je use_either
+ cmpl %eax, %edx
+ jbe 99f /* excl. range is below target range */
+use_prefix: /* Use prefix, discard suffix */
+ addl %eax, %ebx /* %ebx = candidate end address */
+ cmpl %ecx, %ebx /* %ecx = min ( %ebx, %ecx ) */
+ ja 1f
+ movl %ebx, %ecx
+1: jmp 99f
+use_either:
+ /* Calculate "suffix" length */
+ subl %ecx, %edx /* %edx = -( "suffix" length ) */
+ jb 1f
+ xorl %edx, %edx /* Truncate to zero if negative */
+1: negl %edx /* %edx = "suffix" length */
+ /* Use whichever is longest of "prefix" and "suffix" */
+ cmpl %ebx, %edx
+ jbe use_prefix
+use_suffix: /* Use suffix, discard prefix */
+ negl %edx
+ addl %ecx, %edx /* %edx = candidate start address */
+ cmpl %eax, %edx /* %eax = max ( %eax, %edx ) */
+ jb 1f
+ movl %edx, %eax
+1:
+99: subl %eax, %ecx /* Convert back to (start,length) */
+ ret
+
+#ifdef TEST_EXCLUDE_ALGORITHM
+ .globl __test_exclude
+__test_exclude:
+ pushl %ebx
+ pushl %edi
+ movl 12(%esp), %eax
+ movl 16(%esp), %ecx
+ movl 20(%esp), %ebx
+ movl 24(%esp), %edx
+ movl 28(%esp), %edi
+ call exclude_memory_range
+ shll $16, %eax
+ orl %ecx, %eax
+ popl %edi
+ popl %ebx
+ ret
+ .code16
+#endif /* TEST_EXCLUDE_ALGORITHM */
+
+/****************************************************************************
+ * Exclude Etherboot-reserved address ranges from a potentially
+ * overlapping address range
+ *
+ * Parameters:
+ * %eax Base address of memory range
+ * %ecx Length of memory range
+ * %di 0 => truncate either end, 1 => truncate high end only
+ * Returns:
+ * %eax Updated base address of range
+ * %ecx Updated length of range
+ * All other registers (including %di) preserved
+ ****************************************************************************
+ */
+exclude_hidden_memory_ranges:
+ pushw %si
+ pushl %ebx
+ pushl %edx
+ movw $INSTALLED(_hide_memory), %si
+2: movl %cs:0(%si), %ebx
+ movl %cs:4(%si), %edx
+ call exclude_memory_range
+ addw $8, %si
+ cmpw $INSTALLED(_hide_memory_end), %si
+ jl 2b
+ popl %edx
+ popl %ebx
+ popw %si
+ ret
+
+ .globl _hide_memory
+_hide_memory:
+ .long 0,0 /* Etherboot text (base,length) */
+ .long 0,0 /* Heap (base,length) */
+_hide_memory_end:
+
+/****************************************************************************
+ * Intercept INT 15,E820 calls and remove the hidden memory ranges
+ * from the resulting memory map.
+ ****************************************************************************
+ */
+#define SMAP ( 0x534d4150 )
+intercept_e820:
+ /* Check for valid E820 routine */
+ cmpl $SMAP, %eax
+ jne intercept_int15_exit
+ /* If base address isn't in the low 4GB, return unaltered
+ * (since we never claim memory above 4GB). WARNING: we cheat
+ * by assuming that no E820 region will straddle the 4GB
+ * boundary: if this is not a valid assumption then things
+ * will probably break.
+ */
+ cmpl $0, %es:4(%di)
+ jne intercept_int15_exit
+ /* Preserve registers */
+ pushl %eax
+ pushl %ecx
+ /* Update returned memory range */
+ movl %es:0(%di), %eax /* Base */
+ movl %es:8(%di), %ecx /* Length */
+ pushw %di
+ xorw %di, %di /* "truncate either end" flag */
+ call exclude_hidden_memory_ranges
+ popw %di
+ movl %eax, %es:0(%di) /* Store updated base */
+ movl %ecx, %es:8(%di) /* Store updated length */
+ /* Restore registers and return */
+ popl %ecx
+ popl %eax
+ jmp intercept_int15_exit
+
+/****************************************************************************
+ * Intercept INT 15,E801 calls and remove the hidden memory ranges
+ * from the resulting memory map.
+ ****************************************************************************
+ */
+intercept_e801:
+ /* Adjust return values */
+ call e801_adjust
+ xchgw %ax, %cx
+ xchgw %bx, %dx
+ call e801_adjust
+ xchgw %ax, %cx
+ xchgw %bx, %dx
+ jmp intercept_int15_exit
+
+ /* %ax = #KB from 1MB+, %bx = #64KB from 16MB+
+ * Return with modified values in %ax, %bx. Preserver other regs.
+ */
+e801_adjust:
+ pushw %di
+ pushl %ecx
+ pushl %eax
+ movw $1, %di /* "truncate only high end" flag */
+
+ /* Truncate #64KB from 16MB+ as appropriate */
+ movw %bx, %cx /* (no need to zero high word) */
+ shll $16, %ecx /* %ecx = length in bytes */
+ movl $(1<<24), %eax /* 16MB start address */
+ call exclude_hidden_memory_ranges
+ shrl $16, %ecx /* %cx = updated length in 64KB */
+ movw %cx, %bx /* Return in %bx */
+
+ /* Truncate #KB from 1MB+ as appropriate */
+ popw %cx /* Orig. %ax (high word already 0) */
+ shll $10, %ecx /* %ecx = length in bytes */
+ shrl $4, %eax /* 1MB start address */
+ call exclude_hidden_memory_ranges
+ shrl $10, %ecx /* %cx = updated length in KB */
+ pushw %cx /* Will be picked up in %eax */
+
+ popl %eax
+ popl %ecx
+ popw %di
+ ret
+
+/****************************************************************************
+ * Intercept INT 15,88 calls and remove the hidden memory ranges
+ * from the resulting memory map.
+ ****************************************************************************
+ */
+intercept_88:
+ pushw %bx /* E801 adjust, ignore %bx */
+ call e801_adjust
+ popw %bx
+ jmp intercept_int15_exit
+
+ .globl e820mangler_end
+e820mangler_end:
+
+ .globl _e820mangler_size
+ .equ _e820mangler_size, e820mangler_end - e820mangler
+ .globl e820mangler_size
+e820mangler_size:
+ .word _e820mangler_size
+
+#else
+
+ .globl _e820mangler_size
+ .equ _e820mangler_size, 0
+
+#endif /* CODE16 */
diff --git a/src/arch/i386/firmware/pcbios/hidemem.c b/src/arch/i386/firmware/pcbios/hidemem.c
new file mode 100644
index 00000000..a9ae001e
--- /dev/null
+++ b/src/arch/i386/firmware/pcbios/hidemem.c
@@ -0,0 +1,94 @@
+/* Utility functions to hide Etherboot by manipulating the E820 memory
+ * map. These could go in memsizes.c, but are placed here because not
+ * all images will need them.
+ */
+
+#include "etherboot.h"
+#include "hidemem.h"
+
+#ifdef CODE16
+
+static int mangling = 0;
+static void *mangler = NULL;
+
+#define INSTALLED(x) ( (typeof(&x)) ( (void*)(&x) - (void*)e820mangler \
+ + mangler ) )
+#define intercept_int15 INSTALLED(_intercept_int15)
+#define intercepted_int15 INSTALLED(_intercepted_int15)
+#define hide_memory INSTALLED(_hide_memory)
+#define INT15_VECTOR ( (segoff_t*) ( phys_to_virt( 4 * 0x15 ) ) )
+
+int install_e820mangler ( void *new_mangler ) {
+ if ( mangling ) return 0;
+ memcpy ( new_mangler, &e820mangler, e820mangler_size );
+ mangler = new_mangler;
+ return 1;
+}
+
+/* Intercept INT15 calls and pass them through the mangler. The
+ * mangler must have been copied to base memory before making this
+ * call, and "mangler" must point to the base memory copy, which must
+ * be 16-byte aligned.
+ */
+int hide_etherboot ( void ) {
+ if ( mangling ) return 1;
+ if ( !mangler ) return 0;
+
+ /* Hook INT15 handler */
+ *intercepted_int15 = *INT15_VECTOR;
+ (*hide_memory)[0].start = virt_to_phys(_text);
+ (*hide_memory)[0].length = _end - _text;
+ /* IMPORTANT, possibly even FIXME:
+ *
+ * Etherboot has a tendency to claim a very large area of
+ * memory as possible heap; enough to make it impossible to
+ * load an OS if we hide all of it. We hide only the portion
+ * that's currently in use. This means that we MUST NOT
+ * perform further allocations from the heap while the mangler
+ * is active.
+ */
+ (*hide_memory)[1].start = heap_ptr;
+ (*hide_memory)[1].length = heap_bot - heap_ptr;
+ INT15_VECTOR->segment = SEGMENT(mangler);
+ INT15_VECTOR->offset = 0;
+
+ mangling = 1;
+ return 1;
+}
+
+int unhide_etherboot ( void ) {
+ if ( !mangling ) return 1;
+
+ /* Restore original INT15 handler
+ */
+ if ( VIRTUAL(INT15_VECTOR->segment,INT15_VECTOR->offset) != mangler ) {
+ /* Oh dear... */
+
+#ifdef WORK_AROUND_BPBATCH_BUG
+ /* BpBatch intercepts INT15, so can't unhook it, and
+ * then proceeds to ignore our PXENV_KEEP_UNDI return
+ * status, which means that it ends up zeroing out the
+ * INT15 handler routine.
+ *
+ * This rather ugly hack involves poking into
+ * BpBatch's code and changing it's stored value for
+ * the "next handler" in the INT15 chain.
+ */
+ segoff_t *bp_chain = VIRTUAL ( 0x0060, 0x8254 );
+
+ if ( ( bp_chain->segment == SEGMENT(mangler) ) &&
+ ( bp_chain->offset == 0 ) ) {
+ printf ( "\nBPBATCH bug workaround enabled\n" );
+ *bp_chain = *intercepted_int15;
+ }
+#endif /* WORK_AROUND_BPBATCH_BUG */
+
+ return 0;
+ }
+ *INT15_VECTOR = *intercepted_int15;
+
+ mangling = 0;
+ return 1;
+}
+
+#endif /* CODE16 */
diff --git a/src/arch/i386/firmware/pcbios/memsizes.c b/src/arch/i386/firmware/pcbios/memsizes.c
new file mode 100644
index 00000000..18ae86da
--- /dev/null
+++ b/src/arch/i386/firmware/pcbios/memsizes.c
@@ -0,0 +1,201 @@
+#ifdef PCBIOS
+
+#include "etherboot.h"
+#include "realmode.h"
+
+#define CF ( 1 << 0 )
+
+#ifndef MEMSIZES_DEBUG
+#define MEMSIZES_DEBUG 0
+#endif
+
+/* by Eric Biederman */
+
+struct meminfo meminfo;
+
+/**************************************************************************
+BASEMEMSIZE - Get size of the conventional (base) memory
+**************************************************************************/
+unsigned short basememsize ( void )
+{
+ RM_FRAGMENT(rm_basememsize,
+ "int $0x12\n\t"
+ );
+ return real_call ( rm_basememsize, NULL, NULL );
+}
+
+/**************************************************************************
+MEMSIZE - Determine size of extended memory
+**************************************************************************/
+unsigned int memsize ( void )
+{
+ struct {
+ reg16_t ax;
+ } PACKED in_stack;
+ struct {
+ reg16_t ax;
+ reg16_t bx;
+ reg16_t cx;
+ reg16_t dx;
+ reg16_t flags;
+ } PACKED out_stack;
+ int memsize;
+
+ RM_FRAGMENT(rm_memsize,
+ /* Some buggy BIOSes don't clear/set carry on pass/error of
+ * e801h memory size call or merely pass cx,dx through without
+ * changing them, so we set carry and zero cx,dx before call.
+ */
+ "stc\n\t"
+ "xorw %cx,%cx\n\t"
+ "xorw %dx,%dx\n\t"
+ "popw %ax\n\t"
+ "int $0x15\n\t"
+ "pushfw\n\t"
+ "pushw %dx\n\t"
+ "pushw %cx\n\t"
+ "pushw %bx\n\t"
+ "pushw %ax\n\t"
+ );
+
+ /* Try INT 15,e801 first */
+ in_stack.ax.word = 0xe801;
+ real_call ( rm_memsize, &in_stack, &out_stack );
+ if ( out_stack.flags.word & CF ) {
+ /* INT 15,e801 not supported: try INT 15,88 */
+ in_stack.ax.word = 0x8800;
+ memsize = real_call ( rm_memsize, &in_stack, &out_stack );
+ } else {
+ /* Some BIOSes report extended memory via ax,bx rather
+ * than cx,dx
+ */
+ if ( (out_stack.cx.word==0) && (out_stack.dx.word==0) ) {
+ /* Use ax,bx */
+ memsize = ( out_stack.bx.word<<6 ) + out_stack.ax.word;
+ } else {
+ /* Use cx,dx */
+ memsize = ( out_stack.dx.word<<6 ) + out_stack.cx.word;
+ }
+ }
+ return memsize;
+}
+
+#define SMAP ( 0x534d4150 )
+int meme820 ( struct e820entry *buf, int count )
+{
+ struct {
+ reg16_t flags;
+ reg32_t eax;
+ reg32_t ebx;
+ struct e820entry entry;
+ } PACKED stack;
+ int index = 0;
+
+ RM_FRAGMENT(rm_meme820,
+ "addw $6, %sp\n\t" /* skip flags, eax */
+ "popl %ebx\n\t"
+ "pushw %ss\n\t" /* es:di = ss:sp */
+ "popw %es\n\t"
+ "movw %sp, %di\n\t"
+ "movl $0xe820, %eax\n\t"
+ "movl $" RM_STR(SMAP) ", %edx\n\t"
+ "movl $" RM_STR(E820ENTRY_SIZE) ", %ecx\n\t"
+ "int $0x15\n\t"
+ "pushl %ebx\n\t"
+ "pushl %eax\n\t"
+ "pushfw\n\t"
+ );
+
+ stack.ebx.dword = 0; /* 'EOF' marker */
+ while ( ( index < count ) &&
+ ( ( index == 0 ) || ( stack.ebx.dword != 0 ) ) ) {
+ real_call ( rm_meme820, &stack, &stack );
+ if ( stack.eax.dword != SMAP ) return 0;
+ if ( stack.flags.word & CF ) return 0;
+ buf[index++] = stack.entry;
+ }
+ return index;
+}
+
+void get_memsizes(void)
+{
+ /* Ensure we don't stomp bios data structutres.
+ * the interrupt table: 0x000 - 0x3ff
+ * the bios data area: 0x400 - 0x502
+ * Dos variables: 0x502 - 0x5ff
+ */
+ static const unsigned min_addr = 0x600;
+ unsigned i;
+ unsigned basemem;
+ basemem = get_free_base_memory();
+ meminfo.basememsize = basememsize();
+ meminfo.memsize = memsize();
+#ifndef IGNORE_E820_MAP
+ meminfo.map_count = meme820(meminfo.map, E820MAX);
+#else
+ meminfo.map_count = 0;
+#endif
+ if (meminfo.map_count == 0) {
+ /* If we don't have an e820 memory map fake it */
+ meminfo.map_count = 2;
+ meminfo.map[0].addr = 0;
+ meminfo.map[0].size = meminfo.basememsize << 10;
+ meminfo.map[0].type = E820_RAM;
+ meminfo.map[1].addr = 1024*1024;
+ meminfo.map[1].size = meminfo.memsize << 10;
+ meminfo.map[1].type = E820_RAM;
+ }
+ /* Scrub the e820 map */
+ for(i = 0; i < meminfo.map_count; i++) {
+ if (meminfo.map[i].type != E820_RAM) {
+ continue;
+ }
+ /* Reserve the bios data structures */
+ if (meminfo.map[i].addr < min_addr) {
+ unsigned long delta;
+ delta = min_addr - meminfo.map[i].addr;
+ if (delta > meminfo.map[i].size) {
+ delta = meminfo.map[i].size;
+ }
+ meminfo.map[i].addr = min_addr;
+ meminfo.map[i].size -= delta;
+ }
+ /* Ensure the returned e820 map is in sync
+ * with the actual memory state
+ */
+ if ((meminfo.map[i].addr < 0xa0000) &&
+ ((meminfo.map[i].addr + meminfo.map[i].size) > basemem))
+ {
+ if (meminfo.map[i].addr <= basemem) {
+ meminfo.map[i].size = basemem - meminfo.map[i].addr;
+ } else {
+ meminfo.map[i].addr = basemem;
+ meminfo.map[i].size = 0;
+ }
+ }
+ }
+#if MEMSIZES_DEBUG
+{
+ int i;
+ printf("basememsize %d\n", meminfo.basememsize);
+ printf("memsize %d\n", meminfo.memsize);
+ printf("Memory regions(%d):\n", meminfo.map_count);
+ for(i = 0; i < meminfo.map_count; i++) {
+ unsigned long long r_start, r_end;
+ r_start = meminfo.map[i].addr;
+ r_end = r_start + meminfo.map[i].size;
+ printf("[%X%X, %X%X) type %d\n",
+ (unsigned long)(r_start >> 32),
+ (unsigned long)r_start,
+ (unsigned long)(r_end >> 32),
+ (unsigned long)r_end,
+ meminfo.map[i].type);
+#if defined(CONSOLE_FIRMWARE)
+ sleep(1); /* No way to see 32 entries on a standard 80x25 screen... */
+#endif
+ }
+}
+#endif
+}
+
+#endif /* PCBIOS */
diff --git a/src/arch/i386/include/bits/byteswap.h b/src/arch/i386/include/bits/byteswap.h
new file mode 100644
index 00000000..1bc84fda
--- /dev/null
+++ b/src/arch/i386/include/bits/byteswap.h
@@ -0,0 +1,45 @@
+#ifndef ETHERBOOT_BITS_BYTESWAP_H
+#define ETHERBOOT_BITS_BYTESWAP_H
+
+static inline uint16_t __i386_bswap_16(uint16_t x)
+{
+ __asm__("xchgb %b0,%h0\n\t"
+ : "=q" (x)
+ : "0" (x));
+ return x;
+}
+
+static inline uint32_t __i386_bswap_32(uint32_t x)
+{
+ __asm__("xchgb %b0,%h0\n\t"
+ "rorl $16,%0\n\t"
+ "xchgb %b0,%h0"
+ : "=q" (x)
+ : "0" (x));
+ return x;
+}
+
+
+#define __bswap_constant_16(x) \
+ ((uint16_t)((((uint16_t)(x) & 0x00ff) << 8) | \
+ (((uint16_t)(x) & 0xff00) >> 8)))
+
+#define __bswap_constant_32(x) \
+ ((uint32_t)((((uint32_t)(x) & 0x000000ffU) << 24) | \
+ (((uint32_t)(x) & 0x0000ff00U) << 8) | \
+ (((uint32_t)(x) & 0x00ff0000U) >> 8) | \
+ (((uint32_t)(x) & 0xff000000U) >> 24)))
+
+#define __bswap_16(x) \
+ ((uint16_t)(__builtin_constant_p(x) ? \
+ __bswap_constant_16(x) : \
+ __i386_bswap_16(x)))
+
+
+#define __bswap_32(x) \
+ ((uint32_t)(__builtin_constant_p(x) ? \
+ __bswap_constant_32(x) : \
+ __i386_bswap_32(x)))
+
+
+#endif /* ETHERBOOT_BITS_BYTESWAP_H */
diff --git a/src/arch/i386/include/bits/cpu.h b/src/arch/i386/include/bits/cpu.h
new file mode 100644
index 00000000..2e9b27ba
--- /dev/null
+++ b/src/arch/i386/include/bits/cpu.h
@@ -0,0 +1,243 @@
+#ifndef I386_BITS_CPU_H
+#define I386_BITS_CPU_H
+
+
+/* Sample usage: CPU_FEATURE_P(cpu.x86_capability, FPU) */
+#define CPU_FEATURE_P(CAP, FEATURE) \
+ (!!(CAP[(X86_FEATURE_##FEATURE)/32] & ((X86_FEATURE_##FEATURE) & 0x1f)))
+
+#define NCAPINTS 4 /* Currently we have 4 32-bit words worth of info */
+
+/* Intel-defined CPU features, CPUID level 0x00000001, word 0 */
+#define X86_FEATURE_FPU (0*32+ 0) /* Onboard FPU */
+#define X86_FEATURE_VME (0*32+ 1) /* Virtual Mode Extensions */
+#define X86_FEATURE_DE (0*32+ 2) /* Debugging Extensions */
+#define X86_FEATURE_PSE (0*32+ 3) /* Page Size Extensions */
+#define X86_FEATURE_TSC (0*32+ 4) /* Time Stamp Counter */
+#define X86_FEATURE_MSR (0*32+ 5) /* Model-Specific Registers, RDMSR, WRMSR */
+#define X86_FEATURE_PAE (0*32+ 6) /* Physical Address Extensions */
+#define X86_FEATURE_MCE (0*32+ 7) /* Machine Check Architecture */
+#define X86_FEATURE_CX8 (0*32+ 8) /* CMPXCHG8 instruction */
+#define X86_FEATURE_APIC (0*32+ 9) /* Onboard APIC */
+#define X86_FEATURE_SEP (0*32+11) /* SYSENTER/SYSEXIT */
+#define X86_FEATURE_MTRR (0*32+12) /* Memory Type Range Registers */
+#define X86_FEATURE_PGE (0*32+13) /* Page Global Enable */
+#define X86_FEATURE_MCA (0*32+14) /* Machine Check Architecture */
+#define X86_FEATURE_CMOV (0*32+15) /* CMOV instruction (FCMOVCC and FCOMI too if FPU present) */
+#define X86_FEATURE_PAT (0*32+16) /* Page Attribute Table */
+#define X86_FEATURE_PSE36 (0*32+17) /* 36-bit PSEs */
+#define X86_FEATURE_PN (0*32+18) /* Processor serial number */
+#define X86_FEATURE_CLFLSH (0*32+19) /* Supports the CLFLUSH instruction */
+#define X86_FEATURE_DTES (0*32+21) /* Debug Trace Store */
+#define X86_FEATURE_ACPI (0*32+22) /* ACPI via MSR */
+#define X86_FEATURE_MMX (0*32+23) /* Multimedia Extensions */
+#define X86_FEATURE_FXSR (0*32+24) /* FXSAVE and FXRSTOR instructions (fast save and restore */
+ /* of FPU context), and CR4.OSFXSR available */
+#define X86_FEATURE_XMM (0*32+25) /* Streaming SIMD Extensions */
+#define X86_FEATURE_XMM2 (0*32+26) /* Streaming SIMD Extensions-2 */
+#define X86_FEATURE_SELFSNOOP (0*32+27) /* CPU self snoop */
+#define X86_FEATURE_HT (0*32+28) /* Hyper-Threading */
+#define X86_FEATURE_ACC (0*32+29) /* Automatic clock control */
+#define X86_FEATURE_IA64 (0*32+30) /* IA-64 processor */
+
+/* AMD-defined CPU features, CPUID level 0x80000001, word 1 */
+/* Don't duplicate feature flags which are redundant with Intel! */
+#define X86_FEATURE_SYSCALL (1*32+11) /* SYSCALL/SYSRET */
+#define X86_FEATURE_MMXEXT (1*32+22) /* AMD MMX extensions */
+#define X86_FEATURE_LM (1*32+29) /* Long Mode (x86-64) */
+#define X86_FEATURE_3DNOWEXT (1*32+30) /* AMD 3DNow! extensions */
+#define X86_FEATURE_3DNOW (1*32+31) /* 3DNow! */
+
+/* Transmeta-defined CPU features, CPUID level 0x80860001, word 2 */
+#define X86_FEATURE_RECOVERY (2*32+ 0) /* CPU in recovery mode */
+#define X86_FEATURE_LONGRUN (2*32+ 1) /* Longrun power control */
+#define X86_FEATURE_LRTI (2*32+ 3) /* LongRun table interface */
+
+/* Other features, Linux-defined mapping, word 3 */
+/* This range is used for feature bits which conflict or are synthesized */
+#define X86_FEATURE_CXMMX (3*32+ 0) /* Cyrix MMX extensions */
+#define X86_FEATURE_K6_MTRR (3*32+ 1) /* AMD K6 nonstandard MTRRs */
+#define X86_FEATURE_CYRIX_ARR (3*32+ 2) /* Cyrix ARRs (= MTRRs) */
+#define X86_FEATURE_CENTAUR_MCR (3*32+ 3) /* Centaur MCRs (= MTRRs) */
+
+#define MAX_X86_VENDOR_ID 16
+struct cpuinfo_x86 {
+ uint8_t x86; /* CPU family */
+ uint8_t x86_model;
+ uint8_t x86_mask;
+
+ int cpuid_level; /* Maximum supported CPUID level, -1=no CPUID */
+ unsigned x86_capability[NCAPINTS];
+ char x86_vendor_id[MAX_X86_VENDOR_ID];
+};
+
+
+#define X86_VENDOR_INTEL 0
+#define X86_VENDOR_CYRIX 1
+#define X86_VENDOR_AMD 2
+#define X86_VENDOR_UMC 3
+#define X86_VENDOR_NEXGEN 4
+#define X86_VENDOR_CENTAUR 5
+#define X86_VENDOR_RISE 6
+#define X86_VENDOR_TRANSMETA 7
+#define X86_VENDOR_NSC 8
+#define X86_VENDOR_UNKNOWN 0xff
+
+/*
+ * EFLAGS bits
+ */
+#define X86_EFLAGS_CF 0x00000001 /* Carry Flag */
+#define X86_EFLAGS_PF 0x00000004 /* Parity Flag */
+#define X86_EFLAGS_AF 0x00000010 /* Auxillary carry Flag */
+#define X86_EFLAGS_ZF 0x00000040 /* Zero Flag */
+#define X86_EFLAGS_SF 0x00000080 /* Sign Flag */
+#define X86_EFLAGS_TF 0x00000100 /* Trap Flag */
+#define X86_EFLAGS_IF 0x00000200 /* Interrupt Flag */
+#define X86_EFLAGS_DF 0x00000400 /* Direction Flag */
+#define X86_EFLAGS_OF 0x00000800 /* Overflow Flag */
+#define X86_EFLAGS_IOPL 0x00003000 /* IOPL mask */
+#define X86_EFLAGS_NT 0x00004000 /* Nested Task */
+#define X86_EFLAGS_RF 0x00010000 /* Resume Flag */
+#define X86_EFLAGS_VM 0x00020000 /* Virtual Mode */
+#define X86_EFLAGS_AC 0x00040000 /* Alignment Check */
+#define X86_EFLAGS_VIF 0x00080000 /* Virtual Interrupt Flag */
+#define X86_EFLAGS_VIP 0x00100000 /* Virtual Interrupt Pending */
+#define X86_EFLAGS_ID 0x00200000 /* CPUID detection flag */
+
+/*
+ * Generic CPUID function
+ */
+static inline void cpuid(int op,
+ unsigned int *eax, unsigned int *ebx, unsigned int *ecx, unsigned int *edx)
+{
+ __asm__("cpuid"
+ : "=a" (*eax),
+ "=b" (*ebx),
+ "=c" (*ecx),
+ "=d" (*edx)
+ : "0" (op));
+}
+
+/*
+ * CPUID functions returning a single datum
+ */
+static inline unsigned int cpuid_eax(unsigned int op)
+{
+ unsigned int eax;
+
+ __asm__("cpuid"
+ : "=a" (eax)
+ : "0" (op)
+ : "bx", "cx", "dx");
+ return eax;
+}
+static inline unsigned int cpuid_ebx(unsigned int op)
+{
+ unsigned int eax, ebx;
+
+ __asm__("cpuid"
+ : "=a" (eax), "=b" (ebx)
+ : "0" (op)
+ : "cx", "dx" );
+ return ebx;
+}
+static inline unsigned int cpuid_ecx(unsigned int op)
+{
+ unsigned int eax, ecx;
+
+ __asm__("cpuid"
+ : "=a" (eax), "=c" (ecx)
+ : "0" (op)
+ : "bx", "dx" );
+ return ecx;
+}
+static inline unsigned int cpuid_edx(unsigned int op)
+{
+ unsigned int eax, edx;
+
+ __asm__("cpuid"
+ : "=a" (eax), "=d" (edx)
+ : "0" (op)
+ : "bx", "cx");
+ return edx;
+}
+
+/*
+ * Intel CPU features in CR4
+ */
+#define X86_CR4_VME 0x0001 /* enable vm86 extensions */
+#define X86_CR4_PVI 0x0002 /* virtual interrupts flag enable */
+#define X86_CR4_TSD 0x0004 /* disable time stamp at ipl 3 */
+#define X86_CR4_DE 0x0008 /* enable debugging extensions */
+#define X86_CR4_PSE 0x0010 /* enable page size extensions */
+#define X86_CR4_PAE 0x0020 /* enable physical address extensions */
+#define X86_CR4_MCE 0x0040 /* Machine check enable */
+#define X86_CR4_PGE 0x0080 /* enable global pages */
+#define X86_CR4_PCE 0x0100 /* enable performance counters at ipl 3 */
+#define X86_CR4_OSFXSR 0x0200 /* enable fast FPU save and restore */
+#define X86_CR4_OSXMMEXCPT 0x0400 /* enable unmasked SSE exceptions */
+
+
+#define MSR_K6_EFER 0xC0000080
+/* EFER bits: */
+#define _EFER_SCE 0 /* SYSCALL/SYSRET */
+#define _EFER_LME 8 /* Long mode enable */
+#define _EFER_LMA 10 /* Long mode active (read-only) */
+#define _EFER_NX 11 /* No execute enable */
+
+#define EFER_SCE (1<<_EFER_SCE)
+#define EFER_LME (1<<EFER_LME)
+#define EFER_LMA (1<<EFER_LMA)
+#define EFER_NX (1<<_EFER_NX)
+
+#define rdmsr(msr,val1,val2) \
+ __asm__ __volatile__("rdmsr" \
+ : "=a" (val1), "=d" (val2) \
+ : "c" (msr))
+
+#define wrmsr(msr,val1,val2) \
+ __asm__ __volatile__("wrmsr" \
+ : /* no outputs */ \
+ : "c" (msr), "a" (val1), "d" (val2))
+
+
+#define read_cr0() ({ \
+ unsigned int __dummy; \
+ __asm__( \
+ "movl %%cr0, %0\n\t" \
+ :"=r" (__dummy)); \
+ __dummy; \
+})
+#define write_cr0(x) \
+ __asm__("movl %0,%%cr0": :"r" (x));
+
+#define read_cr3() ({ \
+ unsigned int __dummy; \
+ __asm__( \
+ "movl %%cr3, %0\n\t" \
+ :"=r" (__dummy)); \
+ __dummy; \
+})
+#define write_cr3x(x) \
+ __asm__("movl %0,%%cr3": :"r" (x));
+
+
+#define read_cr4() ({ \
+ unsigned int __dummy; \
+ __asm__( \
+ "movl %%cr4, %0\n\t" \
+ :"=r" (__dummy)); \
+ __dummy; \
+})
+#define write_cr4x(x) \
+ __asm__("movl %0,%%cr4": :"r" (x));
+
+
+extern struct cpuinfo_x86 cpu_info;
+#ifdef CONFIG_X86_64
+extern void cpu_setup(void);
+#else
+#define cpu_setup() do {} while(0)
+#endif
+
+#endif /* I386_BITS_CPU_H */
diff --git a/src/arch/i386/include/bits/elf.h b/src/arch/i386/include/bits/elf.h
new file mode 100644
index 00000000..dad9c7b8
--- /dev/null
+++ b/src/arch/i386/include/bits/elf.h
@@ -0,0 +1,91 @@
+#ifndef I386_BITS_ELF_H
+#define I386_BITS_ELF_H
+
+#include "cpu.h"
+
+#ifdef CONFIG_X86_64
+/* ELF Defines for the 64bit version of the current architecture */
+#define EM_CURRENT_64 EM_X86_64
+#define EM_CURRENT_64_PRESENT ( \
+ CPU_FEATURE_P(cpu_info.x86_capability, LM) && \
+ CPU_FEATURE_P(cpu_info.x86_capability, PAE) && \
+ CPU_FEATURE_P(cpu_info.x86_capability, PSE))
+
+#define ELF_CHECK_X86_64_ARCH(x) \
+ (EM_CURRENT_64_PRESENT && ((x).e_machine == EM_X86_64))
+#define __unused_i386
+#else
+#define ELF_CHECK_X86_64_ARCH(x) 0
+#define __unused_i386 __unused
+#endif
+
+
+/* ELF Defines for the current architecture */
+#define EM_CURRENT EM_386
+#define ELFDATA_CURRENT ELFDATA2LSB
+
+#define ELF_CHECK_I386_ARCH(x) \
+ (((x).e_machine == EM_386) || ((x).e_machine == EM_486))
+
+#define ELF_CHECK_ARCH(x) \
+ ((ELF_CHECK_I386_ARCH(x) || ELF_CHECK_X86_64_ARCH(x)) && \
+ ((x).e_entry <= 0xffffffffUL))
+
+#ifdef IMAGE_FREEBSD
+/*
+ * FreeBSD has this rather strange "feature" of its design.
+ * At some point in its evolution, FreeBSD started to rely
+ * externally on private/static/debug internal symbol information.
+ * That is, some of the interfaces that software uses to access
+ * and work with the FreeBSD kernel are made available not
+ * via the shared library symbol information (the .DYNAMIC section)
+ * but rather the debug symbols. This means that any symbol, not
+ * just publicly defined symbols can be (and are) used by system
+ * tools to make the system work. (such as top, swapinfo, swapon,
+ * etc)
+ *
+ * Even worse, however, is the fact that standard ELF loaders do
+ * not know how to load the symbols since they are not within
+ * an ELF PT_LOAD section. The kernel needs these symbols to
+ * operate so the following changes/additions to the boot
+ * loading of EtherBoot have been made to get the kernel to load.
+ * All of the changes are within IMAGE_FREEBSD such that the
+ * extra/changed code only compiles when FREEBSD support is
+ * enabled.
+ */
+
+/*
+ * Section header for FreeBSD (debug symbol kludge!) support
+ */
+typedef struct {
+ Elf32_Word sh_name; /* Section name (index into the
+ section header string table). */
+ Elf32_Word sh_type; /* Section type. */
+ Elf32_Word sh_flags; /* Section flags. */
+ Elf32_Addr sh_addr; /* Address in memory image. */
+ Elf32_Off sh_offset; /* Offset in file. */
+ Elf32_Size sh_size; /* Size in bytes. */
+ Elf32_Word sh_link; /* Index of a related section. */
+ Elf32_Word sh_info; /* Depends on section type. */
+ Elf32_Size sh_addralign; /* Alignment in bytes. */
+ Elf32_Size sh_entsize; /* Size of each entry in section. */
+} Elf32_Shdr;
+
+/* sh_type */
+#define SHT_SYMTAB 2 /* symbol table section */
+#define SHT_STRTAB 3 /* string table section */
+
+/*
+ * Module information subtypes (for the metadata that we need to build)
+ */
+#define MODINFO_END 0x0000 /* End of list */
+#define MODINFO_NAME 0x0001 /* Name of module (string) */
+#define MODINFO_TYPE 0x0002 /* Type of module (string) */
+#define MODINFO_METADATA 0x8000 /* Module-specfic */
+
+#define MODINFOMD_SSYM 0x0003 /* start of symbols */
+#define MODINFOMD_ESYM 0x0004 /* end of symbols */
+
+#endif /* IMAGE_FREEBSD */
+
+#endif /* I386_BITS_ELF_H */
diff --git a/src/arch/i386/include/bits/elf_x.h b/src/arch/i386/include/bits/elf_x.h
new file mode 100644
index 00000000..86c67250
--- /dev/null
+++ b/src/arch/i386/include/bits/elf_x.h
@@ -0,0 +1,5 @@
+#define ARCH_ELF_CLASS ELFCLASS32
+#define ARCH_ELF_DATA ELFDATA2LSB
+#define ARCH_ELF_MACHINE_OK(x) ((x)==EM_386 || (x)==EM_486)
+typedef Elf32_Ehdr Elf_ehdr;
+typedef Elf32_Phdr Elf_phdr;
diff --git a/src/arch/i386/include/bits/eltorito.h b/src/arch/i386/include/bits/eltorito.h
new file mode 100644
index 00000000..d43e9aac
--- /dev/null
+++ b/src/arch/i386/include/bits/eltorito.h
@@ -0,0 +1,3 @@
+#ifndef ELTORITO_PLATFORM
+#define ELTORITO_PLATFORM ELTORITO_PLATFORM_X86
+#endif /* ELTORITO_PLATFORM */
diff --git a/src/arch/i386/include/bits/endian.h b/src/arch/i386/include/bits/endian.h
new file mode 100644
index 00000000..b23b233a
--- /dev/null
+++ b/src/arch/i386/include/bits/endian.h
@@ -0,0 +1,9 @@
+#ifndef ETHERBOOT_BITS_ENDIAN_H
+#define ETHERBOOT_BITS_ENDIAN_H
+
+#define __BYTE_ORDER __LITTLE_ENDIAN
+
+#define le32_to_cpup(x) (*(uint32_t *)(x))
+#define cpu_to_le16p(x) (*(uint16_t*)(x))
+
+#endif /* ETHERBOOT_BITS_ENDIAN_H */
diff --git a/src/arch/i386/include/bits/string.h b/src/arch/i386/include/bits/string.h
new file mode 100644
index 00000000..3743d011
--- /dev/null
+++ b/src/arch/i386/include/bits/string.h
@@ -0,0 +1,99 @@
+#ifndef ETHERBOOT_BITS_STRING_H
+#define ETHERBOOT_BITS_STRING_H
+/*
+ * Taken from Linux /usr/include/asm/string.h
+ * All except memcpy, memmove, memset and memcmp removed.
+ */
+
+/*
+ * This string-include defines all string functions as inline
+ * functions. Use gcc. It also assumes ds=es=data space, this should be
+ * normal. Most of the string-functions are rather heavily hand-optimized,
+ * see especially strtok,strstr,str[c]spn. They should work, but are not
+ * very easy to understand. Everything is done entirely within the register
+ * set, making the functions fast and clean. String instructions have been
+ * used through-out, making for "slightly" unclear code :-)
+ *
+ * NO Copyright (C) 1991, 1992 Linus Torvalds,
+ * consider these trivial functions to be PD.
+ */
+
+
+#define __HAVE_ARCH_MEMMOVE
+static inline void * memmove(void * dest,const void * src, size_t n)
+{
+int d0, d1, d2;
+if (dest<src)
+__asm__ __volatile__(
+ "cld\n\t"
+ "rep\n\t"
+ "movsb"
+ : "=&c" (d0), "=&S" (d1), "=&D" (d2)
+ :"0" (n),"1" (src),"2" (dest)
+ : "memory");
+else
+__asm__ __volatile__(
+ "std\n\t"
+ "rep\n\t"
+ "movsb\n\t"
+ "cld"
+ : "=&c" (d0), "=&S" (d1), "=&D" (d2)
+ :"0" (n),
+ "1" (n-1+(const char *)src),
+ "2" (n-1+(char *)dest)
+ :"memory");
+return dest;
+}
+
+#define __HAVE_ARCH_MEMSET
+static inline void *memset(void *s, int c,size_t count)
+{
+int d0, d1;
+__asm__ __volatile__(
+ "cld\n\t"
+ "rep\n\t"
+ "stosb"
+ : "=&c" (d0), "=&D" (d1)
+ :"a" (c),"1" (s),"0" (count)
+ :"memory");
+return s;
+}
+
+#define __HAVE_ARCH_STRNCMP
+static inline int strncmp(const char * cs,const char * ct,size_t count)
+{
+register int __res;
+int d0, d1, d2;
+__asm__ __volatile__(
+ "1:\tdecl %3\n\t"
+ "js 2f\n\t"
+ "lodsb\n\t"
+ "scasb\n\t"
+ "jne 3f\n\t"
+ "testb %%al,%%al\n\t"
+ "jne 1b\n"
+ "2:\txorl %%eax,%%eax\n\t"
+ "jmp 4f\n"
+ "3:\tsbbl %%eax,%%eax\n\t"
+ "orb $1,%%al\n"
+ "4:"
+ :"=a" (__res), "=&S" (d0), "=&D" (d1), "=&c" (d2)
+ :"1" (cs),"2" (ct),"3" (count));
+return __res;
+}
+
+#define __HAVE_ARCH_STRLEN
+static inline size_t strlen(const char * s)
+{
+int d0;
+register int __res;
+__asm__ __volatile__(
+ "repne\n\t"
+ "scasb\n\t"
+ "notl %0\n\t"
+ "decl %0"
+ :"=c" (__res), "=&D" (d0) :"1" (s),"a" (0), "0" (0xffffffff));
+return __res;
+}
+
+#endif /* ETHERBOOT_BITS_STRING_H */
diff --git a/src/arch/i386/include/callbacks_arch.h b/src/arch/i386/include/callbacks_arch.h
new file mode 100644
index 00000000..f9cba488
--- /dev/null
+++ b/src/arch/i386/include/callbacks_arch.h
@@ -0,0 +1,243 @@
+/* Callout/callback interface for Etherboot
+ *
+ * This file provides the mechanisms for making calls from Etherboot
+ * to external programs and vice-versa.
+ *
+ * Initial version by Michael Brown <mbrown@fensystems.co.uk>, January 2004.
+ *
+ * $Id$
+ */
+
+#ifndef CALLBACKS_ARCH_H
+#define CALLBACKS_ARCH_H
+
+/* Skip the definitions that won't make sense to the assembler */
+#ifndef ASSEMBLY
+
+/* Struct to hold general-purpose register values. PUSHAL and POPAL
+ * can work directly with this structure; do not change the order of
+ * registers.
+ */
+typedef struct {
+ union {
+ uint16_t di;
+ uint32_t edi;
+ };
+ union {
+ uint16_t si;
+ uint32_t esi;
+ };
+ union {
+ uint16_t bp;
+ uint32_t ebp;
+ };
+ union {
+ uint16_t sp;
+ uint32_t esp;
+ };
+ union {
+ struct {
+ uint8_t bl;
+ uint8_t bh;
+ } PACKED;
+ uint16_t bx;
+ uint32_t ebx;
+ };
+ union {
+ struct {
+ uint8_t dl;
+ uint8_t dh;
+ } PACKED;
+ uint16_t dx;
+ uint32_t edx;
+ };
+ union {
+ struct {
+ uint8_t cl;
+ uint8_t ch;
+ } PACKED;
+ uint16_t cx;
+ uint32_t ecx;
+ };
+ union {
+ struct {
+ uint8_t al;
+ uint8_t ah;
+ } PACKED;
+ uint16_t ax;
+ uint32_t eax;
+ };
+} regs_t;
+
+/* Struct to hold segment register values. Don't change the order;
+ * many bits of assembly code rely on it.
+ */
+typedef struct {
+ uint16_t cs;
+ uint16_t ss;
+ uint16_t ds;
+ uint16_t es;
+ uint16_t fs;
+ uint16_t gs;
+} PACKED seg_regs_t;
+
+/* Struct for a GDT descriptor */
+typedef struct {
+ uint16_t limit;
+ uint32_t address;
+ uint16_t padding;
+} PACKED gdt_descriptor_t;
+
+/* Struct for a GDT entry. Use GDT_SEGMENT() to fill it in.
+ */
+typedef struct {
+ uint16_t limit_0_15;
+ uint16_t base_0_15;
+ uint8_t base_16_23;
+ uint8_t accessed__type__sflag__dpl__present;
+ uint8_t limit_16_19__avl__size__granularity;
+ uint8_t base_24_31;
+} PACKED gdt_segment_t;
+
+#define GDT_SEGMENT(base,limit,type,sflag,dpl,avl,size,granularity) \
+ ( (gdt_segment_t) { \
+ ( (limit) & 0xffff ), \
+ ( (base) & 0xffff ), \
+ ( ( (base) >> 16 ) & 0xff ), \
+ ( ( 1 << 0 ) | ( (type) << 1 ) | \
+ ( (sflag) << 4 ) | ( (dpl) << 5 ) | ( 1 << 7 ) ), \
+ ( ( (limit) >> 16 ) | \
+ ( (avl) << 4 ) | ( (size) << 5 ) | ( (granularity) << 7 ) ),\
+ ( (base) >> 24 ) \
+ } )
+#define GDT_SEGMENT_BASE(gdt_segment) \
+ ( (gdt_segment)->base_0_15 | \
+ (gdt_segment)->base_16_23 << 16 | \
+ (gdt_segment)->base_24_31 << 24 )
+#define GDT_SEGMENT_LIMIT(gdt_segment) \
+ ( (gdt_segment)->limit_0_15 | \
+ ( ( (gdt_segment)->limit_16_19__avl__size__granularity \
+ & 0xf ) << 16 ) )
+#define GDT_SEGMENT_GRANULARITY(gdt_segment) \
+ ( ( (gdt_segment)->limit_16_19__avl__size__granularity \
+ & 0x80 ) >> 7 )
+#define GDT_SEGMENT_TYPE(gdt_segment) \
+ ( ( (gdt_segment)->accessed__type__sflag__dpl__present & 0x0e ) >> 1 )
+#define GDT_SEGMENT_SIZE(gdt_segment) \
+ ( ( (gdt_segment)->limit_16_19__avl__size__granularity \
+ & 0x60 ) >> 5 )
+
+#define GDT_TYPE_DATA (0x0)
+#define GDT_TYPE_STACK (0x2)
+#define GDT_TYPE_WRITEABLE (0x1)
+#define GDT_TYPE_CODE (0x6)
+#define GDT_TYPE_EXEC_ONLY_CODE (0x4)
+#define GDT_TYPE_CONFORMING (0x1)
+#define GDT_SFLAG_SYSTEM (0)
+#define GDT_SFLAG_NORMAL (1)
+#define GDT_AVL_NORMAL (0)
+#define GDT_SIZE_16BIT (0x0)
+#define GDT_SIZE_32BIT (0x2)
+#define GDT_SIZE_64BIT (0x1)
+#define GDT_SIZE_UNKNOWN (0x3)
+#define GDT_GRANULARITY_SMALL (0)
+#define GDT_GRANULARITY_LARGE (1)
+#define GDT_SEGMENT_NORMAL(base,limit,type,size,granularity) \
+ GDT_SEGMENT ( base, limit, type, \
+ GDT_SFLAG_NORMAL, 0, GDT_AVL_NORMAL, \
+ size, granularity )
+
+/* Protected mode code segment */
+#define GDT_SEGMENT_PMCS(base) GDT_SEGMENT_NORMAL ( \
+ base, 0xfffff, GDT_TYPE_CODE | GDT_TYPE_CONFORMING, \
+ GDT_SIZE_32BIT, GDT_GRANULARITY_LARGE )
+#define GDT_SEGMENT_PMCS_PHYS GDT_SEGMENT_PMCS(0)
+/* Protected mode data segment */
+#define GDT_SEGMENT_PMDS(base) GDT_SEGMENT_NORMAL ( \
+ base, 0xfffff, GDT_TYPE_DATA | GDT_TYPE_WRITEABLE, \
+ GDT_SIZE_32BIT, GDT_GRANULARITY_LARGE )
+#define GDT_SEGMENT_PMDS_PHYS GDT_SEGMENT_PMDS(0)
+/* Real mode code segment */
+/* Not sure if there's any reason to use GDT_TYPE_EXEC_ONLY_CODE
+ * instead of just GDT_TYPE_CODE, but that's what our old GDT did and
+ * it worked, so I'm not changing it.
+ */
+#define GDT_SEGMENT_RMCS(base) GDT_SEGMENT_NORMAL ( \
+ base, 0xffff, GDT_TYPE_EXEC_ONLY_CODE | GDT_TYPE_CONFORMING, \
+ GDT_SIZE_16BIT, GDT_GRANULARITY_SMALL )
+/* Real mode data segment */
+#define GDT_SEGMENT_RMDS(base) GDT_SEGMENT_NORMAL ( \
+ base, 0xffff, GDT_TYPE_DATA | GDT_TYPE_WRITEABLE, \
+ GDT_SIZE_16BIT, GDT_GRANULARITY_SMALL )
+/* Long mode code segment */
+#define GDT_SEGMENT_LMCS(base) GDT_SEGMENT_NORMAL ( \
+ base, 0xfffff, GDT_TYPE_CODE | GDT_TYPE_CONFORMING, \
+ GDT_SIZE_64BIT, GDT_GRANULARITY_LARGE )
+#define GDT_SEGMENT_LMCS_PHYS GDT_SEGMENT_LMCS(0)
+/* Long mode data segment */
+/* AFIACT, GDT_SIZE_64BIT applies only to code segments */
+#define GDT_SEGMENT_LMDS(base) GDT_SEGMENT_NORMAL ( \
+ base, 0xfffff, GDT_TYPE_DATA | GDT_TYPE_WRITEABLE, \
+ GDT_SIZE_32BIT, GDT_GRANULARITY_LARGE )
+#define GDT_SEGMENT_LMDS_PHYS GDT_SEGMENT_LMDS(0)
+
+/* Template for creating GDT structures (including segment register
+ * lists), suitable for passing as parameters to external_call().
+ */
+#define GDT_STRUCT_t(num_segments) \
+ struct { \
+ gdt_descriptor_t descriptor; \
+ gdt_segment_t segments[num_segments]; \
+ } PACKED
+/* And utility function for filling it in */
+#define GDT_ADJUST(structure) { \
+ (structure)->descriptor.address = \
+ virt_to_phys(&((structure)->descriptor.limit)); \
+ (structure)->descriptor.limit = \
+ sizeof((structure)->segments) + 8 - 1; \
+ (structure)->descriptor.padding = 0; \
+}
+
+/* Data passed in to in_call() by assembly wrapper.
+ */
+typedef struct {
+ regs_t regs;
+ seg_regs_t seg_regs;
+ gdt_descriptor_t gdt_desc;
+ uint32_t flags;
+ struct {
+ uint32_t offset;
+ uint32_t segment;
+ } ret_addr;
+} PACKED i386_pm_in_call_data_t;
+
+typedef struct {
+ seg_regs_t seg_regs;
+ union {
+ uint16_t pad;
+ uint16_t prefix_sp;
+ };
+ uint16_t flags;
+ struct {
+ uint16_t offset;
+ uint16_t segment;
+ } ret_addr;
+ uint32_t orig_opcode;
+} PACKED i386_rm_in_call_data_t;
+
+typedef struct {
+ i386_pm_in_call_data_t *pm;
+ i386_rm_in_call_data_t *rm;
+} i386_in_call_data_t;
+#define in_call_data_t i386_in_call_data_t
+
+/* Function prototypes
+ */
+extern int install_rm_callback_interface ( void *address, size_t available );
+
+#endif /* ASSEMBLY */
+
+#define RM_IN_CALL (0)
+#define RM_IN_CALL_FAR (2)
+
+#endif /* CALLBACKS_ARCH_H */
diff --git a/src/arch/i386/include/hidemem.h b/src/arch/i386/include/hidemem.h
new file mode 100644
index 00000000..600a8692
--- /dev/null
+++ b/src/arch/i386/include/hidemem.h
@@ -0,0 +1,21 @@
+#ifndef HIDEMEM_H
+#define HIDEMEM_H
+
+#include "segoff.h"
+
+extern int install_e820mangler ( void *new_mangler );
+extern int hide_etherboot ( void );
+extern int unhide_etherboot ( void );
+
+/* Symbols in e820mangler.S */
+extern void e820mangler ( void );
+extern void _intercept_int15 ( void );
+extern segoff_t _intercepted_int15;
+typedef struct {
+ uint32_t start;
+ uint32_t length;
+} exclude_range_t;
+extern exclude_range_t _hide_memory[2];
+extern uint16_t e820mangler_size;
+
+#endif /* HIDEMEM_H */
diff --git a/src/arch/i386/include/hooks.h b/src/arch/i386/include/hooks.h
new file mode 100644
index 00000000..0764edaf
--- /dev/null
+++ b/src/arch/i386/include/hooks.h
@@ -0,0 +1,9 @@
+#ifndef ETHERBOOT_I386_HOOKS_H
+#define ETHERBOOT_I386_HOOKS_H
+
+void arch_main(in_call_data_t *data, va_list params);
+void arch_on_exit(int status);
+#define arch_relocate_to(addr)
+void arch_relocated_from ( uint32_t old_addr );
+
+#endif /* ETHERBOOT_I386_HOOKS_H */
diff --git a/src/arch/i386/include/io.h b/src/arch/i386/include/io.h
new file mode 100644
index 00000000..e351a0c1
--- /dev/null
+++ b/src/arch/i386/include/io.h
@@ -0,0 +1,246 @@
+#ifndef ETHERBOOT_IO_H
+#define ETHERBOOT_IO_H
+
+
+/* Amount of relocation etherboot is experiencing */
+extern unsigned long virt_offset;
+
+/* Don't require identity mapped physical memory,
+ * osloader.c is the only valid user at the moment.
+ */
+static inline unsigned long virt_to_phys(volatile const void *virt_addr)
+{
+ return ((unsigned long)virt_addr) + virt_offset;
+}
+
+static inline void *phys_to_virt(unsigned long phys_addr)
+{
+ return (void *)(phys_addr - virt_offset);
+}
+
+/* virt_to_bus converts an addresss inside of etherboot [_start, _end]
+ * into a memory access cards can use.
+ */
+#define virt_to_bus virt_to_phys
+
+
+/* bus_to_virt reverses virt_to_bus, the address must be output
+ * from virt_to_bus to be valid. This function does not work on
+ * all bus addresses.
+ */
+#define bus_to_virt phys_to_virt
+
+/* ioremap converts a random 32bit bus address into something
+ * etherboot can access.
+ */
+static inline void *ioremap(unsigned long bus_addr, unsigned long length __unused)
+{
+ return bus_to_virt(bus_addr);
+}
+
+/* iounmap cleans up anything ioremap had to setup */
+static inline void iounmap(void *virt_addr __unused)
+{
+ return;
+}
+
+/*
+ * This file contains the definitions for the x86 IO instructions
+ * inb/inw/inl/outb/outw/outl and the "string versions" of the same
+ * (insb/insw/insl/outsb/outsw/outsl). You can also use "pausing"
+ * versions of the single-IO instructions (inb_p/inw_p/..).
+ *
+ * This file is not meant to be obfuscating: it's just complicated
+ * to (a) handle it all in a way that makes gcc able to optimize it
+ * as well as possible and (b) trying to avoid writing the same thing
+ * over and over again with slight variations and possibly making a
+ * mistake somewhere.
+ */
+
+/*
+ * Thanks to James van Artsdalen for a better timing-fix than
+ * the two short jumps: using outb's to a nonexistent port seems
+ * to guarantee better timings even on fast machines.
+ *
+ * On the other hand, I'd like to be sure of a non-existent port:
+ * I feel a bit unsafe about using 0x80 (should be safe, though)
+ *
+ * Linus
+ */
+
+#ifdef SLOW_IO_BY_JUMPING
+#define __SLOW_DOWN_IO __asm__ __volatile__("jmp 1f\n1:\tjmp 1f\n1:")
+#else
+#define __SLOW_DOWN_IO __asm__ __volatile__("outb %al,$0x80")
+#endif
+
+#ifdef REALLY_SLOW_IO
+#define SLOW_DOWN_IO { __SLOW_DOWN_IO; __SLOW_DOWN_IO; __SLOW_DOWN_IO; __SLOW_DOWN_IO; }
+#else
+#define SLOW_DOWN_IO __SLOW_DOWN_IO
+#endif
+
+/*
+ * readX/writeX() are used to access memory mapped devices. On some
+ * architectures the memory mapped IO stuff needs to be accessed
+ * differently. On the x86 architecture, we just read/write the
+ * memory location directly.
+ */
+#define readb(addr) (*(volatile unsigned char *) (addr))
+#define readw(addr) (*(volatile unsigned short *) (addr))
+#define readl(addr) (*(volatile unsigned int *) (addr))
+
+#define writeb(b,addr) ((*(volatile unsigned char *) (addr)) = (b))
+#define writew(b,addr) ((*(volatile unsigned short *) (addr)) = (b))
+#define writel(b,addr) ((*(volatile unsigned int *) (addr)) = (b))
+
+#define memcpy_fromio(a,b,c) memcpy((a),(void *)(b),(c))
+#define memcpy_toio(a,b,c) memcpy((void *)(a),(b),(c))
+
+/*
+ * Force strict CPU ordering.
+ * And yes, this is required on UP too when we're talking
+ * to devices.
+ *
+ * For now, "wmb()" doesn't actually do anything, as all
+ * Intel CPU's follow what Intel calls a *Processor Order*,
+ * in which all writes are seen in the program order even
+ * outside the CPU.
+ *
+ * I expect future Intel CPU's to have a weaker ordering,
+ * but I'd also expect them to finally get their act together
+ * and add some real memory barriers if so.
+ *
+ * Some non intel clones support out of order store. wmb() ceases to be a
+ * nop for these.
+ */
+
+#define mb() __asm__ __volatile__ ("lock; addl $0,0(%%esp)": : :"memory")
+#define rmb() mb()
+#define wmb() mb();
+
+
+/*
+ * Talk about misusing macros..
+ */
+
+#define __OUT1(s,x) \
+extern void __out##s(unsigned x value, unsigned short port); \
+extern inline void __out##s(unsigned x value, unsigned short port) {
+
+#define __OUT2(s,s1,s2) \
+__asm__ __volatile__ ("out" #s " %" s1 "0,%" s2 "1"
+
+#define __OUT(s,s1,x) \
+__OUT1(s,x) __OUT2(s,s1,"w") : : "a" (value), "d" (port)); } \
+__OUT1(s##c,x) __OUT2(s,s1,"") : : "a" (value), "id" (port)); } \
+__OUT1(s##_p,x) __OUT2(s,s1,"w") : : "a" (value), "d" (port)); SLOW_DOWN_IO; } \
+__OUT1(s##c_p,x) __OUT2(s,s1,"") : : "a" (value), "id" (port)); SLOW_DOWN_IO; }
+
+#define __IN1(s,x) \
+extern unsigned x __in##s(unsigned short port); \
+extern inline unsigned x __in##s(unsigned short port) { unsigned x _v;
+
+#define __IN2(s,s1,s2) \
+__asm__ __volatile__ ("in" #s " %" s2 "1,%" s1 "0"
+
+#define __IN(s,s1,x,i...) \
+__IN1(s,x) __IN2(s,s1,"w") : "=a" (_v) : "d" (port) ,##i ); return _v; } \
+__IN1(s##c,x) __IN2(s,s1,"") : "=a" (_v) : "id" (port) ,##i ); return _v; } \
+__IN1(s##_p,x) __IN2(s,s1,"w") : "=a" (_v) : "d" (port) ,##i ); SLOW_DOWN_IO; return _v; } \
+__IN1(s##c_p,x) __IN2(s,s1,"") : "=a" (_v) : "id" (port) ,##i ); SLOW_DOWN_IO; return _v; }
+
+#define __INS(s) \
+extern void ins##s(unsigned short port, void * addr, unsigned long count); \
+extern inline void ins##s(unsigned short port, void * addr, unsigned long count) \
+{ __asm__ __volatile__ ("cld ; rep ; ins" #s \
+: "=D" (addr), "=c" (count) : "d" (port),"0" (addr),"1" (count)); }
+
+#define __OUTS(s) \
+extern void outs##s(unsigned short port, const void * addr, unsigned long count); \
+extern inline void outs##s(unsigned short port, const void * addr, unsigned long count) \
+{ __asm__ __volatile__ ("cld ; rep ; outs" #s \
+: "=S" (addr), "=c" (count) : "d" (port),"0" (addr),"1" (count)); }
+
+__IN(b,"", char)
+__IN(w,"",short)
+__IN(l,"", long)
+
+__OUT(b,"b",char)
+__OUT(w,"w",short)
+__OUT(l,,int)
+
+__INS(b)
+__INS(w)
+__INS(l)
+
+__OUTS(b)
+__OUTS(w)
+__OUTS(l)
+
+/*
+ * Note that due to the way __builtin_constant_p() works, you
+ * - can't use it inside a inline function (it will never be true)
+ * - you don't have to worry about side effects within the __builtin..
+ */
+#define outb(val,port) \
+((__builtin_constant_p((port)) && (port) < 256) ? \
+ __outbc((val),(port)) : \
+ __outb((val),(port)))
+
+#define inb(port) \
+((__builtin_constant_p((port)) && (port) < 256) ? \
+ __inbc(port) : \
+ __inb(port))
+
+#define outb_p(val,port) \
+((__builtin_constant_p((port)) && (port) < 256) ? \
+ __outbc_p((val),(port)) : \
+ __outb_p((val),(port)))
+
+#define inb_p(port) \
+((__builtin_constant_p((port)) && (port) < 256) ? \
+ __inbc_p(port) : \
+ __inb_p(port))
+
+#define outw(val,port) \
+((__builtin_constant_p((port)) && (port) < 256) ? \
+ __outwc((val),(port)) : \
+ __outw((val),(port)))
+
+#define inw(port) \
+((__builtin_constant_p((port)) && (port) < 256) ? \
+ __inwc(port) : \
+ __inw(port))
+
+#define outw_p(val,port) \
+((__builtin_constant_p((port)) && (port) < 256) ? \
+ __outwc_p((val),(port)) : \
+ __outw_p((val),(port)))
+
+#define inw_p(port) \
+((__builtin_constant_p((port)) && (port) < 256) ? \
+ __inwc_p(port) : \
+ __inw_p(port))
+
+#define outl(val,port) \
+((__builtin_constant_p((port)) && (port) < 256) ? \
+ __outlc((val),(port)) : \
+ __outl((val),(port)))
+
+#define inl(port) \
+((__builtin_constant_p((port)) && (port) < 256) ? \
+ __inlc(port) : \
+ __inl(port))
+
+#define outl_p(val,port) \
+((__builtin_constant_p((port)) && (port) < 256) ? \
+ __outlc_p((val),(port)) : \
+ __outl_p((val),(port)))
+
+#define inl_p(port) \
+((__builtin_constant_p((port)) && (port) < 256) ? \
+ __inlc_p(port) : \
+ __inl_p(port))
+
+#endif /* ETHERBOOT_IO_H */
diff --git a/src/arch/i386/include/latch.h b/src/arch/i386/include/latch.h
new file mode 100644
index 00000000..38a8bd21
--- /dev/null
+++ b/src/arch/i386/include/latch.h
@@ -0,0 +1,10 @@
+#ifndef LATCH_H
+#define LATCH_H
+
+#define TICKS_PER_SEC 18
+
+/* For different calibrators of the TSC move the declaration of
+ * sleep_latch and the definitions of it's length here...
+ */
+
+#endif /* LATCH_H */
diff --git a/src/arch/i386/include/limits.h b/src/arch/i386/include/limits.h
new file mode 100644
index 00000000..f13db267
--- /dev/null
+++ b/src/arch/i386/include/limits.h
@@ -0,0 +1,59 @@
+#ifndef LIMITS_H
+#define LIMITS_H 1
+
+/* Number of bits in a `char' */
+#define CHAR_BIT 8
+
+/* Minimum and maximum values a `signed char' can hold */
+#define SCHAR_MIN (-128)
+#define SCHAR_MAX 127
+
+/* Maximum value an `unsigned char' can hold. (Minimum is 0.) */
+#define UCHAR_MAX 255
+
+/* Minimum and maximum values a `char' can hold */
+#define CHAR_MIN SCHAR_MIN
+#define CHAR_MAX SCHAR_MAX
+
+/* Minimum and maximum values a `signed short int' can hold */
+#define SHRT_MIN (-32768)
+#define SHRT_MAX 32767
+
+/* Maximum value an `unsigned short' can hold. (Minimum is 0.) */
+#define USHRT_MAX 65535
+
+
+/* Minimum and maximum values a `signed int' can hold */
+#define INT_MIN (-INT_MAX - 1)
+#define INT_MAX 2147483647
+
+/* Maximum value an `unsigned int' can hold. (Minimum is 0.) */
+#define UINT_MAX 4294967295U
+
+
+/* Minimum and maximum values a `signed int' can hold */
+#define INT_MAX 2147483647
+#define INT_MIN (-INT_MAX - 1)
+
+
+/* Maximum value an `unsigned int' can hold. (Minimum is 0.) */
+#define UINT_MAX 4294967295U
+
+
+/* Minimum and maximum values a `signed long' can hold */
+#define LONG_MAX 2147483647
+#define LONG_MIN (-LONG_MAX - 1L)
+
+/* Maximum value an `unsigned long' can hold. (Minimum is 0.) */
+#define ULONG_MAX 4294967295UL
+
+/* Minimum and maximum values a `signed long long' can hold */
+#define LLONG_MAX 9223372036854775807LL
+#define LLONG_MIN (-LONG_MAX - 1LL)
+
+
+/* Maximum value an `unsigned long long' can hold. (Minimum is 0.) */
+#define ULLONG_MAX 18446744073709551615ULL
+
+
+#endif /* LIMITS_H */
diff --git a/src/arch/i386/include/pic8259.h b/src/arch/i386/include/pic8259.h
new file mode 100644
index 00000000..694e9d13
--- /dev/null
+++ b/src/arch/i386/include/pic8259.h
@@ -0,0 +1,96 @@
+/*
+ * Basic support for controlling the 8259 Programmable Interrupt Controllers.
+ *
+ * Initially written by Michael Brown (mcb30).
+ */
+
+#ifndef PIC8259_H
+#define PIC8259_H
+
+/* For segoff_t */
+#include "segoff.h"
+
+#define IRQ_PIC_CUTOFF (8)
+
+/* 8259 register locations */
+#define PIC1_ICW1 (0x20)
+#define PIC1_OCW2 (0x20)
+#define PIC1_OCW3 (0x20)
+#define PIC1_ICR (0x20)
+#define PIC1_IRR (0x20)
+#define PIC1_ISR (0x20)
+#define PIC1_ICW2 (0x21)
+#define PIC1_ICW3 (0x21)
+#define PIC1_ICW4 (0x21)
+#define PIC1_IMR (0x21)
+#define PIC2_ICW1 (0xa0)
+#define PIC2_OCW2 (0xa0)
+#define PIC2_OCW3 (0xa0)
+#define PIC2_ICR (0xa0)
+#define PIC2_IRR (0xa0)
+#define PIC2_ISR (0xa0)
+#define PIC2_ICW2 (0xa1)
+#define PIC2_ICW3 (0xa1)
+#define PIC2_ICW4 (0xa1)
+#define PIC2_IMR (0xa1)
+
+/* Register command values */
+#define OCW3_ID (0x08)
+#define OCW3_READ_IRR (0x03)
+#define OCW3_READ_ISR (0x02)
+#define ICR_EOI_NON_SPECIFIC (0x20)
+#define ICR_EOI_NOP (0x40)
+#define ICR_EOI_SPECIFIC (0x60)
+#define ICR_EOI_SET_PRIORITY (0xc0)
+
+/* Macros to enable/disable IRQs */
+#define IMR_REG(x) ( (x) < IRQ_PIC_CUTOFF ? PIC1_IMR : PIC2_IMR )
+#define IMR_BIT(x) ( 1 << ( (x) % IRQ_PIC_CUTOFF ) )
+#define irq_enabled(x) ( ( inb ( IMR_REG(x) ) & IMR_BIT(x) ) == 0 )
+#define enable_irq(x) outb ( inb( IMR_REG(x) ) & ~IMR_BIT(x), IMR_REG(x) )
+#define disable_irq(x) outb ( inb( IMR_REG(x) ) | IMR_BIT(x), IMR_REG(x) )
+
+/* Macros for acknowledging IRQs */
+#define ICR_REG(x) ( (x) < IRQ_PIC_CUTOFF ? PIC1_ICR : PIC2_ICR )
+#define ICR_VALUE(x) ( (x) % IRQ_PIC_CUTOFF )
+#define CHAINED_IRQ 2
+
+/* Utility macros to convert IRQ numbers to INT numbers and INT vectors */
+#define IRQ_INT(x) ( (x)<IRQ_PIC_CUTOFF ? (x)+0x08 : (x)-IRQ_PIC_CUTOFF+0x70 )
+#define INT_VECTOR(x) ( (segoff_t*) phys_to_virt( 4 * (x) ) )
+#define IRQ_VECTOR(x) ( INT_VECTOR ( IRQ_INT(x) ) )
+
+/* Other constants */
+typedef uint8_t irq_t;
+#define IRQ_MAX (15)
+#define IRQ_NONE (0xff)
+
+/* Function prototypes
+ */
+int install_irq_handler ( irq_t irq, segoff_t *handler,
+ uint8_t *previously_enabled,
+ segoff_t *previous_handler );
+int remove_irq_handler ( irq_t irq, segoff_t *handler,
+ uint8_t *previously_enabled,
+ segoff_t *previous_handler );
+int install_trivial_irq_handler ( irq_t irq );
+int remove_trivial_irq_handler ( irq_t irq );
+int trivial_irq_triggered ( irq_t irq );
+int copy_trivial_irq_handler ( void *target, size_t target_size );
+void send_non_specific_eoi ( irq_t irq );
+void send_specific_eoi ( irq_t irq );
+void fake_irq ( irq_t irq );
+#ifdef DEBUG_IRQ
+void dump_irq_status ( void );
+#else
+#define dump_irq_status()
+#endif
+
+/* Other code (e.g. undi.c) needs to know the size of the trivial IRQ
+ * handler code, so we put prototypes and the size macro here.
+ */
+extern void _trivial_irq_handler ( void );
+extern void _trivial_irq_handler_end ( void );
+#define TRIVIAL_IRQ_HANDLER_SIZE FRAGMENT_SIZE(_trivial_irq_handler)
+
+#endif /* PIC8259_H */
diff --git a/src/arch/i386/include/pxe_callbacks.h b/src/arch/i386/include/pxe_callbacks.h
new file mode 100644
index 00000000..9b941931
--- /dev/null
+++ b/src/arch/i386/include/pxe_callbacks.h
@@ -0,0 +1,33 @@
+/* Header for pxe_callbacks.c.
+ */
+
+#ifndef PXE_CALLBACKS_H
+#define PXE_CALLBACKS_H
+
+#include "etherboot.h"
+#include "segoff.h"
+#include "pxe.h"
+
+typedef struct {
+ segoff_t orig_retaddr;
+ uint16_t opcode;
+ segoff_t segoff;
+} PACKED pxe_call_params_t;
+
+/*
+ * These values are hard-coded into the PXE spec
+ */
+#define PXE_LOAD_SEGMENT (0x0000)
+#define PXE_LOAD_OFFSET (0x7c00)
+#define PXE_LOAD_ADDRESS ( ( PXE_LOAD_SEGMENT << 4 ) + PXE_LOAD_OFFSET )
+
+/* Function prototypes
+ */
+extern pxe_stack_t * install_pxe_stack ( void *base );
+extern void use_undi_ds_for_rm_stack ( uint16_t ds );
+extern int hook_pxe_stack ( void );
+extern int unhook_pxe_stack ( void );
+extern void remove_pxe_stack ( void );
+extern int xstartpxe ( void );
+
+#endif /* PXE_CALLBACKS_H */
diff --git a/src/arch/i386/include/pxe_types.h b/src/arch/i386/include/pxe_types.h
new file mode 100644
index 00000000..7b093e6a
--- /dev/null
+++ b/src/arch/i386/include/pxe_types.h
@@ -0,0 +1,35 @@
+/*
+ * Architecture-specific portion of pxe.h for Etherboot
+ *
+ * This file has to define the types SEGOFF16_t, SEGDESC_t and
+ * SEGSEL_t for use in other PXE structures. See pxe.h for details.
+ */
+
+#ifndef PXE_TYPES_H
+#define PXE_TYPES_H
+
+/* SEGOFF16_t defined in separate header
+ */
+#include "segoff.h"
+typedef segoff_t I386_SEGOFF16_t;
+#define SEGOFF16_t I386_SEGOFF16_t
+
+#define IS_NULL_SEGOFF16(x) ( ( (x).segment == 0 ) && ( (x).offset == 0 ) )
+#define SEGOFF16_TO_PTR(x) ( VIRTUAL( (x).segment, (x).offset ) )
+#define PTR_TO_SEGOFF16(ptr,segoff16) \
+ (segoff16).segment = SEGMENT(ptr); \
+ (segoff16).offset = OFFSET(ptr);
+
+typedef struct {
+ uint16_t Seg_Addr;
+ uint32_t Phy_Addr;
+ uint16_t Seg_Size;
+} PACKED I386_SEGDESC_t; /* PACKED is required, otherwise gcc pads
+ * this out to 12 bytes -
+ * mbrown@fensystems.co.uk (mcb30) 17/5/03 */
+#define SEGDESC_t I386_SEGDESC_t
+
+typedef uint16_t I386_SEGSEL_t;
+#define SEGSEL_t I386_SEGSEL_t
+
+#endif /* PXE_TYPES_H */
diff --git a/src/arch/i386/include/realmode.h b/src/arch/i386/include/realmode.h
new file mode 100644
index 00000000..eca92b9b
--- /dev/null
+++ b/src/arch/i386/include/realmode.h
@@ -0,0 +1,124 @@
+/* Real-mode interface
+ */
+
+#ifndef ASSEMBLY
+
+#include "etherboot.h"
+#include "segoff.h"
+
+typedef union {
+ struct {
+ union {
+ uint8_t l;
+ uint8_t byte;
+ };
+ uint8_t h;
+ } PACKED;
+ uint16_t word;
+} PACKED reg16_t;
+
+typedef union {
+ reg16_t w;
+ uint32_t dword;
+} PACKED reg32_t;
+
+/* Macros to help with defining inline real-mode trampoline fragments.
+ */
+#define RM_XSTR(x) #x /* Macro hackery needed to stringify */
+#define RM_STR(x) RM_XSTR(x)
+#define RM_FRAGMENT(name, asm_code_str) \
+ extern void name ( void ); \
+ extern void name ## _end (void); \
+ __asm__( \
+ ".section \".text16\"\n\t" \
+ ".code16\n\t" \
+ ".arch i386\n\t" \
+ ".globl " #name " \n\t" \
+ #name ":\n\t" \
+ asm_code_str "\n\t" \
+ ".globl " #name "_end\n\t" \
+ #name "_end:\n\t" \
+ ".code32\n\t" \
+ ".previous\n\t" \
+ )
+
+#define FRAGMENT_SIZE(fragment) ( (size_t) ( ( (void*) fragment ## _end )\
+ - ( (void*) (fragment) ) ) )
+
+/* Data structures in _prot_to_real and _real_to_prot. These
+ * structures are accessed by assembly code as well as C code.
+ */
+typedef struct {
+ uint32_t esp;
+ uint16_t cs;
+ uint16_t ss;
+ uint32_t r2p_params;
+} PACKED prot_to_real_params_t;
+
+typedef struct {
+ uint32_t ret_addr;
+ uint32_t esp;
+ uint32_t ebx;
+ uint32_t esi;
+ uint32_t edi;
+ uint32_t ebp;
+ uint32_t out_stack;
+ uint32_t out_stack_len;
+} PACKED real_to_prot_params_t;
+
+/* Function prototypes: realmode.c
+ */
+#define real_call( fragment, in_stack, out_stack ) \
+ _real_call ( fragment, FRAGMENT_SIZE(fragment), \
+ (void*)(in_stack), \
+ ( (in_stack) == NULL ? 0 : sizeof(*(in_stack)) ), \
+ (void*)(out_stack), \
+ ( (out_stack) == NULL ? 0 : sizeof(*(out_stack)) ) )
+extern uint16_t _real_call ( void *fragment, int fragment_len,
+ void *in_stack, int in_stack_len,
+ void *out_stack, int out_stack_len );
+/* Function prototypes: realmode_asm.S
+ */
+extern void rm_callback_interface;
+extern uint16_t rm_callback_interface_size;
+extern uint32_t rm_etherboot_location;
+extern void _rm_in_call ( void );
+extern void _rm_in_call_far ( void );
+
+extern void _prot_to_real_prefix ( void );
+extern void _prot_to_real_prefix_end ( void );
+extern uint16_t prot_to_real_prefix_size;
+
+extern void _real_to_prot_suffix ( void );
+extern void _real_to_prot_suffix_end ( void );
+extern uint16_t real_to_prot_suffix_size;
+
+/* PXE assembler bits */
+extern void pxe_callback_interface;
+extern uint16_t pxe_callback_interface_size;
+extern void _pxe_in_call_far ( void );
+extern void _pxenv_in_call_far ( void );
+extern void _pxe_intercept_int1a ( void );
+extern segoff_t _pxe_intercepted_int1a;
+extern segoff_t _pxe_pxenv_location;
+
+/* Global variables
+ */
+extern uint32_t real_mode_stack;
+extern size_t real_mode_stack_size;
+extern int lock_real_mode_stack;
+
+
+/* Function prototypes from basemem.c
+ */
+#ifdef LINUXBIOS
+/* A silly hard code that let's the code compile and work.
+ * When this becomes a problem feel free to implement
+ * something better.
+ */
+static inline void allot_real_mode_stack(void) { real_mode_stack = 0x7c00; }
+#else
+void allot_real_mode_stack(void);
+#endif
+
+#endif /* ASSEMBLY */
diff --git a/src/arch/i386/include/segoff.h b/src/arch/i386/include/segoff.h
new file mode 100644
index 00000000..822ddd33
--- /dev/null
+++ b/src/arch/i386/include/segoff.h
@@ -0,0 +1,41 @@
+/*
+ * Segment:offset types and macros
+ *
+ * Initially written by Michael Brown (mcb30).
+ */
+
+#ifndef SEGOFF_H
+#define SEGOFF_H
+
+#include <stdint.h>
+#include <osdep.h>
+#include <io.h>
+
+/* Segment:offset structure. Note that the order within the structure
+ * is offset:segment.
+ */
+typedef struct {
+ uint16_t offset;
+ uint16_t segment;
+} segoff_t;
+
+/* Macros for converting from virtual to segment:offset addresses,
+ * when we don't actually care which of the many isomorphic results we
+ * get.
+ */
+#ifdef DEBUG_SEGMENT
+uint16_t SEGMENT ( const void * const ptr ) {
+ uint32_t phys = virt_to_phys ( ptr );
+ if ( phys > 0xfffff ) {
+ printf ( "FATAL ERROR: segment address out of range\n" );
+ }
+ return phys >> 4;
+}
+#else
+#define SEGMENT(x) ( virt_to_phys ( x ) >> 4 )
+#endif
+#define OFFSET(x) ( virt_to_phys ( x ) & 0xf )
+#define SEGOFF(x) { OFFSET(x), SEGMENT(x) }
+#define VIRTUAL(x,y) ( phys_to_virt ( ( ( x ) << 4 ) + ( y ) ) )
+
+#endif /* SEGOFF_H */
diff --git a/src/arch/i386/include/setjmp.h b/src/arch/i386/include/setjmp.h
new file mode 100644
index 00000000..ed2be270
--- /dev/null
+++ b/src/arch/i386/include/setjmp.h
@@ -0,0 +1,12 @@
+#ifndef ETHERBOOT_SETJMP_H
+#define ETHERBOOT_SETJMP_H
+
+
+/* Define a type for use by setjmp and longjmp */
+#define JBLEN 6
+typedef unsigned long jmp_buf[JBLEN];
+
+extern int setjmp (jmp_buf env);
+extern void longjmp (jmp_buf env, int val);
+
+#endif /* ETHERBOOT_SETJMP_H */
diff --git a/src/arch/i386/include/stdint.h b/src/arch/i386/include/stdint.h
new file mode 100644
index 00000000..42b04830
--- /dev/null
+++ b/src/arch/i386/include/stdint.h
@@ -0,0 +1,16 @@
+#ifndef STDINT_H
+#define STDINT_H
+
+typedef unsigned size_t;
+
+typedef unsigned char uint8_t;
+typedef unsigned short uint16_t;
+typedef unsigned long uint32_t;
+typedef unsigned long long uint64_t;
+
+typedef signed char int8_t;
+typedef signed short int16_t;
+typedef signed long int32_t;
+typedef signed long long int64_t;
+
+#endif /* STDINT_H */
diff --git a/src/arch/i386/include/vga.h b/src/arch/i386/include/vga.h
new file mode 100644
index 00000000..01fc39d8
--- /dev/null
+++ b/src/arch/i386/include/vga.h
@@ -0,0 +1,228 @@
+/*
+ *
+ * modified
+ * by Steve M. Gehlbach <steve@kesa.com>
+ *
+ * Originally from linux/drivers/video/vga16.c by
+ * Ben Pfaff <pfaffben@debian.org> and Petr Vandrovec <VANDROVE@vc.cvut.cz>
+ * Copyright 1999 Ben Pfaff <pfaffben@debian.org> and Petr Vandrovec <VANDROVE@vc.cvut.cz>
+ * Based on VGA info at http://www.goodnet.com/~tinara/FreeVGA/home.htm
+ * Based on VESA framebuffer (c) 1998 Gerd Knorr <kraxel@goldbach.in-berlin.de>
+ *
+ */
+
+#ifndef VGA_H_INCL
+#define VGA_H_INCL 1
+
+//#include <cpu/p5/io.h>
+
+#define u8 unsigned char
+#define u16 unsigned short
+#define u32 unsigned int
+#define __u32 u32
+
+#define VERROR -1
+#define CHAR_HEIGHT 16
+#define LINES 25
+#define COLS 80
+
+// macros for writing to vga regs
+#define write_crtc(data,addr) outb(addr,CRT_IC); outb(data,CRT_DC)
+#define write_att(data,addr) inb(IS1_RC); inb(0x80); outb(addr,ATT_IW); inb(0x80); outb(data,ATT_IW); inb(0x80)
+#define write_seq(data,addr) outb(addr,SEQ_I); outb(data,SEQ_D)
+#define write_gra(data,addr) outb(addr,GRA_I); outb(data,GRA_D)
+u8 read_seq_b(u16 addr);
+u8 read_gra_b(u16 addr);
+u8 read_crtc_b(u16 addr);
+u8 read_att_b(u16 addr);
+
+
+#ifdef VGA_HARDWARE_FIXUP
+void vga_hardware_fixup(void);
+#else
+#define vga_hardware_fixup() do{} while(0)
+#endif
+
+#define SYNC_HOR_HIGH_ACT 1 /* horizontal sync high active */
+#define SYNC_VERT_HIGH_ACT 2 /* vertical sync high active */
+#define SYNC_EXT 4 /* external sync */
+#define SYNC_COMP_HIGH_ACT 8 /* composite sync high active */
+#define SYNC_BROADCAST 16 /* broadcast video timings */
+ /* vtotal = 144d/288n/576i => PAL */
+ /* vtotal = 121d/242n/484i => NTSC */
+
+#define SYNC_ON_GREEN 32 /* sync on green */
+
+#define VMODE_NONINTERLACED 0 /* non interlaced */
+#define VMODE_INTERLACED 1 /* interlaced */
+#define VMODE_DOUBLE 2 /* double scan */
+#define VMODE_MASK 255
+
+#define VMODE_YWRAP 256 /* ywrap instead of panning */
+#define VMODE_SMOOTH_XPAN 512 /* smooth xpan possible (internally used) */
+#define VMODE_CONUPDATE 512 /* don't update x/yoffset */
+
+/* VGA data register ports */
+#define CRT_DC 0x3D5 /* CRT Controller Data Register - color emulation */
+#define CRT_DM 0x3B5 /* CRT Controller Data Register - mono emulation */
+#define ATT_R 0x3C1 /* Attribute Controller Data Read Register */
+#define GRA_D 0x3CF /* Graphics Controller Data Register */
+#define SEQ_D 0x3C5 /* Sequencer Data Register */
+
+#define MIS_R 0x3CC // Misc Output Read Register
+#define MIS_W 0x3C2 // Misc Output Write Register
+
+#define IS1_RC 0x3DA /* Input Status Register 1 - color emulation */
+#define IS1_RM 0x3BA /* Input Status Register 1 - mono emulation */
+#define PEL_D 0x3C9 /* PEL Data Register */
+#define PEL_MSK 0x3C6 /* PEL mask register */
+
+/* EGA-specific registers */
+#define GRA_E0 0x3CC /* Graphics enable processor 0 */
+#define GRA_E1 0x3CA /* Graphics enable processor 1 */
+
+
+/* VGA index register ports */
+#define CRT_IC 0x3D4 /* CRT Controller Index - color emulation */
+#define CRT_IM 0x3B4 /* CRT Controller Index - mono emulation */
+#define ATT_IW 0x3C0 /* Attribute Controller Index & Data Write Register */
+#define GRA_I 0x3CE /* Graphics Controller Index */
+#define SEQ_I 0x3C4 /* Sequencer Index */
+#define PEL_IW 0x3C8 /* PEL Write Index */
+#define PEL_IR 0x3C7 /* PEL Read Index */
+
+/* standard VGA indexes max counts */
+#define CRTC_C 25 /* 25 CRT Controller Registers sequentially set*/
+ // the remainder are not in the par array
+#define ATT_C 21 /* 21 Attribute Controller Registers */
+#define GRA_C 9 /* 9 Graphics Controller Registers */
+#define SEQ_C 5 /* 5 Sequencer Registers */
+#define MIS_C 1 /* 1 Misc Output Register */
+
+#define CRTC_H_TOTAL 0
+#define CRTC_H_DISP 1
+#define CRTC_H_BLANK_START 2
+#define CRTC_H_BLANK_END 3
+#define CRTC_H_SYNC_START 4
+#define CRTC_H_SYNC_END 5
+#define CRTC_V_TOTAL 6
+#define CRTC_OVERFLOW 7
+#define CRTC_PRESET_ROW 8
+#define CRTC_MAX_SCAN 9
+#define CRTC_CURSOR_START 0x0A
+#define CRTC_CURSOR_END 0x0B
+#define CRTC_START_HI 0x0C
+#define CRTC_START_LO 0x0D
+#define CRTC_CURSOR_HI 0x0E
+#define CRTC_CURSOR_LO 0x0F
+#define CRTC_V_SYNC_START 0x10
+#define CRTC_V_SYNC_END 0x11
+#define CRTC_V_DISP_END 0x12
+#define CRTC_OFFSET 0x13
+#define CRTC_UNDERLINE 0x14
+#define CRTC_V_BLANK_START 0x15
+#define CRTC_V_BLANK_END 0x16
+#define CRTC_MODE 0x17
+#define CRTC_LINE_COMPARE 0x18
+
+#define ATC_MODE 0x10
+#define ATC_OVERSCAN 0x11
+#define ATC_PLANE_ENABLE 0x12
+#define ATC_PEL 0x13
+#define ATC_COLOR_PAGE 0x14
+
+#define SEQ_CLOCK_MODE 0x01
+#define SEQ_PLANE_WRITE 0x02
+#define SEQ_CHARACTER_MAP 0x03
+#define SEQ_MEMORY_MODE 0x04
+
+#define GDC_SR_VALUE 0x00
+#define GDC_SR_ENABLE 0x01
+#define GDC_COMPARE_VALUE 0x02
+#define GDC_DATA_ROTATE 0x03
+#define GDC_PLANE_READ 0x04
+#define GDC_MODE 0x05
+#define GDC_MISC 0x06
+#define GDC_COMPARE_MASK 0x07
+#define GDC_BIT_MASK 0x08
+
+// text attributes
+#define VGA_ATTR_CLR_RED 0x4
+#define VGA_ATTR_CLR_GRN 0x2
+#define VGA_ATTR_CLR_BLU 0x1
+#define VGA_ATTR_CLR_YEL (VGA_ATTR_CLR_RED | VGA_ATTR_CLR_GRN)
+#define VGA_ATTR_CLR_CYN (VGA_ATTR_CLR_GRN | VGA_ATTR_CLR_BLU)
+#define VGA_ATTR_CLR_MAG (VGA_ATTR_CLR_BLU | VGA_ATTR_CLR_RED)
+#define VGA_ATTR_CLR_BLK 0
+#define VGA_ATTR_CLR_WHT (VGA_ATTR_CLR_RED | VGA_ATTR_CLR_GRN | VGA_ATTR_CLR_BLU)
+#define VGA_ATTR_BNK 0x80
+#define VGA_ATTR_ITN 0x08
+
+/*
+ * vga register parameters
+ * these are copied to the
+ * registers.
+ *
+ */
+struct vga_par {
+ u8 crtc[CRTC_C];
+ u8 atc[ATT_C];
+ u8 gdc[GRA_C];
+ u8 seq[SEQ_C];
+ u8 misc; // the misc register, MIS_W
+ u8 vss;
+};
+
+
+/* Interpretation of offset for color fields: All offsets are from the right,
+ * inside a "pixel" value, which is exactly 'bits_per_pixel' wide (means: you
+ * can use the offset as right argument to <<). A pixel afterwards is a bit
+ * stream and is written to video memory as that unmodified. This implies
+ * big-endian byte order if bits_per_pixel is greater than 8.
+ */
+struct fb_bitfield {
+ __u32 offset; /* beginning of bitfield */
+ __u32 length; /* length of bitfield */
+ __u32 msb_right; /* != 0 : Most significant bit is */
+ /* right */
+};
+
+struct screeninfo {
+ __u32 xres; /* visible resolution */
+ __u32 yres;
+ __u32 xres_virtual; /* virtual resolution */
+ __u32 yres_virtual;
+ __u32 xoffset; /* offset from virtual to visible */
+ __u32 yoffset; /* resolution */
+
+ __u32 bits_per_pixel; /* guess what */
+ __u32 grayscale; /* != 0 Graylevels instead of colors */
+
+ struct fb_bitfield red; /* bitfield in fb mem if true color, */
+ struct fb_bitfield green; /* else only length is significant */
+ struct fb_bitfield blue;
+ struct fb_bitfield transp; /* transparency */
+
+ __u32 nonstd; /* != 0 Non standard pixel format */
+
+ __u32 activate; /* see FB_ACTIVATE_* */
+
+ __u32 height; /* height of picture in mm */
+ __u32 width; /* width of picture in mm */
+
+ __u32 accel_flags; /* acceleration flags (hints) */
+
+ /* Timing: All values in pixclocks, except pixclock (of course) */
+ __u32 pixclock; /* pixel clock in ps (pico seconds) */
+ __u32 left_margin; /* time from sync to picture */
+ __u32 right_margin; /* time from picture to sync */
+ __u32 upper_margin; /* time from sync to picture */
+ __u32 lower_margin;
+ __u32 hsync_len; /* length of horizontal sync */
+ __u32 vsync_len; /* length of vertical sync */
+ __u32 sync; /* sync polarity */
+ __u32 vmode; /* interlaced etc */
+ __u32 reserved[6]; /* Reserved for future compatibility */
+};
+
+#endif
diff --git a/src/arch/i386/prefix/bImageprefix.S b/src/arch/i386/prefix/bImageprefix.S
new file mode 100644
index 00000000..a478713d
--- /dev/null
+++ b/src/arch/i386/prefix/bImageprefix.S
@@ -0,0 +1,614 @@
+/*
+ Copyright (C) 2000, Entity Cyber, Inc.
+
+ Authors: Gary Byers (gb@thinguin.org)
+ Marty Connor (mdc@thinguin.org)
+ Eric Biederman (ebiederman@lnxi.com)
+
+ This code also derives a lot from arch/i386/boot/setup.S in
+ the linux kernel.
+
+ This software may be used and distributed according to the terms
+ of the GNU Public License (GPL), incorporated herein by reference.
+
+ Description:
+
+ This is just a little bit of code and data that can get prepended
+ to an Etherboot ROM image in order to allow LILO to load the
+ result as if it were a Linux kernel image.
+
+ A real Linux kernel image consists of a one-sector boot loader
+ (to load the image from a floppy disk), followed a few sectors
+ of setup code, followed by the kernel code itself. There's
+ a table in the first sector (starting at offset 497) that indicates
+ how many sectors of setup code follow the first sector and which
+ contains some other parameters that aren't interesting in this
+ case.
+
+ When LILO loads the sectors that comprise a kernel image, it doesn't
+ execute the code in the first sector (since that code would try to
+ load the image from a floppy disk.) The code in the first sector
+ below doesn't expect to get executed (and prints an error message
+ if it ever -is- executed.) LILO's only interested in knowing the
+ number of setup sectors advertised in the table (at offset 497 in
+ the first sector.)
+
+ Etherboot doesn't require much in the way of setup code.
+ Historically, the Linux kernel required at least 4 sectors of
+ setup code. Current versions of LILO look at the byte at
+ offset 497 in the first sector to indicate how many sectors
+ of setup code are contained in the image.
+
+ The setup code that is present here does a lot of things
+ exactly the way the linux kernel does them instead of in
+ ways more typical of etherboot. Generally this is so
+ the code can be strongly compatible with the linux kernel.
+ In addition the general etherboot technique of enabling the a20
+ after we switch into protected mode does not work if etherboot
+ is being loaded at 1MB.
+*/
+
+ .equ CR0_PE,1
+
+#ifdef GAS291
+#define DATA32 data32;
+#define ADDR32 addr32;
+#define LJMPI(x) ljmp x
+#else
+#define DATA32 data32
+#define ADDR32 addr32
+/* newer GAS295 require #define LJMPI(x) ljmp *x */
+#define LJMPI(x) ljmp x
+#endif
+
+/* Simple and small GDT entries for booting only */
+#define GDT_ENTRY_BOOT_CS 2
+#define GDT_ENTRY_BOOT_DS (GDT_ENTRY_BOOT_CS + 1)
+#define __BOOT_CS (GDT_ENTRY_BOOT_CS * 8)
+#define __BOOT_DS (GDT_ENTRY_BOOT_DS * 8)
+
+
+#define SETUPSECS 4 /* Minimal nr of setup-sectors */
+#define PREFIXSIZE ((SETUPSECS+1)*512)
+#define PREFIXPGH (PREFIXSIZE / 16 )
+#define BOOTSEG 0x07C0 /* original address of boot-sector */
+#define INITSEG 0x9000 /* we move boot here - out of the way */
+#define SETUPSEG 0x9020 /* setup starts here */
+#define SYSSEG 0x1000 /* system loaded at 0x10000 (65536). */
+
+#define DELTA_INITSEG (SETUPSEG - INITSEG) /* 0x0020 */
+
+/* Signature words to ensure LILO loaded us right */
+#define SIG1 0xAA55
+#define SIG2 0x5A5A
+
+ .text
+ .code16
+ .arch i386
+ .org 0
+ .section ".prefix", "ax", @progbits
+ .globl _prefix
+_prefix:
+
+/*
+ This is a minimal boot sector. If anyone tries to execute it (e.g., if
+ a .lilo file is dd'ed to a floppy), print an error message.
+*/
+
+bootsector:
+ jmp $BOOTSEG, $go - _prefix /* reload cs:ip to match relocation addr */
+go:
+ movw $0x2000, %di /* 0x2000 is arbitrary value >= length
+ of bootsect + room for stack */
+
+ movw $BOOTSEG, %ax
+ movw %ax,%ds
+ movw %ax,%es
+
+ cli
+ movw %ax, %ss /* put stack at BOOTSEG:0x2000. */
+ movw %di,%sp
+ sti
+
+ movw $why_end-why, %cx
+ movw $why - _prefix, %si
+
+ movw $0x0007, %bx /* page 0, attribute 7 (normal) */
+ movb $0x0e, %ah /* write char, tty mode */
+prloop:
+ lodsb
+ int $0x10
+ loop prloop
+freeze: jmp freeze
+
+why: .ascii "This image cannot be loaded from a floppy disk.\r\n"
+why_end:
+
+
+ .org 497
+setup_sects:
+ .byte SETUPSECS
+root_flags:
+ .word 0
+syssize:
+ .word _verbatim_size_pgh - PREFIXPGH
+swap_dev:
+ .word 0
+ram_size:
+ .word 0
+vid_mode:
+ .word 0
+root_dev:
+ .word 0
+boot_flag:
+ .word 0xAA55
+
+/*
+ We're now at the beginning of the second sector of the image -
+ where the setup code goes.
+
+ We don't need to do too much setup for Etherboot.
+
+ This code gets loaded at SETUPSEG:0. It wants to start
+ executing the Etherboot image that's loaded at SYSSEG:0 and
+ whose entry point is SYSSEG:0.
+*/
+setup_code:
+ jmp trampoline
+# This is the setup header, and it must start at %cs:2 (old 0x9020:2)
+
+ .ascii "HdrS" # header signature
+ .word 0x0203 # header version number (>= 0x0105)
+ # or else old loadlin-1.5 will fail)
+realmode_swtch: .word 0, 0 # default_switch, SETUPSEG
+start_sys_seg: .word SYSSEG # low load segment (obsolete)
+ .word kernel_version - setup_code
+ # pointing to kernel version string
+ # above section of header is compatible
+ # with loadlin-1.5 (header v1.5). Don't
+ # change it.
+
+type_of_loader: .byte 0 # = 0, old one (LILO, Loadlin,
+ # Bootlin, SYSLX, bootsect...)
+ # See Documentation/i386/boot.txt for
+ # assigned ids
+
+# flags, unused bits must be zero (RFU) bit within loadflags
+loadflags:
+LOADED_HIGH = 1 # If set, the kernel is loaded high
+CAN_USE_HEAP = 0x80 # If set, the loader also has set
+ # heap_end_ptr to tell how much
+ # space behind setup.S can be used for
+ # heap purposes.
+ # Only the loader knows what is free
+ .byte LOADED_HIGH
+
+setup_move_size: .word 0x8000 # size to move, when setup is not
+ # loaded at 0x90000. We will move setup
+ # to 0x90000 then just before jumping
+ # into the kernel. However, only the
+ # loader knows how much data behind
+ # us also needs to be loaded.
+
+code32_start: # here loaders can put a different
+ # start address for 32-bit code.
+ .long 0x100000 # 0x100000 = default for big kernel
+
+ramdisk_image: .long 0 # address of loaded ramdisk image
+ # Here the loader puts the 32-bit
+ # address where it loaded the image.
+ # This only will be read by the kernel.
+
+ramdisk_size: .long 0 # its size in bytes
+
+bootsect_kludge:
+ .long 0 # obsolete
+
+heap_end_ptr: .word 0 # (Header version 0x0201 or later)
+ # space from here (exclusive) down to
+ # end of setup code can be used by setup
+ # for local heap purposes.
+
+pad1: .word 0
+cmd_line_ptr: .long 0 # (Header version 0x0202 or later)
+ # If nonzero, a 32-bit pointer
+ # to the kernel command line.
+ # The command line should be
+ # located between the start of
+ # setup and the end of low
+ # memory (0xa0000), or it may
+ # get overwritten before it
+ # gets read. If this field is
+ # used, there is no longer
+ # anything magical about the
+ # 0x90000 segment; the setup
+ # can be located anywhere in
+ # low memory 0x10000 or higher.
+
+ramdisk_max: .long 0 # (Header version 0x0203 or later)
+ # The highest safe address for
+ # the contents of an initrd
+
+trampoline: call start_of_setup
+trampoline_end:
+ .space 1024
+# End of setup header #####################################################
+
+start_of_setup:
+# Set %ds = %cs, we know that SETUPSEG = %cs at this point
+ movw %cs, %ax # aka SETUPSEG
+ movw %ax, %ds
+# Check signature at end of setup
+ cmpw $SIG1, (setup_sig1 - setup_code)
+ jne bad_sig
+
+ cmpw $SIG2, (setup_sig2 - setup_code)
+ jne bad_sig
+
+ jmp good_sig1
+
+# Routine to print asciiz string at ds:si
+prtstr:
+ lodsb
+ andb %al, %al
+ jz fin
+
+ call prtchr
+ jmp prtstr
+
+fin: ret
+
+# Part of above routine, this one just prints ascii al
+prtchr: pushw %ax
+ pushw %cx
+ movw $7,%bx
+ movw $0x01, %cx
+ movb $0x0e, %ah
+ int $0x10
+ popw %cx
+ popw %ax
+ ret
+
+no_sig_mess: .string "No setup signature found ..."
+
+good_sig1:
+ jmp good_sig
+
+# We now have to find the rest of the setup code/data
+bad_sig:
+ movw %cs, %ax # SETUPSEG
+ subw $DELTA_INITSEG, %ax # INITSEG
+ movw %ax, %ds
+ xorb %bh, %bh
+ movb (497), %bl # get setup sect from bootsect
+ subw $4, %bx # LILO loads 4 sectors of setup
+ shlw $8, %bx # convert to words (1sect=2^8 words)
+ movw %bx, %cx
+ shrw $3, %bx # convert to segment
+ addw $SYSSEG, %bx
+ movw %bx, %cs:(start_sys_seg - setup_code)
+# Move rest of setup code/data to here
+ movw $2048, %di # four sectors loaded by LILO
+ subw %si, %si
+ pushw %cs
+ popw %es
+ movw $SYSSEG, %ax
+ movw %ax, %ds
+ rep
+ movsw
+ movw %cs, %ax # aka SETUPSEG
+ movw %ax, %ds
+ cmpw $SIG1, (setup_sig1 - setup_code)
+ jne no_sig
+
+ cmpw $SIG2, (setup_sig2 - setup_code)
+ jne no_sig
+
+ jmp good_sig
+
+no_sig:
+ lea (no_sig_mess - setup_code), %si
+ call prtstr
+
+no_sig_loop:
+ hlt
+ jmp no_sig_loop
+
+good_sig:
+ cmpw $0, %cs:(realmode_swtch - setup_code)
+ jz rmodeswtch_normal
+
+ lcall *%cs:(realmode_swtch - setup_code)
+ jmp rmodeswtch_end
+
+rmodeswtch_normal:
+ pushw %cs
+ call default_switch
+
+rmodeswtch_end:
+# we get the code32 start address and modify the below 'jmpi'
+# (loader may have changed it)
+ movl %cs:(code32_start - setup_code), %eax
+ movl %eax, %cs:(code32 - setup_code)
+
+# then we load the segment descriptors
+ movw %cs, %ax # aka SETUPSEG
+ movw %ax, %ds
+
+#
+# Enable A20. This is at the very best an annoying procedure.
+# A20 code ported from SYSLINUX 1.52-1.63 by H. Peter Anvin.
+#
+
+A20_TEST_LOOPS = 32 # Iterations per wait
+A20_ENABLE_LOOPS = 255 # Total loops to try
+
+a20_try_loop:
+
+ # First, see if we are on a system with no A20 gate.
+a20_none:
+ call a20_test
+ jnz a20_done
+
+ # Next, try the BIOS (INT 0x15, AX=0x2401)
+a20_bios:
+ movw $0x2401, %ax
+ pushfl # Be paranoid about flags
+ int $0x15
+ popfl
+
+ call a20_test
+ jnz a20_done
+
+ # Try enabling A20 through the keyboard controller
+a20_kbc:
+ call empty_8042
+
+ call a20_test # Just in case the BIOS worked
+ jnz a20_done # but had a delayed reaction.
+
+ movb $0xD1, %al # command write
+ outb %al, $0x64
+ call empty_8042
+
+ movb $0xDF, %al # A20 on
+ outb %al, $0x60
+ call empty_8042
+
+ # Wait until a20 really *is* enabled; it can take a fair amount of
+ # time on certain systems; Toshiba Tecras are known to have this
+ # problem.
+a20_kbc_wait:
+ xorw %cx, %cx
+a20_kbc_wait_loop:
+ call a20_test
+ jnz a20_done
+ loop a20_kbc_wait_loop
+
+ # Final attempt: use "configuration port A"
+a20_fast:
+ inb $0x92, %al # Configuration Port A
+ orb $0x02, %al # "fast A20" version
+ andb $0xFE, %al # don't accidentally reset
+ outb %al, $0x92
+
+ # Wait for configuration port A to take effect
+a20_fast_wait:
+ xorw %cx, %cx
+a20_fast_wait_loop:
+ call a20_test
+ jnz a20_done
+ loop a20_fast_wait_loop
+
+ # A20 is still not responding. Try frobbing it again.
+ #
+ decb (a20_tries - setup_code)
+ jnz a20_try_loop
+
+ movw $(a20_err_msg - setup_code), %si
+ call prtstr
+
+a20_die:
+ hlt
+ jmp a20_die
+
+a20_tries:
+ .byte A20_ENABLE_LOOPS
+
+a20_err_msg:
+ .ascii "linux: fatal error: A20 gate not responding!"
+ .byte 13, 10, 0
+
+ # If we get here, all is good
+a20_done:
+ # Leave the idt alone
+
+ # set up gdt
+ xorl %eax, %eax # Compute gdt_base
+ movw %ds, %ax # (Convert %ds:gdt to a linear ptr)
+ shll $4, %eax
+ addl $(bImage_gdt - setup_code), %eax
+ movl %eax, (bImage_gdt_48+2 - setup_code)
+ DATA32 lgdt %ds:(bImage_gdt_48 - setup_code) # load gdt with whatever is
+ # appropriate
+
+ # Switch to protected mode
+ movl %cr0, %eax
+ orb $CR0_PE, %al
+ movl %eax, %cr0
+
+ DATA32 ljmp %ds:(code32 - setup_code)
+code32:
+ .long 0x100000
+ .word __BOOT_CS, 0
+
+# Here's a bunch of information about your current kernel..
+kernel_version: .ascii "Etherboot "
+ .ascii VERSION
+ .byte 0
+
+# This is the default real mode switch routine.
+# to be called just before protected mode transition
+default_switch:
+ cli # no interrupts allowed !
+ movb $0x80, %al # disable NMI for bootup
+ # sequence
+ outb %al, $0x70
+ lret
+
+# This routine tests whether or not A20 is enabled. If so, it
+# exits with zf = 0.
+#
+# The memory address used, 0x200, is the int $0x80 vector, which
+# should be safe.
+
+A20_TEST_ADDR = 4*0x80
+
+a20_test:
+ pushw %cx
+ pushw %ax
+ xorw %cx, %cx
+ movw %cx, %fs # Low memory
+ decw %cx
+ movw %cx, %gs # High memory area
+ movw $A20_TEST_LOOPS, %cx
+ movw %fs:(A20_TEST_ADDR), %ax
+ pushw %ax
+a20_test_wait:
+ incw %ax
+ movw %ax, %fs:(A20_TEST_ADDR)
+ call delay # Serialize and make delay constant
+ cmpw %gs:(A20_TEST_ADDR+0x10), %ax
+ loope a20_test_wait
+
+ popw %fs:(A20_TEST_ADDR)
+ popw %ax
+ popw %cx
+ ret
+
+
+# This routine checks that the keyboard command queue is empty
+# (after emptying the output buffers)
+#
+# Some machines have delusions that the keyboard buffer is always full
+# with no keyboard attached...
+#
+# If there is no keyboard controller, we will usually get 0xff
+# to all the reads. With each IO taking a microsecond and
+# a timeout of 100,000 iterations, this can take about half a
+# second ("delay" == outb to port 0x80). That should be ok,
+# and should also be plenty of time for a real keyboard controller
+# to empty.
+#
+
+empty_8042:
+ pushl %ecx
+ movl $100000, %ecx
+
+empty_8042_loop:
+ decl %ecx
+ jz empty_8042_end_loop
+
+ call delay
+
+ inb $0x64, %al # 8042 status port
+ testb $1, %al # output buffer?
+ jz no_output
+
+ call delay
+ inb $0x60, %al # read it
+ jmp empty_8042_loop
+
+no_output:
+ testb $2, %al # is input buffer full?
+ jnz empty_8042_loop # yes - loop
+empty_8042_end_loop:
+ popl %ecx
+
+
+# Delay is needed after doing I/O
+delay:
+ outb %al,$0x80
+ ret
+
+# Descriptor tables
+#
+# NOTE: The intel manual says gdt should be sixteen bytes aligned for
+# efficiency reasons. However, there are machines which are known not
+# to boot with misaligned GDTs, so alter this at your peril! If you alter
+# GDT_ENTRY_BOOT_CS (in asm/segment.h) remember to leave at least two
+# empty GDT entries (one for NULL and one reserved).
+#
+# NOTE: On some CPUs, the GDT must be 8 byte aligned. This is
+# true for the Voyager Quad CPU card which will not boot without
+# This directive. 16 byte aligment is recommended by intel.
+#
+ .balign 16
+bImage_gdt:
+ .fill GDT_ENTRY_BOOT_CS,8,0
+
+ .word 0xFFFF # 4Gb - (0x100000*0x1000 = 4Gb)
+ .word 0 # base address = 0
+ .word 0x9A00 # code read/exec
+ .word 0x00CF # granularity = 4096, 386
+ # (+5th nibble of limit)
+
+ .word 0xFFFF # 4Gb - (0x100000*0x1000 = 4Gb)
+ .word 0 # base address = 0
+ .word 0x9200 # data read/write
+ .word 0x00CF # granularity = 4096, 386
+ # (+5th nibble of limit)
+bImage_gdt_end:
+ .balign 4
+
+ .word 0 # alignment byte
+bImage_idt_48:
+ .word 0 # idt limit = 0
+ .long 0 # idt base = 0L
+
+ .word 0 # alignment byte
+bImage_gdt_48:
+ .word bImage_gdt_end - bImage_gdt - 1 # gdt limit
+ .long bImage_gdt_48 - setup_code # gdt base (filled in later)
+
+ .section ".text16", "ax", @progbits
+ .globl prefix_exit
+prefix_exit:
+ int $0x19 /* should try to boot machine */
+ .globl prefix_exit_end
+prefix_exit_end:
+ .previous
+
+
+ .org (PREFIXSIZE - 4)
+# Setup signature -- must be last
+setup_sig1: .word SIG1
+setup_sig2: .word SIG2
+ /* Etherboot expects to be contiguous in memory once loaded.
+ * The linux bImage protocol does not do this, but since we
+ * don't need any information that's left in the prefix, it
+ * doesn't matter: we just have to ensure that we make it to _start
+ *
+ * protected_start will live at 0x100000 and it will be the
+ * the first code called as we enter protected mode.
+ */
+ .code32
+protected_start:
+ /* Load segment registers */
+ movw $__BOOT_DS, %ax
+ movw %ax, %ss
+ movw %ax, %ds
+ movw %ax, %es
+ movw %ax, %fs
+ movw %ax, %gs
+
+ /* Use the internal etherboot stack */
+ movl $(_prefix_stack_end - protected_start + 0x100000), %esp
+
+ pushl $0 /* No parameters to preserve for exit path */
+ pushl $0 /* Use prefix exit path mechanism */
+
+ jmp _start
+/*
+ That's about it.
+*/
diff --git a/src/arch/i386/prefix/boot1a.s b/src/arch/i386/prefix/boot1a.s
new file mode 100644
index 00000000..557462f1
--- /dev/null
+++ b/src/arch/i386/prefix/boot1a.s
@@ -0,0 +1,410 @@
+# This code is no longer used in Etherboot. It is not maintained and
+# may not work.
+
+
+#
+# Copyright (c) 1998 Robert Nordier
+# All rights reserved.
+# Very small bootrom changes by Luigi Rizzo
+# <comment author="Luigi Rizzo">
+# I recently had the problem of downloading the etherboot code
+# from a hard disk partition instead of a floppy, and noticed that
+# floppyload.S does not do the job. With a bit of hacking to
+# the FreeBSD's boot1.s code, I managed to obtain a boot sector
+# which works both for floppies and hard disks -- basically you
+# do something like
+#
+# cat boot1a bin32/<yourcard>.lzrom > /dev/ad0s4
+#
+# (or whatever is the HD partition you are using, I am using slice
+# 4 on FreeBSD) and you are up and running.
+# Then with "fdisk" you have to mark your partition as having type "1"
+# (which is listed as DOS-- but basically it must be something matching
+# the variable PRT_BSD in the assembly source below).
+# </comment>
+#
+# Redistribution and use in source and binary forms are freely
+# permitted provided that the above copyright notice and this
+# paragraph and the following disclaimer are duplicated in all
+# such forms.
+#
+# This software is provided "AS IS" and without any express or
+# implied warranties, including, without limitation, the implied
+# warranties of merchantability and fitness for a particular
+# purpose.
+#
+# Makefile:
+#boot1a: boot1a.out
+# objcopy -S -O binary boot1a.out boot1a
+#
+#boot1a.out: boot1a.o
+# ld -nostdlib -static -N -e start -Ttext 0x7c00 -o boot1a.out boot1a.o
+#
+#boot1a.o: boot1a.s
+# as --defsym FLAGS=0x80 boot1a.s -o boot1a.o
+#
+#
+
+# $FreeBSD: src/sys/boot/i386/boot2/boot1.s,v 1.10.2.2 2000/07/07 21:12:32 jhb Exp $
+
+# Memory Locations
+ .set MEM_REL,0x700 # Relocation address
+ .set MEM_ARG,0x900 # Arguments
+ .set MEM_ORG,0x7c00 # Origin
+ .set MEM_BUF,0x8c00 # Load area
+ .set MEM_BTX,0x9000 # BTX start
+ .set MEM_JMP,0x9010 # BTX entry point
+ .set MEM_USR,0xa000 # Client start
+ .set BDA_BOOT,0x472 # Boot howto flag
+
+# Partition Constants
+ .set PRT_OFF,0x1be # Partition offset
+ .set PRT_NUM,0x4 # Partitions
+ .set PRT_BSD,0x1 # Partition type
+
+# Flag Bits
+ .set FL_PACKET,0x80 # Packet mode
+
+# Misc. Constants
+ .set SIZ_PAG,0x1000 # Page size
+ .set SIZ_SEC,0x200 # Sector size
+
+ .globl start
+ .globl xread
+ .code16
+
+start: jmp main # Start recognizably
+
+ .org 0x4,0x90
+#
+# Trampoline used by boot2 to call read to read data from the disk via
+# the BIOS. Call with:
+#
+# %cx:%ax - long - LBA to read in
+# %es:(%bx) - caddr_t - buffer to read data into
+# %dl - byte - drive to read from
+# %dh - byte - num sectors to read
+#
+
+xread: push %ss # Address
+ pop %ds # data
+#
+# Setup an EDD disk packet and pass it to read
+#
+xread.1: # Starting
+ pushl $0x0 # absolute
+ push %cx # block
+ push %ax # number
+ push %es # Address of
+ push %bx # transfer buffer
+ xor %ax,%ax # Number of
+ movb %dh,%al # blocks to
+ push %ax # transfer
+ push $0x10 # Size of packet
+ mov %sp,%bp # Packet pointer
+ callw read # Read from disk
+ lea 0x10(%bp),%sp # Clear stack
+ lret # To far caller
+#
+# Load the rest of boot2 and BTX up, copy the parts to the right locations,
+# and start it all up.
+#
+
+#
+# Setup the segment registers to flat addressing (segment 0) and setup the
+# stack to end just below the start of our code.
+#
+main: cld # String ops inc
+ xor %cx,%cx # Zero
+ mov %cx,%es # Address
+ mov %cx,%ds # data
+ mov %cx,%ss # Set up
+ mov $start,%sp # stack
+#
+# Relocate ourself to MEM_REL. Since %cx == 0, the inc %ch sets
+# %cx == 0x100.
+#
+ mov %sp,%si # Source
+ mov $MEM_REL,%di # Destination
+ incb %ch # Word count
+ rep # Copy
+ movsw # code
+#
+# If we are on a hard drive, then load the MBR and look for the first
+# FreeBSD slice. We use the fake partition entry below that points to
+# the MBR when we call nread. The first pass looks for the first active
+# FreeBSD slice. The second pass looks for the first non-active FreeBSD
+# slice if the first one fails.
+#
+ mov $part4,%si # Partition
+ cmpb $0x80,%dl # Hard drive?
+ jb main.4 # No
+ movb $0x1,%dh # Block count
+ callw nread # Read MBR
+ mov $0x1,%cx # Two passes
+main.1: mov $MEM_BUF+PRT_OFF,%si # Partition table
+ movb $0x1,%dh # Partition
+main.2: cmpb $PRT_BSD,0x4(%si) # Our partition type?
+ jne main.3 # No
+ jcxz main.5 # If second pass
+ testb $0x80,(%si) # Active?
+ jnz main.5 # Yes
+main.3: add $0x10,%si # Next entry
+ incb %dh # Partition
+ cmpb $0x1+PRT_NUM,%dh # In table?
+ jb main.2 # Yes
+ dec %cx # Do two
+ jcxz main.1 # passes
+#
+# If we get here, we didn't find any FreeBSD slices at all, so print an
+# error message and die.
+#
+booterror: mov $msg_part,%si # Message
+ jmp error # Error
+#
+# Floppies use partition 0 of drive 0.
+#
+main.4: xor %dx,%dx # Partition:drive
+#
+# Ok, we have a slice and drive in %dx now, so use that to locate and load
+# boot2. %si references the start of the slice we are looking for, so go
+# ahead and load up the first 16 sectors (boot1 + boot2) from that. When
+# we read it in, we conveniently use 0x8c00 as our transfer buffer. Thus,
+# boot1 ends up at 0x8c00, and boot2 starts at 0x8c00 + 0x200 = 0x8e00.
+# The first part of boot2 is the disklabel, which is 0x200 bytes long.
+# The second part is BTX, which is thus loaded into 0x9000, which is where
+# it also runs from. The boot2.bin binary starts right after the end of
+# BTX, so we have to figure out where the start of it is and then move the
+# binary to 0xb000. Normally, BTX clients start at MEM_USR, or 0xa000, but
+# when we use btxld create boot2, we use an entry point of 0x1000. That
+# entry point is relative to MEM_USR; thus boot2.bin starts at 0xb000.
+#
+main.5: mov %dx,MEM_ARG # Save args
+ movb $0x2,%dh # Sector count
+ mov $0x7e00, %bx
+ callw nreadbx # Read disk
+ movb $0x40,%dh # Sector count
+ movb %dh, %al
+ callw puthex
+ mov $0x7e00, %bx
+ callw nreadbx # Read disk
+ push %si
+ mov $msg_r1,%si
+ callw putstr
+ pop %si
+ lcall $0x800,$0 # enter the rom code
+ int $0x19
+
+msg_r1: .asciz " done\r\n"
+
+.if 0
+ mov $MEM_BTX,%bx # BTX
+ mov 0xa(%bx),%si # Get BTX length and set
+ add %bx,%si # %si to start of boot2.bin
+ mov $MEM_USR+SIZ_PAG,%di # Client page 1
+ mov $MEM_BTX+0xe*SIZ_SEC,%cx # Byte
+ sub %si,%cx # count
+ rep # Relocate
+ movsb # client
+ sub %di,%cx # Byte count
+ xorb %al,%al # Zero assumed bss from
+ rep # the end of boot2.bin
+ stosb # up to 0x10000
+ callw seta20 # Enable A20
+ jmp start+MEM_JMP-MEM_ORG # Start BTX
+#
+# Enable A20 so we can access memory above 1 meg.
+#
+seta20: cli # Disable interrupts
+seta20.1: inb $0x64,%al # Get status
+ testb $0x2,%al # Busy?
+ jnz seta20.1 # Yes
+ movb $0xd1,%al # Command: Write
+ outb %al,$0x64 # output port
+seta20.2: inb $0x64,%al # Get status
+ testb $0x2,%al # Busy?
+ jnz seta20.2 # Yes
+ movb $0xdf,%al # Enable
+ outb %al,$0x60 # A20
+ sti # Enable interrupts
+ retw # To caller
+.endif
+#
+# Trampoline used to call read from within boot1.
+#
+nread: mov $MEM_BUF,%bx # Transfer buffer
+nreadbx: # same but address is in bx
+ mov 0x8(%si),%ax # Get
+ mov 0xa(%si),%cx # LBA
+ push %bx
+ push %ax
+ callw putword
+ pop %ax
+ pop %bx
+ push %cs # Read from
+ callw xread.1 # disk
+ jnc return # If success, return
+ mov $msg_read,%si # Otherwise, set the error
+ # message and fall through to
+ # the error routine
+#
+# Print out the error message pointed to by %ds:(%si) followed
+# by a prompt, wait for a keypress, and then reboot the machine.
+#
+error: callw putstr # Display message
+ mov $prompt,%si # Display
+ callw putstr # prompt
+ xorb %ah,%ah # BIOS: Get
+ int $0x16 # keypress
+ movw $0x1234, BDA_BOOT # Do a warm boot
+ ljmp $0xffff,$0x0 # reboot the machine
+#
+# Display a null-terminated string using the BIOS output.
+#
+putstr.0: call putchar
+putstr: lodsb # Get char
+ testb %al,%al # End of string?
+ jne putstr.0 # No
+ retw
+
+putword: push %ax
+ movb $'.', %al
+ callw putchar
+ movb %ah, %al
+ callw puthex
+ pop %ax
+puthex: push %ax
+ shr $4, %al
+ callw putdigit
+ pop %ax
+putdigit:
+ andb $0xf, %al
+ addb $0x30, %al
+ cmpb $0x39, %al
+ jbe putchar
+ addb $7, %al
+putchar: push %ax
+ mov $0x7,%bx
+ movb $0xe,%ah
+ int $0x10
+ pop %ax
+ retw
+
+#
+# Overused return code. ereturn is used to return an error from the
+# read function. Since we assume putstr succeeds, we (ab)use the
+# same code when we return from putstr.
+#
+ereturn: movb $0x1,%ah # Invalid
+ stc # argument
+return: retw # To caller
+#
+# Reads sectors from the disk. If EDD is enabled, then check if it is
+# installed and use it if it is. If it is not installed or not enabled, then
+# fall back to using CHS. Since we use a LBA, if we are using CHS, we have to
+# fetch the drive parameters from the BIOS and divide it out ourselves.
+# Call with:
+#
+# %dl - byte - drive number
+# stack - 10 bytes - EDD Packet
+#
+read: push %dx # Save
+ movb $0x8,%ah # BIOS: Get drive
+ int $0x13 # parameters
+ movb %dh,%ch # Max head number
+ pop %dx # Restore
+ jc return # If error
+ andb $0x3f,%cl # Sectors per track
+ jz ereturn # If zero
+ cli # Disable interrupts
+ mov 0x8(%bp),%eax # Get LBA
+ push %dx # Save
+ movzbl %cl,%ebx # Divide by
+ xor %edx,%edx # sectors
+ div %ebx # per track
+ movb %ch,%bl # Max head number
+ movb %dl,%ch # Sector number
+ inc %bx # Divide by
+ xorb %dl,%dl # number
+ div %ebx # of heads
+ movb %dl,%bh # Head number
+ pop %dx # Restore
+ cmpl $0x3ff,%eax # Cylinder number supportable?
+ sti # Enable interrupts
+ ja read.7 # No, try EDD
+ xchgb %al,%ah # Set up cylinder
+ rorb $0x2,%al # number
+ orb %ch,%al # Merge
+ inc %ax # sector
+ xchg %ax,%cx # number
+ movb %bh,%dh # Head number
+ subb %ah,%al # Sectors this track
+ mov 0x2(%bp),%ah # Blocks to read
+ cmpb %ah,%al # To read
+ jb read.2 # this
+ movb %ah,%al # track
+read.2: mov $0x5,%di # Try count
+read.3: les 0x4(%bp),%bx # Transfer buffer
+ push %ax # Save
+ movb $0x2,%ah # BIOS: Read
+ int $0x13 # from disk
+ pop %bx # Restore
+ jnc read.4 # If success
+ dec %di # Retry?
+ jz read.6 # No
+ xorb %ah,%ah # BIOS: Reset
+ int $0x13 # disk system
+ xchg %bx,%ax # Block count
+ jmp read.3 # Continue
+read.4: movzbw %bl,%ax # Sectors read
+ add %ax,0x8(%bp) # Adjust
+ jnc read.5 # LBA,
+ incw 0xa(%bp) # transfer
+read.5: shlb %bl # buffer
+ add %bl,0x5(%bp) # pointer,
+ sub %al,0x2(%bp) # block count
+ ja read # If not done
+read.6: retw # To caller
+read.7: testb $FL_PACKET,%cs:MEM_REL+flags-start # LBA support enabled?
+ jz ereturn # No, so return an error
+ mov $0x55aa,%bx # Magic
+ push %dx # Save
+ movb $0x41,%ah # BIOS: Check
+ int $0x13 # extensions present
+ pop %dx # Restore
+ jc return # If error, return an error
+ cmp $0xaa55,%bx # Magic?
+ jne ereturn # No, so return an error
+ testb $0x1,%cl # Packet interface?
+ jz ereturn # No, so return an error
+ mov %bp,%si # Disk packet
+ movb $0x42,%ah # BIOS: Extended
+ int $0x13 # read
+ retw # To caller
+
+# Messages
+
+msg_read: .asciz "Rd"
+msg_part: .asciz "Boot"
+
+prompt: .asciz " err\r\n"
+
+flags: .byte FLAGS # Flags
+
+ .org PRT_OFF,0x90
+
+# Partition table
+
+ .fill 0x30,0x1,0x0
+part4: .byte 0x80
+ .byte 0x00 # start head
+ .byte 0x01 # start sector (6 bits) + start cyl (2 bit)
+ .byte 0x00 # start cyl (low 8 bits)
+ .byte 0x1 # part.type
+ .byte 0xff # end head
+ .byte 0xff # end sect (6) + end_cyl(2)
+ .byte 0xff # end cyl
+ .byte 0x00, 0x00, 0x00, 0x00 # explicit start
+ .byte 0x50, 0xc3, 0x00, 0x00 # 50000 sectors long, bleh
+
+ .word 0xaa55 # Magic number
diff --git a/src/arch/i386/prefix/comprefix.S b/src/arch/i386/prefix/comprefix.S
new file mode 100644
index 00000000..8be8db77
--- /dev/null
+++ b/src/arch/i386/prefix/comprefix.S
@@ -0,0 +1,49 @@
+/* We need a real mode stack that won't be stomped on by Etherboot
+ which starts at 0x20000. Choose something that's sufficiently high,
+ but not in DOC territory. Note that we couldn't do this in a real
+ .com program since stack variables are in the same segment as the
+ code and data, but this isn't really a .com program, it just looks
+ like one to make DOS load it into memory. It still has the 64kB
+ limitation of .com files though. */
+#define STACK_SEG 0x7000
+#define STACK_SIZE 0x4000
+
+ .text
+ .code16
+ .arch i386
+ .section ".prefix", "ax", @progbits
+ .globl _prefix
+
+/* Cheat a little with the relocations: .COM files are loaded at 0x100 */
+_prefix:
+ /* Set up temporary stack */
+ movw $STACK_SEG, %ax
+ movw %ax, %ss
+ movw $STACK_SIZE, %sp
+
+ pushl $0 /* No parameters to preserve for exit path */
+ pushw $0 /* Dummy return address - use prefix_exit */
+
+ /* Calculate segment address of image start */
+ pushw %cs
+ popw %ax
+ addw $(0x100/16), %ax
+ pushw %ax
+ pushw $_start
+ /* Calculated lcall to _start with %cs:0000 = image start */
+ lret
+
+ .section ".text16", "ax", @progbits
+ .globl prefix_exit
+prefix_exit:
+ movw $0x4c00,%ax /* return to DOS */
+ int $0x21 /* reach this on Quit */
+ .globl prefix_exit_end
+prefix_exit_end:
+ .previous
+
+/* The body of etherboot is attached here at build time.
+ * Force 16 byte alignment
+ */
+ .align 16,0
+_body:
diff --git a/src/arch/i386/prefix/elf_dprefix.S b/src/arch/i386/prefix/elf_dprefix.S
new file mode 100644
index 00000000..d2453be4
--- /dev/null
+++ b/src/arch/i386/prefix/elf_dprefix.S
@@ -0,0 +1,96 @@
+#include "elf.h"
+
+ .arch i386
+ .section ".prefix", "a", @progbits
+
+#define LOAD_ADDR 0x10000
+
+ /* ELF Header */
+ .globl elf_header
+elf_header:
+e_ident: .byte 0x7f, 'E', 'L', 'F', 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0
+e_type: .short ET_DYN
+e_machine: .short EM_386
+e_version: .long 1
+e_entry: .long LOAD_ADDR + _start - elf_header
+e_phoff: .long elf_program_header - elf_header
+e_shoff: .long 0
+e_flags: .long 0
+e_ehsize: .short elf_header_end - elf_header
+e_phentsize: .short ELF32_PHDR_SIZE
+e_phnum: .short (elf_program_header_end - elf_program_header)/ELF32_PHDR_SIZE
+e_shentsize: .short 0
+e_shnum: .short 0
+e_shstrndx: .short 0
+elf_header_end:
+
+elf_program_header:
+phdr1_p_type: .long PT_NOTE
+phdr1_p_offset: .long elf_note - elf_header
+phdr1_p_vaddr: .long elf_note
+phdr1_p_paddr: .long elf_note
+phdr1_p_filesz: .long elf_note_end - elf_note
+phdr1_p_memsz: .long elf_note_end - elf_note
+phdr1_p_flags: .long PF_R | PF_W | PF_X
+phdr1_p_align: .long 0
+
+/* The decompressor */
+phdr2_p_type: .long PT_LOAD
+phdr2_p_offset: .long 0
+phdr2_p_vaddr: .long elf_header
+phdr2_p_paddr: .long LOAD_ADDR
+phdr2_p_filesz: .long _verbatim_size
+phdr2_p_memsz: .long _image_size
+phdr2_p_flags: .long PF_R | PF_W | PF_X
+phdr2_p_align: .long 16
+
+elf_program_header_end:
+
+ .globl elf_note
+elf_note:
+ .balign 4
+ .int 2f - 1f
+ .int 4f - 3f
+ .int EIN_PROGRAM_NAME
+1: .asciz "ELFBoot"
+2:
+ .balign 4
+3:
+ .asciz "Etherboot"
+4:
+
+
+ .balign 4
+ .int 2f - 1f
+ .int 4f - 3f
+ .int EIN_PROGRAM_VERSION
+1: .asciz "ELFBoot"
+2:
+ .balign 4
+3:
+ .asciz VERSION
+4:
+
+#if 0
+ .balign 4
+ .int 2f - 1f
+ .int 4f - 3f
+ .int EIN_PROGRAM_CHECKSUM
+1: .asciz "ELFBoot"
+2:
+ .balign 4
+3:
+ .word 0
+4:
+#endif
+ .balign 4
+elf_note_end:
+
+ /* Dummy routines to satisfy the build */
+ .section ".text16", "ax", @progbits
+ .globl prefix_exit
+prefix_exit:
+
+ .globl prefix_exit_end
+prefix_exit_end:
+ .previous
diff --git a/src/arch/i386/prefix/elfprefix.S b/src/arch/i386/prefix/elfprefix.S
new file mode 100644
index 00000000..c5200678
--- /dev/null
+++ b/src/arch/i386/prefix/elfprefix.S
@@ -0,0 +1,96 @@
+#include "elf.h"
+
+ .arch i386
+ .section ".prefix", "a", @progbits
+
+#define LOAD_ADDR 0x10000
+
+ /* ELF Header */
+ .globl elf_header
+elf_header:
+e_ident: .byte 0x7f, 'E', 'L', 'F', 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0
+e_type: .short ET_EXEC
+e_machine: .short EM_386
+e_version: .long 1
+e_entry: .long LOAD_ADDR + _start - elf_header
+e_phoff: .long elf_program_header - elf_header
+e_shoff: .long 0
+e_flags: .long 0
+e_ehsize: .short elf_header_end - elf_header
+e_phentsize: .short ELF32_PHDR_SIZE
+e_phnum: .short (elf_program_header_end - elf_program_header)/ELF32_PHDR_SIZE
+e_shentsize: .short 0
+e_shnum: .short 0
+e_shstrndx: .short 0
+elf_header_end:
+
+elf_program_header:
+phdr1_p_type: .long PT_NOTE
+phdr1_p_offset: .long elf_note - elf_header
+phdr1_p_vaddr: .long elf_note
+phdr1_p_paddr: .long elf_note
+phdr1_p_filesz: .long elf_note_end - elf_note
+phdr1_p_memsz: .long elf_note_end - elf_note
+phdr1_p_flags: .long PF_R | PF_W | PF_X
+phdr1_p_align: .long 0
+
+/* The decompressor */
+phdr2_p_type: .long PT_LOAD
+phdr2_p_offset: .long 0
+phdr2_p_vaddr: .long elf_header
+phdr2_p_paddr: .long LOAD_ADDR
+phdr2_p_filesz: .long _verbatim_size
+phdr2_p_memsz: .long _image_size
+phdr2_p_flags: .long PF_R | PF_W | PF_X
+phdr2_p_align: .long 16
+
+elf_program_header_end:
+
+ .globl elf_note
+elf_note:
+ .balign 4
+ .int 2f - 1f
+ .int 4f - 3f
+ .int EIN_PROGRAM_NAME
+1: .asciz "ELFBoot"
+2:
+ .balign 4
+3:
+ .asciz "Etherboot"
+4:
+
+
+ .balign 4
+ .int 2f - 1f
+ .int 4f - 3f
+ .int EIN_PROGRAM_VERSION
+1: .asciz "ELFBoot"
+2:
+ .balign 4
+3:
+ .asciz VERSION
+4:
+
+#if 0
+ .balign 4
+ .int 2f - 1f
+ .int 4f - 3f
+ .int EIN_PROGRAM_CHECKSUM
+1: .asciz "ELFBoot"
+2:
+ .balign 4
+3:
+ .word 0
+4:
+#endif
+ .balign 4
+elf_note_end:
+
+ /* Dummy routines to satisfy the build */
+ .section ".text16", "ax", @progbits
+ .globl prefix_exit
+prefix_exit:
+
+ .globl prefix_exit_end
+prefix_exit_end:
+ .previous
diff --git a/src/arch/i386/prefix/exeprefix.S b/src/arch/i386/prefix/exeprefix.S
new file mode 100755
index 00000000..d169563e
--- /dev/null
+++ b/src/arch/i386/prefix/exeprefix.S
@@ -0,0 +1,44 @@
+/*
+ Prefix for .exe images
+ Doesn't work yet, even though it starts off the same as a .com
+ image as shown by DOS debug.
+*/
+
+ .text
+ .code16
+ .arch i386
+ .section ".prefix", "ax", @progbits
+ .globl _prefix
+
+_prefix:
+ .byte 'M', 'Z'
+ .short _exe_size_tail /* tail */
+ .short _exe_size_pages /* pages */
+ .short 0 /* relocations */
+ .short 2 /* header paras */
+ .short _exe_bss_size /* min */
+ .short 0xFFFF /* max paras */
+ .short _exe_ss_offset /* SS */
+ .short _stack_size /* SP */
+ .short 0 /* checksum */
+ .short 0 /* IP */
+ .short 0 /* CS */
+ .short 0x1C /* reloc offset */
+ .short 0 /* overlay number */
+ .short 0 /* fill */
+ .short 0 /* fill */
+
+ .section ".text16", "ax", @progbits
+ .globl prefix_exit
+prefix_exit:
+ movw $0x4c00,%ax /* return to DOS */
+ int $0x21 /* reach this on Quit */
+ .globl prefix_exit_end
+prefix_exit_end:
+ .previous
+
+/* The body of etherboot is attached here at build time.
+ * Force 16 byte alignment
+ */
+ .align 16,0
+_body:
diff --git a/src/arch/i386/prefix/floppyprefix.S b/src/arch/i386/prefix/floppyprefix.S
new file mode 100644
index 00000000..18bed4c8
--- /dev/null
+++ b/src/arch/i386/prefix/floppyprefix.S
@@ -0,0 +1,359 @@
+/* NOTE: this boot sector contains instructions that need at least an 80186.
+ * Yes, as86 has a bug somewhere in the valid instruction set checks.
+ *
+ * SYS_SIZE is the number of clicks (16 bytes) to be loaded.
+ */
+.equ SYSSIZE, 8192 # 8192 * 16 bytes = 128kB maximum size of .ROM file
+
+/* floppyload.S Copyright (C) 1991, 1992 Linus Torvalds
+ * modified by Drew Eckhardt
+ * modified by Bruce Evans (bde)
+ *
+ * floppyprefix.S is loaded at 0x0000:0x7c00 by the bios-startup routines.
+ *
+ * It then loads the system at SYSSEG<<4, using BIOS interrupts.
+ *
+ * The loader has been made as simple as possible, and continuous read errors
+ * will result in a unbreakable loop. Reboot by hand. It loads pretty fast by
+ * getting whole tracks at a time whenever possible.
+ */
+
+.equ BOOTSEG, 0x07C0 /* original address of boot-sector */
+
+.equ SYSSEG, 0x1000 /* system loaded at SYSSEG<<4 */
+
+ .org 0
+ .arch i386
+ .text
+ .section ".prefix", "ax", @progbits
+ .code16
+
+ jmp $BOOTSEG, $go /* reload cs:ip to match relocation addr */
+go:
+ movw $0x2000-12, %di /* 0x2000 is arbitrary value >= length */
+ /* of bootsect + room for stack + 12 for */
+ /* saved disk parm block */
+
+ movw $BOOTSEG, %ax
+ movw %ax,%ds
+ movw %ax,%es
+ movw %ax,%ss /* put stack at BOOTSEG:0x4000-12. */
+ movw %di,%sp
+
+/* Many BIOS's default disk parameter tables will not recognize multi-sector
+ * reads beyond the maximum sector number specified in the default diskette
+ * parameter tables - this may mean 7 sectors in some cases.
+ *
+ * Since single sector reads are slow and out of the question, we must take care
+ * of this by creating new parameter tables (for the first disk) in RAM. We
+ * will set the maximum sector count to 36 - the most we will encounter on an
+ * ED 2.88. High doesn't hurt. Low does.
+ *
+ * Segments are as follows: ds=es=ss=cs - BOOTSEG
+ */
+
+ xorw %cx,%cx
+ movw %cx,%es /* access segment 0 */
+ movw $0x78, %bx /* 0:bx is parameter table address */
+ pushw %ds /* save ds */
+/* 0:bx is parameter table address */
+ ldsw %es:(%bx),%si /* loads ds and si */
+
+ movw %ax,%es /* ax is BOOTSECT (loaded above) */
+ movb $6, %cl /* copy 12 bytes */
+ cld
+ pushw %di /* keep a copy for later */
+ rep
+ movsw /* ds:si is source, es:di is dest */
+ popw %di
+
+ movb $36,%es:4(%di)
+
+ movw %cx,%ds /* access segment 0 */
+ xchgw %di,(%bx)
+ movw %es,%si
+ xchgw %si,2(%bx)
+ popw %ds /* restore ds */
+ movw %di, dpoff /* save old parameters */
+ movw %si, dpseg /* to restore just before finishing */
+ pushw %ds
+ popw %es /* reload es */
+
+/* Note that es is already set up. Also cx is 0 from rep movsw above. */
+
+ xorb %ah,%ah /* reset FDC */
+ xorb %dl,%dl
+ int $0x13
+
+/* Get disk drive parameters, specifically number of sectors/track.
+ *
+ * It seems that there is no BIOS call to get the number of sectors. Guess
+ * 36 sectors if sector 36 can be read, 18 sectors if sector 18 can be read,
+ * 15 if sector 15 can be read. Otherwise guess 9.
+ */
+
+ movw $disksizes, %si /* table of sizes to try */
+
+probe_loop:
+ lodsb
+ cbtw /* extend to word */
+ movw %ax, sectors
+ cmpw $disksizes+4, %si
+ jae got_sectors /* if all else fails, try 9 */
+ xchgw %cx,%ax /* cx = track and sector */
+ xorw %dx,%dx /* drive 0, head 0 */
+ movw $0x0200, %bx /* address after boot sector */
+ /* (512 bytes from origin, es = cs) */
+ movw $0x0201, %ax /* service 2, 1 sector */
+ int $0x13
+ jc probe_loop /* try next value */
+
+got_sectors:
+ movw $msg1end-msg1, %cx
+ movw $msg1, %si
+ call print_str
+
+/* ok, we've written the Loading... message, now we want to load the system */
+
+ pushw %es /* = ds */
+ movw $SYSSEG, %ax
+ movw %ax,%es /* segment of SYSSEG<<4 */
+ pushw %es
+ call read_it
+
+/* This turns off the floppy drive motor, so that we enter the kernel in a
+ * known state, and don't have to worry about it later.
+ */
+ movw $0x3f2, %dx
+ xorb %al,%al
+ outb %al,%dx
+
+ call print_nl
+ pop %es /* = SYSSEG */
+ pop %es /* balance push/pop es */
+sigok:
+
+/* Restore original disk parameters */
+ movw $0x78, %bx
+ movw dpoff, %di
+ movw dpseg, %si
+ xorw %ax,%ax
+ movw %ax,%ds
+ movw %di,(%bx)
+ movw %si,2(%bx)
+
+/* after that (everything loaded), we call to the .ROM file loaded. */
+
+ pushl $0 /* No parameters to preserve for exit path */
+ pushw $0 /* Use prefix exit path mechanism */
+ ljmp $SYSSEG, $_start
+
+ .section ".text16", "ax", @progbits
+ .globl prefix_exit
+prefix_exit:
+ xchgw %bx, %bx
+ int $0x19 /* should try to boot machine */
+ .globl prefix_exit_end
+prefix_exit_end:
+ .previous
+
+/* This routine loads the system at address SYSSEG<<4, making sure no 64kB
+ * boundaries are crossed. We try to load it as fast as possible, loading whole
+ * tracks whenever we can.
+ *
+ * in: es - starting address segment (normally SYSSEG)
+ */
+read_it:
+ movw $0,sread /* read whole image incl boot sector */
+ movw %es,%ax
+ testw $0x0fff, %ax
+die: jne die /* es must be at 64kB boundary */
+ xorw %bx,%bx /* bx is starting address within segment */
+rp_read:
+ movw %es,%ax
+ movw %bx,%dx
+ movb $4, %cl
+ shrw %cl,%dx /* bx is always divisible by 16 */
+ addw %dx,%ax
+ cmpw $SYSSEG+SYSSIZE, %ax /* have we loaded all yet? */
+ jb ok1_read
+ ret
+ok1_read:
+ movw sectors, %ax
+ subw sread, %ax
+ movw %ax,%cx
+ shlw $9, %cx
+ addw %bx,%cx
+ jnc ok2_read
+ je ok2_read
+ xorw %ax,%ax
+ subw %bx,%ax
+ shrw $9, %ax
+ok2_read:
+ call read_track
+ movw %ax,%cx
+ addw sread, %ax
+ cmpw sectors, %ax
+ jne ok3_read
+ movw $1, %ax
+ subw head, %ax
+ jne ok4_read
+ incw track
+ok4_read:
+ movw %ax, head
+ xorw %ax,%ax
+ok3_read:
+ movw %ax, sread
+ shlw $9, %cx
+ addw %cx,%bx
+ jnc rp_read
+ movw %es,%ax
+ addb $0x10, %ah
+ movw %ax,%es
+ xorw %bx,%bx
+ jmp rp_read
+
+read_track:
+ pusha
+ pushw %ax
+ pushw %bx
+ pushw %bp /* just in case the BIOS is buggy */
+ movw $0x0e2e, %ax /* 0x2e = . */
+ movw $0x0007, %bx
+ int $0x10
+ popw %bp
+ popw %bx
+ popw %ax
+
+ movw track, %dx
+ movw sread, %cx
+ incw %cx
+ movb %dl,%ch
+ movw head, %dx
+ movb %dl,%dh
+ andw $0x0100, %dx
+ movb $2, %ah
+
+ pushw %dx /* save for error dump */
+ pushw %cx
+ pushw %bx
+ pushw %ax
+
+ int $0x13
+ jc bad_rt
+ addw $8, %sp
+ popa
+ ret
+
+bad_rt: pushw %ax /* save error code */
+ call print_all /* ah = error, al = read */
+
+ xorb %ah,%ah
+ xorb %dl,%dl
+ int $0x13
+
+ addw $10, %sp
+ popa
+ jmp read_track
+
+/* print_all is for debugging purposes. It will print out all of the registers.
+ * The assumption is that this is called from a routine, with a stack frame like
+ * dx
+ * cx
+ * bx
+ * ax
+ * error
+ * ret <- sp
+ */
+
+print_all:
+ call print_nl /* nl for readability */
+ movw $5, %cx /* error code + 4 registers */
+ movw %sp,%bp
+
+print_loop:
+ pushw %cx /* save count left */
+
+ cmpb $5, %cl
+ jae no_reg /* see if register name is needed */
+
+ movw $0x0007, %bx /* page 0, attribute 7 (normal) */
+ movw $0xe05+0x41-1, %ax
+ subb %cl,%al
+ int $0x10
+
+ movb $0x58, %al /* 'X' */
+ int $0x10
+
+ movb $0x3A, %al /* ':' */
+ int $0x10
+
+no_reg:
+ addw $2, %bp /* next register */
+ call print_hex /* print it */
+ movb $0x20, %al /* print a space */
+ int $0x10
+ popw %cx
+ loop print_loop
+ call print_nl /* nl for readability */
+ ret
+
+print_str:
+ movw $0x0007, %bx /* page 0, attribute 7 (normal) */
+ movb $0x0e, %ah /* write char, tty mode */
+prloop:
+ lodsb
+ int $0x10
+ loop prloop
+ ret
+
+print_nl:
+ movw $0x0007, %bx /* page 0, attribute 7 (normal) */
+ movw $0xe0d, %ax /* CR */
+ int $0x10
+ movb $0xa, %al /* LF */
+ int $0x10
+ ret
+
+/* print_hex prints the word pointed to by ss:bp in hexadecimal. */
+
+print_hex:
+ movw (%bp),%dx /* load word into dx */
+ movb $4, %cl
+ movb $0x0e, %ah /* write char, tty mode */
+ movw $0x0007, %bx /* page 0, attribute 7 (normal) */
+ call print_digit
+ call print_digit
+ call print_digit
+/* fall through */
+print_digit:
+ rol %cl,%dx /* rotate so that lowest 4 bits are used */
+ movb $0x0f, %al /* mask for nybble */
+ andb %dl,%al
+ addb $0x90, %al /* convert al to ascii hex (four instructions) */
+ daa
+ adcb $0x40, %al
+ daa
+ int $0x10
+ ret
+
+sread: .word 0 /* sectors read of current track */
+head: .word 0 /* current head */
+track: .word 0 /* current track */
+
+sectors:
+ .word 0
+
+dpseg: .word 0
+dpoff: .word 0
+
+disksizes:
+ .byte 36,18,15,9
+
+msg1:
+ .ascii "Loading ROM image"
+msg1end:
+
+ .org 510, 0
+ .word 0xAA55
+
diff --git a/src/arch/i386/prefix/huf.lds b/src/arch/i386/prefix/huf.lds
new file mode 100644
index 00000000..1e8da162
--- /dev/null
+++ b/src/arch/i386/prefix/huf.lds
@@ -0,0 +1,6 @@
+OUTPUT_FORMAT("elf32-i386", "elf32-i386", "elf32-i386")
+OUTPUT_ARCH(i386)
+
+SECTIONS {
+ .huf : { *(*) }
+}
diff --git a/src/arch/i386/prefix/img.lds b/src/arch/i386/prefix/img.lds
new file mode 100644
index 00000000..e383d89e
--- /dev/null
+++ b/src/arch/i386/prefix/img.lds
@@ -0,0 +1,6 @@
+OUTPUT_FORMAT("elf32-i386", "elf32-i386", "elf32-i386")
+OUTPUT_ARCH(i386)
+
+SECTIONS {
+ .img : { *(*) }
+}
diff --git a/src/arch/i386/prefix/liloprefix.S b/src/arch/i386/prefix/liloprefix.S
new file mode 100644
index 00000000..1aeb071f
--- /dev/null
+++ b/src/arch/i386/prefix/liloprefix.S
@@ -0,0 +1,144 @@
+/*
+ Copyright (C) 2000, Entity Cyber, Inc.
+
+ Authors: Gary Byers (gb@thinguin.org)
+ Marty Connor (mdc@thinguin.org)
+
+ This software may be used and distributed according to the terms
+ of the GNU Public License (GPL), incorporated herein by reference.
+
+ Description:
+
+ This is just a little bit of code and data that can get prepended
+ to an Etherboot ROM image in order to allow LILO to load the
+ result as if it were a Linux kernel image.
+
+ A real Linux kernel image consists of a one-sector boot loader
+ (to load the image from a floppy disk), followed a few sectors
+ of setup code, followed by the kernel code itself. There's
+ a table in the first sector (starting at offset 497) that indicates
+ how many sectors of setup code follow the first sector and which
+ contains some other parameters that aren't interesting in this
+ case.
+
+ When LILO loads the sectors that comprise a kernel image, it doesn't
+ execute the code in the first sector (since that code would try to
+ load the image from a floppy disk.) The code in the first sector
+ below doesn't expect to get executed (and prints an error message
+ if it ever -is- executed.) LILO's only interested in knowing the
+ number of setup sectors advertised in the table (at offset 497 in
+ the first sector.)
+
+ Etherboot doesn't require much in the way of setup code.
+ Historically, the Linux kernel required at least 4 sectors of
+ setup code. Current versions of LILO look at the byte at
+ offset 497 in the first sector to indicate how many sectors
+ of setup code are contained in the image.
+
+*/
+
+#define SETUPSECS 4 /* Minimal nr of setup-sectors */
+#define PREFIXSIZE ((SETUPSECS+1)*512)
+#define PREFIXPGH (PREFIXSIZE / 16 )
+#define BOOTSEG 0x07C0 /* original address of boot-sector */
+#define INITSEG 0x9000 /* we move boot here - out of the way */
+#define SETUPSEG 0x9020 /* setup starts here */
+#define SYSSEG 0x1000 /* system loaded at 0x10000 (65536). */
+
+ .text
+ .code16
+ .arch i386
+ .org 0
+ .section ".prefix", "ax", @progbits
+ .globl _prefix
+_prefix:
+
+/*
+ This is a minimal boot sector. If anyone tries to execute it (e.g., if
+ a .lilo file is dd'ed to a floppy), print an error message.
+*/
+
+bootsector:
+ jmp $BOOTSEG, $go - _prefix /* reload cs:ip to match relocation addr */
+go:
+ movw $0x2000, %di /* 0x2000 is arbitrary value >= length
+ of bootsect + room for stack */
+
+ movw $BOOTSEG, %ax
+ movw %ax,%ds
+ movw %ax,%es
+
+ cli
+ movw %ax, %ss /* put stack at BOOTSEG:0x2000. */
+ movw %di,%sp
+ sti
+
+ movw $why_end-why, %cx
+ movw $why - _prefix, %si
+
+ movw $0x0007, %bx /* page 0, attribute 7 (normal) */
+ movb $0x0e, %ah /* write char, tty mode */
+prloop:
+ lodsb
+ int $0x10
+ loop prloop
+freeze: jmp freeze
+
+why: .ascii "This image cannot be loaded from a floppy disk.\r\n"
+why_end:
+
+
+ .org 497
+setup_sects:
+ .byte SETUPSECS
+root_flags:
+ .word 0
+syssize:
+ .word _verbatim_size_pgh - PREFIXPGH
+swap_dev:
+ .word 0
+ram_size:
+ .word 0
+vid_mode:
+ .word 0
+root_dev:
+ .word 0
+boot_flag:
+ .word 0xAA55
+
+/*
+ We're now at the beginning of the second sector of the image -
+ where the setup code goes.
+
+ We don't need to do too much setup for Etherboot.
+
+ This code gets loaded at SETUPSEG:0. It wants to start
+ executing the Etherboot image that's loaded at SYSSEG:0 and
+ whose entry point is SYSSEG:0.
+*/
+setup_code:
+ pushl $0 /* No parameters to preserve for exit path */
+ pushw $0 /* Use prefix exit path mechanism */
+ /* Etherboot expects to be contiguous in memory once loaded.
+ * LILO doesn't do this, but since we don't need any
+ * information that's left in the prefix, it doesn't matter:
+ * we just have to ensure that %cs:0000 is where the start of
+ * the Etherboot image *would* be.
+ */
+ ljmp $(SYSSEG-(PREFIXSIZE/16)), $_start
+
+ .section ".text16", "ax", @progbits
+ .globl prefix_exit
+prefix_exit:
+ int $0x19 /* should try to boot machine */
+ .globl prefix_exit_end
+prefix_exit_end:
+ .previous
+
+ .org (PREFIXSIZE-1)
+ .byte 0
+prefix_end:
+/*
+ That's about it.
+*/
+
diff --git a/src/arch/i386/prefix/lmelf_dprefix.S b/src/arch/i386/prefix/lmelf_dprefix.S
new file mode 100644
index 00000000..65fe1356
--- /dev/null
+++ b/src/arch/i386/prefix/lmelf_dprefix.S
@@ -0,0 +1,163 @@
+#include "elf.h"
+ .arch sledgehammer
+ .code32
+ .equ FLAT_CODE_SEG,_pmcs-_gdt
+ .equ FLAT_DATA_SEG,_pmds-_gdt
+ .equ MSR_K6_EFER, 0xC0000080
+ .equ EFER_LME, 0x00000100
+ .equ X86_CR4_PAE, 0x00000020
+ .equ CR0_PG, 0x80000000
+
+ .section ".prefix", "ax", @progbits
+
+#define LOAD_ADDR 0x10000
+
+ /* ELF Header */
+ .globl elf_header
+elf_header:
+e_ident: .byte 0x7f, 'E', 'L', 'F', 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0
+e_type: .short ET_DYN
+e_machine: .short EM_X86_64
+e_version: .long 1
+e_entry: .long LOAD_ADDR + elf_start - elf_header
+e_phoff: .long elf_program_header - elf_header
+e_shoff: .long 0
+e_flags: .long 0
+e_ehsize: .short elf_header_end - elf_header
+e_phentsize: .short ELF32_PHDR_SIZE
+e_phnum: .short (elf_program_header_end - elf_program_header)/ELF32_PHDR_SIZE
+e_shentsize: .short 0
+e_shnum: .short 0
+e_shstrndx: .short 0
+elf_header_end:
+
+elf_program_header:
+phdr1_p_type: .long PT_NOTE
+phdr1_p_offset: .long elf_note - elf_header
+phdr1_p_vaddr: .long elf_note
+phdr1_p_paddr: .long elf_note
+phdr1_p_filesz: .long elf_note_end - elf_note
+phdr1_p_memsz: .long elf_note_end - elf_note
+phdr1_p_flags: .long PF_R | PF_W | PF_X
+phdr1_p_align: .long 0
+
+/* The decompressor */
+phdr2_p_type: .long PT_LOAD
+phdr2_p_offset: .long 0
+phdr2_p_vaddr: .long elf_header
+phdr2_p_paddr: .long LOAD_ADDR
+phdr2_p_filesz: .long _verbatim_size
+phdr2_p_memsz: .long _image_size
+phdr2_p_flags: .long PF_R | PF_W | PF_X
+phdr2_p_align: .long 16
+
+elf_program_header_end:
+
+ .globl elf_note
+elf_note:
+ .balign 4
+ .int 2f - 1f
+ .int 4f - 3f
+ .int EIN_PROGRAM_NAME
+1: .asciz "ELFBoot"
+2:
+ .balign 4
+3:
+ .asciz "Etherboot"
+4:
+
+
+ .balign 4
+ .int 2f - 1f
+ .int 4f - 3f
+ .int EIN_PROGRAM_VERSION
+1: .asciz "ELFBoot"
+2:
+ .balign 4
+3:
+ .asciz VERSION
+4:
+
+#if 0
+ .balign 4
+ .int 2f - 1f
+ .int 4f - 3f
+ .int EIN_PROGRAM_CHECKSUM
+1: .asciz "ELFBoot"
+2:
+ .balign 4
+3:
+ .word 0
+4:
+#endif
+ .balign 4
+elf_note_end:
+
+elf_start:
+ .code64
+ /* Reload the gdt to something I know */
+ leaq _gdt(%rip), %rax
+ movq %rax, 0x02 + gdtptr(%rip)
+ lgdt gdtptr(%rip)
+
+ /* Enter 32bit compatibility mode */
+ leaq elf_start32(%rip), %rax
+ movl %eax, 0x00 + elf_start32_addr(%rip)
+ ljmp *elf_start32_addr(%rip)
+
+elf_start32:
+ .code32
+ /* Reload the data segments */
+ movl $FLAT_DATA_SEG, %eax
+ movl %eax, %ds
+ movl %eax, %es
+ movl %eax, %ss
+
+ /* Disable paging */
+ movl %cr0, %eax
+ andl $~CR0_PG, %eax
+ movl %eax, %cr0
+
+ /* Disable long mode */
+ movl $MSR_K6_EFER, %ecx
+ rdmsr
+ andl $~EFER_LME, %eax
+ wrmsr
+
+ /* Disable PAE */
+ movl %cr4, %eax
+ andl $~X86_CR4_PAE, %eax
+ movl %eax, %cr4
+
+ /* Save the first argument */
+ pushl %ebx
+ jmp _start
+
+gdtptr:
+ .word _gdt_end - _gdt -1
+ .long _gdt
+ .long 0
+_gdt:
+elf_start32_addr:
+ .long elf_start32
+ .long FLAT_CODE_SEG
+_pmcs:
+ /* 32 bit protected mode code segment, base 0 */
+ .word 0xffff,0
+ .byte 0,0x9f,0xcf,0
+
+_pmds:
+ /* 32 bit protected mode data segment, base 0 */
+ .word 0xffff,0
+ .byte 0,0x93,0xcf,0
+_gdt_end:
+
+
+ /* Dummy routines to satisfy the build */
+ .section ".text16", "ax", @progbits
+ .globl prefix_exit
+prefix_exit:
+
+ .globl prefix_exit_end
+prefix_exit_end:
+ .previous
diff --git a/src/arch/i386/prefix/lmelf_prefix.S b/src/arch/i386/prefix/lmelf_prefix.S
new file mode 100644
index 00000000..7488f452
--- /dev/null
+++ b/src/arch/i386/prefix/lmelf_prefix.S
@@ -0,0 +1,163 @@
+#include "elf.h"
+ .arch sledgehammer
+ .code32
+ .equ FLAT_CODE_SEG,_pmcs-_gdt
+ .equ FLAT_DATA_SEG,_pmds-_gdt
+ .equ MSR_K6_EFER, 0xC0000080
+ .equ EFER_LME, 0x00000100
+ .equ X86_CR4_PAE, 0x00000020
+ .equ CR0_PG, 0x80000000
+
+ .section ".prefix", "ax", @progbits
+
+#define LOAD_ADDR 0x10000
+
+ /* ELF Header */
+ .globl elf_header
+elf_header:
+e_ident: .byte 0x7f, 'E', 'L', 'F', 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0
+e_type: .short ET_EXEC
+e_machine: .short EM_X86_64
+e_version: .long 1
+e_entry: .long LOAD_ADDR + elf_start - elf_header
+e_phoff: .long elf_program_header - elf_header
+e_shoff: .long 0
+e_flags: .long 0
+e_ehsize: .short elf_header_end - elf_header
+e_phentsize: .short ELF32_PHDR_SIZE
+e_phnum: .short (elf_program_header_end - elf_program_header)/ELF32_PHDR_SIZE
+e_shentsize: .short 0
+e_shnum: .short 0
+e_shstrndx: .short 0
+elf_header_end:
+
+elf_program_header:
+phdr1_p_type: .long PT_NOTE
+phdr1_p_offset: .long elf_note - elf_header
+phdr1_p_vaddr: .long elf_note
+phdr1_p_paddr: .long elf_note
+phdr1_p_filesz: .long elf_note_end - elf_note
+phdr1_p_memsz: .long elf_note_end - elf_note
+phdr1_p_flags: .long PF_R | PF_W | PF_X
+phdr1_p_align: .long 0
+
+/* The decompressor */
+phdr2_p_type: .long PT_LOAD
+phdr2_p_offset: .long 0
+phdr2_p_vaddr: .long elf_header
+phdr2_p_paddr: .long LOAD_ADDR
+phdr2_p_filesz: .long _verbatim_size
+phdr2_p_memsz: .long _image_size
+phdr2_p_flags: .long PF_R | PF_W | PF_X
+phdr2_p_align: .long 16
+
+elf_program_header_end:
+
+ .globl elf_note
+elf_note:
+ .balign 4
+ .int 2f - 1f
+ .int 4f - 3f
+ .int EIN_PROGRAM_NAME
+1: .asciz "ELFBoot"
+2:
+ .balign 4
+3:
+ .asciz "Etherboot"
+4:
+
+
+ .balign 4
+ .int 2f - 1f
+ .int 4f - 3f
+ .int EIN_PROGRAM_VERSION
+1: .asciz "ELFBoot"
+2:
+ .balign 4
+3:
+ .asciz VERSION
+4:
+
+#if 0
+ .balign 4
+ .int 2f - 1f
+ .int 4f - 3f
+ .int EIN_PROGRAM_CHECKSUM
+1: .asciz "ELFBoot"
+2:
+ .balign 4
+3:
+ .word 0
+4:
+#endif
+ .balign 4
+elf_note_end:
+
+elf_start:
+ .code64
+ /* Reload the gdt to something I know */
+ leaq _gdt(%rip), %rax
+ movq %rax, 0x02 + gdtptr(%rip)
+ lgdt gdtptr(%rip)
+
+ /* Enter 32bit compatibility mode */
+ leaq elf_start32(%rip), %rax
+ movl %eax, 0x00 + elf_start32_addr(%rip)
+ ljmp *elf_start32_addr(%rip)
+
+elf_start32:
+ .code32
+ /* Reload the data segments */
+ movl $FLAT_DATA_SEG, %eax
+ movl %eax, %ds
+ movl %eax, %es
+ movl %eax, %ss
+
+ /* Disable paging */
+ movl %cr0, %eax
+ andl $~CR0_PG, %eax
+ movl %eax, %cr0
+
+ /* Disable long mode */
+ movl $MSR_K6_EFER, %ecx
+ rdmsr
+ andl $~EFER_LME, %eax
+ wrmsr
+
+ /* Disable PAE */
+ movl %cr4, %eax
+ andl $~X86_CR4_PAE, %eax
+ movl %eax, %cr4
+
+ /* Save the first argument */
+ pushl %ebx
+ jmp _start
+
+gdtptr:
+ .word _gdt_end - _gdt -1
+ .long _gdt
+ .long 0
+_gdt:
+elf_start32_addr:
+ .long elf_start32
+ .long FLAT_CODE_SEG
+_pmcs:
+ /* 32 bit protected mode code segment, base 0 */
+ .word 0xffff,0
+ .byte 0,0x9f,0xcf,0
+
+_pmds:
+ /* 32 bit protected mode data segment, base 0 */
+ .word 0xffff,0
+ .byte 0,0x93,0xcf,0
+_gdt_end:
+
+
+ /* Dummy routines to satisfy the build */
+ .section ".text16", "ax", @progbits
+ .globl prefix_exit
+prefix_exit:
+
+ .globl prefix_exit_end
+prefix_exit_end:
+ .previous
diff --git a/src/arch/i386/prefix/nullprefix.S b/src/arch/i386/prefix/nullprefix.S
new file mode 100644
index 00000000..dbee141b
--- /dev/null
+++ b/src/arch/i386/prefix/nullprefix.S
@@ -0,0 +1,16 @@
+ .org 0
+ .text
+ .arch i386
+
+ .section ".prefix", "ax", @progbits
+ .code16
+ .globl _prefix
+_prefix:
+
+ .section ".text16", "ax", @progbits
+ .globl prefix_exit
+prefix_exit:
+
+ .globl prefix_exit_end
+prefix_exit_end:
+ .previous
diff --git a/src/arch/i386/prefix/pxeprefix.S b/src/arch/i386/prefix/pxeprefix.S
new file mode 100644
index 00000000..246e22b2
--- /dev/null
+++ b/src/arch/i386/prefix/pxeprefix.S
@@ -0,0 +1,398 @@
+/* Offsets of words containing ROM's CS and size (in 512 byte blocks)
+ * from start of floppy boot block at 0x7c00
+ * Offsets must match those in etherboot.h
+ */
+#define FLOPPY_SEGMENT 0x7c0
+
+#define PXENV_UNDI_CLEANUP 0x02
+#define PXENV_UNDI_SHUTDOWN 0x05
+#define PXENV_STOP_UNDI 0x15
+#define PXENV_UNLOAD_STACK 0x70
+#define PXENV_STOP_BASE 0x76
+
+#define PUSHA_SIZE 16
+#define PXE_STACK_MAGIC 0x57ac /* 'STac' */
+
+ .text
+ .code16
+ .arch i386
+ .org 0
+ .section ".prefix", "ax", @progbits
+ .globl _prefix
+/*****************************************************************************
+ * Entry point: set cs, ds, bp, print welcome message
+ *****************************************************************************
+ */
+_prefix:
+ jmp $FLOPPY_SEGMENT, $code_start-_prefix
+10: .asciz "PXE->EB "
+code_start:
+ pusha /* Preserve all registers */
+ push %ds
+ movw %sp, %bp /* %bp must be preserved, hence do
+ * this after the pusha */
+ push $PXE_STACK_MAGIC /* PXE stack magic marker */
+
+ push %cs /* Set up data segment */
+ pop %ds
+ mov $0x40, %cx /* Set up %fs for access to 40:13 */
+ mov %cx, %fs
+ movw $10b-_prefix, %si /* Print welcome message */
+ call print_message
+
+/*****************************************************************************
+ * Detect type of PXE available (!PXE, PXENV+ or none)
+ *****************************************************************************
+ */
+detect_pxe:
+ les 4+PUSHA_SIZE+2(%bp), %di /* !PXE structure */
+ cmpl $0x45585021, %es:(%di) /* '!PXE' signature */
+ je detected_pxe
+ mov $0x5650, %ax
+ int $0x1a
+ cmp $0x564e, %ax
+ jne detected_nothing
+ cmpl $0x4e455850, %es:(%bx) /* 'PXEN' signature */
+ jne detected_nothing
+ cmpw $0x2b56, %es:4(%bx) /* 'V+' signature */
+ je detected_pxenv
+
+detected_nothing:
+ movw $10f-_prefix, %si
+ call print_message
+ jmp finished_with_error
+10: .asciz "No PXE "
+
+detected_pxenv: /* es:bx points to PXENV+ structure */
+ push %es
+ push %bx
+ push %es:0x24(%bx) /* UNDI code segment */
+ push %es:0x26(%bx) /* UNDI code size */
+ push %es:0x20(%bx) /* UNDI data segment */
+ push %es:0x22(%bx) /* UNDI data size */
+ les %es:0x0a(%bx), %di /* Entry point to %es:%di */
+ movw $10f-_prefix, %si
+ jmp pxe_setup_done
+10: .asciz "PXENV+ "
+
+detected_pxe: /* es:di points to !PXE structure */
+ push %es
+ push %di
+ push %es:0x30(%di) /* UNDI code segment */
+ push %es:0x36(%di) /* UNDI code size */
+ push %es:0x28(%di) /* UNDI data segment */
+ push %es:0x2e(%di) /* UNDI data size */
+ les %es:0x10(%di), %di /* Entry point to %es:%di */
+ movw $10f-_prefix, %si
+ jmp pxe_setup_done
+10: .asciz "!PXE "
+
+pxe_setup_done:
+ mov %es, pxe_entry_segment - _prefix
+ mov %di, pxe_entry_offset - _prefix
+ pop %ax
+ mov %ax, undi_data_size - _prefix
+ pop %ax
+ mov %ax, undi_data_segment - _prefix
+ pop %ax
+ mov %ax, undi_code_size - _prefix
+ pop %ax
+ mov %ax, undi_code_segment - _prefix
+ call print_message
+ pop %di
+ pop %es /* Exit with %es:%di containing structure address */
+
+/*****************************************************************************
+ * Print information about located structure
+ *****************************************************************************
+ */
+print_structure_information:
+ call print_segoff /* %es:%di contains address of structure */
+ les %ds:(pxe_entry_segoff - _prefix), %di
+ call print_segoff
+ les %ds:(undi_code_segoff - _prefix), %di
+ call print_segoff
+ les %ds:(undi_data_segoff - _prefix), %di
+ call print_segoff
+
+/*****************************************************************************
+ * Unload PXE base code and UNDI driver
+ *****************************************************************************
+ */
+#ifdef PXELOADER_KEEP_ALL
+ xor %ax, %ax /* Force zero flag to show success */
+ jmp do_not_free_base_mem /* Skip the unloading */
+#endif /* PXELOADER_KEEP_ALL */
+
+unload_pxe:
+ mov $PXENV_UNLOAD_STACK, %bx
+ call pxe_call
+ mov $PXENV_STOP_UNDI, %bx
+ call pxe_call
+ pushfw /* Ignore PXENV_UNDI_CLEANUP errors */
+ mov $PXENV_UNDI_CLEANUP, %bx
+ call pxe_call
+ popfw
+ /* On exit, zero flag is set iff all calls were successful */
+
+/*****************************************************************************
+ * Free base memory
+ *****************************************************************************
+ */
+free_base_mem:
+ jnz do_not_free_base_mem /* Using zero flag from unload_pxe */
+
+ mov undi_code_segment - _prefix, %bx
+ mov undi_data_segment - _prefix, %cx
+ mov undi_code_size - _prefix, %ax
+ cmp %bx, %cx
+ jb 1f
+ mov %cx, %bx
+ mov undi_data_size - _prefix, %ax
+1: add $0x0f, %ax /* Round up to next segment */
+ shr $4, %ax
+ add %bx, %ax /* Highest segment address into %ax */
+ add $(1024 / 16 - 1), %ax /* Round up to next kb */
+ shr $6, %ax /* New free basemem size in %ax */
+ mov %fs:(0x13), %bx /* Old free base memory in %bx */
+ mov %ax, %fs:(0x13) /* Store new free base memory size */
+
+ /* Note that zero_mem_loop will also zero out our stack, so make
+ * sure the stack is empty at this point.
+ */
+ mov %ax, %dx
+ sub %bx, %dx /* numberof kb to zero in %dx */
+ shl $6, %bx /* Segment address into %bx */
+zero_mem_loop:
+ mov %bx, %es /* kB boundary into %es:00 */
+ xor %ax, %ax
+ xor %di, %di
+ mov $0x400, %cx
+ rep stosb /* fill kB with zeroes */
+ add $(1024 / 16), %bx
+ dec %dx
+ jnz zero_mem_loop
+ /* Will exit here with zero flag set, so no need to set it explicitly
+ * in order to indicate success.
+ */
+
+do_not_free_base_mem:
+ pushf /* Save success (zero) flag status */
+ mov %fs:(0x13), %ax /* Free base memory in %ax */
+ call print_hex_word /* Print free base memory */
+ popf /* Restore success (zero) flag */
+
+/*****************************************************************************
+ * Exit point
+ * Jump to finished with the zero flag set to indicate success, or to
+ * finished_with_error to always report an error
+ *****************************************************************************
+ */
+finished:
+ movw $10f-_prefix, %si
+ jz 1f
+finished_with_error:
+ movw $20f-_prefix, %si
+1:
+ call print_message
+ jmp 99f
+10: .asciz " ok\n"
+20: .asciz " err\n"
+
+
+ /* We place a stack here. It doesn't get used until after all
+ * the above code is finished, so we can happily write all
+ * over it. Putting the stack here ensures that it doesn't
+ * accidentally go over the 512 byte threshold, which would
+ * cause problems when returning via start32's prefix
+ * relocation mechanism.
+ */
+_estack:
+99:
+
+/*****************************************************************************
+ * Run Etherboot main code
+ *****************************************************************************
+ */
+run_etherboot:
+ /* Very temporarily switch stacks to one internal to the
+ * prefix. Do this because the stack provided by the PXE ROM
+ * could be absolutely anywhere, including in an area of
+ * memory that the call to prelocate will vapourise...
+ */
+ pushw %ss /* PXE stack pointer to ES:DI */
+ popw %es
+ movw %sp, %di
+ pushw %ds /* Set up stack in "safe" area */
+ popw %ss
+ movw $_estack-_prefix, %sp
+ pushw %es /* Record PXE stack pointer */
+ pushw %di
+ /* Relocate payload and stack to claimed base memory */
+ pushw $4 /* Preserve old PXE stack pointer */
+ call prelocate
+ popw %ax /* Remove parameter */
+ pushl $4 /* Preserve old PXE stack pointer */
+ pushw $0 /* Indicate prefix exit mechanism */
+ jmp _start /* Run Etherboot */
+
+ .section ".text16", "ax", @progbits
+ .globl prefix_exit
+prefix_exit:
+ pushw %cs /* Set %ds, %bp for access to text */
+ popw %ds
+ call 1f
+1: popw %bp
+ popw %di /* Old PXE stack to %es:di */
+ popw %es
+ cmpw $PXE_STACK_MAGIC, %es:0(%di) /* See if PXE stack intact */
+ jne exit_via_int18
+exit_via_pxe: /* Stack OK, return to PXE */
+ push %es /* Restore PXE stack pointer */
+ pop %ss
+ mov %di, %sp
+ pop %ax /* Discard PXE_STACK_MAGIC marker */
+ leaw (10f-1b)(%bp), %si
+ call print_exit_message
+ pop %ds /* Restore PXE's DS */
+ popa /* Restore PXE's other registers */
+ movw $0, %ax /* Return PXENV_STATUS_SUCCESS */
+ lret /* Return control to PXE ROM */
+10: .asciz "EB->PXE\r\n"
+exit_via_int18: /* Stack damaged, do int 18 */
+ leaw (10f-1b)(%bp), %si
+ call print_exit_message
+ int $0x18
+10: .asciz "EB->BIOS\r\n"
+print_exit_message:
+ movw $0x0007, %bx /* page 0, attribute 7 (normal) */
+ movb $0x0e, %ah /* write char, tty mode */
+1: lodsb
+ testb %al, %al
+ je 2f
+ int $0x10
+ jmp 1b
+2: ret
+ .globl prefix_exit_end
+prefix_exit_end:
+ .previous
+
+/*****************************************************************************
+ * Subroutine: print character in %al (with LF -> LF,CR translation)
+ *****************************************************************************
+ */
+print_character:
+ movw $0x0007, %bx /* page 0, attribute 7 (normal) */
+ movb $0x0e, %ah /* write char, tty mode */
+ cmpb $0x0a, %al /* '\n'? */
+ jne 1f
+ int $0x10
+ movb $0x0d, %al
+1: int $0x10
+ ret
+
+/*****************************************************************************
+ * Subroutine: print a zero-terminated message starting at %si
+ *****************************************************************************
+ */
+print_message:
+1: lodsb
+ testb %al, %al
+ je 2f
+ call print_character
+ jmp 1b
+2: ret
+
+/*****************************************************************************
+ * Subroutine: print hex word in %ax
+ *****************************************************************************
+ */
+print_hex_word:
+ mov $4, %cx
+1:
+ push %ax
+ shr $12, %ax
+ /* Courtesy of Norbert Juffa <norbert.juffa@amd.com> */
+ cmp $10, %al
+ sbb $0x69, %al
+ das
+ call print_character
+ pop %ax
+ shl $4, %ax
+ loop 1b
+ ret
+
+/*****************************************************************************
+ * Subroutine: print segment:offset address in %es:%di
+ *****************************************************************************
+ */
+print_segoff:
+ push %di
+ push %es
+ pop %ax
+ call print_hex_word
+ movb $0x3a,%al /* ':' */
+ call print_character
+ pop %ax
+ call print_hex_word
+ mov $0x20, %al /* ' ' */
+ call print_character
+ ret
+
+/*****************************************************************************
+ * Make a PXE API call. Works with either !PXE or PXENV+ API.
+ * Opcode in %bx. pxe_parameter_structure always used.
+ * Returns status code (not exit code) in %bx and prints it.
+ * ORs status code with overall status code in pxe_overall_status, returns
+ * with zero flag set iff all PXE API calls have been successful.
+ *****************************************************************************
+ */
+pxe_call:
+ /* Set up registers for PXENV+ API. %bx already set up */
+ push %ds
+ pop %es
+ mov $pxe_parameter_structure - _prefix, %di
+ /* Set up stack for !PXE API */
+ pushw %cs
+ pushw %di
+ pushw %bx
+ /* Make the API call */
+ lcall *(pxe_entry_segoff - _prefix)
+ /* Reset the stack */
+ add $6, %sp
+ mov pxe_parameter_structure - _prefix, %ax
+ push %ax
+ call print_hex_word
+ mov $0x20, %ax /* ' ' */
+ call print_character
+ pop %bx
+ or %bx, pxe_overall_status - _prefix
+ ret
+
+/*****************************************************************************
+ * PXE data structures
+ *****************************************************************************
+ */
+
+pxe_overall_status: .word 0
+
+pxe_entry_segoff:
+pxe_entry_offset: .word 0
+pxe_entry_segment: .word 0
+
+undi_code_segoff:
+undi_code_size: .word 0
+undi_code_segment: .word 0
+
+undi_data_segoff:
+undi_data_size: .word 0
+undi_data_segment: .word 0
+
+pxe_parameter_structure:
+ .word 0
+ .word 0,0,0,0,0
+
+end_of_pxeloader:
+
+ .balign 16, 0
+payload:
diff --git a/src/arch/i386/prefix/romprefix.S b/src/arch/i386/prefix/romprefix.S
new file mode 100644
index 00000000..c6c83489
--- /dev/null
+++ b/src/arch/i386/prefix/romprefix.S
@@ -0,0 +1,416 @@
+/* At entry, the processor is in 16 bit real mode and the code is being
+ * executed from an address it was not linked to. Code must be pic and
+ * 32 bit sensitive until things are fixed up.
+ *
+ * Also be very careful as the stack is at the rear end of the interrupt
+ * table so using a noticeable amount of stack space is a no-no.
+ */
+
+/* Define DELAYED_INT when NO_DELAYED_INT is not defined.
+ * This allows positive tests instead of tests that contain
+ * double negatives, and become confusing.
+ */
+#ifndef NO_DELAYED_INT
+#define DELAYED_INT
+#endif
+
+/* We need some unique magic ID, if we defer startup thru the INT18H or INT19H
+ * handler. This way, we can check if we have already been installed.
+ */
+#ifndef MAGIC
+#define MAGIC 0xE44C
+#endif
+
+/* Hook into INT18H or INT19H handler */
+#ifdef BOOT_INT18H
+#define BOOT_INT 0x18
+#else
+#define BOOT_INT 0x19
+#endif
+
+#define BOOT_INT_VEC BOOT_INT*4
+#define SCRATCHVEC 0x300
+
+/* Prefix exit codes. We store these on the stack so that we will
+ * know how to return control to the BIOS when Etherboot exits.
+ */
+#define EXIT_VIA_LRET 0x0
+#define EXIT_VIA_INT_18 0x1
+#define EXIT_VIA_BOOT_INT 0x2
+
+ .text
+ .code16
+ .arch i386
+ .org 0
+ .section ".prefix", "ax", @progbits
+ .globl _prefix
+_prefix:
+ .word 0xAA55 /* BIOS extension signature */
+size: .byte 0 /* number of 512 byte blocks */
+ /* = number of 256 word blocks */
+ /* filled in by makerom program */
+ jmp over /* skip over checksum */
+ .byte 0 /* checksum */
+ jmp legacyentry /* alternate entry point +6 */
+ /* used by mknbi-rom */
+
+#ifdef PCI_PNP_HEADER
+mfgstr:
+ .asciz "Etherboot"
+
+#ifdef PXE_EXPORT
+ .org 0x16
+ .word UNDIROMID - _prefix
+#endif /* PXE_EXPORT */
+
+ .org 0x18
+ .word PCI - _prefix
+ .word PnP - _prefix
+
+PCI:
+ .ascii "PCIR"
+ .word 0x0000 /* vendor ID, filled in by makerom */
+ .word 0x0000 /* device ID, filled in by makerom */
+ .word 0x0000 /* pointer to vital product data */
+ .word 0x0018 /* PCI data structure length */
+ .byte 0x00 /* PCI data structure revision */
+ .byte 0x02 /* Device Base Type code */
+ .byte 0x00 /* Device Sub-Type code */
+ .byte 0x00 /* Device Interface Type code */
+ .word 0x0000 /* Image length same as offset 02h */
+ .word 0x0001 /* revision level of code/data */
+ .byte 0x00 /* code type */
+ .byte 0x80 /* indicator (last PCI data structure) */
+ .word 0x0000 /* reserved */
+
+PnP:
+ .ascii "$PnP"
+ .byte 0x01 /* structure revision */
+ .byte 0x02 /* length (in 16 byte increments) */
+ .word 0x0000 /* offset of next header */
+ .byte 0x00 /* Reserved */
+ .byte 0x00 /* checksum filled by makerom */
+ .long 0x00000000 /* Device identifier */
+ .word mfgstr - _prefix
+ .word 0x0 /* pointer to product name */
+ /* filled by makerom */
+ .byte 0x02 /* Device Base Type code */
+ .byte 0x00 /* Device Sub-Type code */
+ .byte 0x00 /* Device Interface Type code */
+ .byte 0x14 /* device indicator */
+ .word 0x0000 /* boot connection vector */
+ .word 0x0000 /* disconnect vector */
+ .word pnpentry - _prefix
+ .word 0x0000 /* reserved */
+ .word 0x0000 /* static resource information vector */
+#ifdef PXE_EXPORT
+UNDIROMID:
+ .ascii "UNDI"
+ .byte UNDIROMID_end - UNDIROMID /* length of structure */
+ .byte 0 /* Checksum */
+ .byte 0 /* Structure revision */
+ .byte 0,1,2 /* PXE version 2.1.0 */
+ .word UNDILoader - _prefix /* Offset to loader routine */
+ .word UNDIStackSize /* Stack segment size */
+ .word UNDIDataSize /* Data segment size */
+ .word UNDICodeSize /* Code segment size */
+ .ascii "PCIR"
+
+ /* The code segment contains our pxe_stack_t plus the PXE and
+ * RM callback interfaces. We don't actually use a data
+ * segment, but we put a nonzero value here to avoid confusing
+ * things. 16k of stack space should be enough.
+ *
+ * When we claim our own memory, we fill out the data segment
+ * with the address and size of the real-mode stack, so that
+ * NBPs will free that area of memory for us. When the UNDI
+ * loader is used to initialise us, we will never need a
+ * real-mode stack because we will only ever be called via the
+ * PXE API, hence our stack is already in base memory.
+ */
+ .equ UNDICodeSize, _pxe_stack_size
+ .equ UNDIDataSize, _real_mode_stack_size
+ .equ UNDIStackSize, _real_mode_stack_size
+UNDIROMID_end:
+#endif /* PXE_EXPORT */
+
+#endif /* PCI_PNP_HEADER */
+
+/*
+ * Explicitly specify DI is wrt ES to avoid problems with some BIOSes
+ * Discovered by Eric Biederman
+ * In addition, some BIOSes don't point DI to the string $PnP so
+ * we need another #define to take care of that.
+ */
+over:
+#ifdef DEBUG_ROMPREFIX
+ call print_bcv
+#endif
+/* Omit this test for ISA cards anyway */
+#ifdef PCI_PNP_HEADER
+/* Accept old name too for backward compatibility */
+#if !defined(BBS_BUT_NOT_PNP_COMPLIANT) && !defined(PNP_BUT_NOT_BBS_COMPLIANT)
+ cmpw $'$'+'P'*256,%es:0(%di)
+ jne notpnp
+ cmpw $'n'+'P'*256,%es:2(%di)
+ jne notpnp
+#endif /* BBS_BUT_NOT_PNP_COMPLIANT */
+ movw $0x20,%ax
+ lret
+#endif /* PCI_PNP_HEADER */
+notpnp:
+#ifdef DEBUG_ROMPREFIX
+ call print_notpnp
+#endif
+#ifdef DELAYED_INT
+ pushw %ax
+ pushw %ds
+ xorw %ax,%ax
+ movw %ax,%ds /* access first 64kB segment */
+ movw SCRATCHVEC+4, %ax /* check if already installed */
+ cmpw $MAGIC, %ax /* check magic word */
+ jz installed
+ movw BOOT_INT_VEC, %ax /* hook into INT18H or INT19H */
+ movw %ax, SCRATCHVEC
+ movw BOOT_INT_VEC+2, %ax
+ movw %ax, SCRATCHVEC+2
+ movw $start_int - _prefix, %ax
+ movw %ax, BOOT_INT_VEC
+ movw %cs,%ax
+ movw %ax, BOOT_INT_VEC+2
+ movw $MAGIC, %ax /* set magic word */
+ movw %ax, SCRATCHVEC+4
+#ifdef DEBUG_ROMPREFIX
+ call print_installed
+#endif
+installed:
+ popw %ds
+ popw %ax
+ movw $0x20,%ax
+ lret
+
+start_int: /* clobber magic id, so that we will */
+#ifdef DEBUG_ROMPREFIX
+ call print_start_int
+#endif
+ xorw %ax,%ax /* not inadvertendly end up in an */
+ movw %ax,%ds /* endless loop */
+ movw %ax, SCRATCHVEC+4
+ movw SCRATCHVEC+2, %ax /* restore original INT19h handler */
+ movw %ax, BOOT_INT_VEC+2
+ movw SCRATCHVEC, %ax
+ movw %ax, BOOT_INT_VEC
+ pushl %eax /* padding */
+ pushw $EXIT_VIA_BOOT_INT
+ jmp invoke
+#endif /* DELAYED_INT */
+
+
+
+
+legacyentry:
+#ifdef DEBUG_ROMPREFIX
+ call print_legacyentry
+#endif
+ pushw $EXIT_VIA_LRET
+ jmp invoke
+
+
+
+#ifdef PCI_PNP_HEADER
+pnpentry:
+#ifdef DEBUG_ROMPREFIX
+ call print_bev
+#endif
+ pushl %eax /* padding */
+ pushw $EXIT_VIA_INT_18
+ jmp invoke
+#endif /* PCI_PNP_HEADER */
+
+
+invoke:
+ /* Store ROM segment and size on stack */
+ pushw %ax
+ pushw %ds
+ pushw %cs
+ movzbw %cs:(size-_prefix), %ax
+ shlw $9, %ax /* 512-byte blocks */
+ pushw %ax
+ /* Relocate to free base memory, switch stacks */
+ pushw $12 /* Preserve exit code & far ret addr */
+ call prelocate
+ /* We are now running in RAM */
+ popw %ax /* padding */
+ movw %cs, %ax
+ movw %ax, %ds
+ popw %ds:(_prefix_rom+2) /* ROM size */
+ popw %ds:(_prefix_rom+0) /* ROM segment */
+ popw %ds /* Original %ds */
+ popw %ax /* Original %ax */
+ pushw %ax /* 4-byte alignment */
+ pushl $8 /* Preserve exit code & far ret addr */
+ pushw $0 /* Set null return address */
+ jmp _start
+
+
+ .section ".text16", "ax", @progbits
+ .globl prefix_exit
+prefix_exit:
+ popw %ax /* padding */
+ popw %ax /* %ax = exit code */
+ cmpw $EXIT_VIA_LRET, %ax
+ jne 1f
+ /* Exit via LRET */
+ lret
+1: addw $4, %sp /* Strip padding */
+ cmpw $EXIT_VIA_BOOT_INT, %ax
+ jne 2f
+ /* Exit via int BOOT_INT */
+ int $BOOT_INT /* Try original vector */
+2: /* Exit via int $0x18 */
+ int $0x18 /* As per BIOS Boot Spec, next dev */
+ .globl prefix_exit_end
+prefix_exit_end:
+ .previous
+
+
+
+#ifdef PXE_EXPORT
+
+#include "callbacks.h"
+#define PXENV_UNDI_LOADER 0x104d
+
+ .section ".prefix"
+UNDILoader:
+ /* Loader API is different to the usual PXE API; there is no
+ * opcode on the stack. We arrange the stack to look like a
+ * normal PXE API call; this makes the Etherboot internals
+ * cleaner and avoids adding an extra API type just for the
+ * PXE loader.
+ */
+ pushw %bx
+ movw %sp, %ax /* Store original %ss:sp */
+ pushw %ss
+ pushw %ax
+ pushl %eax /* Space for loader structure ptr */
+ pushw %bp
+ movw %sp, %bp
+ movw 16(%bp), %ax /* Copy loader structure ptr */
+ movw %ax, 2(%bp)
+ movw 18(%bp), %ax
+ movw %ax, 4(%bp)
+ popw %bp
+ pushw $PXENV_UNDI_LOADER /* PXE 'opcode' */
+ pushl %eax /* dummy return address */
+ /* Stack now looks like a normal PXE API call */
+ /* Store ROM segment and size on stack */
+ pushw %ax
+ pushw %cs
+ movzbw %cs:(size-_prefix), %ax
+ shlw $9, %ax /* 512-byte blocks */
+ pushw %ax
+ /* Unpack Etherboot into temporarily claimed base memory */
+ pushw $20 /* Dummy ret, PXE params, orig ss:sp */
+ call prelocate
+ popw %ax /* discard */
+ popw %cs:(_prefix_rom+2) /* ROM size */
+ popw %cs:(_prefix_rom+0) /* ROM segment */
+ popw %ax /* Original %ax */
+ /* Inhibit automatic deallocation of base memory */
+ movl $0, %cs:_prefix_image_basemem
+ /* Make PXE API call to Etherboot */
+ pushl $0x201 /* PXE API version */
+ /* Need to USE_INTERNAL_STACK, since we will call relocate() */
+ pushl $(EB_OPCODE_PXE|EB_USE_INTERNAL_STACK) /* PXE API call type */
+ call _entry
+ addw $18, %sp /* discard */
+ popw %bx /* Restore original %ss:sp */
+ popw %ss
+ movw %bx, %sp
+ popw %bx
+ call deprelocate
+ lret $2 /* Skip our PXE 'opcode' */
+#endif /* PXE_EXPORT */
+
+#ifdef DEBUG_ROMPREFIX
+ .section ".prefix"
+
+print_bcv:
+ pushw %si
+ movw $1f-_prefix, %si
+ call print_message
+ popw %si
+ ret
+1: .asciz "ROM detected\r\n"
+
+print_bev:
+ pushw %si
+ movw $1f-_prefix, %si
+ call print_message
+ popw %si
+ ret
+1: .asciz "booting\r\n"
+
+print_notpnp:
+ pushw %si
+ movw $1f-_prefix, %si
+ call print_message
+ popw %si
+ ret
+1: .asciz ": Non-PnP BIOS detected!\r\n"
+
+print_legacyentry:
+ pushw %si
+ movw $1f-_prefix, %si
+ call print_message
+ popw %si
+ ret
+1: .asciz "ROM using legacy boot mechanism\r\n"
+
+print_installed:
+ pushw %si
+ movw $1f-_prefix, %si
+ call print_message
+ popw %si
+ ret
+1: .ascii "hooked boot via INT"
+#ifdef BOOT_INT18H
+ .asciz "18\r\n"
+#else
+ .asciz "19\r\n"
+#endif
+
+print_start_int:
+ pushw %si
+ movw $1f-_prefix, %si
+ call print_message
+ popw %si
+ ret
+1: .asciz "booting via hooked interrupt\r\n"
+
+print_message:
+ pushaw
+ pushw %ds
+ pushw %cs
+ popw %ds
+ pushw %si
+ movw $1f-_prefix, %si
+ call print_string
+ popw %si
+ call print_string
+ popw %ds
+ popaw
+ ret
+1: .asciz "Etherboot "
+
+print_string:
+1: lodsb
+ testb %al,%al
+ je 2f
+ movw $0x0007, %bx /* page 0, attribute 7 (normal) */
+ movb $0x0e, %ah /* write char, tty mode */
+ int $0x10
+ jmp 1b
+2: ret
+
+#endif
diff --git a/src/arch/i386/prefix/unhuf.S b/src/arch/i386/prefix/unhuf.S
new file mode 100644
index 00000000..390556e3
--- /dev/null
+++ b/src/arch/i386/prefix/unhuf.S
@@ -0,0 +1,400 @@
+/*****************************************************************************
+ * NOTE: This code is no longer used in Etherboot. The obsolete
+ * Makefile target .lzrom refers to it, but it is no longer being
+ * maintained and may no longer work. Use .zrom instead (which uses
+ * the unnrv2b decompressor).
+ *****************************************************************************
+ */
+
+/* At entry, the processor is in 16 bit real mode and the code is being
+ * executed from an address it was not linked to. Code must be pic and
+ * 32 bit sensitive until things are fixed up.
+ */
+
+
+/* LZHuf (LZSS) Decompressing boot loader for ROM images
+ *
+ * this code is based on the work of Haruyasu Yoshizaki and Haruhiko Okumura
+ * who implemented the original compressor and decompressor in C code
+ *
+ * Converted to 32bit assembly 16 July 2002 Eric Biederman <ebiederman@lnxi.com>
+ * Made PIC 10 Aug 2002 Eric Biederman <ebiederman@lnxi.com>
+ *
+ * Copyright 1997 by M. Gutschke <gutschk@math.uni-muenster.de>
+ *
+ * Compression pays off, as soon as the uncompressed image is bigger than
+ * about 1.5kB. This assumes an average compressibility of about 60%.
+ */
+
+
+/* Do not change these values unless you really know what you are doing
+ * the pre-computed lookup tables rely on the buffer size being 4kB or
+ * smaller. The buffer size must be a power of two. The lookahead size has
+ * to fit into 6 bits. If you change any of these numbers, you will also
+ * have to adjust the compressor accordingly.
+ */
+#define BUFSZ 4096
+#define LOOKAHEAD 60
+#define THRESHOLD 2
+#define NCHAR (256+LOOKAHEAD-THRESHOLD)
+#define TABLESZ (NCHAR+NCHAR-1)
+#define ROOT (TABLESZ-1)
+
+ .text
+ .arch i386
+ .globl _start
+_start:
+ cli
+
+ /* Save the initial register values */
+ pushal
+
+ /*
+ * See where I am running, and compute %ebp
+ */
+ call 1f
+1: pop %ebp
+ subl $1b, %ebp
+
+/*
+ * INIT -- initializes all data structures
+ * ====
+ */
+
+init:
+ cld
+ leal dcodrle(%ebp), %esi /* uncompress run length encoded */
+ leal dcode(%ebp), %edi /* lookup table for codes */
+ movb $6, %dl
+ movb $0x20, %dh
+ xorb %bh,%bh
+init0:
+ lodsb
+ movb %al,%bl
+init1:
+ xorl %ecx, %ecx
+ movb %dh,%cl
+ movb %bh,%al
+ rep
+ stosb
+ incb %bh
+ decb %bl
+ jnz init1
+ shrb %dh
+ decb %dl
+ jnz init0
+ movb $1, %bl /* uncompress run length encoded */
+ movb $6, %bh /* lookup table for code lengths */
+init2:
+ lodsb
+ xorl %ecx, %ecx
+ movb %al,%cl
+ movb %bl,%al
+ rep
+ stosb
+ incb %bl
+ decb %bh
+ jnz init2
+
+ movl $NCHAR, %ecx /* set all frequencies of leaf nodes */
+ movw $1, %ax /* to one */
+ rep
+ stosw
+ leal freq(%ebp), %esi
+ movl $ROOT+1-NCHAR, %ecx
+init3:
+ lodsw /* update frequencies of non-leaf nodes */
+ movw %ax,%bx
+ lodsw
+ addw %bx,%ax
+ stosw
+ loop init3
+ movw $0xFFFF, %ax
+ stosw /* sentinel with infinite frequency */
+ movl $NCHAR, %ecx
+ movw $TABLESZ, %ax
+init4:
+ stosw /* update son pointers for leaf nodes */
+ incw %ax
+ loop init4
+ movl $ROOT+1-NCHAR, %ecx
+ xorw %ax,%ax
+init5:
+ stosw /* update son ptrs for non-leaf nodes */
+ addw $2, %ax
+ loop init5
+ movl $ROOT+1-NCHAR, %ecx
+ movw $NCHAR, %ax
+init6:
+ stosw /* update parent ptrs for non-leaf nd. */
+ stosw
+ incw %ax
+ loop init6
+ movl $NCHAR, %ecx
+ xorw %ax,%ax
+ stosw /* root node has no parent */
+init7:
+ stosw /* update parent ptrs for leaf nodes */
+ incw %ax
+ loop init7
+ xorw %ax,%ax
+ stosb /* clear getlen */
+ stosw /* clear getbuf */
+ movb $0x20, %al /* fill text buffer with spaces */
+ leal spaces(%ebp), %edi
+ movl $BUFSZ-LOOKAHEAD, %ecx
+ rep
+
+ stosb
+ /* fall thru */
+
+/*
+ * MAIN -- reads compressed codes and writes decompressed data
+ * ====
+ */
+
+ leal _payload(%ebp), %esi /* get length of compressed data stream */
+ leal uncompressed(%ebp), %edi
+
+ lodsl
+ movl %eax, %ecx
+main1:
+ pushl %ecx
+ call dcdchr /* decode one code symbol */
+ orb %ah,%ah /* test if 8bit character */
+ jnz main2
+ stosb /* store verbatim */
+ popl %ecx
+ loop main1 /* proceed with next compressed code */
+ jmp done /* until end of input is detected */
+main2:
+ pushl %eax
+ call dcdpos /* compute position in output buffer */
+ movl %esi, %eax
+ subl %edi, %ebx
+ notl %ebx
+ movl %ebx, %esi /* si := di - dcdpos() - 1 */
+ popl %ecx
+ subl $255-THRESHOLD, %ecx /* compute length of code sequence */
+ movl %ecx, %edx
+ rep
+ movsb
+ movl %eax,%esi
+ popl %ecx
+ subl %edx, %ecx /* check end of input condition */
+ jnz main1 /* proceed with next compressed code */
+done:
+ /* Start Etherboot */
+ popal
+ jmp uncompressed
+/*
+ * GETBIT -- gets one bit pointed to by DS:ESI
+ * ======
+ *
+ * changes: AX,CX,DL
+ */
+
+getbit:
+ movb $8, %cl
+ movb getlen(%ebp), %dl /* compute number of bits required */
+ subb %dl,%cl /* to fill read buffer */
+ jae getbit1
+ movw getbuf(%ebp), %ax /* there is still enough read ahead data */
+ jmp getbit2
+getbit1:
+ lodsb /* get next byte from input stream */
+ xorb %ah,%ah
+ shlw %cl,%ax /* shift, so that it will fit into */
+ movw getbuf(%ebp), %cx /* read ahead buffer */
+ orw %cx,%ax
+ addb $8, %dl /* update number of bits in buffer */
+getbit2:
+ movw %ax,%cx
+ shlw %cx /* extract one bit from buffer */
+ movw %cx, getbuf(%ebp)
+ decb %dl
+ movb %dl, getlen(%ebp) /* and update number of bits */
+ shlw %ax /* return in carry flag */
+ ret
+
+
+/*
+ * DCDPOS -- decodes position in textbuffer as pointed to by DS:SI, result in BX
+ * ======
+ *
+ * changes: AX,EBX,ECX,DX
+ */
+
+dcdpos:
+ movl $0x0800, %ebx
+dcdpos1:
+ shlb %bl /* read one byte */
+ call getbit
+ jnc dcdpos2
+ incb %bl
+dcdpos2:
+ decb %bh
+ jnz dcdpos1
+ movb %bl,%dh /* read length of code from table */
+ xorb %bh,%bh
+ xorl %ecx, %ecx
+ movb dlen(%ebx, %ebp),%cl
+ movb dcode(%ebx, %ebp),%bl /* get top six bits from table */
+ shll $6, %ebx
+dcdpos3:
+ pushl %ecx /* read the rest from the input stream */
+ shlb %dh
+ call getbit
+ jnc dcdpos4
+ incb %dh
+dcdpos4:
+ popl %ecx
+ loop dcdpos3
+ andb $0x3f, %dh /* combine upper and lower half of code */
+ orb %dh,%bl
+ ret
+
+/*
+ * DCDCHR -- decodes one compressed character pointed to by DS:SI
+ * ======
+ *
+ * changes: AX,BX,CX,DX
+ */
+
+dcdchr:
+ movl $ROOT, %ebx /* start at root entry */
+ shll %ebx
+ movzwl son(%ebx, %ebp),%ebx
+dcdchr1:
+ call getbit /* get a single bit */
+ jnc dcdchr2
+ incl %ebx /* travel left or right */
+dcdchr2:
+ shll %ebx
+ movzwl son(%ebx, %ebp), %ebx
+ cmpl $TABLESZ, %ebx /* until we come to a leaf node */
+ jb dcdchr1
+ movl %ebx, %eax
+ subl $TABLESZ, %eax
+ /* fall thru */
+
+/*
+ * UPDATE -- updates huffman tree after incrementing frequency for code in BX
+ * ======
+ *
+ * changes: BX,CX,DX
+ */
+
+update:
+ /* we do not check whether the frequency count has overrun.
+ * this will cause problems for large files, but be should be fine
+ * as long as the compressed size does not exceed 32kB and we
+ * cannot do more than this anyways, because we load into the
+ * upper 32kB of conventional memory
+ */
+ pushl %esi
+ pushl %eax
+ shll %ebx
+ movzwl parent(%ebx, %ebp),%ebx
+update1:
+ shll %ebx
+ movzwl freq(%ebx, %ebp), %edx
+ incl %edx /* increment frequency count by one */
+ movw %dx, freq(%ebx, %ebp)
+ leal 2+freq(%ebx, %ebp), %esi
+ lodsw /* check if nodes need reordering */
+ cmpw %ax, %dx
+ jbe update5
+update2:
+ lodsw
+ cmpw %dx, %ax
+ jb update2
+ movzwl -4(%esi), %ecx
+ movw %cx, freq(%ebx, %ebp) /* swap frequency of entries */
+ movw %dx, -4(%esi)
+
+ movl %esi, %eax /* compute index of new entry */
+ subl $freq+4, %eax
+ subl %ebp, %eax
+
+ movl %eax, %edx
+ shrl %eax
+ movzwl son(%ebx, %ebp), %ecx /* get son of old entry */
+ movl %ecx, %esi
+ addl %esi, %esi
+ movw %ax, parent(%esi, %ebp) /* and update the ptr to new parent */
+ cmpl $TABLESZ, %ecx
+ jae update3 /* do this for both branches */
+ movw %ax, parent+2(%esi, %ebp) /* if not a leaf node */
+update3:
+ movl %edx, %esi
+ movzwl son(%esi, %ebp), %edx /* get son of new entry */
+ movw %cx, son(%esi, %ebp) /* update its contents */
+ movl %edx, %esi
+ addl %esi, %esi
+ movl %ebx, %ecx
+ shrl %ecx
+ movw %cx, parent(%esi, %ebp) /* and update the ptr to new paren */
+ cmpl $TABLESZ, %edx
+ jae update4 /* do this for both branches */
+ movw %cx, parent+2(%esi, %ebp) /* if not a leaf node */
+update4:
+ movw %dx, son(%ebx, %ebp) /* update son of old entry */
+ movl %eax, %ebx /* continue with new entry */
+ shll %ebx
+update5:
+ movzwl parent(%ebx, %ebp), %ebx /* continue with parent */
+ orl %ebx, %ebx
+ jnz update1 /* until we found the root entry */
+ popl %eax
+ popl %esi
+ ret
+
+/*
+ * constant data. this part of the program resides in ROM and cannot be
+ * changed
+ *
+ * run length encoded tables will be uncompressed into the bss segment
+ * take care with any symbols here for .com files to add 0x100 to address
+ */
+
+dcodrle: .byte 0x01,0x03,0x08,0x0C,0x18,0x10
+dlenrle: .byte 0x20,0x30,0x40,0x30,0x30,0x10
+
+/*
+ * variable data segment (bss)
+ * this segment will always be found at 0x90000 (i.e. at RELOC - SCRATCH)
+ *
+ * do not change the order or the sizes of any of the following tables
+ * the initialization code makes assumptions on the exact layout of the
+ * data structures...
+ */
+
+.bss
+/* lookup table for index into buffer of recently output characters */
+dcode: .skip 256
+
+/* lookup table for length of code sequence from buffer of recent characters */
+dlen: .skip 256
+
+/* table with frequency counts for all codes */
+freq: .skip 2*(TABLESZ+1)
+
+/* pointer to child nodes */
+son: .skip 2*(TABLESZ)
+
+/* the first part of this table contains all the codes (0..TABLESZ-1) */
+/* the second part contains all leaf nodes (TABLESZ..) */
+parent: .skip 2*(TABLESZ+NCHAR)
+
+/* temporary storage for extracting bits from compressed data stream */
+getlen: .skip 1
+getbuf: .skip 1
+
+ /* the initial buffer has to be filled with spaces */
+ .balign 4
+spaces:
+ .skip BUFSZ - LOOKAHEAD
+ /* uncompressed data will be written here */
+uncompressed:
+
diff --git a/src/arch/i386/prefix/unhuf.lds b/src/arch/i386/prefix/unhuf.lds
new file mode 100644
index 00000000..00d6b55b
--- /dev/null
+++ b/src/arch/i386/prefix/unhuf.lds
@@ -0,0 +1,33 @@
+OUTPUT_FORMAT("elf32-i386", "elf32-i386", "elf32-i386")
+OUTPUT_ARCH(i386)
+
+SECTIONS
+{
+ . = 0;
+ .text : {
+ _text = .;
+ *(.head)
+ *(.text)
+ } = 0x9090
+ .rodata : {
+ *(.rodata)
+ }
+ _etext = . ;
+ .data : {
+ *(.data)
+ /* Force 4 byte alignment */
+ . = ALIGN(4);
+ _payload = . ;
+ *(.huf)
+ _epayload = . ;
+ }
+ _edata = . ;
+ _data_size = _edata - _start;
+ /* Etherboot needs to be 16 byte aligned */
+ . = ALIGN(16);
+ .bss : {
+ *(.bss)
+ }
+ _end = . ;
+ _image_size = _end - _start;
+}
diff --git a/src/arch/i386/prefix/unnrv2b.S b/src/arch/i386/prefix/unnrv2b.S
new file mode 100644
index 00000000..1836fa71
--- /dev/null
+++ b/src/arch/i386/prefix/unnrv2b.S
@@ -0,0 +1,129 @@
+/*
+ * Copyright (C) 1996-2002 Markus Franz Xaver Johannes Oberhumer
+ *
+ * This file 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; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * Originally this code was part of ucl the data compression library
+ * for upx the ``Ultimate Packer of eXecutables''.
+ *
+ * - Converted to gas assembly, and refitted to work with etherboot.
+ * Eric Biederman 20 Aug 2002
+ *
+ * - Structure modified to be a subroutine call rather than an
+ * executable prefix.
+ * Michael Brown 30 Mar 2004
+ */
+
+
+ .text
+ .arch i386
+ .section ".prefix", "ax", @progbits
+ .code32
+
+ .globl decompress
+decompress:
+ /* Save the initial register values */
+ pushal
+
+ /*
+ * See where I am running, and compute %ebp
+ * %ebp holds delta between physical and virtual addresses.
+ */
+ call 1f
+1: popl %ebp
+ subl $1b, %ebp
+
+ /* "compressed" and "decompress_to" defined by linker script */
+ /* move compressed image up to temporary area before decompressing */
+ std
+ movl $_compressed_size, %ecx
+ leal _compressed+4-1(%ebp, %ecx), %esi
+ leal _compressed_copy-1(%ebp, %ecx), %edi
+ rep movsb
+ /* Setup to run the decompressor */
+ cld
+ leal _compressed_copy(%ebp), %esi
+ leal decompress_to(%ebp), %edi
+ movl $-1, %ebp /* last_m_off = -1 */
+ jmp dcl1_n2b
+
+/* ------------- DECOMPRESSION -------------
+
+ Input:
+ %esi - source
+ %edi - dest
+ %ebp - -1
+ cld
+
+ Output:
+ %eax - 0
+ %ecx - 0
+*/
+
+.macro getbit bits
+.if \bits == 1
+ addl %ebx, %ebx
+ jnz 1f
+.endif
+ movl (%esi), %ebx
+ subl $-4, %esi /* sets carry flag */
+ adcl %ebx, %ebx
+1:
+.endm
+
+decompr_literals_n2b:
+ movsb
+
+decompr_loop_n2b:
+ addl %ebx, %ebx
+ jnz dcl2_n2b
+dcl1_n2b:
+ getbit 32
+dcl2_n2b:
+ jc decompr_literals_n2b
+ xorl %eax, %eax
+ incl %eax /* m_off = 1 */
+loop1_n2b:
+ getbit 1
+ adcl %eax, %eax /* m_off = m_off*2 + getbit() */
+ getbit 1
+ jnc loop1_n2b /* while(!getbit()) */
+ xorl %ecx, %ecx
+ subl $3, %eax
+ jb decompr_ebpeax_n2b /* if (m_off == 2) goto decompr_ebpeax_n2b ? */
+ shll $8, %eax
+ movb (%esi), %al /* m_off = (m_off - 3)*256 + src[ilen++] */
+ incl %esi
+ xorl $-1, %eax
+ jz decompr_end_n2b /* if (m_off == 0xffffffff) goto decomp_end_n2b */
+ movl %eax, %ebp /* last_m_off = m_off ?*/
+decompr_ebpeax_n2b:
+ getbit 1
+ adcl %ecx, %ecx /* m_len = getbit() */
+ getbit 1
+ adcl %ecx, %ecx /* m_len = m_len*2 + getbit()) */
+ jnz decompr_got_mlen_n2b /* if (m_len == 0) goto decompr_got_mlen_n2b */
+ incl %ecx /* m_len++ */
+loop2_n2b:
+ getbit 1
+ adcl %ecx, %ecx /* m_len = m_len*2 + getbit() */
+ getbit 1
+ jnc loop2_n2b /* while(!getbit()) */
+ incl %ecx
+ incl %ecx /* m_len += 2 */
+decompr_got_mlen_n2b:
+ cmpl $-0xd00, %ebp
+ adcl $1, %ecx /* m_len = m_len + 1 + (last_m_off > 0xd00) */
+ pushl %esi
+ leal (%edi,%ebp), %esi /* m_pos = dst + olen + -m_off */
+ rep
+ movsb /* dst[olen++] = *m_pos++ while(m_len > 0) */
+ popl %esi
+ jmp decompr_loop_n2b
+decompr_end_n2b:
+ /* Restore the initial register values */
+ popal
+ ret