summaryrefslogtreecommitdiffstats
path: root/src/arch
diff options
context:
space:
mode:
authorMichael Brown2005-04-08 17:01:17 +0200
committerMichael Brown2005-04-08 17:01:17 +0200
commit0ff80b477dcff0726ebdbed95e8a93971e59e82b (patch)
tree860b7150212a07c24a9529ea072f3fb12700974c /src/arch
parentMerged this file into HEAD (diff)
downloadipxe-0ff80b477dcff0726ebdbed95e8a93971e59e82b.tar.gz
ipxe-0ff80b477dcff0726ebdbed95e8a93971e59e82b.tar.xz
ipxe-0ff80b477dcff0726ebdbed95e8a93971e59e82b.zip
Merged mcb30-realmode-redesign back to HEAD
Diffstat (limited to 'src/arch')
-rw-r--r--src/arch/armnommu/core/arm_timer.c6
-rw-r--r--src/arch/e1/core/e1_timer.c5
-rw-r--r--src/arch/i386/Config5
-rw-r--r--src/arch/i386/Makefile493
-rw-r--r--src/arch/i386/README.i386197
-rw-r--r--src/arch/i386/core/callbacks.c1
-rw-r--r--src/arch/i386/core/cpu.c4
-rw-r--r--src/arch/i386/core/elf.c1
-rw-r--r--src/arch/i386/core/hooks.c108
-rw-r--r--src/arch/i386/core/i386_timer.c7
-rw-r--r--src/arch/i386/core/init.S305
-rw-r--r--src/arch/i386/core/pxe_callbacks.c27
-rw-r--r--src/arch/i386/core/realmode.c150
-rw-r--r--src/arch/i386/core/realmode_asm.S564
-rw-r--r--src/arch/i386/core/setjmp.S40
-rw-r--r--src/arch/i386/core/setup.S158
-rw-r--r--src/arch/i386/core/start16.S285
-rw-r--r--src/arch/i386/core/start32.S453
-rw-r--r--src/arch/i386/core/tagged_loader.c1
-rw-r--r--src/arch/i386/core/video_subr.c22
-rw-r--r--src/arch/i386/core/virtaddr.S317
-rw-r--r--src/arch/i386/drivers/net/undi.c2
-rw-r--r--src/arch/i386/firmware/pcbios/bios.c178
-rw-r--r--src/arch/i386/firmware/pcbios/bios_console.c76
-rw-r--r--src/arch/i386/firmware/pcbios/console.c85
-rw-r--r--src/arch/i386/firmware/pcbios/gateA20.c90
-rw-r--r--src/arch/i386/firmware/pcbios/memsizes.c265
-rw-r--r--src/arch/i386/include/bios.h10
-rw-r--r--src/arch/i386/include/bochs.h27
-rw-r--r--src/arch/i386/include/hidemem.h2
-rw-r--r--src/arch/i386/include/hooks.h19
-rw-r--r--src/arch/i386/include/io.h18
-rw-r--r--src/arch/i386/include/kir.h18
-rw-r--r--src/arch/i386/include/libkir.h184
-rw-r--r--src/arch/i386/include/librm.h186
-rw-r--r--src/arch/i386/include/memsizes.h34
-rw-r--r--src/arch/i386/include/pic8259.h4
-rw-r--r--src/arch/i386/include/pxe_callbacks.h1
-rw-r--r--src/arch/i386/include/pxe_types.h2
-rw-r--r--src/arch/i386/include/realmode.h218
-rw-r--r--src/arch/i386/include/registers.h93
-rw-r--r--src/arch/i386/include/segoff.h41
-rw-r--r--src/arch/i386/include/virtaddr.h85
-rw-r--r--src/arch/i386/kir-Makefile29
-rw-r--r--src/arch/i386/prefix/dskprefix.S (renamed from src/arch/i386/prefix/floppyprefix.S)62
-rw-r--r--src/arch/i386/prefix/int19exit.c13
-rw-r--r--src/arch/i386/prefix/unnrv2b.S198
-rw-r--r--src/arch/i386/scripts/i386.lds263
-rw-r--r--src/arch/i386/transitions/libkir.S243
-rw-r--r--src/arch/i386/transitions/libpm.S0
-rw-r--r--src/arch/i386/transitions/librm.S691
-rw-r--r--src/arch/i386/transitions/librm_mgmt.c87
-rw-r--r--src/arch/ia64/core/ia64_timer.c5
53 files changed, 3652 insertions, 2726 deletions
diff --git a/src/arch/armnommu/core/arm_timer.c b/src/arch/armnommu/core/arm_timer.c
index b858ca8d..fef20e7b 100644
--- a/src/arch/armnommu/core/arm_timer.c
+++ b/src/arch/armnommu/core/arm_timer.c
@@ -9,7 +9,7 @@
#include "timer.h"
#include "latch.h"
#include "hardware.h"
-
+#include "init.h"
/* get timer returns the contents of the timer */
static unsigned long get_timer(void)
@@ -29,7 +29,7 @@ static unsigned long configure_timer(void)
static unsigned long clocks_per_tick = 1;
-void setup_timers(void)
+static void setup_timers(void)
{
if (!clocks_per_tick) {
clocks_per_tick = configure_timer();
@@ -75,3 +75,5 @@ int timer2_running(void)
{
return __timer_running();
}
+
+INIT_FN ( INIT_TIMERS, setup_timers, NULL, NULL );
diff --git a/src/arch/e1/core/e1_timer.c b/src/arch/e1/core/e1_timer.c
index d3ddb644..4ed9a0fb 100644
--- a/src/arch/e1/core/e1_timer.c
+++ b/src/arch/e1/core/e1_timer.c
@@ -7,6 +7,7 @@
#include "etherboot.h"
#include "timer.h"
#include "e132_xs_board.h"
+#include "init.h"
/* get timer returns the contents of the timer */
static inline unsigned long get_timer(void)
@@ -46,7 +47,7 @@ static unsigned long configure_timer(void)
static unsigned long clocks_per_tick;
-void setup_timers(void)
+static void setup_timers(void)
{
if (!clocks_per_tick) {
clocks_per_tick = configure_timer();
@@ -92,3 +93,5 @@ int timer2_running(void)
{
return __timer_running();
}
+
+INIT_FN ( INIT_TIMERS, setup_timers, NULL, NULL );
diff --git a/src/arch/i386/Config b/src/arch/i386/Config
index e8ac91b8..a2126527 100644
--- a/src/arch/i386/Config
+++ b/src/arch/i386/Config
@@ -129,3 +129,8 @@ endif
# An alternate location for isolinux.bin can be set here
# ISOLINUX_BIN=/path/to/isolinux.bin
+
+# These seem to have some relevance to compiling on x86_64
+# EXTRA_CFLAGS=-m32
+# EXTRA_ASFLAGS=--32
+# EXTRA_LDFLAGS=-m elf_i386
diff --git a/src/arch/i386/Makefile b/src/arch/i386/Makefile
index 0a80ff94..a4139e74 100644
--- a/src/arch/i386/Makefile
+++ b/src/arch/i386/Makefile
@@ -1,375 +1,130 @@
-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/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 -DASSEMBLY $< | $(AS) $(ASFLAGS) -o $@
-
-$(BIN)/%.o: arch/i386/prefix/%.S $(MAKEDEPS)
- $(CPP) $(CFLAGS) -Ui386 -DASSEMBLY $< | $(AS) $(ASFLAGS) -o $@
-
-$(BIN)/%16.o: arch/i386/prefix/%.S $(MAKEDEPS)
- $(CPP) $(CFLAGS) -Ui386 -DASSEMBLY -DCODE16 $< | $(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
+# i386-specific directories containing source files
+#
+SRCDIRS += arch/i386/core arch/i386/transitions arch/i386/prefix
+SRCDIRS += arch/i386/firmware/pcbios arch/i386/firmware/linuxbios
+SRCDIRS += arch/i386/drivers/net
+
+# The various xxx_loader.c files are #included into core/loader.c and
+# should not be compiled directly.
+#
+NON_AUTO_SRCS += arch/i386/core/aout_loader.c
+NON_AUTO_SRCS += arch/i386/core/freebsd_loader.c
+NON_AUTO_SRCS += arch/i386/core/multiboot_loader.c
+NON_AUTO_SRCS += arch/i386/core/tagged_loader.c
+NON_AUTO_SRCS += arch/i386/core/wince_loader.c
+
+# setup.S and unnrv2b.S are both used to generate 16-bit as well as
+# 32-bit objects.
+#
+OBJS_setup = setup setup16
+CFLAGS_setup16 = -DCODE16
+OBJS_unnrv2b = unnrv2b unnrv2b16
+CFLAGS_unnrv2b16 = -DCODE16
+
+# hooks.c is used to generate hooks.o and hooks_rm.o
+#
+OBJS_hooks = hooks hooks_rm
+CFLAGS_hooks_rm = -DREALMODE
+
+# We need to undefine the default macro "i386" when compiling .S
+# files, otherwise ".arch i386" translates to ".arch 1"...
+#
+CFLAGS_S += -Ui386
+
+# The i386 linker script
+#
+LDSCRIPT = arch/i386/scripts/i386.lds
+
+# Media types.
+#
+# It's ugly that we have to define these repetitive combinations by
+# hand. Unforunately, $(eval ...) is available only in make >= 3.80,
+# and using an external Makefile fragment doesn't work because
+# OBJS_xxx need to be defined *before* the external Makefile fragments
+# for the source files are generated...
+
+CFLAGS_ZPREFIX = -DCOMPRESS
+
+MEDIA += rom
+OBJS_romprefix = isaprefix zisaprefix pciprefix zpciprefix
+CFLAGS_isaprefix =
+CFLAGS_zisaprefix = $(CFLAGS_ZPREFIX)
+CFLAGS_pciprefix = -DPCI_PNP_HEADER
+CFLAGS_zpciprefix = $(CFLAGS_pciprefix) $(CFLAGS_ZPREFIX)
+
+MEDIA += pxe
+OBJS_pxeprefix = pxeprefix zpxeprefix
+CFLAGS_zpxeprefix = $(CFLAGS_ZPREFIX)
+
+MEDIA += elf
+OBJS_elfprefix = elfprefix zelfprefix
+CFLAGS_zelfprefix = $(CFLAGS_ZPREFIX)
+
+MEDIA += elfd
+OBJS_elfdprefix = elfdprefix zelfdprefix
+CFLAGS_zelfdprefix = $(CFLAGS_ZPREFIX)
+
+MEDIA += lmelf
+OBJS_lmelfprefix = lmelfprefix zlmelfprefix
+CFLAGS_zlmelfprefix = $(CFLAGS_ZPREFIX)
+
+MEDIA += lmelfd
+OBJS_lmelfdprefix = lmelfdprefix zlmelfdprefix
+CFLAGS_zlmelfdprefix = $(CFLAGS_ZPREFIX)
+
+MEDIA += lilo
+OBJS_liloprefix = liloprefix zliloprefix
+CFLAGS_zliloprefix = $(CFLAGS_ZPREFIX)
+
+MEDIA += bImage
+OBJS_bImageprefix = bImageprefix zbImageprefix
+CFLAGS_zbImageprefix = $(CFLAGS_ZPREFIX)
+
+MEDIA += dsk
+OBJS_dskprefix = dskprefix zdskprefix
+CFLAGS_zdskprefix = $(CFLAGS_ZPREFIX)
+
+MEDIA += raw
+OBJS_rawprefix = rawprefix zrawprefix
+CFLAGS_zrawprefix = $(CFLAGS_ZPREFIX)
+
+# These media cannot handle compressed payloads
+
+MEDIA += com
+
+MEDIA += exe
+
+# Some suffixes (e.g. %.zfd0) are generated directly from other
+# finished files (e.g. %.zdsk), rather than having their own prefix.
+
+# rule to write disk images to /dev/fd0
+NON_AUTO_MEDIA += fd0
+%fd0 : %dsk
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
+NON_AUTO_MEDIA += iso
+%iso: %lilo util/geniso
+ ISOLINUX_BIN=$(ISOLINUX_BIN) bash util/geniso $@ $<
# rule to make a floppy emulation ISO boot image
-SUFFIXES += liso
-%.liso: util/genliso %.zlilo
- bash util/genliso $*.liso $*.zlilo
-
+NON_AUTO_MEDIA += liso
+%liso: %lilo util/genliso
+ bash util/genliso $@ $<
+
+# Add NON_AUTO_MEDIA to the media list, so that they show up in the
+# output of "make"
+#
+MEDIA += $(NON_AUTO_MEDIA)
+
+# Shortcut to allow typing just
+# make bin-kir/%
+# rather than
+# make -f arch/i386/kir-Makefile bin-kir/%
+# for building a KEEP_IT_REAL flavour.
+#
+$(BIN)-kir/% : kir-target
+ $(MAKE) -f arch/i386/kir-Makefile $(MAKECMDGOALS)
+
+.PHONY : kir-target
diff --git a/src/arch/i386/README.i386 b/src/arch/i386/README.i386
new file mode 100644
index 00000000..b9b79cc4
--- /dev/null
+++ b/src/arch/i386/README.i386
@@ -0,0 +1,197 @@
+Etherboot/NILO i386 initialisation path and external call interface
+===================================================================
+
+1. Background
+
+GCC compiles 32-bit code. It is capable of producing
+position-independent code, but the resulting binary is about 25%
+bigger than the corresponding fixed-position code. Since one main use
+of Etherboot is as firmware to be burned into an EPROM, code size must
+be kept as small as possible.
+
+This means that we want to compile fixed-position code with GCC, and
+link it to have a predetermined start address. The problem then is
+that we must know the address that the code will be loaded to when it
+runs. There are several ways to solve this:
+
+1. Pick an address, link the code with this start address, then make
+ sure that the code gets loaded at that location. This is
+ problematic, because we may pick an address that we later end up
+ wanting to use to load the operating system that we're booting.
+
+2. Pick an address, link the code with this start address, then set up
+ virtual addressing so that the virtual addresses match the
+ link-time addresses regardless of the real physical address that
+ the code is loaded to. This enables us to relocate Etherboot to
+ the top of high memory, where it will be out of the way of any
+ loading operating system.
+
+3. Link the code with a text start address of zero and a data start
+ address also of zero. Use 16-bit real mode and the
+ quasi-position-independence it gives you via segment addressing.
+ Doing this requires that we generate 16-bit code, rather than
+ 32-bit code, and restricts us to a maximum of 64kB in each segment.
+
+There are other possible approaches (e.g. including a relocation table
+and code that performs standard dynamic relocation), but the three
+options listed above are probably the best available.
+
+Etherboot can be invoked in a variety of ways (ROM, floppy, as a PXE
+NBP, etc). Several of these ways involve control being passed to
+Etherboot with the CPU in 16-bit real mode. Some will involve the CPU
+being in 32-bit protected mode, and there's an outside chance that
+some may involve the CPU being in 16-bit protected mode. We will
+almost certainly have to effect a CPU mode change in order to reach
+the mode we want to be in to execute the C code.
+
+Additionally, Etherboot may wish to call external routines, such as
+BIOS interrupts, which must be called in 16-bit real mode. When
+providing a PXE API, Etherboot must provide a mechanism for external
+code to call it from 16-bit real mode.
+
+Not all i386 builds of Etherboot will want to make real-mode calls.
+For example, when built for LinuxBIOS rather than the standard PCBIOS,
+no real-mode calls are necessary.
+
+For the ultimate in PXE compatibility, we may want to build Etherboot
+to run permanently in real mode.
+
+There is a wide variety of potential combinations of mode switches
+that we may wish to implement. There are additional complications,
+such as the inability to access a high-memory stack when running in
+real mode.
+
+2. Transition libraries
+
+To handle all these various combinations of mode switches, we have
+several "transition" libraries in Etherboot. We also have the concept
+of an "internal" and an "external" environment. The internal
+environment is the environment within which we can execute C code.
+The external environment is the environment of whatever external code
+we're trying to interface to, such as the system BIOS or a PXE NBP.
+
+As well as having a separate addressing scheme, the internal
+environment also has a separate stack.
+
+The transition libraries are:
+
+a) librm
+
+librm handles transitions between an external 16-bit real-mode
+environment and an internal 32-bit protected-mode environment with
+virtual addresses.
+
+b) libkir
+
+libkir handles transitions between an external 16-bit real-mode (or
+16:16 or 16:32 protected-mode) environment and an internal 16-bit
+real-mode (or 16:16 protected-mode) environment.
+
+c) libpm
+
+libpm handles transitions between an external 32-bit protected-mode
+environment with flat physical addresses and an internal 32-bit
+protected-mode environment with virtual addresses.
+
+The transition libraries handle the transitions required when
+Etherboot is started up for the first time, the transitions required
+to execute any external code, and the transitions required when
+Etherboot exits (if it exits). When Etherboot provides a PXE API,
+they also handle the transitions required when a PXE client makes a
+PXE API call to Etherboot.
+
+Etherboot may use multiple transition libraries. For example, an
+Etherboot ELF image does not require librm for its initial transitions
+from prefix to runtime, but may require librm for calling external
+real-mode functions.
+
+3. Setup and initialisation
+
+Etherboot is conceptually divided into the prefix, the decompressor,
+and the runtime image. (For non-compressed images, the decompressor
+is a no-op.) The complete image comprises all three parts and is
+distinct from the runtime image, which exclude the prefix and the
+decompressor.
+
+The prefix does several tasks:
+
+ Load the complete image into memory. (For example, the floppy
+ prefix issues BIOS calls to load the remainder of the complete image
+ from the floppy disk into RAM, and the ISA ROM prefix copies the ROM
+ contents into RAM for faster access.)
+
+ Call the decompressor, if the runtime image is compressed. This
+ decompresses the runtime image.
+
+ Call the runtime image's setup() routine. This is a routine
+ implemented in assembly code which sets up the internal environment
+ so that C code can execute.
+
+ Call the runtime image's arch_initialise() routine. This is a
+ routine implemented in C which does some basic startup tasks, such
+ as initialising the console device, obtaining a memory map and
+ relocating the runtime image to high memory.
+
+ Call the runtime image's arch_main() routine. This records the exit
+ mechanism requested by the prefix and calls main(). (The prefix
+ needs to register an exit mechanism because by the time main()
+ returns, the memory occupied by the prefix has most likely been
+ overwritten.)
+
+When acting as a PXE ROM, the ROM prefix contains an UNDI loader
+routine in addition to its usual code. The UNDI loader performs a
+similar sequence of steps:
+
+ Load the complete image into memory.
+
+ Call the decompressor.
+
+ Call the runtime image's setup() routine.
+
+ Call the runtime image's arch_initialise() routine.
+
+ Call the runtime image's install_pxe_stack() routine.
+
+ Return to caller.
+
+The runtime image's setup() routine will perform the following steps:
+
+ Switch to the internal environment using an appropriate transition
+ library. This will record the parameters of the external
+ environment.
+
+ Set up the internal environment: load a stack, and set up a GDT for
+ virtual addressing if virtual addressing is to be used.
+
+ Switch back to the external environment using the transition
+ library. This will record the parameters of the internal
+ environment.
+
+Once the setup() routine has returned, the internal environment has been
+set up ready for C code to run. The prefix can call C routines using
+a function from the transition library.
+
+The runtime image's arch_initialise() routine will perform the
+following steps:
+
+ Zero the bss
+
+ Initialise the console device(s) and print a welcome message.
+
+ Obtain a memory map via the INT 15,E820 BIOS call or suitable
+ fallback mechanism. [not done if libkir is being used]
+
+ Relocate the runtime image to the top of high memory. [not done if
+ libkir is being used]
+
+ Install librm to base memory. [done only if librm is being used]
+
+ Call initialise().
+
+ Return to the prefix, setting registers to indicate to the prefix
+ the new location of the transition library, if applicable. Which
+ registers these are is specific to the transition library being
+ used.
+
+Once the arch_initialise() routine has returned, the prefix will
+probably call arch_main().
diff --git a/src/arch/i386/core/callbacks.c b/src/arch/i386/core/callbacks.c
index d45e63e2..8818e146 100644
--- a/src/arch/i386/core/callbacks.c
+++ b/src/arch/i386/core/callbacks.c
@@ -9,7 +9,6 @@
#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
diff --git a/src/arch/i386/core/cpu.c b/src/arch/i386/core/cpu.c
index 8a0f7333..7b2533f4 100644
--- a/src/arch/i386/core/cpu.c
+++ b/src/arch/i386/core/cpu.c
@@ -2,6 +2,7 @@
#include "stdint.h"
#include "string.h"
#include "bits/cpu.h"
+#include "init.h"
/* Standard macro to see if a specific flag is changeable */
@@ -83,4 +84,7 @@ void cpu_setup(void)
{
identify_cpu(&cpu_info);
}
+
+INIT_FN ( INIT_CPU, cpu_setup, NULL, NULL );
+
#endif /* CONFIG_X86_64 */
diff --git a/src/arch/i386/core/elf.c b/src/arch/i386/core/elf.c
index 6ef78c71..40a18a1f 100644
--- a/src/arch/i386/core/elf.c
+++ b/src/arch/i386/core/elf.c
@@ -1,5 +1,6 @@
#include "etherboot.h"
#include "elf.h"
+#include "memsizes.h"
#define NAME "Etherboot"
diff --git a/src/arch/i386/core/hooks.c b/src/arch/i386/core/hooks.c
index 9ca6f480..32a137b1 100644
--- a/src/arch/i386/core/hooks.c
+++ b/src/arch/i386/core/hooks.c
@@ -1,35 +1,93 @@
-#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();
+#include "stdint.h"
+#include "stddef.h"
+#include "registers.h"
+#include "string.h"
+#include "hooks.h"
+#include "init.h"
+#include "main.h"
+#ifdef REALMODE
+#include "realmode.h"
#endif
-}
+/* Symbols defined by the linker */
+extern char _bss[], _ebss[];
+
+/*
+ * This file provides the basic entry points from assembly code. See
+ * README.i386 for a description of the entry code path.
+ *
+ * This file is compiled to two different object files: hooks.o and
+ * hooks_rm.o. REALMODE is defined when compiling hooks_rm.o
+ *
+ */
-void arch_relocated_from (unsigned long old_addr )
+/*
+ * arch_initialise(): perform any required initialisation such as
+ * setting up the console device and relocating to high memory. Note
+ * that if we relocate to high memory and the prefix is in base
+ * memory, then we will need to install a copy of librm in base memory
+ * and adjust retaddr so that we return to the installed copy.
+ *
+ */
+#ifdef REALMODE
+void arch_rm_initialise ( struct i386_all_regs *regs,
+ void (*retaddr) (void) )
+#else /* REALMODE */
+void arch_initialise ( struct i386_all_regs *regs,
+ void (*retaddr) (void) __unused )
+#endif /* REALMODE */
{
+ /* Zero the BSS */
+ memset ( _bss, 0, _ebss - _bss );
-#ifdef PCBIOS
- /* Deallocate base memory used for the Etherboot runtime,
- * if applicable
+ /* Call all registered initialisation functions.
*/
- forget_runtime_base_memory( old_addr );
-#endif
-
+ call_init_fns ();
}
-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.
+#ifdef REALMODE
+
+/*
+ * arch_rm_main() : call main() and then exit via whatever exit mechanism
+ * the prefix requested.
+ *
+ */
+void arch_rm_main ( struct i386_all_regs *regs ) {
+ struct i386_all_regs regs_copy;
+ void (*exit_fn) ( struct i386_all_regs *regs );
+
+ /* Take a copy of the registers, because the memory holding
+ * them will probably be trashed by the time main() returns.
*/
- forget_real_mode_stack();
-#endif
+ regs_copy = *regs;
+ exit_fn = ( typeof ( exit_fn ) ) regs_copy.eax;
+
+ /* Call to main() */
+ regs_copy.eax = main();
+
+ /* Call registered per-object exit functions */
+ call_exit_fns ();
+
+ if ( exit_fn ) {
+ /* Prefix requested that we use a particular function
+ * as the exit path, so we call this function, which
+ * must not return.
+ */
+ exit_fn ( &regs_copy );
+ }
}
+
+#else /* REALMODE */
+
+/*
+ * arch_main() : call main() and return
+ *
+ */
+void arch_main ( struct i386_all_regs *regs ) {
+ regs->eax = main();
+
+ /* Call registered per-object exit functions */
+ call_exit_fns ();
+};
+
+#endif /* REALMODE */
diff --git a/src/arch/i386/core/i386_timer.c b/src/arch/i386/core/i386_timer.c
index 531183d4..c9aa406f 100644
--- a/src/arch/i386/core/i386_timer.c
+++ b/src/arch/i386/core/i386_timer.c
@@ -10,6 +10,7 @@
#include "etherboot.h"
#include "timer.h"
#include "latch.h"
+#include "init.h"
void __load_timer2(unsigned int ticks)
{
@@ -40,7 +41,7 @@ static int __timer2_running(void)
}
#if !defined(CONFIG_TSC_CURRTICKS)
-void setup_timers(void)
+static void setup_timers(void)
{
return;
}
@@ -126,7 +127,7 @@ bad_ctc:
}
static unsigned long clocks_per_tick;
-void setup_timers(void)
+static void setup_timers(void)
{
if (!clocks_per_tick) {
clocks_per_tick = calibrate_tsc();
@@ -189,3 +190,5 @@ int timer2_running(void)
}
#endif /* RTC_CURRTICKS */
+
+INIT_FN ( INIT_TIMERS, setup_timers, NULL, NULL );
diff --git a/src/arch/i386/core/init.S b/src/arch/i386/core/init.S
deleted file mode 100644
index 71717c25..00000000
--- a/src/arch/i386/core/init.S
+++ /dev/null
@@ -1,305 +0,0 @@
-#include "callbacks.h"
- .equ CR0_PE, 1
-
- .text
- .arch i386
- .section ".prefix", "ax", @progbits
-
-#undef CODE16
-#if defined(PCBIOS)
-#define CODE16
-#endif
-
-/* We have two entry points: "conventional" (at the start of the file)
- * and "callback" (at _entry, 2 bytes in). The "callback" entry
- * should be used if the caller wishes to provide a specific opcode.
- * It is equivalent to a call to in_call. Using the "conventional"
- * entry point is equivalent to using the "callback" entry point with
- * an opcode of EB_OPCODE_MAIN.
- *
- * Both entry points can be called in either 16-bit real or 32-bit
- * protected mode with flat physical addresses. We detect which mode
- * the processor is in and call either in_call or rm_in_call as
- * appropriate. Note that the mode detection code must therefore be
- * capable of doing the same thing in either mode, even though the
- * machine code instructions will be interpreted differently.
- *
- * The decompressor will be invoked if necessary to decompress
- * Etherboot before attempting to jump to it.
- */
-
-/******************************************************************************
- * Entry points and mode detection code
- ******************************************************************************
- */
-
- .code32
-/* "Conventional" entry point: caller provides no opcode */
- .globl _start
-_start:
- /* Set flag to indicate conventional entry point used */
- pushl $0 /* "pushw $0" in 16-bit code */
- /* Fall through to "callback" entry point */
-
-/* "Callback" entry point */
- .globl _entry
-_entry:
-
-#ifdef CODE16
- /* CPU mode detection code */
- pushl %eax /* "pushw %ax" in 16-bit code */
- pushw %ax /* "pushl %eax" in 16-bit code */
- movl %cr0, %eax /* Test protected mode bit */
- testb $CR0_PE, %al
- popw %ax /* "popl %eax" in 16-bit code */
- popl %eax /* "popw %eax" in 16-bit code */
- jz rmode
-#endif /* CODE16 */
-
-/******************************************************************************
- * Entered in protected mode
- ******************************************************************************
- */
-
- .code32
-pmode:
- cmpl $0, 0(%esp) /* Conventional entry point used? */
- jne 1f
- /* Entered via conventional entry point: set up stack */
- xchgl %eax, 4(%esp) /* %eax = return addr, store %eax */
- movl %eax, 0(%esp) /* 0(%esp) = return address */
- movl $(EB_OPCODE_MAIN|EB_USE_INTERNAL_STACK|EB_SKIP_OPCODE), %eax
- xchgl %eax, 4(%esp) /* 4(%esp) = opcode, restore %eax */
-1:
- /* Run decompressor if necessary */
- pushl %eax
- movl $decompress, %eax
- testl %eax, %eax
- jz 1f
- call decompress
-1: popl %eax
-
- /* Make in_call to Etherboot */
- jmp _prefix_in_call
-
-/******************************************************************************
- * Entered in real mode
- ******************************************************************************
- */
-
-#ifdef CODE16
- .code16
-rmode:
- pushw %ax /* Padding */
- pushw %bp
- movw %sp, %bp
- cmpw $0, 6(%bp) /* Conventional entry point used? */
- jne 1f
- /* Entered via conventional entry point: set up stack */
- pushw %ax
- movw 6(%bp), %ax
- movw %ax, 2(%bp) /* Move return address down */
- movl $(EB_OPCODE_MAIN|EB_USE_INTERNAL_STACK|EB_SKIP_OPCODE), 4(%bp)
- popw %ax
- popw %bp
- jmp 2f
-1: /* Entered via callback entry point: do nothing */
- popw %bp
- popw %ax
-2:
- /* Preserve registers */
- pushw %ds
- pushl %eax
-
- /* Run decompressor if necessary. Decompressor is 32-bit
- * code, so we must switch to pmode first. Save and restore
- * GDT over transition to pmode.
- */
- movl $decompress, %eax
- testl %eax, %eax
- jz 1f
- pushw %ds
- pushw %es
- pushw %fs
- pushw %gs
- subw $8, %sp
- pushw %bp
- movw %sp, %bp
- sgdt 2(%bp)
- pushw %ss /* Store params for _prot_to_real */
- pushw %cs
- call _prefix_real_to_prot
- .code32
- call decompress
- call _prefix_prot_to_real
- .code16
- popw %ax /* skip */
- popw %ax /* skip */
- lgdt 2(%bp)
- popw %bp
- addw $8, %sp
- popw %gs
- popw %fs
- popw %es
- popw %ds
-1:
-
- /* Set rm_etherboot_location */
- xorl %eax, %eax
- movw %cs, %ax
- movw %ax, %ds
- shll $4, %eax
- addl $_prefix_size, %eax
- movl %eax, _prefix_rm_etherboot_location
-
- /* Restore registers */
- popl %eax
- popw %ds
-
- /* Make real-mode in_call to Etherboot */
- jmp _prefix_rm_in_call
-#endif /* CODE16 */
-
-/******************************************************************************
- * Utility routines that can be called by the "prefix".
- ******************************************************************************
- */
-
-#ifdef CODE16
-
-/* Prelocate code: either to an area at the top of free base memory.
- * Switch stacks to use the stack within the resulting
- * Etherboot image.
- *
- * On entry, %cs:0000 must be the start of the prefix: this is used to
- * locate the code to be copied.
- *
- * This routine takes a single word parameter: the number of bytes to
- * be transferred from the old stack to the new stack (excluding the
- * return address and this parameter itself, which will always be
- * copied). If this value is negative, the stacks will not be
- * switched.
- *
- * Control will "return" to the appropriate point in the relocated
- * image.
- */
-
-#define PRELOC_PRESERVE ( 20 )
-#define PRELOC_OFFSET_RETADDR ( PRELOC_PRESERVE )
-#define PRELOC_OFFSET_RETADDR_E ( PRELOC_OFFSET_RETADDR + 4 )
-#define PRELOC_OFFSET_COPY ( PRELOC_OFFSET_RETADDR_E )
-#define PRELOC_OFFSET_COPY_E ( PRELOC_OFFSET_COPY + 2 )
-
-#define PRELOC_ALWAYS_COPY ( PRELOC_OFFSET_COPY_E )
-
- .code16
- .globl prelocate
-prelocate:
- /* Pad to allow for expansion of return address */
- pushw %ax
-
- /* Preserve registers */
- pushaw
- pushw %ds
- pushw %es
-
- /* Claim an area of base memory from the BIOS and put the
- * payload there.
- */
- movw $0x40, %bx
- movw %bx, %es
- movw %es:(0x13), %bx /* FBMS in kb to %ax */
- shlw $6, %bx /* ... in paragraphs */
- subw $_image_size_pgh, %bx /* Subtract space for image */
- shrw $6, %bx /* Round down to nearest kb */
- movw %bx, %es:(0x13) /* ...and claim memory from BIOS */
- shlw $6, %bx
-
- /* At this point %bx contains the segment address for the
- * start of the image (image = prefix + runtime).
- */
-
- /* Switch stacks */
- movw %ss, %ax
- movw %ax, %ds
- movw %sp, %si /* %ds:si = current %ss:sp */
- movw %ss:PRELOC_OFFSET_COPY(%si), %cx
- testw %cx, %cx
- js 1f
- leaw _stack_offset_pgh(%bx), %ax /* %ax = new %ss */
- movw %ax, %es
- movw $_stack_size, %di
- addw $PRELOC_ALWAYS_COPY, %cx
- subw %cx, %di /* %es:di = new %ss:sp */
- movw %ax, %ss /* Set new %ss:sp */
- movw %di, %sp
- cld
- rep movsb /* Copy stack contents */
-1:
-
- /* Do the image copy backwards, since if there's overlap with
- * a forward copy then it means we're going to get trashed
- * during the copy anyway...
- */
- pushal /* Preserve 32-bit registers */
- movw %bx, %es /* Destination base for copy */
- pushw %cs
- popw %ds /* Source base for copy */
- movl $_verbatim_size-1, %ecx /* Offset to last byte */
- movl %ecx, %esi
- movl %ecx, %edi
- incl %ecx /* Length */
- std /* Backwards copy of binary */
- ADDR32 rep movsb
- cld
- popal /* Restore 32-bit registers */
-
- /* Store (%bx<<4) as image_basemem to be picked up by
- * basemem.c. Also store image_size, since there's no other
- * way that we can later know how much memory we allocated.
- * (_zfile_size is unavailable when rt2 is linked).
- */
- pushl %eax
- xorl %eax, %eax
- movw %bx, %ax
- shll $4, %eax
- movl %eax, %es:_prefix_image_basemem
- movl $_image_size, %es:_prefix_image_basemem_size
- popl %eax
-
- /* Expand original near return address into far return to new
- * code location.
- */
- movw %sp, %bp
- xchgw %bx, (PRELOC_OFFSET_RETADDR+2)(%bp)
- movw %bx, (PRELOC_OFFSET_RETADDR+0)(%bp)
-
- /* Restore registers and return */
- popw %es
- popw %ds
- popaw
- lret /* Jump to relocated code */
-
- /* Utility routine to free base memory allocated by prelocate.
- * Ensure that said memory is not in use (e.g. for the CPU
- * stack) before calling this routine.
- */
- .globl deprelocate
-deprelocate:
- /* Claim an area of base memory from the BIOS and put the
- * payload there.
- */
- pushw %ax
- pushw %es
- movw $0x40, %ax
- movw %ax, %es
- movw %es:(0x13), %ax /* FBMS in kb to %ax */
- shlw $6, %ax /* ... in paragraphs */
- addw $_image_size_pgh+0x40-1, %ax /* Add space for image and... */
- shrw $6, %ax /* ...round up to nearest kb */
- movw %ax, %es:(0x13) /* Give memory back to BIOS */
- popw %es
- popw %ax
- ret
-
-#endif /* CODE16 */
diff --git a/src/arch/i386/core/pxe_callbacks.c b/src/arch/i386/core/pxe_callbacks.c
index 344d34fe..df522329 100644
--- a/src/arch/i386/core/pxe_callbacks.c
+++ b/src/arch/i386/core/pxe_callbacks.c
@@ -3,6 +3,31 @@
* an NBP to the PXE stack and for starting an NBP from the PXE stack.
*/
+#warning "pxe_callbacks.c is temporarily broken"
+
+void xstartpxe ( void ) {
+}
+
+void install_pxe_stack ( void ) {
+}
+
+void remove_pxe_stack ( void ) {
+}
+
+void hook_pxe_stack ( void ) {
+}
+
+void unhook_pxe_stack ( void ) {
+}
+
+void pxe_in_call ( void ) {
+}
+
+void use_undi_ds_for_rm_stack ( void ) {
+}
+
+#if 0
+
#ifdef PXE_EXPORT
#include "etherboot.h"
@@ -362,3 +387,5 @@ __asm__ ( ".globl _pxe_stack_t_size" );
__asm__ ( ".equ _pxe_stack_t_size, 0" );
#endif /* PXE_EXPORT */
+
+#endif /* 0 */
diff --git a/src/arch/i386/core/realmode.c b/src/arch/i386/core/realmode.c
index ef4ede86..32252bd3 100644
--- a/src/arch/i386/core/realmode.c
+++ b/src/arch/i386/core/realmode.c
@@ -3,146 +3,38 @@
* 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.
+/*
+ * Copy data to/from base memory.
+ *
*/
-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;
+#ifdef KEEP_IT_REAL
- /* 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;
+void memcpy_to_real ( segoff_t dest, void *src, size_t n ) {
- /* 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;
+void memcpy_from_real ( void *dest, segoff_t src, size_t n ) {
- return virt_to_phys ( stack + p->in_stack_len );
}
+#endif /* KEEP_IT_REAL */
-/* 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.
- */
+#define RM_STACK_SIZE ( 0x1000 )
- __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;
+/* 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) );
}
+
+char *real_mode_stack;
+int lock_real_mode_stack;
diff --git a/src/arch/i386/core/realmode_asm.S b/src/arch/i386/core/realmode_asm.S
index 28a5bfed..db8bc22b 100644
--- a/src/arch/i386/core/realmode_asm.S
+++ b/src/arch/i386/core/realmode_asm.S
@@ -31,570 +31,6 @@
#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
diff --git a/src/arch/i386/core/setjmp.S b/src/arch/i386/core/setjmp.S
new file mode 100644
index 00000000..59a1b7cb
--- /dev/null
+++ b/src/arch/i386/core/setjmp.S
@@ -0,0 +1,40 @@
+/* setjmp and longjmp. Use of these functions is deprecated. */
+
+ .text
+ .arch i386
+ .code32
+
+/**************************************************************************
+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
diff --git a/src/arch/i386/core/setup.S b/src/arch/i386/core/setup.S
new file mode 100644
index 00000000..b2dd3b16
--- /dev/null
+++ b/src/arch/i386/core/setup.S
@@ -0,0 +1,158 @@
+/****************************************************************************
+ * This file provides the setup() and setup16() functions. The
+ * purpose of these functions is to set up the internal environment so
+ * that C code can execute. This includes setting up the internal
+ * stack and (where applicable) setting up a GDT for virtual
+ * addressing.
+ *
+ * These functions are designed to be called by the prefix.
+ *
+ * The same basic assembly code is used to compile both setup()
+ * and setup16().
+ ****************************************************************************
+ */
+
+ .text
+ .arch i386
+
+#ifdef CODE16
+/****************************************************************************
+ * setup16 (real-mode far call)
+ *
+ * This function can be called by a 16-bit prefix in order to set up
+ * the internal (either 16-bit or 32-bit) environment.
+ *
+ * Parameters: none
+ *
+ * %cs:0000, %ds:0000 and %es:0000 must point to the start of the
+ * (decompressed) runtime image.
+ *
+ * If KEEP_IT_REAL is defined, then %ds:0000 may instead point to the
+ * start of the (decompressed) data segment portion of the runtime
+ * image. If %ds==%cs, then it will be assumed that the data segment
+ * follows immediately after the code segment.
+ ****************************************************************************
+ */
+
+#ifdef KEEP_IT_REAL
+
+#define ENTER_FROM_EXTERNAL call ext_to_kir
+#define RETURN_TO_EXTERNAL call kir_to_ext
+#define ENTRY_POINT kir_call
+
+#else /* KEEP_IT_REAL */
+
+#define ENTER_FROM_EXTERNAL \
+ pushw %cs ; \
+ call real_to_prot ; \
+ .code32
+#define RETURN_TO_EXTERNAL \
+ call prot_to_real ; \
+ .code16
+#define ENTRY_POINT _prot_call /* _prot_call = OFFSET ( prot_call ) in librm */
+
+#endif /* KEEP_IT_REAL */
+
+#define ENTRY_POINT_REGISTER di
+
+ .section ".text16"
+ .code16
+ .globl setup16
+setup16:
+
+#else /* CODE16 */
+
+/****************************************************************************
+ * setup (32-bit protected-mode near call)
+ *
+ * This function can be called by a 32-bit prefix in order to set up
+ * the internal 32-bit environment.
+ *
+ * Parameters: none
+ ****************************************************************************
+ */
+
+#define ENTER_FROM_EXTERNAL call ext_to_int
+#define RETURN_TO_EXTERNAL call int_to_ext
+#define ENTRY_POINT int_call
+#define ENTRY_POINT_REGISTER edi
+
+ .section ".text"
+ .code32
+ .globl setup
+setup:
+
+#endif /* CODE16 */
+
+ /* Preserve flags (including interrupt status) */
+ pushfl
+
+ /* Switch to (uninitialised) internal environment. This will
+ * preserve the external environment for when we call
+ * RETURN_TO_EXTERNAL.
+ */
+ ENTER_FROM_EXTERNAL
+ /* NOTE: We may have only four bytes of stack at this point */
+
+#if defined(CODE16) && defined(KEEP_IT_REAL)
+
+ /* If %ds == %cs, then the data segment is located immediately
+ * after the code segment.
+ */
+ pushw %ax
+ pushw %bx
+ movw %cs, %ax
+ movw %ds, %bx
+ cmpw %ax, %bx
+ jne 1f
+ addw $_text_load_size_pgh, %ax
+ movw %ax, %ds
+1: popw %bx
+ popw %ax
+
+ /* Switch to internal stack */
+ pushw %ds
+ popw %ss
+ movl $_estack, %esp
+
+#else /* CODE16 && KEEP_IT_REAL */
+
+ /* Work out where we're running */
+ call 1f
+1: popl %ebp
+
+ /* Switch to internal pmode stack */
+ leal (_estack-1b)(%ebp), %esp
+
+ /* Set up GDT for virtual addressing */
+ call run_here
+
+#endif /* CODE16 && KEEP_IT_REAL */
+
+ /* Switch back to external environment. This will preserve
+ * the internal environment ready for the next call.
+ */
+ RETURN_TO_EXTERNAL
+
+ /* Pass pointer to entry-point function back to prefix. %es
+ * may, by now, have been destroyed, so we re-initialise it
+ * from %cs.
+ */
+ pushw %cs
+ popw %es
+ mov $ENTRY_POINT, %ENTRY_POINT_REGISTER
+
+ /* Restore flags (including interrupt status) */
+ popfl
+
+ lret
+
+/****************************************************************************
+ * Internal stack
+ ****************************************************************************
+ */
+ .section ".stack"
+ .align 8
+_stack:
+ .space 4096
+_estack:
diff --git a/src/arch/i386/core/start16.S b/src/arch/i386/core/start16.S
deleted file mode 100644
index 72fcfbfb..00000000
--- a/src/arch/i386/core/start16.S
+++ /dev/null
@@ -1,285 +0,0 @@
-/*****************************************************************************
- *
- * THIS FILE IS NOW OBSOLETE.
- *
- * The functions of this file are now placed in init.S.
- *
- *****************************************************************************
- */
-
-#ifndef PCBIOS
-#error "16bit code is only supported with the PCBIOS"
-#endif
-
-#define CODE_SEG 0x08
-#define DATA_SEG 0x10
-
-#define EXEC_IN_SITU_MAGIC 0x45524548 /* 'HERE' */
-
- .equ CR0_PE, 1
-
-#ifdef GAS291
-#define DATA32 data32;
-#define ADDR32 addr32;
-#define LJMPI(x) ljmp x
-#else
-#define DATA32 data32
-#define ADDR32 addr32
-/* newer GAS295 require #define LJMPI(x) ljmp *x */
-#define LJMPI(x) ljmp x
-#endif
-
-/*****************************************************************************
- *
- * start16 : move payload to desired area of memory, set up for exit
- * back to prefix, set up for 32-bit code.
- *
- * Enter (from prefix) with es:di = 0x4552:0x4548 if you want to
- * prevent start16 from moving the payload. There are three
- * motivations for moving the payload:
- *
- * 1. It may be in ROM, in which case we need to move it to RAM.
- * 2. Whatever loaded us probably didn't know about our memory usage
- * beyond the end of the image file. We should claim this memory
- * before using it.
- *
- * Unless the prefix instructs us otherwise we will move the payload to:
- *
- * An area of memory claimed from the BIOS via 40:13.
- *
- * We use the main Etherboot stack (within the image target) as our
- * stack; we don't rely on the prefix passing us a stack usable for
- * anything other than the prefix's return address. The (first 512
- * bytes of the) prefix code segment is copied to a safe archive
- * location.
- *
- * When we return to the prefix (from start32), we copy this code back
- * to a new area of memory, restore the prefix's ss:sp and ljmp back
- * to the copy of the prefix. The prefix will see a return from
- * start16 *but* may be executing at a new location. Code following
- * the lcall to start16 must therefore be position-independent and
- * must also be within [cs:0000,cs:01ff]. We make absolutely no
- * guarantees about the stack contents when the prefix regains
- * control.
- *
- * Trashes just about all registers, including all the segment
- * registers.
- *
- *****************************************************************************
- */
-
- .text
- .code16
- .arch i386
- .org 0
- .globl _start16
-_start16:
-
-/*****************************************************************************
- * Work out where we are going to place our image (image = optional
- * decompressor + runtime). Exit this stage with %ax containing the
- * runtime target address divided by 16 (i.e. a real-mode segment
- * address).
- *****************************************************************************
- */
- movw %es, %ax
- cmpw $(EXEC_IN_SITU_MAGIC >> 16), %ax
- jne exec_moved
- cmpw $(EXEC_IN_SITU_MAGIC & 0xffff), %di
- jne exec_moved
-exec_in_situ:
- /* Prefix has warned us not to move the payload. Simply
- * calculate where the image is going to end up, so we can
- * work out where to put our stack.
- */
- movw %cs, %ax
- addw $((payload-_start16)/16), %ax
- jmp 99f
-exec_moved:
- /* Claim an area of base memory from the BIOS and put the
- * payload there. arch_relocated_to() will deal with freeing
- * up this memory once we've relocated to high memory.
- */
- movw $0x40, %ax
- movw %ax, %es
- movw %es:(0x13), %ax /* FBMS in kb to %ax */
- shlw $6, %ax /* ... in paragraphs */
- subw $__image_size_pgh, %ax /* Subtract space for image */
- shrw $6, %ax /* Round down to nearest kb */
- movw %ax, %es:(0x13) /* ...and claim memory from BIOS */
- shlw $6, %ax
-99:
- /* At this point %ax contains the segment address for the
- * start of the image (image = optional decompressor + runtime).
- */
-
-/*****************************************************************************
- * Set up stack in start32's stack space within the place we're going
- * to copy Etherboot to, reserve space for GDT, copy return address
- * from prefix stack, store prefix stack address
- *****************************************************************************
- */
- popl %esi /* Return address */
- mov %ss, %bx /* %es:di = prefix stack address */
- mov %bx, %es /* (*after* pop of return address) */
- movw %sp, %di
- movw $__offset_stack_pgh, %bx /* Set up Etherboot stack */
- addw %ax, %bx
- movw %bx, %ss
- movw $__stack_size, %sp
- subw $(_gdt_end - _gdt), %sp /* Reserve space for GDT */
- movw %sp, %bp /* Record GDT location */
- /* Set up i386_rm_in_call_data_t structure on stack. This is
- * the same structure as is set up by rm_in_call.
- */
- pushl $0 /* Dummy opcode */
- pushl %esi /* Prefix return address */
- pushfw /* Flags */
- pushw %di /* Prefix %sp */
- pushw %gs /* Segment registers */
- pushw %fs
- pushw %es
- pushw %ds
- pushw %es /* Prefix %ss */
- pushw %cs
- /* Stack is now 32-bit aligned */
-
- /* %ax still contains image target segment address */
-
-/*****************************************************************************
- * Calculate image target and prefix code physical addresses, store on stack
- * for use in copy routine.
- *****************************************************************************
- */
- movzwl %es:-2(%di), %ebx /* Prefix code segment */
- shll $4, %ebx
- pushl %ebx /* Prefix code physical address */
- movzwl %ax, %edi /* Image target segment */
- shll $4, %edi
- pushl %edi /* Image target physical address */
-
-/*****************************************************************************
- * Transition to 32-bit protected mode. Set up all segment
- * descriptors to use flat physical addresses.
- *****************************************************************************
- */
- /* Copy gdt to area reserved on stack
- */
- push %cs /* GDT source location -> %ds:%si */
- pop %ds
- mov $(_gdt - _start16), %si
- push %ss /* GDT target location -> %es:%di */
- pop %es
- mov %bp, %di
- mov $(_gdt_end - _gdt), %cx
- cld
- rep movsb /* Copy GDT to stack */
- movl %ss, %eax
- shll $4, %eax
- movzwl %bp, %ebx
- addl %eax, %ebx /* Physical addr of GDT copy -> %ebx */
- movl %ebx, 2(%bp) /* Fill in addr field in GDT */
-
- /* Compute the offset I am running at.
- */
- movl %cs, %ebx
- shll $4, %ebx /* %ebx = offset for start16 symbols */
-
- /* Switch to 32bit protected mode.
- */
- cli /* Disable interrupts */
- lgdt (%bp) /* Load GDT from stack */
- movl %cr0, %eax /* Set protected mode bit */
- orb $CR0_PE, %al
- movl %eax, %cr0
- movl %ss, %eax /* Convert stack pointer to 32bit */
- shll $4, %eax
- movzwl %sp, %esp
- addl %eax, %esp
- movl $DATA_SEG, %eax /* Reload the segment registers */
- movl %eax, %ds
- movl %eax, %es
- movl %eax, %ss
- movl %eax, %fs
- movl %eax, %gs
- /* Flush prefetch queue, and reload %cs:%eip by effectively ljmping
- * to code32_start. Do the jump via pushl and lret because the text
- * may not be writable/
- */
- pushl $CODE_SEG
- ADDR32 leal (code32_start-_start16)(%ebx), %eax
- pushl %eax
- DATA32 lret /* DATA32 needed, because we're still in 16-bit mode */
-
-_gdt:
-gdtarg:
- .word _gdt_end - _gdt - 1 /* limit */
- .long 0 /* addr */
- .word 0
-_pmcs:
- /* 32 bit protected mode code segment */
- .word 0xffff, 0
- .byte 0, 0x9f, 0xcf, 0
-_pmds:
- /* 32 bit protected mode data segment */
- .word 0xffff,0
- .byte 0,0x93,0xcf,0
-_gdt_end:
-
- .code32
-code32_start:
-
-/*****************************************************************************
- * Copy payload to target location. Do the copy backwards, since if
- * there's overlap with a forward copy then it means start16 is going
- * to get trashed during the copy anyway...
- *****************************************************************************
- */
- popl %edi /* Image target physical address */
- pushl %edi
- leal (payload-_start16)(%ebx), %esi /* Image source physical addr */
- movl $__payload_size, %ecx /* Payload size (not image size) */
- addl %ecx, %edi /* Start at last byte (length - 1) */
- decl %edi
- addl %ecx, %esi
- decl %esi
- std /* Backward copy of image */
- rep movsb
- cld
- popl %edi /* Restore image target physical address */
- leal __decompressor_uncompressed(%edi), %ebx
- subl $_text, %ebx /* %ebx = offset for runtime symbols */
-
-/*****************************************************************************
- * Copy prefix to storage area within Etherboot image.
- *****************************************************************************
- */
- popl %esi /* Prefix source physical address */
- pushl %edi
- leal _prefix_copy(%ebx), %edi /* Prefix copy phys. addr. */
- leal _eprefix_copy(%ebx), %ecx
- subl %edi, %ecx /* Prefix copy size */
- rep movsb /* Forward copy of prefix */
- popl %edi /* Restore image target physical address */
-
-/*****************************************************************************
- * Record base memory used by Etherboot image
- *****************************************************************************
- */
- movl %edi, _prefix_image_basemem (%ebx)
-
-/*****************************************************************************
- * Jump to start of the image (i.e. the decompressor, or start32 if
- * non-compressed).
- *****************************************************************************
- */
- pushl $0 /* Inform start32 that exit path is 16-bit */
- jmpl *%edi /* Jump to image */
-
- .balign 16
- /* Etherboot needs to be 16byte aligned or data that
- * is virtually aligned is no longer physically aligned
- * which is just nasty in general. 16byte alignment
- * should be sufficient though.
- */
-payload:
diff --git a/src/arch/i386/core/start32.S b/src/arch/i386/core/start32.S
index 6dc3f203..e82454bb 100644
--- a/src/arch/i386/core/start32.S
+++ b/src/arch/i386/core/start32.S
@@ -1,18 +1,5 @@
-/* #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
-
+#include "virtaddr.h"
+
.equ MSR_K6_EFER, 0xC0000080
.equ EFER_LME, 0x00000100
.equ X86_CR4_PAE, 0x00000020
@@ -29,12 +16,6 @@
#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
@@ -54,227 +35,11 @@
* 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
@@ -301,7 +66,7 @@ xstart32:
pushl %ebx
/* Store the destination address on the stack */
- pushl $FLAT_CODE_SEG
+ pushl $PHYSICAL_CS
pushl %ecx
/* Cache virt_offset */
@@ -536,218 +301,6 @@ end_lm:
.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
diff --git a/src/arch/i386/core/tagged_loader.c b/src/arch/i386/core/tagged_loader.c
index 9c6d6c25..8955b0f8 100644
--- a/src/arch/i386/core/tagged_loader.c
+++ b/src/arch/i386/core/tagged_loader.c
@@ -1,5 +1,4 @@
#include "realmode.h"
-#include "segoff.h"
struct segheader
{
diff --git a/src/arch/i386/core/video_subr.c b/src/arch/i386/core/video_subr.c
index dccdd97c..dbcfc73d 100644
--- a/src/arch/i386/core/video_subr.c
+++ b/src/arch/i386/core/video_subr.c
@@ -5,10 +5,14 @@
*
*/
-#ifdef CONSOLE_DIRECT_VGA
+#include "stddef.h"
+#include "string.h"
+#include "io.h"
+#include "console.h"
+#include "init.h"
+#include "vga.h"
-#include <etherboot.h>
-#include <vga.h>
+static struct console_driver vga_console;
static char *vidmem; /* The video buffer */
static int video_line, video_col;
@@ -17,7 +21,7 @@ static int video_line, video_col;
static void memsetw(void *s, int c, unsigned int n)
{
- int i;
+ unsigned int i;
u16 *ss = (u16 *) s;
for (i = 0; i < n; i++) {
@@ -25,7 +29,7 @@ static void memsetw(void *s, int c, unsigned int n)
}
}
-void video_init(void)
+static void video_init(void)
{
static int inited=0;
@@ -50,7 +54,7 @@ static void video_scroll(void)
vidmem[i] = ' ';
}
-void vga_putc(unsigned char byte)
+static void vga_putc(int byte)
{
if (byte == '\n') {
video_line++;
@@ -90,5 +94,9 @@ void vga_putc(unsigned char byte)
write_crtc((video_col + (video_line *COLS)) & 0x0ff, CRTC_CURSOR_LO);
}
-#endif
+static struct console_driver vga_console __console_driver = {
+ .putchar = vga_putc,
+ .disabled = 1,
+};
+INIT_FN ( INIT_CONSOLE, video_init, NULL, NULL );
diff --git a/src/arch/i386/core/virtaddr.S b/src/arch/i386/core/virtaddr.S
new file mode 100644
index 00000000..ed495c35
--- /dev/null
+++ b/src/arch/i386/core/virtaddr.S
@@ -0,0 +1,317 @@
+/*
+ * Functions to support the virtual addressing method of relocation
+ * that Etherboot uses.
+ *
+ */
+
+#include "virtaddr.h"
+
+ .arch i386
+
+/****************************************************************************
+ * GDT for initial transition to protected mode
+ *
+ * The segment values, PHYSICAL_CS et al, are defined in an external
+ * header file virtaddr.h, since they need to be shared with librm.
+ ****************************************************************************
+ */
+ .data
+ .align 16
+
+gdt:
+gdt_limit: .word gdt_length - 1
+gdt_addr: .long 0
+ .word 0 /* padding */
+
+ .org gdt + PHYSICAL_CS
+physical_cs:
+ /* 32 bit protected mode code segment, physical addresses */
+ .word 0xffff,0
+ .byte 0,0x9f,0xcf,0
+
+ .org gdt + PHYSICAL_DS
+physical_ds:
+ /* 32 bit protected mode data segment, physical addresses */
+ .word 0xffff,0
+ .byte 0,0x93,0xcf,0
+
+ .org gdt + VIRTUAL_CS
+virtual_cs:
+ /* 32 bit protected mode code segment, virtual addresses */
+ .word 0xffff,0
+ .byte 0,0x9f,0xcf,0
+
+ .org gdt + VIRTUAL_DS
+virtual_ds:
+ /* 32 bit protected mode data segment, virtual addresses */
+ .word 0xffff,0
+ .byte 0,0x93,0xcf,0
+
+#ifdef CONFIG_X86_64
+
+ .org gdt + LONG_CS
+long_cs:
+ /* 64bit long mode code segment, base 0 */
+ .word 0xffff, 0
+ .byte 0x00, 0x9f, 0xaf , 0x00
+
+ .org gdt + LONG_DS
+long_ds:
+ /* 64bit long mode data segment, base 0 */
+ .word 0xffff, 0
+ .byte 0x00, 0x93, 0xcf, 0x00
+
+#endif /* CONFIG_X86_64 */
+
+gdt_end:
+ .equ gdt_length, gdt_end - gdt
+
+ /* The virtual address offset */
+ .globl virt_offset
+virt_offset: .long 0
+
+ .text
+ .code32
+
+/****************************************************************************
+ * run_here (flat physical addressing, position-independent)
+ *
+ * Set up a GDT to run Etherboot at the current location with virtual
+ * addressing. This call does not switch to virtual addresses or move
+ * the stack pointer. The GDT will be located within the copy of
+ * Etherboot. All registers are preserved.
+ *
+ * This gets called at startup and at any subsequent relocation of
+ * Etherboot.
+ *
+ * Parameters: none
+ ****************************************************************************
+ */
+ .globl run_here
+run_here:
+ /* Preserve registers */
+ pushl %eax
+ pushl %ebp
+
+ /* Find out where we're running */
+ call 1f
+1: popl %ebp
+ subl $1b, %ebp
+
+ /* Store as virt_offset */
+ movl %ebp, virt_offset(%ebp)
+
+ /* Set segment base addresses in GDT */
+ leal virtual_cs(%ebp), %eax
+ pushl %eax
+ pushl %ebp
+ call set_seg_base
+ popl %eax /* discard */
+ popl %eax /* discard */
+
+ /* Set physical location of GDT */
+ leal gdt(%ebp), %eax
+ movl %eax, gdt_addr(%ebp)
+
+ /* Load the new GDT */
+ lgdt gdt(%ebp)
+
+ /* Reload new flat physical segment registers */
+ movl $PHYSICAL_DS, %eax
+ movl %eax, %ds
+ movl %eax, %es
+ movl %eax, %fs
+ movl %eax, %gs
+ movl %eax, %ss
+
+ /* Restore registers, convert return address to far return
+ * address.
+ */
+ popl %ebp
+ movl $PHYSICAL_CS, %eax
+ xchgl %eax, 4(%esp) /* cs now on stack, ret offset now in eax */
+ xchgl %eax, 0(%esp) /* ret offset now on stack, eax restored */
+
+ /* Return to caller, reloading %cs with new value */
+ lret
+
+/****************************************************************************
+ * set_seg_base (any addressing, position-independent)
+ *
+ * Set the base address of a pair of segments in the GDT. This relies
+ * on the layout of the GDT being (CS,DS) pairs.
+ *
+ * Parameters:
+ * uint32_t base_address
+ * struct gdt_entry * code_segment
+ * Returns:
+ * none
+ ****************************************************************************
+ */
+ .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
+
+/****************************************************************************
+ * _virt_to_phys (virtual addressing)
+ *
+ * Switch from virtual to flat physical addresses. %esp is adjusted
+ * to a physical value. Segment registers are set to flat physical
+ * selectors. All other registers are preserved. Flags are
+ * preserved.
+ *
+ * Parameters: none
+ * Returns: none
+ ****************************************************************************
+ */
+ .globl _virt_to_phys
+_virt_to_phys:
+ /* Preserve registers and flags */
+ pushfl
+ pushl %eax
+ pushl %ebp
+
+ /* Change return address to a physical address */
+ movl virt_offset, %ebp
+ addl %ebp, 12(%esp)
+
+ /* Switch to physical code segment */
+ pushl $PHYSICAL_CS
+ leal 1f(%ebp), %eax
+ pushl %eax
+ lret
+1:
+ /* Reload other segment registers and adjust %esp */
+ movl $PHYSICAL_DS, %eax
+ movl %eax, %ds
+ movl %eax, %es
+ movl %eax, %fs
+ movl %eax, %gs
+ movl %eax, %ss
+ addl %ebp, %esp
+
+ /* Restore registers and flags, and return */
+ popl %ebp
+ popl %eax
+ popfl
+ ret
+
+/****************************************************************************
+ * _phys_to_virt (flat physical addressing)
+ *
+ * Switch from flat physical to virtual addresses. %esp is adjusted
+ * to a virtual value. Segment registers are set to virtual
+ * selectors. All other registers are preserved. Flags are
+ * preserved.
+ *
+ * Note that this depends on the GDT already being correctly set up
+ * (e.g. by a call to run_here()).
+ *
+ * Parameters: none
+ * Returns: none
+ ****************************************************************************
+ */
+ .globl _phys_to_virt
+_phys_to_virt:
+ /* Preserve registers and flags */
+ pushfl
+ pushl %eax
+ pushl %ebp
+
+ /* Switch to virtual code segment */
+ ljmp $VIRTUAL_CS, $1f
+1:
+ /* Reload data segment registers */
+ movl $VIRTUAL_DS, %eax
+ movl %eax, %ds
+ movl %eax, %es
+ movl %eax, %fs
+ movl %eax, %gs
+
+ /* Reload stack segment and adjust %esp */
+ movl virt_offset, %ebp
+ movl %eax, %ss
+ subl %ebp, %esp
+
+ /* Change the return address to a virtual address */
+ subl %ebp, 12(%esp)
+
+ /* Restore registers and flags, and return */
+ popl %ebp
+ popl %eax
+ popfl
+ ret
+
+/****************************************************************************
+ * relocate_to (virtual addressing)
+ *
+ * Relocate Etherboot to the specified address. The runtime image
+ * (excluding the prefix, decompressor and compressed image) is copied
+ * to a new location, and execution continues in the new copy. This
+ * routine is designed to be called from C code.
+ *
+ * Parameters:
+ * uint32_t new_phys_addr
+ ****************************************************************************
+ */
+ .globl relocate_to
+relocate_to:
+ /* Save the callee save registers */
+ pushl %ebp
+ pushl %esi
+ pushl %edi
+
+ /* Compute the physical source address and data length */
+ movl $_text, %esi
+ movl $_end, %ecx
+ subl %esi, %ecx
+ addl virt_offset, %esi
+
+ /* Compute the physical destination address */
+ movl 16(%esp), %edi
+
+ /* Switch to flat physical addressing */
+ call _virt_to_phys
+
+ /* Do the copy */
+ cld
+ rep movsb
+
+ /* Calculate offset to new image */
+ subl %esi, %edi
+
+ /* Switch to executing in new image */
+ call 1f
+1: popl %ebp
+ leal (2f-1b)(%ebp,%edi), %eax
+ jmpl *%eax
+2:
+ /* Switch to stack in new image */
+ addl %edi, %esp
+
+ /* Call run_here() to set up GDT */
+ call run_here
+
+ /* Switch to virtual addressing */
+ call _phys_to_virt
+
+ /* Restore the callee save registers */
+ popl %edi
+ popl %esi
+ popl %ebp
+
+ /* return */
+ ret
diff --git a/src/arch/i386/drivers/net/undi.c b/src/arch/i386/drivers/net/undi.c
index 084fc18a..7e3e7df8 100644
--- a/src/arch/i386/drivers/net/undi.c
+++ b/src/arch/i386/drivers/net/undi.c
@@ -1436,7 +1436,7 @@ static int undi_isa_probe ( struct dev *dev,
* this list.
*/
static struct pci_id undi_nics[] = {
- /* PCI_ROM(0x0000, 0x0000, "undi", "UNDI adaptor"), */
+ PCI_ROM(0x0000, 0x0000, "undi", "UNDI driver support"),
};
static struct pci_driver undi_driver __pci_driver = {
diff --git a/src/arch/i386/firmware/pcbios/bios.c b/src/arch/i386/firmware/pcbios/bios.c
index 70f26703..fcbe7f32 100644
--- a/src/arch/i386/firmware/pcbios/bios.c
+++ b/src/arch/i386/firmware/pcbios/bios.c
@@ -3,11 +3,11 @@
* Body of routines taken from old pcbios.S
*/
-#ifdef PCBIOS
-
-#include "etherboot.h"
+#include "stdint.h"
#include "realmode.h"
-#include "segoff.h"
+#include "compiler.h"
+
+#define BIOS_DATA_SEG 0x0040
#define CF ( 1 << 0 )
@@ -23,133 +23,101 @@ timeofday BIOS interrupt.
#define CONFIG_BIOS_CURRTICKS 1
#endif
#if defined(CONFIG_BIOS_CURRTICKS)
-unsigned long currticks (void)
-{
+unsigned long currticks ( void ) {
static uint32_t days = 0;
- uint32_t *ticks = VIRTUAL(0x0040,0x006c);
- uint8_t *midnight = VIRTUAL(0x0040,0x0070);
+ uint32_t ticks;
+ uint8_t midnight;
/* 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;
+ REAL_EXEC ( rm_currticks,
+ "sti\n\t"
+ "nop\n\t"
+ "nop\n\t"
+ "cli\n\t",
+ 0,
+ OUT_CONSTRAINTS (),
+ IN_CONSTRAINTS (),
+ CLOBBER ( "eax" ) ); /* can't have an empty clobber list */
+
+ get_real ( ticks, BIOS_DATA_SEG, 0x006c );
+ get_real ( midnight, BIOS_DATA_SEG, 0x0070 );
+
+ if ( midnight ) {
+ midnight = 0;
+ put_real ( midnight, BIOS_DATA_SEG, 0x0070 );
days += 0x1800b0;
}
- return ( days + *ticks );
+ 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 );
+void cpu_nap ( void ) {
+ REAL_EXEC ( rm_cpu_nap,
+ "sti\n\t"
+ "hlt\n\t"
+ "cli\n\t",
+ 0,
+ OUT_CONSTRAINTS (),
+ IN_CONSTRAINTS (),
+ CLOBBER ( "eax" ) ); /* can't have an empty clobber list */
}
-#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 );
+void disk_init ( void ) {
+ REAL_EXEC ( rm_disk_init,
+ "sti\n\t"
+ "xorw %ax,%ax\n\t"
+ "movb $0x80,%dl\n\t"
+ "int $0x13\n\t"
+ "cli\n\t",
+ 0,
+ OUT_CONSTRAINTS (),
+ IN_CONSTRAINTS (),
+ CLOBBER ( "eax", "ebx", "ecx", "edx",
+ "ebp", "esi", "edi" ) );
}
/**************************************************************************
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;
+ char *fixme_buf ) {
+ uint16_t ax, flags, discard_c, discard_d;
+ segoff_t buf = SEGOFF ( fixme_buf );
+
+ /* FIXME: buf should be passed in as a segoff_t rather than a
+ * char *
+ */
- 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"
+ REAL_EXEC ( rm_pcbios_disk_read,
+ "sti\n\t"
+ "pushl %%ebx\n\t" /* Convert %ebx to %es:bx */
+ "popl %%bx\n\t"
+ "popl %%es\n\t"
+ "movb $0x02, %%ah\n\t" /* INT 13,2 - Read disk sector */
+ "movb $0x01, %%al\n\t" /* Read one sector */
+ "int $0x13\n\t"
+ "pushfw\n\t"
+ "popw %%bx\n\t"
+ "cli\n\t",
+ 4,
+ OUT_CONSTRAINTS ( "=a" ( ax ), "=b" ( flags ),
+ "=c" ( discard_c ), "=d" ( discard_d ) ),
+ IN_CONSTRAINTS ( "c" ( ( ( cylinder & 0xff ) << 8 ) |
+ ( ( cylinder >> 8 ) & 0x3 ) |
+ sector ),
+ "d" ( ( head << 8 ) | drive ),
+ "b" ( buf ) ),
+ CLOBBER ( "ebp", "esi", "edi" ) );
);
- 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;
+ return ( flags & CF ) ? ax : 0;
}
#endif /* TRY_FLOPPY_FIRST */
-
-#endif /* PCBIOS */
diff --git a/src/arch/i386/firmware/pcbios/bios_console.c b/src/arch/i386/firmware/pcbios/bios_console.c
new file mode 100644
index 00000000..b0bee1ab
--- /dev/null
+++ b/src/arch/i386/firmware/pcbios/bios_console.c
@@ -0,0 +1,76 @@
+/* Etherboot routines for PCBIOS firmware.
+ *
+ * Body of routines taken from old pcbios.S
+ */
+
+#include "compiler.h"
+#include "realmode.h"
+#include "console.h"
+
+#define ZF ( 1 << 6 )
+
+/**************************************************************************
+bios_putchar - Print a character on console
+**************************************************************************/
+static void bios_putchar ( int character ) {
+ REAL_EXEC ( rm_console_putc,
+ "sti\n\t"
+ "movb $0x0e, %%ah\n\t"
+ "movl $1, %%ebx\n\t"
+ "int $0x10\n\t"
+ "cli\n\t",
+ 1,
+ OUT_CONSTRAINTS ( "=a" ( character ) ),
+ IN_CONSTRAINTS ( "a" ( character ) ),
+ CLOBBER ( "ebx", "ecx", "edx", "ebp", "esi", "edi" ) );
+
+ /* NOTE: %eax may be clobbered, so must be specified as an output
+ * parameter, even though we don't then do anything with it.
+ */
+}
+
+/**************************************************************************
+bios_getchar - Get a character from console
+**************************************************************************/
+static int bios_getchar ( void ) {
+ uint16_t character;
+
+ REAL_EXEC ( rm_console_getc,
+ "sti\n\t"
+ "xorw %%ax, %%ax\n\t"
+ "int $0x16\n\t"
+ "cli\n\t",
+ 1,
+ OUT_CONSTRAINTS ( "=a" ( character ) ),
+ IN_CONSTRAINTS (),
+ CLOBBER ( "ebx", "ecx", "edx", "ebp", "esi", "edi" ) );
+
+ return ( character & 0xff );
+}
+
+/**************************************************************************
+bios_iskey - Check for keyboard interrupt
+**************************************************************************/
+static int bios_iskey ( void ) {
+ uint16_t flags;
+
+ REAL_EXEC ( 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",
+ 1,
+ OUT_CONSTRAINTS ( "=a" ( flags ) ),
+ IN_CONSTRAINTS (),
+ CLOBBER ( "ebx", "ecx", "edx", "ebp", "esi", "edi" ) );
+
+ return ( ( flags & ZF ) == 0 );
+}
+
+static struct console_driver bios_console __console_driver = {
+ .putchar = bios_putchar,
+ .getchar = bios_getchar,
+ .iskey = bios_iskey,
+};
diff --git a/src/arch/i386/firmware/pcbios/console.c b/src/arch/i386/firmware/pcbios/console.c
deleted file mode 100644
index f994f06e..00000000
--- a/src/arch/i386/firmware/pcbios/console.c
+++ /dev/null
@@ -1,85 +0,0 @@
-/* Etherboot routines for PCBIOS firmware.
- *
- * Body of routines taken from old pcbios.S
- */
-
-#ifdef PCBIOS
-
-#include "etherboot.h"
-#include "realmode.h"
-#include "segoff.h"
-
-#define ZF ( 1 << 6 )
-
-/**************************************************************************
-CONSOLE_PUTC - Print a character on console
-**************************************************************************/
-void console_putc ( int character )
-{
- struct {
- reg16_t ax;
- } PACKED in_stack;
-
- RM_FRAGMENT(rm_console_putc,
- "sti\n\t"
- "popw %ax\n\t"
- "movb $0x0e, %ah\n\t"
- "movl $1, %ebx\n\t"
- "int $0x10\n\t"
- "cli\n\t"
- );
-
- in_stack.ax.l = character;
- real_call ( rm_console_putc, &in_stack, NULL );
-}
-
-/**************************************************************************
-CONSOLE_GETC - Get a character from console
-**************************************************************************/
-int console_getc ( void )
-{
- RM_FRAGMENT(rm_console_getc,
- "sti\n\t"
- "xorw %ax, %ax\n\t"
- "int $0x16\n\t"
- "xorb %ah, %ah\n\t"
- "cli\n\t"
- );
-
- return real_call ( rm_console_getc, NULL, NULL );
-}
-
-/**************************************************************************
-CONSOLE_ISCHAR - Check for keyboard interrupt
-**************************************************************************/
-int console_ischar ( void )
-{
- RM_FRAGMENT(rm_console_ischar,
- "sti\n\t"
- "movb $1, %ah\n\t"
- "int $0x16\n\t"
- "pushfw\n\t"
- "popw %ax\n\t"
- "cli\n\t"
- );
-
- return ( ( real_call ( rm_console_ischar, NULL, NULL ) & ZF ) == 0 );
-}
-
-/**************************************************************************
-GETSHIFT - Get keyboard shift state
-**************************************************************************/
-int getshift ( void )
-{
- RM_FRAGMENT(rm_getshift,
- "sti\n\t"
- "movb $2, %ah\n\t"
- "int $0x16\n\t"
- "andw $0x3, %ax\n\t"
- "cli\n\t"
- );
-
- return real_call ( rm_getshift, NULL, NULL );
-}
-
-#endif /* PCBIOS */
diff --git a/src/arch/i386/firmware/pcbios/gateA20.c b/src/arch/i386/firmware/pcbios/gateA20.c
new file mode 100644
index 00000000..bbee376d
--- /dev/null
+++ b/src/arch/i386/firmware/pcbios/gateA20.c
@@ -0,0 +1,90 @@
+#include "realmode.h"
+#include "timer.h"
+#include "latch.h"
+#include "bios.h"
+
+#define K_RDWR 0x60 /* keyboard data & cmds (read/write) */
+#define K_STATUS 0x64 /* keyboard status */
+#define K_CMD 0x64 /* keybd ctlr command (write-only) */
+
+#define K_OBUF_FUL 0x01 /* output buffer full */
+#define K_IBUF_FUL 0x02 /* input buffer full */
+
+#define KC_CMD_WIN 0xd0 /* read output port */
+#define KC_CMD_WOUT 0xd1 /* write output port */
+#define KB_SET_A20 0xdf /* enable A20,
+ enable output buffer full interrupt
+ enable data line
+ disable clock line */
+#define KB_UNSET_A20 0xdd /* enable A20,
+ enable output buffer full interrupt
+ enable data line
+ disable clock line */
+
+enum { Disable_A20 = 0x2400, Enable_A20 = 0x2401, Query_A20_Status = 0x2402,
+ Query_A20_Support = 0x2403 };
+
+#define CF ( 1 << 0 )
+
+#ifndef IBM_L40
+static void empty_8042 ( void )
+{
+ unsigned long time;
+ char st;
+
+ time = currticks() + TICKS_PER_SEC; /* max wait of 1 second */
+ while ((((st = inb(K_CMD)) & K_OBUF_FUL) ||
+ (st & K_IBUF_FUL)) &&
+ currticks() < time)
+ inb(K_RDWR);
+}
+#endif /* IBM_L40 */
+
+/*
+ * Gate A20 for high memory
+ *
+ * Note that this function gets called as part of the return path from
+ * librm's real_call, which is used to make the int15 call if librm is
+ * being used. To avoid an infinite recursion, we make gateA20_set
+ * return immediately if it is already part of the call stack.
+ */
+void gateA20_set ( void ) {
+ static char reentry_guard = 0;
+ uint16_t flags, status;
+
+ if ( reentry_guard )
+ return;
+ reentry_guard = 1;
+
+ REAL_EXEC ( rm_enableA20,
+ "sti\n\t"
+ "stc\n\t"
+ "int $0x15\n\t"
+ "pushfw\n\t"
+ "popw %%bx\n\t"
+ "cli\n\t",
+ 2,
+ OUT_CONSTRAINTS ( "=a" ( status ), "=b" ( flags ) ),
+ IN_CONSTRAINTS ( "a" ( Enable_A20 ) ),
+ CLOBBER ( "ecx", "edx", "ebp", "esi", "edi" ) );
+
+ if ( ( flags & CF ) ||
+ ( ( status >> 8 ) & 0xff ) ) {
+ /* INT 15 method failed, try alternatives */
+#ifdef IBM_L40
+ outb(0x2, 0x92);
+#else /* IBM_L40 */
+ empty_8042();
+ outb(KC_CMD_WOUT, K_CMD);
+ empty_8042();
+ outb(KB_SET_A20, K_RDWR);
+ empty_8042();
+#endif /* IBM_L40 */
+ }
+
+ reentry_guard = 0;
+}
+
+void gateA20_unset ( void ) {
+ /* Not currently implemented */
+}
diff --git a/src/arch/i386/firmware/pcbios/memsizes.c b/src/arch/i386/firmware/pcbios/memsizes.c
index 18ae86da..3284e1ef 100644
--- a/src/arch/i386/firmware/pcbios/memsizes.c
+++ b/src/arch/i386/firmware/pcbios/memsizes.c
@@ -1,7 +1,8 @@
-#ifdef PCBIOS
-
-#include "etherboot.h"
+#include "stdint.h"
+#include "stddef.h"
#include "realmode.h"
+#include "init.h"
+#include "memsizes.h"
#define CF ( 1 << 0 )
@@ -16,109 +17,131 @@ 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 );
+static unsigned short basememsize ( void ) {
+ uint16_t int12_basememsize, fbms_basememsize;
+
+ /* There are two methods for retrieving the base memory size:
+ * INT 12 and the BIOS FBMS counter at 40:13. We read both
+ * and use the smaller value, to be paranoid.
+ */
+
+ REAL_EXEC ( rm_basememsize,
+ "int $0x12\n\t",
+ 1,
+ OUT_CONSTRAINTS ( "=a" ( int12_basememsize ) ),
+ IN_CONSTRAINTS (),
+ CLOBBER ( "ebx", "ecx", "edx", "ebp", "esi", "edi" ) );
+
+ get_real ( fbms_basememsize, 0x40, 0x13 );
+
+ return ( int12_basememsize < fbms_basememsize ?
+ int12_basememsize : fbms_basememsize );
}
/**************************************************************************
-MEMSIZE - Determine size of extended memory
+MEMSIZE - Determine size of extended memory, in kB
**************************************************************************/
-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;
+static unsigned int memsize ( void ) {
+ uint16_t extmem_1m_to_16m_k, extmem_16m_plus_64k;
+ uint16_t confmem_1m_to_16m_k, confmem_16m_plus_64k;
+ uint16_t flags;
int memsize;
- RM_FRAGMENT(rm_memsize,
- /* Some buggy BIOSes don't clear/set carry on pass/error of
+ /* Try INT 15,e801 first
+ *
+ * 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;
+ REAL_EXEC ( rm_mem_e801,
+ "stc\n\t"
+ "int $0x15\n\t"
+ "pushfw\n\t" /* flags -> %di */
+ "popw %%di\n\t",
+ 5,
+ OUT_CONSTRAINTS ( "=a" ( extmem_1m_to_16m_k ),
+ "=b" ( extmem_16m_plus_64k ),
+ "=c" ( confmem_1m_to_16m_k ),
+ "=d" ( confmem_16m_plus_64k ),
+ "=D" ( flags ) ),
+ IN_CONSTRAINTS ( "a" ( 0xe801 ),
+ "c" ( 0 ),
+ "d" ( 0 ) ),
+ CLOBBER ( "ebp", "esi" ) );
+
+ if ( ! ( flags & CF ) ) {
+ /* INT 15,e801 succeeded */
+ if ( confmem_1m_to_16m_k || confmem_16m_plus_64k ) {
+ /* Use confmem (cx,dx) values */
+ memsize = confmem_1m_to_16m_k +
+ ( confmem_16m_plus_64k << 6 );
} else {
- /* Use cx,dx */
- memsize = ( out_stack.dx.word<<6 ) + out_stack.cx.word;
+ /* Use extmem (ax,bx) values */
+ memsize = extmem_1m_to_16m_k +
+ ( extmem_16m_plus_64k << 6 );
}
+ } else {
+ /* INT 15,e801 failed; fall back to INT 15,88
+ *
+ * CF is apparently unreliable and should be ignored.
+ */
+ REAL_EXEC ( rm_mem_88,
+ "int $0x15\n\t",
+ 1,
+ OUT_CONSTRAINTS ( "=a" ( extmem_1m_to_16m_k ) ),
+ IN_CONSTRAINTS ( "a" ( 0x88 << 8 ) ),
+ CLOBBER ( "ebx", "ecx", "edx",
+ "ebp", "esi", "edi" ) );
+ memsize = extmem_1m_to_16m_k;
}
+
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;
- }
+/**************************************************************************
+MEME820 - Retrieve the E820 BIOS memory map
+**************************************************************************/
+#define SMAP ( 0x534d4150 ) /* "SMAP" */
+static int meme820 ( struct e820entry *buf, int count ) {
+ int index;
+ uint16_t basemem_entry;
+ uint32_t smap, next;
+ uint16_t flags;
+ uint32_t discard_c, discard_d;
+
+ index = 0;
+ next = 0;
+ do {
+ basemem_entry = BASEMEM_PARAMETER_INIT ( buf[index] );
+ REAL_EXEC ( rm_mem_e820,
+ "int $0x15\n\t"
+ "pushfw\n\t" /* flags -> %di */
+ "popw %%di\n\t",
+ 5,
+ OUT_CONSTRAINTS ( "=a" ( smap ),
+ "=b" ( next ),
+ "=c" ( discard_c ),
+ "=d" ( discard_d ),
+ "=D" ( flags ) ),
+ IN_CONSTRAINTS ( "a" ( 0xe820 ),
+ "b" ( next ),
+ "c" ( sizeof (struct e820entry) ),
+ "d" ( SMAP ),
+ "D" ( basemem_entry ) ),
+ CLOBBER ( "ebp", "esi" ) );
+ BASEMEM_PARAMETER_DONE ( buf[index] );
+ if ( smap != SMAP ) return 0;
+ if ( flags & CF ) break;
+ index++;
+ } while ( ( index < count ) && ( next != 0 ) );
+
return index;
}
-void get_memsizes(void)
-{
+/**************************************************************************
+GET_MEMSIZES - Retrieve the system memory map via any available means
+**************************************************************************/
+void get_memsizes ( void ) {
/* Ensure we don't stomp bios data structutres.
* the interrupt table: 0x000 - 0x3ff
* the bios data area: 0x400 - 0x502
@@ -127,16 +150,19 @@ void get_memsizes(void)
static const unsigned min_addr = 0x600;
unsigned i;
unsigned basemem;
- basemem = get_free_base_memory();
+
+ /* Retrieve memory information from the BIOS */
meminfo.basememsize = basememsize();
+ basemem = meminfo.basememsize << 10;
meminfo.memsize = memsize();
#ifndef IGNORE_E820_MAP
- meminfo.map_count = meme820(meminfo.map, E820MAX);
+ 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 */
+
+ /* If we don't have an e820 memory map fake it */
+ if ( meminfo.map_count == 0 ) {
meminfo.map_count = 2;
meminfo.map[0].addr = 0;
meminfo.map[0].size = meminfo.basememsize << 10;
@@ -145,57 +171,56 @@ void get_memsizes(void)
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) {
+ 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) {
+ if ( meminfo.map[i].addr < min_addr ) {
unsigned long delta;
delta = min_addr - meminfo.map[i].addr;
- if (delta > meminfo.map[i].size) {
+ 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
+
+ /* 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;
+ 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++) {
+ 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
+ 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 );
}
-}
#endif
+
}
-#endif /* PCBIOS */
+INIT_FN ( INIT_MEMSIZES, get_memsizes, NULL, NULL );
diff --git a/src/arch/i386/include/bios.h b/src/arch/i386/include/bios.h
new file mode 100644
index 00000000..afc648ba
--- /dev/null
+++ b/src/arch/i386/include/bios.h
@@ -0,0 +1,10 @@
+#ifndef BIOS_H
+#define BIOS_H
+
+extern unsigned long currticks ( void );
+extern void cpu_nap ( void );
+extern void disk_init ( void );
+extern unsigned int pcbios_disk_read ( int drive, int cylinder, int head,
+ int sector, char *fixme_buf );
+
+#endif /* BIOS_H */
diff --git a/src/arch/i386/include/bochs.h b/src/arch/i386/include/bochs.h
new file mode 100644
index 00000000..73f43c36
--- /dev/null
+++ b/src/arch/i386/include/bochs.h
@@ -0,0 +1,27 @@
+#ifndef BOCHS_H
+#define BOCHS_H
+
+/*
+ * This file defines "bochsbp", the magic breakpoint instruction that
+ * is incredibly useful when debugging under bochs.
+ *
+ */
+
+#ifdef ASSEMBLY
+
+/* Breakpoint for when debugging under bochs */
+#define bochsbp xchgw %bx, %bx
+#define BOCHSBP bochsbp
+
+#else /* ASSEMBLY */
+
+/* Breakpoint for when debugging under bochs */
+static inline void bochsbp ( void ) {
+ __asm__ __volatile__ ( "xchgw %bx, %bx" );
+}
+
+#endif /* ASSEMBLY */
+
+#warning "bochs.h should not be included into production code"
+
+#endif /* BOCHS_H */
diff --git a/src/arch/i386/include/hidemem.h b/src/arch/i386/include/hidemem.h
index 600a8692..71b0905f 100644
--- a/src/arch/i386/include/hidemem.h
+++ b/src/arch/i386/include/hidemem.h
@@ -1,7 +1,7 @@
#ifndef HIDEMEM_H
#define HIDEMEM_H
-#include "segoff.h"
+#include "realmode.h"
extern int install_e820mangler ( void *new_mangler );
extern int hide_etherboot ( void );
diff --git a/src/arch/i386/include/hooks.h b/src/arch/i386/include/hooks.h
index 0764edaf..a4e4b397 100644
--- a/src/arch/i386/include/hooks.h
+++ b/src/arch/i386/include/hooks.h
@@ -1,9 +1,14 @@
-#ifndef ETHERBOOT_I386_HOOKS_H
-#define ETHERBOOT_I386_HOOKS_H
+#ifndef HOOKS_H
+#define 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 );
+/* in hooks.o */
+extern void arch_initialise ( struct i386_all_regs *regs,
+ void (*retaddr) (void) );
+extern void arch_main ( struct i386_all_regs *regs );
-#endif /* ETHERBOOT_I386_HOOKS_H */
+/* in hooks_rm.o */
+extern void arch_rm_initialise ( struct i386_all_regs *regs,
+ void (*retaddr) (void) );
+extern void arch_rm_main ( struct i386_all_regs *regs );
+
+#endif /* HOOKS_H */
diff --git a/src/arch/i386/include/io.h b/src/arch/i386/include/io.h
index e351a0c1..5b6a6243 100644
--- a/src/arch/i386/include/io.h
+++ b/src/arch/i386/include/io.h
@@ -1,22 +1,8 @@
#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);
-}
+#include "compiler.h"
+#include "virtaddr.h"
/* virt_to_bus converts an addresss inside of etherboot [_start, _end]
* into a memory access cards can use.
diff --git a/src/arch/i386/include/kir.h b/src/arch/i386/include/kir.h
new file mode 100644
index 00000000..84633d26
--- /dev/null
+++ b/src/arch/i386/include/kir.h
@@ -0,0 +1,18 @@
+#ifndef KIR_H
+#define KIR_H
+
+#ifndef KEEP_IT_REAL
+#error "kir.h can be used only with -DKEEP_IT_REAL"
+#endif
+
+#ifdef ASSEMBLY
+
+#define code32 code16gcc
+
+#else /* ASSEMBLY */
+
+__asm__ ( ".code16gcc" );
+
+#endif /* ASSEMBLY */
+
+#endif /* KIR_H */
diff --git a/src/arch/i386/include/libkir.h b/src/arch/i386/include/libkir.h
new file mode 100644
index 00000000..5bac42d8
--- /dev/null
+++ b/src/arch/i386/include/libkir.h
@@ -0,0 +1,184 @@
+#ifndef LIBKIR_H
+#define LIBKIR_H
+
+#include "realmode.h"
+
+#ifndef ASSEMBLY
+
+/*
+ * Full API documentation for these functions is in realmode.h.
+ *
+ */
+
+/* Copy to/from base memory */
+
+static inline void copy_to_real_libkir ( uint16_t dest_seg, uint16_t dest_off,
+ void *src, size_t n ) {
+ __asm__ ( "movw %4, %%es\n\t"
+ "cld\n\t"
+ "rep movsb\n\t"
+ "pushw %%ds\n\t" /* restore %es */
+ "popw %%es\n\t"
+ : "=S" ( src ), "=D" ( dest_off ), "=c" ( n ) /* clobbered */
+ : "S" ( src ), "r" ( dest_seg ), "D" ( dest_off ), "c" ( n )
+ : "memory"
+ );
+}
+
+static inline void copy_from_real_libkir ( void *dest,
+ uint16_t src_seg, uint16_t src_off,
+ size_t n ) {
+ __asm__ ( "movw %%ax, %%ds\n\t"
+ "cld\n\t"
+ "rep movsb\n\t"
+ "pushw %%es\n\t" /* restore %ds */
+ "popw %%ds\n\t"
+ : "=S" ( src_off ), "=D" ( dest ), "=c" ( n ) /* clobbered */
+ : "a" ( src_seg ), "S" ( src_off ), "D" ( dest ), "c" ( n )
+ : "memory"
+ );
+}
+#define copy_to_real copy_to_real_libkir
+#define copy_from_real copy_from_real_libkir
+
+/*
+ * Transfer individual values to/from base memory. There may well be
+ * a neater way to do this. We have two versions: one for constant
+ * offsets (where the mov instruction must be of the form "mov
+ * %es:123, %xx") and one for non-constant offsets (where the mov
+ * instruction must be of the form "mov %es:(%xx), %yx". If it's
+ * possible to incorporate both forms into one __asm__ instruction, I
+ * don't know how to do it.
+ *
+ * Ideally, the mov instruction should be "mov%z0"; the "%z0" is meant
+ * to expand to either "b", "w" or "l" depending on the size of
+ * operand 0. This would remove the (minor) ambiguity in the mov
+ * instruction. However, gcc on at least my system barfs with an
+ * "internal compiler error" when confronted with %z0.
+ *
+ */
+
+#define put_real_kir_const_off( var, seg, off ) \
+ __asm__ ( "movw %w1, %%es\n\t" \
+ "mov %0, %%es:%c2\n\t" \
+ "pushw %%ds\n\t" /* restore %es */ \
+ "popw %%es\n\t" \
+ : \
+ : "r,r" ( var ), "rm,rm" ( seg ), "i,!r" ( off ) \
+ )
+
+#define put_real_kir_nonconst_off( var, seg, off ) \
+ __asm__ ( "movw %w1, %%es\n\t" \
+ "mov %0, %%es:(%2)\n\t" \
+ "pushw %%ds\n\t" /* restore %es */ \
+ "popw %%es\n\t" \
+ : \
+ : "r" ( var ), "rm" ( seg ), "r" ( off ) \
+ )
+
+#define put_real_kir( var, seg, off ) \
+ do { \
+ if ( __builtin_constant_p ( off ) ) \
+ put_real_kir_const_off ( var, seg, off ); \
+ else \
+ put_real_kir_nonconst_off ( var, seg, off ); \
+ } while ( 0 )
+
+#define get_real_kir_const_off( var, seg, off ) \
+ __asm__ ( "movw %w1, %%es\n\t" \
+ "mov %%es:%c2, %0\n\t" \
+ "pushw %%ds\n\t" /* restore %es */ \
+ "popw %%es\n\t" \
+ : "=r,r" ( var ) \
+ : "rm,rm" ( seg ), "i,!r" ( off ) \
+ )
+
+#define get_real_kir_nonconst_off( var, seg, off ) \
+ __asm__ ( "movw %w1, %%es\n\t" \
+ "mov %%es:(%2), %0\n\t" \
+ "pushw %%ds\n\t" /* restore %es */ \
+ "popw %%es\n\t" \
+ : "=r" ( var ) \
+ : "rm" ( seg ), "r" ( off ) \
+ )
+
+#define get_real_kir( var, seg, off ) \
+ do { \
+ if ( __builtin_constant_p ( off ) ) \
+ get_real_kir_const_off ( var, seg, off ); \
+ else \
+ get_real_kir_nonconst_off ( var, seg, off ); \
+ } while ( 0 )
+
+#define put_real put_real_kir
+#define get_real get_real_kir
+
+/* Place/remove parameter on real-mode stack in a way that's
+ * compatible with libkir
+ */
+#define BASEMEM_PARAMETER_INIT_LIBKIR( param ) \
+ ( ( uint16_t ) ( ( uint32_t ) & ( param ) ) )
+#define BASEMEM_PARAMETER_DONE_LIBKIR( param )
+#define BASEMEM_PARAMETER_INIT BASEMEM_PARAMETER_INIT_LIBKIR
+#define BASEMEM_PARAMETER_DONE BASEMEM_PARAMETER_DONE_LIBKIR
+
+/* REAL_CALL: call an external real-mode routine */
+#define OUT_CONSTRAINTS(...) __VA_ARGS__
+#define IN_CONSTRAINTS(...) "m" ( __routine ), ## __VA_ARGS__
+#define CLOBBER(...) __VA_ARGS__
+#define REAL_CALL( routine, num_out_constraints, out_constraints, \
+ in_constraints, clobber ) \
+ do { \
+ segoff_t __routine = routine; \
+ __asm__ __volatile__ ( \
+ "pushl %" #num_out_constraints "\n\t" \
+ ".code16\n\t" \
+ "pushw %%gs\n\t" /* preserve segs */ \
+ "pushw %%fs\n\t" \
+ "pushw %%es\n\t" \
+ "pushw %%ds\n\t" \
+ "pushw %%cs\n\t" /* lcall to routine */ \
+ "call 1f\n\t" \
+ "jmp 2f\n\t" \
+ "\n1:\n\t" \
+ "addr32 pushl 12(%%esp)\n\t" \
+ "lret\n\t" \
+ "\n2:\n\t" \
+ "popw %%ds\n\t" /* restore segs */ \
+ "popw %%es\n\t" \
+ "popw %%fs\n\t" \
+ "popw %%gs\n\t" \
+ "addw $4, %%sp\n\t" \
+ ".code16gcc\n\t" \
+ : out_constraints : in_constraints : clobber \
+ ); \
+ } while ( 0 )
+
+/* REAL_EXEC: execute some inline assembly code in a way that matches
+ * the interface of librm
+ */
+
+#define IN_CONSTRAINTS_NO_ROUTINE( routine, ... ) __VA_ARGS__
+#define REAL_EXEC( name, asm_code_str, num_out_constraints, out_constraints, \
+ in_constraints, clobber ) \
+ __asm__ __volatile__ ( \
+ ".code16\n\t" \
+ "pushw %%gs\n\t" \
+ "pushw %%fs\n\t" \
+ "pushw %%es\n\t" \
+ "pushw %%ds\n\t" \
+ "\n" #name ":\n\t" \
+ asm_code_str \
+ "popw %%ds\n\t" \
+ "popw %%es\n\t" \
+ "popw %%fs\n\t" \
+ "popw %%gs\n\t" \
+ ".code16gcc\n\t" \
+ : out_constraints \
+ : IN_CONSTRAINTS_NO_ROUTINE ( in_constraints ) \
+ : clobber \
+ );
+
+#endif /* ASSEMBLY */
+
+#endif /* LIBKIR_H */
diff --git a/src/arch/i386/include/librm.h b/src/arch/i386/include/librm.h
new file mode 100644
index 00000000..16bfc089
--- /dev/null
+++ b/src/arch/i386/include/librm.h
@@ -0,0 +1,186 @@
+#ifndef LIBRM_H
+#define LIBRM_H
+
+/* Drag in protected-mode segment selector values */
+#include "virtaddr.h"
+#include "realmode.h"
+
+#ifndef ASSEMBLY
+
+#include "stddef.h"
+#include "string.h"
+
+/*
+ * Data structures and type definitions
+ *
+ */
+
+/* Real-mode call parameter block, as passed to real_call */
+struct real_call_params {
+ struct i386_seg_regs;
+ struct i386_regs;
+ segoff_t rm_code;
+ segoff_t reserved;
+} PACKED;
+
+/* Current location of librm in base memory */
+extern char *installed_librm;
+
+/* Start and size of our source copy of librm (i.e. the one that we
+ * can install by copying it to base memory and setting
+ * installed_librm)
+ */
+extern char librm[];
+extern size_t _librm_size[];
+
+/* Linker symbols for offsets within librm. Other symbols should
+ * almost certainly not be referred to from C code.
+ */
+extern void (*_real_to_prot[]) ( void );
+extern void (*_prot_to_real[]) ( void );
+extern void (*_prot_call[]) ( void );
+extern void (*_real_call[]) ( void );
+extern segoff_t _rm_stack[];
+extern uint32_t _pm_stack[];
+extern char _librm_ref_count[];
+
+/* Symbols within current installation of librm */
+#define LIBRM_VAR( sym ) \
+ ( * ( ( typeof ( * _ ## sym ) * ) \
+ & ( installed_librm [ (int) _ ## sym ] ) ) )
+#define LIBRM_FN( sym ) \
+ ( ( typeof ( * _ ## sym ) ) \
+ & ( installed_librm [ (int) _ ## sym ] ) )
+#define LIBRM_CONSTANT( sym ) \
+ ( ( typeof ( * _ ## sym ) ) ( _ ## sym ) )
+#define inst_real_to_prot LIBRM_FN ( real_to_prot )
+#define inst_prot_to_real LIBRM_FN ( prot_to_real )
+#define inst_prot_call LIBRM_FN ( prot_call )
+#define inst_real_call LIBRM_FN ( real_call )
+#define inst_rm_stack LIBRM_VAR ( rm_stack )
+#define inst_pm_stack LIBRM_VAR ( pm_stack )
+#define inst_librm_ref_count LIBRM_VAR ( librm_ref_count )
+#define librm_size LIBRM_CONSTANT ( librm_size )
+
+/* Functions that librm expects to be able to link to. Included here
+ * so that the compiler will catch prototype mismatches.
+ */
+extern void _phys_to_virt ( void );
+extern void _virt_to_phys ( void );
+extern void gateA20_set ( void );
+
+/*
+ * librm_mgmt: functions for manipulating base memory and executing
+ * real-mode code.
+ *
+ * Full API documentation for these functions is in realmode.h.
+ *
+ */
+
+/* Macro for obtaining a physical address from a segment:offset pair. */
+#define VIRTUAL(x,y) ( phys_to_virt ( ( ( x ) << 4 ) + ( y ) ) )
+
+/* Copy to/from base memory */
+static inline void copy_to_real_librm ( uint16_t dest_seg, uint16_t dest_off,
+ void *src, size_t n ) {
+ memcpy ( VIRTUAL ( dest_seg, dest_off ), src, n );
+}
+static inline void copy_from_real_librm ( void *dest,
+ uint16_t src_seg, uint16_t src_off,
+ size_t n ) {
+ memcpy ( dest, VIRTUAL ( src_seg, src_off ), n );
+}
+#define put_real_librm( var, dest_seg, dest_off ) \
+ do { \
+ * ( ( typeof(var) * ) VIRTUAL ( dest_seg, dest_off ) ) = var; \
+ } while ( 0 )
+#define get_real_librm( var, src_seg, src_off ) \
+ do { \
+ var = * ( ( typeof(var) * ) VIRTUAL ( src_seg, src_off ) ); \
+ } while ( 0 )
+#define copy_to_real copy_to_real_librm
+#define copy_from_real copy_from_real_librm
+#define put_real put_real_librm
+#define get_real get_real_librm
+
+/* Copy to/from real-mode stack */
+extern uint16_t copy_to_rm_stack ( void *data, size_t size );
+extern void remove_from_rm_stack ( void *data, size_t size );
+
+/* Place/remove parameter on real-mode stack in a way that's
+ * compatible with libkir
+ */
+#define BASEMEM_PARAMETER_INIT_LIBRM( param ) \
+ copy_to_rm_stack ( & ( param ), sizeof ( param ) )
+#define BASEMEM_PARAMETER_DONE_LIBRM( param ) \
+ remove_from_rm_stack ( & ( param ), sizeof ( param ) )
+#define BASEMEM_PARAMETER_INIT BASEMEM_PARAMETER_INIT_LIBRM
+#define BASEMEM_PARAMETER_DONE BASEMEM_PARAMETER_DONE_LIBRM
+
+/* REAL_FRAGMENT: Declare and define a real-mode code fragment in .text16 */
+#define REAL_FRAGMENT( name, asm_code_str ) \
+ extern void name ( void ); \
+ extern char name ## _size[]; \
+ __asm__ __volatile__ ( \
+ ".section \".text16\"\n\t" \
+ ".code16\n\t" \
+ ".arch i386\n\t" \
+ #name ":\n\t" \
+ asm_code_str "\n\t" \
+ "lret\n\t" \
+ #name "_end:\n\t" \
+ ".equ " #name "_size, " #name "_end - " #name "\n\t" \
+ ".code32\n\t" \
+ ".previous\n\t" \
+ : : \
+ )
+#define FRAGMENT_SIZE( fragment ) ( (size_t) fragment ## _size )
+
+/* REAL_CALL: call a real-mode routine via librm */
+#define OUT_CONSTRAINTS(...) __VA_ARGS__
+#define IN_CONSTRAINTS(...) "m" ( __routine ), ## __VA_ARGS__
+#define CLOBBER(...) __VA_ARGS__
+#define REAL_CALL( routine, num_out_constraints, out_constraints, \
+ in_constraints, clobber ) \
+ do { \
+ segoff_t __routine = routine; \
+ __asm__ __volatile__ ( \
+ "pushl %" #num_out_constraints "\n\t" \
+ "call 1f\n\t" \
+ "jmp 2f\n\t" \
+ "\n1:\n\t" \
+ "pushl installed_librm\n\t" \
+ "addl $_real_call, 0(%%esp)\n\t" \
+ "ret\n\t" \
+ "\n2:\n\t" \
+ "addl $4, %%esp\n\t" \
+ : out_constraints \
+ : in_constraints \
+ : clobber \
+ ); \
+ } while ( 0 )
+
+/* REAL_EXEC: combine RM_FRAGMENT and REAL_CALL into one handy unit */
+#define PASSTHRU(...) __VA_ARGS__
+#define REAL_EXEC( name, asm_code_str, num_out_constraints, out_constraints, \
+ in_constraints, clobber ) \
+ do { \
+ segoff_t fragment; \
+ \
+ REAL_FRAGMENT ( name, asm_code_str ); \
+ \
+ fragment.segment = inst_rm_stack.segment; \
+ fragment.offset = \
+ copy_to_rm_stack ( name, FRAGMENT_SIZE ( name ) ); \
+ \
+ REAL_CALL ( fragment, num_out_constraints, \
+ PASSTHRU ( out_constraints ), \
+ PASSTHRU ( in_constraints ), \
+ PASSTHRU ( clobber ) ); \
+ \
+ remove_from_rm_stack ( NULL, FRAGMENT_SIZE ( name ) ); \
+ } while ( 0 )
+
+#endif /* ASSEMBLY */
+
+#endif /* LIBRM_H */
diff --git a/src/arch/i386/include/memsizes.h b/src/arch/i386/include/memsizes.h
new file mode 100644
index 00000000..a5bb3f2d
--- /dev/null
+++ b/src/arch/i386/include/memsizes.h
@@ -0,0 +1,34 @@
+#ifndef MEMSIZES_H
+#define MEMSIZES_H
+
+/*
+ * These structures seem to be very i386 (and, in fact, PCBIOS)
+ * specific, so I've moved them out of etherboot.h.
+ *
+ */
+
+struct e820entry {
+ uint64_t addr;
+ uint64_t size;
+ uint32_t type;
+#define E820_RAM 1
+#define E820_RESERVED 2
+#define E820_ACPI 3 /* usable as RAM once ACPI tables have been read */
+#define E820_NVS 4
+} __attribute__ (( packed ));
+#define E820ENTRY_SIZE 20
+#define E820MAX 32
+
+struct meminfo {
+ uint16_t basememsize;
+ uint16_t pad;
+ uint32_t memsize;
+ uint32_t map_count;
+ struct e820entry map[E820MAX];
+} __attribute__ (( packed ));
+
+extern struct meminfo meminfo;
+
+extern void get_memsizes ( void );
+
+#endif /* MEMSIZES_H */
diff --git a/src/arch/i386/include/pic8259.h b/src/arch/i386/include/pic8259.h
index 694e9d13..d3f9b879 100644
--- a/src/arch/i386/include/pic8259.h
+++ b/src/arch/i386/include/pic8259.h
@@ -8,7 +8,7 @@
#define PIC8259_H
/* For segoff_t */
-#include "segoff.h"
+#include "realmode.h"
#define IRQ_PIC_CUTOFF (8)
@@ -90,7 +90,7 @@ void dump_irq_status ( void );
* handler code, so we put prototypes and the size macro here.
*/
extern void _trivial_irq_handler ( void );
-extern void _trivial_irq_handler_end ( void );
+extern char _trivial_irq_handler_size[];
#define TRIVIAL_IRQ_HANDLER_SIZE FRAGMENT_SIZE(_trivial_irq_handler)
#endif /* PIC8259_H */
diff --git a/src/arch/i386/include/pxe_callbacks.h b/src/arch/i386/include/pxe_callbacks.h
index 9b941931..cf5a7a87 100644
--- a/src/arch/i386/include/pxe_callbacks.h
+++ b/src/arch/i386/include/pxe_callbacks.h
@@ -5,7 +5,6 @@
#define PXE_CALLBACKS_H
#include "etherboot.h"
-#include "segoff.h"
#include "pxe.h"
typedef struct {
diff --git a/src/arch/i386/include/pxe_types.h b/src/arch/i386/include/pxe_types.h
index 7b093e6a..45736a2b 100644
--- a/src/arch/i386/include/pxe_types.h
+++ b/src/arch/i386/include/pxe_types.h
@@ -10,7 +10,7 @@
/* SEGOFF16_t defined in separate header
*/
-#include "segoff.h"
+#include "realmode.h"
typedef segoff_t I386_SEGOFF16_t;
#define SEGOFF16_t I386_SEGOFF16_t
diff --git a/src/arch/i386/include/realmode.h b/src/arch/i386/include/realmode.h
index eca92b9b..d641869f 100644
--- a/src/arch/i386/include/realmode.h
+++ b/src/arch/i386/include/realmode.h
@@ -1,124 +1,124 @@
-/* Real-mode interface
- */
+#ifndef REALMODE_H
+#define REALMODE_H
#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.
+#include "stdint.h"
+#include "compiler.h"
+#include "registers.h"
+#include "io.h"
+
+/*
+ * Data structures and type definitions
+ *
*/
-#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.
+
+/* All i386 registers, as passed in by prot_call or kir_call */
+struct real_mode_regs {
+ struct i386_all_regs;
+} PACKED;
+
+/* Segment:offset structure. Note that the order within the structure
+ * is offset:segment.
*/
typedef struct {
- uint32_t esp;
- uint16_t cs;
- uint16_t ss;
- uint32_t r2p_params;
-} PACKED prot_to_real_params_t;
+ uint16_t offset;
+ uint16_t segment;
+} segoff_t PACKED;
-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
+/* Macro hackery needed to stringify bits of inline assembly */
+#define RM_XSTR(x) #x
+#define RM_STR(x) RM_XSTR(x)
+
+/* Drag in the selected real-mode transition library header */
+#ifdef KEEP_IT_REAL
+#include "libkir.h"
+#else
+#include "librm.h"
+#endif
+
+/*
+ * The API to some functions is identical between librm and libkir, so
+ * they are documented here, even though the prototypes are in librm.h
+ * and libkir.h.
+ *
*/
-#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
+
+/*
+ * void copy_to_real ( uint16_t dest_seg, uint16_t dest_off,
+ * void *src, size_t n )
+ * void copy_from_real ( void *dest, uint16_t src_seg, uint16_t src_off,
+ * size_t n )
+ *
+ * These functions can be used to copy data to and from arbitrary
+ * locations in base memory.
*/
-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
+
+/*
+ * put_real ( variable, uint16_t dest_seg, uint16_t dest_off )
+ * get_real ( variable, uint16_t src_seg, uint16_t src_off )
+ *
+ * These macros can be used to read or write single variables to and
+ * from arbitrary locations in base memory. "variable" must be a
+ * variable of either 1, 2 or 4 bytes in length.
*/
-extern uint32_t real_mode_stack;
-extern size_t real_mode_stack_size;
+
+/*
+ * REAL_CALL ( routine, num_out_constraints, out_constraints,
+ * in_constraints, clobber )
+ * REAL_EXEC ( name, asm_code_str, num_out_constraints, out_constraints,
+ * in_constraints, clobber )
+ *
+ * If you have a pre-existing real-mode routine that you want to make
+ * a far call to, use REAL_CALL. If you have a code fragment that you
+ * want to copy down to base memory, execute, and then remove, use
+ * REAL_EXEC.
+ *
+ * out_constraints must be of the form OUT_CONSTRAINTS(constraints),
+ * and in_constraints must be of the form IN_CONSTRAINTS(constraints),
+ * where "constraints" is a constraints list as would be used in an
+ * inline __asm__()
+ *
+ * clobber must be of the form CLOBBER ( clobber_list ), where
+ * "clobber_list" is a clobber list as would be used in an inline
+ * __asm__().
+ *
+ * These are best illustrated by example. To write a character to the
+ * console using INT 10, you would do something like:
+ *
+ * REAL_EXEC ( rm_test_librm,
+ * "int $0x10",
+ * 1,
+ * OUT_CONSTRAINTS ( "=a" ( discard ) ),
+ * IN_CONSTRAINTS ( "a" ( 0x0e00 + character ),
+ * "b" ( 1 ) ),
+ * CLOBBER ( "ebx", "ecx", "edx", "ebp", "esi", "edi" ) );
+ *
+ * IMPORTANT: gcc does not automatically assume that input operands
+ * get clobbered. The only way to specify that an input operand may
+ * be modified is to also specify it as an output operand; hence the
+ * "(discard)" in the above code.
+ */
+
+#warning "realmode.h contains placeholders for obsolete macros"
+
+
+/* Just for now */
+#define SEGMENT(x) ( virt_to_phys ( x ) >> 4 )
+#define OFFSET(x) ( virt_to_phys ( x ) & 0xf )
+#define SEGOFF(x) { OFFSET(x), SEGMENT(x) }
+
+/* To make basemem.c compile */
extern int lock_real_mode_stack;
+extern char *real_mode_stack;
+extern char real_mode_stack_size[];
+
+#define RM_FRAGMENT(name,asm) \
+ void name ( void ) {} \
+ extern char name ## _size[];
-/* 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 */
+
+#endif /* REALMODE_H */
diff --git a/src/arch/i386/include/registers.h b/src/arch/i386/include/registers.h
new file mode 100644
index 00000000..2e78d418
--- /dev/null
+++ b/src/arch/i386/include/registers.h
@@ -0,0 +1,93 @@
+#ifndef REGISTERS_H
+#define REGISTERS_H
+
+#include "stdint.h"
+#include "compiler.h"
+
+/* Basic 16-bit and 32-bit register types */
+typedef union {
+ struct {
+ union {
+ uint8_t l;
+ uint8_t byte;
+ };
+ uint8_t h;
+ } PACKED;
+ uint16_t word;
+} PACKED reg16_t;
+
+typedef union {
+ reg16_t;
+ uint32_t dword;
+} PACKED reg32_t;
+
+/* As created by pushal / read by popal */
+struct i386_regs {
+ 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;
+ };
+} PACKED;
+
+/* Our pushal/popal equivalent for segment registers */
+struct i386_seg_regs {
+ uint16_t cs;
+ uint16_t ss;
+ uint16_t ds;
+ uint16_t es;
+ uint16_t fs;
+ uint16_t gs;
+} PACKED;
+
+/* All i386 registers, as passed in by prot_call or kir_call */
+struct i386_all_regs {
+ struct i386_seg_regs;
+ struct i386_regs;
+ uint32_t i386_flags;
+} PACKED;
+
+#endif /* REGISTERS_H */
diff --git a/src/arch/i386/include/segoff.h b/src/arch/i386/include/segoff.h
deleted file mode 100644
index 822ddd33..00000000
--- a/src/arch/i386/include/segoff.h
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- * Segment:offset types and macros
- *
- * Initially written by Michael Brown (mcb30).
- */
-
-#ifndef SEGOFF_H
-#define SEGOFF_H
-
-#include <stdint.h>
-#include <osdep.h>
-#include <io.h>
-
-/* Segment:offset structure. Note that the order within the structure
- * is offset:segment.
- */
-typedef struct {
- uint16_t offset;
- uint16_t segment;
-} segoff_t;
-
-/* Macros for converting from virtual to segment:offset addresses,
- * when we don't actually care which of the many isomorphic results we
- * get.
- */
-#ifdef DEBUG_SEGMENT
-uint16_t SEGMENT ( const void * const ptr ) {
- uint32_t phys = virt_to_phys ( ptr );
- if ( phys > 0xfffff ) {
- printf ( "FATAL ERROR: segment address out of range\n" );
- }
- return phys >> 4;
-}
-#else
-#define SEGMENT(x) ( virt_to_phys ( x ) >> 4 )
-#endif
-#define OFFSET(x) ( virt_to_phys ( x ) & 0xf )
-#define SEGOFF(x) { OFFSET(x), SEGMENT(x) }
-#define VIRTUAL(x,y) ( phys_to_virt ( ( ( x ) << 4 ) + ( y ) ) )
-
-#endif /* SEGOFF_H */
diff --git a/src/arch/i386/include/virtaddr.h b/src/arch/i386/include/virtaddr.h
new file mode 100644
index 00000000..15fad1cc
--- /dev/null
+++ b/src/arch/i386/include/virtaddr.h
@@ -0,0 +1,85 @@
+#ifndef VIRTADDR_H
+#define VIRTADDR_H
+
+/* Segment selectors as used in our protected-mode GDTs.
+ *
+ * Don't change these unless you really know what you're doing.
+ */
+#define PHYSICAL_CS 0x08
+#define PHYSICAL_DS 0x10
+#define VIRTUAL_CS 0x18
+#define VIRTUAL_DS 0x20
+#define LONG_CS 0x28
+#define LONG_DS 0x30
+
+#ifndef ASSEMBLY
+
+#include "stdint.h"
+
+#ifndef KEEP_IT_REAL
+
+/*
+ * Without -DKEEP_IT_REAL, we are in 32-bit protected mode with a
+ * fixed link address but an unknown physical start address. Our GDT
+ * sets up code and data segments with an offset of virt_offset, so
+ * that link-time addresses can still work.
+ *
+ */
+
+/* C-callable function prototypes */
+
+extern void relocate_to ( uint32_t new_phys_addr );
+
+/* Variables in virtaddr.S */
+extern unsigned long virt_offset;
+
+/*
+ * Convert between virtual and physical addresses
+ *
+ */
+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 );
+}
+
+#else /* KEEP_IT_REAL */
+
+/*
+ * With -DKEEP_IT_REAL, we are in 16-bit real mode with fixed link
+ * addresses and a segmented memory model. We have separate code and
+ * data segments.
+ *
+ * Because we may be called in 16-bit protected mode (damn PXE spec),
+ * we cannot simply assume that physical = segment * 16 + offset.
+ * Instead, we have to look up the physical start address of the
+ * segment in the !PXE structure. We have to assume that
+ * virt_to_phys() is called only on pointers within the data segment,
+ * because nothing passes segment information to us.
+ *
+ * We don't implement phys_to_virt at all, because there will be many
+ * addresses that simply cannot be reached via a virtual address when
+ * the virtual address space is limited to 64kB!
+ */
+
+static inline unsigned long virt_to_phys ( volatile const void *virt_addr ) {
+ /* Cheat: just for now, do the segment*16+offset calculation */
+ uint16_t ds;
+
+ __asm__ ( "movw %%ds, %%ax" : "=a" ( ds ) : );
+ return ( 16 * ds + ( ( unsigned long ) virt_addr ) );
+}
+
+/* Define it as a deprecated function so that we get compile-time
+ * warnings, rather than just the link-time errors.
+ */
+extern void * phys_to_virt ( unsigned long phys_addr )
+ __attribute__ ((deprecated));
+
+#endif /* KEEP_IT_REAL */
+
+#endif /* ASSEMBLY */
+
+#endif /* VIRTADDR_H */
diff --git a/src/arch/i386/kir-Makefile b/src/arch/i386/kir-Makefile
new file mode 100644
index 00000000..cc37a91a
--- /dev/null
+++ b/src/arch/i386/kir-Makefile
@@ -0,0 +1,29 @@
+# Makefile to build a KEEP_IT_REAL flavour
+#
+# KEEP_IT_REAL, by its nature, requires a different build of every
+# single object file, since the inclusion of ".code16gcc" will
+# generate different machine code from the assembly. Unlike the other
+# config options, there is no way that this global dependency can ever
+# be reduced, so it makes sense to be able to build both the normal
+# and the KIR versions without having to force a full rebuild each
+# time.
+
+# Add this Makefile to MAKEDEPS
+#
+MAKEDEPS += arch/i386/kir-Makefile
+
+# Place binaries in bin-kir
+#
+BIN = bin-kir
+
+# Compile with -DKEEP_IT_REAL, forcibly include kir.h at the start of
+# each file to drag in ".code16gcc"
+#
+CFLAGS += -DKEEP_IT_REAL -include kir.h
+
+# Link with _data_link_addr = 0; data symbols are relative to the data
+# segment.
+#
+LDFLAGS += --defsym _data_link_addr=0
+
+include Makefile
diff --git a/src/arch/i386/prefix/floppyprefix.S b/src/arch/i386/prefix/dskprefix.S
index 18bed4c8..d1f64e7a 100644
--- a/src/arch/i386/prefix/floppyprefix.S
+++ b/src/arch/i386/prefix/dskprefix.S
@@ -1,3 +1,5 @@
+#include "compiler.h"
+
/* NOTE: this boot sector contains instructions that need at least an 80186.
* Yes, as86 has a bug somewhere in the valid instruction set checks.
*
@@ -115,7 +117,6 @@ got_sectors:
/* 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
@@ -130,8 +131,6 @@ got_sectors:
call print_nl
pop %es /* = SYSSEG */
- pop %es /* balance push/pop es */
-sigok:
/* Restore original disk parameters */
movw $0x78, %bx
@@ -142,20 +141,47 @@ sigok:
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
+ /* Everything now loaded. %es = SYSSEG, so %es:0000 points to
+ * start of loaded image.
+ */
+
+start_runtime:
+
+#ifdef COMPRESS
+ /* Decompress runtime image. %es:0000 points to decompressed
+ * image on exit.
+ */
+ lcall $SYSSEG, $decompress16
+#endif
+
+ /* Set up internal environment. Address of entry-point
+ * function is returned in %es:di.
+ */
+ pushw %es /* setup16 says %ds:0000 must point to image */
+ popw %ds
+ movw $setup16, %di
+ pushw %cs
+ call ljmp_to_es_di
+
+ /* Call initialisation routine. Relocation may be done. New
+ * address of entry-point function is returned in %es:di.
+ */
+ pushl $arch_rm_initialise
+ pushw %cs /* == lcall %es:di */
+ call ljmp_to_es_di
+
+ /* Call to arch_rm_main. Register INT19 as an exit path. This
+ * call will never return.
+ */
+ movl $exit_via_int19, %eax
+ pushl $arch_rm_main
+ pushl %eax /* Dummy return address */
+
+ /* Do the equivalent of ljmp *%es:di */
+ljmp_to_es_di:
+ pushw %es
+ pushw %di
+ lret
/* 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
@@ -164,7 +190,7 @@ prefix_exit_end:
* in: es - starting address segment (normally SYSSEG)
*/
read_it:
- movw $0,sread /* read whole image incl boot sector */
+ movw $1,sread /* don't reload the prefix */
movw %es,%ax
testw $0x0fff, %ax
die: jne die /* es must be at 64kB boundary */
diff --git a/src/arch/i386/prefix/int19exit.c b/src/arch/i386/prefix/int19exit.c
new file mode 100644
index 00000000..e7be0624
--- /dev/null
+++ b/src/arch/i386/prefix/int19exit.c
@@ -0,0 +1,13 @@
+#include "bochs.h"
+#include "realmode.h"
+
+/*
+ * The "exit via INT 19" exit path. INT 19 is the old (pre-BBS) "boot
+ * system" interrupt.
+ *
+ */
+
+void exit_via_int19 ( struct real_mode_regs *rm_regs ) {
+ bochsbp();
+ /* Placeholder */
+}
diff --git a/src/arch/i386/prefix/unnrv2b.S b/src/arch/i386/prefix/unnrv2b.S
index 1836fa71..50776dc9 100644
--- a/src/arch/i386/prefix/unnrv2b.S
+++ b/src/arch/i386/prefix/unnrv2b.S
@@ -15,115 +15,159 @@
* - Structure modified to be a subroutine call rather than an
* executable prefix.
* Michael Brown 30 Mar 2004
+ *
+ * - Modified to be compilable as either 16-bit or 32-bit code.
+ * Michael Brown 9 Mar 2005
*/
+/****************************************************************************
+ * This file provides the decompress_block() and decompress_block16()
+ * functions which can be called in order to decompress an image
+ * compressed with the nrv2b utility in src/util.
+ *
+ * These functions are designed to be called by the prefix. They are
+ * position-independent code.
+ *
+ * The same basic assembly code is used to compile both
+ * decompress_block() and decompress_block16().
+ ****************************************************************************
+ */
.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
+#ifdef CODE16
+/****************************************************************************
+ * decompress_block16 (real-mode near call, position independent)
+ *
+ * Parameters (passed via registers):
+ * %ds:%si - Pointer to compressed input data
+ * %es:%di - Pointer to output buffer
+ * Returns:
+ * All registers are preserved
+ *
+ * NOTE: The compressed data size must be in the range [1,65533-%si]
+ * and the uncompressed data size must be in the range [1,65536-%di]
+ * (where %si and %di are the input values for those registers). Note
+ * particularly that the lower limit is 1, not 0, and that the upper
+ * limit on the input (compressed) data really is 65533, since the
+ * algorithm may read up to three bytes beyond the end of the input
+ * data, since it reads dwords.
+ *
+ * Although splitting up the data into (almost) 64kB chunks for
+ * compression is awkward and worsens the compression ratio, it has
+ * little to no practical effect since our image size is currently
+ * <64kB for all single drivers. Having a decompression routine that
+ * can run in real-mode avoids the need to duplicate RM-to-PM
+ * transition code from librm (or have part of librm kept
+ * uncompressed, which is itself awkward) and means that we don't need
+ * to set up the PM stack until we hit the setup routine itself.
+ ****************************************************************************
+ */
+
+#define REG(x) x
- /* "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
+ .code16
+ .globl decompress_block16
+decompress_block16:
+
+#else /* CODE16 */
-/* ------------- DECOMPRESSION -------------
+/****************************************************************************
+ * decompress_block (32-bit protected-mode near call, position independent)
+ *
+ * Parameters (passed via registers):
+ * %ds:%esi - Pointer to compressed input data
+ * %es:%edi - Pointer to output buffer
+ * Returns:
+ * All registers are preserved
+ ****************************************************************************
+ */
- Input:
- %esi - source
- %edi - dest
- %ebp - -1
- cld
+#define REG(x) e ## x
+
+ .code32
+ .globl decompress_block
+decompress_block:
- Output:
- %eax - 0
- %ecx - 0
-*/
+#endif /* CODE16 */
-.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
+#define xAX REG(ax)
+#define xCX REG(cx)
+#define xBP REG(bp)
+#define xSI REG(si)
+#define xDI REG(di)
+ /* Save registers */
+ pushal
+ /* Do the decompression */
+ cld
+ xor %xBP, %xBP
+ dec %xBP /* last_m_off = -1 */
+ jmp dcl1_n2b
+
decompr_literals_n2b:
movsb
-
decompr_loop_n2b:
addl %ebx, %ebx
jnz dcl2_n2b
dcl1_n2b:
- getbit 32
+ call getbit32
dcl2_n2b:
jc decompr_literals_n2b
- xorl %eax, %eax
- incl %eax /* m_off = 1 */
+ xor %xAX, %xAX
+ inc %xAX /* m_off = 1 */
loop1_n2b:
- getbit 1
- adcl %eax, %eax /* m_off = m_off*2 + getbit() */
- getbit 1
+ call getbit1
+ adc %xAX, %xAX /* m_off = m_off*2 + getbit() */
+ call getbit1
jnc loop1_n2b /* while(!getbit()) */
- xorl %ecx, %ecx
- subl $3, %eax
+ sub $3, %xAX
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
+ shl $8, %xAX
+ movb (%xSI), %al /* m_off = (m_off - 3)*256 + src[ilen++] */
+ inc %xSI
+ not %xAX
jz decompr_end_n2b /* if (m_off == 0xffffffff) goto decomp_end_n2b */
- movl %eax, %ebp /* last_m_off = m_off ?*/
+ mov %xAX, %xBP /* 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()) */
+ xor %xCX, %xCX
+ call getbit1
+ adc %xCX, %xCX /* m_len = getbit() */
+ call getbit1
+ adc %xCX, %xCX /* m_len = m_len*2 + getbit()) */
jnz decompr_got_mlen_n2b /* if (m_len == 0) goto decompr_got_mlen_n2b */
- incl %ecx /* m_len++ */
+ inc %xCX /* m_len++ */
loop2_n2b:
- getbit 1
- adcl %ecx, %ecx /* m_len = m_len*2 + getbit() */
- getbit 1
+ call getbit1
+ adc %xCX, %xCX /* m_len = m_len*2 + getbit() */
+ call getbit1
jnc loop2_n2b /* while(!getbit()) */
- incl %ecx
- incl %ecx /* m_len += 2 */
+ inc %xCX
+ inc %xCX /* 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 */
+ cmp $-0xd00, %xBP
+ adc $1, %xCX /* m_len = m_len + 1 + (last_m_off > 0xd00) */
+ push %xSI
+ lea (%xBP,%xDI), %xSI /* m_pos = dst + olen + -m_off */
rep
- movsb /* dst[olen++] = *m_pos++ while(m_len > 0) */
- popl %esi
+ es movsb /* dst[olen++] = *m_pos++ while(m_len > 0) */
+ pop %xSI
jmp decompr_loop_n2b
+
+
+getbit1:
+ addl %ebx, %ebx
+ jnz 1f
+getbit32:
+ movl (%xSI), %ebx
+ sub $-4, %xSI /* sets carry flag */
+ adcl %ebx, %ebx
+1:
+ ret
+
decompr_end_n2b:
- /* Restore the initial register values */
+ /* Restore registers and return */
popal
ret
diff --git a/src/arch/i386/scripts/i386.lds b/src/arch/i386/scripts/i386.lds
new file mode 100644
index 00000000..4f9df561
--- /dev/null
+++ b/src/arch/i386/scripts/i386.lds
@@ -0,0 +1,263 @@
+/* -*- sh -*- */
+
+/*
+ * Linker script for i386 images
+ *
+ */
+
+OUTPUT_FORMAT ( "elf32-i386", "elf32-i386", "elf32-i386" )
+OUTPUT_ARCH ( i386 )
+ENTRY ( _entry )
+
+SECTIONS {
+
+ /* All sections in the resulting file have consecutive load
+ * addresses, but may have individual link addresses depending on
+ * the memory model being used.
+ *
+ * The linker symbols {prefix,decompress,text,data}_link_addr,
+ * load_addr, and _max_align may be specified explicitly. If not
+ * specified, they will default to:
+ *
+ * _prefix_link_addr = 0
+ * _decompress_link_addr = 0
+ * _text_link_addr = 0
+ * _data_link_addr = _text_link_addr + sizeof ( text sections )
+ * _load_addr = 0
+ * _max_align = 16
+ *
+ * We guarantee alignment of virtual addresses to any alignment
+ * specified by the constituent object files (e.g. via
+ * __attribute__((aligned(x)))). Load addresses are guaranteed
+ * only up to _max_align. Provided that all loader and relocation
+ * code honours _max_align, this means that physical addresses are
+ * also guaranteed up to _max_align.
+ *
+ * Note that when using -DKEEP_IT_REAL, the UNDI segments are only
+ * guaranteed to be loaded on a paragraph boundary (i.e. 16-byte
+ * alignment). Using _max_align>16 will therefore not guarantee
+ * >16-byte alignment of physical addresses when -DKEEP_IT_REAL is
+ * used (though virtual addresses will still be fully aligned).
+ *
+ * The real-mode prefixes rely on _text_link_addr and
+ * _decompress_link_addr being 0, since they issue far calls into
+ * those sections, thus requiring that symbol_value ==
+ * symbol_offset therein. Using the linker to calculate
+ * e.g. offset_setup16=setup16-_text will not work, since you then
+ * cannot use the reference from the prefix to setup16 to drag in
+ * setup16.o. Life is hard.
+ *
+ * If librm is included, then it must go at offset 0 within the
+ * text section. This is because librm is dual-usage: it is
+ * called from setup16 with %cs:0000 pointing to the start of the
+ * text section, and later it will be copied to base memory and
+ * called with %cs:0000 pointing to the start of librm.
+ *
+ * The decompressor is designed to decompress in-place. After
+ * calling the decompressor, the image will look exactly the same
+ * as the uncompressed image; the compressed data and the
+ * decompressor code itself will have been overwritten.
+ */
+
+ /*
+ * The prefix
+ */
+
+ _prefix_link_addr = DEFINED ( _prefix_link_addr ) ? _prefix_link_addr : 0;
+ . = _prefix_link_addr;
+ _prefix = .;
+
+ .prefix : AT ( _prefix_load_offset + __prefix ) {
+ __prefix = .;
+ _entry = .;
+ *(.prefix)
+ *(.prefix.*)
+ }
+
+ _eprefix = .;
+
+ /*
+ * The decompressor (may be absent)
+ */
+
+ _decompress_link_addr = DEFINED ( _decompress_link_addr ) ?
+ _decompress_link_addr : 0;
+ . = _decompress_link_addr;
+ _decompress = .;
+
+ .decompress : AT ( _decompress_load_offset + __decompress ) {
+ __decompress = .;
+ *(.decompress)
+ *(.decompress.*)
+ }
+
+ _edecompress = .;
+
+ /*
+ * The text sections
+ */
+
+ _text_link_addr = DEFINED ( _text_link_addr ) ? _text_link_addr : 0;
+ . = _text_link_addr;
+ _text = .;
+
+ .text16 : AT ( _text_load_offset + __text16 ) {
+ __text16 = .;
+
+ /* librm is a special case; it must go at the start of the
+ * text section if it is included.
+ */
+ _assert = ASSERT ( ( . == _text_link_addr ), "librm cannot go first" );
+ *(.librm)
+
+ *(.text16)
+ *(.text16.*)
+ } = 0x9090
+
+ .text : AT ( _text_load_offset + __text ) {
+ __text = .;
+ *(.text)
+ *(.text.*)
+ } = 0x9090
+
+ _etext = .;
+
+ /*
+ * The data sections
+ */
+
+ _data_link_addr = DEFINED ( _data_link_addr ) ? _data_link_addr : .;
+ . = _data_link_addr;
+ _data = .;
+
+ .rodata : AT ( _data_load_offset + __rodata ) {
+ __rodata = .;
+ *(.rodata)
+ *(.rodata.*)
+ }
+
+ .data : AT ( _data_load_offset + __data ) {
+ __data = .;
+ *(.data)
+ *(.data.*)
+ pci_drivers = .;
+ *(.drivers.pci)
+ pci_drivers_end = .;
+ isa_drivers = .;
+ *(.drivers.isa)
+ isa_drivers_end = .;
+ console_drivers = .;
+ *(.drivers.console)
+ console_drivers_end = .;
+ init_fns = .;
+ *(SORT(.init_fns.*))
+ init_fns_end = .;
+
+ _progbits_end = .;
+ }
+
+ .bss : AT ( _data_load_offset + __bss ) {
+ __bss = .;
+ _bss = .;
+ *(.bss)
+ *(.bss.*)
+ *(COMMON)
+ _ebss = .;
+ }
+
+ .stack : AT ( _data_load_offset + __stack ) {
+ __stack = .;
+ *(.stack)
+ *(.stack.*)
+ }
+
+ _edata = .;
+
+ _end = .;
+
+ /*
+ * Dispose of the comment and note sections to make the link map
+ * easier to read
+ */
+
+ /DISCARD/ : {
+ *(.comment)
+ *(.note)
+ }
+
+ /*
+ * Load address calculations. The slightly obscure nature of the
+ * calculations is because ALIGN(x) can only operate on the
+ * location counter.
+ */
+
+ _max_align = DEFINED ( _max_align ) ? _max_align : 16;
+ _load_addr = DEFINED ( _load_addr ) ? _load_addr : 0;
+
+ . = _load_addr;
+
+ . -= _prefix_link_addr;
+ _prefix_load_offset = ALIGN ( _max_align );
+ _prefix_load_addr = _prefix_link_addr + _prefix_load_offset;
+ _prefix_size = _eprefix - _prefix;
+ . = _prefix_load_addr + _prefix_size;
+
+ . -= _decompress_link_addr;
+ _decompress_load_offset = ALIGN ( _max_align );
+ _decompress_load_addr = _decompress_link_addr + _decompress_load_offset;
+ _decompress_size = _edecompress - _decompress;
+ . = _decompress_load_addr + _decompress_size;
+
+ . -= _text_link_addr;
+ _text_load_offset = ALIGN ( _max_align );
+ _text_load_addr = _text_link_addr + _text_load_offset;
+ _text_size = _etext - _text;
+ . = _text_load_addr + _text_size;
+
+ . -= _data_link_addr;
+ _data_load_offset = ALIGN ( _max_align );
+ _data_load_addr = _data_link_addr + _data_load_offset;
+ _data_size = _edata - _data;
+ . = _data_load_addr + _data_size;
+
+ /*
+ * Alignment checks. ALIGN() can only operate on the location
+ * counter, so we set the location counter to each value we want
+ * to check.
+ */
+
+ . = _prefix_load_addr - _prefix_link_addr;
+ _assert = ASSERT ( ( . == ALIGN ( _max_align ) ),
+ "_prefix is badly aligned" );
+
+ . = _decompress_load_addr - _prefix_link_addr;
+ _assert = ASSERT ( ( . == ALIGN ( _max_align ) ),
+ "_decompress is badly aligned" );
+
+ . = _text_load_addr - _text_link_addr;
+ _assert = ASSERT ( ( . == ALIGN ( _max_align ) ),
+ "_text is badly aligned" );
+
+ . = _data_load_addr - _data_link_addr;
+ _assert = ASSERT ( ( . == ALIGN ( _max_align ) ),
+ "_data is badly aligned" );
+
+ /*
+ * setup16 needs to know this when KEEP_IT_REAL is used. There
+ * are no harmful side-effects of calculating it all the time.
+ */
+ _text_load_size_pgh = ( _data_load_addr - _text_load_addr ) / 16 ;
+
+ /*
+ * Useful-to-know values.
+ */
+
+ /* Size of the decompressed runtime image */
+ _runtime_size = _edata - _text;
+ /* Size of the initialised-contents portion of the runtime image */
+ _runtime_progbits_size = _progbits_end - _text;
+ /* Size of the (non-compressed) binary file */
+ _file_size = _prefix_size + _runtime_progbits_size;
+ /* Size of the non-compressed portion of the compressed binary file */
+ _zfile_noncompressed_size = _prefix_size + _decompress_size;
+}
diff --git a/src/arch/i386/transitions/libkir.S b/src/arch/i386/transitions/libkir.S
new file mode 100644
index 00000000..79a0aa00
--- /dev/null
+++ b/src/arch/i386/transitions/libkir.S
@@ -0,0 +1,243 @@
+/*
+ * libkir: a transition library for -DKEEP_IT_REAL
+ *
+ * Michael Brown <mbrown@fensystems.co.uk>
+ *
+ */
+
+/****************************************************************************
+ * This file defines libkir: an interface between external and
+ * internal environments when -DKEEP_IT_REAL is used, so that both
+ * internal and external environments are in real mode. It deals with
+ * switching data segments and the stack. It provides the following
+ * functions:
+ *
+ * ext_to_kir & switch between external and internal (kir)
+ * kir_to_ext environments, preserving all non-segment
+ * registers
+ *
+ * kir_call issue a call to an internal routine from external
+ * code
+ *
+ * libkir is written to avoid assuming that segments are anything
+ * other than opaque data types, and also avoids assuming that the
+ * stack pointer is 16-bit. This should enable it to run just as well
+ * in 16:16 or 16:32 protected mode as in real mode.
+ ****************************************************************************
+ */
+
+/* Breakpoint for when debugging under bochs */
+#define BOCHSBP xchgw %bx, %bx
+
+ .text
+ .arch i386
+ .section ".text16", "awx", @progbits
+ .code16
+
+/****************************************************************************
+ * ext_to_kir (real-mode or 16:xx protected-mode near call)
+ *
+ * Switch from external stack and segment registers to internal stack
+ * and segment registers. %ss:sp is restored from the saved kir_ds
+ * and kir_sp. %ds, %es, %fs and %gs are all restored from the saved
+ * kir_ds. All other registers are preserved.
+ *
+ * %cs:0000 must point to the start of the runtime image code segment
+ * on entry.
+ *
+ * Note that this routine can be called *without* having first set up
+ * a stored kir_ds and kir_sp. If you do this, ext_to_kir will return
+ * without altering the segment registers or stack pointer.
+ *
+ * Parameters: none
+ ****************************************************************************
+ */
+
+ .globl ext_to_kir
+ext_to_kir:
+ /* Record external segment registers */
+ movw %ds, %cs:ext_ds
+ pushw %cs
+ popw %ds /* Set %ds = %cs for easier access to variables */
+ movw %es, %ds:ext_es
+ movw %fs, %ds:ext_fs
+ movw %gs, %ds:ext_fs
+
+ /* Preserve registers */
+ movw %ax, %ds:save_ax
+
+ /* Extract near return address from stack */
+ popw %ds:save_retaddr
+
+ /* Record external %ss:esp */
+ movw %ss, %ds:ext_ss
+ movl %esp, %ds:ext_esp
+
+ /* Load internal segment registers and stack pointer, if available */
+ movw %ds:kir_ds, %ax
+ testw %ax, %ax
+ jz 1f
+ movw %ax, %ss
+ movzwl %ds:kir_sp, %esp
+ movw %ax, %ds
+ movw %ax, %es
+ movw %ax, %fs
+ movw %ax, %gs
+1:
+
+ /* Place return address on new stack */
+ pushw %cs:save_retaddr
+
+ /* Restore registers and return */
+ movw %cs:save_ax, %ax
+ ret
+
+/****************************************************************************
+ * kir_to_ext (real-mode or 16:xx protected-mode near call)
+ *
+ * Switch from internal stack and segment registers to external stack
+ * and segment registers. %ss:%esp is restored from the saved ext_ss
+ * and ext_esp. Other segment registers are restored from the
+ * corresponding locations. All other registers are preserved.
+ *
+ * Note that it is actually %ss that is recorded as kir_ds, on the
+ * assumption that %ss == %ds when kir_to_ext is called.
+ *
+ * Parameters: none
+ ****************************************************************************
+ */
+
+ .globl kir_to_ext
+kir_to_ext:
+ /* Record near return address */
+ pushw %cs
+ popw %ds /* Set %ds = %cs for easier access to variables */
+ popw %ds:save_retaddr
+
+ /* Record internal segment registers and %sp */
+ movw %ss, %ds:kir_ds
+ movw %sp, %ds:kir_sp
+
+ /* Load external segment registers and stack pointer */
+ movw %ds:ext_ss, %ss
+ movl %ds:ext_esp, %esp
+ movw %ds:ext_gs, %gs
+ movw %ds:ext_fs, %fs
+ movw %ds:ext_es, %es
+ movw %ds:ext_ds, %ds
+
+ /* Return */
+ pushw %cs:save_retaddr
+ ret
+
+/****************************************************************************
+ * kir_call (real-mode or 16:xx protected-mode far call)
+ *
+ * Call a specific C function in the internal code. The prototype of
+ * the C function must be
+ * void function ( struct real_mode_regs *rm_regs );
+ * rm_regs will point to a struct containing the real-mode registers
+ * at entry to kir_call.
+ *
+ * All registers will be preserved across kir_call(), unless the C
+ * function explicitly overwrites values in rm_regs. Interrupt status
+ * will also be preserved.
+ *
+ * Parameters:
+ * function : (16-bit) virtual address of protected-mode function to call
+ *
+ * Example usage:
+ * pushw $pxe_api_call
+ * lcall $UNDI_CS, $kir_call
+ * addw $2, %sp
+ * to call in to the C function
+ * void pxe_api_call ( struct real_mode_regs *rm_regs );
+ ****************************************************************************
+ */
+
+ .globl kir_call
+kir_call:
+
+ /* Preserve flags. Must do this before any operation that may
+ * affect flags.
+ */
+ pushfl
+ popl %cs:save_flags
+
+ /* Disable interrupts. We do funny things with the stack, and
+ * we're not re-entrant.
+ */
+ cli
+
+ /* Extract address of internal routine from stack. We must do
+ * this without using (%bp), because we may be called with
+ * either a 16-bit or a 32-bit stack segment.
+ */
+ popl %cs:save_retaddr /* Scratch location */
+ popw %cs:save_function
+ subl $6, %esp /* Restore %esp */
+
+ /* Switch to internal stack. Note that the external stack is
+ * inaccessible once we're running internally (since we have
+ * no concept of 48-bit far pointers)
+ */
+ call ext_to_kir
+
+ /* Store external registers on internal stack */
+ pushl %cs:save_flags
+ pushal
+ pushl %cs:ext_fs_and_gs
+ pushl %cs:ext_ds_and_es
+ pushl %cs:ext_cs_and_ss
+
+ /* Push &rm_regs on stack and call function */
+ pushl %esp
+ data32 call *%cs:save_function
+ popl %eax /* discard */
+
+ /* Restore external registers from internal stack */
+ popl %cs:ext_cs_and_ss
+ popl %cs:ext_ds_and_es
+ popl %cs:ext_fs_and_gs
+ popal
+ popl %cs:save_flags
+
+ /* Switch to external stack */
+ call kir_to_ext
+
+ /* Restore flags */
+ pushl %cs:save_flags
+ popfl
+
+ /* Return */
+ lret
+
+/****************************************************************************
+ * Stored internal and external stack and segment registers
+ ****************************************************************************
+ */
+
+ext_cs_and_ss:
+ext_cs: .word 0
+ext_ss: .word 0
+ext_ds_and_es:
+ext_ds: .word 0
+ext_es: .word 0
+ext_fs_and_gs:
+ext_fs: .word 0
+ext_gs: .word 0
+ext_esp: .long 0
+
+ .globl kir_ds
+kir_ds: .word 0
+ .globl kir_sp
+kir_sp: .word 0
+
+/****************************************************************************
+ * Temporary variables
+ ****************************************************************************
+ */
+save_ax: .word 0
+save_retaddr: .word 0
+save_flags: .long 0
+save_function: .long 0
diff --git a/src/arch/i386/transitions/libpm.S b/src/arch/i386/transitions/libpm.S
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/src/arch/i386/transitions/libpm.S
diff --git a/src/arch/i386/transitions/librm.S b/src/arch/i386/transitions/librm.S
new file mode 100644
index 00000000..6dc6b4fd
--- /dev/null
+++ b/src/arch/i386/transitions/librm.S
@@ -0,0 +1,691 @@
+/*
+ * librm: a library for interfacing to real-mode code
+ *
+ * Michael Brown <mbrown@fensystems.co.uk>
+ *
+ */
+
+/* Drag in local definitions */
+#include "librm.h"
+
+/****************************************************************************
+ * This file defines librm: a block of code that is designed to reside
+ * permanently in base memory and provide the interface between
+ * real-mode code running in base memory and protected-mode code
+ * running in high memory. It provides the following functions:
+ *
+ * real_to_prot & switch between real and protected mode
+ * prot_to_real while running in base memory, preserving
+ * all non-segment registers
+ *
+ * real_call issue a call to a real-mode routine from
+ * protected-mode code running in high memory
+ *
+ * prot_call issue a call to a protected-mode routine from
+ * real-mode code running in base memory
+ *
+ * librm requires the following functions to be present in the
+ * protected-mode code:
+ *
+ * _phys_to_virt Switch from physical to virtual addressing. This
+ * routine must be position-independent and must
+ * *not* assume that it is genuinely running with
+ * flat physical addresses
+ *
+ * _virt_to_phys Switch from virtual to physical addresses.
+ *
+ * gateA20_set Enable the A20 line to permit access to the odd
+ * megabytes of RAM. (This function will be called
+ * with virtual addresses set up).
+ *
+ * librm needs to be linked against the protected-mode binary so that
+ * it can import the symbols for these functions.
+ *
+ * librm requires that the protected-mode code set up the following
+ * segments:
+ *
+ * PHYSICAL_CS 32-bit pmode code and data segments with flat
+ * PHYSICAL_DS physical addresses.
+ *
+ * VIRTUAL_CS 32-bit pmode code segment with virtual
+ * addressing, such that a protected-mode routine
+ * can always be found at $VIRTUAL_CS:routine.
+ *
+ * These segments must be set as #define constants when compiling
+ * librm. Edit librm.h to change the values.
+ *
+ * librm does not know the location of the code executing in high
+ * memory. It relies on the code running in high memory setting up a
+ * GDT such that the high-memory code is accessible at virtual
+ * addresses fixed at compile-time.
+ *
+ * librm symbols are exported as absolute values and represent offsets
+ * into librm. This is the most useful form of the symbols, since
+ * librm is basically a binary blob that you place somewhere in base
+ * memory.
+ *
+ * librm.h provides convenient ways to use these symbols: you simply
+ * set the pointer ( char * ) installed_librm to point to wherever
+ * librm is installed, and can then use e.g. inst_rm_stack just like
+ * any other variable and have it automatically refer to the value of
+ * rm_stack in the installed librm. Macro trickery makes this
+ * completely transparent, and the resulting assembler code is
+ * amazingly efficient.
+ *
+ * Note that librm must be called in genuine real mode, not 16:16 or
+ * 16:32 protected mode. It makes the assumption that
+ * physical_address = 16*segment+offset, and also that it can use
+ * OFFSET(%bp) to access stack variables. The former assumption will
+ * break in either protected mode, the latter may break in 16:32
+ * protected mode.
+ ****************************************************************************
+ */
+
+/*
+ * Default values for pmode segments if not defined
+ */
+#ifndef PHYSICAL_CS
+#warning "Assuming PHYSICAL_CS = 0x08"
+#define PHYSICAL_CS 0x08
+#endif
+#ifndef PHYSICAL_DS
+#warning "Assuming PHYSICAL_DS = 0x10"
+#define PHYSICAL_DS 0x10
+#endif
+#ifndef VIRTUAL_CS
+#warning "Assuming VIRTUAL_CS = 0x18"
+#define VIRTUAL_CS 0x18
+#endif
+
+/* For switches to/from protected mode */
+#define CR0_PE 1
+
+/* Size of various C data structures */
+#define SIZEOF_I386_SEG_REGS 12
+#define SIZEOF_I386_REGS 32
+#define SIZEOF_I386_ALL_REGS ( SIZEOF_I386_SEG_REGS + SIZEOF_I386_REGS )
+#define SIZEOF_I386_FLAGS 4
+#define SIZEOF_REAL_MODE_REGS ( SIZEOF_I386_ALL_REGS + SIZEOF_I386_FLAGS )
+#define SIZEOF_SEGOFF_T 4
+#define SIZEOF_REAL_CALL_PARAMS ( SIZEOF_I386_ALL_REGS + 2 * SIZEOF_SEGOFF_T )
+
+ .text
+ .arch i386
+ .section ".librm", "awx", @progbits
+ .align 16
+
+ .globl librm
+librm:
+
+_librm_start:
+
+#undef OFFSET
+#define OFFSET(sym) ( sym - _librm_start )
+
+#undef EXPORT
+#define EXPORT(sym) \
+ .globl sym ; \
+ .globl _ ## sym ; \
+ .equ _ ## sym, OFFSET(sym) ; \
+ sym
+
+/****************************************************************************
+ * GDT for initial transition to protected mode
+ *
+ * PHYSICAL_CS and PHYSICAL_DS are defined in an external header file.
+ * We use only those selectors, and construct our GDT to match the
+ * selector values we're asked to use. Use PHYSICAL_CS=0x08 and
+ * PHYSICAL_DS=0x10 to minimise the space occupied by this GDT.
+ *
+ * Note: pm_gdt is also used to store the location of the
+ * protected-mode GDT as recorded on entry to prot_to_real.
+ ****************************************************************************
+ */
+ .align 16
+pm_gdt:
+pm_gdt_limit: .word pm_gdt_length - 1
+pm_gdt_addr: .long 0
+ .word 0 /* padding */
+
+ .org pm_gdt + PHYSICAL_CS
+pm_gdt_pm_cs:
+ /* 32 bit protected mode code segment, physical addresses */
+ .word 0xffff, 0
+ .byte 0, 0x9f, 0xcf, 0
+
+ .org pm_gdt + PHYSICAL_DS
+pm_gdt_pm_ds:
+ /* 32 bit protected mode data segment, physical addresses */
+ .word 0xffff,0
+ .byte 0,0x93,0xcf,0
+
+pm_gdt_end:
+ .equ pm_gdt_length, pm_gdt_end - pm_gdt
+
+/****************************************************************************
+ * GDT for transition to real mode
+ *
+ * This is used primarily to set 64kB segment limits. Define
+ * FLATTEN_REAL_MODE if you want to use so-called "flat real mode"
+ * with 4GB limits instead. The base address of each of the segments
+ * will be adjusted at run-time.
+ *
+ * NOTE: This must be located before prot_to_real, otherwise gas
+ * throws a "can't handle non absolute segment in `ljmp'" error due to
+ * not knowing the value of RM_CS when the ljmp is encountered.
+ *
+ * Note also that putting ".word rm_gdt_end - rm_gdt - 1" directly
+ * into rm_gdt_limit, rather than going via rm_gdt_length, will also
+ * produce the "non absolute segment" error. This is most probably a
+ * bug in gas.
+ ****************************************************************************
+ */
+
+#ifdef FLATTEN_REAL_MODE
+#define RM_LIMIT_16_19__AVL__SIZE__GRANULARITY 0x8f
+#else
+#define RM_LIMIT_16_19__AVL__SIZE__GRANULARITY 0x00
+#endif
+ .align 16
+rm_gdt:
+rm_gdt_limit: .word rm_gdt_length - 1
+rm_gdt_base: .long 0
+ .word 0 /* padding */
+
+rm_gdt_rm_cs: /* 16 bit real mode code segment */
+ .equ RM_CS, rm_gdt_rm_cs - rm_gdt
+ .word 0xffff,(0&0xffff)
+ .byte (0>>16),0x9b,RM_LIMIT_16_19__AVL__SIZE__GRANULARITY,(0>>24)
+
+rm_gdt_rm_ds: /* 16 bit real mode data segment */
+ .equ RM_DS, rm_gdt_rm_ds - rm_gdt
+ .word 0xffff,(0&0xffff)
+ .byte (0>>16),0x93,RM_LIMIT_16_19__AVL__SIZE__GRANULARITY,(0>>24)
+
+rm_gdt_end:
+ .equ rm_gdt_length, rm_gdt_end - rm_gdt
+
+/****************************************************************************
+ * real_to_prot (real-mode far call)
+ *
+ * Switch from 16-bit real-mode to 32-bit protected mode with flat
+ * physical addresses. %esp is restored from the saved pm_esp. All
+ * segment registers are set to flat physical-mode values. All other
+ * registers are preserved. Interrupts are disabled.
+ *
+ * Note that this routine can be called *without* having first set up
+ * a stored pm_esp or stored GDT. If you do this, real_to_prot will
+ * return with a temporary stack that is only *FOUR BYTES* in size.
+ * This is just enough to enable you to do a "call 1f; popl %ebp"
+ * sequence in order to find out your physical address and then load a
+ * proper 32-bit protected-mode stack pointer. Do *NOT* use more than
+ * four bytes since this will overwrite code in librm!
+ *
+ * Parameters: none
+ ****************************************************************************
+ */
+
+ .code16
+EXPORT(real_to_prot):
+ /* Disable interrupts */
+ cli
+
+ /* Set %ds = %cs, for easier access to variables */
+ pushw %cs
+ popw %ds
+
+ /* Preserve registers */
+ movl %eax, %ds:OFFSET(save_eax)
+ movl %ebx, %ds:OFFSET(save_ebx)
+
+ /* Extract real-mode far return address from stack */
+ popl %ds:OFFSET(save_retaddr)
+
+ /* Record real-mode stack pointer */
+ movw %sp, %ds:OFFSET(rm_sp)
+ pushw %ss
+ popw %ds:OFFSET(rm_ss)
+
+ /* Physical base address of librm to %ebx */
+ xorl %ebx, %ebx
+ movw %cs, %bx
+ shll $4, %ebx
+
+ /* Check base address of stored protected-mode GDT. If it's
+ * zero, set it up to use our internal GDT (with physical
+ * segments only).
+ */
+ movl %ds:OFFSET(pm_gdt_addr), %eax
+ testl %eax, %eax
+ jnz 1f
+ /* Use internal GDT */
+ movl %ebx, %eax
+ addl $OFFSET(pm_gdt), %eax
+ movl %eax, %ds:OFFSET(pm_gdt_addr)
+1:
+
+ /* Set up protected-mode continuation address on real-mode stack */
+ pushl $PHYSICAL_CS
+ movl %ebx, %eax
+ addl $OFFSET(1f), %eax
+ pushl %eax
+
+ /* Restore protected-mode GDT */
+ lgdt %ds:OFFSET(pm_gdt)
+
+ /* Switch to protected mode */
+ movl %cr0, %eax
+ orb $CR0_PE, %al
+ movl %eax, %cr0
+
+ /* Flush prefetch queue and reload %cs:eip */
+ data32 lret
+1: .code32
+
+ /* Set up protected-mode stack and data segments */
+ movw $PHYSICAL_DS, %ax
+ movw %ax, %ds
+ movw %ax, %es
+ movw %ax, %fs
+ movw %ax, %gs
+ movw %ax, %ss
+
+ /* Switch to saved protected-mode stack. Note that there may
+ * not actually *be* a saved protected-mode stack.
+ */
+ movl OFFSET(pm_esp)(%ebx), %esp
+ testl %esp, %esp
+ jnz 1f
+ /* No stack - use save_retaddr as a 4-byte temporary stack */
+ leal OFFSET(save_retaddr+4)(%ebx), %esp
+1:
+
+ /* Convert real-mode far return address to physical address
+ * and place on stack
+ */
+ pushl OFFSET(save_retaddr)(%ebx)
+ xorl %eax, %eax
+ xchgw 2(%esp), %ax
+ shll $4, %eax
+ addl %eax, 0(%esp)
+
+ /* Restore registers and return */
+ movl OFFSET(save_eax)(%ebx), %eax
+ movl OFFSET(save_ebx)(%ebx), %ebx
+ ret
+
+/****************************************************************************
+ * prot_to_real (protected-mode near call, physical addresses)
+ *
+ * Switch from 32-bit protected mode with flat physical addresses to
+ * 16-bit real mode. %ss:sp is restored from the saved rm_ss and
+ * rm_sp. %cs is set such that %cs:0000 is the start of librm. All
+ * other segment registers are set to %ss. All other registers are
+ * preserved. Interrupts are *not* enabled, since we want to be able
+ * to use this routine inside an ISR.
+ *
+ * Note that since %cs:0000 points to the start of librm on exit, it
+ * follows that the code calling prot_to_real must be located within
+ * 64kB of the start of librm.
+ *
+ * Parameters: none
+ ****************************************************************************
+ */
+
+ .code32
+EXPORT(prot_to_real):
+ /* Calculate physical base address of librm in %ebx, preserve
+ * original %eax and %ebx in save_eax and save_ebx
+ */
+ pushl %ebx
+ call 1f
+1: popl %ebx
+ subl $OFFSET(1b), %ebx
+ popl OFFSET(save_ebx)(%ebx)
+ movl %eax, OFFSET(save_eax)(%ebx)
+
+ /* Extract return address from the stack, convert to offset
+ * within librm and save in save_retaddr
+ */
+ popl %eax
+ subl %ebx, %eax
+ movl %eax, OFFSET(save_retaddr)(%ebx)
+
+ /* Record protected-mode stack pointer */
+ movl %esp, OFFSET(pm_esp)(%ebx)
+
+ /* Record protected-mode GDT */
+ sgdt OFFSET(pm_gdt)(%ebx)
+
+ /* Set up real-mode GDT */
+ leal OFFSET(rm_gdt)(%ebx), %eax
+ movl %eax, OFFSET(rm_gdt_base)(%ebx)
+ movl %ebx, %eax
+ rorl $16, %eax
+ movw %bx, OFFSET(rm_gdt_rm_cs+2)(%ebx)
+ movb %al, OFFSET(rm_gdt_rm_cs+4)(%ebx)
+ movw %bx, OFFSET(rm_gdt_rm_ds+2)(%ebx)
+ movb %al, OFFSET(rm_gdt_rm_ds+4)(%ebx)
+
+ /* Switch to real-mode GDT and reload segment registers to get
+ * 64kB limits. Stack is invalidated by this process.
+ */
+ lgdt OFFSET(rm_gdt)(%ebx)
+ ljmp $RM_CS, $1f
+1: .code16
+ movw $RM_DS, %ax
+ movw %ax, %ds
+ movw %ax, %es
+ movw %ax, %fs
+ movw %ax, %gs
+ movw %ax, %ss
+
+ /* Calculate real-mode code segment in %ax and store in ljmp
+ * instruction
+ */
+ movl %ebx, %eax
+ shrl $4, %eax
+ movw %ax, OFFSET(p2r_ljmp) + 3
+
+ /* Switch to real mode */
+ movl %cr0, %ebx
+ andb $0!CR0_PE, %bl
+ movl %ebx, %cr0
+
+ /* Intersegment jump to flush prefetch queue and reload
+ * %cs:eip. The segment gets filled in by the above code. We
+ * can't just use lret to achieve this, because we have no
+ * stack at the moment.
+ */
+p2r_ljmp:
+ ljmp $0, $OFFSET(1f)
+1:
+
+ /* Set %ds to point to code segment for easier data access */
+ movw %ax, %ds
+
+ /* Restore registers */
+ movl OFFSET(save_eax), %eax
+ movl OFFSET(save_ebx), %ebx
+
+ /* Set up real-mode data segments and stack */
+ movw OFFSET(rm_ss), %ss
+ movw OFFSET(rm_sp), %sp
+ pushw %ss
+ pushw %ss
+ pushw %ss
+ pushw %ss
+ popw %ds
+ popw %es
+ popw %fs
+ popw %gs
+
+ /* Set up return address on stack and return */
+ pushw %cs:OFFSET(save_retaddr)
+ ret
+
+/****************************************************************************
+ * prot_call (real-mode far call)
+ *
+ * Call a specific C function in the protected-mode code. The
+ * prototype of the C function must be
+ * void function ( struct real_mode_regs *rm_regs,
+ * void (*retaddr) (void) );
+ * rm_regs will point to a struct containing the real-mode registers
+ * at entry to prot_call. retaddr will point to the (virtual) return
+ * address from "function". This return address will point into
+ * librm. It is included so that "function" may, if desired, relocate
+ * librm and return via the new copy. It must not be directly called
+ * as a function, i.e. you may not do "*retaddr()"; you must instead
+ * do something like:
+ * *retaddr += ( new_librm_location - old_librm_location );
+ * return;
+ *
+ * All registers will be preserved across prot_call(), unless the C
+ * function explicitly overwrites values in rm_regs. Interrupt status
+ * will also be preserved. Gate A20 will be enabled.
+ *
+ * Parameters:
+ * function : virtual address of protected-mode function to call
+ *
+ * Example usage:
+ * pushl $pxe_api_call
+ * lcall $LIBRM_SEGMENT, $prot_call
+ * addw $4, %sp
+ * to call in to the C function
+ * void pxe_api_call ( struct real_mode_regs *rm_regs );
+ ****************************************************************************
+ */
+
+#define PC_OFFSET_RM_REGS ( 0 )
+#define PC_OFFSET_RETADDR ( PC_OFFSET_RM_REGS + SIZEOF_REAL_MODE_REGS )
+#define PC_OFFSET_FUNCTION ( PC_OFFSET_RETADDR + 4 )
+
+ .code16
+EXPORT(prot_call):
+ /* Preserve registers and flags on RM stack */
+ pushfl
+ pushal
+ pushw %gs
+ pushw %fs
+ pushw %es
+ pushw %ds
+ pushw %ss
+ pushw %cs
+
+ /* Record RM stack pointer */
+ xorl %ebp, %ebp
+ movw %sp, %bp
+
+ /* Physical address of RM stack pointer to %esi */
+ xorl %esi, %esi
+ pushw %ss
+ popw %si
+ shll $4, %esi
+ addl %ebp, %esi
+
+ /* Address of pmode function to %ebx */
+ movl %ss:(PC_OFFSET_FUNCTION)(%bp), %ebx
+
+ /* Switch to protected mode */
+ pushw %cs
+ call real_to_prot
+ .code32
+
+ /* Copy rm_regs from RM stack to PM stack */
+ movl $SIZEOF_REAL_MODE_REGS, %ecx
+ subl %ecx, %esp
+ movl %esp, %edi
+ cld
+ rep movsb
+
+ /* Switch to virtual addresses. */
+ call 1f
+ jmp 2f
+1: ljmp $VIRTUAL_CS, $_phys_to_virt
+2:
+
+ /* Enable A20 line */
+ pushal
+ lcall $VIRTUAL_CS, $gateA20_set
+ popl %eax /* discard */
+ popal
+
+ /* Push &rm_regs and &retaddr on the stack, and call function */
+ movl %esp, %ebp
+ pushl %esp
+ subl $12, 0(%esp)
+ pushl %ebp
+ call *%ebx
+ popl %eax /* discard */
+ popl %eax /* discard */
+
+ /* Switch to physical addresses, discard PM register store */
+ lcall $VIRTUAL_CS, $_virt_to_phys
+ addl $SIZEOF_REAL_MODE_REGS+4, %esp /* also discard lcall seg */
+
+ /* Switch to real mode */
+ call prot_to_real
+ .code16
+
+ /* Restore registers and flags, and return */
+ popw %ax /* skip %cs */
+ popw %ax /* skip %ss */
+ popw %ds
+ popw %es
+ popw %fs
+ popw %gs
+ popal
+ popfl
+ lret
+
+/****************************************************************************
+ * real_call (protected-mode near call, virtual addresses)
+ *
+ * Call a real-mode function from protected-mode code.
+ *
+ * The non-segment register values will be passed directly to the
+ * real-mode code. The segment registers will be set as per
+ * prot_to_real. The non-segment register values set by the real-mode
+ * function will be passed back to the protected-mode caller. A
+ * result of this is that this routine cannot be called directly from
+ * C code, since it clobbers registers that the C ABI expects the
+ * callee to preserve. Gate A20 will be re-enabled in case the
+ * real-mode routine disabled it.
+ *
+ * librm.h defines two convenient macros for using real_call:
+ * REAL_CALL and REAL_EXEC. See librm.h and realmode.h for details
+ * and examples.
+ *
+ * Parameters:
+ * far pointer to real-mode function to call
+ *
+ * Returns: none
+ ****************************************************************************
+ */
+
+#define RC_OFFSET_PRESERVE_REGS ( 0 )
+#define RC_OFFSET_RETADDR ( RC_OFFSET_PRESERVE_REGS + 8 )
+#define RC_OFFSET_RM_FUNCTION ( RC_OFFSET_RETADDR + 4 )
+
+ .code32
+EXPORT(real_call):
+ /* Preserve registers */
+ pushl %ebp
+ pushl %eax
+
+ /* Switch to physical addresses */
+ lcall $VIRTUAL_CS, $_virt_to_phys
+ addl $4, %esp
+
+ /* Extract real-mode function address and store in ljmp instruction */
+ call 1f
+1: popl %ebp
+ movl RC_OFFSET_RM_FUNCTION(%esp), %eax
+ movl %eax, (rc_ljmp + 1 - 1b)(%ebp)
+
+ /* Restore registers */
+ popl %eax
+ popl %ebp
+
+ /* Switch to real mode, preserving non-segment registers */
+ call prot_to_real
+ .code16
+
+ /* Far call to real-mode routine */
+ pushw %cs
+ call rc_ljmp
+ jmp 2f
+rc_ljmp:
+ ljmp $0, $0 /* address filled in by above code */
+2:
+
+ /* Switch to protected mode */
+ pushw %cs
+ call real_to_prot
+ .code32
+
+ /* Switch to virtual addresses */
+ call 1f
+ jmp 2f
+1: ljmp $VIRTUAL_CS, $_phys_to_virt
+2:
+
+ /* Enable A20 line */
+ pushal
+ lcall $VIRTUAL_CS, $gateA20_set
+ popl %eax /* discard */
+ popal
+
+ /* Return */
+ ret
+
+/****************************************************************************
+ * Relocation lock counter
+ *
+ * librm may be moved in base memory only when this counter is zero.
+ * The counter gets incremented whenever a reference to librm is
+ * generated (e.g. a real_call is made, resulting in a return address
+ * pointing to librm being placed on the stack), and decremented when
+ * the reference goes out of scope (e.g. the real_call returns).
+ ****************************************************************************
+ */
+EXPORT(librm_ref_count): .byte 0
+
+/****************************************************************************
+ * Stored real-mode and protected-mode stack pointers
+ *
+ * The real-mode stack pointer is stored here whenever real_to_prot
+ * is called and restored whenever prot_to_real is called. The
+ * converse happens for the protected-mode stack pointer.
+ *
+ * Despite initial appearances this scheme is, in fact re-entrant,
+ * because program flow dictates that we always return via the point
+ * we left by. For example:
+ * PXE API call entry
+ * 1 real => prot
+ * ...
+ * Print a text string
+ * ...
+ * 2 prot => real
+ * INT 10
+ * 3 real => prot
+ * ...
+ * ...
+ * 4 prot => real
+ * PXE API call exit
+ *
+ * At point 1, the RM mode stack value, say RPXE, is stored in
+ * rm_ss,sp. We want this value to still be present in rm_ss,sp when
+ * we reach point 4.
+ *
+ * At point 2, the RM stack value is restored from RPXE. At point 3,
+ * the RM stack value is again stored in rm_ss,sp. This *does*
+ * overwrite the RPXE that we have stored there, but it's the same
+ * value, since the code between points 2 and 3 has managed to return
+ * to us.
+ ****************************************************************************
+ */
+
+EXPORT(rm_stack): /* comprises rm_ss and rm_sp */
+rm_sp: .word 0
+rm_ss: .word 0
+
+EXPORT(pm_stack):
+pm_esp: .long 0
+
+/****************************************************************************
+ * Temporary variables
+ ****************************************************************************
+ */
+save_eax: .long 0
+save_ebx: .long 0
+save_retaddr: .long 0
+
+/****************************************************************************
+ * End of librm
+ ****************************************************************************
+ */
+_librm_end:
+ .globl _librm_size
+ .equ _librm_size, _librm_end - _librm_start
diff --git a/src/arch/i386/transitions/librm_mgmt.c b/src/arch/i386/transitions/librm_mgmt.c
new file mode 100644
index 00000000..bfe963d4
--- /dev/null
+++ b/src/arch/i386/transitions/librm_mgmt.c
@@ -0,0 +1,87 @@
+/*
+ * librm: a library for interfacing to real-mode code
+ *
+ * Michael Brown <mbrown@fensystems.co.uk>
+ *
+ */
+
+#ifdef KEEP_IT_REAL
+/* Build a null object under -DKEEP_IT_REAL */
+#else
+
+#include "stdint.h"
+#include "stddef.h"
+#include "string.h"
+#include "librm.h"
+
+/*
+ * This file provides functions for managing librm.
+ *
+ */
+
+/* Current location of librm in base memory */
+char *installed_librm = librm;
+
+/*
+ * Install librm to base memory
+ *
+ */
+void install_librm ( void *addr ) {
+ memcpy ( addr, librm, librm_size );
+ installed_librm = addr;
+}
+
+/*
+ * Increment lock count of librm
+ *
+ */
+void lock_librm ( void ) {
+ inst_librm_ref_count++;
+}
+
+/*
+ * Decrement lock count of librm
+ *
+ */
+void unlock_librm ( void ) {
+#ifdef DEBUG_LIBRM
+ if ( inst_librm_ref_count == 0 ) {
+ printf ( "librm: ref count gone negative\n" );
+ lockup();
+ }
+#endif
+ inst_librm_ref_count--;
+}
+
+/*
+ * Allocate space on the real-mode stack and copy data there.
+ *
+ */
+uint16_t copy_to_rm_stack ( void *data, size_t size ) {
+#ifdef DEBUG_LIBRM
+ if ( inst_rm_stack.offset <= size ) {
+ printf ( "librm: out of space in RM stack\n" );
+ lockup();
+ }
+#endif
+ inst_rm_stack.offset -= size;
+ copy_to_real ( inst_rm_stack.segment, inst_rm_stack.offset,
+ data, size );
+ return inst_rm_stack.offset;
+};
+
+/*
+ * Deallocate space on the real-mode stack, optionally copying back
+ * data.
+ *
+ */
+void remove_from_rm_stack ( void *data, size_t size ) {
+ if ( data ) {
+ copy_from_real ( data,
+ inst_rm_stack.segment, inst_rm_stack.offset,
+ size );
+ }
+ inst_rm_stack.offset += size;
+};
+
+#endif /* KEEP_IT_REAL */
diff --git a/src/arch/ia64/core/ia64_timer.c b/src/arch/ia64/core/ia64_timer.c
index 3115714a..b7f54739 100644
--- a/src/arch/ia64/core/ia64_timer.c
+++ b/src/arch/ia64/core/ia64_timer.c
@@ -2,6 +2,7 @@
#include "timer.h"
#include "sal.h"
#include "pal.h"
+#include "init.h"
static inline unsigned long get_cycles(void)
{
@@ -39,7 +40,7 @@ static unsigned long calibrate_cycles(void)
}
static unsigned long clocks_per_tick;
-void setup_timers(void)
+static void setup_timers(void)
{
if (!clocks_per_tick) {
clocks_per_tick = calibrate_cycles();
@@ -87,3 +88,5 @@ int timer2_running(void)
{
return __timer_running();
}
+
+INIT_FN ( INIT_TIMERS, setup_timers, NULL, NULL );