mirror of
https://github.com/ipxe/ipxe
synced 2025-12-18 02:20:19 +03:00
Initial revision
This commit is contained in:
131
src/arch/i386/Config
Normal file
131
src/arch/i386/Config
Normal file
@@ -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
|
||||
373
src/arch/i386/Makefile
Normal file
373
src/arch/i386/Makefile
Normal file
@@ -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
|
||||
|
||||
144
src/arch/i386/core/aout_loader.c
Normal file
144
src/arch/i386/core/aout_loader.c
Normal file
@@ -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;
|
||||
}
|
||||
107
src/arch/i386/core/callbacks.c
Normal file
107
src/arch/i386/core/callbacks.c
Normal file
@@ -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 */
|
||||
86
src/arch/i386/core/cpu.c
Normal file
86
src/arch/i386/core/cpu.c
Normal file
@@ -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 */
|
||||
135
src/arch/i386/core/elf.c
Normal file
135
src/arch/i386/core/elf.c
Normal file
@@ -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(¬es, 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(¬es.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(¬es.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(¬es, 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 ¬es.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);
|
||||
}
|
||||
90
src/arch/i386/core/etherboot.lds
Normal file
90
src/arch/i386/core/etherboot.lds
Normal file
@@ -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 ;
|
||||
|
||||
}
|
||||
100
src/arch/i386/core/etherboot.prefix.lds
Normal file
100
src/arch/i386/core/etherboot.prefix.lds
Normal file
@@ -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 ;
|
||||
}
|
||||
377
src/arch/i386/core/freebsd_loader.c
Normal file
377
src/arch/i386/core/freebsd_loader.c
Normal file
@@ -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
|
||||
35
src/arch/i386/core/hooks.c
Normal file
35
src/arch/i386/core/hooks.c
Normal file
@@ -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
|
||||
}
|
||||
191
src/arch/i386/core/i386_timer.c
Normal file
191
src/arch/i386/core/i386_timer.c
Normal file
@@ -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 */
|
||||
305
src/arch/i386/core/init.S
Normal file
305
src/arch/i386/core/init.S
Normal file
@@ -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 */
|
||||
143
src/arch/i386/core/multiboot_loader.c
Normal file
143
src/arch/i386/core/multiboot_loader.c
Normal file
@@ -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);
|
||||
}
|
||||
352
src/arch/i386/core/pci_io.c
Normal file
352
src/arch/i386/core/pci_io.c
Normal file
@@ -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 */
|
||||
331
src/arch/i386/core/pic8259.c
Normal file
331
src/arch/i386/core/pic8259.c
Normal file
@@ -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
|
||||
8
src/arch/i386/core/prefixudata.lds
Normal file
8
src/arch/i386/core/prefixudata.lds
Normal file
@@ -0,0 +1,8 @@
|
||||
OUTPUT_FORMAT("elf32-i386", "elf32-i386", "elf32-i386")
|
||||
OUTPUT_ARCH(i386)
|
||||
|
||||
SECTIONS {
|
||||
.prefix.udata : {
|
||||
*(*)
|
||||
}
|
||||
}
|
||||
8
src/arch/i386/core/prefixzdata.lds
Normal file
8
src/arch/i386/core/prefixzdata.lds
Normal file
@@ -0,0 +1,8 @@
|
||||
OUTPUT_FORMAT("elf32-i386", "elf32-i386", "elf32-i386")
|
||||
OUTPUT_ARCH(i386)
|
||||
|
||||
SECTIONS {
|
||||
.prefix.zdata : {
|
||||
*(*)
|
||||
}
|
||||
}
|
||||
364
src/arch/i386/core/pxe_callbacks.c
Normal file
364
src/arch/i386/core/pxe_callbacks.c
Normal file
@@ -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 */
|
||||
94
src/arch/i386/core/pxe_loader.c
Normal file
94
src/arch/i386/core/pxe_loader.c
Normal file
@@ -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 );
|
||||
}
|
||||
148
src/arch/i386/core/realmode.c
Normal file
148
src/arch/i386/core/realmode.c
Normal file
@@ -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;
|
||||
}
|
||||
695
src/arch/i386/core/realmode_asm.S
Normal file
695
src/arch/i386/core/realmode_asm.S
Normal file
@@ -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 */
|
||||
285
src/arch/i386/core/start16.S
Normal file
285
src/arch/i386/core/start16.S
Normal file
@@ -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:
|
||||
8
src/arch/i386/core/start16.lds
Normal file
8
src/arch/i386/core/start16.lds
Normal file
@@ -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
|
||||
65
src/arch/i386/core/start16z.lds
Normal file
65
src/arch/i386/core/start16z.lds
Normal file
@@ -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 );
|
||||
}
|
||||
|
||||
767
src/arch/i386/core/start32.S
Normal file
767
src/arch/i386/core/start32.S
Normal file
@@ -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
|
||||
201
src/arch/i386/core/tagged_loader.c
Normal file
201
src/arch/i386/core/tagged_loader.c
Normal file
@@ -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 );
|
||||
}
|
||||
94
src/arch/i386/core/video_subr.c
Normal file
94
src/arch/i386/core/video_subr.c
Normal file
@@ -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
|
||||
|
||||
273
src/arch/i386/core/wince_loader.c
Normal file
273
src/arch/i386/core/wince_loader.c
Normal file
@@ -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);
|
||||
}
|
||||
1458
src/arch/i386/drivers/net/undi.c
Normal file
1458
src/arch/i386/drivers/net/undi.c
Normal file
File diff suppressed because it is too large
Load Diff
178
src/arch/i386/drivers/net/undi.h
Normal file
178
src/arch/i386/drivers/net/undi.h
Normal file
@@ -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
|
||||
317
src/arch/i386/firmware/pcbios/basemem.c
Normal file
317
src/arch/i386/firmware/pcbios/basemem.c
Normal file
@@ -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 */
|
||||
155
src/arch/i386/firmware/pcbios/bios.c
Normal file
155
src/arch/i386/firmware/pcbios/bios.c
Normal file
@@ -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 */
|
||||
85
src/arch/i386/firmware/pcbios/console.c
Normal file
85
src/arch/i386/firmware/pcbios/console.c
Normal file
@@ -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 */
|
||||
296
src/arch/i386/firmware/pcbios/e820mangler.S
Normal file
296
src/arch/i386/firmware/pcbios/e820mangler.S
Normal file
@@ -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 */
|
||||
94
src/arch/i386/firmware/pcbios/hidemem.c
Normal file
94
src/arch/i386/firmware/pcbios/hidemem.c
Normal file
@@ -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 */
|
||||
201
src/arch/i386/firmware/pcbios/memsizes.c
Normal file
201
src/arch/i386/firmware/pcbios/memsizes.c
Normal file
@@ -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 */
|
||||
45
src/arch/i386/include/bits/byteswap.h
Normal file
45
src/arch/i386/include/bits/byteswap.h
Normal file
@@ -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 */
|
||||
243
src/arch/i386/include/bits/cpu.h
Normal file
243
src/arch/i386/include/bits/cpu.h
Normal file
@@ -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 */
|
||||
91
src/arch/i386/include/bits/elf.h
Normal file
91
src/arch/i386/include/bits/elf.h
Normal file
@@ -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 */
|
||||
5
src/arch/i386/include/bits/elf_x.h
Normal file
5
src/arch/i386/include/bits/elf_x.h
Normal file
@@ -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;
|
||||
3
src/arch/i386/include/bits/eltorito.h
Normal file
3
src/arch/i386/include/bits/eltorito.h
Normal file
@@ -0,0 +1,3 @@
|
||||
#ifndef ELTORITO_PLATFORM
|
||||
#define ELTORITO_PLATFORM ELTORITO_PLATFORM_X86
|
||||
#endif /* ELTORITO_PLATFORM */
|
||||
9
src/arch/i386/include/bits/endian.h
Normal file
9
src/arch/i386/include/bits/endian.h
Normal file
@@ -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 */
|
||||
99
src/arch/i386/include/bits/string.h
Normal file
99
src/arch/i386/include/bits/string.h
Normal file
@@ -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 */
|
||||
243
src/arch/i386/include/callbacks_arch.h
Normal file
243
src/arch/i386/include/callbacks_arch.h
Normal file
@@ -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 */
|
||||
21
src/arch/i386/include/hidemem.h
Normal file
21
src/arch/i386/include/hidemem.h
Normal file
@@ -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 */
|
||||
9
src/arch/i386/include/hooks.h
Normal file
9
src/arch/i386/include/hooks.h
Normal file
@@ -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 */
|
||||
246
src/arch/i386/include/io.h
Normal file
246
src/arch/i386/include/io.h
Normal file
@@ -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 */
|
||||
10
src/arch/i386/include/latch.h
Normal file
10
src/arch/i386/include/latch.h
Normal file
@@ -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 */
|
||||
59
src/arch/i386/include/limits.h
Normal file
59
src/arch/i386/include/limits.h
Normal file
@@ -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 */
|
||||
96
src/arch/i386/include/pic8259.h
Normal file
96
src/arch/i386/include/pic8259.h
Normal file
@@ -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 */
|
||||
33
src/arch/i386/include/pxe_callbacks.h
Normal file
33
src/arch/i386/include/pxe_callbacks.h
Normal file
@@ -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 */
|
||||
35
src/arch/i386/include/pxe_types.h
Normal file
35
src/arch/i386/include/pxe_types.h
Normal file
@@ -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 */
|
||||
124
src/arch/i386/include/realmode.h
Normal file
124
src/arch/i386/include/realmode.h
Normal file
@@ -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 */
|
||||
41
src/arch/i386/include/segoff.h
Normal file
41
src/arch/i386/include/segoff.h
Normal file
@@ -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 */
|
||||
12
src/arch/i386/include/setjmp.h
Normal file
12
src/arch/i386/include/setjmp.h
Normal file
@@ -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 */
|
||||
16
src/arch/i386/include/stdint.h
Normal file
16
src/arch/i386/include/stdint.h
Normal file
@@ -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 */
|
||||
228
src/arch/i386/include/vga.h
Normal file
228
src/arch/i386/include/vga.h
Normal file
@@ -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
|
||||
614
src/arch/i386/prefix/bImageprefix.S
Normal file
614
src/arch/i386/prefix/bImageprefix.S
Normal file
@@ -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.
|
||||
*/
|
||||
410
src/arch/i386/prefix/boot1a.s
Normal file
410
src/arch/i386/prefix/boot1a.s
Normal file
@@ -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
|
||||
49
src/arch/i386/prefix/comprefix.S
Normal file
49
src/arch/i386/prefix/comprefix.S
Normal file
@@ -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:
|
||||
96
src/arch/i386/prefix/elf_dprefix.S
Normal file
96
src/arch/i386/prefix/elf_dprefix.S
Normal file
@@ -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
|
||||
96
src/arch/i386/prefix/elfprefix.S
Normal file
96
src/arch/i386/prefix/elfprefix.S
Normal file
@@ -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
|
||||
44
src/arch/i386/prefix/exeprefix.S
Executable file
44
src/arch/i386/prefix/exeprefix.S
Executable file
@@ -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:
|
||||
359
src/arch/i386/prefix/floppyprefix.S
Normal file
359
src/arch/i386/prefix/floppyprefix.S
Normal file
@@ -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
|
||||
|
||||
6
src/arch/i386/prefix/huf.lds
Normal file
6
src/arch/i386/prefix/huf.lds
Normal file
@@ -0,0 +1,6 @@
|
||||
OUTPUT_FORMAT("elf32-i386", "elf32-i386", "elf32-i386")
|
||||
OUTPUT_ARCH(i386)
|
||||
|
||||
SECTIONS {
|
||||
.huf : { *(*) }
|
||||
}
|
||||
6
src/arch/i386/prefix/img.lds
Normal file
6
src/arch/i386/prefix/img.lds
Normal file
@@ -0,0 +1,6 @@
|
||||
OUTPUT_FORMAT("elf32-i386", "elf32-i386", "elf32-i386")
|
||||
OUTPUT_ARCH(i386)
|
||||
|
||||
SECTIONS {
|
||||
.img : { *(*) }
|
||||
}
|
||||
144
src/arch/i386/prefix/liloprefix.S
Normal file
144
src/arch/i386/prefix/liloprefix.S
Normal file
@@ -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.
|
||||
*/
|
||||
|
||||
163
src/arch/i386/prefix/lmelf_dprefix.S
Normal file
163
src/arch/i386/prefix/lmelf_dprefix.S
Normal file
@@ -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
|
||||
163
src/arch/i386/prefix/lmelf_prefix.S
Normal file
163
src/arch/i386/prefix/lmelf_prefix.S
Normal file
@@ -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
|
||||
16
src/arch/i386/prefix/nullprefix.S
Normal file
16
src/arch/i386/prefix/nullprefix.S
Normal file
@@ -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
|
||||
398
src/arch/i386/prefix/pxeprefix.S
Normal file
398
src/arch/i386/prefix/pxeprefix.S
Normal file
@@ -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:
|
||||
416
src/arch/i386/prefix/romprefix.S
Normal file
416
src/arch/i386/prefix/romprefix.S
Normal file
@@ -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
|
||||
400
src/arch/i386/prefix/unhuf.S
Normal file
400
src/arch/i386/prefix/unhuf.S
Normal file
@@ -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:
|
||||
|
||||
33
src/arch/i386/prefix/unhuf.lds
Normal file
33
src/arch/i386/prefix/unhuf.lds
Normal file
@@ -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;
|
||||
}
|
||||
129
src/arch/i386/prefix/unnrv2b.S
Normal file
129
src/arch/i386/prefix/unnrv2b.S
Normal file
@@ -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
|
||||
Reference in New Issue
Block a user