Initial revision

This commit is contained in:
Michael Brown
2005-03-08 18:53:11 +00:00
commit 3d6123e69a
373 changed files with 114041 additions and 0 deletions

131
src/arch/i386/Config Normal file
View 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
View 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

View 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;
}

View 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
View 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
View 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(&notes, 0, sizeof(notes));
notes.hdr.b_signature = ELF_BHDR_MAGIC;
notes.hdr.b_size = sizeof(notes);
notes.hdr.b_checksum = 0;
notes.hdr.b_records = ELF_NOTE_COUNT;
/* Initialize the fixed length entries. */
notes.nf1.n_namesz = sizeof(EB_PARAM_NOTE);
notes.nf1.n_descsz = sizeof(notes.nf1_bootp_data);
notes.nf1.n_type = EB_BOOTP_DATA;
CP(notes.nf1_name, EB_PARAM_NOTE);
notes.nf1_bootp_data = virt_to_phys(BOOTP_DATA_ADDR);
notes.nf2.n_namesz = sizeof(EB_PARAM_NOTE);
notes.nf2.n_descsz = sizeof(notes.nf2_header);
notes.nf2.n_type = EB_HEADER;
CP(notes.nf2_name, EB_PARAM_NOTE);
notes.nf2_header = virt_to_phys(header);
notes.nf3.n_namesz = sizeof(EB_PARAM_NOTE);
notes.nf3.n_descsz = sizeof(notes.nf3_meminfo);
notes.nf3.n_type = EB_I386_MEMMAP;
CP(notes.nf3_name, EB_PARAM_NOTE);
memcpy(&notes.nf3_meminfo, &meminfo, sizeof(meminfo));
/* Initialize the variable length entries */
notes.nv1.n_namesz = 0;
notes.nv1.n_descsz = sizeof(NAME);
notes.nv1.n_type = EBN_BOOTLOADER_NAME;
CP(notes.nv1_desc, NAME);
notes.nv2.n_namesz = 0;
notes.nv2.n_descsz = sizeof(VERSION);
notes.nv2.n_type = EBN_BOOTLOADER_VERSION;
CP(notes.nv2_desc, VERSION);
notes.nv3.n_namesz = 0;
notes.nv3.n_descsz = sizeof(FIRMWARE);
notes.nv3.n_type = EBN_FIRMWARE_TYPE;
CP(notes.nv3_desc, FIRMWARE);
/* Attempt to pass the name of the loaded image */
notes.nv4.n_namesz = 0;
notes.nv4.n_descsz = sizeof(notes.nv4_loaded_image);
notes.nv4.n_type = EBN_LOADED_IMAGE;
memcpy(&notes.nv4_loaded_image, KERNEL_BUF, sizeof(notes.nv4_loaded_image));
/* Pass an empty command line for now */
notes.nv5.n_namesz = 0;
notes.nv5.n_descsz = sizeof("");
notes.nv5.n_type = EBN_COMMAND_LINE;
CP(notes.nv5_cmdline, "");
notes.hdr.b_checksum = ipchksum(&notes, sizeof(notes));
/* Like UDP invert a 0 checksum to show that a checksum is present */
if (notes.hdr.b_checksum == 0) {
notes.hdr.b_checksum = 0xffff;
}
return &notes.hdr;
}
int elf_start(unsigned long machine __unused_i386, unsigned long entry, unsigned long params)
{
#if defined(CONFIG_X86_64)
if (machine == EM_X86_64) {
return xstart_lm(entry, params);
}
#endif
return xstart32(entry, params);
}

View 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 ;
}

View 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 ;
}

View 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

View 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
}

View 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
View 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 */

View 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
View 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 */

View 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

View File

@@ -0,0 +1,8 @@
OUTPUT_FORMAT("elf32-i386", "elf32-i386", "elf32-i386")
OUTPUT_ARCH(i386)
SECTIONS {
.prefix.udata : {
*(*)
}
}

View File

@@ -0,0 +1,8 @@
OUTPUT_FORMAT("elf32-i386", "elf32-i386", "elf32-i386")
OUTPUT_ARCH(i386)
SECTIONS {
.prefix.zdata : {
*(*)
}
}

View 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 */

View 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 );
}

View 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;
}

View 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 */

View 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:

View 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

View 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 );
}

View 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

View 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 );
}

View 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

View 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);
}

File diff suppressed because it is too large Load Diff

View 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

View 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 */

View 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 */

View 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 */

View 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 */

View 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 */

View 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 */

View 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 */

View 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 */

View 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 */

View 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;

View File

@@ -0,0 +1,3 @@
#ifndef ELTORITO_PLATFORM
#define ELTORITO_PLATFORM ELTORITO_PLATFORM_X86
#endif /* ELTORITO_PLATFORM */

View 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 */

View 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 */

View 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 */

View 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 */

View 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
View 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 */

View 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 */

View 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 */

View 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 */

View 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 */

View 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 */

View 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 */

View 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 */

View 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 */

View 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
View 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

View 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.
*/

View 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

View 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:

View 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

View 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

View 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:

View 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

View File

@@ -0,0 +1,6 @@
OUTPUT_FORMAT("elf32-i386", "elf32-i386", "elf32-i386")
OUTPUT_ARCH(i386)
SECTIONS {
.huf : { *(*) }
}

View File

@@ -0,0 +1,6 @@
OUTPUT_FORMAT("elf32-i386", "elf32-i386", "elf32-i386")
OUTPUT_ARCH(i386)
SECTIONS {
.img : { *(*) }
}

View 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.
*/

View 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

View 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

View 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

View 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:

View 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

View 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:

View 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;
}

View 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