summaryrefslogtreecommitdiffstats
path: root/src/arch
diff options
context:
space:
mode:
authorMichael Brown2005-03-08 19:53:11 +0100
committerMichael Brown2005-03-08 19:53:11 +0100
commit3d6123e69ab879c72ff489afc5bf93ef0b7a94ce (patch)
tree9f3277569153a550fa8d81ebd61bd88f266eb8da /src/arch
downloadipxe-3d6123e69ab879c72ff489afc5bf93ef0b7a94ce.tar.gz
ipxe-3d6123e69ab879c72ff489afc5bf93ef0b7a94ce.tar.xz
ipxe-3d6123e69ab879c72ff489afc5bf93ef0b7a94ce.zip
Initial revision
Diffstat (limited to 'src/arch')
-rw-r--r--src/arch/armnommu/Config23
-rw-r--r--src/arch/armnommu/Makefile58
-rw-r--r--src/arch/armnommu/core/arm_timer.c77
-rw-r--r--src/arch/armnommu/core/etherboot.lds55
-rw-r--r--src/arch/armnommu/core/lib1funcs.c105
-rw-r--r--src/arch/armnommu/core/mem.c27
-rw-r--r--src/arch/armnommu/core/raw_loader.c48
-rw-r--r--src/arch/armnommu/core/serial.c66
-rw-r--r--src/arch/armnommu/core/setjmp.S33
-rw-r--r--src/arch/armnommu/core/start.S184
-rw-r--r--src/arch/armnommu/drivers/net/p2001_eth.c525
-rw-r--r--src/arch/armnommu/include/bits/byteswap.h47
-rw-r--r--src/arch/armnommu/include/bits/cpu.h13
-rw-r--r--src/arch/armnommu/include/bits/elf.h18
-rw-r--r--src/arch/armnommu/include/bits/endian.h17
-rw-r--r--src/arch/armnommu/include/bits/string.h11
-rw-r--r--src/arch/armnommu/include/callbacks_arch.h1
-rw-r--r--src/arch/armnommu/include/hardware.h179
-rw-r--r--src/arch/armnommu/include/hooks.h25
-rw-r--r--src/arch/armnommu/include/io.h27
-rw-r--r--src/arch/armnommu/include/latch.h15
-rw-r--r--src/arch/armnommu/include/limits.h43
-rw-r--r--src/arch/armnommu/include/lxt971a.h30
-rw-r--r--src/arch/armnommu/include/setjmp.h23
-rw-r--r--src/arch/armnommu/include/stdint.h23
-rw-r--r--src/arch/e1/Config7
-rw-r--r--src/arch/e1/Makefile70
-rw-r--r--src/arch/e1/Makefile.working67
-rw-r--r--src/arch/e1/README80
-rw-r--r--src/arch/e1/core/coff_loader.c176
-rw-r--r--src/arch/e1/core/e132_xs.c70
-rw-r--r--src/arch/e1/core/e1_timer.c94
-rw-r--r--src/arch/e1/core/etherboot.lds126
-rw-r--r--src/arch/e1/core/longjmp.c35
-rw-r--r--src/arch/e1/core/memcmp.S54
-rw-r--r--src/arch/e1/core/memcpy.S79
-rw-r--r--src/arch/e1/core/memset.S47
-rw-r--r--src/arch/e1/core/setjmp.c26
-rw-r--r--src/arch/e1/core/start.S111
-rw-r--r--src/arch/e1/core/strcmp.S76
-rw-r--r--src/arch/e1/include/bits/byteswap.h39
-rw-r--r--src/arch/e1/include/bits/cpu.h6
-rw-r--r--src/arch/e1/include/bits/elf.h6
-rw-r--r--src/arch/e1/include/bits/endian.h6
-rw-r--r--src/arch/e1/include/bits/string.h35
-rw-r--r--src/arch/e1/include/e132_xs_board.h22
-rw-r--r--src/arch/e1/include/hooks.h9
-rw-r--r--src/arch/e1/include/io.h210
-rw-r--r--src/arch/e1/include/latch.h12
-rw-r--r--src/arch/e1/include/limits.h34
-rw-r--r--src/arch/e1/include/setjmp.h23
-rw-r--r--src/arch/e1/include/stdint.h16
-rw-r--r--src/arch/i386/Config131
-rw-r--r--src/arch/i386/Makefile373
-rw-r--r--src/arch/i386/core/aout_loader.c144
-rw-r--r--src/arch/i386/core/callbacks.c107
-rw-r--r--src/arch/i386/core/cpu.c86
-rw-r--r--src/arch/i386/core/elf.c135
-rw-r--r--src/arch/i386/core/etherboot.lds90
-rw-r--r--src/arch/i386/core/etherboot.prefix.lds100
-rw-r--r--src/arch/i386/core/freebsd_loader.c377
-rw-r--r--src/arch/i386/core/hooks.c35
-rw-r--r--src/arch/i386/core/i386_timer.c191
-rw-r--r--src/arch/i386/core/init.S305
-rw-r--r--src/arch/i386/core/multiboot_loader.c143
-rw-r--r--src/arch/i386/core/pci_io.c352
-rw-r--r--src/arch/i386/core/pic8259.c331
-rw-r--r--src/arch/i386/core/prefixudata.lds8
-rw-r--r--src/arch/i386/core/prefixzdata.lds8
-rw-r--r--src/arch/i386/core/pxe_callbacks.c364
-rw-r--r--src/arch/i386/core/pxe_loader.c94
-rw-r--r--src/arch/i386/core/realmode.c148
-rw-r--r--src/arch/i386/core/realmode_asm.S695
-rw-r--r--src/arch/i386/core/start16.S285
-rw-r--r--src/arch/i386/core/start16.lds8
-rw-r--r--src/arch/i386/core/start16z.lds65
-rw-r--r--src/arch/i386/core/start32.S767
-rw-r--r--src/arch/i386/core/tagged_loader.c201
-rw-r--r--src/arch/i386/core/video_subr.c94
-rw-r--r--src/arch/i386/core/wince_loader.c273
-rw-r--r--src/arch/i386/drivers/net/undi.c1458
-rw-r--r--src/arch/i386/drivers/net/undi.h178
-rw-r--r--src/arch/i386/firmware/pcbios/basemem.c317
-rw-r--r--src/arch/i386/firmware/pcbios/bios.c155
-rw-r--r--src/arch/i386/firmware/pcbios/console.c85
-rw-r--r--src/arch/i386/firmware/pcbios/e820mangler.S296
-rw-r--r--src/arch/i386/firmware/pcbios/hidemem.c94
-rw-r--r--src/arch/i386/firmware/pcbios/memsizes.c201
-rw-r--r--src/arch/i386/include/bits/byteswap.h45
-rw-r--r--src/arch/i386/include/bits/cpu.h243
-rw-r--r--src/arch/i386/include/bits/elf.h91
-rw-r--r--src/arch/i386/include/bits/elf_x.h5
-rw-r--r--src/arch/i386/include/bits/eltorito.h3
-rw-r--r--src/arch/i386/include/bits/endian.h9
-rw-r--r--src/arch/i386/include/bits/string.h99
-rw-r--r--src/arch/i386/include/callbacks_arch.h243
-rw-r--r--src/arch/i386/include/hidemem.h21
-rw-r--r--src/arch/i386/include/hooks.h9
-rw-r--r--src/arch/i386/include/io.h246
-rw-r--r--src/arch/i386/include/latch.h10
-rw-r--r--src/arch/i386/include/limits.h59
-rw-r--r--src/arch/i386/include/pic8259.h96
-rw-r--r--src/arch/i386/include/pxe_callbacks.h33
-rw-r--r--src/arch/i386/include/pxe_types.h35
-rw-r--r--src/arch/i386/include/realmode.h124
-rw-r--r--src/arch/i386/include/segoff.h41
-rw-r--r--src/arch/i386/include/setjmp.h12
-rw-r--r--src/arch/i386/include/stdint.h16
-rw-r--r--src/arch/i386/include/vga.h228
-rw-r--r--src/arch/i386/prefix/bImageprefix.S614
-rw-r--r--src/arch/i386/prefix/boot1a.s410
-rw-r--r--src/arch/i386/prefix/comprefix.S49
-rw-r--r--src/arch/i386/prefix/elf_dprefix.S96
-rw-r--r--src/arch/i386/prefix/elfprefix.S96
-rwxr-xr-xsrc/arch/i386/prefix/exeprefix.S44
-rw-r--r--src/arch/i386/prefix/floppyprefix.S359
-rw-r--r--src/arch/i386/prefix/huf.lds6
-rw-r--r--src/arch/i386/prefix/img.lds6
-rw-r--r--src/arch/i386/prefix/liloprefix.S144
-rw-r--r--src/arch/i386/prefix/lmelf_dprefix.S163
-rw-r--r--src/arch/i386/prefix/lmelf_prefix.S163
-rw-r--r--src/arch/i386/prefix/nullprefix.S16
-rw-r--r--src/arch/i386/prefix/pxeprefix.S398
-rw-r--r--src/arch/i386/prefix/romprefix.S416
-rw-r--r--src/arch/i386/prefix/unhuf.S400
-rw-r--r--src/arch/i386/prefix/unhuf.lds33
-rw-r--r--src/arch/i386/prefix/unnrv2b.S129
-rw-r--r--src/arch/ia64/Config22
-rw-r--r--src/arch/ia64/Makefile125
-rw-r--r--src/arch/ia64/core/__call.S68
-rw-r--r--src/arch/ia64/core/efi.c1026
-rw-r--r--src/arch/ia64/core/etherboot.lds82
-rw-r--r--src/arch/ia64/core/ia64_timer.c89
-rw-r--r--src/arch/ia64/core/idiv32.S86
-rw-r--r--src/arch/ia64/core/idiv64.S96
-rw-r--r--src/arch/ia64/core/longjmp.S163
-rw-r--r--src/arch/ia64/core/memmove.S244
-rw-r--r--src/arch/ia64/core/memset.S133
-rw-r--r--src/arch/ia64/core/pal.c84
-rw-r--r--src/arch/ia64/core/pci_io.c62
-rw-r--r--src/arch/ia64/core/reloc.S133
-rw-r--r--src/arch/ia64/core/relocate_to.S92
-rw-r--r--src/arch/ia64/core/sal.c278
-rw-r--r--src/arch/ia64/core/setjmp.S173
-rw-r--r--src/arch/ia64/core/start.S28
-rw-r--r--src/arch/ia64/drivers/net/undi_nii.c1079
-rw-r--r--src/arch/ia64/include/bits/byteswap.h36
-rw-r--r--src/arch/ia64/include/bits/cpu.h6
-rw-r--r--src/arch/ia64/include/bits/elf.h11
-rw-r--r--src/arch/ia64/include/bits/endian.h6
-rw-r--r--src/arch/ia64/include/bits/string.h6
-rw-r--r--src/arch/ia64/include/hooks.h12
-rw-r--r--src/arch/ia64/include/io.h228
-rw-r--r--src/arch/ia64/include/latch.h11
-rw-r--r--src/arch/ia64/include/limits.h57
-rw-r--r--src/arch/ia64/include/pal.h11
-rw-r--r--src/arch/ia64/include/sal.h29
-rw-r--r--src/arch/ia64/include/setjmp.h13
-rw-r--r--src/arch/ia64/include/stdint.h16
-rwxr-xr-xsrc/arch/ia64/prefix/apply_efi_prefix.pl63
-rw-r--r--src/arch/ia64/prefix/apply_unnrv2b_prefix.pl198
-rw-r--r--src/arch/ia64/prefix/efi_prefix.S195
-rw-r--r--src/arch/ia64/prefix/efi_prefix.lds91
-rw-r--r--src/arch/ia64/prefix/unnrv2b.S196
-rw-r--r--src/arch/ia64/prefix/unnrv2b.lds28
165 files changed, 22389 insertions, 0 deletions
diff --git a/src/arch/armnommu/Config b/src/arch/armnommu/Config
new file mode 100644
index 00000000..4c220cd5
--- /dev/null
+++ b/src/arch/armnommu/Config
@@ -0,0 +1,23 @@
+# Config for armnommu Etherboot
+#
+
+# For a clean compilation, switch in global Config
+# off: -DCONFIG_PCI, -DTAGGED_IMAGE, -DELF_IMAGE, -DPXE*, -DRELOCATE and INCLUDE_FILO
+# on : -DRAW_IMAGE
+
+# Serial line settings
+CFLAGS+= -DCONSOLE_SERIAL -DCONSPEED=57600
+
+# System Frequency
+CFLAGS+= -DSYSCLK=73728000
+
+# Image Download Address
+CFLAGS+= -DRAWADDR=0x40100000
+
+# NIC Debug Outputs
+#CFLAGS+= -DDEBUG_NIC
+
+# Fixed MAC address
+# p2001_eth has no flash and fixed mac address
+#CFLAGS+= -DMAC_HW_ADDR_DRV="'H','Y','L','N','X','1'"
+CFLAGS+= -DMAC_HW_ADDR_DRV="0x00,0x09,0x4F,0x00,0x00,0x02"
diff --git a/src/arch/armnommu/Makefile b/src/arch/armnommu/Makefile
new file mode 100644
index 00000000..a0cbb0c0
--- /dev/null
+++ b/src/arch/armnommu/Makefile
@@ -0,0 +1,58 @@
+ARCH_FORMAT= armnommu
+
+ROMLIMIT= 20480
+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)/start.o
+
+SRCS+= arch/armnommu/core/arm_timer.c
+SRCS+= arch/armnommu/core/start.S
+SRCS+= arch/armnommu/core/serial.c
+SRCS+= arch/armnommu/core/mem.c
+SRCS+= arch/armnommu/core/setjmp.S
+SRCS+= arch/armnommu/drivers/net/p2001_eth.c
+
+# not greater than 100kB
+ROMLIMIT:=1024000
+
+include $(BIN)/Roms
+
+ROMS= $(BIN)/p2001_eth.rom
+IMGS= $(BIN)/p2001_eth.img
+
+
+allfiles: $(ROMS)
+
+BOBJS+= $(BIN)/arm_timer.o
+BOBJS+= $(BIN)/serial.o
+BOBJS+= $(BIN)/mem.o
+BOBJS+= $(BIN)/setjmp.o
+BOBJS+= $(BIN)/lib1funcs.o
+
+# Utilities
+
+$(BIN)/nrv2b: util/nrv2b.c
+ $(HOST_CC) -O2 -DENCODE -DDECODE -DMAIN -DVERBOSE -DNDEBUG -DBITSIZE=32 -DENDIAN=0 -o $@ $<
+
+# Pattern Rules
+# General for compiling/assembly source files
+$(BIN)/%.o: arch/armnommu/core/%.c $(MAKEDEPS)
+ $(CC) $(CFLAGS) -o $@ -c $<
+
+$(BIN)/%.o: arch/armnommu/drivers/net/%.c $(MAKEDEPS)
+ $(CC) $(CFLAGS) -o $@ -c $<
+
+$(BIN)/%.S: arch/armnommu/core/%.c $(MAKEDEPS)
+ $(CC) $(CFLAGS) -S -o $@ -c $<
+
+$(BIN)/%.o: arch/armnommu/core/%.S $(MAKEDEPS)
+ $(CPP) $(CFLAGS) -D ASSEMBLY $< | $(AS) $(ASFLAGS) -o $@
+
+# general ruls for generating .img files
+$(BIN)/%.tmp: $(BIN)/%.o $(START) $(BIN)/config.o arch/$(ARCH)/core/etherboot.lds $(LIBS) $(STDDEPS) $(MAKEDEPS)
+ $(LD) $(LDFLAGS) -T arch/$(ARCH)/core/etherboot.lds -o $@ $(START) $(BIN)/config.o $< $(LIBS)
+ @$(SIZE) $@ | $(CHECKSIZE)
+
+$(BIN)/%.img: $(BIN)/%.tmp $(MAKEDEPS)
+ $(OBJCOPY) -O binary $< $@
diff --git a/src/arch/armnommu/core/arm_timer.c b/src/arch/armnommu/core/arm_timer.c
new file mode 100644
index 00000000..b858ca8d
--- /dev/null
+++ b/src/arch/armnommu/core/arm_timer.c
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2004 Tobias Lorenz
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#include "etherboot.h"
+#include "timer.h"
+#include "latch.h"
+#include "hardware.h"
+
+
+/* get timer returns the contents of the timer */
+static unsigned long get_timer(void)
+{
+ return P2001_TIMER->Freerun_Timer;
+}
+
+/* ------ Calibrate the TSC -------
+ * Time how long it takes to excute a loop that runs in known time.
+ * And find the convertion needed to get to CLOCK_TICK_RATE
+ */
+
+static unsigned long configure_timer(void)
+{
+ return (1);
+}
+
+static unsigned long clocks_per_tick = 1;
+
+void setup_timers(void)
+{
+ if (!clocks_per_tick) {
+ clocks_per_tick = configure_timer();
+ }
+}
+
+unsigned long currticks(void)
+{
+ return get_timer(); /* /clocks_per_tick */
+}
+
+static unsigned long timer_timeout;
+static int __timer_running(void)
+{
+ return get_timer() < timer_timeout;
+}
+
+void udelay(unsigned int usecs)
+{
+ unsigned long now;
+ now = get_timer();
+ timer_timeout = now + usecs * ((clocks_per_tick * TICKS_PER_SEC)/(1000*1000));
+ while(__timer_running());
+}
+void ndelay(unsigned int nsecs)
+{
+ unsigned long now;
+ now = get_timer();
+ timer_timeout = now + nsecs * ((clocks_per_tick * TICKS_PER_SEC)/(1000*1000*1000));
+ while(__timer_running());
+}
+
+void load_timer2(unsigned int timer2_ticks)
+{
+ unsigned long now;
+ unsigned long clocks;
+ now = get_timer();
+ clocks = timer2_ticks * ((clocks_per_tick * TICKS_PER_SEC)/CLOCK_TICK_RATE);
+ timer_timeout = now + clocks;
+}
+
+int timer2_running(void)
+{
+ return __timer_running();
+}
diff --git a/src/arch/armnommu/core/etherboot.lds b/src/arch/armnommu/core/etherboot.lds
new file mode 100644
index 00000000..0901a2f9
--- /dev/null
+++ b/src/arch/armnommu/core/etherboot.lds
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2004 Tobias Lorenz
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+OUTPUT_FORMAT("elf32-littlearm", "elf32-littlearm", "elf32-littlearm")
+OUTPUT_ARCH(arm)
+ENTRY(_start)
+SECTIONS
+{
+ /*. = 0x00000000;*/ /* PIC */
+ /*. = 0x00000400;*/ /* ROM Bootloader */
+ . = 0x40000000; /* SDRAM */
+
+ . = ALIGN(4);
+ _text = . ;
+ .text :
+ {
+ _start = .;
+ _virt_start = .;
+ bin/start.o (.text)
+ *(.text)
+
+ . = ALIGN(16);
+ isa_drivers = . ;
+ *(.drivers.isa);
+ isa_drivers_end = . ;
+ }
+
+ . = ALIGN(4);
+ .rodata : { *(.rodata) }
+
+ . = ALIGN(4);
+ .data : { *(.data) }
+
+ . = ALIGN(4);
+ .got : { *(.got) }
+
+ . = ALIGN(4);
+ _bss = . ;
+ .bss : { *(.bss) }
+
+ . = ALIGN(4);
+ _ebss = .;
+ _end = .;
+
+ . = ALIGN(16);
+ .text :
+ {
+ *(.dma.desc);
+ *(.dma.buffer);
+ }
+}
diff --git a/src/arch/armnommu/core/lib1funcs.c b/src/arch/armnommu/core/lib1funcs.c
new file mode 100644
index 00000000..e9e3a8fa
--- /dev/null
+++ b/src/arch/armnommu/core/lib1funcs.c
@@ -0,0 +1,105 @@
+/*
+ * Copyright (C) 2004 Tobias Lorenz
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+
+/*
+ * from gcc/config/udivmodsi4.c
+ */
+unsigned long
+udivmodsi4(unsigned long num, unsigned long den, int modwanted)
+{
+ unsigned long bit = 1;
+ unsigned long res = 0;
+
+ while (den < num && bit && !(den & (1L<<31)))
+ {
+ den <<=1;
+ bit <<=1;
+ }
+ while (bit)
+ {
+ if (num >= den)
+ {
+ num -= den;
+ res |= bit;
+ }
+ bit >>=1;
+ den >>=1;
+ }
+ if (modwanted) return num;
+ return res;
+}
+
+
+/*
+ * from gcc/config/udivmod.c
+ */
+long
+__udivsi3 (long a, long b)
+{
+ return udivmodsi4 (a, b, 0);
+}
+
+long
+__umodsi3 (long a, long b)
+{
+ return udivmodsi4 (a, b, 1);
+}
+
+
+/*
+ * from gcc/config/divmod.c
+ */
+long
+__divsi3 (long a, long b)
+{
+ int neg = 0;
+ long res;
+
+ if (a < 0)
+ {
+ a = -a;
+ neg = !neg;
+ }
+
+ if (b < 0)
+ {
+ b = -b;
+ neg = !neg;
+ }
+
+ res = udivmodsi4 (a, b, 0);
+
+ if (neg)
+ res = -res;
+
+ return res;
+}
+
+long
+__modsi3 (long a, long b)
+{
+ int neg = 0;
+ long res;
+
+ if (a < 0)
+ {
+ a = -a;
+ neg = 1;
+ }
+
+ if (b < 0)
+ b = -b;
+
+ res = udivmodsi4 (a, b, 1);
+
+ if (neg)
+ res = -res;
+
+ return res;
+}
diff --git a/src/arch/armnommu/core/mem.c b/src/arch/armnommu/core/mem.c
new file mode 100644
index 00000000..a185c5d6
--- /dev/null
+++ b/src/arch/armnommu/core/mem.c
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2004 Tobias Lorenz
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#include "hooks.h"
+#include "io.h"
+#include "etherboot.h"
+
+struct meminfo meminfo;
+void get_memsizes(void)
+{
+/* We initialize the meminfo structure
+ * according to our development board's specs
+ * We do not have a way to automatically probe the
+ * memspace instead we initialize it manually
+ */
+ meminfo.basememsize = 0x00000000;
+ meminfo.memsize = 0x00008000;
+ meminfo.map_count = 1;
+
+ meminfo.map[0].addr = 0x40000000;
+ meminfo.map[0].size = 0x01000000;
+ meminfo.map[0].type = E820_RAM;
+}
diff --git a/src/arch/armnommu/core/raw_loader.c b/src/arch/armnommu/core/raw_loader.c
new file mode 100644
index 00000000..1d49ffeb
--- /dev/null
+++ b/src/arch/armnommu/core/raw_loader.c
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2004 Tobias Lorenz
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#ifdef RAW_IMAGE
+static unsigned long raw_load_addr;
+
+int mach_boot(register unsigned long entry_point)
+{
+ void (*fnc)(void) = (void *) entry_point;
+ // r0 = 0
+ // r1 = 625 (machine nr. MACH_TYPE_P2001)
+ (*fnc)();
+
+ return 0; /* We should never reach this point ! */
+}
+
+static sector_t raw_download(unsigned char *data, unsigned int len, int eof)
+{
+ memcpy(phys_to_virt(raw_load_addr), data, len);
+ raw_load_addr += len;
+ if (!eof)
+ return 0;
+
+ done(1);
+ printf("Starting program.\n");
+ mach_boot(RAWADDR);
+ printf("Bootsector returned?");
+ longjmp(restart_etherboot, -2);
+ return 1;
+}
+
+static os_download_t raw_probe(unsigned char *data __unused, unsigned int len __unused)
+{
+ printf("(RAW");
+ // probe something here...
+ printf(")... \n");
+
+ //raw_load_addr = phys_to_virt(_end);
+ raw_load_addr = RAWADDR;
+ printf("Writing image to 0x%x\n", raw_load_addr);
+ return raw_download;
+}
+
+#endif
diff --git a/src/arch/armnommu/core/serial.c b/src/arch/armnommu/core/serial.c
new file mode 100644
index 00000000..3ae98d47
--- /dev/null
+++ b/src/arch/armnommu/core/serial.c
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2004 Tobias Lorenz
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#include "etherboot.h"
+#include "hardware.h"
+#ifdef CONSOLE_SERIAL
+
+/*
+ * void serial_putc(int ch);
+ * Write character `ch' to port UART_BASE.
+ */
+void serial_putc(int ch)
+{
+ /* wait for room in the 32 byte tx FIFO */
+ while ((P2001_UART->r.STATUS & 0x3f) > /* 30 */ 0) ;
+ P2001_UART->w.TX1 = ch & 0xff;
+}
+
+/*
+ * int serial_getc(void);
+ * Read a character from port UART_BASE.
+ */
+int serial_getc(void)
+{
+ while (((P2001_UART->r.STATUS >> 6) & 0x3f) == 0) ;
+ return P2001_UART->r.RX1 & 0xff;
+}
+
+/*
+ * int serial_ischar(void);
+ * If there is a character in the input buffer of port UART_BASE,
+ * return nonzero; otherwise return 0.
+ */
+int serial_ischar(void)
+{
+ return (P2001_UART->r.STATUS >> 6) & 0x3f;
+}
+
+/*
+ * int serial_init(void);
+ * Initialize port to speed 57.600, line settings 8N1.
+ */
+int serial_init(void)
+{
+ static unsigned int N;
+ // const M=3
+ P2001_UART->w.Clear = 0; // clear
+ N = ((SYSCLK/8)*3)/CONSPEED;
+ P2001_UART->w.Baudrate = (N<<16)+3; // set 57.600 BAUD
+ P2001_UART->w.Config = 0xcc100; // set 8N1, *water = 12
+ return 1;
+}
+
+/*
+ * void serial_fini(void);
+ * Cleanup our use of the serial port, in particular flush the
+ * output buffer so we don't accidentially loose characters.
+ */
+void serial_fini(void)
+{
+}
+#endif
diff --git a/src/arch/armnommu/core/setjmp.S b/src/arch/armnommu/core/setjmp.S
new file mode 100644
index 00000000..7e0a3eec
--- /dev/null
+++ b/src/arch/armnommu/core/setjmp.S
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2004 Tobias Lorenz
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+.text
+
+.global sigsetjmp;
+.type sigsetjmp,%function
+.align 4;
+sigsetjmp:
+ /* Save registers */
+ stmia r0, {v1-v6, sl, fp, sp, lr}
+
+ mov r0, #0
+ bx lr
+.size sigsetjmp,.-sigsetjmp;
+
+
+
+.global longjmp;
+.type longjmp,%function
+.align 4;
+longjmp:
+ mov ip, r0 /* save jmp_buf pointer */
+
+ movs r0, r1 /* get the return value in place */
+ moveq r0, #1 /* can't let setjmp() return zero! */
+
+ ldmia ip, {v1-v6, sl, fp, sp, pc}
+.size longjmp,.-longjmp;
diff --git a/src/arch/armnommu/core/start.S b/src/arch/armnommu/core/start.S
new file mode 100644
index 00000000..61b1d31b
--- /dev/null
+++ b/src/arch/armnommu/core/start.S
@@ -0,0 +1,184 @@
+/*
+ * Copyright (C) 2004 Tobias Lorenz
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+.global _start
+
+/* Mode definitions */
+#define Mode_USR 0x10
+#define Mode_FIQ 0x11
+#define Mode_IRQ 0x12
+#define Mode_SVC 0x13
+#define Mode_ABT 0x17
+#define Mode_UNDEF 0x1B
+#define Mode_SYS 0x1F // only available on ARM Arch. v4
+#define I_Bit 0x80
+#define F_Bit 0x40
+
+/* LPEC register definitions */
+#define Adr_SYS_BASE 0x00100000
+#define REL_Adr_SDRAM_Ctrl 0x10
+#define REL_Adr_ExtMem_Ctrl 0x14
+#define REL_Adr_WaitState_Ext 0x18
+#define REL_Adr_WaitState_Asic 0x1c
+#define Adr_TIMER_BASE 0x00110000
+#define REL_Adr_Timer12_PreDiv 0x0c
+#define REL_Adr_PLL_12000_config 0x30
+#define REL_Adr_PLL_12288_config 0x34
+#define REL_Adr_DIV_12288_config 0x38
+#define REL_Adr_FSC_CONFIG 0x44
+#define Adr_GPIO_BASE 0x00120000
+#define REL_Adr_NRES_OUT 0x2c
+
+
+
+/* Define entry point */
+.arm // Next instruction will be ARM
+_start:
+
+/*
+ * Initialize memory system
+ */
+
+
+/*
+ * Initialize stack pointer registers
+ */
+
+/* Enter SVC mode and set up the SVC stack pointer */
+ mov r0, #(Mode_SVC|I_Bit|F_Bit)
+ msr cpsr_c, r0
+ ldr sp, SP_SVC
+
+
+/*
+ * Initialize critical IO devices
+ */
+
+ /* watchdog off */
+ mov r0, #Adr_TIMER_BASE
+ ldr r1, Timer12_PreDiv
+ str r1, [r0, #REL_Adr_Timer12_PreDiv]
+
+ /* NRES=1 */
+ mov r0, #Adr_GPIO_BASE
+ ldr r1, NRES_OUT
+ str r1, [r0, #REL_Adr_NRES_OUT]
+
+ /* ExtMem */
+ mov r0, #Adr_SYS_BASE
+ ldr r1, ExtMem_Ctrl
+ str r1, [r0, #REL_Adr_ExtMem_Ctrl]
+
+ /* SDRAM */
+ mov r0, #Adr_SYS_BASE
+ ldr r1, SDRAM_Ctrl
+ str r1, [r0, #REL_Adr_SDRAM_Ctrl]
+/*
+_wait_sdram_ctrl:
+ ldr r1, [r0]
+ tst r1, #0x20000
+ beq _wait_sdram_ctrl
+*/
+
+ /* WaitState_Ext */
+ ldr r1, WaitState_Ext
+ str r1, [r0, #REL_Adr_WaitState_Ext]
+ /* WaitState_Asic */
+ ldr r1, WaitState_Asic
+ str r1, [r0, #REL_Adr_WaitState_Asic]
+
+ /* PLL_12288 */
+ mov r0, #Adr_TIMER_BASE
+ ldr r1, PLL_12288_config
+ str r1, [r0, #REL_Adr_PLL_12288_config]
+ /* DIV_12288 */
+ ldr r1, DIV_12288_config
+ str r1, [r0, #REL_Adr_DIV_12288_config]
+ /* PLL_12200 */
+ ldr r1, PLL_12000_config
+ str r1, [r0, #REL_Adr_PLL_12000_config]
+
+ /* FSC_CONFIG */
+ ldr r1, [r0, #REL_Adr_FSC_CONFIG]
+ bic r1, r1, #0x07
+ ldr r2, FSC_CONFIG
+ orr r1, r1, r2
+ str r1, [r0, #REL_Adr_FSC_CONFIG]
+
+
+/*
+ * Initialize interrupt system variables here
+ */
+
+
+/*
+ * Initialize memory required by main C code
+ */
+
+
+/* jump to main program */
+ mov r0, #0
+ b main
+
+
+Timer12_PreDiv:
+ .word 0x40bb0000 /* watchdog off */
+NRES_OUT:
+ .word 0x00000003 /* NRES_OUT_DRV=1, NRES_OUT_DAT=1 */
+
+#if SYSCLK == 73728000
+ExtMem_Ctrl:
+ .word 0x000000e8 /* fuer FPGA 32 Bit konfiguriert */
+SDRAM_Ctrl:
+// .word 0x28fc0037 /* default */
+ .word 0xaef40027 /* p2001_bit_compact */
+WaitState_Ext:
+ .word 0xa0001245 /* fuer 73 MHz */
+// .word 0x0000fff3 /* rom bootloader */
+WaitState_Asic:
+ .word 0x00ff8a5f /* fuer 85 MHz */
+// .word 0x00000203 /* rom bootloader */
+PLL_12288_config:
+ .word 0x00000004 /* fuer 73 MHz */
+DIV_12288_config:
+ .word 0x00010601 /* fuer 73 MHz */
+PLL_12000_config:
+ .word 0x10004e75 /* fuer 85 MHz */
+FSC_CONFIG:
+ .word 0xc0000005 /* fuer 73 MHz */
+#else
+#error "Please define proper timings and wait states for that sysclk."
+#endif
+
+SP_SVC:
+ .word 0x40fffffc
+
+
+
+#ifndef NORELOCATE
+/**************************************************************************
+RELOCATE_TO - relocate etherboot to the specified address
+**************************************************************************/
+ .global relocate_to
+
+relocate_to:
+ ldr r1, =_start
+ ldr r2, =_end
+
+ /* while (r1 < r2) { *(r0++) = *(r1++) } */
+_relocate_loop:
+ cmp r1, r2
+ ldrcc r3, [r1], #4
+ strcc r3, [r0], #4
+ bcc _relocate_loop
+ mov pc, lr
+#endif
+
+
+.global __gccmain
+__gccmain:
+ mov pc, lr /* return from subroutine */
diff --git a/src/arch/armnommu/drivers/net/p2001_eth.c b/src/arch/armnommu/drivers/net/p2001_eth.c
new file mode 100644
index 00000000..81bc84c3
--- /dev/null
+++ b/src/arch/armnommu/drivers/net/p2001_eth.c
@@ -0,0 +1,525 @@
+/**************************************************************************
+Etherboot - BOOTP/TFTP Bootstrap Program
+P2001 NIC driver for Etherboot
+***************************************************************************/
+
+/*
+ * Copyright (C) 2004 Tobias Lorenz
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+/* to get some global routines like printf */
+#include "etherboot.h"
+/* to get the interface to the body of the program */
+#include "nic.h"
+/* to get the ISA support functions, if this is an ISA NIC */
+#include "isa.h"
+
+#include "hardware.h"
+#include "lxt971a.h"
+#include "timer.h"
+
+
+/* NIC specific static variables go here */
+static unsigned char MAC_HW_ADDR[6]={MAC_HW_ADDR_DRV};
+
+/* DMA descriptors and buffers */
+#define NUM_RX_DESC 4 /* Number of Rx descriptor registers. */
+#define DMA_BUF_SIZE 2048 /* Buffer size */
+static DMA_DSC txd __attribute__ ((__section__(".dma.desc")));
+static DMA_DSC rxd[NUM_RX_DESC] __attribute__ ((__section__(".dma.desc")));
+static unsigned char rxb[NUM_RX_DESC * DMA_BUF_SIZE] __attribute__ ((__section__(".dma.buffer")));
+static unsigned char txb[ DMA_BUF_SIZE] __attribute__ ((__section__(".dma.buffer")));
+static unsigned int cur_rx;
+
+/* Device selectors */
+static unsigned int cur_channel; // DMA channel : 0..3
+static unsigned int cur_phy; // PHY Address : 0..31
+static P2001_ETH_regs_ptr EU; // Ethernet Unit : 0x0018_000 with _=0..3
+static P2001_ETH_regs_ptr MU; // Management Unit: 0x00180000
+
+#define MDIO_MAXCOUNT 1000 /* mdio abort */
+static unsigned int mdio_error; /* mdio error */
+
+/* Function prototypes */
+static void p2001_eth_mdio_init ();
+static void p2001_eth_mdio_write(unsigned int phyadr, unsigned int regadr, unsigned int data);
+static unsigned int p2001_eth_mdio_read (unsigned int phyadr, unsigned int regadr);
+extern unsigned int p2001_eth_mdio_error;
+
+static int p2001_eth_poll (struct nic *nic, int retrieve);
+static void p2001_eth_transmit (struct nic *nic, const char *d,
+ unsigned int t, unsigned int s, const char *p);
+
+static void p2001_eth_irq (struct nic *nic, irq_action_t action);
+
+static void p2001_eth_init ();
+static void p2001_eth_disable (struct dev *dev);
+
+static int p2001_eth_check_link(unsigned int phy);
+static int p2001_eth_probe (struct dev *dev, unsigned short *probe_addrs __unused);
+
+
+/**************************************************************************
+PHY MANAGEMENT UNIT - Read/write
+***************************************************************************/
+static void p2001_eth_mdio_init()
+{
+ /* reset ethernet PHYs */
+ printf("Resetting PHYs...\n");
+
+ /* GPIO24/25: TX_ER2/TX_ER0 */
+ /* GPIO26/27: PHY_RESET/TX_ER1 */
+ P2001_GPIO->PIN_MUX |= 0x0018;
+ // 31-16: 0000 1111 0000 0000
+ P2001_GPIO->GPIO2_En |= 0x0400;
+
+ P2001_GPIO->GPIO2_Out |= 0x04000000;
+ P2001_GPIO->GPIO2_Out &= ~0x0400;
+ mdelay(500);
+ P2001_GPIO->GPIO2_Out |= 0x0400;
+
+ /* set management unit clock divisor */
+ // max. MDIO CLK = 2.048 MHz (EU.doc)
+ // max. MDIO CLK = 8.000 MHz (LXT971A)
+ // sysclk/(2*(n+1)) = MDIO CLK <= 2.048 MHz
+ // n >= sysclk/4.096 MHz - 1
+#if SYSCLK == 73728000
+ P2001_MU->MU_DIV = 17; // 73.728 MHZ =17=> 2.020 MHz
+#else
+ //MU->MU_DIV = (SYSCLK/4.096)-1;
+#error "Please define a proper MDIO CLK divisor for that sysclk."
+#endif
+ asm("nop \n nop");
+}
+
+static void p2001_eth_mdio_write(unsigned int phyadr, unsigned int regadr, unsigned int data)
+{
+ static unsigned int count;
+ count = 0;
+
+ /* Warten bis Hardware inaktiv (MIU = "0") */
+ while ((MU->MU_CNTL & 0x8000) && (count < MDIO_MAXCOUNT))
+ count++;
+
+ /* Schreiben MU_DATA */
+ MU->MU_DATA = data;
+
+ /* Schreiben MU_CNTL */
+ MU->MU_CNTL = regadr + (phyadr<<5) + (1<<10);
+
+ /* Warten bis Hardware aktiv (MIU = "1") */
+ while (((MU->MU_CNTL & 0x8000) == 0) && (count < MDIO_MAXCOUNT))
+ count++;
+ //asm("nop \r\n nop");
+
+ /* Warten bis Hardware inaktiv (MIU = "0") */
+ while ((MU->MU_CNTL & 0x8000) && (count < MDIO_MAXCOUNT))
+ count++;
+
+ mdio_error = (count >= MDIO_MAXCOUNT);
+}
+
+static unsigned int p2001_eth_mdio_read(unsigned int phyadr, unsigned int regadr)
+{
+ static unsigned int count;
+ count = 0;
+
+ do {
+ /* Warten bis Hardware inaktiv (MIU = "0") */
+ while ((MU->MU_CNTL & 0x8000) && (count < MDIO_MAXCOUNT))
+ count++;
+
+ /* Schreiben MU_CNTL */
+ MU->MU_CNTL = regadr + (phyadr<<5) + (2<<10);
+
+ /* Warten bis Hardware aktiv (MIU = "1") */
+ while (((MU->MU_CNTL & 0x8000) == 0) && (count < MDIO_MAXCOUNT))
+ count++;
+ //asm("nop \r\n nop");
+
+ /* Warten bis Hardware inaktiv (MIU = "0") */
+ while ((MU->MU_CNTL & 0x8000) && (count < MDIO_MAXCOUNT))
+ count++;
+
+ /* Fehler, wenn MDIO Read Error (MRE = "1") */
+ } while ((MU->MU_CNTL & 0x4000) && (count < MDIO_MAXCOUNT));
+
+ /* Lesen MU_DATA */
+ mdio_error = (count >= MDIO_MAXCOUNT);
+ return MU->MU_DATA;
+}
+
+
+/**************************************************************************
+POLL - Wait for a frame
+***************************************************************************/
+/* Function: p2001_eth_poll
+ *
+ * Description: checks for a received packet and returns it if found.
+ *
+ * Arguments: struct nic *nic: NIC data structure
+ *
+ * Returns: 1 if a packet was received.
+ * 0 if no pacet was received.
+ *
+ * Side effects:
+ * Returns (copies) the packet to the array nic->packet.
+ * Returns the length of the packet in nic->packetlen.
+ */
+static int p2001_eth_poll(struct nic *nic, int retrieve)
+{
+ /* return true if there's an ethernet packet ready to read */
+ /* nic->packet should contain data on return */
+ /* nic->packetlen should contain length of data */
+
+ int retstat = 0;
+
+ if (rxd[cur_rx].stat & (1<<31)) // OWN
+ return retstat;
+
+ if (!retrieve)
+ return 1;
+
+ nic->packetlen = rxd[cur_rx].cntl & 0xffff;
+
+ if (rxd[cur_rx].stat & ((1<<26)|(1<<25)|(1<<24)|(1<<23)|(1<<22))) {
+ /* corrupted packet received */
+ printf("p2001_eth_poll: Corrupted packet received, stat = %X\n",
+ rxd[cur_rx].stat);
+ retstat = 0;
+ } else {
+ /* give packet to higher routine */
+ memcpy(nic->packet, (rxb + cur_rx*DMA_BUF_SIZE), nic->packetlen);
+ retstat = 1;
+ }
+
+#ifdef DEBUG_NIC
+ printf("p2001_eth_poll: packet from %! to %! received\n",
+ (rxb+cur_rx*DMA_BUF_SIZE)+ETH_ALEN,
+ (rxb+cur_rx*DMA_BUF_SIZE));
+#endif
+
+ /* disable receiver */
+ // FIXME: is that ok? it can produce grave errors.
+ EU->RMAC_DMA_EN = 0; /* clear run bit */
+
+ /* return the descriptor and buffer to receive ring */
+ rxd[cur_rx].stat = (1<<31) | (1<<30) | (1<<29); // DSC0 OWN|START|END
+ rxd[cur_rx].cntl = (1<<23); // DSC1 RECEIVE
+ rxd[cur_rx].cntl |= cur_channel << 16; // DSC1 CHANNEL
+ rxd[cur_rx].cntl |= DMA_BUF_SIZE; // DSC1 LEN
+
+ if (++cur_rx == NUM_RX_DESC)
+ cur_rx = 0;
+
+ /* enable receiver */
+ if (!(EU->RMAC_DMA_EN & 0x01))
+ EU->RMAC_DMA_EN = 0x01; /* set run bit */
+
+#ifdef DEBUG_NIC
+ printf("RMAC_MIB0..5: %d:%d:%d:%d:%d:%d\n",
+ EU->RMAC_MIB0, EU->RMAC_MIB1,
+ EU->RMAC_MIB2, EU->RMAC_MIB3,
+ EU->RMAC_MIB4, EU->RMAC_MIB5);
+#endif
+
+ return retstat; /* initially as this is called to flush the input */
+}
+
+
+/**************************************************************************
+TRANSMIT - Transmit a frame
+***************************************************************************/
+/* Function: p2001_eth_transmit
+ *
+ * Description: transmits a packet and waits for completion or timeout.
+ *
+ * Arguments: char d[6]: destination ethernet address.
+ * unsigned short t: ethernet protocol type.
+ * unsigned short s: size of the data-part of the packet.
+ * char *p: the data for the packet.
+ *
+ * Returns: void.
+ */
+static void p2001_eth_transmit(
+ struct nic *nic __unused,
+ const char *d, /* Destination */
+ unsigned int t, /* Type */
+ unsigned int s, /* size */
+ const char *p) /* Packet */
+{
+ unsigned int nstype;
+#ifdef DEBUG_NIC
+ unsigned int status;
+#endif
+
+ /* assemble packet */
+ memcpy(txb, d, ETH_ALEN); // destination
+ memcpy(txb+ETH_ALEN, nic->node_addr, ETH_ALEN); // source
+ nstype = htons(t);
+ memcpy(txb+2*ETH_ALEN, (char*)&nstype, 2); // type
+ memcpy(txb+ETH_HLEN, p, s); // packet
+ s += ETH_HLEN;
+
+ /* pad to minimum packet size */
+// while (s<ETH_ZLEN)
+// txb[s++] = '\0';
+ // TMAC_CNTL.ATP does the same
+
+#ifdef DEBUG_NIC
+ printf("p2001_eth_transmit: packet from %! to %! sent\n", txb+ETH_ALEN, txb);
+#endif
+
+ /* configure descriptor */
+ txd.stat = (1<<31) | (1<<30) | (1<<29); // DSC0 OWN|START|END
+ txd.cntl = cur_channel << 16; // DSC1 CHANNEL
+ txd.cntl |= s; // DSC1 LEN
+
+ /* restart the transmitter */
+ EU->TMAC_DMA_EN = 0x01; /* set run bit */
+ while(EU->TMAC_DMA_EN & 0x01) ; /* wait */
+
+#ifdef DEBUG_NIC
+ /* check status */
+ status = EU->TMAC_DMA_STAT;
+ if (status & ~(0x40))
+ printf("p2001_eth_transmit: dma status=0x%hx\n", status);
+
+ printf("TMAC_MIB6..7: %d:%d\n", EU->TMAC_MIB6, EU->TMAC_MIB7);
+#endif
+}
+
+
+/**************************************************************************
+IRQ - Enable, Disable or Force Interrupts
+***************************************************************************/
+/* Function: p2001_eth_irq
+ *
+ * Description: Enable, Disable, or Force, interrupts
+ *
+ * Arguments: struct nic *nic: NIC data structure
+ * irq_action_t action: Requested action
+ *
+ * Returns: void.
+ */
+
+static void
+p2001_eth_irq(struct nic *nic __unused, irq_action_t action __unused)
+{
+ switch ( action ) {
+ case DISABLE :
+ break;
+ case ENABLE :
+ break;
+ case FORCE :
+ break;
+ }
+}
+
+
+/**************************************************************************
+INIT - Initialize device
+***************************************************************************/
+/* Function: p2001_init
+ *
+ * Description: resets the ethernet controller chip and various
+ * data structures required for sending and receiving packets.
+ *
+ * returns: void.
+ */
+static void p2001_eth_init()
+{
+ static int i;
+
+ /* disable transceiver */
+// EU->TMAC_DMA_EN = 0; /* clear run bit */
+// EU->RMAC_DMA_EN = 0; /* clear run bit */
+
+ /* set rx filter (physical mac addresses) */
+ EU->RMAC_PHYU =
+ (MAC_HW_ADDR[0]<< 8) +
+ (MAC_HW_ADDR[1]<< 0);
+ EU->RMAC_PHYL =
+ (MAC_HW_ADDR[2]<<24) +
+ (MAC_HW_ADDR[3]<<16) +
+ (MAC_HW_ADDR[4]<<8 ) +
+ (MAC_HW_ADDR[5]<<0 );
+
+ /* initialize the tx descriptor ring */
+// txd.stat = (1<<31) | (1<<30) | (1<<29); // DSC0 OWN|START|END
+// txd.cntl = cur_channel << 16; // DSC1 CHANNEL
+// txd.cntl |= DMA_BUF_SIZE; // DSC1 LEN
+ txd.buf = &txb; // DSC2 BUFFER
+ txd.next = &txd; // DSC3 NEXTDSC @self
+ EU->TMAC_DMA_DESC = &txd;
+
+ /* initialize the rx descriptor ring */
+ cur_rx = 0;
+ for (i = 0; i < NUM_RX_DESC; i++) {
+ rxd[i].stat = (1<<31) | (1<<30) | (1<<29); // DSC0 OWN|START|END
+ rxd[i].cntl = (1<<23); // DSC1 RECEIVE
+ rxd[i].cntl |= cur_channel << 16; // DSC1 CHANNEL
+ rxd[i].cntl |= DMA_BUF_SIZE; // DSC1 LEN
+ rxd[i].buf = &rxb[i*DMA_BUF_SIZE]; // DSC2 BUFFER (EU-RX data)
+ rxd[i].next = &rxd[i+1]; // DSC3 NEXTDSC @next
+ }
+ rxd[NUM_RX_DESC-1].next = &rxd[0]; // DSC3 NEXTDSC @first
+ EU->RMAC_DMA_DESC = &rxd[0];
+
+ /* set transmitter mode */
+ EU->TMAC_CNTL = (1<<4) | /* COI: Collision ignore */
+ //(1<<3) | /* CSI: Carrier Sense ignore */
+ (1<<2); /* ATP: Automatic Transmit Padding */
+
+ /* set receive mode */
+ EU->RMAC_CNTL = (1<<3) | /* BROAD: Broadcast packets */
+ (1<<1); /* PHY : Packets to out MAC address */
+
+ /* enable receiver */
+ EU->RMAC_DMA_EN = 1; /* set run bit */
+}
+
+
+/**************************************************************************
+DISABLE - Turn off ethernet interface
+***************************************************************************/
+static void p2001_eth_disable(struct dev *dev __unused)
+{
+ /* put the card in its initial state */
+ /* This function serves 3 purposes.
+ * This disables DMA and interrupts so we don't receive
+ * unexpected packets or interrupts from the card after
+ * etherboot has finished.
+ * This frees resources so etherboot may use
+ * this driver on another interface
+ * This allows etherboot to reinitialize the interface
+ * if something is something goes wrong.
+ */
+
+ /* disable transmitter */
+ EU->TMAC_DMA_EN = 0; /* clear run bit */
+
+ /* disable receiver */
+ EU->RMAC_DMA_EN = 0; /* clear run bit */
+}
+
+
+/**************************************************************************
+LINK - Check for valid link
+***************************************************************************/
+static int p2001_eth_check_link(unsigned int phy)
+{
+ static int status;
+ static unsigned int count;
+ count = 0;
+
+ /* Use 0x3300 for restarting NWay */
+ printf("Starting auto-negotiation... ");
+ p2001_eth_mdio_write(phy, Adr_LXT971A_Control, 0x3300);
+ if (mdio_error)
+ goto failed;
+
+ /* Bits 1.5 and 17.7 are set to 1 once the Auto-Negotiation process to completed. */
+ do {
+ mdelay(500);
+ status = p2001_eth_mdio_read(phy, Adr_LXT971A_Status1);
+ if (mdio_error || (count++ > 6)) // 6*500ms = 3s timeout
+ goto failed;
+ } while (!(status & 0x20));
+
+ /* Bits 1.2 and 17.10 are set to 1 once the link is established. */
+ if (p2001_eth_mdio_read(phy, Adr_LXT971A_Status1) & 0x04) {
+ /* Bits 17.14 and 17.9 can be used to determine the link operation conditions (speed and duplex). */
+ printf("Valid link, operating at: %sMb-%s\n",
+ (p2001_eth_mdio_read(phy, Adr_LXT971A_Status2) & 0x4000) ? "100" : "10",
+ (p2001_eth_mdio_read(phy, Adr_LXT971A_Status2) & 0x0200) ? "FD" : "HD");
+ return 1;
+ }
+
+failed:
+ if (mdio_error)
+ printf("Failed\n");
+ else
+ printf("No valid link\n");
+ return 0;
+}
+
+
+/**************************************************************************
+PROBE - Look for an adapter, this routine's visible to the outside
+***************************************************************************/
+static int p2001_eth_probe(struct dev *dev, unsigned short *probe_addrs __unused)
+{
+ struct nic *nic = (struct nic *)dev;
+ /* if probe_addrs is 0, then routine can use a hardwired default */
+ static int board_found;
+ static int valid_link;
+
+ /* reset phys and configure mdio clk */
+ p2001_eth_mdio_init();
+
+ /* find the correct PHY/DMA/MAC combination */
+ MU = P2001_MU; // MU for all PHYs is only in EU0
+ printf("Searching for P2001 NICs...\n");
+ for (cur_channel=0; cur_channel<4; cur_channel++) {
+ switch(cur_channel) {
+ case 0:
+ EU = P2001_EU0;
+ cur_phy = 0;
+ break;
+ case 1:
+ EU = P2001_EU1;
+ cur_phy = 1;
+ break;
+ case 2:
+ EU = P2001_EU2;
+ cur_phy = 2;
+ break;
+ case 3:
+ EU = P2001_EU3;
+ cur_phy = 3;
+ break;
+ }
+
+ /* first a non destructive test for initial value RMAC_TLEN=1518 */
+ board_found = (EU->RMAC_TLEN == 1518);
+ if (board_found) {
+ printf("Checking EU%d...\n", cur_channel);
+
+ valid_link = p2001_eth_check_link(cur_phy);
+ if (valid_link) {
+ /* initialize device */
+ p2001_eth_init(nic);
+
+ /* set node address */
+ printf("Setting MAC address to %!\n", MAC_HW_ADDR);
+ memcpy(nic->node_addr, MAC_HW_ADDR, 6);
+
+ /* point to NIC specific routines */
+ dev->disable = p2001_eth_disable;
+ nic->poll = p2001_eth_poll;
+ nic->transmit = p2001_eth_transmit;
+ nic->irq = p2001_eth_irq;
+
+ /* Report the ISA pnp id of the board */
+ dev->devid.vendor_id = htons(GENERIC_ISAPNP_VENDOR);
+ dev->devid.vendor_id = htons(0x1234);
+ return 1;
+ }
+ }
+ }
+ /* else */
+ return 0;
+}
+
+ISA_ROM("p2001_eth", "P2001 Ethernet Driver")
+static struct isa_driver p2001_eth_driver __isa_driver = {
+ .type = NIC_DRIVER,
+ .name = "P2001 Ethernet Driver",
+ .probe = p2001_eth_probe,
+ .ioaddrs = 0,
+};
diff --git a/src/arch/armnommu/include/bits/byteswap.h b/src/arch/armnommu/include/bits/byteswap.h
new file mode 100644
index 00000000..2aae0800
--- /dev/null
+++ b/src/arch/armnommu/include/bits/byteswap.h
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2004 Tobias Lorenz
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#ifndef ETHERBOOT_BITS_BYTESWAP_H
+#define ETHERBOOT_BITS_BYTESWAP_H
+
+/* We do not have byte swap functions ... We are
+ * RISC processor ...
+ */
+
+static inline unsigned short __swap16(volatile unsigned short v)
+{
+ return ((v << 8) | (v >> 8));
+}
+
+static inline unsigned int __swap32(volatile unsigned long v)
+{
+ return ((v << 24) | ((v & 0xff00) << 8) | ((v & 0xff0000) >> 8) | (v >> 24));
+}
+
+#define __bswap_constant_16(x) \
+ ((uint16_t)((((uint16_t)(x) & 0x00ff) << 8) | \
+ (((uint16_t)(x) & 0xff00) >> 8)))
+
+#define __bswap_constant_32(x) \
+ ((uint32_t)((((uint32_t)(x) & 0x000000ffU) << 24) | \
+ (((uint32_t)(x) & 0x0000ff00U) << 8) | \
+ (((uint32_t)(x) & 0x00ff0000U) >> 8) | \
+ (((uint32_t)(x) & 0xff000000U) >> 24)))
+
+# define __bswap_16(x) \
+ (__extension__ \
+ ({ unsigned short int __bsx = (x); \
+ ((((__bsx) >> 8) & 0xff) | (((__bsx) & 0xff) << 8)); }))
+
+
+# define __bswap_32(x) \
+ (__extension__ \
+ ({ unsigned int __bsx = (x); \
+ ((((__bsx) & 0xff000000) >> 24) | (((__bsx) & 0x00ff0000) >> 8) | \
+ (((__bsx) & 0x0000ff00) << 8) | (((__bsx) & 0x000000ff) << 24)); }))
+
+#endif /* ETHERBOOT_BITS_BYTESWAP_H */
diff --git a/src/arch/armnommu/include/bits/cpu.h b/src/arch/armnommu/include/bits/cpu.h
new file mode 100644
index 00000000..9516a600
--- /dev/null
+++ b/src/arch/armnommu/include/bits/cpu.h
@@ -0,0 +1,13 @@
+/*
+ * Copyright (C) 2004 Tobias Lorenz
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#ifndef ARM_BITS_CPU_H
+#define ARM_BITS_CPU_H
+
+#define cpu_setup() do {} while(0)
+
+#endif /* ARM_BITS_CPU_H */
diff --git a/src/arch/armnommu/include/bits/elf.h b/src/arch/armnommu/include/bits/elf.h
new file mode 100644
index 00000000..e0a17aa5
--- /dev/null
+++ b/src/arch/armnommu/include/bits/elf.h
@@ -0,0 +1,18 @@
+/*
+ * Copyright (C) 2004 Tobias Lorenz
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#ifndef ARM_BITS_ELF_H
+#define ARM_BITS_ELF_H
+
+/* ELF Defines for the current architecture */
+#define EM_CURRENT EM_ARM
+#define ELFDATA_CURRENT ELFDATA2LSB
+
+#define ELF_CHECK_ARCH(x) \
+ ((x).e_machine == EM_CURRENT)
+
+#endif /* ARM_BITS_ELF_H */
diff --git a/src/arch/armnommu/include/bits/endian.h b/src/arch/armnommu/include/bits/endian.h
new file mode 100644
index 00000000..0a18d97d
--- /dev/null
+++ b/src/arch/armnommu/include/bits/endian.h
@@ -0,0 +1,17 @@
+/*
+ * Copyright (C) 2004 Tobias Lorenz
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#ifndef ETHERBOOT_BITS_ENDIAN_H
+#define ETHERBOOT_BITS_ENDIAN_H
+
+#ifdef __ARMEB__
+#define __BYTE_ORDER __BIG_ENDIAN
+#else
+#define __BYTE_ORDER __LITTLE_ENDIAN
+#endif
+
+#endif /* ETHERBOOT_BITS_ENDIAN_H */
diff --git a/src/arch/armnommu/include/bits/string.h b/src/arch/armnommu/include/bits/string.h
new file mode 100644
index 00000000..9cfce809
--- /dev/null
+++ b/src/arch/armnommu/include/bits/string.h
@@ -0,0 +1,11 @@
+/*
+ * Copyright (C) 2004 Tobias Lorenz
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#ifndef ETHERBOOT_BITS_STRING_H
+#define ETHERBOOT_BITS_STRING_H
+
+#endif /* ETHERBOOT_BITS_STRING_H */
diff --git a/src/arch/armnommu/include/callbacks_arch.h b/src/arch/armnommu/include/callbacks_arch.h
new file mode 100644
index 00000000..fa882cb5
--- /dev/null
+++ b/src/arch/armnommu/include/callbacks_arch.h
@@ -0,0 +1 @@
+/* empty file */
diff --git a/src/arch/armnommu/include/hardware.h b/src/arch/armnommu/include/hardware.h
new file mode 100644
index 00000000..203b78b7
--- /dev/null
+++ b/src/arch/armnommu/include/hardware.h
@@ -0,0 +1,179 @@
+/*
+ * Copyright (C) 2004 Tobias Lorenz
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+/*
+ * Architecture: ARM9TDMI
+ * Processor : P2001
+ */
+
+#ifndef ARCH_HARDWARE_H
+#define ARCH_HARDWARE_H
+
+#ifndef __ASSEMBLY__
+
+/* DMA descriptor */
+typedef struct {
+ unsigned int stat; /* status: own, start, end, offset, status */
+ unsigned int cntl; /* control: loop, int, type, channel, length */
+ char *buf; /* buffer */
+ void *next; /* nextdsc */
+} DMA_DSC;
+
+
+/* The address definitions are from asic_bf.h */
+typedef struct { // 0x00100000U
+ volatile unsigned int reserved1[0x3];
+ volatile unsigned int ArmDmaPri; // 0x0000000CU
+ volatile unsigned int SDRAM_Ctrl; // 0x00000010U
+ volatile unsigned int ExtMem_Ctrl; // 0x00000014U
+ volatile unsigned int WaitState_Ext; // 0x00000018U
+ volatile unsigned int WaitState_Asic; // 0x0000001CU
+ volatile unsigned int TOP; // 0x00000020U
+ volatile unsigned int reserved2[0x3];
+ volatile unsigned int Adr1_EQ_30Bit; // 0x00000030U
+ volatile unsigned int Adr2_EQ_30Bit; // 0x00000034U
+ volatile unsigned int Adr3_EQ_30Bit; // 0x00000038U
+ volatile unsigned int Dat3_EQ_32Bit; // 0x0000003CU
+ volatile unsigned int Adr4_HE_20Bit; // 0x00000040U
+ volatile unsigned int Adr4_LT_20Bit; // 0x00000044U
+ volatile unsigned int Adr5_HE_20Bit; // 0x00000048U
+ volatile unsigned int Adr5_LT_20Bit; // 0x0000004CU
+ volatile unsigned int Adr_Control; // 0x00000050U
+ volatile unsigned int ABORT_IA_32Bit; // 0x00000054U
+} *P2001_SYS_regs_ptr;
+#define P2001_SYS ((volatile P2001_SYS_regs_ptr) 0x00100000)
+
+typedef struct { // 0x00110000U
+ volatile unsigned int Timer1; // 0x00000000U
+ volatile unsigned int Timer2; // 0x00000004U
+ volatile unsigned int TIMER_PRELOAD; // 0x00000008U
+ volatile unsigned int Timer12_PreDiv; // 0x0000000CU
+ volatile unsigned int TIMER_INT; // 0x00000010U
+ volatile unsigned int Freerun_Timer; // 0x00000014U
+ volatile unsigned int WatchDog_Timer; // 0x00000018U
+ volatile unsigned int PWM_CNT; // 0x00000020U
+ volatile unsigned int PWM_CNT2; // 0x00000024U
+ volatile unsigned int PLL_12000_config; // 0x00000030U
+ volatile unsigned int PLL_12288_config; // 0x00000034U
+ volatile unsigned int DIV_12288_config; // 0x00000038U
+ volatile unsigned int MOD_CNT_768; // 0x0000003CU
+ volatile unsigned int FSC_IRQ_STATUS; // 0x00000040U
+ volatile unsigned int FSC_CONFIG; // 0x00000044U
+ volatile unsigned int FSC_CONSTRUCT; // 0x00000048U
+ volatile unsigned int FSC_base_clk_reg; // 0x0000004CU
+ volatile unsigned int SYSCLK_SHAPE; // 0x00000050U
+ volatile unsigned int SDRAMCLK_SHAPE; // 0x00000054U
+ volatile unsigned int RING_OSZI; // 0x00000058U
+} *P2001_TIMER_regs_ptr;
+#define P2001_TIMER ((volatile P2001_TIMER_regs_ptr) 0x00110000)
+
+typedef struct { // 0x00120000U
+ volatile unsigned int reserved1[0x5];
+ volatile unsigned int GPIO_Config; // 0x00000014U
+ volatile unsigned int GPIO_INT; // 0x00000018U
+ volatile unsigned int GPIO_Out; // 0x0000001CU
+ volatile unsigned int GPIO_IN; // 0x00000020U
+ volatile unsigned int GPIO_En; // 0x00000024U
+ volatile unsigned int PIN_MUX; // 0x00000028U
+ volatile unsigned int NRES_OUT; // 0x0000002CU
+ volatile unsigned int GPIO2_Out; // 0x00000030U
+ volatile unsigned int GPIO2_IN; // 0x00000034U
+ volatile unsigned int GPIO2_En; // 0x00000038U
+ volatile unsigned int GPIO_INT_SEL; // 0x0000003CU
+ volatile unsigned int GPI3_IN; // 0x00000040U
+ volatile unsigned int GPO4_OUT; // 0x00000044U
+} *P2001_GPIO_regs_ptr;
+#define P2001_GPIO ((volatile P2001_GPIO_regs_ptr) 0x00120000)
+
+typedef struct { // 0x00130000U
+ volatile unsigned int Main_NFIQ_Int_Ctrl; // 0x00000000U
+ volatile unsigned int Main_NIRQ_Int_Ctrl; // 0x00000004U
+ volatile unsigned int Status_NFIQ; // 0x00000008U
+ volatile unsigned int Status_NIRQ; // 0x0000000CU
+} *P2001_INT_CTRL_regs_ptr;
+#define P2001_INT_CTRL ((volatile P2001_INT_CTRL_regs_ptr) 0x00130000)
+
+typedef union { // 0x00140000U
+ struct { // write
+ volatile unsigned int TX1; // 0x00000000U
+ volatile unsigned int TX2; // 0x00000004U
+ volatile unsigned int TX3; // 0x00000008U
+ volatile unsigned int TX4; // 0x0000000CU
+ volatile unsigned int Baudrate; // 0x00000010U
+ volatile unsigned int reserved1[0x3];
+ volatile unsigned int Config; // 0x00000020U
+ volatile unsigned int Clear; // 0x00000024U
+ volatile unsigned int Echo_EN; // 0x00000028U
+ volatile unsigned int IRQ_Status; // 0x0000002CU
+ } w; // write
+
+ struct { // read
+ volatile unsigned int RX1; // 0x00000000U
+ volatile unsigned int RX2; // 0x00000004U
+ volatile unsigned int RX3; // 0x00000008U
+ volatile unsigned int RX4; // 0x0000000CU
+ volatile unsigned int reserved1[0x4];
+ volatile unsigned int PRE_STATUS; // 0x00000020U
+ volatile unsigned int STATUS; // 0x00000024U
+ volatile unsigned int reserved2[0x1];
+ volatile unsigned int IRQ_Status; // 0x0000002CU
+ } r; // read
+} *P2001_UART_regs_ptr;
+#define P2001_UART ((volatile P2001_UART_regs_ptr) 0x00140000)
+
+typedef struct { // 0x0018_000U _=0,1,2,3
+ volatile DMA_DSC * RMAC_DMA_DESC; // 0x00000000U
+ volatile unsigned int RMAC_DMA_CNTL; // 0x00000004U
+ volatile unsigned int RMAC_DMA_STAT; // 0x00000008U
+ volatile unsigned int RMAC_DMA_EN; // 0x0000000CU
+ volatile unsigned int RMAC_CNTL; // 0x00000010U
+ volatile unsigned int RMAC_TLEN; // 0x00000014U
+ volatile unsigned int RMAC_PHYU; // 0x00000018U
+ volatile unsigned int RMAC_PHYL; // 0x0000001CU
+ volatile unsigned int RMAC_PFM0; // 0x00000020U
+ volatile unsigned int RMAC_PFM1; // 0x00000024U
+ volatile unsigned int RMAC_PFM2; // 0x00000028U
+ volatile unsigned int RMAC_PFM3; // 0x0000002CU
+ volatile unsigned int RMAC_PFM4; // 0x00000030U
+ volatile unsigned int RMAC_PFM5; // 0x00000034U
+ volatile unsigned int RMAC_PFM6; // 0x00000038U
+ volatile unsigned int RMAC_PFM7; // 0x0000003CU
+ volatile unsigned int RMAC_MIB0; // 0x00000040U
+ volatile unsigned int RMAC_MIB1; // 0x00000044U
+ volatile unsigned int RMAC_MIB2; // 0x00000048U
+ volatile unsigned int RMAC_MIB3; // 0x0000004CU
+ volatile unsigned int RMAC_MIB4; // 0x00000050U
+ volatile unsigned int RMAC_MIB5; // 0x00000054U
+ volatile unsigned int reserved1[0x1e8];
+ volatile unsigned int RMAC_DMA_DATA; // 0x000007F8U
+ volatile unsigned int RMAC_DMA_ADR; // 0x000007FCU
+ volatile DMA_DSC * TMAC_DMA_DESC; // 0x00000800U
+ volatile unsigned int TMAC_DMA_CNTL; // 0x00000804U
+ volatile unsigned int TMAC_DMA_STAT; // 0x00000808U
+ volatile unsigned int TMAC_DMA_EN; // 0x0000080CU
+ volatile unsigned int TMAC_CNTL; // 0x00000810U
+ volatile unsigned int TMAC_MIB6; // 0x00000814U
+ volatile unsigned int TMAC_MIB7; // 0x00000818U
+ volatile unsigned int reserved2[0x1];
+ volatile unsigned int MU_CNTL; // 0x00000820U
+ volatile unsigned int MU_DATA; // 0x00000824U
+ volatile unsigned int MU_DIV; // 0x00000828U
+ volatile unsigned int CONF_RMII; // 0x0000082CU
+ volatile unsigned int reserved3[0x1f2];
+ volatile unsigned int TMAC_DMA_DATA; // 0x00000FF8U
+ volatile unsigned int TMAC_DMA_ADR; // 0x00000FFCU
+} *P2001_ETH_regs_ptr;
+#define P2001_EU0 ((volatile P2001_ETH_regs_ptr) 0x00180000)
+#define P2001_EU1 ((volatile P2001_ETH_regs_ptr) 0x00181000)
+#define P2001_EU2 ((volatile P2001_ETH_regs_ptr) 0x00182000)
+#define P2001_EU3 ((volatile P2001_ETH_regs_ptr) 0x00183000)
+#define P2001_MU P2001_EU0
+
+#endif
+
+#endif /* ARCH_HARDWARE_H */
diff --git a/src/arch/armnommu/include/hooks.h b/src/arch/armnommu/include/hooks.h
new file mode 100644
index 00000000..cdcf023b
--- /dev/null
+++ b/src/arch/armnommu/include/hooks.h
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2004 Tobias Lorenz
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#ifndef ETHERBOOT_ARM_HOOKS_H
+#define ETHERBOOT_ARM_HOOKS_H
+
+struct Elf_Bhdr;
+
+#define arch_main(data, params) do {} while(0)
+//void arch_main(in_call_data_t *data, va_list params);
+
+#define arch_on_exit(status) do {} while(0)
+//void arch_on_exit(int status);
+
+#define arch_relocate_to(addr) do {} while(0)
+//void arch_relocate_to(unsigned long addr);
+
+#define arch_relocated_from(old_addr) do {} while(0)
+//void arch_relocate_from(unsigned long old_addr);
+
+#endif /* ETHERBOOT_ARM_HOOKS_H */
diff --git a/src/arch/armnommu/include/io.h b/src/arch/armnommu/include/io.h
new file mode 100644
index 00000000..478e443d
--- /dev/null
+++ b/src/arch/armnommu/include/io.h
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2004 Tobias Lorenz
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#ifndef ETHERBOOT_IO_H
+#define ETHERBOOT_IO_H
+
+#define virt_to_phys(vaddr) ((unsigned long) (vaddr))
+#define phys_to_virt(vaddr) ((void *) (vaddr))
+
+#define virt_to_bus virt_to_phys
+#define bus_to_virt phys_to_virt
+
+#define iounmap(addr) ((void)0)
+#define ioremap(physaddr, size) (physaddr)
+
+extern unsigned char inb (unsigned long int port);
+extern unsigned short int inw (unsigned long int port);
+extern unsigned long int inl (unsigned long int port);
+extern void outb (unsigned char value, unsigned long int port);
+extern void outw (unsigned short value, unsigned long int port);
+extern void outl (unsigned long value, unsigned long int port);
+
+#endif /* ETHERBOOT_IO_H */
diff --git a/src/arch/armnommu/include/latch.h b/src/arch/armnommu/include/latch.h
new file mode 100644
index 00000000..23ceaa30
--- /dev/null
+++ b/src/arch/armnommu/include/latch.h
@@ -0,0 +1,15 @@
+/*
+ * Copyright (C) 2004 Tobias Lorenz
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#ifndef LATCH_H
+#define LATCH_H
+
+// Freerun_Timer is always at 12.288 MHZ
+#define TICKS_PER_SEC (12288000UL)
+//#define TICKS_PER_SEC (73728000UL)
+
+#endif /* LATCH_H */
diff --git a/src/arch/armnommu/include/limits.h b/src/arch/armnommu/include/limits.h
new file mode 100644
index 00000000..8df03ef2
--- /dev/null
+++ b/src/arch/armnommu/include/limits.h
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2004 Tobias Lorenz
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#ifndef __LIMITS_H
+#define __LIMITS_H 1
+
+#define MB_LEN_MAX 16
+
+#define CHAR_BIT 8
+
+#define SCHAR_MIN (-128)
+#define SCHAR_MAX 127
+
+#define UCHAR_MAX 255
+
+#define CHAR_MIN SCHAR_MIN
+#define CHAR_MAX SCHAR_MAX
+
+#define SHRT_MIN (-32768)
+#define SHRT_MAX 32767
+
+#define USHRT_MAX 65535
+
+#define INT_MIN (-INT_MAX - 1)
+#define INT_MAX 2147483647
+
+#define UINT_MAX 4294967295U
+
+#define LONG_MAX 2147483647L
+#define LONG_MIN (-LONG_MAX - 1L)
+
+#define ULONG_MAX 4294967295UL
+
+#define LLONG_MAX 9223372036854775807LL
+#define LLONG_MIN (-LLONG_MAX - 1LL)
+
+#define ULLONG_MAX 18446744073709551615ULL
+
+#endif
diff --git a/src/arch/armnommu/include/lxt971a.h b/src/arch/armnommu/include/lxt971a.h
new file mode 100644
index 00000000..16314ec2
--- /dev/null
+++ b/src/arch/armnommu/include/lxt971a.h
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2004 Tobias Lorenz
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+/*
+ * Intel LXT971ALE (MII-compatible PHY)
+ */
+
+#define Adr_LXT971A_Control 0 /* Control Register */
+#define Adr_LXT971A_Status1 1 /* MII Status Register #1 */
+#define Adr_LXT971A_PHY_ID1 2 /* PHY Identification Register 1 */
+#define Adr_LXT971A_PHY_ID2 3 /* PHY Identification Register 2 */
+#define Adr_LXT971A_AN_Advertise 4 /* Auto Negotiation Advertisement Register */
+#define Adr_LXT971A_AN_Link_Ability 5 /* Auto Negotiation Link Partner Base Page Ability Register */
+#define Adr_LXT971A_AN_Expansion 6 /* Auto Negotiation Expansion */
+#define Adr_LXT971A_AN_Next_Page_Txmit 7 /* Auto Negotiation Next Page Transmit Register */
+#define Adr_LXT971A_AN_Link_Next_Page 8 /* Auto Negotiation Link Partner Next Page Receive Register */
+#define Adr_LXT971A_Fast_Control 9 /* Not Implemented */
+#define Adr_LXT971A_Fast_Status 10 /* Not Implemented */
+#define Adr_LXT971A_Extended_Status 15 /* Not Implemented */
+#define Adr_LXT971A_Port_Config 16 /* Configuration Register */
+#define Adr_LXT971A_Status2 17 /* Status Register #2 */
+#define Adr_LXT971A_Interrupt_Enable 18 /* Interrupt Enable Register */
+#define Adr_LXT971A_Interrupt_Status 19 /* Interrupt Status Register */
+#define Adr_LXT971A_LED_Config 20 /* LED Configuration Register */
+#define Adr_LXT971A_Transmit_Control 30 /* Transmit Control Register */
diff --git a/src/arch/armnommu/include/setjmp.h b/src/arch/armnommu/include/setjmp.h
new file mode 100644
index 00000000..6499c488
--- /dev/null
+++ b/src/arch/armnommu/include/setjmp.h
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2004 Tobias Lorenz
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#ifndef ETHERBOOT_SETJMP_H
+#define ETHERBOOT_SETJMP_H
+
+#ifndef __ASSEMBLER__
+/* Jump buffer contains v1-v6, sl, fp, sp and pc. Other registers are not
+ saved. */
+//typedef int jmp_buf[22];
+typedef int jmp_buf[10];
+#endif
+
+extern int sigsetjmp(jmp_buf __env, int __savemask);
+extern void longjmp(jmp_buf __env, int __val) __attribute__((__noreturn__));
+
+#define setjmp(env) sigsetjmp(env,0)
+
+#endif /* ETHERBOOT_SETJMP_H */
diff --git a/src/arch/armnommu/include/stdint.h b/src/arch/armnommu/include/stdint.h
new file mode 100644
index 00000000..3f5dc3f9
--- /dev/null
+++ b/src/arch/armnommu/include/stdint.h
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2004 Tobias Lorenz
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#ifndef STDINT_H
+#define STDINT_H
+
+typedef unsigned long size_t;
+
+typedef unsigned char uint8_t;
+typedef unsigned short uint16_t;
+typedef unsigned long uint32_t;
+typedef unsigned long long uint64_t;
+
+typedef signed char int8_t;
+typedef signed short int16_t;
+typedef signed int int32_t;
+typedef signed long long int64_t;
+
+#endif /* STDINT_H */
diff --git a/src/arch/e1/Config b/src/arch/e1/Config
new file mode 100644
index 00000000..69a7a978
--- /dev/null
+++ b/src/arch/e1/Config
@@ -0,0 +1,7 @@
+# Config for e1 Etherboot
+#
+
+CFLAGS+= -mgnu-param -DCONSOLE_SERIAL -DCOMCONSOLE=0x01C00000 -DCONSPEED=28800
+CFLAGS+= -DSIZEINDICATOR -DNO_DHCP_SUPPORT -DBAR_PROGRESS
+#CFLAGS+= -DEMBEDDED -DMAC_HW_ADDR_DRV="'H','Y','L','N','X','1'"
+
diff --git a/src/arch/e1/Makefile b/src/arch/e1/Makefile
new file mode 100644
index 00000000..a7c6f3b8
--- /dev/null
+++ b/src/arch/e1/Makefile
@@ -0,0 +1,70 @@
+ARCH_FORMAT= coff-e1
+
+BUILD_ROMS= $(ROMS)
+BUILD_COFFS= $(patsubst %img, %coff, $(IMGS))
+SUFFIXES+= rom zrom coff
+
+CC= e1-coff-gcc
+AS= e1-coff-as
+LD= e1-coff-ld
+SIZE= e1-coff-size
+AR= e1-coff-ar
+RANLIB= e1-coff-ranlib
+OBJCOPY=e1-coff-objcopy
+
+# DMAC_HW_ADDR_DRV holds the ethernet's MAC address. It is passed as
+# flag to the low level driver instead of reading it from an
+# external EEPROM, which we do not have!
+EXTRA_CFLAGS = -DEMBEDDED -DMAC_HW_ADDR_DRV="'H','Y','L','N','X','1'"
+
+START= $(BIN)/start.o
+START16= $(BIN)/start.o
+
+SRCS+= arch/e1/core/e132_xs.c
+SRCS+= arch/e1/core/e1_timer.c
+SRCS+= arch/e1/core/longjmp.c
+SRCS+= arch/e1/core/memcmp.S
+SRCS+= arch/e1/core/memcpy.S
+SRCS+= arch/e1/core/memset.S
+SRCS+= arch/e1/core/setjmp.c
+SRCS+= arch/e1/core/strcmp.S
+SRCS+= arch/e1/core/start.S
+
+ROMLIMIT:=3276800
+
+include $(BIN)/Roms
+
+ROMS= $(BIN)/cs89x0.rom
+IMGS= $(BIN)/cs89x0.img
+
+#allfiles: $(BUILD_ROMS)
+all: $(BUILD_COFFS)
+
+BOBJS+= $(BIN)/e1_timer.o
+BOBJS+= $(BIN)/memcmp.o $(BIN)/memcpy.o $(BIN)/memset.o
+BOBJS+= $(BIN)/setjmp.o $(BIN)/longjmp.o
+BOBJS+= $(BIN)/e132_xs.o
+
+# Utilities
+
+$(BIN)/nrv2b: util/nrv2b.c
+ $(HOST_CC) -O2 -DENCODE -DDECODE -DMAIN -DVERBOSE -DNDEBUG -DBITSIZE=32 -DENDIAN=0 -o $@ $<
+
+# Pattern Rules
+# General for compiling/assembly source files
+$(BIN)/cs89x0.o: drivers/net/cs89x0.c $(MAKEDEPS)
+ $(CC) $(CFLAGS) $(EXTRA_CFLAGS) -o $@ -c $<
+# With the current tools we have problem with the compilation
+# of the vsprintf file when the -O2 is selected. So we compile
+# the aforemntioned file with -O1 !!!
+$(BIN)/vsprintf.o: core/vsprintf.c $(MAKEDEPS)
+ $(CC) $(CFLAGS) -O1 -o $@ -c $<
+
+$(BIN)/%.o: arch/e1/core/%.c $(MAKEDEPS)
+ $(CC) $(CFLAGS) -o $@ -c $<
+
+$(BIN)/%.o: arch/e1/core/%.S $(MAKEDEPS)
+ $(CPP) $(CFLAGS) -D ASSEMBLY $< | $(AS) $(ASFLAGS) -o $@
+
+$(BIN)/%.coff: $(BIN)/%.tmp $(MAKEDEPS)
+ mv $< $(BIN)/etherboot.coff
diff --git a/src/arch/e1/Makefile.working b/src/arch/e1/Makefile.working
new file mode 100644
index 00000000..a0ebefe5
--- /dev/null
+++ b/src/arch/e1/Makefile.working
@@ -0,0 +1,67 @@
+ARCH_FORMAT= coff-e1
+
+CC= e1-coff-gcc
+AS= e1-coff-as
+LD= e1-coff-ld
+SIZE= e1-coff-size
+AR= e1-coff-ar
+RANLIB= e1-coff-ranlib
+OBJCOPY=e1-coff-objcopy
+
+EXTRA_CFLAGS = -DEMBEDDED -DMAC_HW_ADDR_DRV="'H','Y','L','N','X','1'"
+
+BUILD_ROMS= $(ROMS)
+BUILD_COFFS= $(BIN)/cs89x0.coff
+#BUILD_COFFS= $(patsubst %img, %coff, $(IMGS))
+
+START= $(BIN)/start.o
+START16= $(BIN)/start.o
+
+#SRCS+= arch/e1/core/coff_loader.c
+SRCS+= arch/e1/core/e132_xs.c
+SRCS+= arch/e1/core/e1_timer.c
+SRCS+= arch/e1/core/longjmp.c
+SRCS+= arch/e1/core/memcmp.S
+SRCS+= arch/e1/core/memcpy.S
+SRCS+= arch/e1/core/memset.S
+SRCS+= arch/e1/core/setjmp.c
+SRCS+= arch/e1/core/strcmp.S
+SRCS+= arch/e1/core/start.S
+
+ROMLIMIT:=3276800
+
+include $(BIN)/Roms
+
+hyperstone: $(BUILD_COFFS)
+coff: $(BUILD_COFFS)
+
+BOBJS+= $(BIN)/e1_timer.o
+BOBJS+= $(BIN)/memcmp.o $(BIN)/memcpy.o $(BIN)/memset.o
+BOBJS+= $(BIN)/setjmp.o $(BIN)/longjmp.o
+BOBJS+= $(BIN)/e132_xs.o
+
+# Utilities
+
+$(BIN)/nrv2b: util/nrv2b.c
+ $(HOST_CC) -O2 -DENCODE -DDECODE -DMAIN -DVERBOSE -DNDEBUG -DBITSIZE=32 -DENDIAN=0 -o $@ $<
+
+# Pattern Rules
+# General for compiling/assembly source files
+
+$(BIN)/cs89x0.o: drivers/net/cs89x0.c $(MAKEDEPS)
+ $(CC) $(CFLAGS) $(EXTRA_CFLAGS) -o $@ -c $<
+# With the current tools we have problem with the compilation
+# of the vsprintf file when the -O2 is selected. So we compile
+# the aforemntioned file with -O1 !!!
+$(BIN)/vsprintf.o: core/vsprintf.c $(MAKEDEPS)
+ $(CC) $(CFLAGS) -O1 -o $@ -c $<
+
+$(BIN)/%.o: arch/e1/core/%.c $(MAKEDEPS)
+ $(CC) $(CFLAGS) -o $@ -c $<
+
+$(BIN)/%.o: arch/e1/core/%.S $(MAKEDEPS)
+ $(CPP) $(CFLAGS) -D ASSEMBLY $< | $(AS) $(ASFLAGS) -o $@
+
+$(BIN)/%.coff: $(BIN)/%.tmp $(MAKEDEPS)
+ mv $< $(BIN)/etherboot.coff
+
diff --git a/src/arch/e1/README b/src/arch/e1/README
new file mode 100644
index 00000000..cdf676a8
--- /dev/null
+++ b/src/arch/e1/README
@@ -0,0 +1,80 @@
+Introduction
+---------------------
+This README file provides guideliness to compile successfully the
+Etherboot for Hyperstone.
+This directory (src/arch/e1) contains the files that depend on
+Hyperstone's architecture. The header files that include the
+configuration of the system are based on Hyperstone's E132-XS
+development board. The can be very easily modified to support
+anyother configuration environment.
+
+Software Perquisites:
+---------------------
+The build environment requires a C compiler for the E1-32XS
+processor. Normally you can simply install a cross-compiling tool
+chain based on the GNU tools (that is binutils and gcc). If you
+are running a Linux system on a x86 CPU then you can just download
+the toolchain as a binary from Hyperstone's official web-site. The
+binary distribution will untar the tools in the /usr/local/e1-coff
+directory. On any other system you will need to build the tool
+chain from the sources.
+
+To compile successfully the following tools should be available:
+ - GNU toolchain:
+ - GCC ver 2.95.2 20030110 (release) e1-coff-gcc -v
+ - LD ver 2.12.90 20020726 e1-coff-ld -V
+
+Hardware Perquisites:
+---------------------
+The etherboot has been successfully tested in the E1-32XS
+development board. A second serial device is initialized
+to act as a console. The standard messages
+are redirected to the console. Nevertheless, if one wants not
+to use the serial console he may omit the corresponding switches
+from the Config file located under "src/arch/e1/" directory.
+
+On the E1-32XS board that was used, a daughter-board was employed
+to connect a second HyIce to the development board. Since the HyIce
+embeds a standard 16550 device, the Etherboot's standard device
+driver is used.
+
+The position of the jumpers of the development board in order to
+initialize both the second HyIce and the Ethernet device is
+depicted in the following table:
+
+Jumper: Position
+------:--------------
+J3 1-2 (default)
+J4 1-2 (default)
+J13 5-6
+J5 1-2 (default)
+J6 1-2 & 3-4
+J7 3-4
+J9 1-2 (default)
+J10 1-2
+J11 3-4
+
+Compilation
+---------------------
+In order to compile Etherboot for Hyperstone, the following steps should be followed:
+1) Edit the main Makefile (located under "src" directory") and comment-out
+the ARCH variable (by putting a "#" in front of it). Append the following line:
+ARCH:=e1
+2) Edit the Config file (the one located under "src" directory) and make sure that
+the CFLAGS variable will contain *only* the following swithces:
+CFLAGS+= -DCONFIG_ISA
+CFLAGS+= -DBOOT_FIRST=BOOT_NIC
+CFLAGS+= -DALLOW_ONLY_ENCAPSULATED
+CFLAGS+= -DBACKOFF_LIMIT=7 -DCONGESTED
+CFLAGS+= -DCOFF_IMAGE
+CFLAGS+= -DDOWNLOAD_PROTO_TFTP
+Please note that extra or any other switches may cause failure of compilation!
+3) type "make hyperstone" or "make coff"
+4) the generated file will be located under the "src/bin" directory and will be called :
+ "etherboot.coff". Now you may download it with usual way using e1-coff-gdb ..
+
+Have Fun
+
+Yannis Mitsos, George Thanos
+{gmitsos,gthanos}@telecom.ntua.gr
+
diff --git a/src/arch/e1/core/coff_loader.c b/src/arch/e1/core/coff_loader.c
new file mode 100644
index 00000000..d628e5ed
--- /dev/null
+++ b/src/arch/e1/core/coff_loader.c
@@ -0,0 +1,176 @@
+/*
+ * Copyright 2003 Yannis Mitsos and George Thanos
+ * {gmitsos@gthanos}@telecom.ntua.gr
+ * Released under GPL2, see the file COPYING in the top directory
+ * COFF loader is based on the source code of the ELF loader.
+ *
+ */
+#include "coff.h"
+
+#define COFF_DEBUG 0
+
+typedef struct {
+ COFF_filehdr coff32;
+ COFF_opthdr opthdr32;
+ union {
+ COFF_scnhdr scnhdr32[1];
+ unsigned char dummy[1024];
+ } p;
+ unsigned long curaddr;
+ signed int segment; /* current segment number, -1 for none */
+ unsigned int loc; /* start offset of current block */
+ unsigned int skip; /* padding to be skipped to current segment */
+ unsigned long toread; /* remaining data to be read in the segment */
+}coff_state;
+
+coff_state cstate;
+
+static sector_t coff32_download(unsigned char *data, unsigned int len, int eof);
+static inline os_download_t coff_probe(unsigned char *data, unsigned int len)
+{
+ unsigned long phdr_size;
+
+ if (len < (sizeof(cstate.coff32)+ sizeof(cstate.opthdr32))) {
+ return 0;
+ }
+ memcpy(&cstate.coff32, data, (sizeof(cstate.coff32)+sizeof(cstate.opthdr32)));
+
+ if ((cstate.coff32.f_magic != EM_E1) ||
+ (cstate.opthdr32.magic != O_MAGIC)){
+ return 0;
+ }
+ printf("(COFF");
+ printf(")... \n");
+
+ if (cstate.coff32.f_opthdr == 0){
+ printf("No optional header in COFF file, cannot find the entry point\n");
+ return dead_download;
+ }
+
+ phdr_size = cstate.coff32.f_nscns * sizeof(cstate.p.scnhdr32);
+ if (sizeof(cstate.coff32) + cstate.coff32.f_opthdr + phdr_size > len) {
+ printf("COFF header outside first block\n");
+ return dead_download;
+ }
+
+ memcpy(&cstate.p.scnhdr32, data + (sizeof(cstate.coff32) + cstate.coff32.f_opthdr), phdr_size);
+
+ /* Check for Etherboot related limitations. Memory
+ * between _text and _end is not allowed.
+ * Reasons: the Etherboot code/data area.
+ */
+ for (cstate.segment = 0; cstate.segment < cstate.coff32.f_nscns; cstate.segment++) {
+ unsigned long start, mid, end, istart, iend;
+
+ if ((cstate.p.scnhdr32[cstate.segment].s_flags != S_TYPE_TEXT) &&
+ (cstate.p.scnhdr32[cstate.segment].s_flags != S_TYPE_DATA) &&
+ (cstate.p.scnhdr32[cstate.segment].s_flags != S_TYPE_BSS)){ /* Do we realy need to check the BSS section ? */
+#ifdef COFF_DEBUG
+ printf("Section <%s> in not a loadable section \n",cstate.p.scnhdr32[cstate.segment].s_name);
+#endif
+ continue;
+ }
+
+ start = cstate.p.scnhdr32[cstate.segment].s_paddr;
+ mid = start + cstate.p.scnhdr32[cstate.segment].s_size;
+ end = start + cstate.p.scnhdr32[cstate.segment].s_size;
+
+ /* Do we need the following variables ? */
+ istart = 0x8000;
+ iend = 0x8000;
+
+ if (!prep_segment(start, mid, end, istart, iend)) {
+ return dead_download;
+ }
+}
+ cstate.segment = -1;
+ cstate.loc = 0;
+ cstate.skip = 0;
+ cstate.toread = 0;
+ return coff32_download;
+}
+
+extern int mach_boot(unsigned long entry_point);
+static sector_t coff32_download(unsigned char *data, unsigned int len, int eof)
+{
+ unsigned long skip_sectors = 0;
+ unsigned int offset; /* working offset in the current data block */
+ int i;
+
+ offset = 0;
+ do {
+ if (cstate.segment != -1) {
+ if (cstate.skip) {
+ if (cstate.skip >= len - offset) {
+ cstate.skip -= len - offset;
+ break;
+ }
+ offset += cstate.skip;
+ cstate.skip = 0;
+ }
+
+ if (cstate.toread) {
+ unsigned int cplen;
+ cplen = len - offset;
+ if (cplen >= cstate.toread) {
+ cplen = cstate.toread;
+ }
+ memcpy(phys_to_virt(cstate.curaddr), data+offset, cplen);
+ cstate.curaddr += cplen;
+ cstate.toread -= cplen;
+ offset += cplen;
+ if (cstate.toread)
+ break;
+ }
+ }
+
+ /* Data left, but current segment finished - look for the next
+ * segment (in file offset order) that needs to be loaded.
+ * We can only seek forward, so select the program headers,
+ * in the correct order.
+ */
+ cstate.segment = -1;
+ for (i = 0; i < cstate.coff32.f_nscns; i++) {
+
+ if ((cstate.p.scnhdr32[i].s_flags != S_TYPE_TEXT) &&
+ (cstate.p.scnhdr32[i].s_flags != S_TYPE_DATA))
+ continue;
+ if (cstate.p.scnhdr32[i].s_size == 0)
+ continue;
+ if (cstate.p.scnhdr32[i].s_scnptr < cstate.loc + offset)
+ continue; /* can't go backwards */
+ if ((cstate.segment != -1) &&
+ (cstate.p.scnhdr32[i].s_scnptr >= cstate.p.scnhdr32[cstate.segment].s_scnptr))
+ continue; /* search minimum file offset */
+ cstate.segment = i;
+ }
+
+ if (cstate.segment == -1) {
+ /* No more segments to be loaded, so just start the
+ * kernel. This saves a lot of network bandwidth if
+ * debug info is in the kernel but not loaded. */
+ goto coff_startkernel;
+ break;
+ }
+ cstate.curaddr = cstate.p.scnhdr32[cstate.segment].s_paddr;
+ cstate.skip = cstate.p.scnhdr32[cstate.segment].s_scnptr - (cstate.loc + offset);
+ cstate.toread = cstate.p.scnhdr32[cstate.segment].s_size;
+#if COFF_DEBUG
+ printf("PHDR %d, size %#lX, curaddr %#lX\n",
+ cstate.segment, cstate.toread, cstate.curaddr);
+#endif
+ } while (offset < len);
+
+ cstate.loc += len + (cstate.skip & ~0x1ff);
+ skip_sectors = cstate.skip >> 9;
+ cstate.skip &= 0x1ff;
+
+ if (eof) {
+ unsigned long entry;
+coff_startkernel:
+ entry = cstate.opthdr32.entry;
+ done();
+ mach_boot(entry);
+ }
+ return skip_sectors;
+}
diff --git a/src/arch/e1/core/e132_xs.c b/src/arch/e1/core/e132_xs.c
new file mode 100644
index 00000000..cc4eaae2
--- /dev/null
+++ b/src/arch/e1/core/e132_xs.c
@@ -0,0 +1,70 @@
+/*
+ * Copyright 2003 Yannis Mitsos and George Thanos
+ * {gmitsos@gthanos}@telecom.ntua.gr
+ * Released under GPL2, see the file COPYING in the top directory
+ *
+ */
+#include "hooks.h"
+#include "io.h"
+#include "etherboot.h"
+#include "e132_xs_board.h"
+
+unsigned int io_periph[NR_CS] = {[0 ... NR_CS-1] = 0 };
+
+/*
+void arch_main(struct Elf_Bhdr *ptr __unused)
+{
+
+}
+*/
+
+void init_peripherals(void)
+{
+ int i;
+
+ for(i=0; i< NR_CS; i++){
+ io_periph[i]= (SLOW_IO_ACCESS | i << 22);
+ }
+
+ io_periph[ETHERNET_CS] = (io_periph[ETHERNET_CS] | 1 << IOWait);
+
+ asm volatile("
+ ori SR, 0x20
+ movi FCR, 0x66ffFFFF"
+ :
+ :);
+}
+
+struct meminfo meminfo;
+void get_memsizes(void)
+{
+/* We initialize the meminfo structure
+ * according to our development board's specs
+ * We do not have a way to automatically probe the
+ * memspace instead we initialize it manually
+ */
+ meminfo.basememsize = BASEMEM;
+ meminfo.memsize = SDRAM_SIZE;
+ meminfo.map_count = NR_MEMORY_REGNS;
+
+ meminfo.map[0].addr = SDRAM_BASEMEM;
+ meminfo.map[0].size = SDRAM_SIZE;
+ meminfo.map[0].type = E820_RAM;
+ meminfo.map[1].addr = SRAM_BASEMEM;
+ meminfo.map[1].size = SRAM_SIZE;
+ meminfo.map[1].type = E820_RAM;
+ meminfo.map[2].addr = IRAM_BASEMEM;
+ meminfo.map[2].size = IRAM_SIZE;
+ meminfo.map[2].type = E820_RAM;
+}
+
+int mach_boot(register unsigned long entry_point)
+{
+ asm volatile(
+ "mov PC, %0"
+ : /* no outputs */
+ : "l" (entry_point) );
+ return 0; /* We should never reach this point ! */
+
+}
+
diff --git a/src/arch/e1/core/e1_timer.c b/src/arch/e1/core/e1_timer.c
new file mode 100644
index 00000000..d3ddb644
--- /dev/null
+++ b/src/arch/e1/core/e1_timer.c
@@ -0,0 +1,94 @@
+/*
+ * Copyright 2003 Yannis Mitsos and George Thanos
+ * {gmitsos@gthanos}@telecom.ntua.gr
+ * Released under GPL2, see the file COPYING in the top directory
+ *
+ */
+#include "etherboot.h"
+#include "timer.h"
+#include "e132_xs_board.h"
+
+/* get timer returns the contents of the timer */
+static inline unsigned long get_timer(void)
+{
+ unsigned long result;
+ __asm__ __volatile__("
+ ORI SR, 0x20
+ mov %0, TR"
+ : "=l"(result));
+ return result;
+}
+
+/* ------ Calibrate the TSC -------
+ * Time how long it takes to excute a loop that runs in known time.
+ * And find the convertion needed to get to CLOCK_TICK_RATE
+ */
+
+static unsigned long configure_timer(void)
+{
+ unsigned long TPR_value; /* Timer Prescalar Value */
+
+ TPR_value = 0x000C00000;
+
+ asm volatile ("
+ FETCH 4
+ ORI SR, 0x20
+ MOV TPR, %0
+ ORI SR, 0x20
+ MOVI TR, 0x0"
+ : /* no outputs */
+ : "l" (TPR_value)
+ );
+
+ printf("The time prescaler register is set to: <%#x>\n",TPR_value);
+ return (1);
+}
+
+static unsigned long clocks_per_tick;
+
+void setup_timers(void)
+{
+ if (!clocks_per_tick) {
+ clocks_per_tick = configure_timer();
+ }
+}
+
+unsigned long currticks(void)
+{
+ return get_timer()/clocks_per_tick;
+}
+
+static unsigned long timer_timeout;
+static int __timer_running(void)
+{
+ return get_timer() < timer_timeout;
+}
+
+void udelay(unsigned int usecs)
+{
+ unsigned long now;
+ now = get_timer();
+ timer_timeout = now + usecs * ((clocks_per_tick * TICKS_PER_SEC)/(1000*1000));
+ while(__timer_running());
+}
+void ndelay(unsigned int nsecs)
+{
+ unsigned long now;
+ now = get_timer();
+ timer_timeout = now + nsecs * ((clocks_per_tick * TICKS_PER_SEC)/(1000*1000*1000));
+ while(__timer_running());
+}
+
+void load_timer2(unsigned int timer2_ticks)
+{
+ unsigned long now;
+ unsigned long clocks;
+ now = get_timer();
+ clocks = timer2_ticks * ((clocks_per_tick * TICKS_PER_SEC)/CLOCK_TICK_RATE);
+ timer_timeout = now + clocks;
+}
+
+int timer2_running(void)
+{
+ return __timer_running();
+}
diff --git a/src/arch/e1/core/etherboot.lds b/src/arch/e1/core/etherboot.lds
new file mode 100644
index 00000000..75d2dba1
--- /dev/null
+++ b/src/arch/e1/core/etherboot.lds
@@ -0,0 +1,126 @@
+/* Default linker script, for normal executables */
+OUTPUT_FORMAT("coff-e1-big")
+MEMORY
+ {
+ romvec : ORIGIN = 0x2000000, LENGTH = 0x0000400
+ flash : ORIGIN = 0xE0000000, LENGTH = 0x20000
+ eflash : ORIGIN = 0x2200000, LENGTH = 0x20000
+ ram : ORIGIN = 0x00000000, LENGTH = 0x1000000
+ eram16MB : ORIGIN = 0x01000000, LENGTH = 0
+ sram : ORIGIN = 0x40000000, LENGTH = 0x40000
+ iram : ORIGIN = 0xC0000000, LENGTH = 0x4000
+ }
+
+SEARCH_DIR("/usr/local/e1-coff/lib");
+ENTRY(Main)
+MEM0start = 0x00000000;
+MEM0size = 0x40000000;
+MEM1start = 0x40000000;
+MEM1size = 0x40000000;
+MEM2start = 0x80000000;
+MEM2size = 0x40000000;
+IRAMstart = 0xC0000000;
+IRAMsize = 0x20000000;
+MEM3start = 0xE0000000;
+MEM3size = 0x20000000;
+Stack1Reserve = 560;
+_Stack1Size = DEFINED(_Stack1Size)? _Stack1Size : 1*1024;
+_Stack2Size = DEFINED(_Stack2Size)? _Stack2Size : 16*1024;
+_Stack1Base = DEFINED(_Stack1Base)? _Stack1Base : __bss_end__;
+_Stack2Base = DEFINED(_Stack2Base)? _Stack2Base : __bss_end__ + _Stack1Size + Stack1Reserve;
+_Mem0HeapBase = DEFINED(_Mem0HeapBase)? _Mem0HeapBase : _Stack2Base + _Stack2Size;
+_Mem1HeapBase = DEFINED(_Mem1HeapBase)? _Mem1HeapBase : 0;
+Priority = DEFINED(Priority) ? Priority : 31;
+TextBase = DEFINED(TextBase) ? TextBase : 0xa00000;
+SECTIONS
+{
+ .G6 (DEFINED(G6Base) ? G6Base : 0x9000) : {
+ *(.G6)
+ }
+ .G7 (DEFINED(G7Base) ? G7Base : 0x40001000) : {
+ *(.G7)
+ }
+ .G8 (DEFINED(G8Base) ? G8Base : 0xC0000000) : {
+ *(.G8)
+ }
+ .G9 (DEFINED(G9Base) ? G9Base : 0) : {
+ *(.G9)
+ }
+ .G10 (DEFINED(G10Base) ? G10Base : 0) : {
+ *(.G10)
+ }
+ .G11 (DEFINED(G11Base) ? G11Base : 0) : {
+ *(.G11)
+ }
+ .G12 (DEFINED(G12Base) ? G12Base : 0) : {
+ *(.G12)
+ }
+ .G13 (DEFINED(G13Base) ? G13Base : 0) : {
+ *(.G13)
+ }
+
+ .text TextBase : {
+ __virt_start = .;
+ __text = . ;
+ *(.text)
+ *(.fini)
+ . = ALIGN(16);
+ _isa_drivers = . ;
+ *(.drivisa);
+ _isa_drivers_end = . ;
+ . = ALIGN(16);
+
+ *(.init)
+ _etext = . ;
+ /* _init = DEFINED(_init) ? _init : 0; */
+ /* _fini = DEFINED(_fini) ? _fini : 0; */
+ /* _argc = DEFINED(_argc) ? _argc : 0; */
+ /* _argv = DEFINED(_argv) ? _argv : 0; */
+ /* _envp = DEFINED(_envp) ? _envp : 0; */
+ /* _hwinit = DEFINED(_hwinit) ? _hwinit : 0; */
+ /* _atexit = DEFINED(_atexit) ? _atexit : 0; */
+ G6Size = SIZEOF(.G6);
+ G7Size = SIZEOF(.G7);
+ G8Size = SIZEOF(.G8);
+ G9Size = SIZEOF(.G9);
+ G10Size = SIZEOF(.G10);
+ G11Size = SIZEOF(.G11);
+ G12Size = SIZEOF(.G12);
+ G13Size = SIZEOF(.G13);
+
+}
+
+ .data SIZEOF(.text) + ADDR(.text) : {
+ *(.data)
+ _edata = . ;
+}
+
+ .bss SIZEOF(.data) + ADDR(.data) :
+ {
+ __bss_start__ = ALIGN( 0x10 ) ;
+ __bss = . ;
+ *(.bss)
+ *(COMMON)
+ __end = . ;
+ __bss_end__ = ALIGN( 0x10 ) ;
+ __ebss = . ;
+ }
+
+.eram16MB :
+ {
+ ___ramend = . - 0x7000;
+ } > eram16MB
+
+ .stab 0 (NOLOAD) :
+ {
+ [ .stab ]
+ }
+ .stabstr 0 (NOLOAD) :
+ {
+ [ .stabstr ]
+ }
+ _GOT_ 0 (NOLOAD) :
+ {
+ [ _GOT_ ]
+ }
+}
diff --git a/src/arch/e1/core/longjmp.c b/src/arch/e1/core/longjmp.c
new file mode 100644
index 00000000..1665f07d
--- /dev/null
+++ b/src/arch/e1/core/longjmp.c
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2003 Yannis Mitsos and George Thanos
+ * {gmitsos@gthanos}@telecom.ntua.gr
+ * Released under GPL2, see the file COPYING in the top directory
+ *
+ */
+#include "setjmp.h"
+
+unsigned long jmpbuf_ptr;
+
+void longjmp(jmp_buf state, int value )
+{
+ if(!value)
+ state->__jmpbuf->ReturnValue = 1;
+ else
+ state->__jmpbuf->ReturnValue = value;
+
+ jmpbuf_ptr = (unsigned long)state;
+
+#define _state_ ((struct __jmp_buf_tag*)jmpbuf_ptr)
+ asm volatile("mov L0, %0\n\t"
+ "mov L1, %1\n\t"
+ "mov L2, %2\n\t"
+ "mov G3, %3\n\t"
+ "mov G4, %4\n\t"
+ "ret PC, L1\n\t"
+ :/*no output*/
+ :"l"(_state_->__jmpbuf->ReturnValue),
+ "l"(_state_->__jmpbuf->SavedPC),
+ "l"(_state_->__jmpbuf->SavedSR),
+ "l"(_state_->__jmpbuf->G3),
+ "l"(_state_->__jmpbuf->G4)
+ :"%G3", "%G4", "%L0", "%L1" );
+#undef _state_
+}
diff --git a/src/arch/e1/core/memcmp.S b/src/arch/e1/core/memcmp.S
new file mode 100644
index 00000000..2347317a
--- /dev/null
+++ b/src/arch/e1/core/memcmp.S
@@ -0,0 +1,54 @@
+/*
+ * Derived from the Hyperstone's library source code.
+ * Modefied src in order to apply the -mgnu-param compiler option.
+ * Copyright (C) 2002-2003 GDT, Yannis Mitsos <gmitsos@telecom.ntua.gr>
+ * George Thanos <gthanos@telecom.ntua.gr>
+ */
+ .text
+ .align 2
+ .global _memcmp
+
+;ENTRY (_memcmp)
+_memcmp:
+ FRAME L9, L3 # get incoming parameters
+ CMPBI L2,3 # check word alignment
+ BNZ byte_compare
+ CMPBI L1,3 # check word alignment
+ BNZ byte_compare
+
+double_compare:
+ ADDI L0, -8
+ BLT is_equal
+ LDD.P L1, L5
+ LDD.P L2, L7
+ SUB L5, L7
+ DBNZ corr_8
+ SUB L6, L8
+ BZ double_compare
+ ADDI L0, 4
+ ADDI L2, -4
+ ADDI L1, -4
+ BR byte_compare
+
+corr_8: ADDI L0, 8
+ ADDI L2, -8
+ ADDI L1, -8
+byte_compare:
+ ADDI L0, -1
+ BLT equal
+ LDBU.N L2, L5, 1 # Load and compare bytes
+ LDBU.N L1, L6, 1
+ SUB L5, L6
+ BZ byte_compare
+ MOV L2, L5
+ RET PC, L3
+
+is_equal: CMPI L0, -8
+ DBNE byte_compare
+ ADDI L0, 8
+equal:
+ MOVI L2, 0
+ RET PC, L3
+
+ .END
+
diff --git a/src/arch/e1/core/memcpy.S b/src/arch/e1/core/memcpy.S
new file mode 100644
index 00000000..0581c9d1
--- /dev/null
+++ b/src/arch/e1/core/memcpy.S
@@ -0,0 +1,79 @@
+/*
+ * Derived from the Hyperstone's library source code.
+ * Modefied src in order to apply the -mgnu-param compiler option.
+ * Copyright (C) 2002-2003 GDT, Yannis Mitsos <gmitsos@telecom.ntua.gr>
+ * George Thanos <gthanos@telecom.ntua.gr>
+ */
+ .text
+ .align 2
+ .global _memcpy
+;ENTRY(_memcpy)
+_memcpy:
+ FRAME L8, L3
+ MOV L7, L2 # Save for return
+
+#*****************************
+# Perform byte copy if both
+# not on a word alignment
+#*****************************
+ CMPBI L2, 3 # check word alignment
+ BNZ mem_except
+ CMPBI L1, 3 # check word alignment
+ BNZ mem_except
+
+#*****************************
+# Copy Double,Word,Halfword,
+# then byte
+#*****************************
+DBL_LOOP:
+ CMPI L0, 8 # Copy Doubles
+ BLT DO_WORD
+ LDD.P L1, L5
+ ADDI L0, -8
+ DBR DBL_LOOP
+ STD.P L2, L5
+
+DO_WORD:
+ CMPI L0, 4 # Copy leftover word
+ BLT DO_HALF
+ LDW.P L1, L5
+ ADDI L0, -4
+ DBZ DONE # Done if L0 is 0
+ STW.P L2, L5
+
+DO_HALF:
+ CMPI L0, 2 # Copy leftover byte
+ BLT DO_BYTE
+ LDHU.N L1, L5, 2
+ ADDI L0, -2
+ DBZ DONE # Done if L0 is 0
+ STHU.N L2, L5, 2
+
+DO_BYTE:
+ CMPI L0, 1 # Copy leftover byte
+ BLT DONE
+ LDBU.D L1, L5, 0
+ STBU.D L2, L5, 0
+
+DONE: # Copy done
+ MOV L2, L7 # Return pointer
+ RET PC, L3
+
+#****************************
+# Byte memcpy
+#****************************
+mem_except:
+ DBR L_5
+ MOVI L6,0
+L_3:
+ LDBS.D L1, L5, 0 # Transfer the byte
+ ADDI L6, 1
+ STBS.D L2, L5, 0
+ ADDI L2, 1
+ ADDI L1, 1
+L_5: # Loop test
+ CMP L6, L0
+ BST L_3
+ MOV L2, L7 # Return pointer
+ RET PC, L3
+ .END
diff --git a/src/arch/e1/core/memset.S b/src/arch/e1/core/memset.S
new file mode 100644
index 00000000..a8ceb535
--- /dev/null
+++ b/src/arch/e1/core/memset.S
@@ -0,0 +1,47 @@
+/*
+ * Derived from the Hyperstone's library source code.
+ * Modefied src in order to apply the -mgnu-param compiler option.
+ * Copyright (C) 2002-2003 GDT, Yannis Mitsos <gmitsos@telecom.ntua.gr>
+ * George Thanos <gthanos@telecom.ntua.gr>
+ */
+ .text
+ .align 2
+ .global _memset
+
+;ENTRY(_memset)
+_memset: FRAME L9, L3
+ MASK L5, L1, 0xFF
+ MOV L8, L2
+ CMPI L0, 0 # if n = 0 then return
+ BE retour
+
+loop0: CMPBI L8, 0x3
+ BZ word_bound
+ ADDI L0, -1
+ DBNZ loop0
+ STBU.N L8, L5, 1
+retour: RET PC, L3
+
+word_bound:
+ CMPI L0, 8
+ DBLT loop2
+ MOV L7, L5
+ SHLI L7, 8
+ OR L5, L7
+ MOV L7, L5
+ SHLI L7, 16
+ OR L5, L7
+ MOV L6, L5
+loop1: ADDI L0, -8
+ CMPI L0, 8
+ DBGE loop1
+ STD.P L8, L5
+ CMPI L0, 0
+ DBNZ loop2
+ ANDNI L5, ~ 0xFF
+ RET PC, L3
+
+loop2: ADDI L0, -1
+ DBNZ loop2
+ STBU.N L8, L5, 1
+ RET PC, L3
diff --git a/src/arch/e1/core/setjmp.c b/src/arch/e1/core/setjmp.c
new file mode 100644
index 00000000..63bcc484
--- /dev/null
+++ b/src/arch/e1/core/setjmp.c
@@ -0,0 +1,26 @@
+/*
+ * Copyright 2003 Yannis Mitsos and George Thanos
+ * {gmitsos@gthanos}@telecom.ntua.gr
+ * Released under GPL2, see the file COPYING in the top directory
+ *
+ */
+#include "setjmp.h"
+
+int setjmp( jmp_buf state)
+{
+ asm volatile( "mov %0, G3\n\t"
+ "mov %1, G4\n\t"
+ :"=l"(state->__jmpbuf->G3),
+ "=l"(state->__jmpbuf->G4)
+ :/*no input*/
+ :"%G3", "%G4" );
+
+ asm volatile( "setadr %0\n\t"
+ "mov %1, L1\n\t"
+ "mov %2, L2\n\t"
+ :"=l"(state->__jmpbuf->SavedSP),
+ "=l"(state->__jmpbuf->SavedPC),
+ "=l"(state->__jmpbuf->SavedSR)
+ :/*no input*/);
+ return 0;
+}
diff --git a/src/arch/e1/core/start.S b/src/arch/e1/core/start.S
new file mode 100644
index 00000000..691f39bd
--- /dev/null
+++ b/src/arch/e1/core/start.S
@@ -0,0 +1,111 @@
+/*
+ * Derived from the Hyperstone's library source code.
+ * Copyright (C) 2002-2003 GDT, Yannis Mitsos <gmitsos@telecom.ntua.gr>
+ * George Thanos <gthanos@telecom.ntua.gr>
+ */
+ .global Priority ; Task-Priority
+ .global _Stack1Size ; Size of hardware stack
+ .global _Stack2Size ; Size of aggregate stack
+ .global _Stack1Base ; Base of hardware stack
+ .global _Stack2Base ; Base of aggregate stack
+ .global _Mem0HeapBase ; Base of Heap in Mem0
+ .global _Mem1HeapBase ; Base of Heap in Mem1
+
+ .global _init_peripherals
+ .global _main
+ .global Main
+
+ .global __exit
+ .global __fmode
+ .global __MaxArgCount
+
+ .text
+BasePtrs:
+ .weak G6Base,G7Base,G8Base,G9Base,G10Base,G11Base,G12Base,G13Base
+ .long G6Base,G7Base,G8Base,G9Base,G10Base,G11Base,G12Base,G13Base
+BasePtrsEnd:
+HeapPtrs:
+ .long _Mem0HeapBase
+ .long _Mem1HeapBase
+HeapPtrsEnd:
+
+__MaxArgCount:
+ .long 32
+__StandAloneMode:
+ .long 0 ; 0 indicate stand alone mode
+
+;============================================================================;
+; Startup-Code ;
+;============================================================================;
+ .data
+
+; do not change the order of: __argc,..
+__argc:
+ .long 0
+__argv:
+ .long 0
+__IsShell:
+ .long 1
+ErrorLevel:
+ .long 0
+__lab:
+ .long 0
+__fmode:
+ .long 0x4000 ; O_TEXT attribute
+
+_isa_drivers:
+ .long 0
+
+_isa_drivers_end:
+ .long 0
+
+
+ .text
+Main:
+StartUp:
+ FRAME L5, L0
+ MOVI L2, __bss_start__ ; clear the .bss segment
+0: CMPI L2, __bss_end__
+ BHE 0f
+ DBR 0b
+ STW.P L2, 0
+0: SUM L2, PC, BasePtrs-$ ; Load BasePtrs G6-G13
+ LDD.P L2, G6
+ LDD.P L2, G8
+; LDD.P L2, G10
+ LDD.P L2, G12
+ MOVI L2, 1
+ SUM L3, PC, __StandAloneMode-$
+ STW.R L3, L2
+
+ ;----------------------------------------------------------------;
+ ; Call main C function ;
+ ;----------------------------------------------------------------;
+2: LDW.D PC, L2, __argc - $ ; pass count of arguments to main
+ LDW.D PC, L3, __argv - $ ; pass pointer array to main
+ CALL L4, PC, _init_peripherals - $
+ CALL L4, PC, _main - $ ; --> Call Main-Program
+ CHK PC, PC ; trap if execution arrives here
+
+__exit:
+ FRAME L5, L1
+ STW.D PC, L0, ErrorLevel - $ ; Store ERRORLEVEL
+
+ CHK PC, PC
+ RET PC, L1
+
+ .global ___main
+___main:
+ FRAME L4, L1
+ MOVI L3, 2
+ STW.D PC, L3, __StandAloneMode-$
+ RET PC, L1 ; does not return
+
+ .section _GOT_
+ .long Main+4 ; OnCreate
+ .long Main+8 ; OnError
+ .long BasePtrs ; G6
+ .long BasePtrs+4 ; G7
+ .long BasePtrs+8 ; G8
+
+ .END
diff --git a/src/arch/e1/core/strcmp.S b/src/arch/e1/core/strcmp.S
new file mode 100644
index 00000000..5f09daa7
--- /dev/null
+++ b/src/arch/e1/core/strcmp.S
@@ -0,0 +1,76 @@
+/*
+ * Derived from the Hyperstone's library source code.
+ * Modefied src in order to apply the -mgnu-param compiler option.
+ * Copyright (C) 2002-2003 GDT, Yannis Mitsos <gmitsos@telecom.ntua.gr>
+ * George Thanos <gthanos@telecom.ntua.gr>
+ */
+ .text
+ .align 2
+ .global _strcmp
+;ENTRY(_strcmp)
+_strcmp:
+ FRAME L8,L2
+ CMPBI L1, 3 # check for word alignment
+ BNZ str_except
+ CMPBI L0, 3 # check for word alignment
+ BNZ str_except
+
+start:
+ LDD.P L1, L4 # post inc mode
+ LDD.P L0, L6 # post inc mode
+ CMPBI L4, ANYBZ
+ BE correct1
+ CMP L4, L6
+ BNE correct1
+ CMP L5, L7
+ BNE correct
+ CMPBI L5, ANYBZ
+ BE correct
+ CMPBI L6, ANYBZ
+ BE correct1
+ CMPBI L7, ANYBZ
+ BNE start
+
+correct: MASK L4, L5, 0xff000000
+ MASK L6, L7, 0xff000000
+ CMP L4, L6
+ BNE Exit
+ SHLI L5, 8
+ CMPI L4, 0
+ DBNE correct
+ SHLI L7, 8
+ MOV L1, L4
+ RET PC, L2
+
+Exit: SUB L4, L6 # Subtract chars
+ SARI L4, 24
+ MOV L1, L4
+ RET PC, L2
+
+correct1: MASK L5, L4, 0xff000000
+ MASK L7, L6, 0xff000000
+ CMP L5, L7
+ BNE Exit1
+ SHLI L4, 8
+ CMPI L5, 0
+ DBNE correct1
+ SHLI L6, 8
+ MOV L1, L5
+ RET PC, L2
+Exit1: SUB L5, L7 # Subtract chars
+ SARI L5, 24
+ MOV L1, L5
+ RET PC, L2
+
+testzero: CMPI L4, 0
+ BE L_5
+str_except:
+ LDBU.N L1, L4, 1 # Load *s1, compare bytes
+ LDBU.N L0, L5, 1 # Load *s2, compare bytes
+ CMP L4, L5
+ BE testzero
+ SUB L4, L5 # Subtract chars
+L_5: MOV L1, L4
+ RET PC, L2
+ .END
+
diff --git a/src/arch/e1/include/bits/byteswap.h b/src/arch/e1/include/bits/byteswap.h
new file mode 100644
index 00000000..1d1a7d2f
--- /dev/null
+++ b/src/arch/e1/include/bits/byteswap.h
@@ -0,0 +1,39 @@
+#ifndef ETHERBOOT_BITS_BYTESWAP_H
+#define ETHERBOOT_BITS_BYTESWAP_H
+
+/* We do not have byte swap functions ... We are
+ * RISC processor ...
+ */
+
+static inline unsigned short __swap16(volatile unsigned short v)
+{
+ return ((v << 8) | (v >> 8));
+}
+
+static inline unsigned int __swap32(volatile unsigned long v)
+{
+ return ((v << 24) | ((v & 0xff00) << 8) | ((v & 0xff0000) >> 8) | (v >> 24));
+}
+
+#define __bswap_constant_16(x) \
+ ((uint16_t)((((uint16_t)(x) & 0x00ff) << 8) | \
+ (((uint16_t)(x) & 0xff00) >> 8)))
+
+#define __bswap_constant_32(x) \
+ ((uint32_t)((((uint32_t)(x) & 0x000000ffU) << 24) | \
+ (((uint32_t)(x) & 0x0000ff00U) << 8) | \
+ (((uint32_t)(x) & 0x00ff0000U) >> 8) | \
+ (((uint32_t)(x) & 0xff000000U) >> 24)))
+
+#define __bswap_16(x) \
+ (__builtin_constant_p(x) ? \
+ __bswap_constant_16(x) : \
+ __swap16(x))
+
+
+#define __bswap_32(x) \
+ (__builtin_constant_p(x) ? \
+ __bswap_constant_32(x) : \
+ __swap32(x))
+
+#endif /* ETHERBOOT_BITS_BYTESWAP_H */
diff --git a/src/arch/e1/include/bits/cpu.h b/src/arch/e1/include/bits/cpu.h
new file mode 100644
index 00000000..f25c009a
--- /dev/null
+++ b/src/arch/e1/include/bits/cpu.h
@@ -0,0 +1,6 @@
+#ifndef E1_BITS_CPU_H
+#define E1_BITS_CPU_H
+
+#define cpu_setup() do {} while(0)
+
+#endif /* E1_BITS_CPU_H */
diff --git a/src/arch/e1/include/bits/elf.h b/src/arch/e1/include/bits/elf.h
new file mode 100644
index 00000000..aa40e110
--- /dev/null
+++ b/src/arch/e1/include/bits/elf.h
@@ -0,0 +1,6 @@
+#ifndef E1_BITS_ELF_H
+#define E1_BITS_ELF_H
+
+/* dummy file, needed for the compilation of core/nic.c */
+
+#endif /* E1_BITS_ELF_H */
diff --git a/src/arch/e1/include/bits/endian.h b/src/arch/e1/include/bits/endian.h
new file mode 100644
index 00000000..4145518b
--- /dev/null
+++ b/src/arch/e1/include/bits/endian.h
@@ -0,0 +1,6 @@
+#ifndef ETHERBOOT_BITS_ENDIAN_H
+#define ETHERBOOT_BITS_ENDIAN_H
+
+#define __BYTE_ORDER __BIG_ENDIAN
+
+#endif /* ETHERBOOT_BITS_ENDIAN_H */
diff --git a/src/arch/e1/include/bits/string.h b/src/arch/e1/include/bits/string.h
new file mode 100644
index 00000000..b6df2fcb
--- /dev/null
+++ b/src/arch/e1/include/bits/string.h
@@ -0,0 +1,35 @@
+#ifndef ETHERBOOT_BITS_STRING_H
+#define ETHERBOOT_BITS_STRING_H
+
+/* define inline optimized string functions here */
+
+#define __HAVE_ARCH_MEMCPY
+//extern void * memcpy(const void *d, const void *s, size_t count);
+
+#define __HAVE_ARCH_MEMCMP
+//extern int memcmp(const void * s ,const void * d ,size_t );
+
+#define __HAVE_ARCH_MEMSET
+//extern void * memset(const void * s, int c, size_t count);
+
+#define __HAVE_ARCH_MEMMOVE
+static inline void *memmove(void *s1, const void *s2, size_t n) {
+
+ unsigned int i;
+ char *tmp = s1;
+ char *cs2 = (char *) s2;
+
+ if (tmp < cs2) {
+ for(i=0; i<n; ++i, ++tmp, ++cs2)
+ *tmp = *cs2;
+ }
+ else {
+ tmp += n - 1;
+ cs2 += n - 1;
+ for(i=0; i<n; ++i, --tmp, --cs2)
+ *tmp = *cs2;
+ }
+ return(s1);
+}
+
+#endif /* ETHERBOOT_BITS_STRING_H */
diff --git a/src/arch/e1/include/e132_xs_board.h b/src/arch/e1/include/e132_xs_board.h
new file mode 100644
index 00000000..257cfc37
--- /dev/null
+++ b/src/arch/e1/include/e132_xs_board.h
@@ -0,0 +1,22 @@
+#ifndef __E132_XS_BOARD_H
+#define __E132_XS_BOARD_H
+
+#define CONFIG_HYPERSTONE_OSC_FREQ_MHZ 15
+
+#define NR_MEMORY_REGNS 3
+#define BASEMEM 0x0
+
+/* SDRAM mapping */
+#define SDRAM_SIZE 0x01000000
+#define SDRAM_BASEMEM BASEMEM
+
+/* SRAM mapping */
+#define SRAM_BASEMEM 0x40000000
+#define SRAM_SIZE 0x0003FFFF
+
+/* IRAM mapping */
+#define IRAM_BASEMEM 0xC0000000
+#define IRAM_SIZE 0x00003FFF
+
+
+#endif /* __E132_XS_BOARD_H */
diff --git a/src/arch/e1/include/hooks.h b/src/arch/e1/include/hooks.h
new file mode 100644
index 00000000..a67aa193
--- /dev/null
+++ b/src/arch/e1/include/hooks.h
@@ -0,0 +1,9 @@
+#ifndef ETHERBOOT_E1_HOOKS_H
+#define ETHERBOOT_E1_HOOKS_H
+
+#define arch_main(data,params) do {} while(0)
+#define arch_on_exit(status) do {} while(0)
+#define arch_relocate_to(addr) do {} while(0)
+#define arch_relocated_from(old_addr) do {} while(0)
+
+#endif /* ETHERBOOT_E1_HOOKS_H */
diff --git a/src/arch/e1/include/io.h b/src/arch/e1/include/io.h
new file mode 100644
index 00000000..acf940ce
--- /dev/null
+++ b/src/arch/e1/include/io.h
@@ -0,0 +1,210 @@
+#ifndef ETHERBOOT_IO_H
+#define ETHERBOOT_IO_H
+
+/* Don't require identity mapped physical memory,
+ * osloader.c is the only valid user at the moment.
+ */
+#if 0
+static inline unsigned long virt_to_phys(volatile const void *virt_addr)
+{
+ return ((unsigned long)virt_addr);
+}
+#else
+#define virt_to_phys(vaddr) ((unsigned long) (vaddr))
+#endif
+
+#if 0
+static inline void *phys_to_virt(unsigned long phys_addr)
+{
+ return (void *)(phys_addr);
+}
+#else
+#define phys_to_virt(vaddr) ((void *) (vaddr))
+#endif
+
+/* virt_to_bus converts an addresss inside of etherboot [_start, _end]
+ * into a memory address cards can use.
+ */
+#define virt_to_bus virt_to_phys
+
+/* bus_to_virt reverses virt_to_bus, the address must be output
+ * from virt_to_bus to be valid. This function does not work on
+ * all bus addresses.
+ */
+#define bus_to_virt phys_to_virt
+
+#define iounmap(addr) ((void)0)
+#define ioremap(physaddr, size) (physaddr)
+
+#define IORegAddress 13
+#define IOWait 11
+#define IOSetupTime 8
+#define IOAccessTime 5
+#define IOHoldTime 3
+
+#define SLOW_IO_ACCESS ( 0x3 << IOSetupTime | 0x0 << IOWait | 7 << IOAccessTime | 3 << IOHoldTime )
+
+/* The development board can generate up to 15 Chip selects */
+#define NR_CS 16
+
+extern unsigned int io_periph[NR_CS];
+#define ETHERNET_CS 4
+
+static inline unsigned short _swapw(volatile unsigned short v)
+{
+ return ((v << 8) | (v >> 8));
+}
+
+static inline unsigned int _swapl(volatile unsigned long v)
+{
+ return ((v << 24) | ((v & 0xff00) << 8) | ((v & 0xff0000) >> 8) | (v >> 24));
+}
+
+#define hy_inpw(addr) \
+ ({ register unsigned long dummy, dummy1; \
+ dummy = addr; \
+ asm volatile ("LDW.IOD %1, %0, 0" \
+ : "=l" (dummy1) \
+ : "l" (dummy)); dummy1; })
+
+
+#define hy_outpw(x, addr) \
+ ({ register unsigned long dummy0,dummy1; \
+ dummy0 = addr; \
+ dummy1 = x; \
+ asm volatile ("STW.IOD %1, %0, 0" \
+ : "=l" (dummy1) \
+ : "l"(dummy0), "l" (dummy1)); dummy1; })
+
+#define readb(addr) ({ unsigned char __v = inregb(addr); __v; })
+#define readw(addr) ({ unsigned short __v = inregw(addr); __v; })
+#define readl(addr) ({ unsigned long __v = inregl(addr); __v; })
+
+#define writeb(b,addr) (void)(outreg(b, addr))
+#define writew(b,addr) (void)(outreg(b, addr))
+#define writel(b,addr) (void)(outreg(b, addr))
+
+static inline unsigned long common_io_access(unsigned long addr)
+{
+ return io_periph[(addr & 0x03C00000) >> 22];
+}
+
+static inline volatile unsigned char inregb(volatile unsigned long reg)
+{
+ unsigned char val;
+
+ val = hy_inpw(common_io_access(reg) | ((0xf & reg) << IORegAddress));
+ return val;
+}
+
+static inline volatile unsigned short inregw(volatile unsigned long reg)
+{
+ unsigned short val;
+
+ val = hy_inpw(common_io_access(reg) | ((0xf & reg) << IORegAddress));
+ return val;
+}
+
+static inline volatile unsigned long inregl(volatile unsigned long reg)
+{
+ unsigned long val;
+
+ val = hy_inpw(common_io_access(reg) | ((0xf & reg) << IORegAddress));
+ return val;
+}
+
+static inline void outreg(volatile unsigned long val, volatile unsigned long reg)
+{
+
+ hy_outpw(val, (common_io_access(reg) | ((0xf & reg) << IORegAddress)));
+}
+
+static inline void io_outsb(unsigned int addr, void *buf, int len)
+{
+ unsigned long tmp;
+ unsigned char *bp = (unsigned char *) buf;
+
+ tmp = (common_io_access(addr)) | ((0xf & addr) << IORegAddress);
+
+ while (len--){
+ hy_outpw(_swapw(*bp++), tmp);
+ }
+}
+
+static inline void io_outsw(volatile unsigned int addr, void *buf, int len)
+{
+ unsigned long tmp;
+ unsigned short *bp = (unsigned short *) buf;
+
+ tmp = (common_io_access(addr)) | ((0xf & addr) << IORegAddress);
+
+ while (len--){
+ hy_outpw(_swapw(*bp++), tmp);
+ }
+}
+
+static inline void io_outsl(volatile unsigned int addr, void *buf, int len)
+{
+ unsigned long tmp;
+ unsigned int *bp = (unsigned int *) buf;
+
+ tmp = (common_io_access(addr)) | ((0xf & addr) << IORegAddress);
+
+ while (len--){
+ hy_outpw(_swapl(*bp++), tmp);
+ }
+}
+
+static inline void io_insb(volatile unsigned int addr, void *buf, int len)
+{
+ unsigned long tmp;
+ unsigned char *bp = (unsigned char *) buf;
+
+ tmp = (common_io_access(addr)) | ((0xf & addr) << IORegAddress);
+
+ while (len--)
+ *bp++ = hy_inpw((unsigned char) tmp);
+
+}
+
+static inline void io_insw(unsigned int addr, void *buf, int len)
+{
+ unsigned long tmp;
+ unsigned short *bp = (unsigned short *) buf;
+
+ tmp = (common_io_access(addr)) | ((0xf & addr) << IORegAddress);
+
+ while (len--)
+ *bp++ = _swapw((unsigned short)hy_inpw(tmp));
+
+}
+
+static inline void io_insl(unsigned int addr, void *buf, int len)
+{
+ unsigned long tmp;
+ unsigned int *bp = (unsigned int *) buf;
+
+ tmp = (common_io_access(addr)) | ((0xf & addr) << IORegAddress);
+
+ while (len--)
+ *bp++ = _swapl((unsigned int)hy_inpw(tmp));
+}
+
+#define inb(addr) readb(addr)
+#define inw(addr) readw(addr)
+#define inl(addr) readl(addr)
+#define outb(x,addr) ((void) writeb(x,addr))
+#define outw(x,addr) ((void) writew(x,addr))
+#define outl(x,addr) ((void) writel(x,addr))
+
+#define insb(a,b,l) io_insb(a,b,l)
+#define insw(a,b,l) io_insw(a,b,l)
+#define insl(a,b,l) io_insl(a,b,l)
+#define outsb(a,b,l) io_outsb(a,b,l)
+#define outsw(a,b,l) io_outsw(a,b,l)
+#define outsl(a,b,l) io_outsl(a,b,l)
+
+#define memcpy_fromio(a,b,c) memcpy((a),(void *)(b),(c))
+#define memcpy_toio(a,b,c) memcpy((void *)(a),(b),(c))
+
+#endif /* ETHERBOOT_IO_H */
diff --git a/src/arch/e1/include/latch.h b/src/arch/e1/include/latch.h
new file mode 100644
index 00000000..0ee6fb2a
--- /dev/null
+++ b/src/arch/e1/include/latch.h
@@ -0,0 +1,12 @@
+#ifndef LATCH_H
+#define LATCH_H
+
+//#define TICKS_PER_SEC (1000000UL)
+#define TICKS_PER_SEC (625000UL)
+
+/* Fixed timer interval used for calibrating a more precise timer */
+//#define LATCHES_PER_SEC 10
+
+void sleep_latch(void);
+
+#endif /* LATCH_H */
diff --git a/src/arch/e1/include/limits.h b/src/arch/e1/include/limits.h
new file mode 100644
index 00000000..e7056ce5
--- /dev/null
+++ b/src/arch/e1/include/limits.h
@@ -0,0 +1,34 @@
+/*--------------------------------------------------------------------------*/
+/* Project: ANSI C Standard Header Files */
+/* File: LIMITS.H */
+/* Edited by: hyperstone electronics GmbH */
+/* Am Seerhein 8 */
+/* D-78467 Konstanz, Germany */
+/* Date: January 30, 1996 */
+/*--------------------------------------------------------------------------*/
+/* Purpose: */
+/* The header file <limits.h> defines limits of ordinal types */
+/* (char, short, int, long) */
+/*--------------------------------------------------------------------------*/
+
+#ifndef __LIMITS_H
+#define __LIMITS_H 1
+
+#define MB_LEN_MAX 1
+#define CHAR_BIT 8
+#define SCHAR_MIN -128L
+#define SCHAR_MAX 127L
+#define UCHAR_MAX 255
+#define CHAR_MIN 0
+#define CHAR_MAX UCHAR_MAX
+#define SHRT_MIN -32768
+#define SHRT_MAX 32767
+#define USHRT_MAX 65535
+#define INT_MIN 0x80000000
+#define INT_MAX 0x7FFFFFFF
+#define UINT_MAX 0xFFFFFFFFL
+#define LONG_MIN INT_MIN
+#define LONG_MAX INT_MAX
+#define ULONG_MAX UINT_MAX
+
+#endif
diff --git a/src/arch/e1/include/setjmp.h b/src/arch/e1/include/setjmp.h
new file mode 100644
index 00000000..ef401b62
--- /dev/null
+++ b/src/arch/e1/include/setjmp.h
@@ -0,0 +1,23 @@
+#ifndef _SETJMP_H
+#define _SETJMP_H
+
+
+typedef struct {
+ unsigned long G3;
+ unsigned long G4;
+ unsigned long SavedSP;
+ unsigned long SavedPC;
+ unsigned long SavedSR;
+ unsigned long ReturnValue;
+} __jmp_buf[1];
+
+typedef struct __jmp_buf_tag /* C++ doesn't like tagless structs. */
+ {
+ __jmp_buf __jmpbuf; /* Calling environment. */
+ int __mask_was_saved; /* Saved the signal mask? */
+ } jmp_buf[1];
+
+void longjmp(jmp_buf state, int value );
+int setjmp( jmp_buf state);
+
+#endif
diff --git a/src/arch/e1/include/stdint.h b/src/arch/e1/include/stdint.h
new file mode 100644
index 00000000..8a7ad978
--- /dev/null
+++ b/src/arch/e1/include/stdint.h
@@ -0,0 +1,16 @@
+#ifndef STDINT_H
+#define STDINT_H
+
+typedef unsigned long size_t;
+
+typedef unsigned char uint8_t;
+typedef unsigned short uint16_t;
+typedef unsigned long uint32_t;
+typedef unsigned long long uint64_t;
+
+typedef signed char int8_t;
+typedef signed short int16_t;
+typedef signed int int32_t;
+typedef signed long long int64_t;
+
+#endif /* STDINT_H */
diff --git a/src/arch/i386/Config b/src/arch/i386/Config
new file mode 100644
index 00000000..e8ac91b8
--- /dev/null
+++ b/src/arch/i386/Config
@@ -0,0 +1,131 @@
+# Config for i386 Etherboot
+#
+# Do not delete the tag OptionDescription and /OptionDescription
+# It is used to automatically generate the documentation.
+#
+# @OptionDescrition@
+#
+# BIOS interface options:
+#
+# -DPCBIOS
+# Compile in support for the normal pcbios
+# -DLINUXBIOS
+# Compile in support for LinuxBIOS
+# -DBBS_BUT_NOT_PNP_COMPLIANT
+# Some BIOSes claim to be PNP but they don't conform
+# to the BBS spec which specifies that ES:DI must
+# point to the string $PnP on entry. This option
+# works around those. This option must be added to
+# LCONFIG.
+# -DNO_DELAYED_INT
+# Take control as soon as BIOS detects the ROM.
+# Normally hooks onto INT18H or INT19H. Use only if you
+# have a very non-conformant BIOS as it bypasses
+# BIOS initialisation of devices. This only works for
+# legacy ROMs, i.e. PCI_PNP_HEADER not defined.
+# This option was formerly called NOINT19H.
+# -DBOOT_INT18H
+# Etherboot normally hooks onto INT19H for legacy ROMs.
+# You can choose to hook onto INT18H (BASIC interpreter
+# entry point) instead. This entry point is used when
+# all boot devices have been exhausted. This option must
+# be added to LCONFIG.
+# -DCONFIG_PCI_DIRECT
+# Define this for PCI BIOSes that do not implement
+# BIOS32 or not correctly. Normally not needed.
+# Only works for BIOSes of a certain era.
+# -DCONFIG_TSC_CURRTICKS
+# Uses the processor time stamp counter instead of reading
+# the BIOS time counter. This allows Etherboot to work
+# even without a BIOS. This only works on late model
+# 486s and above.
+# -DCONFIG_NO_TIMER2
+# Some systems do not have timer2 implemented.
+# If you have a RTC this will allow you to roughly calibrate
+# it using outb instructions.
+# -DIBM_L40
+# This option uses the 0x92 method of controlling
+# A20 instead of the traditional method of using the
+# keyboard controller. An explanation of A20 is here:
+# http://www.win.tue.nl/~aeb/linux/kbd/A20.html
+# This occurs on MCA, EISA and some embedded boards,
+# and sometimes with the Fast Gate A20 option on some
+# BIOSes.
+# Enable this only if you are sure of what you are doing.
+#
+# Extended cpu options
+
+# -DCONFIG_X86_64
+# Compile in support for booting x86_64 64bit binaries.
+#
+# PXE loader options:
+#
+# -DPXELOADER_KEEP_ALL
+# Prevent PXE loader (prefix) from unloading the
+# PXE stack. You will want to use this if, for
+# example, you are booting via PXE-on-floppy.
+# You may want to use it under certain
+# circumstances when using the Etherboot UNDI
+# driver; these are complex and best practice is
+# not yet established.
+#
+# Obscure options you probably don't need to touch:
+#
+# -DIGNORE_E820_MAP
+# Ignore the memory map returned by the E820 BIOS
+# call. May be necessary on some buggy BIOSes.
+# -DT503_AUI
+# Use AUI by default on 3c503 cards.
+# -DFLATTEN_REAL_MODE
+# Use 4GB segment limits when calling out to or
+# returning to real-mode code. This is necessary to
+# work around some buggy code (e.g. OpenBSD's pxeboot)
+# that uses flat real-mode without being sufficiently
+# paranoid about the volatility of its segment limits.
+
+#
+# @/OptionDescription@
+
+# BIOS select don't change unless you know what you are doing
+CFLAGS+= -DPCBIOS
+
+# Compile in k8/hammer support
+# CFLAGS+= -DCONFIG_X86_64
+
+# Options to make a version of Etherboot that will work under linuxBIOS.
+# CFLAGS+= -DLINUXBIOS -DCONFIG_TSC_CURRTICKS -DCONSOLE_SERIAL -DCOMCONSOLE=0x3f8 -DCOMPRESERVE -DCONFIG_PCI_DIRECT -DELF_IMAGE
+
+# These options affect the loader that is prepended to the Etherboot image
+# LCONFIG+= -DBBS_BUT_NOT_PNP_COMPLIANT
+# LCONFIG+= -DBOOT_INT18H
+
+# Produce code that will work inside the Bochs emulator. The pnic
+# driver is probably the best one to try.
+# CFLAGS+= -DCONFIG_PCI_DIRECT
+
+# Produce code that will work with OpenBSD's pxeboot
+# CFLAGS+= -DFLATTEN_REAL_MODE
+
+CFLAGS+= -fstrength-reduce -fomit-frame-pointer -march=i386
+# Squeeze the code in as little space as possible.
+# gcc3 needs a different syntax to gcc2 if you want to avoid spurious warnings.
+GCC_VERSION = $(subst ., ,$(shell $(CC) -dumpversion))
+GCC_MAJORVERSION = $(firstword $(GCC_VERSION))
+ifeq ($(GCC_MAJORVERSION),2)
+CFLAGS+= -malign-jumps=1 -malign-loops=1 -malign-functions=1
+else
+CFLAGS+= -falign-jumps=1 -falign-loops=1 -falign-functions=1
+endif
+GCC_MINORVERSION = $(word 2, $(GCC_VERSION))
+ifneq ($(GCC_MINORVERSION),4)
+CFLAGS+= -mcpu=i386
+endif
+
+LDFLAGS+= -N
+
+ifeq "$(shell uname -s)" "FreeBSD"
+CFLAGS+= -DIMAGE_FREEBSD -DELF_IMAGE -DAOUT_IMAGE
+endif
+
+# An alternate location for isolinux.bin can be set here
+# ISOLINUX_BIN=/path/to/isolinux.bin
diff --git a/src/arch/i386/Makefile b/src/arch/i386/Makefile
new file mode 100644
index 00000000..cded46c3
--- /dev/null
+++ b/src/arch/i386/Makefile
@@ -0,0 +1,373 @@
+ARCH_FORMAT= elf32-i386
+
+# For debugging, don't delete intermediates
+#.SECONDARY:
+
+LDSCRIPT= arch/i386/core/etherboot.lds
+PLDSCRIPT= arch/i386/core/etherboot.prefix.lds
+
+LCONFIG+= -Ui386
+
+ROMLIMIT= 524288
+CHECKSIZE= { read d1; read d1 d2 d3 size d4; [ $$size -gt $(ROMLIMIT) ] &&\
+ { $(RM) $@; echo "ERROR: code size exceeds limit!"; exit 1; }; exit 0; }
+
+START= $(BIN)/start32.o $(BIN)/linuxbios.o \
+ $(BIN)/bios.o $(BIN)/console.o $(BIN)/memsizes.o $(BIN)/basemem.o \
+ $(BIN)/hidemem.o $(BIN)/e820mangler.o \
+ $(BIN)/realmode.o $(BIN)/realmode_asm.o \
+ $(BIN)/callbacks.o $(BIN)/pxe_callbacks.o
+
+SRCS+= arch/i386/prefix/floppyprefix.S
+SRCS+= arch/i386/prefix/unhuf.S
+SRCS+= arch/i386/prefix/unnrv2b.S
+SRCS+= arch/i386/firmware/pcbios/bios.c
+SRCS+= arch/i386/firmware/pcbios/console.c
+SRCS+= arch/i386/firmware/pcbios/memsizes.c
+SRCS+= arch/i386/firmware/pcbios/basemem.c
+SRCS+= arch/i386/firmware/pcbios/hidemem.c
+SRCS+= arch/i386/firmware/pcbios/e820mangler.S
+SRCS+= arch/i386/prefix/liloprefix.S
+SRCS+= arch/i386/prefix/elfprefix.S
+SRCS+= arch/i386/prefix/lmelf_prefix.S
+SRCS+= arch/i386/prefix/elf_dprefix.S
+SRCS+= arch/i386/prefix/lmelf_dprefix.S
+SRCS+= arch/i386/prefix/comprefix.S
+SRCS+= arch/i386/prefix/exeprefix.S
+SRCS+= arch/i386/prefix/pxeprefix.S
+SRCS+= arch/i386/prefix/romprefix.S
+
+SRCS+= arch/i386/core/init.S
+SRCS+= arch/i386/core/start32.S
+SRCS+= arch/i386/core/pci_io.c
+SRCS+= arch/i386/core/i386_timer.c
+SRCS+= arch/i386/core/elf.c
+SRCS+= arch/i386/core/cpu.c
+SRCS+= arch/i386/core/video_subr.c
+SRCS+= arch/i386/core/pic8259.c
+SRCS+= arch/i386/core/hooks.c
+SRCS+= arch/i386/core/callbacks.c
+SRCS+= arch/i386/core/realmode.c
+SRCS+= arch/i386/core/realmode_asm.S
+SRCS+= arch/i386/core/pxe_callbacks.c
+
+# ROM loaders: ISA and PCI versions
+ISAPREFIX= $(BIN)/isaprefix.o
+ISAENTRY= $(BIN)/isaprefix.entry.o
+ISAEXIT= $(BIN)/isaprefix.exit.o
+PCIPREFIX= $(BIN)/pciprefix.o
+PCIENTRY= $(BIN)/pciprefix.entry.o
+PCIEXIT= $(BIN)/pciprefix.exit.o
+# Variables xxx_ROMTYPE are defined by genrules.pl. ROMENTRY and
+# ROMEXIT will evaluate to give the correct objects to use.
+TARGETBASE=$(patsubst $(BIN)/%,%,$(firstword $(subst ., ,$@)))
+ROMCARD=$(firstword $(subst --, ,$(TARGETBASE)))
+ROMTYPE=$(firstword $(ROMTYPE_$(ROMCARD)) ISA)
+romENTRY=$($(ROMTYPE)ENTRY)
+romEXIT=$($(ROMTYPE)EXIT)
+
+# Target type for generic prf rules
+TARGETTYPE=$(patsubst .%,%, $(suffix $(basename $@)))
+TARGETENTRY=$($(TARGETTYPE)ENTRY)
+TARGETEXIT=$($(TARGETTYPE)EXIT)
+
+# Other real-mode entry loaders
+dskPREFIX= $(BIN)/floppyprefix.o
+dskENTRY= $(BIN)/floppyprefix.entry.o
+dskEXIT= $(BIN)/floppyprefix.exit.o
+comPREFIX= $(BIN)/comprefix.o
+comENTRY= $(BIN)/comprefix.entry.o
+comEXIT= $(BIN)/comprefix.exit.o
+exePREFIX= $(BIN)/exeprefix.o
+exeENTRY= $(BIN)/exeprefix.entry.o
+exeEXIT= $(BIN)/exeprefix.exit.o
+liloPREFIX= $(BIN)/liloprefix.o
+liloENTRY= $(BIN)/liloprefix.entry.o
+liloEXIT= $(BIN)/liloprefix.exit.o
+bImagePREFIX= $(BIN)/bImageprefix.o
+bImageENTRY= $(BIN)/bImageprefix.entry.o
+bImageEXIT= $(BIN)/bImageprefix.exit.o
+pxePREFIX= $(BIN)/pxeprefix.o
+pxeENTRY= $(BIN)/pxeprefix.entry.o
+pxeEXIT= $(BIN)/pxeprefix.exit.o
+rawPREFIX= $(BIN)/nullprefix.o
+rawENTRY= $(BIN)/nullprefix.entry.o
+rawEXIT= $(BIN)/nullprefix.exit.o
+
+# Protected mode entry loaders
+elfPREFIX= $(BIN)/elfprefix.o
+elfENTRY= $(BIN)/elfprefix.entry.o
+elfEXIT= $(BIN)/elfprefix.exit.o
+lmelfPREFIX= $(BIN)/lmelf_prefix.o
+lmelfENTRY= $(BIN)/lmelf_prefix.entry.o
+lmelfEXIT= $(BIN)/lmelf_prefix.exit.o
+elfdPREFIX= $(BIN)/elf_dprefix.o
+elfdENTRY= $(BIN)/elf_dprefix.entry.o
+elfdEXIT= $(BIN)/elf_dprefix.exit.o
+lmelfdPREFIX= $(BIN)/lmelf_dprefix.o
+lmelfdENTRY= $(BIN)/lmelf_dprefix.entry.o
+lmelfdEXIT= $(BIN)/lmelf_dprefix.exit.o
+
+include $(BIN)/Roms
+
+all: $(ROMS)
+allroms: $(ROMS)
+allzroms: $(ROMS)
+alldsks: $(EB_DSKS)
+allzdsks: $(EB_ZDSKS)
+alllilos: $(EB_LILOS)
+allzlilos: $(EB_ZLILOS)
+allbImages: $(EB_BIMAGES)
+allbzImages: $(EB_BZIMAGES)
+allpxes: $(EB_PXES)
+allzpxes: $(EB_ZPXES)
+allelfs: $(EB_ELFS)
+allzelfs: $(EB_ZELFS)
+alllmelfs: $(EB_LMELFS)
+allzlmelfs: $(EB_ZLMELFS)
+allelfds: $(EB_ELFDS)
+allzelfds: $(EB_ZELFDS)
+alllmelfds: $(EB_LMELFDS)
+allzlmelfds: $(EB_ZLMELFDS)
+allcoms: $(EB_COMS)
+allexes: $(EB_EXES)
+allisos: $(EB_ISOS)
+alllisos: $(EB_LISOS)
+
+BOBJS+= $(BIN)/pci_io.o $(BIN)/i386_timer.o
+BOBJS+= $(BIN)/elf.o $(BIN)/cpu.o $(BIN)/video_subr.o
+BOBJS+= $(BIN)/pic8259.o $(BIN)/hooks.o
+
+# ROM loaders
+
+$(ISAPREFIX): arch/i386/prefix/romprefix.S $(MAKEDEPS)
+ $(CPP) $(CFLAGS) $(LCONFIG) -Ui386 -D ASSEMBLY $< \
+ | $(AS) $(ASFLAGS) -o $@
+
+$(PCIPREFIX): arch/i386/prefix/romprefix.S $(MAKEDEPS)
+ $(CPP) -DPCI_PNP_HEADER $(CFLAGS) $(LCONFIG) -Ui386 -D ASSEMBLY $< \
+ | $(AS) $(ASFLAGS) -o $@
+
+# Prefix splitters
+$(BIN)/%prefix.entry.o: $(BIN)/%prefix.o $(MAKEDEPS)
+ $(OBJCOPY) -R .text16 $< $@
+
+$(BIN)/%prefix.exit.o: $(BIN)/%prefix.o $(MAKEDEPS)
+ $(OBJCOPY) -R .prefix $< $@
+
+# Generic prefix objects
+PREFIXOBJS = $(BIN)/init.o
+ZPREFIXOBJS = $(BIN)/init.o $(BIN)/unnrv2b.o
+
+# Utilities
+$(BIN)/nrv2b: util/nrv2b.c
+ $(HOST_CC) -O2 -DENCODE -DDECODE -DMAIN -DVERBOSE -DNDEBUG -DBITSIZE=32 -DENDIAN=0 -o $@ $<
+
+ZFILELEN = perl util/zfilelen.pl
+
+# Pattern Rules
+
+# General for compiling/assembly source files
+
+$(BIN)/%.o: arch/i386/core/%.c $(MAKEDEPS)
+ $(CC) $(CFLAGS) -o $@ -c $<
+
+$(BIN)/%.o: arch/i386/core/%.S $(MAKEDEPS)
+ $(CPP) $(CFLAGS) -Ui386 -D ASSEMBLY $< | $(AS) $(ASFLAGS) -o $@
+
+$(BIN)/%.o: arch/i386/firmware/pcbios/%.c $(MAKEDEPS)
+ $(CC) $(CFLAGS) -o $@ -c $<
+
+$(BIN)/%.o: arch/i386/firmware/pcbios/%.S $(MAKEDEPS)
+ $(CPP) $(CFLAGS) -Ui386 -D ASSEMBLY $< | $(AS) $(ASFLAGS) -o $@
+
+$(BIN)/%.o: arch/i386/prefix/%.S $(MAKEDEPS)
+ $(CPP) $(CFLAGS) -Ui386 -D ASSEMBLY $< | $(AS) $(ASFLAGS) -o $@
+
+# general rule for 16bit .o, may be overridden
+$(BIN)/%.o: $(BIN)/%.s
+ $(AS) $(ASFLAGS) -o $@ $<
+
+# general rule for .bin (plain binary loader code), may be overridden
+$(BIN)/%.bin: $(BIN)/%.o
+ $(OBJCOPY) -O binary $< $@
+
+# general rule for .z (compressed binary code), may be overridden
+# rule for .z is in top level Makefile
+# Give the directory name, e.g. use $(BIN)/rtl8139.com as the target.
+
+$(BIN)/%.zo: $(BIN)/%.zbin arch/i386/core/prefixzdata.lds $(MAKEDEPS)
+ $(LD) -T arch/i386/core/prefixzdata.lds -b binary $< -o $@
+
+$(BIN)/%.uo: $(BIN)/%.bin arch/i386/core/prefixudata.lds $(MAKEDEPS)
+ $(LD) -T arch/i386/core/prefixudata.lds -b binary $< -o $@
+
+# Intermediate prf rules
+
+%.prf: %.rt $(PREFIXOBJS) %.rt1.uo %.rt2.uo $(MAKEDEPS)
+ $(MAKE) $(TARGETENTRY)
+ $(LD) $(LDFLAGS) -T $(PLDSCRIPT) $(TARGETENTRY) -R $(subst $(MAKEDEPS),,$^) -o $@
+
+%.zprf: %.rt $(ZPREFIXOBJS) %.rt1.uo %.rt2.zo $(MAKEDEPS)
+ $(MAKE) $(TARGETENTRY)
+ $(LD) $(LDFLAGS) -T $(PLDSCRIPT) $(TARGETENTRY) -R $(subst $(MAKEDEPS),,$^) -o $@
+
+# general rules for normal/compressed ROM images, may be overridden
+SUFFIXES += rom zrom
+
+$(BIN)/%.rom.rt: $(BIN)/%.rt.o $(ISAENTRY) $(PCIENTRY) $(ISAEXIT) $(PCIEXIT) $(LDSCRIPT) $(MAKEDEPS)
+ $(LD) $(LDFLAGS) -T $(LDSCRIPT) -o $@ $(romEXIT) $<
+ @$(SIZE) $@ | $(CHECKSIZE)
+
+$(BIN)/%.rom: $(BIN)/%.rom.prf
+ $(OBJCOPY) -O binary $< $@
+ $(MAKEROM) $(MAKEROM_FLAGS) $(MAKEROM_$(ROMCARD)) $(MAKEROM_ID_$(ROMCARD)) -i$(IDENT) $@
+
+$(BIN)/%.zrom: $(BIN)/%.rom.zprf
+ $(OBJCOPY) -O binary $< $@
+ $(MAKEROM) $(MAKEROM_FLAGS) $(MAKEROM_$(ROMCARD)) $(MAKEROM_ID_$(ROMCARD)) -i$(IDENT) $@
+
+# general rules for ELF images
+SUFFIXES += elf zelf
+$(BIN)/%.elf.rt: $(BIN)/%.rt.o $(elfENTRY) $(elfEXIT) $(LDSCRIPT) $(MAKEDEPS)
+ $(LD) $(LDFLAGS) -T $(LDSCRIPT) -o $@ $(elfEXIT) $<
+
+$(BIN)/%.elf: $(BIN)/%.elf.prf
+ $(OBJCOPY) -O binary $< $@
+
+$(BIN)/%.zelf: $(BIN)/%.elf.zprf
+ $(OBJCOPY) -O binary $< $@
+
+# general rules for Long Mode ELF images
+SUFFIXES += lmelf zlmelf
+$(BIN)/%.lmelf.rt: $(BIN)/%.rt.o $(lmelfENTRY) $(lmelfEXIT) $(LDSCRIPT) $(MAKEDEPS)
+ $(LD) $(LDFLAGS) -T $(LDSCRIPT) -o $@ $(lmelfEXIT) $<
+
+$(BIN)/%.lmelf: $(BIN)/%.lmelf.prf
+ $(OBJCOPY) -O binary $< $@
+
+$(BIN)/%.zlmelf: $(BIN)/%.lmelf.zprf
+ $(OBJCOPY) -O binary $< $@
+
+# general rules for ELF dynamic images
+SUFFIXES += elfd zelfd
+$(BIN)/%.elfd.rt: $(BIN)/%.rt.o $(elfdENTRY) $(elfdEXIT) $(LDSCRIPT) $(MAKEDEPS)
+ $(LD) $(LDFLAGS) -T $(LDSCRIPT) -o $@ $(elfdEXIT) $<
+
+$(BIN)/%.elfd: $(BIN)/%.elfd.prf
+ $(OBJCOPY) -O binary $< $@
+
+$(BIN)/%.zelfd: $(BIN)/%.elfd.zprf
+ $(OBJCOPY) -O binary $< $@
+
+# general rules for Long Mode ELF dynamic images
+SUFFIXES += lmelfd zlmelfd
+$(BIN)/%.lmelfd.rt: $(BIN)/%.rt.o $(lmelfdENTRY) $(lmelfdEXIT) $(LDSCRIPT) $(MAKEDEPS)
+ $(LD) $(LDFLAGS) -T $(LDSCRIPT) -o $@ $(lmelfdEXIT) $<
+
+$(BIN)/%.lmelfd: $(BIN)/%.lmelfd.prf
+ $(OBJCOPY) -O binary $< $@
+
+$(BIN)/%.zlmelfd: $(BIN)/%.lmelfd.zprf
+ $(OBJCOPY) -O binary $< $@
+
+# rules to generate a DOS loadable .com executable
+SUFFIXES += com
+$(BIN)/%.com.rt: $(BIN)/%.rt.o $(comENTRY) $(comEXIT) $(LDSCRIPT) $(MAKEDEPS)
+ $(LD) $(LDFLAGS) -T $(LDSCRIPT) -o $@ $< $(comEXIT)
+
+$(BIN)/%.com: $(BIN)/%.com.zprf
+ $(OBJCOPY) -O binary $< $@
+
+# rules to generate a DOS loadable .exe executable
+SUFFIXES += exe
+$(BIN)/%.exe.rt: $(BIN)/%.rt.o $(exeENTRY) $(exeEXIT) $(LDSCRIPT) $(MAKEDEPS)
+ $(LD) $(LDFLAGS) -T $(LDSCRIPT) -o $@ $< $(exeEXIT)
+ @$(SIZE) $@ | $(CHECKSIZE)
+
+$(BIN)/%.exe: $(BIN)/%.exe.prf
+ $(OBJCOPY) -O binary $< $@
+
+# rules to make a LILO loadable image
+SUFFIXES += lilo zlilo
+
+$(BIN)/%.lilo.rt: $(BIN)/%.rt.o $(liloENTRY) $(liloEXIT) $(LDSCRIPT) $(MAKEDEPS)
+ $(LD) $(LDFLAGS) -T $(LDSCRIPT) -o $@ $< $(liloEXIT)
+ @$(SIZE) $@ | $(CHECKSIZE)
+
+$(BIN)/%.lilo: $(BIN)/%.lilo.prf
+ $(OBJCOPY) -O binary $< $@
+
+$(BIN)/%.zlilo: $(BIN)/%.lilo.zprf
+ $(OBJCOPY) -O binary $< $@
+
+# rules to make big linux boot protocol image
+SUFFIXES += bImage bzImage
+
+$(BIN)/%.bImage.rt: $(BIN)/%.rt.o $(bImageENTRY) $(bImageEXIT) $(LDSCRIPT) $(MAKEDEPS)
+ $(LD) $(LDFLAGS) -T $(LDSCRIPT) -o $@ $< $(bImageEXIT)
+
+$(BIN)/%.bImage: $(BIN)/%.bImage.prf
+ $(OBJCOPY) -O binary $< $@
+
+$(BIN)/%.bzImage: $(BIN)/%.bImage.zprf
+ $(OBJCOPY) -O binary $< $@
+
+
+# rules to generate a PXE loadable image
+SUFFIXES += pxe zpxe
+
+$(BIN)/%.pxe.rt: $(BIN)/%.rt.o $(pxeENTRY) $(pxeEXIT) $(LDSCRIPT) $(MAKEDEPS)
+ $(LD) $(LDFLAGS) -T $(LDSCRIPT) -o $@ $< $(pxeEXIT)
+ @$(SIZE) $@ | $(CHECKSIZE)
+
+$(BIN)/%.pxe: $(BIN)/%.pxe.prf
+ $(OBJCOPY) -O binary $< $@
+
+$(BIN)/%.zpxe: $(BIN)/%.pxe.zprf
+ $(OBJCOPY) -O binary $< $@
+
+# rules to generate the .dsk/.zdsk floppy images
+SUFFIXES += dsk zdsk
+
+$(BIN)/%.dsk.rt: $(BIN)/%.rt.o $(dskENTRY) $(dskEXIT) $(LDSCRIPT) $(MAKEDEPS)
+ $(LD) $(LDFLAGS) -T $(LDSCRIPT) -o $@ $< $(dskEXIT)
+ @$(SIZE) $@ | $(CHECKSIZE)
+
+$(BIN)/%.dsk: $(BIN)/%.dsk.prf
+ $(OBJCOPY) -O binary $< $@
+
+$(BIN)/%.zdsk: $(BIN)/%.dsk.zprf
+ $(OBJCOPY) -O binary $< $@
+
+# rules to write the .dsk/.zdsk image onto a blank floppy
+SUFFIXES += fd0 zfd0
+%.fd0: %.dsk
+ dd if=$< bs=512 conv=sync of=/dev/fd0
+ sync
+
+%.zfd0: %.zdsk
+ dd if=$< bs=512 conv=sync of=/dev/fd0
+ sync
+
+# rules to create raw executable images
+SUFFIXES += raw zraw
+$(BIN)/%.raw.rt: $(BIN)/%.rt.o $(rawENTRY) $(rawEXIT) $(LDSCRIPT) $(MAKEDEPS)
+ $(LD) $(LDFLAGS) -T $(LDSCRIPT) -o $@ $< $(rawEXIT)
+
+$(BIN)/%.raw: $(BIN)/%.raw.prf
+ $(OBJCOPY) -O binary $< $@
+
+$(BIN)/%.zraw: $(BIN)/%.raw.zprf
+ $(OBJCOPY) -O binary $< $@
+
+# rule to make a non-emulation ISO boot image
+SUFFIXES += iso
+%.iso: util/geniso %.zlilo
+ ISOLINUX_BIN=${ISOLINUX_BIN} bash util/geniso $*.iso $*.zlilo
+
+# rule to make a floppy emulation ISO boot image
+SUFFIXES += liso
+%.liso: util/genliso %.zlilo
+ bash util/genliso $*.liso $*.zlilo
+
diff --git a/src/arch/i386/core/aout_loader.c b/src/arch/i386/core/aout_loader.c
new file mode 100644
index 00000000..f85620e9
--- /dev/null
+++ b/src/arch/i386/core/aout_loader.c
@@ -0,0 +1,144 @@
+/* a.out */
+struct exec {
+ unsigned long a_midmag; /* flags<<26 | mid<<16 | magic */
+ unsigned long a_text; /* text segment size */
+ unsigned long a_data; /* initialized data size */
+ unsigned long a_bss; /* uninitialized data size */
+ unsigned long a_syms; /* symbol table size */
+ unsigned long a_entry; /* entry point */
+ unsigned long a_trsize; /* text relocation size */
+ unsigned long a_drsize; /* data relocation size */
+};
+
+struct aout_state {
+ struct exec head;
+ unsigned long curaddr;
+ int segment; /* current segment number, -1 for none */
+ unsigned long loc; /* start offset of current block */
+ unsigned long skip; /* padding to be skipped to current segment */
+ unsigned long toread; /* remaining data to be read in the segment */
+};
+
+static struct aout_state astate;
+
+static sector_t aout_download(unsigned char *data, unsigned int len, int eof);
+static inline os_download_t aout_probe(unsigned char *data, unsigned int len)
+{
+ unsigned long start, mid, end, istart, iend;
+ if (len < sizeof(astate.head)) {
+ return 0;
+ }
+ memcpy(&astate.head, data, sizeof(astate.head));
+ if ((astate.head.a_midmag & 0xffff) != 0x010BL) {
+ return 0;
+ }
+
+ printf("(a.out");
+ aout_freebsd_probe();
+ printf(")... ");
+ /* Check the aout image */
+ start = astate.head.a_entry;
+ mid = (((start + astate.head.a_text) + 4095) & ~4095) + astate.head.a_data;
+ end = ((mid + 4095) & ~4095) + astate.head.a_bss;
+ istart = 4096;
+ iend = istart + (mid - start);
+ if (!prep_segment(start, mid, end, istart, iend))
+ return dead_download;
+ astate.segment = -1;
+ astate.loc = 0;
+ astate.skip = 0;
+ astate.toread = 0;
+ return aout_download;
+}
+
+static sector_t aout_download(unsigned char *data, unsigned int len, int eof)
+{
+ unsigned int offset; /* working offset in the current data block */
+
+ offset = 0;
+
+#ifdef AOUT_LYNX_KDI
+ astate.segment++;
+ if (astate.segment == 0) {
+ astate.curaddr = 0x100000;
+ astate.head.a_entry = astate.curaddr + 0x20;
+ }
+ memcpy(phys_to_virt(astate.curaddr), data, len);
+ astate.curaddr += len;
+ return 0;
+#endif
+
+ do {
+ if (astate.segment != -1) {
+ if (astate.skip) {
+ if (astate.skip >= len - offset) {
+ astate.skip -= len - offset;
+ break;
+ }
+ offset += astate.skip;
+ astate.skip = 0;
+ }
+
+ if (astate.toread) {
+ if (astate.toread >= len - offset) {
+ memcpy(phys_to_virt(astate.curaddr), data+offset,
+ len - offset);
+ astate.curaddr += len - offset;
+ astate.toread -= len - offset;
+ break;
+ }
+ memcpy(phys_to_virt(astate.curaddr), data+offset, astate.toread);
+ offset += astate.toread;
+ astate.toread = 0;
+ }
+ }
+
+ /* Data left, but current segment finished - look for the next
+ * segment. This is quite simple for a.out files. */
+ astate.segment++;
+ switch (astate.segment) {
+ case 0:
+ /* read text */
+ astate.curaddr = astate.head.a_entry;
+ astate.skip = 4096;
+ astate.toread = astate.head.a_text;
+ break;
+ case 1:
+ /* read data */
+ /* skip and curaddr may be wrong, but I couldn't find
+ * examples where this failed. There is no reasonable
+ * documentation for a.out available. */
+ astate.skip = ((astate.curaddr + 4095) & ~4095) - astate.curaddr;
+ astate.curaddr = (astate.curaddr + 4095) & ~4095;
+ astate.toread = astate.head.a_data;
+ break;
+ case 2:
+ /* initialize bss and start kernel */
+ astate.curaddr = (astate.curaddr + 4095) & ~4095;
+ astate.skip = 0;
+ astate.toread = 0;
+ memset(phys_to_virt(astate.curaddr), '\0', astate.head.a_bss);
+ goto aout_startkernel;
+ default:
+ break;
+ }
+ } while (offset < len);
+
+ astate.loc += len;
+
+ if (eof) {
+ unsigned long entry;
+
+aout_startkernel:
+ entry = astate.head.a_entry;
+ done(1);
+
+ aout_freebsd_boot();
+#ifdef AOUT_LYNX_KDI
+ xstart32(entry);
+#endif
+ printf("unexpected a.out variant\n");
+ longjmp(restart_etherboot, -2);
+ }
+ return 0;
+}
diff --git a/src/arch/i386/core/callbacks.c b/src/arch/i386/core/callbacks.c
new file mode 100644
index 00000000..d45e63e2
--- /dev/null
+++ b/src/arch/i386/core/callbacks.c
@@ -0,0 +1,107 @@
+/* Callout/callback interface for Etherboot
+ *
+ * This file provides the mechanisms for making calls from Etherboot
+ * to external programs and vice-versa.
+ *
+ * Initial version by Michael Brown <mbrown@fensystems.co.uk>, January 2004.
+ */
+
+#include "etherboot.h"
+#include "callbacks.h"
+#include "realmode.h"
+#include "segoff.h"
+#include <stdarg.h>
+
+/* Maximum amount of stack data that prefix may request to be passed
+ * to its exit routine
+ */
+#define MAX_PREFIX_STACK_DATA 16
+
+/* Prefix exit routine is defined in prefix object */
+extern void prefix_exit ( void );
+extern void prefix_exit_end ( void );
+
+/*****************************************************************************
+ *
+ * IN_CALL INTERFACE
+ *
+ *****************************************************************************
+ */
+
+/* in_call(): entry point for calls in to Etherboot from external code.
+ *
+ * Parameters: some set up by assembly code _in_call(), others as
+ * passed from external code.
+ */
+uint32_t i386_in_call ( va_list ap, i386_pm_in_call_data_t pm_data,
+ uint32_t opcode ) {
+ uint32_t ret;
+ i386_rm_in_call_data_t rm_data;
+ in_call_data_t in_call_data = { &pm_data, NULL };
+ struct {
+ int data[MAX_PREFIX_STACK_DATA/4];
+ } in_stack;
+
+ /* Fill out rm_data if we were called from real mode */
+ if ( opcode & EB_CALL_FROM_REAL_MODE ) {
+ in_call_data.rm = &rm_data;
+ rm_data = va_arg ( ap, typeof(rm_data) );
+ /* Null return address indicates to use the special
+ * prefix exit mechanism, and that there are
+ * parameters on the stack that the prefix wants
+ * handed to its exit routine.
+ */
+ if ( rm_data.ret_addr.offset == 0 ) {
+ int n = va_arg ( ap, int ) / 4;
+ int i;
+ for ( i = 0; i < n; i++ ) {
+ in_stack.data[i] = va_arg ( ap, int );
+ }
+ }
+ }
+
+ /* Hand off to main in_call() routine */
+ ret = in_call ( &in_call_data, opcode, ap );
+
+ /* If real-mode return address is null, it means that we
+ * should exit via the prefix's exit path, which is part of
+ * our image. (This arrangement is necessary since the prefix
+ * code itself may have been vapourised by the time we want to
+ * return.)
+ */
+ if ( ( opcode & EB_CALL_FROM_REAL_MODE ) &&
+ ( rm_data.ret_addr.offset == 0 ) ) {
+ real_call ( prefix_exit, &in_stack, NULL );
+ /* Should never return */
+ }
+
+ return ret;
+}
+
+#ifdef CODE16
+
+/* install_rm_callback_interface(): install real-mode callback
+ * interface at specified address.
+ *
+ * Real-mode code may then call to this address (or lcall to this
+ * address plus RM_IN_CALL_FAR) in order to make an in_call() to
+ * Etherboot.
+ *
+ * Returns the size of the installed code, or 0 if the code could not
+ * be installed.
+ */
+int install_rm_callback_interface ( void *address, size_t available ) {
+ if ( available &&
+ ( available < rm_callback_interface_size ) ) return 0;
+
+ /* Inform RM code where to find Etherboot */
+ rm_etherboot_location = virt_to_phys(_text);
+
+ /* Install callback interface */
+ memcpy ( address, &rm_callback_interface,
+ rm_callback_interface_size );
+
+ return rm_callback_interface_size;
+}
+
+#endif /* CODE16 */
diff --git a/src/arch/i386/core/cpu.c b/src/arch/i386/core/cpu.c
new file mode 100644
index 00000000..8a0f7333
--- /dev/null
+++ b/src/arch/i386/core/cpu.c
@@ -0,0 +1,86 @@
+#ifdef CONFIG_X86_64
+#include "stdint.h"
+#include "string.h"
+#include "bits/cpu.h"
+
+
+/* Standard macro to see if a specific flag is changeable */
+static inline int flag_is_changeable_p(uint32_t flag)
+{
+ uint32_t f1, f2;
+
+ asm("pushfl\n\t"
+ "pushfl\n\t"
+ "popl %0\n\t"
+ "movl %0,%1\n\t"
+ "xorl %2,%0\n\t"
+ "pushl %0\n\t"
+ "popfl\n\t"
+ "pushfl\n\t"
+ "popl %0\n\t"
+ "popfl\n\t"
+ : "=&r" (f1), "=&r" (f2)
+ : "ir" (flag));
+
+ return ((f1^f2) & flag) != 0;
+}
+
+
+/* Probe for the CPUID instruction */
+static inline int have_cpuid_p(void)
+{
+ return flag_is_changeable_p(X86_EFLAGS_ID);
+}
+
+static void identify_cpu(struct cpuinfo_x86 *c)
+{
+ unsigned xlvl;
+
+ c->cpuid_level = -1; /* CPUID not detected */
+ c->x86_model = c->x86_mask = 0; /* So far unknown... */
+ c->x86_vendor_id[0] = '\0'; /* Unset */
+ memset(&c->x86_capability, 0, sizeof c->x86_capability);
+
+ if (!have_cpuid_p()) {
+ /* CPU doesn'thave CPUID */
+
+ /* If there are any capabilities, they'r vendor-specific */
+ /* enable_cpuid() would have set c->x86 for us. */
+ }
+ else {
+ /* CPU does have CPUID */
+
+ /* Get vendor name */
+ cpuid(0x00000000, &c->cpuid_level,
+ (int *)&c->x86_vendor_id[0],
+ (int *)&c->x86_vendor_id[8],
+ (int *)&c->x86_vendor_id[4]);
+
+ /* Initialize the standard set of capabilities */
+ /* Note that the vendor-specific code below might override */
+
+ /* Intel-defined flags: level 0x00000001 */
+ if ( c->cpuid_level >= 0x00000001 ) {
+ unsigned tfms, junk;
+ cpuid(0x00000001, &tfms, &junk, &junk,
+ &c->x86_capability[0]);
+ c->x86 = (tfms >> 8) & 15;
+ c->x86_model = (tfms >> 4) & 15;
+ c->x86_mask = tfms & 15;
+ }
+
+ /* AMD-defined flags: level 0x80000001 */
+ xlvl = cpuid_eax(0x80000000);
+ if ( (xlvl & 0xffff0000) == 0x80000000 ) {
+ if ( xlvl >= 0x80000001 )
+ c->x86_capability[1] = cpuid_edx(0x80000001);
+ }
+ }
+}
+
+struct cpuinfo_x86 cpu_info;
+void cpu_setup(void)
+{
+ identify_cpu(&cpu_info);
+}
+#endif /* CONFIG_X86_64 */
diff --git a/src/arch/i386/core/elf.c b/src/arch/i386/core/elf.c
new file mode 100644
index 00000000..6ef78c71
--- /dev/null
+++ b/src/arch/i386/core/elf.c
@@ -0,0 +1,135 @@
+#include "etherboot.h"
+#include "elf.h"
+
+
+#define NAME "Etherboot"
+
+#if defined(PCBIOS)
+#define FIRMWARE "PCBIOS"
+#endif
+#if defined(LINUXBIOS)
+#define FIRMWARE "LinuxBIOS"
+#endif
+#if !defined(FIRMWARE)
+#error "No BIOS selected"
+#endif
+
+#define SZ(X) ((sizeof(X)+3) & ~3)
+#define CP(D,S) (memcpy(&(D), &(S), sizeof(S)))
+
+struct elf_notes {
+ /* The note header */
+ struct Elf_Bhdr hdr;
+
+ /* First the Fixed sized entries that must be well aligned */
+
+ /* Pointer to bootp data */
+ Elf_Nhdr nf1;
+ char nf1_name[SZ(EB_PARAM_NOTE)];
+ uint32_t nf1_bootp_data;
+
+ /* Pointer to ELF header */
+ Elf_Nhdr nf2;
+ char nf2_name[SZ(EB_PARAM_NOTE)];
+ uint32_t nf2_header;
+
+ /* A copy of the i386 memory map */
+ Elf_Nhdr nf3;
+ char nf3_name[SZ(EB_PARAM_NOTE)];
+ struct meminfo nf3_meminfo;
+
+ /* Then the variable sized data string data where alignment does not matter */
+
+ /* The bootloader name */
+ Elf_Nhdr nv1;
+ char nv1_desc[SZ(NAME)];
+ /* The bootloader version */
+ Elf_Nhdr nv2;
+ char nv2_desc[SZ(VERSION)];
+ /* The firmware type */
+ Elf_Nhdr nv3;
+ char nv3_desc[SZ(FIRMWARE)];
+ /* Name of the loaded image */
+ Elf_Nhdr nv4;
+ char nv4_loaded_image[128];
+ /* An empty command line */
+ Elf_Nhdr nv5;
+ char nv5_cmdline[SZ("")];
+};
+
+#define ELF_NOTE_COUNT (3 + 5)
+
+static struct elf_notes notes;
+struct Elf_Bhdr *prepare_boot_params(void *header)
+{
+ memset(&notes, 0, sizeof(notes));
+ notes.hdr.b_signature = ELF_BHDR_MAGIC;
+ notes.hdr.b_size = sizeof(notes);
+ notes.hdr.b_checksum = 0;
+ notes.hdr.b_records = ELF_NOTE_COUNT;
+
+ /* Initialize the fixed length entries. */
+ notes.nf1.n_namesz = sizeof(EB_PARAM_NOTE);
+ notes.nf1.n_descsz = sizeof(notes.nf1_bootp_data);
+ notes.nf1.n_type = EB_BOOTP_DATA;
+ CP(notes.nf1_name, EB_PARAM_NOTE);
+ notes.nf1_bootp_data = virt_to_phys(BOOTP_DATA_ADDR);
+
+ notes.nf2.n_namesz = sizeof(EB_PARAM_NOTE);
+ notes.nf2.n_descsz = sizeof(notes.nf2_header);
+ notes.nf2.n_type = EB_HEADER;
+ CP(notes.nf2_name, EB_PARAM_NOTE);
+ notes.nf2_header = virt_to_phys(header);
+
+ notes.nf3.n_namesz = sizeof(EB_PARAM_NOTE);
+ notes.nf3.n_descsz = sizeof(notes.nf3_meminfo);
+ notes.nf3.n_type = EB_I386_MEMMAP;
+ CP(notes.nf3_name, EB_PARAM_NOTE);
+ memcpy(&notes.nf3_meminfo, &meminfo, sizeof(meminfo));
+
+ /* Initialize the variable length entries */
+ notes.nv1.n_namesz = 0;
+ notes.nv1.n_descsz = sizeof(NAME);
+ notes.nv1.n_type = EBN_BOOTLOADER_NAME;
+ CP(notes.nv1_desc, NAME);
+
+ notes.nv2.n_namesz = 0;
+ notes.nv2.n_descsz = sizeof(VERSION);
+ notes.nv2.n_type = EBN_BOOTLOADER_VERSION;
+ CP(notes.nv2_desc, VERSION);
+
+ notes.nv3.n_namesz = 0;
+ notes.nv3.n_descsz = sizeof(FIRMWARE);
+ notes.nv3.n_type = EBN_FIRMWARE_TYPE;
+ CP(notes.nv3_desc, FIRMWARE);
+
+ /* Attempt to pass the name of the loaded image */
+ notes.nv4.n_namesz = 0;
+ notes.nv4.n_descsz = sizeof(notes.nv4_loaded_image);
+ notes.nv4.n_type = EBN_LOADED_IMAGE;
+ memcpy(&notes.nv4_loaded_image, KERNEL_BUF, sizeof(notes.nv4_loaded_image));
+
+ /* Pass an empty command line for now */
+ notes.nv5.n_namesz = 0;
+ notes.nv5.n_descsz = sizeof("");
+ notes.nv5.n_type = EBN_COMMAND_LINE;
+ CP(notes.nv5_cmdline, "");
+
+
+ notes.hdr.b_checksum = ipchksum(&notes, sizeof(notes));
+ /* Like UDP invert a 0 checksum to show that a checksum is present */
+ if (notes.hdr.b_checksum == 0) {
+ notes.hdr.b_checksum = 0xffff;
+ }
+ return &notes.hdr;
+}
+
+int elf_start(unsigned long machine __unused_i386, unsigned long entry, unsigned long params)
+{
+#if defined(CONFIG_X86_64)
+ if (machine == EM_X86_64) {
+ return xstart_lm(entry, params);
+ }
+#endif
+ return xstart32(entry, params);
+}
diff --git a/src/arch/i386/core/etherboot.lds b/src/arch/i386/core/etherboot.lds
new file mode 100644
index 00000000..6f406329
--- /dev/null
+++ b/src/arch/i386/core/etherboot.lds
@@ -0,0 +1,90 @@
+OUTPUT_FORMAT("elf32-i386", "elf32-i386", "elf32-i386")
+OUTPUT_ARCH(i386)
+
+ENTRY(_text)
+SECTIONS {
+ . = ALIGN(16);
+ /* Start address of Etherboot in the virtual address space */
+ _virt_start = 0;
+ _text = . ;
+ .text.nocompress : {
+ *(.text*.nocompress)
+ . = ALIGN(16);
+ } = 0x9090
+
+ .text16 : {
+ _text16 = .;
+ *(.text16)
+ *(.text16.*)
+ _etext16 = . ;
+ }
+ .text.compress : {
+ *(.text)
+ *(.text.*)
+ } = 0x9090
+ .rodata : {
+ . = ALIGN(4);
+ *(.rodata)
+ *(.rodata.*)
+ }
+ . = ALIGN(4);
+ .drivers.pci : {
+ pci_drivers = . ;
+ *(.drivers.pci);
+ pci_drivers_end = . ;
+ }
+ . = ALIGN(4);
+ .drivers.isa : {
+ isa_drivers = . ;
+ *(.drivers.isa);
+ isa_drivers_end = .;
+ }
+ _etext = . ;
+ _data = . ;
+ .data : {
+ *(.data)
+ *(.data.*)
+ }
+ _edata = . ;
+ _uncompressed_verbatim_end = . ;
+ . = ALIGN(16);
+ .bss.preserve : {
+ *(.bss.preserve)
+ *(.bss.preserve.*)
+ }
+ _bss = . ;
+ .bss : {
+ *(.bss)
+ *(.bss.*)
+ }
+ . = ALIGN(16);
+ _ebss = .;
+ _stack = . ;
+ .stack : {
+ _stack_start = . ;
+ *(.stack)
+ *(.stack.*)
+ _stack_end = . ;
+ }
+ _bss_size = _ebss - _bss;
+ _stack_offset = _stack - _text ;
+ _stack_offset_pgh = _stack_offset / 16 ;
+ _stack_size = _stack_end - _stack_start ;
+ . = ALIGN(16);
+ _end = . ;
+
+ /DISCARD/ : {
+ *(.comment)
+ *(.note)
+ }
+
+ /* PXE-specific symbol calculations. The results of these are
+ * needed in romprefix.S, which is why they must be calculated
+ * here.
+ */
+ _pxe_stack_size = _pxe_stack_t_size
+ + _pxe_callback_interface_size
+ + _rm_callback_interface_size
+ + _e820mangler_size + 15 ;
+
+}
diff --git a/src/arch/i386/core/etherboot.prefix.lds b/src/arch/i386/core/etherboot.prefix.lds
new file mode 100644
index 00000000..3550a2a3
--- /dev/null
+++ b/src/arch/i386/core/etherboot.prefix.lds
@@ -0,0 +1,100 @@
+OUTPUT_FORMAT("elf32-i386", "elf32-i386", "elf32-i386")
+OUTPUT_ARCH(i386)
+
+ENTRY(_prefix_start)
+SECTIONS {
+ /* Prefix */
+ .prefix : {
+ _verbatim_start = . ;
+ _prefix_start = . ;
+ *(.prefix)
+ . = ALIGN(16);
+ _prefix_end = . ;
+ } = 0x9090
+ _prefix_size = _prefix_end - _prefix_start;
+
+ .text.nocompress : {
+ *(.prefix.udata)
+ } = 0x9090
+
+ decompress_to = . ;
+ .prefix.zdata : {
+ _compressed = . ;
+ *(.prefix.zdata)
+ _compressed_end = . ;
+ }
+ _compressed_size = _compressed_end - _compressed;
+
+ . = ALIGN(16);
+ _verbatim_end = . ;
+
+
+ /* Size of the core of etherboot in memory */
+ _base_size = _end - _text;
+
+ /* _prefix_size is the length of the non-core etherboot prefix */
+ _prefix_size = _prefix_end - _prefix_start;
+
+ /* _verbatim_size is the actual amount that has to be copied to base memory */
+ _verbatim_size = _verbatim_end - _verbatim_start;
+
+ /* _image_size is the amount of base memory needed to run */
+ _image_size = _base_size + _prefix_size;
+
+ /* Standard sizes rounded up to paragraphs */
+ _prefix_size_pgh = (_prefix_size + 15) / 16;
+ _verbatim_size_pgh = (_verbatim_size + 15) / 16;
+ _image_size_pgh = (_image_size + 15) / 16 ;
+
+ /* Standard sizes in sectors */
+ _prefix_size_sct = (_prefix_size + 511) / 512;
+ _verbatim_size_sct = (_verbatim_size + 511) / 512;
+ _image_size_sct = (_image_size + 511) / 512;
+
+ /* Symbol offsets and sizes for the exe prefix */
+ _exe_hdr_size = 32;
+ _exe_size = _verbatim_size; /* Should this be - 32 to exclude the header? */
+ _exe_size_tail = (_exe_size) % 512;
+ _exe_size_pages = ((_exe_size) + 511) / 512;
+ _exe_bss_size = ((_image_size - _verbatim_size) + 15) / 16;
+ _exe_ss_offset = (_stack_offset + _prefix_size - _exe_hdr_size + 15) / 16 ;
+
+ /* This is where we copy the compressed image before decompression.
+ * Prepare to decompress in place. The end mark is about 8.25 bytes long,
+ * and the worst case symbol is about 16.5 bytes long. Therefore
+ * We need to reserve at least 25 bytes of slack here.
+ * Currently I reserve 2048 bytes of just slack to be safe :)
+ * 2048 bytes easily falls within the BSS (the defualt stack is 4096 bytes)
+ * so we really are decompressing in place.
+ *
+ * Hmm. I missed a trick. In the very worst case (no compression)
+ * the encoded data is 9/8 the size as it started out so to be completely
+ * safe I need to be 1/8 of the uncompressed code size past the end.
+ * This will still fit compfortably into our bss in any conceivable scenario.
+ */
+ _compressed_copy = _edata + _prefix_size - _compressed_size +
+ /* The amount to overflow _edata */
+ MAX( ((_edata - _text + 7) / 8) , 2016 ) + 32;
+ _assert = ASSERT( ( _compressed_copy - _prefix_size ) < _ebss , "Cannot decompress in place" ) ;
+
+ decompress = DEFINED(decompress) ? decompress : 0;
+ /DISCARD/ : {
+ *(.comment)
+ *(.note)
+ }
+
+ /* Symbols used by the prefixes whose addresses are inconvinient
+ * to compute, at runtime in the code.
+ */
+ image_basemem_size = DEFINED(image_basemem_size)? image_basemem_size : 65536;
+ image_basemem = DEFINED(image_basemem)? image_basemem : 65536;
+ _prefix_real_to_prot = _real_to_prot + _prefix_size ;
+ _prefix_prot_to_real = _prot_to_real + _prefix_size ;
+ _prefix_image_basemem_size = image_basemem_size + _prefix_size ;
+ _prefix_image_basemem = image_basemem + _prefix_size ;
+ _prefix_rm_in_call = _rm_in_call + _prefix_size ;
+ _prefix_in_call = _in_call + _prefix_size ;
+ _prefix_rom = rom + _prefix_size ;
+ _prefix_rm_etherboot_location = rm_etherboot_location + _prefix_size ;
+ _prefix_stack_end = _stack_end + _prefix_size ;
+}
diff --git a/src/arch/i386/core/freebsd_loader.c b/src/arch/i386/core/freebsd_loader.c
new file mode 100644
index 00000000..4e820e8e
--- /dev/null
+++ b/src/arch/i386/core/freebsd_loader.c
@@ -0,0 +1,377 @@
+/* bootinfo */
+#define BOOTINFO_VERSION 1
+#define NODEV (-1) /* non-existent device */
+#define PAGE_SHIFT 12 /* LOG2(PAGE_SIZE) */
+#define PAGE_SIZE (1<<PAGE_SHIFT) /* bytes/page */
+#define PAGE_MASK (PAGE_SIZE-1)
+#define N_BIOS_GEOM 8
+
+struct bootinfo {
+ unsigned int bi_version;
+ const unsigned char *bi_kernelname;
+ struct nfs_diskless *bi_nfs_diskless;
+ /* End of fields that are always present. */
+#define bi_endcommon bi_n_bios_used
+ unsigned int bi_n_bios_used;
+ unsigned long bi_bios_geom[N_BIOS_GEOM];
+ unsigned int bi_size;
+ unsigned char bi_memsizes_valid;
+ unsigned char bi_pad[3];
+ unsigned long bi_basemem;
+ unsigned long bi_extmem;
+ unsigned long bi_symtab;
+ unsigned long bi_esymtab;
+ /* Note that these are in the FreeBSD headers but were not here... */
+ unsigned long bi_kernend; /* end of kernel space */
+ unsigned long bi_envp; /* environment */
+ unsigned long bi_modulep; /* preloaded modules */
+};
+
+static struct bootinfo bsdinfo;
+
+#ifdef ELF_IMAGE
+static Elf32_Shdr *shdr; /* To support the FreeBSD kludge! */
+static Address symtab_load;
+static Address symstr_load;
+static int symtabindex;
+static int symstrindex;
+#endif
+
+static enum {
+ Unknown, Tagged, Aout, Elf, Aout_FreeBSD, Elf_FreeBSD,
+} image_type = Unknown;
+
+static unsigned int off;
+
+
+#ifdef ELF_IMAGE
+static void elf_freebsd_probe(void)
+{
+ image_type = Elf;
+ if ( (estate.e.elf32.e_entry & 0xf0000000) &&
+ (estate.e.elf32.e_type == ET_EXEC))
+ {
+ image_type = Elf_FreeBSD;
+ printf("/FreeBSD");
+ off = -(estate.e.elf32.e_entry & 0xff000000);
+ estate.e.elf32.e_entry += off;
+ }
+ /* Make sure we have a null to start with... */
+ shdr = 0;
+
+ /* Clear the symbol index values... */
+ symtabindex = -1;
+ symstrindex = -1;
+
+ /* ...and the load addresses of the symbols */
+ symtab_load = 0;
+ symstr_load = 0;
+}
+
+static void elf_freebsd_fixup_segment(void)
+{
+ if (image_type == Elf_FreeBSD) {
+ estate.p.phdr32[estate.segment].p_paddr += off;
+ }
+}
+
+static void elf_freebsd_find_segment_end(void)
+{
+ /* Count the bytes read even for the last block
+ * as we will need to know where the last block
+ * ends in order to load the symbols correctly.
+ * (plus it could be useful elsewhere...)
+ * Note that we need to count the actual size,
+ * not just the end of the disk image size.
+ */
+ estate.curaddr +=
+ (estate.p.phdr32[estate.segment].p_memsz -
+ estate.p.phdr32[estate.segment].p_filesz);
+}
+
+static int elf_freebsd_debug_loader(unsigned int offset)
+{
+ /* No more segments to be loaded - time to start the
+ * nasty state machine to support the loading of
+ * FreeBSD debug symbols due to the fact that FreeBSD
+ * uses/exports the kernel's debug symbols in order
+ * to make much of the system work! Amazing (arg!)
+ *
+ * We depend on the fact that for the FreeBSD kernel,
+ * there is only one section of debug symbols and that
+ * the section is after all of the loaded sections in
+ * the file. This assumes a lot but is somewhat required
+ * to make this code not be too annoying. (Where do you
+ * load symbols when the code has not loaded yet?)
+ * Since this function is actually just a callback from
+ * the network data transfer code, we need to be able to
+ * work with the data as it comes in. There is no chance
+ * for doing a seek other than forwards.
+ *
+ * The process we use is to first load the section
+ * headers. Once they are loaded (shdr != 0) we then
+ * look for where the symbol table and symbol table
+ * strings are and setup some state that we found
+ * them and fall into processing the first one (which
+ * is the symbol table) and after that has been loaded,
+ * we try the symbol strings. Note that the order is
+ * actually required as the memory image depends on
+ * the symbol strings being loaded starting at the
+ * end of the symbol table. The kernel assumes this
+ * layout of the image.
+ *
+ * At any point, if we get to the end of the load file
+ * or the section requested is earlier in the file than
+ * the current file pointer, we just end up falling
+ * out of this and booting the kernel without this
+ * information.
+ */
+
+ /* Make sure that the next address is long aligned... */
+ /* Assumes size of long is a power of 2... */
+ estate.curaddr = (estate.curaddr + sizeof(long) - 1) & ~(sizeof(long) - 1);
+
+ /* If we have not yet gotten the shdr loaded, try that */
+ if (shdr == 0)
+ {
+ estate.toread = estate.e.elf32.e_shnum * estate.e.elf32.e_shentsize;
+ estate.skip = estate.e.elf32.e_shoff - (estate.loc + offset);
+ if (estate.toread)
+ {
+#if ELF_DEBUG
+ printf("shdr *, size %lX, curaddr %lX\n",
+ estate.toread, estate.curaddr);
+#endif
+
+ /* Start reading at the curaddr and make that the shdr */
+ shdr = (Elf32_Shdr *)phys_to_virt(estate.curaddr);
+
+ /* Start to read... */
+ return 1;
+ }
+ }
+ else
+ {
+ /* We have the shdr loaded, check if we have found
+ * the indexs where the symbols are supposed to be */
+ if ((symtabindex == -1) && (symstrindex == -1))
+ {
+ int i;
+ /* Make sure that the address is page aligned... */
+ /* Symbols need to start in their own page(s)... */
+ estate.curaddr = (estate.curaddr + 4095) & ~4095;
+
+ /* Need to make new indexes... */
+ for (i=0; i < estate.e.elf32.e_shnum; i++)
+ {
+ if (shdr[i].sh_type == SHT_SYMTAB)
+ {
+ int j;
+ for (j=0; j < estate.e.elf32.e_phnum; j++)
+ {
+ /* Check only for loaded sections */
+ if ((estate.p.phdr32[j].p_type | 0x80) == (PT_LOAD | 0x80))
+ {
+ /* Only the extra symbols */
+ if ((shdr[i].sh_offset >= estate.p.phdr32[j].p_offset) &&
+ ((shdr[i].sh_offset + shdr[i].sh_size) <=
+ (estate.p.phdr32[j].p_offset + estate.p.phdr32[j].p_filesz)))
+ {
+ shdr[i].sh_offset=0;
+ shdr[i].sh_size=0;
+ break;
+ }
+ }
+ }
+ if ((shdr[i].sh_offset != 0) && (shdr[i].sh_size != 0))
+ {
+ symtabindex = i;
+ symstrindex = shdr[i].sh_link;
+ }
+ }
+ }
+ }
+
+ /* Check if we have a symbol table index and have not loaded it */
+ if ((symtab_load == 0) && (symtabindex >= 0))
+ {
+ /* No symbol table yet? Load it first... */
+
+ /* This happens to work out in a strange way.
+ * If we are past the point in the file already,
+ * we will skip a *large* number of bytes which
+ * ends up bringing us to the end of the file and
+ * an old (default) boot. Less code and lets
+ * the state machine work in a cleaner way but this
+ * is a nasty side-effect trick... */
+ estate.skip = shdr[symtabindex].sh_offset - (estate.loc + offset);
+
+ /* And we need to read this many bytes... */
+ estate.toread = shdr[symtabindex].sh_size;
+
+ if (estate.toread)
+ {
+#if ELF_DEBUG
+ printf("db sym, size %lX, curaddr %lX\n",
+ estate.toread, estate.curaddr);
+#endif
+ /* Save where we are loading this... */
+ symtab_load = phys_to_virt(estate.curaddr);
+
+ *((long *)phys_to_virt(estate.curaddr)) = estate.toread;
+ estate.curaddr += sizeof(long);
+
+ /* Start to read... */
+ return 1;
+ }
+ }
+ else if ((symstr_load == 0) && (symstrindex >= 0))
+ {
+ /* We have already loaded the symbol table, so
+ * now on to the symbol strings... */
+
+
+ /* Same nasty trick as above... */
+ estate.skip = shdr[symstrindex].sh_offset - (estate.loc + offset);
+
+ /* And we need to read this many bytes... */
+ estate.toread = shdr[symstrindex].sh_size;
+
+ if (estate.toread)
+ {
+#if ELF_DEBUG
+ printf("db str, size %lX, curaddr %lX\n",
+ estate.toread, estate.curaddr);
+#endif
+ /* Save where we are loading this... */
+ symstr_load = phys_to_virt(estate.curaddr);
+
+ *((long *)phys_to_virt(estate.curaddr)) = estate.toread;
+ estate.curaddr += sizeof(long);
+
+ /* Start to read... */
+ return 1;
+ }
+ }
+ }
+ /* all done */
+ return 0;
+}
+
+static void elf_freebsd_boot(unsigned long entry)
+{
+ if (image_type != Elf_FreeBSD)
+ return;
+
+ memset(&bsdinfo, 0, sizeof(bsdinfo));
+ bsdinfo.bi_basemem = meminfo.basememsize;
+ bsdinfo.bi_extmem = meminfo.memsize;
+ bsdinfo.bi_memsizes_valid = 1;
+ bsdinfo.bi_version = BOOTINFO_VERSION;
+ bsdinfo.bi_kernelname = virt_to_phys(KERNEL_BUF);
+ bsdinfo.bi_nfs_diskless = NULL;
+ bsdinfo.bi_size = sizeof(bsdinfo);
+#define RB_BOOTINFO 0x80000000 /* have `struct bootinfo *' arg */
+ if(freebsd_kernel_env[0] != '\0'){
+ freebsd_howto |= RB_BOOTINFO;
+ bsdinfo.bi_envp = (unsigned long)freebsd_kernel_env;
+ }
+
+ /* Check if we have symbols loaded, and if so,
+ * made the meta_data needed to pass those to
+ * the kernel. */
+ if ((symtab_load !=0) && (symstr_load != 0))
+ {
+ unsigned long *t;
+
+ bsdinfo.bi_symtab = symtab_load;
+
+ /* End of symbols (long aligned...) */
+ /* Assumes size of long is a power of 2... */
+ bsdinfo.bi_esymtab = (symstr_load +
+ sizeof(long) +
+ *((long *)symstr_load) +
+ sizeof(long) - 1) & ~(sizeof(long) - 1);
+
+ /* Where we will build the meta data... */
+ t = phys_to_virt(bsdinfo.bi_esymtab);
+
+#if ELF_DEBUG
+ printf("Metadata at %lX\n",t);
+#endif
+
+ /* Set up the pointer to the memory... */
+ bsdinfo.bi_modulep = virt_to_phys(t);
+
+ /* The metadata structure is an array of 32-bit
+ * words where we store some information about the
+ * system. This is critical, as FreeBSD now looks
+ * only for the metadata for the extended symbol
+ * information rather than in the bootinfo.
+ */
+ /* First, do the kernel name and the kernel type */
+ /* Note that this assumed x86 byte order... */
+
+ /* 'kernel\0\0' */
+ *t++=MODINFO_NAME; *t++= 7; *t++=0x6E72656B; *t++=0x00006C65;
+
+ /* 'elf kernel\0\0' */
+ *t++=MODINFO_TYPE; *t++=11; *t++=0x20666C65; *t++=0x6E72656B; *t++ = 0x00006C65;
+
+ /* Now the symbol start/end - note that they are
+ * here in local/physical address - the Kernel
+ * boot process will relocate the addresses. */
+ *t++=MODINFOMD_SSYM | MODINFO_METADATA; *t++=sizeof(*t); *t++=bsdinfo.bi_symtab;
+ *t++=MODINFOMD_ESYM | MODINFO_METADATA; *t++=sizeof(*t); *t++=bsdinfo.bi_esymtab;
+
+ *t++=MODINFO_END; *t++=0; /* end of metadata */
+
+ /* Since we have symbols we need to make
+ * sure that the kernel knows its own end
+ * of memory... It is not _end but after
+ * the symbols and the metadata... */
+ bsdinfo.bi_kernend = virt_to_phys(t);
+
+ /* Signal locore.s that we have a valid bootinfo
+ * structure that was completely filled in. */
+ freebsd_howto |= 0x80000000;
+ }
+
+ xstart32(entry, freebsd_howto, NODEV, 0, 0, 0,
+ virt_to_phys(&bsdinfo), 0, 0, 0);
+ longjmp(restart_etherboot, -2);
+}
+#endif
+
+#ifdef AOUT_IMAGE
+static void aout_freebsd_probe(void)
+{
+ image_type = Aout;
+ if (((astate.head.a_midmag >> 16) & 0xffff) == 0) {
+ /* Some other a.out variants have a different
+ * value, and use other alignments (e.g. 1K),
+ * not the 4K used by FreeBSD. */
+ image_type = Aout_FreeBSD;
+ printf("/FreeBSD");
+ off = -(astate.head.a_entry & 0xff000000);
+ astate.head.a_entry += off;
+ }
+}
+
+static void aout_freebsd_boot(void)
+{
+ if (image_type == Aout_FreeBSD) {
+ memset(&bsdinfo, 0, sizeof(bsdinfo));
+ bsdinfo.bi_basemem = meminfo.basememsize;
+ bsdinfo.bi_extmem = meminfo.memsize;
+ bsdinfo.bi_memsizes_valid = 1;
+ bsdinfo.bi_version = BOOTINFO_VERSION;
+ bsdinfo.bi_kernelname = virt_to_phys(KERNEL_BUF);
+ bsdinfo.bi_nfs_diskless = NULL;
+ bsdinfo.bi_size = sizeof(bsdinfo);
+ xstart32(astate.head.a_entry, freebsd_howto, NODEV, 0, 0, 0,
+ virt_to_phys(&bsdinfo), 0, 0, 0);
+ longjmp(restart_etherboot, -2);
+ }
+}
+#endif
diff --git a/src/arch/i386/core/hooks.c b/src/arch/i386/core/hooks.c
new file mode 100644
index 00000000..9ca6f480
--- /dev/null
+++ b/src/arch/i386/core/hooks.c
@@ -0,0 +1,35 @@
+#include "etherboot.h"
+#include "callbacks.h"
+#include <stdarg.h>
+
+void arch_main ( in_call_data_t *data __unused, va_list params __unused )
+{
+#ifdef PCBIOS
+ /* Deallocate base memory used for the prefix, if applicable
+ */
+ forget_prefix_base_memory();
+#endif
+
+}
+
+void arch_relocated_from (unsigned long old_addr )
+{
+
+#ifdef PCBIOS
+ /* Deallocate base memory used for the Etherboot runtime,
+ * if applicable
+ */
+ forget_runtime_base_memory( old_addr );
+#endif
+
+}
+
+void arch_on_exit ( int exit_status __unused )
+{
+#ifdef PCBIOS
+ /* Deallocate the real-mode stack now. We will reallocate
+ * the stack if are going to use it after this point.
+ */
+ forget_real_mode_stack();
+#endif
+}
diff --git a/src/arch/i386/core/i386_timer.c b/src/arch/i386/core/i386_timer.c
new file mode 100644
index 00000000..531183d4
--- /dev/null
+++ b/src/arch/i386/core/i386_timer.c
@@ -0,0 +1,191 @@
+/* A couple of routines to implement a low-overhead timer for drivers */
+
+ /*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2, or (at
+ * your option) any later version.
+ */
+
+#include "etherboot.h"
+#include "timer.h"
+#include "latch.h"
+
+void __load_timer2(unsigned int ticks)
+{
+ /*
+ * Now let's take care of PPC channel 2
+ *
+ * Set the Gate high, program PPC channel 2 for mode 0,
+ * (interrupt on terminal count mode), binary count,
+ * load 5 * LATCH count, (LSB and MSB) to begin countdown.
+ *
+ * Note some implementations have a bug where the high bits byte
+ * of channel 2 is ignored.
+ */
+ /* Set up the timer gate, turn off the speaker */
+ /* Set the Gate high, disable speaker */
+ outb((inb(PPC_PORTB) & ~PPCB_SPKR) | PPCB_T2GATE, PPC_PORTB);
+ /* binary, mode 0, LSB/MSB, Ch 2 */
+ outb(TIMER2_SEL|WORD_ACCESS|MODE0|BINARY_COUNT, TIMER_MODE_PORT);
+ /* LSB of ticks */
+ outb(ticks & 0xFF, TIMER2_PORT);
+ /* MSB of ticks */
+ outb(ticks >> 8, TIMER2_PORT);
+}
+
+static int __timer2_running(void)
+{
+ return ((inb(PPC_PORTB) & PPCB_T2OUT) == 0);
+}
+
+#if !defined(CONFIG_TSC_CURRTICKS)
+void setup_timers(void)
+{
+ return;
+}
+
+void load_timer2(unsigned int ticks)
+{
+ return __load_timer2(ticks);
+}
+
+int timer2_running(void)
+{
+ return __timer2_running();
+}
+
+void ndelay(unsigned int nsecs)
+{
+ waiton_timer2((nsecs * CLOCK_TICK_RATE)/1000000000);
+}
+void udelay(unsigned int usecs)
+{
+ waiton_timer2((usecs * TICKS_PER_MS)/1000);
+}
+#endif /* !defined(CONFIG_TSC_CURRTICKS) */
+
+#if defined(CONFIG_TSC_CURRTICKS)
+
+#define rdtsc(low,high) \
+ __asm__ __volatile__("rdtsc" : "=a" (low), "=d" (high))
+
+#define rdtscll(val) \
+ __asm__ __volatile__ ("rdtsc" : "=A" (val))
+
+
+/* Number of clock ticks to time with the rtc */
+#define LATCH 0xFF
+
+#define LATCHES_PER_SEC ((CLOCK_TICK_RATE + (LATCH/2))/LATCH)
+#define TICKS_PER_LATCH ((LATCHES_PER_SEC + (TICKS_PER_SEC/2))/TICKS_PER_SEC)
+
+static void sleep_latch(void)
+{
+ __load_timer2(LATCH);
+ while(__timer2_running());
+}
+
+/* ------ Calibrate the TSC -------
+ * Time how long it takes to excute a loop that runs in known time.
+ * And find the convertion needed to get to CLOCK_TICK_RATE
+ */
+
+
+static unsigned long long calibrate_tsc(void)
+{
+ unsigned long startlow, starthigh;
+ unsigned long endlow, endhigh;
+
+ rdtsc(startlow,starthigh);
+ sleep_latch();
+ rdtsc(endlow,endhigh);
+
+ /* 64-bit subtract - gcc just messes up with long longs */
+ __asm__("subl %2,%0\n\t"
+ "sbbl %3,%1"
+ :"=a" (endlow), "=d" (endhigh)
+ :"g" (startlow), "g" (starthigh),
+ "0" (endlow), "1" (endhigh));
+
+ /* Error: ECPUTOOFAST */
+ if (endhigh)
+ goto bad_ctc;
+
+ endlow *= TICKS_PER_LATCH;
+ return endlow;
+
+ /*
+ * The CTC wasn't reliable: we got a hit on the very first read,
+ * or the CPU was so fast/slow that the quotient wouldn't fit in
+ * 32 bits..
+ */
+bad_ctc:
+ printf("bad_ctc\n");
+ return 0;
+}
+
+static unsigned long clocks_per_tick;
+void setup_timers(void)
+{
+ if (!clocks_per_tick) {
+ clocks_per_tick = calibrate_tsc();
+ /* Display the CPU Mhz to easily test if the calibration was bad */
+ printf("CPU %ld Mhz\n", (clocks_per_tick/1000 * TICKS_PER_SEC)/1000);
+ }
+}
+
+unsigned long currticks(void)
+{
+ unsigned long clocks_high, clocks_low;
+ unsigned long currticks;
+ /* Read the Time Stamp Counter */
+ rdtsc(clocks_low, clocks_high);
+
+ /* currticks = clocks / clocks_per_tick; */
+ __asm__("divl %1"
+ :"=a" (currticks)
+ :"r" (clocks_per_tick), "0" (clocks_low), "d" (clocks_high));
+
+
+ return currticks;
+}
+
+static unsigned long long timer_timeout;
+static int __timer_running(void)
+{
+ unsigned long long now;
+ rdtscll(now);
+ return now < timer_timeout;
+}
+
+void udelay(unsigned int usecs)
+{
+ unsigned long long now;
+ rdtscll(now);
+ timer_timeout = now + usecs * ((clocks_per_tick * TICKS_PER_SEC)/(1000*1000));
+ while(__timer_running());
+}
+void ndelay(unsigned int nsecs)
+{
+ unsigned long long now;
+ rdtscll(now);
+ timer_timeout = now + nsecs * ((clocks_per_tick * TICKS_PER_SEC)/(1000*1000*1000));
+ while(__timer_running());
+}
+
+void load_timer2(unsigned int timer2_ticks)
+{
+ unsigned long long now;
+ unsigned long clocks;
+ rdtscll(now);
+ clocks = timer2_ticks * ((clocks_per_tick * TICKS_PER_SEC)/CLOCK_TICK_RATE);
+ timer_timeout = now + clocks;
+}
+
+int timer2_running(void)
+{
+ return __timer_running();
+}
+
+#endif /* RTC_CURRTICKS */
diff --git a/src/arch/i386/core/init.S b/src/arch/i386/core/init.S
new file mode 100644
index 00000000..71717c25
--- /dev/null
+++ b/src/arch/i386/core/init.S
@@ -0,0 +1,305 @@
+#include "callbacks.h"
+ .equ CR0_PE, 1
+
+ .text
+ .arch i386
+ .section ".prefix", "ax", @progbits
+
+#undef CODE16
+#if defined(PCBIOS)
+#define CODE16
+#endif
+
+/* We have two entry points: "conventional" (at the start of the file)
+ * and "callback" (at _entry, 2 bytes in). The "callback" entry
+ * should be used if the caller wishes to provide a specific opcode.
+ * It is equivalent to a call to in_call. Using the "conventional"
+ * entry point is equivalent to using the "callback" entry point with
+ * an opcode of EB_OPCODE_MAIN.
+ *
+ * Both entry points can be called in either 16-bit real or 32-bit
+ * protected mode with flat physical addresses. We detect which mode
+ * the processor is in and call either in_call or rm_in_call as
+ * appropriate. Note that the mode detection code must therefore be
+ * capable of doing the same thing in either mode, even though the
+ * machine code instructions will be interpreted differently.
+ *
+ * The decompressor will be invoked if necessary to decompress
+ * Etherboot before attempting to jump to it.
+ */
+
+/******************************************************************************
+ * Entry points and mode detection code
+ ******************************************************************************
+ */
+
+ .code32
+/* "Conventional" entry point: caller provides no opcode */
+ .globl _start
+_start:
+ /* Set flag to indicate conventional entry point used */
+ pushl $0 /* "pushw $0" in 16-bit code */
+ /* Fall through to "callback" entry point */
+
+/* "Callback" entry point */
+ .globl _entry
+_entry:
+
+#ifdef CODE16
+ /* CPU mode detection code */
+ pushl %eax /* "pushw %ax" in 16-bit code */
+ pushw %ax /* "pushl %eax" in 16-bit code */
+ movl %cr0, %eax /* Test protected mode bit */
+ testb $CR0_PE, %al
+ popw %ax /* "popl %eax" in 16-bit code */
+ popl %eax /* "popw %eax" in 16-bit code */
+ jz rmode
+#endif /* CODE16 */
+
+/******************************************************************************
+ * Entered in protected mode
+ ******************************************************************************
+ */
+
+ .code32
+pmode:
+ cmpl $0, 0(%esp) /* Conventional entry point used? */
+ jne 1f
+ /* Entered via conventional entry point: set up stack */
+ xchgl %eax, 4(%esp) /* %eax = return addr, store %eax */
+ movl %eax, 0(%esp) /* 0(%esp) = return address */
+ movl $(EB_OPCODE_MAIN|EB_USE_INTERNAL_STACK|EB_SKIP_OPCODE), %eax
+ xchgl %eax, 4(%esp) /* 4(%esp) = opcode, restore %eax */
+1:
+ /* Run decompressor if necessary */
+ pushl %eax
+ movl $decompress, %eax
+ testl %eax, %eax
+ jz 1f
+ call decompress
+1: popl %eax
+
+ /* Make in_call to Etherboot */
+ jmp _prefix_in_call
+
+/******************************************************************************
+ * Entered in real mode
+ ******************************************************************************
+ */
+
+#ifdef CODE16
+ .code16
+rmode:
+ pushw %ax /* Padding */
+ pushw %bp
+ movw %sp, %bp
+ cmpw $0, 6(%bp) /* Conventional entry point used? */
+ jne 1f
+ /* Entered via conventional entry point: set up stack */
+ pushw %ax
+ movw 6(%bp), %ax
+ movw %ax, 2(%bp) /* Move return address down */
+ movl $(EB_OPCODE_MAIN|EB_USE_INTERNAL_STACK|EB_SKIP_OPCODE), 4(%bp)
+ popw %ax
+ popw %bp
+ jmp 2f
+1: /* Entered via callback entry point: do nothing */
+ popw %bp
+ popw %ax
+2:
+ /* Preserve registers */
+ pushw %ds
+ pushl %eax
+
+ /* Run decompressor if necessary. Decompressor is 32-bit
+ * code, so we must switch to pmode first. Save and restore
+ * GDT over transition to pmode.
+ */
+ movl $decompress, %eax
+ testl %eax, %eax
+ jz 1f
+ pushw %ds
+ pushw %es
+ pushw %fs
+ pushw %gs
+ subw $8, %sp
+ pushw %bp
+ movw %sp, %bp
+ sgdt 2(%bp)
+ pushw %ss /* Store params for _prot_to_real */
+ pushw %cs
+ call _prefix_real_to_prot
+ .code32
+ call decompress
+ call _prefix_prot_to_real
+ .code16
+ popw %ax /* skip */
+ popw %ax /* skip */
+ lgdt 2(%bp)
+ popw %bp
+ addw $8, %sp
+ popw %gs
+ popw %fs
+ popw %es
+ popw %ds
+1:
+
+ /* Set rm_etherboot_location */
+ xorl %eax, %eax
+ movw %cs, %ax
+ movw %ax, %ds
+ shll $4, %eax
+ addl $_prefix_size, %eax
+ movl %eax, _prefix_rm_etherboot_location
+
+ /* Restore registers */
+ popl %eax
+ popw %ds
+
+ /* Make real-mode in_call to Etherboot */
+ jmp _prefix_rm_in_call
+#endif /* CODE16 */
+
+/******************************************************************************
+ * Utility routines that can be called by the "prefix".
+ ******************************************************************************
+ */
+
+#ifdef CODE16
+
+/* Prelocate code: either to an area at the top of free base memory.
+ * Switch stacks to use the stack within the resulting
+ * Etherboot image.
+ *
+ * On entry, %cs:0000 must be the start of the prefix: this is used to
+ * locate the code to be copied.
+ *
+ * This routine takes a single word parameter: the number of bytes to
+ * be transferred from the old stack to the new stack (excluding the
+ * return address and this parameter itself, which will always be
+ * copied). If this value is negative, the stacks will not be
+ * switched.
+ *
+ * Control will "return" to the appropriate point in the relocated
+ * image.
+ */
+
+#define PRELOC_PRESERVE ( 20 )
+#define PRELOC_OFFSET_RETADDR ( PRELOC_PRESERVE )
+#define PRELOC_OFFSET_RETADDR_E ( PRELOC_OFFSET_RETADDR + 4 )
+#define PRELOC_OFFSET_COPY ( PRELOC_OFFSET_RETADDR_E )
+#define PRELOC_OFFSET_COPY_E ( PRELOC_OFFSET_COPY + 2 )
+
+#define PRELOC_ALWAYS_COPY ( PRELOC_OFFSET_COPY_E )
+
+ .code16
+ .globl prelocate
+prelocate:
+ /* Pad to allow for expansion of return address */
+ pushw %ax
+
+ /* Preserve registers */
+ pushaw
+ pushw %ds
+ pushw %es
+
+ /* Claim an area of base memory from the BIOS and put the
+ * payload there.
+ */
+ movw $0x40, %bx
+ movw %bx, %es
+ movw %es:(0x13), %bx /* FBMS in kb to %ax */
+ shlw $6, %bx /* ... in paragraphs */
+ subw $_image_size_pgh, %bx /* Subtract space for image */
+ shrw $6, %bx /* Round down to nearest kb */
+ movw %bx, %es:(0x13) /* ...and claim memory from BIOS */
+ shlw $6, %bx
+
+ /* At this point %bx contains the segment address for the
+ * start of the image (image = prefix + runtime).
+ */
+
+ /* Switch stacks */
+ movw %ss, %ax
+ movw %ax, %ds
+ movw %sp, %si /* %ds:si = current %ss:sp */
+ movw %ss:PRELOC_OFFSET_COPY(%si), %cx
+ testw %cx, %cx
+ js 1f
+ leaw _stack_offset_pgh(%bx), %ax /* %ax = new %ss */
+ movw %ax, %es
+ movw $_stack_size, %di
+ addw $PRELOC_ALWAYS_COPY, %cx
+ subw %cx, %di /* %es:di = new %ss:sp */
+ movw %ax, %ss /* Set new %ss:sp */
+ movw %di, %sp
+ cld
+ rep movsb /* Copy stack contents */
+1:
+
+ /* Do the image copy backwards, since if there's overlap with
+ * a forward copy then it means we're going to get trashed
+ * during the copy anyway...
+ */
+ pushal /* Preserve 32-bit registers */
+ movw %bx, %es /* Destination base for copy */
+ pushw %cs
+ popw %ds /* Source base for copy */
+ movl $_verbatim_size-1, %ecx /* Offset to last byte */
+ movl %ecx, %esi
+ movl %ecx, %edi
+ incl %ecx /* Length */
+ std /* Backwards copy of binary */
+ ADDR32 rep movsb
+ cld
+ popal /* Restore 32-bit registers */
+
+ /* Store (%bx<<4) as image_basemem to be picked up by
+ * basemem.c. Also store image_size, since there's no other
+ * way that we can later know how much memory we allocated.
+ * (_zfile_size is unavailable when rt2 is linked).
+ */
+ pushl %eax
+ xorl %eax, %eax
+ movw %bx, %ax
+ shll $4, %eax
+ movl %eax, %es:_prefix_image_basemem
+ movl $_image_size, %es:_prefix_image_basemem_size
+ popl %eax
+
+ /* Expand original near return address into far return to new
+ * code location.
+ */
+ movw %sp, %bp
+ xchgw %bx, (PRELOC_OFFSET_RETADDR+2)(%bp)
+ movw %bx, (PRELOC_OFFSET_RETADDR+0)(%bp)
+
+ /* Restore registers and return */
+ popw %es
+ popw %ds
+ popaw
+ lret /* Jump to relocated code */
+
+ /* Utility routine to free base memory allocated by prelocate.
+ * Ensure that said memory is not in use (e.g. for the CPU
+ * stack) before calling this routine.
+ */
+ .globl deprelocate
+deprelocate:
+ /* Claim an area of base memory from the BIOS and put the
+ * payload there.
+ */
+ pushw %ax
+ pushw %es
+ movw $0x40, %ax
+ movw %ax, %es
+ movw %es:(0x13), %ax /* FBMS in kb to %ax */
+ shlw $6, %ax /* ... in paragraphs */
+ addw $_image_size_pgh+0x40-1, %ax /* Add space for image and... */
+ shrw $6, %ax /* ...round up to nearest kb */
+ movw %ax, %es:(0x13) /* Give memory back to BIOS */
+ popw %es
+ popw %ax
+ ret
+
+#endif /* CODE16 */
diff --git a/src/arch/i386/core/multiboot_loader.c b/src/arch/i386/core/multiboot_loader.c
new file mode 100644
index 00000000..e9785f1c
--- /dev/null
+++ b/src/arch/i386/core/multiboot_loader.c
@@ -0,0 +1,143 @@
+/* Multiboot support
+ *
+ * 2003-07-02 mmap fix and header probe by SONE Takeshi
+ */
+
+struct multiboot_mods {
+ unsigned mod_start;
+ unsigned mod_end;
+ unsigned char *string;
+ unsigned reserved;
+};
+
+struct multiboot_mmap {
+ unsigned int size;
+ unsigned int base_addr_low;
+ unsigned int base_addr_high;
+ unsigned int length_low;
+ unsigned int length_high;
+ unsigned int type;
+};
+
+/* The structure of a Multiboot 0.6 parameter block. */
+struct multiboot_info {
+ unsigned int flags;
+#define MULTIBOOT_MEM_VALID 0x01
+#define MULTIBOOT_BOOT_DEV_VALID 0x02
+#define MULTIBOOT_CMDLINE_VALID 0x04
+#define MULTIBOOT_MODS_VALID 0x08
+#define MULTIBOOT_AOUT_SYMS_VALID 0x10
+#define MULTIBOOT_ELF_SYMS_VALID 0x20
+#define MULTIBOOT_MMAP_VALID 0x40
+ unsigned int memlower;
+ unsigned int memupper;
+ unsigned int bootdev;
+ unsigned int cmdline; /* physical address of the command line */
+ unsigned mods_count;
+ struct multiboot_mods *mods_addr;
+ unsigned syms_num;
+ unsigned syms_size;
+ unsigned syms_addr;
+ unsigned syms_shndx;
+ unsigned mmap_length;
+ unsigned mmap_addr;
+ /* The structure actually ends here, so I might as well put
+ * the ugly e820 parameters here...
+ */
+ struct multiboot_mmap mmap[E820MAX];
+};
+
+/* Multiboot image header (minimal part) */
+struct multiboot_header {
+ unsigned int magic;
+#define MULTIBOOT_HEADER_MAGIC 0x1BADB002
+ unsigned int flags;
+ unsigned int checksum;
+};
+
+static struct multiboot_header *mbheader;
+
+static struct multiboot_info mbinfo;
+
+static void multiboot_probe(unsigned char *data, int len)
+{
+ int offset;
+ struct multiboot_header *h;
+
+ /* Multiboot spec requires the header to be in first 8KB of the image */
+ if (len > 8192)
+ len = 8192;
+
+ for (offset = 0; offset < len; offset += 4) {
+ h = (struct multiboot_header *) (data + offset);
+ if (h->magic == MULTIBOOT_HEADER_MAGIC
+ && h->magic + h->flags + h->checksum == 0) {
+ printf("/Multiboot");
+ mbheader = h;
+ return;
+ }
+ }
+ mbheader = 0;
+}
+
+static inline void multiboot_boot(unsigned long entry)
+{
+ unsigned char cmdline[512], *c;
+ int i;
+ if (!mbheader)
+ return;
+ /* Etherboot limits the command line to the kernel name,
+ * default parameters and user prompted parameters. All of
+ * them are shorter than 256 bytes. As the kernel name and
+ * the default parameters come from the same BOOTP/DHCP entry
+ * (or if they don't, the parameters are empty), only two
+ * strings of the maximum size are possible. Note this buffer
+ * can overrun if a stupid file name is chosen. Oh well. */
+ c = cmdline;
+ for (i = 0; KERNEL_BUF[i] != 0; i++) {
+ switch (KERNEL_BUF[i]) {
+ case ' ':
+ case '\\':
+ case '"':
+ *c++ = '\\';
+ break;
+ default:
+ break;
+ }
+ *c++ = KERNEL_BUF[i];
+ }
+ (void)sprintf(c, " -retaddr %#lX", virt_to_phys(xend32));
+
+ mbinfo.flags = MULTIBOOT_MMAP_VALID | MULTIBOOT_MEM_VALID |MULTIBOOT_CMDLINE_VALID;
+ mbinfo.memlower = meminfo.basememsize;
+ mbinfo.memupper = meminfo.memsize;
+ mbinfo.bootdev = 0; /* not booted from disk */
+ mbinfo.cmdline = virt_to_phys(cmdline);
+ for (i = 0; i < (int) meminfo.map_count; i++) {
+ mbinfo.mmap[i].size = sizeof(struct multiboot_mmap)
+ - sizeof(unsigned int);
+ mbinfo.mmap[i].base_addr_low =
+ (unsigned int) meminfo.map[i].addr;
+ mbinfo.mmap[i].base_addr_high =
+ (unsigned int) (meminfo.map[i].addr >> 32);
+ mbinfo.mmap[i].length_low =
+ (unsigned int) meminfo.map[i].size;
+ mbinfo.mmap[i].length_high =
+ (unsigned int) (meminfo.map[i].size >> 32);
+ mbinfo.mmap[i].type = meminfo.map[i].type;
+ }
+ mbinfo.mmap_length = meminfo.map_count * sizeof(struct multiboot_mmap);
+ mbinfo.mmap_addr = virt_to_phys(mbinfo.mmap);
+
+ /* The Multiboot 0.6 spec requires all segment registers to be
+ * loaded with an unrestricted, writeable segment.
+ * xstart32 does this for us.
+ */
+
+ /* Start the kernel, passing the Multiboot information record
+ * and the magic number. */
+ os_regs.eax = 0x2BADB002;
+ os_regs.ebx = virt_to_phys(&mbinfo);
+ xstart32(entry);
+ longjmp(restart_etherboot, -2);
+}
diff --git a/src/arch/i386/core/pci_io.c b/src/arch/i386/core/pci_io.c
new file mode 100644
index 00000000..e89561e4
--- /dev/null
+++ b/src/arch/i386/core/pci_io.c
@@ -0,0 +1,352 @@
+/*
+** Support for NE2000 PCI clones added David Monro June 1997
+** Generalised to other NICs by Ken Yap July 1997
+**
+** Most of this is taken from:
+**
+** /usr/src/linux/drivers/pci/pci.c
+** /usr/src/linux/include/linux/pci.h
+** /usr/src/linux/arch/i386/bios32.c
+** /usr/src/linux/include/linux/bios32.h
+** /usr/src/linux/drivers/net/ne.c
+*/
+#ifdef CONFIG_PCI
+#include "etherboot.h"
+#include "pci.h"
+
+#ifdef CONFIG_PCI_DIRECT
+#define PCIBIOS_SUCCESSFUL 0x00
+
+#define DEBUG 0
+
+/*
+ * Functions for accessing PCI configuration space with type 1 accesses
+ */
+
+#define CONFIG_CMD(bus, device_fn, where) (0x80000000 | (bus << 16) | (device_fn << 8) | (where & ~3))
+
+int pcibios_read_config_byte(unsigned int bus, unsigned int device_fn,
+ unsigned int where, uint8_t *value)
+{
+ outl(CONFIG_CMD(bus,device_fn,where), 0xCF8);
+ *value = inb(0xCFC + (where&3));
+ return PCIBIOS_SUCCESSFUL;
+}
+
+int pcibios_read_config_word (unsigned int bus,
+ unsigned int device_fn, unsigned int where, uint16_t *value)
+{
+ outl(CONFIG_CMD(bus,device_fn,where), 0xCF8);
+ *value = inw(0xCFC + (where&2));
+ return PCIBIOS_SUCCESSFUL;
+}
+
+int pcibios_read_config_dword (unsigned int bus, unsigned int device_fn,
+ unsigned int where, uint32_t *value)
+{
+ outl(CONFIG_CMD(bus,device_fn,where), 0xCF8);
+ *value = inl(0xCFC);
+ return PCIBIOS_SUCCESSFUL;
+}
+
+int pcibios_write_config_byte (unsigned int bus, unsigned int device_fn,
+ unsigned int where, uint8_t value)
+{
+ outl(CONFIG_CMD(bus,device_fn,where), 0xCF8);
+ outb(value, 0xCFC + (where&3));
+ return PCIBIOS_SUCCESSFUL;
+}
+
+int pcibios_write_config_word (unsigned int bus, unsigned int device_fn,
+ unsigned int where, uint16_t value)
+{
+ outl(CONFIG_CMD(bus,device_fn,where), 0xCF8);
+ outw(value, 0xCFC + (where&2));
+ return PCIBIOS_SUCCESSFUL;
+}
+
+int pcibios_write_config_dword (unsigned int bus, unsigned int device_fn, unsigned int where, uint32_t value)
+{
+ outl(CONFIG_CMD(bus,device_fn,where), 0xCF8);
+ outl(value, 0xCFC);
+ return PCIBIOS_SUCCESSFUL;
+}
+
+#undef CONFIG_CMD
+
+#else /* CONFIG_PCI_DIRECT not defined */
+
+#if !defined(PCBIOS)
+#error "The pcibios can only be used when the PCBIOS support is compiled in"
+#endif
+
+/* Macro for calling the BIOS32 service. This replaces the old
+ * bios32_call function. Use in a statement such as
+ * __asm__ ( BIOS32_CALL,
+ * : <output registers>
+ * : "S" ( bios32_entry ), <other input registers> );
+ */
+#define BIOS32_CALL "call _virt_to_phys\n\t" \
+ "pushl %%cs\n\t" \
+ "call *%%esi\n\t" \
+ "cli\n\t" \
+ "cld\n\t" \
+ "call _phys_to_virt\n\t"
+
+static unsigned long bios32_entry;
+static unsigned long pcibios_entry;
+
+static unsigned long bios32_service(unsigned long service)
+{
+ unsigned char return_code; /* %al */
+ unsigned long address; /* %ebx */
+ unsigned long length; /* %ecx */
+ unsigned long entry; /* %edx */
+
+ __asm__(BIOS32_CALL
+ : "=a" (return_code),
+ "=b" (address),
+ "=c" (length),
+ "=d" (entry)
+ : "0" (service),
+ "1" (0),
+ "S" (bios32_entry));
+
+ switch (return_code) {
+ case 0:
+ return address + entry;
+ case 0x80: /* Not present */
+ printf("bios32_service(%d) : not present\n", service);
+ return 0;
+ default: /* Shouldn't happen */
+ printf("bios32_service(%d) : returned %#X????\n",
+ service, return_code);
+ return 0;
+ }
+}
+
+int pcibios_read_config_byte(unsigned int bus,
+ unsigned int device_fn, unsigned int where, uint8_t *value)
+{
+ unsigned long ret;
+ unsigned long bx = (bus << 8) | device_fn;
+
+ __asm__(BIOS32_CALL
+ "jc 1f\n\t"
+ "xor %%ah, %%ah\n"
+ "1:"
+ : "=c" (*value),
+ "=a" (ret)
+ : "1" (PCIBIOS_READ_CONFIG_BYTE),
+ "b" (bx),
+ "D" ((long) where),
+ "S" (pcibios_entry));
+ return (int) (ret & 0xff00) >> 8;
+}
+
+int pcibios_read_config_word(unsigned int bus,
+ unsigned int device_fn, unsigned int where, uint16_t *value)
+{
+ unsigned long ret;
+ unsigned long bx = (bus << 8) | device_fn;
+
+ __asm__(BIOS32_CALL
+ "jc 1f\n\t"
+ "xor %%ah, %%ah\n"
+ "1:"
+ : "=c" (*value),
+ "=a" (ret)
+ : "1" (PCIBIOS_READ_CONFIG_WORD),
+ "b" (bx),
+ "D" ((long) where),
+ "S" (pcibios_entry));
+ return (int) (ret & 0xff00) >> 8;
+}
+
+int pcibios_read_config_dword(unsigned int bus,
+ unsigned int device_fn, unsigned int where, uint32_t *value)
+{
+ unsigned long ret;
+ unsigned long bx = (bus << 8) | device_fn;
+
+ __asm__(BIOS32_CALL
+ "jc 1f\n\t"
+ "xor %%ah, %%ah\n"
+ "1:"
+ : "=c" (*value),
+ "=a" (ret)
+ : "1" (PCIBIOS_READ_CONFIG_DWORD),
+ "b" (bx),
+ "D" ((long) where),
+ "S" (pcibios_entry));
+ return (int) (ret & 0xff00) >> 8;
+}
+
+int pcibios_write_config_byte (unsigned int bus,
+ unsigned int device_fn, unsigned int where, uint8_t value)
+{
+ unsigned long ret;
+ unsigned long bx = (bus << 8) | device_fn;
+
+ __asm__(BIOS32_CALL
+ "jc 1f\n\t"
+ "xor %%ah, %%ah\n"
+ "1:"
+ : "=a" (ret)
+ : "0" (PCIBIOS_WRITE_CONFIG_BYTE),
+ "c" (value),
+ "b" (bx),
+ "D" ((long) where),
+ "S" (pcibios_entry));
+ return (int) (ret & 0xff00) >> 8;
+}
+
+int pcibios_write_config_word (unsigned int bus,
+ unsigned int device_fn, unsigned int where, uint16_t value)
+{
+ unsigned long ret;
+ unsigned long bx = (bus << 8) | device_fn;
+
+ __asm__(BIOS32_CALL
+ "jc 1f\n\t"
+ "xor %%ah, %%ah\n"
+ "1:"
+ : "=a" (ret)
+ : "0" (PCIBIOS_WRITE_CONFIG_WORD),
+ "c" (value),
+ "b" (bx),
+ "D" ((long) where),
+ "S" (pcibios_entry));
+ return (int) (ret & 0xff00) >> 8;
+}
+
+int pcibios_write_config_dword (unsigned int bus,
+ unsigned int device_fn, unsigned int where, uint32_t value)
+{
+ unsigned long ret;
+ unsigned long bx = (bus << 8) | device_fn;
+
+ __asm__(BIOS32_CALL
+ "jc 1f\n\t"
+ "xor %%ah, %%ah\n"
+ "1:"
+ : "=a" (ret)
+ : "0" (PCIBIOS_WRITE_CONFIG_DWORD),
+ "c" (value),
+ "b" (bx),
+ "D" ((long) where),
+ "S" (pcibios_entry));
+ return (int) (ret & 0xff00) >> 8;
+}
+
+static void check_pcibios(void)
+{
+ unsigned long signature;
+ unsigned char present_status;
+ unsigned char major_revision;
+ unsigned char minor_revision;
+ int pack;
+
+ if ((pcibios_entry = bios32_service(PCI_SERVICE))) {
+ __asm__(BIOS32_CALL
+ "jc 1f\n\t"
+ "xor %%ah, %%ah\n"
+ "1:\tshl $8, %%eax\n\t"
+ "movw %%bx, %%ax"
+ : "=d" (signature),
+ "=a" (pack)
+ : "1" (PCIBIOS_PCI_BIOS_PRESENT),
+ "S" (pcibios_entry)
+ : "bx", "cx");
+
+ present_status = (pack >> 16) & 0xff;
+ major_revision = (pack >> 8) & 0xff;
+ minor_revision = pack & 0xff;
+ if (present_status || (signature != PCI_SIGNATURE)) {
+ printf("ERROR: BIOS32 says PCI BIOS, but no PCI "
+ "BIOS????\n");
+ pcibios_entry = 0;
+ }
+#if DEBUG
+ if (pcibios_entry) {
+ printf ("pcibios_init : PCI BIOS revision %hhX.%hhX"
+ " entry at %#X\n", major_revision,
+ minor_revision, pcibios_entry);
+ }
+#endif
+ }
+}
+
+static void pcibios_init(void)
+{
+ union bios32 *check;
+ unsigned char sum;
+ int i, length;
+ bios32_entry = 0;
+
+ /*
+ * Follow the standard procedure for locating the BIOS32 Service
+ * directory by scanning the permissible address range from
+ * 0xe0000 through 0xfffff for a valid BIOS32 structure.
+ *
+ */
+
+ for (check = phys_to_virt(0xe0000); (void *)check <= phys_to_virt(0xffff0); ++check) {
+ if (check->fields.signature != BIOS32_SIGNATURE)
+ continue;
+ length = check->fields.length * 16;
+ if (!length)
+ continue;
+ sum = 0;
+ for (i = 0; i < length ; ++i)
+ sum += check->chars[i];
+ if (sum != 0)
+ continue;
+ if (check->fields.revision != 0) {
+ printf("pcibios_init : unsupported revision %d at %#X, mail drew@colorado.edu\n",
+ check->fields.revision, check);
+ continue;
+ }
+#if DEBUG
+ printf("pcibios_init : BIOS32 Service Directory "
+ "structure at %#X\n", check);
+#endif
+ if (!bios32_entry) {
+ if (check->fields.entry >= 0x100000) {
+ printf("pcibios_init: entry in high "
+ "memory, giving up\n");
+ return;
+ } else {
+ bios32_entry = check->fields.entry;
+#if DEBUG
+ printf("pcibios_init : BIOS32 Service Directory"
+ " entry at %#X\n", bios32_entry);
+#endif
+ }
+ }
+ }
+ if (bios32_entry)
+ check_pcibios();
+}
+#endif /* CONFIG_PCI_DIRECT not defined*/
+
+unsigned long pcibios_bus_base(unsigned int bus __unused)
+{
+ /* architecturally this must be 0 */
+ return 0;
+}
+
+void find_pci(int type, struct pci_device *dev)
+{
+#ifndef CONFIG_PCI_DIRECT
+ if (!pcibios_entry) {
+ pcibios_init();
+ }
+ if (!pcibios_entry) {
+ printf("pci_init: no BIOS32 detected\n");
+ return;
+ }
+#endif
+ return scan_pci_bus(type, dev);
+}
+#endif /* CONFIG_PCI */
diff --git a/src/arch/i386/core/pic8259.c b/src/arch/i386/core/pic8259.c
new file mode 100644
index 00000000..cc2071fc
--- /dev/null
+++ b/src/arch/i386/core/pic8259.c
@@ -0,0 +1,331 @@
+/*
+ * Basic support for controlling the 8259 Programmable Interrupt Controllers.
+ *
+ * Initially written by Michael Brown (mcb30).
+ */
+
+#include <etherboot.h>
+#include "pic8259.h"
+#include "realmode.h"
+
+#ifdef DEBUG_IRQ
+#define DBG(...) printf ( __VA_ARGS__ )
+#else
+#define DBG(...)
+#endif
+
+/* State of trivial IRQ handler */
+irq_t trivial_irq_installed_on = IRQ_NONE;
+static uint16_t trivial_irq_previous_trigger_count = 0;
+
+/* The actual trivial IRQ handler
+ *
+ * Note: we depend on the C compiler not realising that we're putting
+ * variables in the ".text16" section and therefore not forcing them
+ * back to the ".data" section. I don't see any reason to expect this
+ * behaviour to change.
+ *
+ * These must *not* be the first variables to appear in this file; the
+ * first variable to appear gets the ".data" directive.
+ */
+RM_FRAGMENT(_trivial_irq_handler,
+ "pushw %bx\n\t"
+ "call 1f\n1:\tpopw %bx\n\t" /* PIC access to variables */
+ "incw %cs:(_trivial_irq_trigger_count-1b)(%bx)\n\t"
+ "popw %bx\n\t"
+ "iret\n\t"
+ "\n\t"
+ ".globl _trivial_irq_trigger_count\n\t"
+ "_trivial_irq_trigger_count: .short 0\n\t"
+ "\n\t"
+ ".globl _trivial_irq_chain_to\n\t"
+ "_trivial_irq_chain_to: .short 0,0\n\t"
+ "\n\t"
+ ".globl _trivial_irq_chain\n\t"
+ "_trivial_irq_chain: .byte 0\n\t"
+ );
+extern volatile uint16_t _trivial_irq_trigger_count;
+extern segoff_t _trivial_irq_chain_to;
+extern int8_t _trivial_irq_chain;
+
+/* Current locations of trivial IRQ handler. These will change at
+ * runtime when relocation is used; the handler needs to be copied to
+ * base memory before being installed.
+ */
+void (*trivial_irq_handler)P((void)) = _trivial_irq_handler;
+uint16_t volatile *trivial_irq_trigger_count = &_trivial_irq_trigger_count;
+segoff_t *trivial_irq_chain_to = &_trivial_irq_chain_to;
+uint8_t *trivial_irq_chain = &_trivial_irq_chain;
+
+/* Install a handler for the specified IRQ. Address of previous
+ * handler will be stored in previous_handler. Enabled/disabled state
+ * of IRQ will be preserved across call, therefore if the handler does
+ * chaining, ensure that either (a) IRQ is disabled before call, or
+ * (b) previous_handler points directly to the place that the handler
+ * picks up its chain-to address.
+ */
+
+int install_irq_handler ( irq_t irq, segoff_t *handler,
+ uint8_t *previously_enabled,
+ segoff_t *previous_handler ) {
+ segoff_t *irq_vector = IRQ_VECTOR ( irq );
+ *previously_enabled = irq_enabled ( irq );
+
+ if ( irq > IRQ_MAX ) {
+ DBG ( "Invalid IRQ number %d\n" );
+ return 0;
+ }
+
+ previous_handler->segment = irq_vector->segment;
+ previous_handler->offset = irq_vector->offset;
+ if ( *previously_enabled ) disable_irq ( irq );
+ DBG ( "Installing handler at %hx:%hx for IRQ %d (vector 0000:%hx),"
+ " leaving %s\n",
+ handler->segment, handler->offset, irq, virt_to_phys(irq_vector),
+ ( *previously_enabled ? "enabled" : "disabled" ) );
+ DBG ( "...(previous handler at %hx:%hx)\n",
+ previous_handler->segment, previous_handler->offset );
+ irq_vector->segment = handler->segment;
+ irq_vector->offset = handler->offset;
+ if ( *previously_enabled ) enable_irq ( irq );
+ return 1;
+}
+
+/* Remove handler for the specified IRQ. Routine checks that another
+ * handler has not been installed that chains to handler before
+ * uninstalling handler. Enabled/disabled state of the IRQ will be
+ * restored to that specified by previously_enabled.
+ */
+
+int remove_irq_handler ( irq_t irq, segoff_t *handler,
+ uint8_t *previously_enabled,
+ segoff_t *previous_handler ) {
+ segoff_t *irq_vector = IRQ_VECTOR ( irq );
+
+ if ( irq > IRQ_MAX ) {
+ DBG ( "Invalid IRQ number %d\n" );
+ return 0;
+ }
+ if ( ( irq_vector->segment != handler->segment ) ||
+ ( irq_vector->offset != handler->offset ) ) {
+ DBG ( "Cannot remove handler for IRQ %d\n" );
+ return 0;
+ }
+
+ DBG ( "Removing handler for IRQ %d\n", irq );
+ disable_irq ( irq );
+ irq_vector->segment = previous_handler->segment;
+ irq_vector->offset = previous_handler->offset;
+ if ( *previously_enabled ) enable_irq ( irq );
+ return 1;
+}
+
+/* Install the trivial IRQ handler. This routine installs the
+ * handler, tests it and enables the IRQ.
+ */
+
+int install_trivial_irq_handler ( irq_t irq ) {
+ segoff_t trivial_irq_handler_segoff = SEGOFF(trivial_irq_handler);
+
+ if ( trivial_irq_installed_on != IRQ_NONE ) {
+ DBG ( "Can install trivial IRQ handler only once\n" );
+ return 0;
+ }
+ if ( SEGMENT(trivial_irq_handler) > 0xffff ) {
+ DBG ( "Trivial IRQ handler not in base memory\n" );
+ return 0;
+ }
+
+ DBG ( "Installing trivial IRQ handler on IRQ %d\n", irq );
+ if ( ! install_irq_handler ( irq, &trivial_irq_handler_segoff,
+ trivial_irq_chain,
+ trivial_irq_chain_to ) )
+ return 0;
+ trivial_irq_installed_on = irq;
+
+ DBG ( "Testing trivial IRQ handler\n" );
+ disable_irq ( irq );
+ *trivial_irq_trigger_count = 0;
+ trivial_irq_previous_trigger_count = 0;
+ fake_irq ( irq );
+ if ( ! trivial_irq_triggered ( irq ) ) {
+ DBG ( "Installation of trivial IRQ handler failed\n" );
+ remove_trivial_irq_handler ( irq );
+ return 0;
+ }
+ /* Send EOI just in case there was a leftover interrupt */
+ send_specific_eoi ( irq );
+ DBG ( "Trivial IRQ handler installed successfully\n" );
+ enable_irq ( irq );
+ return 1;
+}
+
+/* Remove the trivial IRQ handler.
+ */
+
+int remove_trivial_irq_handler ( irq_t irq ) {
+ segoff_t trivial_irq_handler_segoff = SEGOFF(trivial_irq_handler);
+
+ if ( trivial_irq_installed_on == IRQ_NONE ) return 1;
+ if ( irq != trivial_irq_installed_on ) {
+ DBG ( "Cannot uninstall trivial IRQ handler from IRQ %d; "
+ "is installed on IRQ %d\n", irq,
+ trivial_irq_installed_on );
+ return 0;
+ }
+
+ if ( ! remove_irq_handler ( irq, &trivial_irq_handler_segoff,
+ trivial_irq_chain,
+ trivial_irq_chain_to ) )
+ return 0;
+
+ if ( trivial_irq_triggered ( trivial_irq_installed_on ) ) {
+ DBG ( "Sending EOI for unwanted trivial IRQ\n" );
+ send_specific_eoi ( trivial_irq_installed_on );
+ }
+
+ trivial_irq_installed_on = IRQ_NONE;
+ return 1;
+}
+
+/* Safe method to detect whether or not trivial IRQ has been
+ * triggered. Using this call avoids potential race conditions. This
+ * call will return success only once per trigger.
+ */
+
+int trivial_irq_triggered ( irq_t irq ) {
+ uint16_t trivial_irq_this_trigger_count = *trivial_irq_trigger_count;
+ int triggered = ( trivial_irq_this_trigger_count -
+ trivial_irq_previous_trigger_count );
+
+ /* irq is not used at present, but we have it in the API for
+ * future-proofing; in case we want the facility to have
+ * multiple trivial IRQ handlers installed simultaneously.
+ *
+ * Avoid compiler warning about unused variable.
+ */
+ if ( irq == IRQ_NONE ) {};
+
+ trivial_irq_previous_trigger_count = trivial_irq_this_trigger_count;
+ return triggered ? 1 : 0;
+}
+
+/* Copy trivial IRQ handler to a new location. Typically used to copy
+ * the handler into base memory; when relocation is being used we need
+ * to do this before installing the handler.
+ *
+ * Call with target=NULL in order to restore the handler to its
+ * original location.
+ */
+
+int copy_trivial_irq_handler ( void *target, size_t target_size ) {
+ irq_t currently_installed_on = trivial_irq_installed_on;
+ uint32_t offset = ( target == NULL ? 0 :
+ target - (void*)_trivial_irq_handler );
+
+ if (( target != NULL ) && ( target_size < TRIVIAL_IRQ_HANDLER_SIZE )) {
+ DBG ( "Insufficient space to copy trivial IRQ handler\n" );
+ return 0;
+ }
+
+ if ( currently_installed_on != IRQ_NONE ) {
+ DBG ("WARNING: relocating trivial IRQ handler while in use\n");
+ if ( ! remove_trivial_irq_handler ( currently_installed_on ) )
+ return 0;
+ }
+
+ /* Do the actual copy */
+ if ( target != NULL ) {
+ DBG ( "Copying trivial IRQ handler to %hx:%hx\n",
+ SEGMENT(target), OFFSET(target) );
+ memcpy ( target, _trivial_irq_handler,
+ TRIVIAL_IRQ_HANDLER_SIZE );
+ } else {
+ DBG ( "Restoring trivial IRQ handler to original location\n" );
+ }
+ /* Update all the pointers to structures within the handler */
+ trivial_irq_handler = ( void (*)P((void)) )
+ ( (void*)_trivial_irq_handler + offset );
+ trivial_irq_trigger_count = (uint16_t*)
+ ( (void*)&_trivial_irq_trigger_count + offset );
+ trivial_irq_chain_to = (segoff_t*)
+ ( (void*)&_trivial_irq_chain_to + offset );
+ trivial_irq_chain = (uint8_t*)
+ ( (void*)&_trivial_irq_chain + offset );
+
+ if ( currently_installed_on != IRQ_NONE ) {
+ if ( ! install_trivial_irq_handler ( currently_installed_on ) )
+ return 0;
+ }
+ return 1;
+}
+
+/* Send non-specific EOI(s). This seems to be inherently unsafe.
+ */
+
+void send_nonspecific_eoi ( irq_t irq ) {
+ DBG ( "Sending non-specific EOI for IRQ %d\n", irq );
+ if ( irq >= IRQ_PIC_CUTOFF ) {
+ outb ( ICR_EOI_NON_SPECIFIC, PIC2_ICR );
+ }
+ outb ( ICR_EOI_NON_SPECIFIC, PIC1_ICR );
+}
+
+/* Send specific EOI(s).
+ */
+
+void send_specific_eoi ( irq_t irq ) {
+ DBG ( "Sending specific EOI for IRQ %d\n", irq );
+ outb ( ICR_EOI_SPECIFIC | ICR_VALUE(irq), ICR_REG(irq) );
+ if ( irq >= IRQ_PIC_CUTOFF ) {
+ outb ( ICR_EOI_SPECIFIC | ICR_VALUE(CHAINED_IRQ),
+ ICR_REG(CHAINED_IRQ) );
+ }
+}
+
+/* Fake an IRQ
+ */
+
+void fake_irq ( irq_t irq ) {
+ struct {
+ uint16_t int_number;
+ } PACKED in_stack;
+
+ /* Convert IRQ to INT number:
+ *
+ * subb $0x08,%cl Invert bit 3, set bits 4-7 iff irq < 8
+ * xorb $0x70,%cl Invert bits 4-6
+ * andb $0x7f,%cl Clear bit 7
+ *
+ * No, it's not the most intuitive method, but I was proud to
+ * get it down to three lines of assembler when this routine
+ * was originally implemented in pcbios.S.
+ */
+ in_stack.int_number = ( ( irq - 8 ) ^ 0x70 ) & 0x7f;
+
+ RM_FRAGMENT(rm_fake_irq,
+ "popw %ax\n\t" /* %ax = INT number */
+ "call 1f\n1:\tpop %bx\n\t"
+ "movb %al, %cs:(2f-1b+1)(%bx)\n\t" /* Overwrite INT number..*/
+ "\n2:\tint $0x00\n\t" /* ..in this instruction */
+ );
+
+ real_call ( rm_fake_irq, &in_stack, NULL );
+}
+
+/* Dump current 8259 status: enabled IRQs and handler addresses.
+ */
+
+#ifdef DEBUG_IRQ
+void dump_irq_status ( void ) {
+ int irq = 0;
+
+ for ( irq = 0; irq < 16; irq++ ) {
+ if ( irq_enabled ( irq ) ) {
+ printf ( "IRQ%d enabled, ISR at %hx:%hx\n", irq,
+ IRQ_VECTOR(irq)->segment,
+ IRQ_VECTOR(irq)->offset );
+ }
+ }
+}
+#endif
diff --git a/src/arch/i386/core/prefixudata.lds b/src/arch/i386/core/prefixudata.lds
new file mode 100644
index 00000000..1c76128e
--- /dev/null
+++ b/src/arch/i386/core/prefixudata.lds
@@ -0,0 +1,8 @@
+OUTPUT_FORMAT("elf32-i386", "elf32-i386", "elf32-i386")
+OUTPUT_ARCH(i386)
+
+SECTIONS {
+ .prefix.udata : {
+ *(*)
+ }
+}
diff --git a/src/arch/i386/core/prefixzdata.lds b/src/arch/i386/core/prefixzdata.lds
new file mode 100644
index 00000000..bf6ea977
--- /dev/null
+++ b/src/arch/i386/core/prefixzdata.lds
@@ -0,0 +1,8 @@
+OUTPUT_FORMAT("elf32-i386", "elf32-i386", "elf32-i386")
+OUTPUT_ARCH(i386)
+
+SECTIONS {
+ .prefix.zdata : {
+ *(*)
+ }
+}
diff --git a/src/arch/i386/core/pxe_callbacks.c b/src/arch/i386/core/pxe_callbacks.c
new file mode 100644
index 00000000..344d34fe
--- /dev/null
+++ b/src/arch/i386/core/pxe_callbacks.c
@@ -0,0 +1,364 @@
+/* PXE callback mechanisms. This file contains only the portions
+ * specific to i386: i.e. the low-level mechanisms for calling in from
+ * an NBP to the PXE stack and for starting an NBP from the PXE stack.
+ */
+
+#ifdef PXE_EXPORT
+
+#include "etherboot.h"
+#include "callbacks.h"
+#include "realmode.h"
+#include "pxe.h"
+#include "pxe_callbacks.h"
+#include "pxe_export.h"
+#include "hidemem.h"
+#include <stdarg.h>
+
+#define INSTALLED(x) ( (typeof(&x)) ( (void*)(&x) \
+ - &pxe_callback_interface \
+ + (void*)&pxe_stack->arch_data ) )
+#define pxe_intercept_int1a INSTALLED(_pxe_intercept_int1a)
+#define pxe_intercepted_int1a INSTALLED(_pxe_intercepted_int1a)
+#define pxe_pxenv_location INSTALLED(_pxe_pxenv_location)
+#define INT1A_VECTOR ( (segoff_t*) ( phys_to_virt( 4 * 0x1a ) ) )
+
+/* The overall size of the PXE stack is ( sizeof(pxe_stack_t) +
+ * pxe_callback_interface_size + rm_callback_interface_size ).
+ * Unfortunately, this isn't a compile-time constant, since
+ * {pxe,rm}_callback_interface_size depend on the length of the
+ * assembly code in these interfaces.
+ *
+ * We used to have a function pxe_stack_size() which returned this
+ * value. However, it actually needs to be a link-time constant, so
+ * that it can appear in the UNDIROMID structure in romprefix.S. We
+ * therefore export the three component sizes as absolute linker
+ * symbols, get the linker to add them together and generate a new
+ * absolute symbol _pxe_stack_size. We then import this value into a
+ * C variable pxe_stack_size, for access from C code.
+ */
+
+/* gcc won't let us use extended asm outside a function (compiler
+ * bug), ao we have to put these asm statements inside a dummy
+ * function.
+ */
+static void work_around_gcc_bug ( void ) __attribute__ ((used));
+static void work_around_gcc_bug ( void ) {
+ /* Export sizeof(pxe_stack_t) as absolute linker symbol */
+ __asm__ ( ".globl _pxe_stack_t_size" );
+ __asm__ ( ".equ _pxe_stack_t_size, %c0"
+ : : "i" (sizeof(pxe_stack_t)) );
+}
+/* Import _pxe_stack_size absolute linker symbol into C variable */
+extern int pxe_stack_size;
+__asm__ ( "pxe_stack_size: .long _pxe_stack_size" );
+
+/* Utility routine: byte checksum
+ */
+uint8_t byte_checksum ( void *address, size_t size ) {
+ unsigned int i, sum = 0;
+
+ for ( i = 0; i < size; i++ ) {
+ sum += ((uint8_t*)address)[i];
+ }
+ return (uint8_t)sum;
+}
+
+/* install_pxe_stack(): install PXE stack.
+ *
+ * Use base = NULL for auto-allocation of base memory
+ *
+ * IMPORTANT: no further allocation of base memory should take place
+ * before the PXE stack is removed. This is to work around a small
+ * but important deficiency in the PXE specification.
+ */
+pxe_stack_t * install_pxe_stack ( void *base ) {
+ pxe_t *pxe;
+ pxenv_t *pxenv;
+ void *pxe_callback_code;
+ void (*pxe_in_call_far)(void);
+ void (*pxenv_in_call_far)(void);
+ void *rm_callback_code;
+ void *e820mangler_code;
+ void *end;
+
+ /* If already installed, just return */
+ if ( pxe_stack != NULL ) return pxe_stack;
+
+ /* Allocate base memory if requested to do so
+ */
+ if ( base == NULL ) {
+ base = allot_base_memory ( pxe_stack_size );
+ if ( base == NULL ) return NULL;
+ }
+
+ /* Round address up to 16-byte physical alignment */
+ pxe_stack = (pxe_stack_t *)
+ ( phys_to_virt ( ( virt_to_phys(base) + 0xf ) & ~0xf ) );
+ /* Zero out allocated stack */
+ memset ( pxe_stack, 0, sizeof(*pxe_stack) );
+
+ /* Calculate addresses for portions of the stack */
+ pxe = &(pxe_stack->pxe);
+ pxenv = &(pxe_stack->pxenv);
+ pxe_callback_code = &(pxe_stack->arch_data);
+ pxe_in_call_far = _pxe_in_call_far +
+ ( pxe_callback_code - &pxe_callback_interface );
+ pxenv_in_call_far = _pxenv_in_call_far +
+ ( pxe_callback_code - &pxe_callback_interface );
+ rm_callback_code = pxe_callback_code + pxe_callback_interface_size;
+
+ e820mangler_code = (void*)(((int)rm_callback_code +
+ rm_callback_interface_size + 0xf ) & ~0xf);
+ end = e820mangler_code + e820mangler_size;
+
+ /* Initialise !PXE data structures */
+ memcpy ( pxe->Signature, "!PXE", 4 );
+ pxe->StructLength = sizeof(*pxe);
+ pxe->StructRev = 0;
+ pxe->reserved_1 = 0;
+ /* We don't yet have an UNDI ROM ID structure */
+ pxe->UNDIROMID.segment = 0;
+ pxe->UNDIROMID.offset = 0;
+ /* or a BC ROM ID structure */
+ pxe->BaseROMID.segment = 0;
+ pxe->BaseROMID.offset = 0;
+ pxe->EntryPointSP.segment = SEGMENT(pxe_stack);
+ pxe->EntryPointSP.offset = (void*)pxe_in_call_far - (void*)pxe_stack;
+ /* No %esp-compatible entry point yet */
+ pxe->EntryPointESP.segment = 0;
+ pxe->EntryPointESP.offset = 0;
+ pxe->StatusCallout.segment = -1;
+ pxe->StatusCallout.offset = -1;
+ pxe->reserved_2 = 0;
+ pxe->SegDescCn = 7;
+ pxe->FirstSelector = 0;
+ /* PXE specification doesn't say anything about when the stack
+ * space should get freed. We work around this by claiming it
+ * as our data segment as well.
+ */
+ pxe->Stack.Seg_Addr = pxe->UNDIData.Seg_Addr = real_mode_stack >> 4;
+ pxe->Stack.Phy_Addr = pxe->UNDIData.Phy_Addr = real_mode_stack;
+ pxe->Stack.Seg_Size = pxe->UNDIData.Seg_Size = real_mode_stack_size;
+ /* Code segment has to be the one containing the data structures... */
+ pxe->UNDICode.Seg_Addr = SEGMENT(pxe_stack);
+ pxe->UNDICode.Phy_Addr = virt_to_phys(pxe_stack);
+ pxe->UNDICode.Seg_Size = end - (void*)pxe_stack;
+ /* No base code loaded */
+ pxe->BC_Data.Seg_Addr = 0;
+ pxe->BC_Data.Phy_Addr = 0;
+ pxe->BC_Data.Seg_Size = 0;
+ pxe->BC_Code.Seg_Addr = 0;
+ pxe->BC_Code.Phy_Addr = 0;
+ pxe->BC_Code.Seg_Size = 0;
+ pxe->BC_CodeWrite.Seg_Addr = 0;
+ pxe->BC_CodeWrite.Phy_Addr = 0;
+ pxe->BC_CodeWrite.Seg_Size = 0;
+ pxe->StructCksum -= byte_checksum ( pxe, sizeof(*pxe) );
+
+ /* Initialise PXENV+ data structures */
+ memcpy ( pxenv->Signature, "PXENV+", 6 );
+ pxenv->Version = 0x201;
+ pxenv->Length = sizeof(*pxenv);
+ pxenv->RMEntry.segment = SEGMENT(pxe_stack);
+ pxenv->RMEntry.offset = (void*)pxenv_in_call_far - (void*)pxe_stack;
+ pxenv->PMOffset = 0; /* "Do not use" says the PXE spec */
+ pxenv->PMSelector = 0; /* "Do not use" says the PXE spec */
+ pxenv->StackSeg = pxenv->UNDIDataSeg = real_mode_stack >> 4;
+ pxenv->StackSize = pxenv->UNDIDataSize = real_mode_stack_size;
+ pxenv->BC_CodeSeg = 0;
+ pxenv->BC_CodeSize = 0;
+ pxenv->BC_DataSeg = 0;
+ pxenv->BC_DataSize = 0;
+ /* UNDIData{Seg,Size} set above */
+ pxenv->UNDICodeSeg = SEGMENT(pxe_stack);
+ pxenv->UNDICodeSize = end - (void*)pxe_stack;
+ pxenv->PXEPtr.segment = SEGMENT(pxe);
+ pxenv->PXEPtr.offset = OFFSET(pxe);
+ pxenv->Checksum -= byte_checksum ( pxenv, sizeof(*pxenv) );
+
+ /* Mark stack as inactive */
+ pxe_stack->state = CAN_UNLOAD;
+
+ /* Install PXE and RM callback code and E820 mangler */
+ memcpy ( pxe_callback_code, &pxe_callback_interface,
+ pxe_callback_interface_size );
+ install_rm_callback_interface ( rm_callback_code, 0 );
+ install_e820mangler ( e820mangler_code );
+
+ return pxe_stack;
+}
+
+/* Use the UNDI data segment as our real-mode stack. This is for when
+ * we have been loaded via the UNDI loader
+ */
+void use_undi_ds_for_rm_stack ( uint16_t ds ) {
+ forget_real_mode_stack();
+ real_mode_stack = virt_to_phys ( VIRTUAL ( ds, 0 ) );
+ lock_real_mode_stack = 1;
+}
+
+/* Activate PXE stack (i.e. hook interrupt vectors). The PXE stack
+ * *can* be used before it is activated, but it really shoudln't.
+ */
+int hook_pxe_stack ( void ) {
+ if ( pxe_stack == NULL ) return 0;
+ if ( pxe_stack->state >= MIDWAY ) return 1;
+
+ /* Hook INT15 handler */
+ hide_etherboot();
+
+ /* Hook INT1A handler */
+ *pxe_intercepted_int1a = *INT1A_VECTOR;
+ pxe_pxenv_location->segment = SEGMENT(pxe_stack);
+ pxe_pxenv_location->offset = (void*)&pxe_stack->pxenv
+ - (void*)pxe_stack;
+ INT1A_VECTOR->segment = SEGMENT(&pxe_stack->arch_data);
+ INT1A_VECTOR->offset = (void*)pxe_intercept_int1a
+ - (void*)&pxe_stack->arch_data;
+
+ /* Mark stack as active */
+ pxe_stack->state = MIDWAY;
+ return 1;
+}
+
+/* Deactivate the PXE stack (i.e. unhook interrupt vectors).
+ */
+int unhook_pxe_stack ( void ) {
+ if ( pxe_stack == NULL ) return 0;
+ if ( pxe_stack->state <= CAN_UNLOAD ) return 1;
+
+ /* Restore original INT15 and INT1A handlers */
+ *INT1A_VECTOR = *pxe_intercepted_int1a;
+ if ( !unhide_etherboot() ) {
+ /* Cannot unhook INT15. We're up the creek without
+ * even a suitable log out of which to fashion a
+ * paddle. There are some very badly behaved NBPs
+ * that will ignore plaintive pleas such as
+ * PXENV_KEEP_UNDI and just zero out our code anyway.
+ * This means they end up vapourising an active INT15
+ * handler, which is generally not a good thing to do.
+ */
+ return 0;
+ }
+
+ /* Mark stack as inactive */
+ pxe_stack->state = CAN_UNLOAD;
+ return 1;
+}
+
+/* remove_pxe_stack(): remove PXE stack installed by install_pxe_stack()
+ */
+void remove_pxe_stack ( void ) {
+ /* Ensure stack is deactivated, then free up the memory */
+ if ( ensure_pxe_state ( CAN_UNLOAD ) ) {
+ forget_base_memory ( pxe_stack, pxe_stack_size );
+ pxe_stack = NULL;
+ } else {
+ printf ( "Cannot remove PXE stack!\n" );
+ }
+}
+
+/* xstartpxe(): start up a PXE image
+ */
+int xstartpxe ( void ) {
+ int nbp_exit;
+ struct {
+ reg16_t bx;
+ reg16_t es;
+ segoff_t pxe;
+ } PACKED in_stack;
+
+ /* Set up registers and stack parameters to pass to PXE NBP */
+ in_stack.es.word = SEGMENT(&(pxe_stack->pxenv));
+ in_stack.bx.word = OFFSET(&(pxe_stack->pxenv));
+ in_stack.pxe.segment = SEGMENT(&(pxe_stack->pxe));
+ in_stack.pxe.offset = OFFSET(&(pxe_stack->pxe));
+
+ /* Real-mode trampoline fragment used to jump to PXE NBP
+ */
+ RM_FRAGMENT(jump_to_pxe_nbp,
+ "popw %bx\n\t"
+ "popw %es\n\t"
+ "lcall $" RM_STR(PXE_LOAD_SEGMENT) ", $" RM_STR(PXE_LOAD_OFFSET) "\n\t"
+ );
+
+ /* Call to PXE image */
+ gateA20_unset();
+ nbp_exit = real_call ( jump_to_pxe_nbp, &in_stack, NULL );
+ gateA20_set();
+
+ return nbp_exit;
+}
+
+int pxe_in_call ( in_call_data_t *in_call_data, va_list params ) {
+ /* i386 calling conventions; the only two defined by Intel's
+ * PXE spec.
+ *
+ * Assembly code must pass a long containing the PXE version
+ * code (i.e. 0x201 for !PXE, 0x200 for PXENV+) as the first
+ * parameter after the in_call opcode. This is used to decide
+ * whether to take parameters from the stack (!PXE) or from
+ * registers (PXENV+).
+ */
+ uint32_t api_version = va_arg ( params, typeof(api_version) );
+ uint16_t opcode;
+ segoff_t segoff;
+ t_PXENV_ANY *structure;
+
+ if ( api_version >= 0x201 ) {
+ /* !PXE calling convention */
+ pxe_call_params_t pxe_params
+ = va_arg ( params, typeof(pxe_params) );
+ opcode = pxe_params.opcode;
+ segoff = pxe_params.segoff;
+ } else {
+ /* PXENV+ calling convention */
+ opcode = in_call_data->pm->regs.bx;
+ segoff.segment = in_call_data->rm->seg_regs.es;
+ segoff.offset = in_call_data->pm->regs.di;
+ }
+ structure = VIRTUAL ( segoff.segment, segoff.offset );
+ return pxe_api_call ( opcode, structure );
+}
+
+#ifdef TEST_EXCLUDE_ALGORITHM
+/* This code retained because it's a difficult algorithm to tweak with
+ * confidence
+ */
+int ___test_exclude ( int start, int len, int estart, int elen, int fixbase );
+void __test_exclude ( int start, int len, int estart, int elen, int fixbase ) {
+ int newrange = ___test_exclude ( start, len, estart, elen, fixbase );
+ int newstart = ( newrange >> 16 ) & 0xffff;
+ int newlen = ( newrange & 0xffff );
+
+ printf ( "[%x,%x): excluding [%x,%x) %s gives [%x,%x)\n",
+ start, start + len,
+ estart, estart + elen,
+ ( fixbase == 0 ) ? " " : "fb",
+ newstart, newstart + newlen );
+}
+void _test_exclude ( int start, int len, int estart, int elen ) {
+ __test_exclude ( start, len, estart, elen, 0 );
+ __test_exclude ( start, len, estart, elen, 1 );
+}
+void test_exclude ( void ) {
+ _test_exclude ( 0x8000, 0x1000, 0x0400, 0x200 ); /* before */
+ _test_exclude ( 0x8000, 0x1000, 0x9000, 0x200 ); /* after */
+ _test_exclude ( 0x8000, 0x1000, 0x7f00, 0x200 ); /* before overlap */
+ _test_exclude ( 0x8000, 0x1000, 0x8f00, 0x200 ); /* after overlap */
+ _test_exclude ( 0x8000, 0x1000, 0x8000, 0x200 ); /* align start */
+ _test_exclude ( 0x8000, 0x1000, 0x8e00, 0x200 ); /* align end */
+ _test_exclude ( 0x8000, 0x1000, 0x8100, 0x200 ); /* early overlap */
+ _test_exclude ( 0x8000, 0x1000, 0x8d00, 0x200 ); /* late overlap */
+ _test_exclude ( 0x8000, 0x1000, 0x7000, 0x3000 ); /* total overlap */
+ _test_exclude ( 0x8000, 0x1000, 0x8000, 0x1000 ); /* exact overlap */
+}
+#endif /* TEST_EXCLUDE_ALGORITHM */
+
+#else /* PXE_EXPORT */
+
+/* Define symbols used by the linker scripts, to prevent link errors */
+__asm__ ( ".globl _pxe_stack_t_size" );
+__asm__ ( ".equ _pxe_stack_t_size, 0" );
+
+#endif /* PXE_EXPORT */
diff --git a/src/arch/i386/core/pxe_loader.c b/src/arch/i386/core/pxe_loader.c
new file mode 100644
index 00000000..1b611891
--- /dev/null
+++ b/src/arch/i386/core/pxe_loader.c
@@ -0,0 +1,94 @@
+/*
+ * PXE image loader for Etherboot.
+ *
+ * Note: There is no signature check for PXE images because there is
+ * no signature. Well done, Intel! Consequently, pxe_probe() must be
+ * called last of all the image_probe() routines, because it will
+ * *always* claim the image.
+ */
+
+#ifndef PXE_EXPORT
+#error PXE_IMAGE requires PXE_EXPORT
+#endif
+
+#include "etherboot.h"
+#include "pxe_callbacks.h"
+#include "pxe_export.h"
+#include "pxe.h"
+
+unsigned long pxe_load_offset;
+
+static sector_t pxe_download ( unsigned char *data,
+ unsigned int len, int eof );
+
+static inline os_download_t pxe_probe ( unsigned char *data __unused,
+ unsigned int len __unused ) {
+ printf("(PXE)");
+ pxe_load_offset = 0;
+ return pxe_download;
+}
+
+static sector_t pxe_download ( unsigned char *data,
+ unsigned int len, int eof ) {
+ unsigned long block_address = PXE_LOAD_ADDRESS + pxe_load_offset;
+ PXENV_STATUS_t nbp_exit;
+
+ /* Check segment will fit. We can't do this in probe()
+ * because there's nothing in the non-existent header to tell
+ * us how long the image is.
+ */
+ if ( ! prep_segment ( block_address, block_address + len,
+ block_address + len,
+ pxe_load_offset, pxe_load_offset + len ) ) {
+ longjmp ( restart_etherboot, -2 );
+ }
+
+ /* Load block into memory, continue loading until eof */
+ memcpy ( phys_to_virt ( block_address ), data, len );
+ pxe_load_offset += len;
+ if ( ! eof ) {
+ return 0;
+ }
+
+ /* Start up PXE NBP */
+ done ( 0 );
+
+ /* Install and activate a PXE stack */
+ pxe_stack = install_pxe_stack ( NULL );
+ if ( ensure_pxe_state ( READY ) ) {
+ /* Invoke the NBP */
+ nbp_exit = xstartpxe();
+ } else {
+ /* Fake success so we tear down the stack */
+ nbp_exit = PXENV_STATUS_SUCCESS;
+ }
+
+ /* NBP has three exit codes:
+ * PXENV_STATUS_KEEP_UNDI : keep UNDI and boot next device
+ * PXENV_STATUS_KEEP_ALL : keep all and boot next device
+ * anything else : remove all and boot next device
+ *
+ * Strictly, we're meant to hand back to the BIOS, but this
+ * would prevent the useful combination of "PXE NBP fails, so
+ * let Etherboot try to boot its next device". We therefore
+ * take liberties.
+ */
+ if ( nbp_exit != PXENV_STATUS_KEEP_UNDI &&
+ nbp_exit != PXENV_STATUS_KEEP_ALL ) {
+ /* Tear down PXE stack */
+ remove_pxe_stack();
+ }
+
+ /* Boot next device. Under strict PXE compliance, exit back
+ * to the BIOS, otherwise let Etherboot move to the next
+ * device.
+ */
+#ifdef PXE_STRICT
+ longjmp ( restart_etherboot, 255 );
+#else
+ longjmp ( restart_etherboot, 4 );
+#endif
+
+ /* Never reached; avoid compiler warning */
+ return ( 0 );
+}
diff --git a/src/arch/i386/core/realmode.c b/src/arch/i386/core/realmode.c
new file mode 100644
index 00000000..ef4ede86
--- /dev/null
+++ b/src/arch/i386/core/realmode.c
@@ -0,0 +1,148 @@
+/* Real-mode interface: C portions.
+ *
+ * Initial version by Michael Brown <mbrown@fensystems.co.uk>, January 2004.
+ */
+
+#include "etherboot.h"
+#include "realmode.h"
+#include "segoff.h"
+
+#define RM_STACK_SIZE ( 0x1000 )
+
+/* gcc won't let us use extended asm outside a function (compiler
+ * bug), ao we have to put these asm statements inside a dummy
+ * function.
+ */
+static void work_around_gcc_bug ( void ) __attribute__ ((used));
+static void work_around_gcc_bug ( void ) {
+ /* Export _real_mode_stack_size as absolute linker symbol */
+ __asm__ ( ".globl _real_mode_stack_size" );
+ __asm__ ( ".equ _real_mode_stack_size, %c0" : : "i" (RM_STACK_SIZE) );
+}
+
+/* While Etherboot remains in base memory the real-mode stack is
+ * placed in the Etherboot main stack. The first allocation or
+ * deallocation of base memory will cause a 'proper' real-mode stack
+ * to be allocated. This will happen before Etherboot is relocated to
+ * high memory.
+ */
+uint32_t real_mode_stack = 0;
+size_t real_mode_stack_size = RM_STACK_SIZE;
+int lock_real_mode_stack = 0; /* Set to make stack immobile */
+
+/* Make a call to a real-mode code block.
+ */
+
+/* These is the structure that exists on the stack as the paramters
+ * passed in to _real_call. We pass a pointer to this struct to
+ * prepare_real_call(), to save stack space.
+ */
+typedef struct {
+ void *fragment;
+ int fragment_len;
+ void *in_stack;
+ int in_stack_len;
+ void *out_stack;
+ int out_stack_len;
+} real_call_params_t;
+
+uint32_t prepare_real_call ( real_call_params_t *p,
+ int local_stack_len, char *local_stack ) {
+ char *stack_base;
+ char *stack_end;
+ char *stack;
+ char *s;
+ prot_to_real_params_t *p2r_params;
+ real_to_prot_params_t *r2p_params;
+
+ /* Work out where we're putting the stack */
+ if ( virt_to_phys(local_stack) < 0xa0000 ) {
+ /* Current stack is in base memory. We can therefore
+ * use it directly, with a constraint on the size that
+ * we don't know; assume that we can use up to
+ * real_mode_stack_size. (Not a valid assumption, but
+ * it will do).
+ */
+ stack_end = local_stack + local_stack_len;
+ stack_base = stack_end - real_mode_stack_size;
+ } else {
+ if (!real_mode_stack) {
+ allot_real_mode_stack();
+ }
+ /* Use the allocated real-mode stack in base memory.
+ * This has fixed start and end points.
+ */
+ stack_base = phys_to_virt(real_mode_stack);
+ stack_end = stack_base + real_mode_stack_size;
+ }
+ s = stack = stack_end - local_stack_len;
+
+ /* Compile input stack and trampoline code to stack */
+ if ( p->in_stack_len ) {
+ memcpy ( s, p->in_stack, p->in_stack_len );
+ s += p->in_stack_len;
+ }
+ memcpy ( s, _prot_to_real_prefix, prot_to_real_prefix_size );
+ s += prot_to_real_prefix_size;
+ p2r_params = (prot_to_real_params_t*) ( s - sizeof(*p2r_params) );
+ memcpy ( s, p->fragment, p->fragment_len );
+ s += p->fragment_len;
+ memcpy ( s, _real_to_prot_suffix, real_to_prot_suffix_size );
+ s += real_to_prot_suffix_size;
+ r2p_params = (real_to_prot_params_t*) ( s - sizeof(*r2p_params) );
+
+ /* Set parameters within compiled stack */
+ p2r_params->ss = p2r_params->cs = SEGMENT ( stack_base );
+ p2r_params->esp = virt_to_phys ( stack );
+ p2r_params->r2p_params = virt_to_phys ( r2p_params );
+ r2p_params->out_stack = ( p->out_stack == NULL ) ?
+ 0 : virt_to_phys ( p->out_stack );
+ r2p_params->out_stack_len = p->out_stack_len;
+
+ return virt_to_phys ( stack + p->in_stack_len );
+}
+
+
+/* Parameters are not genuinely unused; they are passed to
+ * prepare_real_call() as part of a real_call_params_t struct.
+ */
+uint16_t _real_call ( void *fragment, int fragment_len,
+ void *in_stack __unused, int in_stack_len,
+ void *out_stack __unused, int out_stack_len __unused ) {
+ uint16_t retval;
+
+ /* This code is basically equivalent to
+ *
+ * uint32_t trampoline;
+ * char local_stack[ in_stack_len + prot_to_real_prefix_size +
+ * fragment_len + real_to_prot_suffix_size ];
+ * trampoline = prepare_real_call ( &fragment, local_stack );
+ * __asm__ ( "call _virt_to_phys\n\t"
+ * "call %%eax\n\t"
+ * "call _phys_to_virt\n\t"
+ * : "=a" (retval) : "0" (trampoline) );
+ *
+ * but implemented in assembly to avoid problems with not
+ * being certain exactly how gcc handles %esp.
+ */
+
+ __asm__ ( "pushl %%ebp\n\t"
+ "movl %%esp, %%ebp\n\t" /* %esp preserved via %ebp */
+ "subl %%ecx, %%esp\n\t" /* space for inline RM stack */
+ "pushl %%esp\n\t" /* set up RM stack */
+ "pushl %%ecx\n\t"
+ "pushl %%eax\n\t"
+ "call prepare_real_call\n\t" /* %eax = trampoline addr */
+ "addl $12, %%esp\n\t"
+ "call _virt_to_phys\n\t" /* switch to phys addr */
+ "call *%%eax\n\t" /* call to trampoline */
+ "call _phys_to_virt\n\t" /* switch to virt addr */
+ "movl %%ebp, %%esp\n\t" /* restore %esp & %ebp */
+ "popl %%ebp\n\t"
+ : "=a" ( retval )
+ : "0" ( &fragment )
+ , "c" ( ( ( in_stack_len + prot_to_real_prefix_size +
+ fragment_len + real_to_prot_suffix_size )
+ + 0x3 ) & ~0x3 ) );
+ return retval;
+}
diff --git a/src/arch/i386/core/realmode_asm.S b/src/arch/i386/core/realmode_asm.S
new file mode 100644
index 00000000..28a5bfed
--- /dev/null
+++ b/src/arch/i386/core/realmode_asm.S
@@ -0,0 +1,695 @@
+/* Real-mode interface: assembly-language portions.
+ *
+ * Initial version by Michael Brown <mbrown@fensystems.co.uk>, January 2004.
+ */
+
+#include "realmode.h"
+#include "callbacks.h"
+
+#if 1 /* CODE16 */
+
+#define BOCHSBP xchgw %bx,%bx
+
+#define NUM_PUSHA_REGS (8)
+#define NUM_SEG_REGS (6)
+
+ .text
+ .arch i386
+ .section ".text16.nocompress", "ax", @progbits
+ .code16
+
+ .equ CR0_PE,1
+
+#ifdef GAS291
+#define DATA32 data32;
+#define ADDR32 addr32;
+#define LJMPI(x) ljmp x
+#else
+#define DATA32 data32
+#define ADDR32 addr32
+/* newer GAS295 require #define LJMPI(x) ljmp *x */
+#define LJMPI(x) ljmp x
+#endif
+
+/****************************************************************************
+ * REAL-MODE CALLBACK INTERFACE
+ *
+ * This must be copied down to base memory in order for external
+ * programs to be able to make calls in to Etherboot. Store the
+ * current physical address of Etherboot (i.e. virt_to_phys(_text)) in
+ * (uint32_t)rm_etherboot_location, then copy
+ * (uint16_t)rm_callback_interface_size bytes starting at
+ * &((void)rm_callback_interface).
+ *
+ * There are two defined entry points:
+ * Offset RM_IN_CALL = 0 Near call entry point
+ * Offset RM_IN_CALL_FAR = 2 Far call entry point
+ *
+ * Note that the routines _prot_to_real and _real_to_prot double as
+ * trampoline fragments for external calls (calls from Etherboot to
+ * real-mode code). _prot_to_real does not automatically re-enable
+ * interrupts; this is to allow for the potential of using Etherboot
+ * code as an ISR. _real_to_prot does automatically disable
+ * interrupts, since we don't have a protected-mode IDT.
+ ****************************************************************************
+ */
+
+ .globl rm_callback_interface
+ .code16
+rm_callback_interface:
+ .globl _rm_in_call
+_rm_in_call:
+ jmp _real_in_call
+ .globl _rm_in_call_far
+_rm_in_call_far:
+ jmp _real_in_call_far
+
+/****************************************************************************
+ * _real_in_call
+ *
+ * Parameters:
+ * 16-bit real-mode near/far return address (implicit from [l]call
+ * to routine) Other parameters as for _in_call_far().
+ *
+ * This routine will convert the 16-bit real-mode far return address
+ * to a 32-bit real-mode far return address, switch to protected mode
+ * using _real_to_prot and call in to _in_call_far.
+ ****************************************************************************
+ */
+
+#define RIC_PRESERVE ( 8 )
+#define RIC_OFFSET_CALLADDR ( RIC_PRESERVE )
+#define RIC_OFFSET_CALLADDR_E ( RIC_OFFSET_CALLADDR + 4 )
+#define RIC_OFFSET_CONTADDR ( RIC_OFFSET_CALLADDR_E )
+#define RIC_OFFSET_CONTADDR_E ( RIC_OFFSET_CONTADDR + 4 )
+#define RIC_OFFSET_OPCODE ( RIC_OFFSET_CONTADDR_E )
+#define RIC_OFFSET_OPCODE_E ( RIC_OFFSET_OPCODE + 4 )
+#define RIC_OFFSET_SEG_REGS ( RIC_OFFSET_OPCODE_E )
+#define RIC_OFFSET_SEG_REGS_E ( RIC_OFFSET_SEG_REGS + ( NUM_SEG_REGS * 2 ) )
+#define RIC_OFFSET_PAD ( RIC_OFFSET_SEG_REGS_E )
+#define RIC_OFFSET_PAD_E ( RIC_OFFSET_PAD + 2 )
+#define RIC_OFFSET_FLAGS ( RIC_OFFSET_PAD_E )
+#define RIC_OFFSET_FLAGS_E ( RIC_OFFSET_FLAGS + 2 )
+#define RIC_OFFSET_RETADDR ( RIC_OFFSET_FLAGS_E )
+#define RIC_OFFSET_RETADDR_E ( RIC_OFFSET_RETADDR + 4 )
+#define RIC_OFFSET_ORIG_OPCODE ( RIC_OFFSET_RETADDR_E )
+#define RIC_INSERT_LENGTH ( RIC_OFFSET_OPCODE_E - RIC_OFFSET_CALLADDR )
+
+ .code16
+_real_in_call:
+ /* Expand near return address to far return address
+ */
+ pushw %ax /* Extend stack, store %ax */
+ pushfw
+ pushw %bp
+ movw %sp, %bp
+ movw %cs, %ax
+ xchgw %ax, 6(%bp)
+ xchgw %ax, 4(%bp) /* also restores %ax */
+ popw %bp
+ popfw
+ /* Fall through to _real_in_call_far */
+
+_real_in_call_far:
+ /* Store flags and pad */
+ pushfw
+ pushw %ax
+
+ /* Store segment registers. Order matches that of seg_regs_t */
+ pushw %gs
+ pushw %fs
+ pushw %es
+ pushw %ds
+ pushw %ss
+ pushw %cs
+
+ /* Switch to protected mode */
+ call _real_to_prot
+ .code32
+
+ /* Allow space for expanded stack */
+ subl $RIC_INSERT_LENGTH, %esp
+
+ /* Store temporary registers */
+ pushl %ebp
+ pushl %eax
+
+ /* Copy opcode, set EB_CALL_FROM_REAL_MODE and EP_SKIP_OPCODE.
+ * Copy it because _in_call() and i386_in_call() expect it at
+ * a fixed position, not as part of the va_list.
+ */
+ movl RIC_OFFSET_ORIG_OPCODE(%esp), %eax
+ orl $(EB_CALL_FROM_REAL_MODE|EB_SKIP_OPCODE), %eax
+ movl %eax, RIC_OFFSET_OPCODE(%esp)
+
+ /* Set up call and return addresses */
+ call 1f
+1: popl %ebp
+ subl $1b, %ebp /* %ebp = offset */
+ movl rm_etherboot_location(%ebp), %eax /* Etherboot phys addr */
+ subl $_text, %eax
+ addl $_in_call, %eax /* _in_call phys addr */
+ movl %eax, RIC_OFFSET_CALLADDR(%esp)
+ leal 2f(%ebp), %eax /* continuation address */
+ movl %eax, RIC_OFFSET_CONTADDR(%esp)
+
+ /* Restore temporary registers */
+ popl %eax
+ popl %ebp
+
+ /* Call to _in_call */
+ ret
+ /* opcode will be popped automatically thanks to EB_SKIP_OPCODE */
+
+2: /* Continuation point */
+ call _prot_to_real /* Return to real mode */
+ /* Note: the first two words of our segment register store
+ * happens to be exactly what we need to pass as parameters to
+ * _prot_to_real.
+ */
+ .code16
+ popw %ds /* Restore segment registers */
+ popw %ds /* (skip cs&ss since these */
+ popw %ds /* have already been set by */
+ popw %es /* _prot_to_real */
+ popw %fs
+ popw %gs
+ addw $2, %sp /* skip pad */
+
+ /* Check for EB_SKIP_OPCODE */
+ pushw %bp
+ movw %sp, %bp
+ testl $EB_SKIP_OPCODE, 6(%bp)
+ popw %bp
+ jnz 1f
+ /* Normal return */
+ popfw /* Restore interrupt status */
+ lret /* Back to caller */
+1: /* Return and skip opcode */
+ popfw
+ lret $4
+
+/****************************************************************************
+ * rm_etherboot_location: the current physical location of Etherboot.
+ * Needed so that real-mode callback routines can locate Etherboot.
+ ****************************************************************************
+ */
+ .globl rm_etherboot_location
+rm_etherboot_location: .long 0
+
+/****************************************************************************
+ * _prot_to_real_prefix
+ *
+ * Trampoline fragment. Switch from 32-bit protected mode with flat
+ * physical addresses to 16-bit real mode. Store registers in the
+ * trampoline for restoration by _real_to_prot_suffix. Switch to
+ * stack in base memory.
+ ****************************************************************************
+ */
+
+ .globl _prot_to_real_prefix
+ .code32
+_prot_to_real_prefix:
+ /* Registers to preserve */
+ pushl %ebx
+ pushl %esi
+ pushl %edi
+ pushl %ebp
+
+ /* Calculate offset */
+ call 1f
+1: popl %ebp
+ subl $1b, %ebp /* %ebp = offset for labels in p2r*/
+
+ /* Preserve registers and return address in r2p_params */
+ movl p2r_r2p_params(%ebp), %ebx
+ subl $r2p_params, %ebx /* %ebx = offset for labels in r2p */
+ popl r2p_ebp(%ebx)
+ popl r2p_edi(%ebx)
+ popl r2p_esi(%ebx)
+ popl r2p_ebx(%ebx)
+ popl r2p_ret_addr(%ebx)
+ movl %esp, r2p_esp(%ebx)
+
+ /* Switch stacks */
+ movl p2r_esp(%ebp), %esp
+
+ /* Switch to real mode */
+ pushl p2r_segments(%ebp)
+ call _prot_to_real
+ .code16
+ addw $4, %sp
+
+ /* Fall through to next trampoline fragment */
+ jmp _prot_to_real_prefix_end
+
+/****************************************************************************
+ * _prot_to_real
+ *
+ * Switch from 32-bit protected mode with flat physical addresses to
+ * 16-bit real mode. Stack and code must be in base memory when
+ * called. %cs, %ss, %eip, %esp are changed to real-mode values,
+ * other segment registers are destroyed, all other registers are
+ * preserved. Interrupts are *not* enabled.
+ *
+ * Parameters:
+ * %cs Real-mode code segment (word)
+ * %ss Real-mode stack segment (word)
+ ****************************************************************************
+ */
+
+#define P2R_PRESERVE ( 12 )
+#define P2R_OFFSET_RETADDR ( P2R_PRESERVE )
+#define P2R_OFFSET_RETADDR_E ( P2R_OFFSET_RETADDR + 4 )
+#define P2R_OFFSET_CS ( P2R_OFFSET_RETADDR_E )
+#define P2R_OFFSET_CS_E ( P2R_OFFSET_CS + 2 )
+#define P2R_OFFSET_SS ( P2R_OFFSET_CS_E )
+#define P2R_OFFSET_SS_E ( P2R_OFFSET_SS + 2 )
+
+ .globl _prot_to_real
+ .code32
+_prot_to_real:
+ /* Preserve registers */
+ pushl %ebp
+ pushl %ebx
+ pushl %eax
+
+ /* Calculate offset */
+ call 1f
+1: popl %ebp
+ subl $1b, %ebp /* %ebp = offset for labels in p2r*/
+
+ /* Set up GDT with real-mode limits and appropriate bases for
+ * real-mode %cs and %ss. Set up protected-mode continuation
+ * point on stack.
+ */
+ /* Fixup GDT */
+ leal p2r_gdt(%ebp), %eax
+ movl %eax, p2r_gdt_addr(%ebp)
+
+ /* Calculate CS base address: set GDT code segment, adjust
+ * return address, set up continuation address on stack.
+ */
+ movzwl P2R_OFFSET_CS(%esp), %eax
+ shll $4, %eax
+ /* Change return address to real-mode far address */
+ subl %eax, P2R_OFFSET_RETADDR(%esp)
+ movl %eax, %ebx
+ shrl $4, %ebx
+ movw %bx, (P2R_OFFSET_RETADDR+2)(%esp)
+ /* First real mode address */
+ movl %eax, %ebx
+ shrl $4, %ebx
+ pushw %bx
+ leal 3f(%ebp), %ebx
+ subl %eax, %ebx
+ pushw %bx
+ /* Continuation address */
+ pushl $(p2r_rmcs - p2r_gdt)
+ leal 2f(%ebp), %ebx
+ subl %eax, %ebx
+ pushl %ebx
+ /* Code segment in GDT */
+ movw %ax, (p2r_rmcs+2)(%ebp)
+ shrl $16, %eax /* Remainder of cs base addr */
+ movb %al, (p2r_rmcs+4)(%ebp)
+ movb %ah, (p2r_rmcs+7)(%ebp)
+
+ /* Calculate SS base address: set GDT data segment, retain to
+ * use for adjusting %esp.
+ */
+ movzwl (12+P2R_OFFSET_SS)(%esp), %eax /* Set ss base address */
+ shll $4, %eax
+ movw %ax, (p2r_rmds+2)(%ebp)
+ movl %eax, %ebx
+ shrl $16, %ebx
+ movb %bl, (p2r_rmds+4)(%ebp)
+ movb %bh, (p2r_rmds+7)(%ebp)
+
+ /* Load GDT */
+ lgdt p2r_gdt(%ebp)
+ /* Reload all segment registers and adjust %esp */
+ movw $(p2r_rmds - p2r_gdt), %bx /* Pmode DS */
+ movw %bx, %ss
+ subl %eax, %esp /* %esp now less than 0x10000 */
+ movw %bx, %ds
+ movw %bx, %es
+ movw %bx, %fs
+ movw %bx, %gs
+ lret /* %cs:eip */
+2: /* Segment registers now have 16-bit limits. */
+ .code16
+
+ /* Switch to real mode */
+ movl %cr0, %ebx
+ andb $0!CR0_PE, %bl
+ movl %ebx, %cr0
+
+ /* Make intersegment jmp to flush the processor pipeline
+ * and reload %cs:%eip (to clear upper 16 bits of %eip).
+ */
+ lret
+3:
+
+ /* Load real-mode segment value to %ss. %sp already OK */
+ shrl $4, %eax
+ movw %ax, %ss
+
+ /* Restore registers */
+ popl %eax
+ popl %ebx
+ popl %ebp
+
+ /* Return to caller in real-mode */
+ lret
+
+#ifdef FLATTEN_REAL_MODE
+#define RM_LIMIT_16_19__AVL__SIZE__GRANULARITY 0x8f
+#else
+#define RM_LIMIT_16_19__AVL__SIZE__GRANULARITY 0x00
+#endif
+
+p2r_gdt:
+p2r_gdtarg:
+p2r_gdt_limit: .word p2r_gdt_end - p2r_gdt - 1
+p2r_gdt_addr: .long 0
+p2r_gdt_padding: .word 0
+p2r_rmcs:
+ /* 16 bit real mode code segment */
+ .word 0xffff,(0&0xffff)
+ .byte (0>>16),0x9b,RM_LIMIT_16_19__AVL__SIZE__GRANULARITY,(0>>24)
+p2r_rmds:
+ /* 16 bit real mode data segment */
+ .word 0xffff,(0&0xffff)
+ .byte (0>>16),0x93,RM_LIMIT_16_19__AVL__SIZE__GRANULARITY,(0>>24)
+p2r_gdt_end:
+
+ /* This is the end of the trampoline prefix code. When used
+ * as a prefix, fall through to the following code in the
+ * trampoline.
+ */
+p2r_params: /* Structure must match prot_to_real_params_t in realmode.h */
+p2r_esp: .long 0
+p2r_segments:
+p2r_cs: .word 0
+p2r_ss: .word 0
+p2r_r2p_params: .long 0
+ .globl _prot_to_real_prefix_end
+_prot_to_real_prefix_end:
+
+ .globl _prot_to_real_prefix_size
+ .equ _prot_to_real_prefix_size, _prot_to_real_prefix_end - _prot_to_real_prefix
+ .globl prot_to_real_prefix_size
+prot_to_real_prefix_size:
+ .word _prot_to_real_prefix_size
+
+/****************************************************************************
+ * _real_to_prot_suffix
+ *
+ * Trampoline fragment. Switch from 16-bit real-mode to 32-bit
+ * protected mode with flat physical addresses. Copy returned stack
+ * parameters to output_stack. Restore registers preserved by
+ * _prot_to_real_prefix. Restore stack to previous location.
+ ****************************************************************************
+ */
+
+ .globl _real_to_prot_suffix
+ .code16
+_real_to_prot_suffix:
+
+ /* Switch to protected mode */
+ call _real_to_prot
+ .code32
+
+ /* Calculate offset */
+ call 1f
+1: popl %ebp
+ subl $1b, %ebp /* %ebp = offset for labels in r2p */
+
+ /* Copy stack to out_stack */
+ movl r2p_out_stack(%ebp), %edi
+ movl r2p_out_stack_len(%ebp), %ecx
+ movl %esp, %esi
+ cld
+ rep movsb
+
+ /* Switch back to original stack */
+ movl r2p_esp(%ebp), %esp
+
+ /* Restore registers and return */
+ pushl r2p_ret_addr(%ebp) /* Set up return address on stack */
+ movl r2p_ebx(%ebp), %ebx
+ movl r2p_esi(%ebp), %esi
+ movl r2p_edi(%ebp), %edi
+ movl r2p_ebp(%ebp), %ebp
+ ret
+
+/****************************************************************************
+ * _real_to_prot
+ *
+ * Switch from 16-bit real-mode to 32-bit protected mode with flat
+ * physical addresses. All segment registers are destroyed, %eip and
+ * %esp are changed to flat physical values, all other registers are
+ * preserved. Interrupts are disabled.
+ *
+ * Parameters: none
+ ****************************************************************************
+ */
+
+#define R2P_PRESERVE ( 12 )
+#define R2P_OFFSET_RETADDR ( R2P_PRESERVE )
+#define R2P_OFFSET_ORIG_RETADDR ( R2P_OFFSET_RETADDR + 2 )
+
+ .globl _real_to_prot
+ .code16
+_real_to_prot:
+ /* Disable interrupts */
+ cli
+ /* zero extend the return address */
+ pushw $0
+
+ /* Preserve registers */
+ pushl %ebp
+ pushl %ebx
+ pushl %eax
+
+ /* Convert 16-bit real-mode near return address to
+ * 32-bit pmode physical near return address
+ */
+ movw %sp, %bp
+ xorl %ebx, %ebx
+ push %cs
+ popw %bx
+ movw %bx, %ds
+ shll $4, %ebx
+ movzwl %ss:R2P_OFFSET_ORIG_RETADDR(%bp), %eax
+ addl %ebx, %eax
+ movl %eax, %ss:(R2P_OFFSET_RETADDR)(%bp)
+
+ /* Store the code segment physical base address in %ebp */
+ movl %ebx, %ebp
+
+ /* Find the offset within the code segment that I am running at */
+ xorl %ebx, %ebx
+ call 1f
+1: popw %bx
+
+ /* Set up GDT */
+ leal (r2p_gdt-1b)(%bx), %eax /* %ds:ebx = %ds:bx = &(r2p_gdt) */
+ addl %ebp, %eax /* %eax = &r2p_gdt (physical) */
+ movl %eax, %ds:(r2p_gdt-1b+2)(%bx) /* Set phys. addr. in r2p_gdt */
+
+ /* Compute the first protected mode physical address */
+ leal (2f-1b)(%bx), %eax
+ addl %ebp, %eax
+ movl %eax, %ds:(r2p_paddr-1b)(%bx)
+
+ /* Calculate new %esp */
+ xorl %eax, %eax
+ push %ss
+ popw %ax
+ shll $4, %eax
+ movzwl %sp, %ebp
+ addl %eax, %ebp /* %ebp = new %esp */
+
+ /* Load GDT */
+ DATA32 lgdt %ds:(r2p_gdt-1b)(%bx) /* Load GDT */
+
+ /* Switch to protected mode */
+ movl %cr0, %eax
+ orb $CR0_PE, %al
+ movl %eax, %cr0
+
+ /* flush prefetch queue, and reload %cs:%eip */
+ DATA32 ljmp %ds:(r2p_paddr-1b)(%bx)
+ .code32
+2:
+
+ /* Load segment registers, adjust %esp */
+ movw $(r2p_pmds-r2p_gdt), %ax
+ movw %ax, %ss
+ movl %ebp, %esp
+ movw %ax, %ds
+ movw %ax, %es
+ movw %ax, %fs
+ movw %ax, %gs
+
+ /* Restore registers */
+ popl %eax
+ popl %ebx
+ popl %ebp
+
+ /* return to caller */
+ ret
+
+r2p_gdt:
+ .word r2p_gdt_end - r2p_gdt - 1 /* limit */
+ .long 0 /* addr */
+ .word 0
+r2p_pmcs:
+ /* 32 bit protected mode code segment, physical addresses */
+ .word 0xffff, 0
+ .byte 0, 0x9f, 0xcf, 0
+r2p_pmds:
+ /* 32 bit protected mode data segment, physical addresses */
+ .word 0xffff,0
+ .byte 0,0x93,0xcf,0
+r2p_gdt_end:
+
+r2p_paddr:
+ .long 2b
+ .word r2p_pmcs - r2p_gdt, 0
+
+
+ /* This is the end of the trampoline suffix code.
+ */
+r2p_params: /* Structure must match real_to_prot_params_t in realmode.h */
+r2p_ret_addr: .long 0
+r2p_esp: .long 0
+r2p_ebx: .long 0
+r2p_esi: .long 0
+r2p_edi: .long 0
+r2p_ebp: .long 0
+r2p_out_stack: .long 0
+r2p_out_stack_len: .long 0
+ .globl _real_to_prot_suffix_end
+_real_to_prot_suffix_end:
+
+ .globl _real_to_prot_suffix_size
+ .equ _real_to_prot_suffix_size, _real_to_prot_suffix_end - _real_to_prot_suffix
+ .globl real_to_prot_suffix_size
+real_to_prot_suffix_size:
+ .word _real_to_prot_suffix_size
+
+rm_callback_interface_end:
+
+ .globl _rm_callback_interface_size
+ .equ _rm_callback_interface_size, rm_callback_interface_end - rm_callback_interface
+ .globl rm_callback_interface_size
+rm_callback_interface_size:
+ .word _rm_callback_interface_size
+
+/****************************************************************************
+ * END OF REAL-MODE CALLBACK INTERFACE
+ ****************************************************************************
+ */
+
+
+#ifdef PXE_EXPORT
+/****************************************************************************
+ * PXE CALLBACK INTERFACE
+ *
+ * Prepend this to rm_callback_interface to create a real-mode PXE
+ * callback interface.
+ ****************************************************************************
+ */
+ .section ".text16", "ax", @progbits
+ .globl pxe_callback_interface
+ .code16
+pxe_callback_interface:
+
+/* Macro to calculate offset of labels within code segment in
+ * installed copy of code.
+ */
+#define INSTALLED(x) ( (x) - pxe_callback_interface )
+
+/****************************************************************************
+ * PXE entry points (!PXE and PXENV+ APIs)
+ ****************************************************************************
+ */
+ /* in_call mechanism for !PXE API calls */
+ .globl _pxe_in_call_far
+_pxe_in_call_far:
+ /* Prepend "PXE API call" and "API version 0x201" to stack */
+ pushl $0x201
+ jmp 1f
+ /* in_call mechanism for PXENV+ API calls */
+ .globl _pxenv_in_call_far
+_pxenv_in_call_far:
+ /* Prepend "PXE API call" and "API version 0x200" to stack */
+ pushl $0x200
+1: pushl $EB_OPCODE_PXE
+ /* Perform real-mode in_call */
+ call pxe_rm_in_call
+ /* Return */
+ addw $8, %sp
+ lret
+
+/****************************************************************************
+ * PXE installation check (INT 1A) code
+ ****************************************************************************
+ */
+ .globl _pxe_intercept_int1a
+_pxe_intercept_int1a:
+ pushfw
+ cmpw $0x5650, %ax
+ jne 2f
+1: /* INT 1A,5650 - Intercept */
+ popfw
+ /* Set up return values according to PXE spec: */
+ movw $0x564e, %ax /* AX := 564Eh (VN) */
+ pushw %cs:INSTALLED(_pxe_pxenv_segment)
+ popw %es /* ES:BX := &(PXENV+ structure) */
+ movw %cs:INSTALLED(_pxe_pxenv_offset), %bx
+ clc /* CF is cleared */
+ lret $2 /* 'iret' without reloading flags */
+2: /* INT 1A,other - Do not intercept */
+ popfw
+ ljmp %cs:*INSTALLED(_pxe_intercepted_int1a)
+
+ .globl _pxe_intercepted_int1a
+_pxe_intercepted_int1a: .word 0,0
+ .globl _pxe_pxenv_location
+_pxe_pxenv_location:
+_pxe_pxenv_offset: .word 0
+_pxe_pxenv_segment: .word 0
+
+pxe_rm_in_call:
+pxe_attach_rm:
+ /* rm_callback_interface must be appended here */
+
+pxe_callback_interface_end:
+
+ .globl _pxe_callback_interface_size
+ .equ _pxe_callback_interface_size, pxe_callback_interface_end - pxe_callback_interface
+ .globl pxe_callback_interface_size
+pxe_callback_interface_size:
+ .word _pxe_callback_interface_size
+
+#else /* PXE_EXPORT */
+
+/* Define symbols used by the linker scripts, to prevent link errors */
+ .globl _pxe_callback_interface_size
+ .equ _pxe_callback_interface_size, 0
+
+#endif /* PXE_EXPORT */
+
+#else /* CODE16 */
+
+/* Define symbols used by the linker scripts, to prevent link errors */
+ .globl _rm_callback_interface_size
+ .equ _rm_callback_interface_size, 0
+ .globl _pxe_callback_interface_size
+ .equ _pxe_callback_interface_size, 0
+
+#endif /* CODE16 */
diff --git a/src/arch/i386/core/start16.S b/src/arch/i386/core/start16.S
new file mode 100644
index 00000000..72fcfbfb
--- /dev/null
+++ b/src/arch/i386/core/start16.S
@@ -0,0 +1,285 @@
+/*****************************************************************************
+ *
+ * THIS FILE IS NOW OBSOLETE.
+ *
+ * The functions of this file are now placed in init.S.
+ *
+ *****************************************************************************
+ */
+
+#ifndef PCBIOS
+#error "16bit code is only supported with the PCBIOS"
+#endif
+
+#define CODE_SEG 0x08
+#define DATA_SEG 0x10
+
+#define EXEC_IN_SITU_MAGIC 0x45524548 /* 'HERE' */
+
+ .equ CR0_PE, 1
+
+#ifdef GAS291
+#define DATA32 data32;
+#define ADDR32 addr32;
+#define LJMPI(x) ljmp x
+#else
+#define DATA32 data32
+#define ADDR32 addr32
+/* newer GAS295 require #define LJMPI(x) ljmp *x */
+#define LJMPI(x) ljmp x
+#endif
+
+/*****************************************************************************
+ *
+ * start16 : move payload to desired area of memory, set up for exit
+ * back to prefix, set up for 32-bit code.
+ *
+ * Enter (from prefix) with es:di = 0x4552:0x4548 if you want to
+ * prevent start16 from moving the payload. There are three
+ * motivations for moving the payload:
+ *
+ * 1. It may be in ROM, in which case we need to move it to RAM.
+ * 2. Whatever loaded us probably didn't know about our memory usage
+ * beyond the end of the image file. We should claim this memory
+ * before using it.
+ *
+ * Unless the prefix instructs us otherwise we will move the payload to:
+ *
+ * An area of memory claimed from the BIOS via 40:13.
+ *
+ * We use the main Etherboot stack (within the image target) as our
+ * stack; we don't rely on the prefix passing us a stack usable for
+ * anything other than the prefix's return address. The (first 512
+ * bytes of the) prefix code segment is copied to a safe archive
+ * location.
+ *
+ * When we return to the prefix (from start32), we copy this code back
+ * to a new area of memory, restore the prefix's ss:sp and ljmp back
+ * to the copy of the prefix. The prefix will see a return from
+ * start16 *but* may be executing at a new location. Code following
+ * the lcall to start16 must therefore be position-independent and
+ * must also be within [cs:0000,cs:01ff]. We make absolutely no
+ * guarantees about the stack contents when the prefix regains
+ * control.
+ *
+ * Trashes just about all registers, including all the segment
+ * registers.
+ *
+ *****************************************************************************
+ */
+
+ .text
+ .code16
+ .arch i386
+ .org 0
+ .globl _start16
+_start16:
+
+/*****************************************************************************
+ * Work out where we are going to place our image (image = optional
+ * decompressor + runtime). Exit this stage with %ax containing the
+ * runtime target address divided by 16 (i.e. a real-mode segment
+ * address).
+ *****************************************************************************
+ */
+ movw %es, %ax
+ cmpw $(EXEC_IN_SITU_MAGIC >> 16), %ax
+ jne exec_moved
+ cmpw $(EXEC_IN_SITU_MAGIC & 0xffff), %di
+ jne exec_moved
+exec_in_situ:
+ /* Prefix has warned us not to move the payload. Simply
+ * calculate where the image is going to end up, so we can
+ * work out where to put our stack.
+ */
+ movw %cs, %ax
+ addw $((payload-_start16)/16), %ax
+ jmp 99f
+exec_moved:
+ /* Claim an area of base memory from the BIOS and put the
+ * payload there. arch_relocated_to() will deal with freeing
+ * up this memory once we've relocated to high memory.
+ */
+ movw $0x40, %ax
+ movw %ax, %es
+ movw %es:(0x13), %ax /* FBMS in kb to %ax */
+ shlw $6, %ax /* ... in paragraphs */
+ subw $__image_size_pgh, %ax /* Subtract space for image */
+ shrw $6, %ax /* Round down to nearest kb */
+ movw %ax, %es:(0x13) /* ...and claim memory from BIOS */
+ shlw $6, %ax
+99:
+ /* At this point %ax contains the segment address for the
+ * start of the image (image = optional decompressor + runtime).
+ */
+
+/*****************************************************************************
+ * Set up stack in start32's stack space within the place we're going
+ * to copy Etherboot to, reserve space for GDT, copy return address
+ * from prefix stack, store prefix stack address
+ *****************************************************************************
+ */
+ popl %esi /* Return address */
+ mov %ss, %bx /* %es:di = prefix stack address */
+ mov %bx, %es /* (*after* pop of return address) */
+ movw %sp, %di
+ movw $__offset_stack_pgh, %bx /* Set up Etherboot stack */
+ addw %ax, %bx
+ movw %bx, %ss
+ movw $__stack_size, %sp
+ subw $(_gdt_end - _gdt), %sp /* Reserve space for GDT */
+ movw %sp, %bp /* Record GDT location */
+ /* Set up i386_rm_in_call_data_t structure on stack. This is
+ * the same structure as is set up by rm_in_call.
+ */
+ pushl $0 /* Dummy opcode */
+ pushl %esi /* Prefix return address */
+ pushfw /* Flags */
+ pushw %di /* Prefix %sp */
+ pushw %gs /* Segment registers */
+ pushw %fs
+ pushw %es
+ pushw %ds
+ pushw %es /* Prefix %ss */
+ pushw %cs
+ /* Stack is now 32-bit aligned */
+
+ /* %ax still contains image target segment address */
+
+/*****************************************************************************
+ * Calculate image target and prefix code physical addresses, store on stack
+ * for use in copy routine.
+ *****************************************************************************
+ */
+ movzwl %es:-2(%di), %ebx /* Prefix code segment */
+ shll $4, %ebx
+ pushl %ebx /* Prefix code physical address */
+ movzwl %ax, %edi /* Image target segment */
+ shll $4, %edi
+ pushl %edi /* Image target physical address */
+
+/*****************************************************************************
+ * Transition to 32-bit protected mode. Set up all segment
+ * descriptors to use flat physical addresses.
+ *****************************************************************************
+ */
+ /* Copy gdt to area reserved on stack
+ */
+ push %cs /* GDT source location -> %ds:%si */
+ pop %ds
+ mov $(_gdt - _start16), %si
+ push %ss /* GDT target location -> %es:%di */
+ pop %es
+ mov %bp, %di
+ mov $(_gdt_end - _gdt), %cx
+ cld
+ rep movsb /* Copy GDT to stack */
+ movl %ss, %eax
+ shll $4, %eax
+ movzwl %bp, %ebx
+ addl %eax, %ebx /* Physical addr of GDT copy -> %ebx */
+ movl %ebx, 2(%bp) /* Fill in addr field in GDT */
+
+ /* Compute the offset I am running at.
+ */
+ movl %cs, %ebx
+ shll $4, %ebx /* %ebx = offset for start16 symbols */
+
+ /* Switch to 32bit protected mode.
+ */
+ cli /* Disable interrupts */
+ lgdt (%bp) /* Load GDT from stack */
+ movl %cr0, %eax /* Set protected mode bit */
+ orb $CR0_PE, %al
+ movl %eax, %cr0
+ movl %ss, %eax /* Convert stack pointer to 32bit */
+ shll $4, %eax
+ movzwl %sp, %esp
+ addl %eax, %esp
+ movl $DATA_SEG, %eax /* Reload the segment registers */
+ movl %eax, %ds
+ movl %eax, %es
+ movl %eax, %ss
+ movl %eax, %fs
+ movl %eax, %gs
+ /* Flush prefetch queue, and reload %cs:%eip by effectively ljmping
+ * to code32_start. Do the jump via pushl and lret because the text
+ * may not be writable/
+ */
+ pushl $CODE_SEG
+ ADDR32 leal (code32_start-_start16)(%ebx), %eax
+ pushl %eax
+ DATA32 lret /* DATA32 needed, because we're still in 16-bit mode */
+
+_gdt:
+gdtarg:
+ .word _gdt_end - _gdt - 1 /* limit */
+ .long 0 /* addr */
+ .word 0
+_pmcs:
+ /* 32 bit protected mode code segment */
+ .word 0xffff, 0
+ .byte 0, 0x9f, 0xcf, 0
+_pmds:
+ /* 32 bit protected mode data segment */
+ .word 0xffff,0
+ .byte 0,0x93,0xcf,0
+_gdt_end:
+
+ .code32
+code32_start:
+
+/*****************************************************************************
+ * Copy payload to target location. Do the copy backwards, since if
+ * there's overlap with a forward copy then it means start16 is going
+ * to get trashed during the copy anyway...
+ *****************************************************************************
+ */
+ popl %edi /* Image target physical address */
+ pushl %edi
+ leal (payload-_start16)(%ebx), %esi /* Image source physical addr */
+ movl $__payload_size, %ecx /* Payload size (not image size) */
+ addl %ecx, %edi /* Start at last byte (length - 1) */
+ decl %edi
+ addl %ecx, %esi
+ decl %esi
+ std /* Backward copy of image */
+ rep movsb
+ cld
+ popl %edi /* Restore image target physical address */
+ leal __decompressor_uncompressed(%edi), %ebx
+ subl $_text, %ebx /* %ebx = offset for runtime symbols */
+
+/*****************************************************************************
+ * Copy prefix to storage area within Etherboot image.
+ *****************************************************************************
+ */
+ popl %esi /* Prefix source physical address */
+ pushl %edi
+ leal _prefix_copy(%ebx), %edi /* Prefix copy phys. addr. */
+ leal _eprefix_copy(%ebx), %ecx
+ subl %edi, %ecx /* Prefix copy size */
+ rep movsb /* Forward copy of prefix */
+ popl %edi /* Restore image target physical address */
+
+/*****************************************************************************
+ * Record base memory used by Etherboot image
+ *****************************************************************************
+ */
+ movl %edi, _prefix_image_basemem (%ebx)
+
+/*****************************************************************************
+ * Jump to start of the image (i.e. the decompressor, or start32 if
+ * non-compressed).
+ *****************************************************************************
+ */
+ pushl $0 /* Inform start32 that exit path is 16-bit */
+ jmpl *%edi /* Jump to image */
+
+ .balign 16
+ /* Etherboot needs to be 16byte aligned or data that
+ * is virtually aligned is no longer physically aligned
+ * which is just nasty in general. 16byte alignment
+ * should be sufficient though.
+ */
+payload:
diff --git a/src/arch/i386/core/start16.lds b/src/arch/i386/core/start16.lds
new file mode 100644
index 00000000..544fc78f
--- /dev/null
+++ b/src/arch/i386/core/start16.lds
@@ -0,0 +1,8 @@
+/* When linking with an uncompressed image, these symbols are not
+ * defined so we provide them here.
+ */
+
+__decompressor_uncompressed = 0 ;
+__decompressor__start = 0 ;
+
+INCLUDE arch/i386/core/start16z.lds
diff --git a/src/arch/i386/core/start16z.lds b/src/arch/i386/core/start16z.lds
new file mode 100644
index 00000000..711bcf7b
--- /dev/null
+++ b/src/arch/i386/core/start16z.lds
@@ -0,0 +1,65 @@
+OUTPUT_FORMAT("elf32-i386", "elf32-i386", "elf32-i386")
+OUTPUT_ARCH(i386)
+
+/* Linker-generated symbols are prefixed with a double underscore.
+ * Decompressor symbols are prefixed with __decompressor_. All other
+ * symbols are the same as in the original object file, i.e. the
+ * runtime addresses.
+ */
+
+ENTRY(_start16)
+
+SECTIONS {
+ .text : {
+ *(.text)
+ }
+ .payload : {
+ __payload_start = .;
+ *(.data)
+ __payload_end = .;
+ }
+
+ /* _payload_size is the size of the binary image appended to
+ * start16, in bytes.
+ */
+ __payload_size = __payload_end - __payload_start ;
+
+ /* _size is the size of the runtime image
+ * (start32 + the C code), in bytes.
+ */
+ __size = _end - _start ;
+
+ /* _decompressor_size is the size of the decompressor, in
+ * bytes. For a non-compressed image, start16.lds sets
+ * _decompressor_uncompressed = _decompressor__start = 0.
+ */
+ __decompressor_size = __decompressor_uncompressed - __decompressor__start ;
+
+ /* image__size is the total size of the image, after
+ * decompression and including the decompressor if applicable.
+ * It is therefore the amount of memory that start16's payload
+ * needs in order to execute, in bytes.
+ */
+ __image_size = __size + __decompressor_size ;
+
+ /* Amount to add to runtime symbols to obtain the offset of
+ * that symbol within the image.
+ */
+ __offset_adjust = __decompressor_size - _start ;
+
+ /* Calculations for the stack
+ */
+ __stack_size = _estack - _stack ;
+ __offset_stack = _stack + __offset_adjust ;
+
+ /* Some symbols will be larger than 16 bits but guaranteed to
+ * be multiples of 16. We calculate them in paragraphs and
+ * export these symbols which can be used in 16-bit code
+ * without risk of overflow.
+ */
+ __image_size_pgh = ( __image_size / 16 );
+ __start_pgh = ( _start / 16 );
+ __decompressor_size_pgh = ( __decompressor_size / 16 );
+ __offset_stack_pgh = ( __offset_stack / 16 );
+}
+
diff --git a/src/arch/i386/core/start32.S b/src/arch/i386/core/start32.S
new file mode 100644
index 00000000..6dc3f203
--- /dev/null
+++ b/src/arch/i386/core/start32.S
@@ -0,0 +1,767 @@
+/* #defines because ljmp wants a number, probably gas bug */
+/* .equ KERN_CODE_SEG,_pmcs-_gdt */
+#define KERN_CODE_SEG 0x08
+ .equ KERN_DATA_SEG,_pmds-_gdt
+/* .equ REAL_CODE_SEG,_rmcs-_gdt */
+#define REAL_CODE_SEG 0x18
+ .equ REAL_DATA_SEG,_rmds-_gdt
+ .equ FLAT_CODE_SEG,_pmcs2-_gdt
+ .equ FLAT_DATA_SEG,_pmds2-_gdt
+ .equ CR0_PE,1
+#ifdef CONFIG_X86_64
+ .equ LM_CODE_SEG, _lmcs-_gdt
+ .equ LM_DATA_SEG, _lmds-_gdt
+#endif
+
+ .equ MSR_K6_EFER, 0xC0000080
+ .equ EFER_LME, 0x00000100
+ .equ X86_CR4_PAE, 0x00000020
+ .equ CR0_PG, 0x80000000
+
+#ifdef GAS291
+#define DATA32 data32;
+#define ADDR32 addr32;
+#define LJMPI(x) ljmp x
+#else
+#define DATA32 data32
+#define ADDR32 addr32
+/* newer GAS295 require #define LJMPI(x) ljmp *x */
+#define LJMPI(x) ljmp x
+#endif
+
+#define BOCHSBP xchgw %bx, %bx
+
+#include "callbacks.h"
+#define NUM_PUSHA_REGS (8)
+#define NUM_SEG_REGS (6)
+
+/*
+ * NOTE: if you write a subroutine that is called from C code (gcc/egcs),
+ * then you only have to take care of %ebx, %esi, %edi and %ebp. These
+ * registers must not be altered under any circumstance. All other registers
+ * may be clobbered without any negative side effects. If you don't follow
+ * this rule then you'll run into strange effects that only occur on some
+ * gcc versions (because the register allocator may use different registers).
+ *
+ * All the data32 prefixes for the ljmp instructions are necessary, because
+ * the assembler emits code with a relocation address of 0. This means that
+ * all destinations are initially negative, which the assembler doesn't grok,
+ * because for some reason negative numbers don't fit into 16 bits. The addr32
+ * prefixes are there for the same reasons, because otherwise the memory
+ * references are only 16 bit wide. Theoretically they are all superfluous.
+ * One last note about prefixes: the data32 prefixes on all call _real_to_prot
+ * instructions could be removed if the _real_to_prot function is changed to
+ * deal correctly with 16 bit return addresses. I tried it, but failed.
+ */
+
+/**************************************************************************
+ * START
+ *
+ * This file is no longer enterered from the top. init.S will jump to
+ * either _in_call or _rm_in_call, depending on the processor mode
+ * when init.S was entered.
+ **************************************************************************/
+ .text
+ .arch i386
+ .code32
+
+/**************************************************************************
+_IN_CALL - make a call in to Etherboot.
+**************************************************************************/
+
+/* There are two 32-bit entry points: _in_call and _in_call_far, for
+ * near calls and far calls respectively. Both should be called with
+ * flat physical addresses. They will result in a call to the C
+ * routine in_call(); see there for API details.
+ *
+ * Note that this routine makes fairly heavy use of the stack and no
+ * use of fixed data areas. This is because it must be re-entrant;
+ * there may be more than one concurrent call in to Etherboot.
+ */
+
+#define IC_OFFSET_VA_LIST_PTR ( 0 )
+#define IC_OFFSET_VA_LIST_PTR_E ( IC_OFFSET_VA_LIST_PTR + 4 )
+#define IC_OFFSET_REGISTERS ( IC_OFFSET_VA_LIST_PTR_E )
+#define IC_OFFSET_REGISTERS_E ( IC_OFFSET_REGISTERS + ( NUM_PUSHA_REGS * 4 ) )
+#define IC_OFFSET_SEG_REGS ( IC_OFFSET_REGISTERS_E )
+#define IC_OFFSET_SEG_REGS_E ( IC_OFFSET_SEG_REGS + ( NUM_SEG_REGS * 2 ) )
+#define IC_OFFSET_GDT ( IC_OFFSET_SEG_REGS_E )
+#define IC_OFFSET_GDT_E ( IC_OFFSET_GDT + 8 )
+#define IC_OFFSET_FLAGS ( IC_OFFSET_GDT_E )
+#define IC_OFFSET_FLAGS_E ( IC_OFFSET_FLAGS + 4 )
+#define IC_OFFSET_RETADDR ( IC_OFFSET_FLAGS_E )
+#define IC_OFFSET_RETADDR_E ( IC_OFFSET_RETADDR + 8 )
+#define IC_OFFSET_ORIG_STACK ( IC_OFFSET_RETADDR )
+#define IC_OFFSET_OPCODE ( IC_OFFSET_ORIG_STACK + 8 )
+#define IC_OFFSET_OPCODE_E ( IC_OFFSET_OPCODE + 4 )
+#define IC_OFFSET_VA_LIST ( IC_OFFSET_OPCODE_E )
+
+ .code32
+ .globl _in_call
+ .globl _in_call_far
+_in_call:
+ /* Expand to far return address */
+ pushl %eax /* Store %eax */
+ xorl %eax, %eax
+ movw %cs, %ax
+ xchgl %eax, 4(%esp) /* 4(%esp) = %cs, %eax = ret addr */
+ xchgl %eax, 0(%esp) /* 0(%esp) = ret addr, restore %eax */
+_in_call_far:
+ /* Store flags */
+ pushfl
+ /* Store the GDT */
+ subl $8, %esp
+ sgdt 0(%esp)
+ /* Store segment register values */
+ pushw %gs
+ pushw %fs
+ pushw %es
+ pushw %ds
+ pushw %ss
+ pushw %cs
+ /* Store general-purpose register values */
+ pushal
+ /* Replace %esp in store with physical %esp value on entry */
+ leal (IC_OFFSET_ORIG_STACK - IC_OFFSET_REGISTERS)(%esp), %eax
+ movl %eax, (IC_OFFSET_REGISTERS - IC_OFFSET_REGISTERS + 12)(%esp)
+ /* Store va_list pointer (physical address) */
+ leal (IC_OFFSET_VA_LIST - IC_OFFSET_VA_LIST_PTR_E)(%esp), %eax
+ pushl %eax
+ /* IC_OFFSET_*(%esp) are now valid */
+
+ /* Switch to virtual addresses */
+ call _phys_to_virt
+
+ /* Fixup the va_list pointer */
+ movl virt_offset, %ebp
+ subl %ebp, IC_OFFSET_VA_LIST_PTR(%esp)
+
+ /* Check opcode for EB_USE_INTERNAL_STACK flag */
+ movl IC_OFFSET_OPCODE(%esp), %eax
+ testl $EB_USE_INTERNAL_STACK, %eax
+ je 2f
+ /* Use internal stack flag set */
+ /* Check %esp is not already in internal stack range */
+ leal _stack, %esi /* %esi = bottom of internal stack */
+ leal _estack, %edi /* %edi = top of internal stack */
+ cmpl %esi, %esp
+ jb 1f
+ cmpl %edi, %esp
+ jbe 2f
+1: /* %esp not currently in internal stack range */
+ movl %esp, %esi /* %esi = original stack */
+ movl $IC_OFFSET_OPCODE_E, %ecx /* %ecx = length to transfer */
+ subl %ecx, %edi /* %edi = internal stack pos */
+ movl %edi, %esp /* = new %esp */
+ rep movsb /* Copy data to internal stack */
+2:
+
+ /* Call to C code */
+ call i386_in_call
+
+ /* Set %eax (return code from C) in registers structure on
+ * stack, so that we return it to the caller.
+ */
+ movl %eax, (IC_OFFSET_REGISTERS + 28)(%esp)
+
+ /* Calculate physical continuation address */
+ movl virt_offset, %ebp
+ movzwl (IC_OFFSET_SEG_REGS + 0)(%esp), %eax /* %cs */
+ movzwl (IC_OFFSET_SEG_REGS + 2)(%esp), %ebx /* %ss */
+ pushl %eax /* Continuation segment */
+ leal 1f(%ebp), %eax
+ pushl %eax /* Continuation offset */
+
+ /* Restore caller's GDT */
+ cli /* Temporarily disable interrupts */
+ lgdt (8+IC_OFFSET_GDT)(%esp)
+ /* Reset %ss and adjust %esp */
+ movw %bx, %ss
+ addl %ebp, %esp
+ lret /* Reload %cs:eip, flush prefetch */
+1:
+
+ /* Skip va_list ptr */
+ popl %eax
+ /* Reload general-purpose registers to be returned */
+ popal
+ /* Reload segment registers as passed in from caller */
+ popw %gs
+ popw %fs
+ popw %es
+ popw %ds
+ addl $(4+8), %esp /* Skip %cs, %ss and GDT (already reloaded) */
+ /* Restore flags (including revert of interrupt status) */
+ popfl
+
+ /* Restore physical %esp from entry. It will only be
+ * different if EB_USE_INTERNAL_STACK was specified.
+ */
+ movl ( 12 + IC_OFFSET_REGISTERS - IC_OFFSET_RETADDR )(%esp), %esp
+
+ /* Check for EB_SKIP_OPCODE */
+ pushfl
+ testl $EB_SKIP_OPCODE, 12(%esp)
+ jnz 1f
+ /* Normal return */
+ popfl
+ lret
+1: /* Return and skip opcode */
+ popfl
+ lret $4
+
+/**************************************************************************
+RELOCATE_TO - relocate etherboot to the specified address
+**************************************************************************/
+ .globl relocate_to
+relocate_to:
+ /* Save the callee save registers */
+ pushl %ebp
+ pushl %esi
+ pushl %edi
+
+ /* Compute the virtual destination address */
+ movl 16(%esp), %edi # dest
+ subl virt_offset, %edi
+
+
+ /* Compute the new value of virt_offset */
+ movl 16(%esp), %ebp # virt_offset
+ subl $_text, %ebp
+
+ /* Fixup the gdt */
+ pushl $_pmcs
+ pushl %ebp # virt_offset
+ call set_seg_base
+ addl $8, %esp
+
+ /* Fixup gdtarg */
+ leal _gdt(%ebp), %eax
+ movl %eax, gdtarg +2
+
+ /* Fixup virt_offset */
+ movl %ebp, virt_offset
+
+ /* Load the move parameters */
+ movl $_text, %esi
+ movl $_end, %ecx
+ subl %esi, %ecx
+
+ /* Move etherboot uses %esi, %edi, %ecx */
+ rep
+ movsb
+
+ /* Reload the gdt */
+ cs
+ lgdt gdtarg
+
+ /* Reload %cs */
+ ljmp $KERN_CODE_SEG, $1f
+1:
+ /* reload other segment registers */
+ movl $KERN_DATA_SEG, %eax
+ movl %eax,%ds
+ movl %eax,%es
+ movl %eax,%ss
+ movl %eax,%fs
+ movl %eax,%gs
+
+ /* Restore the callee save registers */
+ popl %edi
+ popl %esi
+ popl %ebp
+
+ /* return */
+ ret
+
+/**************************************************************************
+XSTART32 - Transfer control to the kernel just loaded
+**************************************************************************/
+ .globl xstart32
+xstart32:
+ /* Save the callee save registers */
+ movl %ebp, os_regs + 32
+ movl %esi, os_regs + 36
+ movl %edi, os_regs + 40
+ movl %ebx, os_regs + 44
+
+ /* save the return address */
+ popl %eax
+ movl %eax, os_regs + 48
+
+ /* save the stack pointer */
+ movl %esp, os_regs + 52
+
+ /* Get the new destination address */
+ popl %ecx
+
+ /* Store the physical address of xend on the stack */
+ movl $xend32, %ebx
+ addl virt_offset, %ebx
+ pushl %ebx
+
+ /* Store the destination address on the stack */
+ pushl $FLAT_CODE_SEG
+ pushl %ecx
+
+ /* Cache virt_offset */
+ movl virt_offset, %ebp
+
+ /* Switch to using physical addresses */
+ call _virt_to_phys
+
+ /* Save the target stack pointer */
+ movl %esp, os_regs + 12(%ebp)
+ leal os_regs(%ebp), %esp
+
+ /* Store the pointer to os_regs */
+ movl %esp, os_regs_ptr(%ebp)
+
+ /* Load my new registers */
+ popal
+ movl (-32 + 12)(%esp), %esp
+
+ /* Jump to the new kernel
+ * The lret switches to a flat code segment
+ */
+ lret
+
+ .balign 4
+ .globl xend32
+xend32:
+ /* Fixup %eflags */
+ nop
+ cli
+ cld
+
+ /* Load %esp with &os_regs + virt_offset */
+ .byte 0xbc /* movl $0, %esp */
+os_regs_ptr:
+ .long 0
+
+ /* Save the result registers */
+ addl $32, %esp
+ pushal
+
+ /* Compute virt_offset */
+ movl %esp, %ebp
+ subl $os_regs, %ebp
+
+ /* Load the stack pointer */
+ movl 52(%esp), %esp
+
+ /* Enable the virtual addresses */
+ leal _phys_to_virt(%ebp), %eax
+ call *%eax
+
+ /* Restore the callee save registers */
+ movl os_regs + 32, %ebp
+ movl os_regs + 36, %esi
+ movl os_regs + 40, %edi
+ movl os_regs + 44, %ebx
+ movl os_regs + 48, %edx
+ movl os_regs + 52, %esp
+
+ /* Get the C return value */
+ movl os_regs + 28, %eax
+
+ jmpl *%edx
+
+#ifdef CONFIG_X86_64
+ .arch sledgehammer
+/**************************************************************************
+XSTART_lm - Transfer control to the kernel just loaded in long mode
+**************************************************************************/
+ .globl xstart_lm
+xstart_lm:
+ /* Save the callee save registers */
+ pushl %ebp
+ pushl %esi
+ pushl %edi
+ pushl %ebx
+
+ /* Cache virt_offset && (virt_offset & 0xfffff000) */
+ movl virt_offset, %ebp
+ movl %ebp, %ebx
+ andl $0xfffff000, %ebx
+
+ /* Switch to using physical addresses */
+ call _virt_to_phys
+
+ /* Initialize the page tables */
+ /* Level 4 */
+ leal 0x23 + pgt_level3(%ebx), %eax
+ leal pgt_level4(%ebx), %edi
+ movl %eax, (%edi)
+
+ /* Level 3 */
+ leal 0x23 + pgt_level2(%ebx), %eax
+ leal pgt_level3(%ebx), %edi
+ movl %eax, 0x00(%edi)
+ addl $4096, %eax
+ movl %eax, 0x08(%edi)
+ addl $4096, %eax
+ movl %eax, 0x10(%edi)
+ addl $4096, %eax
+ movl %eax, 0x18(%edi)
+
+ /* Level 2 */
+ movl $0xe3, %eax
+ leal pgt_level2(%ebx), %edi
+ leal 16384(%edi), %esi
+pgt_level2_loop:
+ movl %eax, (%edi)
+ addl $8, %edi
+ addl $0x200000, %eax
+ cmp %esi, %edi
+ jne pgt_level2_loop
+
+ /* Point at the x86_64 page tables */
+ leal pgt_level4(%ebx), %edi
+ movl %edi, %cr3
+
+
+ /* Setup for the return from 64bit mode */
+ /* 64bit align the stack */
+ movl %esp, %ebx /* original stack pointer + 16 */
+ andl $0xfffffff8, %esp
+
+ /* Save original stack pointer + 16 */
+ pushl %ebx
+
+ /* Save virt_offset */
+ pushl %ebp
+
+ /* Setup for the jmp to 64bit long mode */
+ leal start_lm(%ebp), %eax
+ movl %eax, 0x00 + start_lm_addr(%ebp)
+ movl $LM_CODE_SEG, %eax
+ movl %eax, 0x04 + start_lm_addr(%ebp)
+
+ /* Setup for the jump out of 64bit long mode */
+ leal end_lm(%ebp), %eax
+ movl %eax, 0x00 + end_lm_addr(%ebp)
+ movl $FLAT_CODE_SEG, %eax
+ movl %eax, 0x04 + end_lm_addr(%ebp)
+
+ /* Enable PAE mode */
+ movl %cr4, %eax
+ orl $X86_CR4_PAE, %eax
+ movl %eax, %cr4
+
+ /* Enable long mode */
+ movl $MSR_K6_EFER, %ecx
+ rdmsr
+ orl $EFER_LME, %eax
+ wrmsr
+
+ /* Start paging, entering 32bit compatiblity mode */
+ movl %cr0, %eax
+ orl $CR0_PG, %eax
+ movl %eax, %cr0
+
+ /* Enter 64bit long mode */
+ ljmp *start_lm_addr(%ebp)
+ .code64
+start_lm:
+ /* Load 64bit data segments */
+ movl $LM_DATA_SEG, %eax
+ movl %eax, %ds
+ movl %eax, %es
+ movl %eax, %ss
+
+ andq $0xffffffff, %rbx
+ /* Get the address to jump to */
+ movl 20(%rbx), %edx
+ andq $0xffffffff, %rdx
+
+ /* Get the argument pointer */
+ movl 24(%rbx), %ebx
+ andq $0xffffffff, %rbx
+
+ /* Jump to the 64bit code */
+ call *%rdx
+
+ /* Preserve the result */
+ movl %eax, %edx
+
+ /* Fixup %eflags */
+ cli
+ cld
+
+ /* Switch to 32bit compatibility mode */
+ ljmp *end_lm_addr(%rip)
+
+ .code32
+end_lm:
+ /* Disable paging */
+ movl %cr0, %eax
+ andl $~CR0_PG, %eax
+ movl %eax, %cr0
+
+ /* Disable long mode */
+ movl $MSR_K6_EFER, %ecx
+ rdmsr
+ andl $~EFER_LME, %eax
+ wrmsr
+
+ /* Disable PAE */
+ movl %cr4, %eax
+ andl $~X86_CR4_PAE, %eax
+ movl %eax, %cr4
+
+ /* Compute virt_offset */
+ popl %ebp
+
+ /* Compute the original stack pointer + 16 */
+ popl %ebx
+ movl %ebx, %esp
+
+ /* Enable the virtual addresses */
+ leal _phys_to_virt(%ebp), %eax
+ call *%eax
+
+ /* Restore the callee save registers */
+ popl %ebx
+ popl %esi
+ popl %edi
+ popl %ebp
+
+ /* Get the C return value */
+ movl %edx, %eax
+
+ /* Return */
+ ret
+
+ .arch i386
+#endif /* CONFIG_X86_64 */
+
+/**************************************************************************
+SETJMP - Save stack context for non-local goto
+**************************************************************************/
+ .globl setjmp
+setjmp:
+ movl 4(%esp),%ecx /* jmpbuf */
+ movl 0(%esp),%edx /* return address */
+ movl %edx,0(%ecx)
+ movl %ebx,4(%ecx)
+ movl %esp,8(%ecx)
+ movl %ebp,12(%ecx)
+ movl %esi,16(%ecx)
+ movl %edi,20(%ecx)
+ movl $0,%eax
+ ret
+
+/**************************************************************************
+LONGJMP - Non-local jump to a saved stack context
+**************************************************************************/
+ .globl longjmp
+longjmp:
+ movl 4(%esp),%edx /* jumpbuf */
+ movl 8(%esp),%eax /* result */
+ movl 0(%edx),%ecx
+ movl 4(%edx),%ebx
+ movl 8(%edx),%esp
+ movl 12(%edx),%ebp
+ movl 16(%edx),%esi
+ movl 20(%edx),%edi
+ cmpl $0,%eax
+ jne 1f
+ movl $1,%eax
+1: movl %ecx,0(%esp)
+ ret
+
+/**************************************************************************
+_VIRT_TO_PHYS - Transition from virtual to physical addresses
+ Preserves all preservable registers and flags
+**************************************************************************/
+ .globl _virt_to_phys
+_virt_to_phys:
+ pushfl
+ pushl %ebp
+ pushl %eax
+ movl virt_offset, %ebp /* Load virt_offset */
+ addl %ebp, 12(%esp) /* Adjust the return address */
+
+ /* reload the code segment */
+ pushl $FLAT_CODE_SEG
+ leal 1f(%ebp), %eax
+ pushl %eax
+ lret
+
+1:
+ /* reload other segment registers */
+ movl $FLAT_DATA_SEG, %eax
+ movl %eax, %ds
+ movl %eax, %es
+ movl %eax, %ss
+ addl %ebp, %esp /* Adjust the stack pointer */
+ movl %eax, %fs
+ movl %eax, %gs
+
+ popl %eax
+ popl %ebp
+ popfl
+ ret
+
+
+/**************************************************************************
+_PHYS_TO_VIRT - Transition from using physical to virtual addresses
+ Preserves all preservable registers and flags
+**************************************************************************/
+ .globl _phys_to_virt
+_phys_to_virt:
+ pushfl
+ pushl %ebp
+ pushl %eax
+
+ call 1f
+1: popl %ebp
+ subl $1b, %ebp
+ movl %ebp, virt_offset(%ebp)
+
+ /* Fixup the gdt */
+ leal _pmcs(%ebp), %eax
+ pushl %eax
+ pushl %ebp
+ call set_seg_base
+ addl $8, %esp
+
+ /* Fixup gdtarg */
+ leal _gdt(%ebp), %eax
+ movl %eax, (gdtarg+2)(%ebp)
+
+ /* Load the global descriptor table */
+ cli
+ lgdt %cs:gdtarg(%ebp)
+ ljmp $KERN_CODE_SEG, $1f
+1:
+ /* reload other segment regsters */
+ movl $KERN_DATA_SEG, %eax
+ movl %eax, %ds
+ movl %eax, %es
+ movl %eax, %ss
+ subl %ebp, %esp /* Adjust the stack pointer */
+ movl %eax, %fs
+ movl %eax, %gs
+
+ subl %ebp, 12(%esp) /* Adjust the return address */
+ popl %eax
+ popl %ebp
+ popfl
+ ret
+
+
+/**************************************************************************
+SET_SEG_BASE - Set the base address of a segment register
+**************************************************************************/
+ .globl set_seg_base
+set_seg_base:
+ pushl %eax
+ pushl %ebx
+ movl 12(%esp), %eax /* %eax = base address */
+ movl 16(%esp), %ebx /* %ebx = &code_descriptor */
+ movw %ax, (0+2)(%ebx) /* CS base bits 0-15 */
+ movw %ax, (8+2)(%ebx) /* DS base bits 0-15 */
+ shrl $16, %eax
+ movb %al, (0+4)(%ebx) /* CS base bits 16-23 */
+ movb %al, (8+4)(%ebx) /* DS base bits 16-23 */
+ movb %ah, (0+7)(%ebx) /* CS base bits 24-31 */
+ movb %ah, (8+7)(%ebx) /* DS base bits 24-31 */
+ popl %ebx
+ popl %eax
+ ret
+
+/**************************************************************************
+GLOBAL DESCRIPTOR TABLE
+**************************************************************************/
+ .data
+ .align 4
+
+ .globl _gdt
+ .globl gdtarg
+_gdt:
+gdtarg:
+ .word _gdt_end - _gdt - 1 /* limit */
+ .long _gdt /* addr */
+ .word 0
+
+ .globl _pmcs
+_pmcs:
+ /* 32 bit protected mode code segment */
+ .word 0xffff,0
+ .byte 0,0x9f,0xcf,0
+
+_pmds:
+ /* 32 bit protected mode data segment */
+ .word 0xffff,0
+ .byte 0,0x93,0xcf,0
+
+_rmcs:
+ /* 16 bit real mode code segment */
+ .word 0xffff,(0&0xffff)
+ .byte (0>>16),0x9b,0x00,(0>>24)
+
+_rmds:
+ /* 16 bit real mode data segment */
+ .word 0xffff,(0&0xffff)
+ .byte (0>>16),0x93,0x00,(0>>24)
+
+_pmcs2:
+ /* 32 bit protected mode code segment, base 0 */
+ .word 0xffff,0
+ .byte 0,0x9f,0xcf,0
+
+_pmds2:
+ /* 32 bit protected mode data segment, base 0 */
+ .word 0xffff,0
+ .byte 0,0x93,0xcf,0
+
+#ifdef CONFIG_X86_64
+_lmcs:
+ /* 64bit long mode code segment, base 0 */
+ .word 0xffff, 0
+ .byte 0x00, 0x9f, 0xaf , 0x00
+_lmds:
+ /* 64bit long mode data segment, base 0 */
+ .word 0xffff, 0
+ .byte 0x00, 0x93, 0xcf, 0x00
+#endif
+_gdt_end:
+
+ /* The initial register contents */
+ .balign 4
+ .globl initial_regs
+initial_regs:
+ .fill 8, 4, 0
+
+ /* The virtual address offset */
+ .globl virt_offset
+virt_offset:
+ .long 0
+
+ .section ".stack"
+ .p2align 3
+ /* allocate a 4K stack in the stack segment */
+ .globl _stack
+_stack:
+ .space 4096
+ .globl _estack
+_estack:
+#ifdef CONFIG_X86_64
+ .section ".bss"
+ .p2align 12
+ /* Include a dummy space in case we are loaded badly aligned */
+ .space 4096
+ /* Reserve enough space for a page table convering 4GB with 2MB pages */
+pgt_level4:
+ .space 4096
+pgt_level3:
+ .space 4096
+pgt_level2:
+ .space 16384
+start_lm_addr:
+ .space 8
+end_lm_addr:
+ .space 8
+#endif
diff --git a/src/arch/i386/core/tagged_loader.c b/src/arch/i386/core/tagged_loader.c
new file mode 100644
index 00000000..9c6d6c25
--- /dev/null
+++ b/src/arch/i386/core/tagged_loader.c
@@ -0,0 +1,201 @@
+#include "realmode.h"
+#include "segoff.h"
+
+struct segheader
+{
+ unsigned char length;
+ unsigned char vendortag;
+ unsigned char reserved;
+ unsigned char flags;
+ unsigned long loadaddr;
+ unsigned long imglength;
+ unsigned long memlength;
+};
+
+struct imgheader
+{
+ unsigned long magic;
+ unsigned long length; /* and flags */
+ union
+ {
+ segoff_t segoff;
+ unsigned long location;
+ } u;
+ unsigned long execaddr;
+};
+
+/* Keep all context about loaded image in one place */
+static struct tagged_context
+{
+ struct imgheader img; /* copy of header */
+ unsigned long linlocation; /* addr of header */
+ unsigned long last0, last1; /* of prev segment */
+ unsigned long segaddr, seglen; /* of current segment */
+ unsigned char segflags;
+ unsigned char first;
+ unsigned long curaddr;
+} tctx;
+
+#define TAGGED_PROGRAM_RETURNS (tctx.img.length & 0x00000100) /* bit 8 */
+#define LINEAR_EXEC_ADDR (tctx.img.length & 0x80000000) /* bit 31 */
+
+static sector_t tagged_download(unsigned char *data, unsigned int len, int eof);
+void xstart16 (unsigned long execaddr, segoff_t location,
+ void *bootp);
+
+static inline os_download_t tagged_probe(unsigned char *data, unsigned int len)
+{
+ struct segheader *sh;
+ unsigned long loc;
+ if (*((uint32_t *)data) != 0x1B031336L) {
+ return 0;
+ }
+ printf("(NBI)");
+ /* If we don't have enough data give up */
+ if (len < 512)
+ return dead_download;
+ /* Zero all context info */
+ memset(&tctx, 0, sizeof(tctx));
+ /* Copy first 4 longwords */
+ memcpy(&tctx.img, data, sizeof(tctx.img));
+ /* Memory location where we are supposed to save it */
+ tctx.segaddr = tctx.linlocation =
+ ((tctx.img.u.segoff.segment) << 4) + tctx.img.u.segoff.offset;
+ if (!prep_segment(tctx.segaddr, tctx.segaddr + 512, tctx.segaddr + 512,
+ 0, 512)) {
+ return dead_download;
+ }
+ /* Now verify the segments we are about to load */
+ loc = 512;
+ for(sh = (struct segheader *)(data
+ + ((tctx.img.length & 0x0F) << 2)
+ + ((tctx.img.length & 0xF0) >> 2) );
+ (sh->length > 0) && ((unsigned char *)sh < data + 512);
+ sh = (struct segheader *)((unsigned char *)sh
+ + ((sh->length & 0x0f) << 2) + ((sh->length & 0xf0) >> 2)) ) {
+ if (!prep_segment(
+ sh->loadaddr,
+ sh->loadaddr + sh->imglength,
+ sh->loadaddr + sh->imglength,
+ loc, loc + sh->imglength)) {
+ return dead_download;
+ }
+ loc = loc + sh->imglength;
+ if (sh->flags & 0x04)
+ break;
+ }
+ if (!(sh->flags & 0x04))
+ return dead_download;
+ /* Grab a copy */
+ memcpy(phys_to_virt(tctx.segaddr), data, 512);
+ /* Advance to first segment descriptor */
+ tctx.segaddr += ((tctx.img.length & 0x0F) << 2)
+ + ((tctx.img.length & 0xF0) >> 2);
+ /* Remember to skip the first 512 data bytes */
+ tctx.first = 1;
+
+ return tagged_download;
+
+}
+static sector_t tagged_download(unsigned char *data, unsigned int len, int eof)
+{
+ int i;
+
+ if (tctx.first) {
+ tctx.first = 0;
+ if (len > 512) {
+ len -= 512;
+ data += 512;
+ /* and fall through to deal with rest of block */
+ } else
+ return 0;
+ }
+ for (;;) {
+ if (len == 0) /* Detect truncated files */
+ eof = 0;
+ while (tctx.seglen == 0) {
+ struct segheader sh;
+ if (tctx.segflags & 0x04) {
+ done(1);
+ if (LINEAR_EXEC_ADDR) {
+ int result;
+ /* no gateA20_unset for PM call */
+ result = xstart32(tctx.img.execaddr,
+ virt_to_phys(&loaderinfo),
+ tctx.linlocation,
+ virt_to_phys(BOOTP_DATA_ADDR));
+ printf("Secondary program returned %d\n",
+ result);
+ if (!TAGGED_PROGRAM_RETURNS) {
+ /* We shouldn't have returned */
+ result = -2;
+ }
+ if (result == 0)
+ result = -2;
+ longjmp(restart_etherboot, result);
+
+ } else {
+ gateA20_unset();
+ xstart16(tctx.img.execaddr,
+ tctx.img.u.segoff,
+ BOOTP_DATA_ADDR);
+ longjmp(restart_etherboot, -2);
+ }
+ }
+ sh = *((struct segheader *)phys_to_virt(tctx.segaddr));
+ tctx.seglen = sh.imglength;
+ if ((tctx.segflags = sh.flags & 0x03) == 0)
+ tctx.curaddr = sh.loadaddr;
+ else if (tctx.segflags == 0x01)
+ tctx.curaddr = tctx.last1 + sh.loadaddr;
+ else if (tctx.segflags == 0x02)
+ tctx.curaddr = (Address)(meminfo.memsize * 1024L
+ + 0x100000L)
+ - sh.loadaddr;
+ else
+ tctx.curaddr = tctx.last0 - sh.loadaddr;
+ tctx.last1 = (tctx.last0 = tctx.curaddr) + sh.memlength;
+ tctx.segflags = sh.flags;
+ tctx.segaddr += ((sh.length & 0x0F) << 2)
+ + ((sh.length & 0xF0) >> 2);
+ /* Avoid lock-up */
+ if ( sh.length == 0 ) longjmp(restart_etherboot, -2);
+ }
+ if ((len <= 0) && !eof)
+ break;
+ i = (tctx.seglen > len) ? len : tctx.seglen;
+ memcpy(phys_to_virt(tctx.curaddr), data, i);
+ tctx.seglen -= i;
+ tctx.curaddr += i;
+ len -= i;
+ data += i;
+ }
+ return 0;
+}
+
+void xstart16 (unsigned long execaddr, segoff_t location,
+ void *bootp) {
+ struct {
+ segoff_t execaddr;
+ segoff_t location;
+ segoff_t bootp;
+ } PACKED in_stack;
+
+ /* AFAICT, execaddr is actually already a segment:offset */
+ *((unsigned long *)&in_stack.execaddr) = execaddr;
+ in_stack.location = location;
+ in_stack.bootp.segment = SEGMENT(bootp);
+ in_stack.bootp.offset = OFFSET(bootp);
+
+ RM_FRAGMENT(rm_xstart16,
+ "popl %eax\n\t" /* Calculated lcall */
+ "pushw %cs\n\t"
+ "call 1f\n1:\tpopw %bp\n\t"
+ "leaw (2f-1b)(%bp), %bx\n\t"
+ "pushw %bx\n\t"
+ "pushl %eax\n\t"
+ "lret\n2:\n\t"
+ );
+
+ real_call ( rm_xstart16, &in_stack, NULL );
+}
diff --git a/src/arch/i386/core/video_subr.c b/src/arch/i386/core/video_subr.c
new file mode 100644
index 00000000..dccdd97c
--- /dev/null
+++ b/src/arch/i386/core/video_subr.c
@@ -0,0 +1,94 @@
+/*
+ *
+ * modified from linuxbios code
+ * by Cai Qiang <rimy2000@hotmail.com>
+ *
+ */
+
+#ifdef CONSOLE_DIRECT_VGA
+
+#include <etherboot.h>
+#include <vga.h>
+
+static char *vidmem; /* The video buffer */
+static int video_line, video_col;
+
+#define VIDBUFFER 0xB8000
+
+static void memsetw(void *s, int c, unsigned int n)
+{
+ int i;
+ u16 *ss = (u16 *) s;
+
+ for (i = 0; i < n; i++) {
+ ss[i] = ( u16 ) c;
+ }
+}
+
+void video_init(void)
+{
+ static int inited=0;
+
+ vidmem = (unsigned char *)phys_to_virt(VIDBUFFER);
+
+ if (!inited) {
+ video_line = 0;
+ video_col = 0;
+
+ memsetw(vidmem, VGA_ATTR_CLR_WHT, 2*1024); //
+
+ inited=1;
+ }
+}
+
+static void video_scroll(void)
+{
+ int i;
+
+ memcpy(vidmem, vidmem + COLS * 2, (LINES - 1) * COLS * 2);
+ for (i = (LINES - 1) * COLS * 2; i < LINES * COLS * 2; i += 2)
+ vidmem[i] = ' ';
+}
+
+void vga_putc(unsigned char byte)
+{
+ if (byte == '\n') {
+ video_line++;
+ video_col = 0;
+
+ } else if (byte == '\r') {
+ video_col = 0;
+
+ } else if (byte == '\b') {
+ video_col--;
+
+ } else if (byte == '\t') {
+ video_col += 4;
+
+ } else if (byte == '\a') {
+ //beep
+ //beep(500);
+
+ } else {
+ vidmem[((video_col + (video_line *COLS)) * 2)] = byte;
+ vidmem[((video_col + (video_line *COLS)) * 2) +1] = VGA_ATTR_CLR_WHT;
+ video_col++;
+ }
+ if (video_col < 0) {
+ video_col = 0;
+ }
+ if (video_col >= COLS) {
+ video_line++;
+ video_col = 0;
+ }
+ if (video_line >= LINES) {
+ video_scroll();
+ video_line--;
+ }
+ // move the cursor
+ write_crtc((video_col + (video_line *COLS)) >> 8, CRTC_CURSOR_HI);
+ write_crtc((video_col + (video_line *COLS)) & 0x0ff, CRTC_CURSOR_LO);
+}
+
+#endif
+
diff --git a/src/arch/i386/core/wince_loader.c b/src/arch/i386/core/wince_loader.c
new file mode 100644
index 00000000..f452b659
--- /dev/null
+++ b/src/arch/i386/core/wince_loader.c
@@ -0,0 +1,273 @@
+#define LOAD_DEBUG 0
+
+static int get_x_header(unsigned char *data, unsigned long now);
+static void jump_2ep();
+static unsigned char ce_signature[] = {'B', '0', '0', '0', 'F', 'F', '\n',};
+static char ** ep;
+
+#define BOOT_ARG_PTR_LOCATION 0x001FFFFC
+
+typedef struct _BOOT_ARGS{
+ unsigned char ucVideoMode;
+ unsigned char ucComPort;
+ unsigned char ucBaudDivisor;
+ unsigned char ucPCIConfigType;
+
+ unsigned long dwSig;
+ #define BOOTARG_SIG 0x544F4F42
+ unsigned long dwLen;
+
+ unsigned char ucLoaderFlags;
+ unsigned char ucEshellFlags;
+ unsigned char ucEdbgAdapterType;
+ unsigned char ucEdbgIRQ;
+
+ unsigned long dwEdbgBaseAddr;
+ unsigned long dwEdbgDebugZone;
+ unsigned long dwDHCPLeaseTime;
+ unsigned long dwEdbgFlags;
+
+ unsigned long dwEBootFlag;
+ unsigned long dwEBootAddr;
+ unsigned long dwLaunchAddr;
+
+ unsigned long pvFlatFrameBuffer;
+ unsigned short vesaMode;
+ unsigned short cxDisplayScreen;
+ unsigned short cyDisplayScreen;
+ unsigned short cxPhysicalScreen;
+ unsigned short cyPhysicalScreen;
+ unsigned short cbScanLineLength;
+ unsigned short bppScreen;
+
+ unsigned char RedMaskSize;
+ unsigned char REdMaskPosition;
+ unsigned char GreenMaskSize;
+ unsigned char GreenMaskPosition;
+ unsigned char BlueMaskSize;
+ unsigned char BlueMaskPosition;
+} BOOT_ARGS;
+
+BOOT_ARGS BootArgs;
+
+static struct segment_info{
+ unsigned long addr; // Section Address
+ unsigned long size; // Section Size
+ unsigned long checksum; // Section CheckSum
+} X;
+
+#define PSIZE (1500) //Max Packet Size
+#define DSIZE (PSIZE+12)
+static unsigned long dbuffer_available =0;
+static unsigned long not_loadin =0;
+static unsigned long d_now =0;
+
+unsigned long entry;
+static unsigned long ce_curaddr;
+
+
+static sector_t ce_loader(unsigned char *data, unsigned int len, int eof);
+static os_download_t wince_probe(unsigned char *data, unsigned int len)
+{
+ if (strncmp(ce_signature, data, sizeof(ce_signature)) != 0) {
+ return 0;
+ }
+ printf("(WINCE)");
+ return ce_loader;
+}
+
+static sector_t ce_loader(unsigned char *data, unsigned int len, int eof)
+{
+ static unsigned char dbuffer[DSIZE];
+ int this_write = 0;
+ static int firsttime = 1;
+
+ /*
+ * new packet in, we have to
+ * [1] copy data to dbuffer,
+ *
+ * update...
+ * [2] dbuffer_available
+ */
+ memcpy( (dbuffer+dbuffer_available), data, len); //[1]
+ dbuffer_available += len; // [2]
+ len = 0;
+
+ d_now = 0;
+
+#if 0
+ printf("dbuffer_available =%ld \n", dbuffer_available);
+#endif
+
+ if (firsttime)
+ {
+ d_now = sizeof(ce_signature);
+ printf("String Physical Address = %lx \n",
+ *(unsigned long *)(dbuffer+d_now));
+
+ d_now += sizeof(unsigned long);
+ printf("Image Size = %ld [%lx]\n",
+ *(unsigned long *)(dbuffer+d_now),
+ *(unsigned long *)(dbuffer+d_now));
+
+ d_now += sizeof(unsigned long);
+ dbuffer_available -= d_now;
+
+ d_now = (unsigned long)get_x_header(dbuffer, d_now);
+ firsttime = 0;
+ }
+
+ if (not_loadin == 0)
+ {
+ d_now = get_x_header(dbuffer, d_now);
+ }
+
+ while ( not_loadin > 0 )
+ {
+ /* dbuffer do not have enough data to loading, copy all */
+#if LOAD_DEBUG
+ printf("[0] not_loadin = [%ld], dbuffer_available = [%ld] \n",
+ not_loadin, dbuffer_available);
+ printf("[0] d_now = [%ld] \n", d_now);
+#endif
+
+ if( dbuffer_available <= not_loadin)
+ {
+ this_write = dbuffer_available ;
+ memcpy(phys_to_virt(ce_curaddr), (dbuffer+d_now), this_write );
+ ce_curaddr += this_write;
+ not_loadin -= this_write;
+
+ /* reset index and available in the dbuffer */
+ dbuffer_available = 0;
+ d_now = 0;
+#if LOAD_DEBUG
+ printf("[1] not_loadin = [%ld], dbuffer_available = [%ld] \n",
+ not_loadin, dbuffer_available);
+ printf("[1] d_now = [%ld], this_write = [%d] \n",
+ d_now, this_write);
+#endif
+
+ // get the next packet...
+ return (0);
+ }
+
+ /* dbuffer have more data then loading ... , copy partital.... */
+ else
+ {
+ this_write = not_loadin;
+ memcpy(phys_to_virt(ce_curaddr), (dbuffer+d_now), this_write);
+ ce_curaddr += this_write;
+ not_loadin = 0;
+
+ /* reset index and available in the dbuffer */
+ dbuffer_available -= this_write;
+ d_now += this_write;
+#if LOAD_DEBUG
+ printf("[2] not_loadin = [%ld], dbuffer_available = [%ld] \n",
+ not_loadin, dbuffer_available);
+ printf("[2] d_now = [%ld], this_write = [%d] \n\n",
+ d_now, this_write);
+#endif
+
+ /* dbuffer not empty, proceed processing... */
+
+ // don't have enough data to get_x_header..
+ if ( dbuffer_available < (sizeof(unsigned long) * 3) )
+ {
+// printf("we don't have enough data remaining to call get_x. \n");
+ memcpy( (dbuffer+0), (dbuffer+d_now), dbuffer_available);
+ return (0);
+ }
+ else
+ {
+#if LOAD_DEBUG
+ printf("with remaining data to call get_x \n");
+ printf("dbuffer available = %ld , d_now = %ld\n",
+ dbuffer_available, d_now);
+#endif
+ d_now = get_x_header(dbuffer, d_now);
+ }
+ }
+ }
+ return (0);
+}
+
+static int get_x_header(unsigned char *dbuffer, unsigned long now)
+{
+ X.addr = *(unsigned long *)(dbuffer + now);
+ X.size = *(unsigned long *)(dbuffer + now + sizeof(unsigned long));
+ X.checksum = *(unsigned long *)(dbuffer + now + sizeof(unsigned long)*2);
+
+ if (X.addr == 0)
+ {
+ entry = X.size;
+ done(1);
+ printf("Entry Point Address = [%lx] \n", entry);
+ jump_2ep();
+ }
+
+ if (!prep_segment(X.addr, X.addr + X.size, X.addr + X.size, 0, 0)) {
+ longjmp(restart_etherboot, -2);
+ }
+
+ ce_curaddr = X.addr;
+ now += sizeof(unsigned long)*3;
+
+ /* re-calculate dbuffer available... */
+ dbuffer_available -= sizeof(unsigned long)*3;
+
+ /* reset index of this section */
+ not_loadin = X.size;
+
+#if 1
+ printf("\n");
+ printf("\t Section Address = [%lx] \n", X.addr);
+ printf("\t Size = %d [%lx]\n", X.size, X.size);
+ printf("\t Checksum = %ld [%lx]\n", X.checksum, X.checksum);
+#endif
+#if LOAD_DEBUG
+ printf("____________________________________________\n");
+ printf("\t dbuffer_now = %ld \n", now);
+ printf("\t dbuffer available = %ld \n", dbuffer_available);
+ printf("\t not_loadin = %ld \n", not_loadin);
+#endif
+
+ return now;
+}
+
+static void jump_2ep()
+{
+ BootArgs.ucVideoMode = 1;
+ BootArgs.ucComPort = 1;
+ BootArgs.ucBaudDivisor = 1;
+ BootArgs.ucPCIConfigType = 1; // do not fill with 0
+
+ BootArgs.dwSig = BOOTARG_SIG;
+ BootArgs.dwLen = sizeof(BootArgs);
+
+ if(BootArgs.ucVideoMode == 0)
+ {
+ BootArgs.cxDisplayScreen = 640;
+ BootArgs.cyDisplayScreen = 480;
+ BootArgs.cxPhysicalScreen = 640;
+ BootArgs.cyPhysicalScreen = 480;
+ BootArgs.bppScreen = 16;
+ BootArgs.cbScanLineLength = 1024;
+ BootArgs.pvFlatFrameBuffer = 0x800a0000; // ollie say 0x98000000
+ }
+ else if(BootArgs.ucVideoMode != 0xFF)
+ {
+ BootArgs.cxDisplayScreen = 0;
+ BootArgs.cyDisplayScreen = 0;
+ BootArgs.cxPhysicalScreen = 0;
+ BootArgs.cyPhysicalScreen = 0;
+ BootArgs.bppScreen = 0;
+ BootArgs.cbScanLineLength = 0;
+ BootArgs.pvFlatFrameBuffer = 0;
+ }
+
+ ep = phys_to_virt(BOOT_ARG_PTR_LOCATION);
+ *ep= virt_to_phys(&BootArgs);
+ xstart32(entry);
+}
diff --git a/src/arch/i386/drivers/net/undi.c b/src/arch/i386/drivers/net/undi.c
new file mode 100644
index 00000000..084fc18a
--- /dev/null
+++ b/src/arch/i386/drivers/net/undi.c
@@ -0,0 +1,1458 @@
+/**************************************************************************
+Etherboot - BOOTP/TFTP Bootstrap Program
+UNDI NIC driver for Etherboot
+
+This file Copyright (C) 2003 Michael Brown <mbrown@fensystems.co.uk>
+of Fen Systems Ltd. (http://www.fensystems.co.uk/). All rights
+reserved.
+
+$Id$
+***************************************************************************/
+
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2, or (at
+ * your option) any later version.
+ */
+
+#ifdef PCBIOS
+
+/* to get some global routines like printf */
+#include "etherboot.h"
+/* to get the interface to the body of the program */
+#include "nic.h"
+/* to get the PCI support functions, if this is a PCI NIC */
+#include "pci.h"
+/* UNDI and PXE defines. Includes pxe.h. */
+#include "undi.h"
+/* 8259 PIC defines */
+#include "pic8259.h"
+/* Real-mode calls */
+#include "realmode.h"
+/* E820 map mangler */
+#include "hidemem.h"
+
+/* NIC specific static variables go here */
+static undi_t undi = {
+ .pnp_bios = NULL,
+ .rom = NULL,
+ .undi_rom_id = NULL,
+ .pxe = NULL,
+ .pxs = NULL,
+ .xmit_data = NULL,
+ .base_mem_data = NULL,
+ .driver_code = NULL,
+ .driver_code_size = 0,
+ .driver_data = NULL,
+ .driver_data_size = 0,
+ .xmit_buffer = NULL,
+ .prestarted = 0,
+ .started = 0,
+ .initialized = 0,
+ .opened = 0,
+ .pci = { 0, 0, 0, NULL, 0, 0, 0, 0, 0, 0, 0, NULL },
+ .irq = IRQ_NONE
+};
+
+/* Function prototypes */
+static int allocate_base_mem_data ( void );
+static int free_base_mem_data ( void );
+static int eb_pxenv_undi_shutdown ( void );
+static int eb_pxenv_stop_undi ( void );
+static int undi_unload_base_code ( void );
+static int undi_full_shutdown ( void );
+
+/* Trivial/nontrivial IRQ handler selection */
+#ifdef UNDI_NONTRIVIAL_IRQ
+static void nontrivial_irq_handler ( void );
+static void nontrivial_irq_handler_end ( void );
+static int install_nontrivial_irq_handler ( irq_t irq );
+static int remove_nontrivial_irq_handler ( irq_t irq );
+static int nontrivial_irq_triggered ( irq_t irq );
+static int copy_nontrivial_irq_handler ( void *target, size_t target_size );
+#define NONTRIVIAL_IRQ_HANDLER_SIZE FRAGMENT_SIZE(nontrivial_irq_handler)
+#define install_undi_irq_handler(irq) install_nontrivial_irq_handler(irq)
+#define remove_undi_irq_handler(irq) remove_nontrivial_irq_handler(irq)
+#define undi_irq_triggered(irq) nontrivial_irq_triggered(irq)
+#define UNDI_IRQ_HANDLER_SIZE NONTRIVIAL_IRQ_HANDLER_SIZE
+#define copy_undi_irq_handler(dest,size) copy_nontrivial_irq_handler(dest,size)
+#else
+#define install_undi_irq_handler(irq) install_trivial_irq_handler(irq)
+#define remove_undi_irq_handler(irq) remove_trivial_irq_handler(irq)
+#define undi_irq_triggered(irq) trivial_irq_triggered(irq)
+#define UNDI_IRQ_HANDLER_SIZE TRIVIAL_IRQ_HANDLER_SIZE
+#define copy_undi_irq_handler(dest,size) copy_trivial_irq_handler(dest,size)
+#endif /* UNDI_NONTRIVIAL_IRQ */
+
+/* Size of variable-length data in base_mem_data */
+#define BASE_MEM_VARDATA_SIZE ( UNDI_IRQ_HANDLER_SIZE > e820mangler_size ? \
+ UNDI_IRQ_HANDLER_SIZE : e820mangler_size )
+
+/**************************************************************************
+ * Utility functions
+ **************************************************************************/
+
+/* Checksum a block.
+ */
+
+static uint8_t checksum ( void *block, size_t size ) {
+ uint8_t sum = 0;
+ uint16_t i = 0;
+ for ( i = 0; i < size; i++ ) {
+ sum += ( ( uint8_t * ) block )[i];
+ }
+ return sum;
+}
+
+/* Print the status of a !PXE structure
+ */
+
+static void pxe_dump ( void ) {
+ printf ( "API %hx:%hx St %hx:%hx UD %hx:%hx UC %hx:%hx "
+ "BD %hx:%hx BC %hx:%hx\n",
+ undi.pxe->EntryPointSP.segment, undi.pxe->EntryPointSP.offset,
+ undi.pxe->Stack.Seg_Addr, undi.pxe->Stack.Seg_Size,
+ undi.pxe->UNDIData.Seg_Addr, undi.pxe->UNDIData.Seg_Size,
+ undi.pxe->UNDICode.Seg_Addr, undi.pxe->UNDICode.Seg_Size,
+ undi.pxe->BC_Data.Seg_Addr, undi.pxe->BC_Data.Seg_Size,
+ undi.pxe->BC_Code.Seg_Addr, undi.pxe->BC_Code.Seg_Size );
+}
+
+/* Allocate/free space for structures that must reside in base memory
+ */
+
+static int allocate_base_mem_data ( void ) {
+ /* Allocate space in base memory.
+ * Initialise pointers to base memory structures.
+ */
+ if ( undi.base_mem_data == NULL ) {
+ undi.base_mem_data =
+ allot_base_memory ( sizeof(undi_base_mem_data_t) +
+ BASE_MEM_VARDATA_SIZE );
+ if ( undi.base_mem_data == NULL ) {
+ printf ( "Failed to allocate base memory\n" );
+ free_base_mem_data();
+ return 0;
+ }
+ memset ( undi.base_mem_data, 0, sizeof(undi_base_mem_data_t) );
+ undi.pxs = &undi.base_mem_data->pxs;
+ undi.xmit_data = &undi.base_mem_data->xmit_data;
+ undi.xmit_buffer = undi.base_mem_data->xmit_buffer;
+ }
+ return 1;
+}
+
+static int free_base_mem_data ( void ) {
+ if ( undi.base_mem_data != NULL ) {
+ forget_base_memory ( undi.base_mem_data,
+ sizeof(undi_base_mem_data_t) +
+ BASE_MEM_VARDATA_SIZE );
+ undi.base_mem_data = NULL;
+ undi.pxs = NULL;
+ undi.xmit_data = NULL;
+ undi.xmit_buffer = NULL;
+ copy_undi_irq_handler ( NULL, 0 );
+ }
+ return 1;
+}
+
+static void assemble_firing_squad ( firing_squad_lineup_t *lineup,
+ void *start, size_t size,
+ firing_squad_shoot_t shoot ) {
+ int target;
+ int index;
+ int bit;
+ int start_kb = virt_to_phys(start) >> 10;
+ int end_kb = ( virt_to_phys(start+size) + (1<<10) - 1 ) >> 10;
+
+ for ( target = start_kb; target <= end_kb; target++ ) {
+ index = FIRING_SQUAD_TARGET_INDEX ( target );
+ bit = FIRING_SQUAD_TARGET_BIT ( target );
+ lineup->targets[index] = ( shoot << bit ) |
+ ( lineup->targets[index] & ~( 1 << bit ) );
+ }
+}
+
+static void shoot_targets ( firing_squad_lineup_t *lineup ) {
+ int shoot_this_target = 0;
+ int shoot_last_target = 0;
+ int start_target = 0;
+ int target;
+
+ for ( target = 0; target <= 640; target++ ) {
+ shoot_this_target = ( target == 640 ? 0 :
+ ( 1 << FIRING_SQUAD_TARGET_BIT(target) ) &
+ lineup->targets[FIRING_SQUAD_TARGET_INDEX(target)] );
+ if ( shoot_this_target && !shoot_last_target ) {
+ start_target = target;
+ } else if ( shoot_last_target && !shoot_this_target ) {
+ size_t range_size = ( target - start_target ) << 10;
+ forget_base_memory ( phys_to_virt( start_target<<10 ),
+ range_size );
+ }
+ shoot_last_target = shoot_this_target;
+ }
+}
+
+/* Debug macros
+ */
+
+#ifdef TRACE_UNDI
+#define DBG(...) printf ( __VA_ARGS__ )
+#else
+#define DBG(...)
+#endif
+
+#define UNDI_STATUS(pxs) ( (pxs)->Status == PXENV_EXIT_SUCCESS ? \
+ "SUCCESS" : \
+ ( (pxs)->Status == PXENV_EXIT_FAILURE ? \
+ "FAILURE" : "UNKNOWN" ) )
+
+/**************************************************************************
+ * Base memory scanning functions
+ **************************************************************************/
+
+/* Locate the $PnP structure indicating a PnP BIOS.
+ */
+
+static int hunt_pnp_bios ( void ) {
+ uint32_t off = 0x10000;
+
+ printf ( "Hunting for PnP BIOS..." );
+ while ( off > 0 ) {
+ off -= 16;
+ undi.pnp_bios = (pnp_bios_t *) phys_to_virt ( 0xf0000 + off );
+ if ( undi.pnp_bios->signature == PNP_BIOS_SIGNATURE ) {
+ printf ( "found $PnP at f000:%hx...", off );
+ if ( checksum(undi.pnp_bios,sizeof(pnp_bios_t)) !=0) {
+ printf ( "invalid checksum\n..." );
+ continue;
+ }
+ printf ( "ok\n" );
+ return 1;
+ }
+ }
+ printf ( "none found\n" );
+ undi.pnp_bios = NULL;
+ return 0;
+}
+
+/* Locate the !PXE structure indicating a loaded UNDI driver.
+ */
+
+static int hunt_pixie ( void ) {
+ static uint32_t ptr = 0;
+ pxe_t *pxe = NULL;
+
+ printf ( "Hunting for pixies..." );
+ if ( ptr == 0 ) ptr = 0xa0000;
+ while ( ptr > 0x10000 ) {
+ ptr -= 16;
+ pxe = (pxe_t *) phys_to_virt ( ptr );
+ if ( memcmp ( pxe->Signature, "!PXE", 4 ) == 0 ) {
+ printf ( "found !PXE at %x...", ptr );
+ if ( checksum ( pxe, sizeof(pxe_t) ) != 0 ) {
+ printf ( "invalid checksum\n..." );
+ continue;
+ }
+ if ( ptr < get_free_base_memory() ) {
+ printf ( "in free base memory!\n\n"
+ "WARNING: a valid !PXE structure was "
+ "found in an area of memory marked "
+ "as free!\n\n" );
+ undi.pxe = pxe;
+ pxe_dump();
+ undi.pxe = NULL;
+ printf ( "\nIgnoring and continuing, but this "
+ "may cause problems later!\n\n" );
+ continue;
+ }
+ printf ( "ok\n" );
+ undi.pxe = pxe;
+ pxe_dump();
+ printf ( "Resetting pixie...\n" );
+ undi_unload_base_code();
+ eb_pxenv_stop_undi();
+ pxe_dump();
+ return 1;
+ }
+ }
+ printf ( "none found\n" );
+ ptr = 0;
+ return 0;
+}
+
+/* Locate PCI PnP ROMs.
+ */
+
+static int hunt_rom ( void ) {
+ static uint32_t ptr = 0;
+
+ /* If we are not a PCI device, we cannot search for a ROM that
+ * matches us (?)
+ */
+ if ( ! undi.pci.vendor )
+ return 0;
+
+ printf ( "Hunting for ROMs..." );
+ if ( ptr == 0 ) ptr = 0x100000;
+ while ( ptr > 0x0c0000 ) {
+ ptr -= 0x800;
+ undi.rom = ( rom_t * ) phys_to_virt ( ptr );
+ if ( undi.rom->signature == ROM_SIGNATURE ) {
+ pcir_header_t *pcir_header = NULL;
+ pnp_header_t *pnp_header = NULL;
+
+ printf ( "found 55AA at %x...", ptr );
+ if ( undi.rom->pcir_off == 0 ) {
+ printf ( "not a PCI ROM\n..." );
+ continue;
+ }
+ pcir_header = (pcir_header_t*)( ( void * ) undi.rom +
+ undi.rom->pcir_off );
+ if ( pcir_header->signature != PCIR_SIGNATURE ) {
+ printf ( "invalid PCI signature\n..." );
+ continue;
+ }
+ printf ( "PCI:%hx:%hx...", pcir_header->vendor_id,
+ pcir_header->device_id );
+ if ( ( pcir_header->vendor_id != undi.pci.vendor ) ||
+ ( pcir_header->device_id != undi.pci.dev_id ) ) {
+ printf ( "not me (%hx:%hx)\n...",
+ undi.pci.vendor,
+ undi.pci.dev_id );
+ continue;
+ }
+ if ( undi.rom->pnp_off == 0 ) {
+ printf ( "not a PnP ROM\n..." );
+ continue;
+ }
+ pnp_header = (pnp_header_t*)( ( void * ) undi.rom +
+ undi.rom->pnp_off );
+ if ( pnp_header->signature != PNP_SIGNATURE ) {
+ printf ( "invalid $PnP signature\n..." );
+ continue;
+ }
+ if ( checksum(pnp_header,sizeof(pnp_header_t)) != 0 ) {
+ printf ( "invalid PnP checksum\n..." );
+ continue;
+ }
+ printf ( "ok\nROM contains %s by %s\n",
+ pnp_header->product_str_off==0 ? "(unknown)" :
+ (void*)undi.rom+pnp_header->product_str_off,
+ pnp_header->manuf_str_off==0 ? "(unknown)" :
+ (void*)undi.rom+pnp_header->manuf_str_off );
+ return 1;
+ }
+ }
+ printf ( "none found\n" );
+ ptr = 0;
+ undi.rom = NULL;
+ return 0;
+}
+
+/* Locate ROMs containing UNDI drivers.
+ */
+
+static int hunt_undi_rom ( void ) {
+ while ( hunt_rom() ) {
+ if ( undi.rom->undi_rom_id_off == 0 ) {
+ printf ( "Not a PXE ROM\n" );
+ continue;
+ }
+ undi.undi_rom_id = (undi_rom_id_t *)
+ ( (void *)undi.rom + undi.rom->undi_rom_id_off );
+ if ( undi.undi_rom_id->signature != UNDI_SIGNATURE ) {
+ printf ( "Invalid UNDI signature\n" );
+ continue;
+ }
+ if ( checksum ( undi.undi_rom_id,
+ undi.undi_rom_id->struct_length ) != 0 ) {
+ printf ( "Invalid checksum\n" );
+ continue;
+ }
+ printf ( "Located UNDI ROM supporting revision %d.%d.%d\n",
+ undi.undi_rom_id->undi_rev[2],
+ undi.undi_rom_id->undi_rev[1],
+ undi.undi_rom_id->undi_rev[0] );
+ return 1;
+ }
+ return 0;
+}
+
+/**************************************************************************
+ * Low-level UNDI API call wrappers
+ **************************************************************************/
+
+/* Make a real-mode UNDI API call to the UNDI routine at
+ * routine_seg:routine_off, passing in three uint16 parameters on the
+ * real-mode stack.
+ */
+
+static PXENV_EXIT_t _undi_call ( uint16_t routine_seg,
+ uint16_t routine_off, uint16_t st0,
+ uint16_t st1, uint16_t st2 ) {
+ PXENV_EXIT_t ret = PXENV_EXIT_FAILURE;
+ struct {
+ segoff_t routine;
+ uint16_t st0;
+ uint16_t st1;
+ uint16_t st2;
+ } PACKED in_stack = {
+ { routine_off, routine_seg }, st0, st1, st2
+ };
+
+ RM_FRAGMENT(rm_undi_call,
+ "popw %di\n\t" /* %es:di = routine */
+ "popw %es\n\t"
+ "pushw %cs\n\t" /* set up return address */
+ "call 1f\n\t1:popw %bx\n\t"
+ "leaw (2f-1b)(%bx), %ax\n\t"
+ "pushw %ax\n\t"
+ "pushw %es\n\t" /* routine address to stack */
+ "pushw %di\n\t"
+ "lret\n\t" /* calculated lcall */
+ "\n2:\n\t" /* continuation point */
+ );
+
+ /* Parameters are left on stack: set out_stack = in_stack */
+ ret = real_call ( rm_undi_call, &in_stack, &in_stack );
+
+ /* UNDI API calls may rudely change the status of A20 and not
+ * bother to restore it afterwards. Intel is known to be
+ * guilty of this.
+ *
+ * Note that we will return to this point even if A20 gets
+ * screwed up by the UNDI driver, because Etherboot always
+ * resides in an even megabyte of RAM.
+ */
+ gateA20_set();
+
+ return ret;
+}
+
+/* Make a real-mode call to the UNDI loader routine at
+ * routine_seg:routine_off, passing in the seg:off address of a
+ * pxenv_structure on the real-mode stack.
+ */
+
+static int undi_call_loader ( void ) {
+ PXENV_EXIT_t pxenv_exit = PXENV_EXIT_FAILURE;
+
+ /* Hide Etherboot around the loader, so that the PXE stack
+ * doesn't trash our memory areas
+ */
+ install_e820mangler ( undi.base_mem_data->e820mangler );
+ hide_etherboot();
+ pxenv_exit = _undi_call ( SEGMENT( undi.rom ),
+ undi.undi_rom_id->undi_loader_off,
+ OFFSET( undi.pxs ),
+ SEGMENT( undi.pxs ),
+ 0 /* Unused for UNDI loader API */ );
+ if ( !unhide_etherboot() ) {
+ printf ( "FATAL: corrupt INT15\n" );
+ return 0;
+ }
+
+ /* Return 1 for success, to be consistent with other routines */
+ if ( pxenv_exit == PXENV_EXIT_SUCCESS ) return 1;
+ printf ( "UNDI loader call failed with status %#hx\n",
+ undi.pxs->Status );
+ return 0;
+}
+
+/* Make a real-mode UNDI API call, passing in the opcode and the
+ * seg:off address of a pxenv_structure on the real-mode stack.
+ *
+ * Two versions: undi_call() will automatically report any failure
+ * codes, undi_call_silent() will not.
+ */
+
+static int undi_call_silent ( uint16_t opcode ) {
+ PXENV_EXIT_t pxenv_exit = PXENV_EXIT_FAILURE;
+
+ pxenv_exit = _undi_call ( undi.pxe->EntryPointSP.segment,
+ undi.pxe->EntryPointSP.offset,
+ opcode,
+ OFFSET( undi.pxs ),
+ SEGMENT( undi.pxs ) );
+ /* Return 1 for success, to be consistent with other routines */
+ return pxenv_exit == PXENV_EXIT_SUCCESS ? 1 : 0;
+}
+
+static int undi_call ( uint16_t opcode ) {
+ if ( undi_call_silent ( opcode ) ) return 1;
+ printf ( "UNDI API call %#hx failed with status %#hx\n",
+ opcode, undi.pxs->Status );
+ return 0;
+}
+
+#ifdef UNDI_NONTRIVIAL_IRQ
+/* IRQ handler that actually calls PXENV_UNDI_ISR. It's probably
+ * better to use the trivial IRQ handler, since this seems to work for
+ * just about all known NICs and doesn't involve making a PXE API call
+ * in interrupt context.
+ *
+ * This routine is mainly used for testing the Etherboot PXE stack's
+ * ability to be called in interrupt context. It is not compiled in
+ * by default.
+ *
+ * This code has fewer safety checks than those in the
+ * trivial_irq_handler routines. These are omitted because this code
+ * is not intended for mainstream use.
+ */
+
+uint16_t nontrivial_irq_previous_trigger_count = 0;
+
+static int copy_nontrivial_irq_handler ( void *target,
+ size_t target_size __unused ) {
+ RM_FRAGMENT(nontrivial_irq_handler,
+ /* Will be installed on a paragraph boundary, so access variables
+ * using %cs:(xxx-irqstart)
+ */
+ "\n\t"
+ "irqstart:\n\t"
+ /* Fields here must match those in undi_irq_handler_t */
+ "chain_to:\t.word 0,0\n\t"
+ "irq_chain:\t.byte 0,0,0,0\n\t"
+ "entry:\t.word 0,0\n\t"
+ "count_all:\t.word 0\n\t"
+ "count_ours:\t.word 0\n\t"
+ "undi_isr:\n\t"
+ "undi_isr_Status:\t.word 0\n\t"
+ "undi_isr_FuncFlag:\t.word 0\n\t"
+ "undi_isr_others:\t.word 0,0,0,0,0,0\n\t"
+ "handler:\n\t"
+ /* Assume that PXE stack will corrupt everything */
+ "pushal\n\t"
+ "push %ds\n\t"
+ "push %es\n\t"
+ "push %fs\n\t"
+ "push %gs\n\t"
+ /* Set DS == CS */
+ "pushw %cs\n\t"
+ "popw %ds\n\t"
+ /* Set up parameters for call */
+ "movw $" RM_STR(PXENV_UNDI_ISR_IN_START) ", %ds:(undi_isr_FuncFlag-irqstart)\n\t"
+ "pushw %cs\n\t"
+ "popw %es\n\t"
+ "movw $(undi_isr-irqstart), %di\n\t"
+ "movw $" RM_STR(PXENV_UNDI_ISR) ", %bx\n\t"
+ "pushw %es\n\t" /* Registers for PXENV+, stack for !PXE */
+ "pushw %di\n\t"
+ "pushw %bx\n\t"
+ /* Make PXE API call */
+ "lcall *%ds:(entry-irqstart)\n\t"
+ "addw $6, %sp\n\t"
+ /* Set DS == CS */
+ "pushw %cs\n\t"
+ "popw %ds\n\t"
+ /* Check return status to see if it's one of our interrupts */
+ "cmpw $" RM_STR(PXENV_STATUS_SUCCESS) ", %cs:(undi_isr_Status-irqstart)\n\t"
+ "jne 1f\n\t"
+ "cmpw $" RM_STR(PXENV_UNDI_ISR_OUT_OURS) ", %cs:(undi_isr_FuncFlag-irqstart)\n\t"
+ "jne 1f\n\t"
+ /* Increment count_ours if so */
+ "incw %ds:(count_ours-irqstart)\n\t"
+ "1:\n\t"
+ /* Increment count_all anyway */
+ "incw %ds:(count_all-irqstart)\n\t"
+ /* Restore registers and return */
+ "popw %gs\n\t"
+ "popw %fs\n\t"
+ "popw %es\n\t"
+ "popw %ds\n\t"
+ "popal\n\t"
+ "\n\t"
+ /* Chain to acknowledge the interrupt */
+ "cmpb $0, %cs:(irq_chain-irqstart)\n\t"
+ "jz 2f\n\t"
+ "ljmp %cs:(chain_to-irqstart)\n\t"
+ "2:\n\t"
+ "\n\t"
+ "iret\n\t"
+ "\n\t"
+ );
+
+ /* Copy handler */
+ memcpy ( target, nontrivial_irq_handler, NONTRIVIAL_IRQ_HANDLER_SIZE );
+
+ return 1;
+}
+
+static int install_nontrivial_irq_handler ( irq_t irq ) {
+ undi_irq_handler_t *handler =
+ &undi.base_mem_data->nontrivial_irq_handler;
+ segoff_t isr_segoff;
+
+ printf ( "WARNING: using non-trivial IRQ handler [EXPERIMENTAL]\n" );
+ /*
+ * This code is deliberately quick and dirty. The whole
+ * nontrivial IRQ stuff is only present in order to test out
+ * calling our PXE stack in interrupt context. Do NOT use
+ * this in production code.
+ */
+
+ disable_irq ( irq );
+ handler->count_all = 0;
+ handler->count_ours = 0;
+ handler->entry = undi.pxe->EntryPointSP;
+ nontrivial_irq_previous_trigger_count = 0;
+ isr_segoff.segment = SEGMENT(handler);
+ isr_segoff.offset = (void*)&handler->code - (void*)handler;
+ install_irq_handler( irq, &isr_segoff,
+ &handler->irq_chain, &handler->chain_to);
+ enable_irq ( irq );
+
+ return 1;
+}
+
+static int remove_nontrivial_irq_handler ( irq_t irq ) {
+ undi_irq_handler_t *handler =
+ &undi.base_mem_data->nontrivial_irq_handler;
+ segoff_t isr_segoff;
+
+ isr_segoff.segment = SEGMENT(handler);
+ isr_segoff.offset = (char*)&handler->code - (char*)handler;
+ remove_irq_handler( irq, &isr_segoff,
+ &handler->irq_chain, &handler->chain_to);
+ return 1;
+}
+
+static int nontrivial_irq_triggered ( irq_t irq __unused ) {
+ undi_irq_handler_t *handler =
+ &undi.base_mem_data->nontrivial_irq_handler;
+ uint16_t nontrivial_irq_this_trigger_count = handler->count_ours;
+ int triggered = ( nontrivial_irq_this_trigger_count -
+ nontrivial_irq_previous_trigger_count );
+
+ nontrivial_irq_previous_trigger_count =
+ nontrivial_irq_this_trigger_count;
+ return triggered ? 1 : 0;
+}
+
+static void nontrivial_irq_debug ( irq_t irq ) {
+ undi_irq_handler_t *handler =
+ &undi.base_mem_data->nontrivial_irq_handler;
+
+ printf ( "IRQ %d triggered %d times (%d of which were ours)\n",
+ irq, handler->count_all, handler->count_ours );
+}
+#endif /* UNDI_NONTRIVIAL_IRQ */
+
+/**************************************************************************
+ * High-level UNDI API call wrappers
+ **************************************************************************/
+
+/* Install the UNDI driver from a located UNDI ROM.
+ */
+
+static int undi_loader ( void ) {
+ pxe_t *pxe = NULL;
+
+ if ( ! undi.pci.vendor ) {
+ printf ( "ERROR: attempted to call loader of an ISA ROM?\n" );
+ return 0;
+ }
+
+ /* AX contains PCI bus:devfn (PCI specification) */
+ undi.pxs->loader.ax = ( undi.pci.bus << 8 ) | undi.pci.devfn;
+ /* BX and DX set to 0xffff for non-ISAPnP devices
+ * (BIOS boot specification)
+ */
+ undi.pxs->loader.bx = 0xffff;
+ undi.pxs->loader.dx = 0xffff;
+ /* ES:DI points to PnP BIOS' $PnP structure
+ * (BIOS boot specification)
+ */
+ if ( undi.pnp_bios ) {
+ undi.pxs->loader.es = 0xf000;
+ undi.pxs->loader.di = virt_to_phys ( undi.pnp_bios ) - 0xf0000;
+ } else {
+ /* Set to a NULL pointer and hope that we don't need it */
+ undi.pxs->loader.es = 0x0000;
+ undi.pxs->loader.di = 0x0000;
+ }
+
+ /* Allocate space for UNDI driver's code and data segments */
+ undi.driver_code_size = undi.undi_rom_id->code_size;
+ undi.driver_code = allot_base_memory ( undi.driver_code_size );
+ if ( undi.driver_code == NULL ) {
+ printf ( "Could not allocate %d bytes for UNDI code segment\n",
+ undi.driver_code_size );
+ return 0;
+ }
+ undi.pxs->loader.undi_cs = SEGMENT( undi.driver_code );
+
+ undi.driver_data_size = undi.undi_rom_id->data_size;
+ undi.driver_data = allot_base_memory ( undi.driver_data_size );
+ if ( undi.driver_data == NULL ) {
+ printf ( "Could not allocate %d bytes for UNDI code segment\n",
+ undi.driver_data_size );
+ return 0;
+ }
+ undi.pxs->loader.undi_ds = SEGMENT( undi.driver_data );
+
+ printf ( "Installing UNDI driver code to %hx:0000, data at %hx:0000\n",
+ undi.pxs->loader.undi_cs, undi.pxs->loader.undi_ds );
+
+ /* Do the API call to install the loader */
+ if ( ! undi_call_loader () ) return 0;
+
+ pxe = VIRTUAL( undi.pxs->loader.undi_cs,
+ undi.pxs->loader.pxe_ptr.offset );
+ printf ( "UNDI driver created a pixie at %hx:%hx...",
+ undi.pxs->loader.undi_cs, undi.pxs->loader.pxe_ptr.offset );
+ if ( memcmp ( pxe->Signature, "!PXE", 4 ) != 0 ) {
+ printf ( "invalid signature\n" );
+ return 0;
+ }
+ if ( checksum ( pxe, sizeof(pxe_t) ) != 0 ) {
+ printf ( "invalid checksum\n" );
+ return 0;
+ }
+ printf ( "ok\n" );
+ undi.pxe = pxe;
+ pxe_dump();
+ return 1;
+}
+
+/* Start the UNDI driver.
+ */
+
+static int eb_pxenv_start_undi ( void ) {
+ int success = 0;
+
+ /* AX contains PCI bus:devfn (PCI specification) */
+ undi.pxs->start_undi.ax = ( undi.pci.bus << 8 ) | undi.pci.devfn;
+
+ /* BX and DX set to 0xffff for non-ISAPnP devices
+ * (BIOS boot specification)
+ */
+ undi.pxs->start_undi.bx = 0xffff;
+ undi.pxs->start_undi.dx = 0xffff;
+ /* ES:DI points to PnP BIOS' $PnP structure
+ * (BIOS boot specification)
+ */
+ if ( undi.pnp_bios ) {
+ undi.pxs->start_undi.es = 0xf000;
+ undi.pxs->start_undi.di =
+ virt_to_phys ( undi.pnp_bios ) - 0xf0000;
+ } else {
+ /* Set to a NULL pointer and hope that we don't need it */
+ undi.pxs->start_undi.es = 0x0000;
+ undi.pxs->start_undi.di = 0x0000;
+ }
+
+ DBG ( "PXENV_START_UNDI => AX=%hx BX=%hx DX=%hx ES:DI=%hx:%hx\n",
+ undi.pxs->start_undi.ax,
+ undi.pxs->start_undi.bx, undi.pxs->start_undi.dx,
+ undi.pxs->start_undi.es, undi.pxs->start_undi.di );
+ success = undi_call ( PXENV_START_UNDI );
+ DBG ( "PXENV_START_UNDI <= Status=%s\n", UNDI_STATUS(undi.pxs) );
+ if ( success ) undi.prestarted = 1;
+ return success;
+}
+
+static int eb_pxenv_undi_startup ( void ) {
+ int success = 0;
+
+ DBG ( "PXENV_UNDI_STARTUP => (void)\n" );
+ success = undi_call ( PXENV_UNDI_STARTUP );
+ DBG ( "PXENV_UNDI_STARTUP <= Status=%s\n", UNDI_STATUS(undi.pxs) );
+ if ( success ) undi.started = 1;
+ return success;
+}
+
+static int eb_pxenv_undi_cleanup ( void ) {
+ int success = 0;
+
+ DBG ( "PXENV_UNDI_CLEANUP => (void)\n" );
+ success = undi_call ( PXENV_UNDI_CLEANUP );
+ DBG ( "PXENV_UNDI_CLEANUP <= Status=%s\n", UNDI_STATUS(undi.pxs) );
+ return success;
+}
+
+static int eb_pxenv_undi_initialize ( void ) {
+ int success = 0;
+
+ undi.pxs->undi_initialize.ProtocolIni = 0;
+ memset ( &undi.pxs->undi_initialize.reserved, 0,
+ sizeof ( undi.pxs->undi_initialize.reserved ) );
+ DBG ( "PXENV_UNDI_INITIALIZE => ProtocolIni=%x\n" );
+ success = undi_call ( PXENV_UNDI_INITIALIZE );
+ DBG ( "PXENV_UNDI_INITIALIZE <= Status=%s\n", UNDI_STATUS(undi.pxs) );
+ if ( success ) undi.initialized = 1;
+ return success;
+}
+
+static int eb_pxenv_undi_shutdown ( void ) {
+ int success = 0;
+
+ DBG ( "PXENV_UNDI_SHUTDOWN => (void)\n" );
+ success = undi_call ( PXENV_UNDI_SHUTDOWN );
+ DBG ( "PXENV_UNDI_SHUTDOWN <= Status=%s\n", UNDI_STATUS(undi.pxs) );
+ if ( success ) {
+ undi.initialized = 0;
+ undi.started = 0;
+ }
+ return success;
+}
+
+static int eb_pxenv_undi_open ( void ) {
+ int success = 0;
+
+ undi.pxs->undi_open.OpenFlag = 0;
+ undi.pxs->undi_open.PktFilter = FLTR_DIRECTED | FLTR_BRDCST;
+
+ /* Multicast support not yet implemented */
+ undi.pxs->undi_open.R_Mcast_Buf.MCastAddrCount = 0;
+ DBG ( "PXENV_UNDI_OPEN => OpenFlag=%hx PktFilter=%hx "
+ "MCastAddrCount=%hx\n",
+ undi.pxs->undi_open.OpenFlag, undi.pxs->undi_open.PktFilter,
+ undi.pxs->undi_open.R_Mcast_Buf.MCastAddrCount );
+ success = undi_call ( PXENV_UNDI_OPEN );
+ DBG ( "PXENV_UNDI_OPEN <= Status=%s\n", UNDI_STATUS(undi.pxs) );
+ if ( success ) undi.opened = 1;
+ return success;
+}
+
+static int eb_pxenv_undi_close ( void ) {
+ int success = 0;
+
+ DBG ( "PXENV_UNDI_CLOSE => (void)\n" );
+ success = undi_call ( PXENV_UNDI_CLOSE );
+ DBG ( "PXENV_UNDI_CLOSE <= Status=%s\n", UNDI_STATUS(undi.pxs) );
+ if ( success ) undi.opened = 0;
+ return success;
+}
+
+static int eb_pxenv_undi_transmit_packet ( void ) {
+ int success = 0;
+ static const uint8_t broadcast[] = { 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF };
+
+ /* XMitFlag selects unicast / broadcast */
+ if ( memcmp ( undi.xmit_data->destaddr, broadcast,
+ sizeof(broadcast) ) == 0 ) {
+ undi.pxs->undi_transmit.XmitFlag = XMT_BROADCAST;
+ } else {
+ undi.pxs->undi_transmit.XmitFlag = XMT_DESTADDR;
+ }
+
+ /* Zero reserved dwords */
+ undi.pxs->undi_transmit.Reserved[0] = 0;
+ undi.pxs->undi_transmit.Reserved[1] = 0;
+
+ /* Segment:offset pointer to DestAddr in base memory */
+ undi.pxs->undi_transmit.DestAddr.segment =
+ SEGMENT( undi.xmit_data->destaddr );
+ undi.pxs->undi_transmit.DestAddr.offset =
+ OFFSET( undi.xmit_data->destaddr );
+
+ /* Segment:offset pointer to TBD in base memory */
+ undi.pxs->undi_transmit.TBD.segment = SEGMENT( &undi.xmit_data->tbd );
+ undi.pxs->undi_transmit.TBD.offset = OFFSET( &undi.xmit_data->tbd );
+
+ /* Use only the "immediate" part of the TBD */
+ undi.xmit_data->tbd.DataBlkCount = 0;
+
+ DBG ( "PXENV_UNDI_TRANSMIT_PACKET => Protocol=%hx XmitFlag=%hx ...\n"
+ "... DestAddr=%hx:%hx TBD=%hx:%hx ...\n",
+ undi.pxs->undi_transmit.Protocol,
+ undi.pxs->undi_transmit.XmitFlag,
+ undi.pxs->undi_transmit.DestAddr.segment,
+ undi.pxs->undi_transmit.DestAddr.offset,
+ undi.pxs->undi_transmit.TBD.segment,
+ undi.pxs->undi_transmit.TBD.offset );
+ DBG ( "... TBD { ImmedLength=%hx Xmit=%hx:%hx DataBlkCount=%hx }\n",
+ undi.xmit_data->tbd.ImmedLength,
+ undi.xmit_data->tbd.Xmit.segment,
+ undi.xmit_data->tbd.Xmit.offset,
+ undi.xmit_data->tbd.DataBlkCount );
+ success = undi_call ( PXENV_UNDI_TRANSMIT );
+ DBG ( "PXENV_UNDI_TRANSMIT_PACKET <= Status=%s\n",
+ UNDI_STATUS(undi.pxs) );
+ return success;
+}
+
+static int eb_pxenv_undi_set_station_address ( void ) {
+ /* This will spuriously fail on some cards. Ignore failures.
+ * We only ever use it to set the MAC address to the card's
+ * permanent value anyway, so it's a useless call (although we
+ * make it because PXE spec says we should).
+ */
+ DBG ( "PXENV_UNDI_SET_STATION_ADDRESS => "
+ "StationAddress=%!\n",
+ undi.pxs->undi_set_station_address.StationAddress );
+ undi_call_silent ( PXENV_UNDI_SET_STATION_ADDRESS );
+ DBG ( "PXENV_UNDI_SET_STATION_ADDRESS <= Status=%s\n",
+ UNDI_STATUS(undi.pxs) );
+ return 1;
+}
+
+static int eb_pxenv_undi_get_information ( void ) {
+ int success = 0;
+ memset ( undi.pxs, 0, sizeof ( undi.pxs ) );
+ DBG ( "PXENV_UNDI_GET_INFORMATION => (void)\n" );
+ success = undi_call ( PXENV_UNDI_GET_INFORMATION );
+ DBG ( "PXENV_UNDI_GET_INFORMATION <= Status=%s "
+ "BaseIO=%hx IntNumber=%hx ...\n"
+ "... MaxTranUnit=%hx HwType=%hx HwAddrlen=%hx ...\n"
+ "... CurrentNodeAddress=%! PermNodeAddress=%! ...\n"
+ "... ROMAddress=%hx RxBufCt=%hx TxBufCt=%hx\n",
+ UNDI_STATUS(undi.pxs),
+ undi.pxs->undi_get_information.BaseIo,
+ undi.pxs->undi_get_information.IntNumber,
+ undi.pxs->undi_get_information.MaxTranUnit,
+ undi.pxs->undi_get_information.HwType,
+ undi.pxs->undi_get_information.HwAddrLen,
+ undi.pxs->undi_get_information.CurrentNodeAddress,
+ undi.pxs->undi_get_information.PermNodeAddress,
+ undi.pxs->undi_get_information.ROMAddress,
+ undi.pxs->undi_get_information.RxBufCt,
+ undi.pxs->undi_get_information.TxBufCt );
+ return success;
+}
+
+static int eb_pxenv_undi_get_iface_info ( void ) {
+ int success = 0;
+
+ DBG ( "PXENV_UNDI_GET_IFACE_INFO => (void)\n" );
+ success = undi_call ( PXENV_UNDI_GET_IFACE_INFO );
+ DBG ( "PXENV_UNDI_GET_IFACE_INFO <= Status=%s IfaceType=%s ...\n"
+ "... LinkSpeed=%x ServiceFlags=%x\n",
+ UNDI_STATUS(undi.pxs),
+ undi.pxs->undi_get_iface_info.IfaceType,
+ undi.pxs->undi_get_iface_info.LinkSpeed,
+ undi.pxs->undi_get_iface_info.ServiceFlags );
+ return success;
+}
+
+static int eb_pxenv_undi_isr ( void ) {
+ int success = 0;
+
+ DBG ( "PXENV_UNDI_ISR => FuncFlag=%hx\n",
+ undi.pxs->undi_isr.FuncFlag );
+ success = undi_call ( PXENV_UNDI_ISR );
+ DBG ( "PXENV_UNDI_ISR <= Status=%s FuncFlag=%hx BufferLength=%hx ...\n"
+ "... FrameLength=%hx FrameHeaderLength=%hx Frame=%hx:%hx "
+ "ProtType=%hhx ...\n... PktType=%hhx\n",
+ UNDI_STATUS(undi.pxs), undi.pxs->undi_isr.FuncFlag,
+ undi.pxs->undi_isr.BufferLength,
+ undi.pxs->undi_isr.FrameLength,
+ undi.pxs->undi_isr.FrameHeaderLength,
+ undi.pxs->undi_isr.Frame.segment,
+ undi.pxs->undi_isr.Frame.offset,
+ undi.pxs->undi_isr.ProtType,
+ undi.pxs->undi_isr.PktType );
+ return success;
+}
+
+static int eb_pxenv_stop_undi ( void ) {
+ int success = 0;
+
+ DBG ( "PXENV_STOP_UNDI => (void)\n" );
+ success = undi_call ( PXENV_STOP_UNDI );
+ DBG ( "PXENV_STOP_UNDI <= Status=%s\n", UNDI_STATUS(undi.pxs) );
+ if ( success ) undi.prestarted = 0;
+ return success;
+}
+
+static int eb_pxenv_unload_stack ( void ) {
+ int success = 0;
+
+ memset ( undi.pxs, 0, sizeof ( undi.pxs ) );
+ DBG ( "PXENV_UNLOAD_STACK => (void)\n" );
+ success = undi_call_silent ( PXENV_UNLOAD_STACK );
+ DBG ( "PXENV_UNLOAD_STACK <= Status=%s ...\n... (%s)\n",
+ UNDI_STATUS(undi.pxs),
+ ( undi.pxs->Status == PXENV_STATUS_SUCCESS ?
+ "base-code is ready to be removed" :
+ ( undi.pxs->Status == PXENV_STATUS_FAILURE ?
+ "the size of free base memory has been changed" :
+ ( undi.pxs->Status == PXENV_STATUS_KEEP_ALL ?
+ "the NIC interrupt vector has been changed" :
+ "UNEXPECTED STATUS CODE" ) ) ) );
+ return success;
+}
+
+static int eb_pxenv_stop_base ( void ) {
+ int success = 0;
+
+ DBG ( "PXENV_STOP_BASE => (void)\n" );
+ success = undi_call ( PXENV_STOP_BASE );
+ DBG ( "PXENV_STOP_BASE <= Status=%s\n", UNDI_STATUS(undi.pxs) );
+ return success;
+}
+
+/* Unload UNDI base code (if any present) and free memory.
+ */
+static int undi_unload_base_code ( void ) {
+ void *bc_code = VIRTUAL( undi.pxe->BC_Code.Seg_Addr, 0 );
+ size_t bc_code_size = undi.pxe->BC_Code.Seg_Size;
+ void *bc_data = VIRTUAL( undi.pxe->BC_Data.Seg_Addr, 0 );
+ size_t bc_data_size = undi.pxe->BC_Data.Seg_Size;
+ void *bc_stck = VIRTUAL( undi.pxe->Stack.Seg_Addr, 0 );
+ size_t bc_stck_size = undi.pxe->Stack.Seg_Size;
+ firing_squad_lineup_t lineup;
+
+ /* Since we never start the base code, the only time we should
+ * reach this is if we were loaded via PXE. There are many
+ * different and conflicting versions of the "correct" way to
+ * unload the PXE base code, several of which appear within
+ * the PXE specification itself. This one seems to work for
+ * our purposes.
+ *
+ * We always call PXENV_STOP_BASE and PXENV_UNLOAD_STACK even
+ * if the !PXE structure indicates that no base code is
+ * present. We do this for the case that there is a
+ * base-code-less UNDI driver loaded that has hooked some
+ * interrupts. If the base code really is absent, then these
+ * calls will fail, we will ignore the failure, and our
+ * subsequent memory-freeing code is robust enough to handle
+ * whatever's thrown at it.
+ */
+ eb_pxenv_stop_base();
+ eb_pxenv_unload_stack();
+ if ( ( undi.pxs->unload_stack.Status != PXENV_STATUS_SUCCESS ) &&
+ ( undi.pxs->unload_stack.Status != PXENV_STATUS_FAILURE ) &&
+ ( undi.pxe->BC_Code.Seg_Addr != 0 ) )
+ {
+ printf ( "Could not free memory allocated to PXE base code: "
+ "possible memory leak\n" );
+ return 0;
+ }
+ /* Free data structures. Forget what the PXE specification
+ * says about how to calculate the new size of base memory;
+ * basemem.c takes care of all that for us. Note that we also
+ * have to free the stack (even though PXE spec doesn't say
+ * anything about it) because nothing else is going to do so.
+ *
+ * Structures will almost certainly not be kB-aligned and
+ * there's a reasonable chance that the UNDI code or data
+ * portions will lie in the same kB as the base code. Since
+ * forget_base_memory works only in 1kB increments, this means
+ * we have to do some arcane trickery.
+ */
+ memset ( &lineup, 0, sizeof(lineup) );
+ if ( SEGMENT(bc_code) != 0 )
+ assemble_firing_squad( &lineup, bc_code, bc_code_size, SHOOT );
+ if ( SEGMENT(bc_data) != 0 )
+ assemble_firing_squad( &lineup, bc_data, bc_data_size, SHOOT );
+ if ( SEGMENT(bc_stck) != 0 )
+ assemble_firing_squad( &lineup, bc_stck, bc_stck_size, SHOOT );
+ /* Don't shoot any bits of the UNDI driver code or data */
+ assemble_firing_squad ( &lineup,
+ VIRTUAL(undi.pxe->UNDICode.Seg_Addr, 0),
+ undi.pxe->UNDICode.Seg_Size, DONTSHOOT );
+ assemble_firing_squad ( &lineup,
+ VIRTUAL(undi.pxe->UNDIData.Seg_Addr, 0),
+ undi.pxe->UNDIData.Seg_Size, DONTSHOOT );
+ shoot_targets ( &lineup );
+ undi.pxe->BC_Code.Seg_Addr = 0;
+ undi.pxe->BC_Data.Seg_Addr = 0;
+ undi.pxe->Stack.Seg_Addr = 0;
+
+ /* Free and reallocate our own base memory data structures, to
+ * allow the freed base-code blocks to be fully released.
+ */
+ free_base_mem_data();
+ if ( ! allocate_base_mem_data() ) {
+ printf ( "FATAL: memory unaccountably lost\n" );
+ while ( 1 ) {};
+ }
+
+ return 1;
+}
+
+/* UNDI full initialization
+ *
+ * This calls all the various UNDI initialization routines in sequence.
+ */
+
+static int undi_full_startup ( void ) {
+ if ( ! eb_pxenv_start_undi() ) return 0;
+ if ( ! eb_pxenv_undi_startup() ) return 0;
+ if ( ! eb_pxenv_undi_initialize() ) return 0;
+ if ( ! eb_pxenv_undi_get_information() ) return 0;
+ undi.irq = undi.pxs->undi_get_information.IntNumber;
+ copy_undi_irq_handler ( undi.base_mem_data->irq_handler,
+ UNDI_IRQ_HANDLER_SIZE );
+ if ( ! install_undi_irq_handler ( undi.irq ) ) {
+ undi.irq = IRQ_NONE;
+ return 0;
+ }
+ memmove ( &undi.pxs->undi_set_station_address.StationAddress,
+ &undi.pxs->undi_get_information.PermNodeAddress,
+ sizeof (undi.pxs->undi_set_station_address.StationAddress) );
+ if ( ! eb_pxenv_undi_set_station_address() ) return 0;
+ if ( ! eb_pxenv_undi_open() ) return 0;
+ return 1;
+}
+
+/* UNDI full shutdown
+ *
+ * This calls all the various UNDI shutdown routines in sequence and
+ * also frees any memory that it can.
+ */
+
+static int undi_full_shutdown ( void ) {
+ if ( undi.pxe != NULL ) {
+ /* In case we didn't allocate the driver's memory in the first
+ * place, try to grab the code and data segments and sizes
+ * from the !PXE structure.
+ */
+ if ( undi.driver_code == NULL ) {
+ undi.driver_code = VIRTUAL(undi.pxe->UNDICode.Seg_Addr,
+ 0 );
+ undi.driver_code_size = undi.pxe->UNDICode.Seg_Size;
+ }
+ if ( undi.driver_data == NULL ) {
+ undi.driver_data = VIRTUAL(undi.pxe->UNDIData.Seg_Addr,
+ 0 );
+ undi.driver_data_size = undi.pxe->UNDIData.Seg_Size;
+ }
+
+ /* Ignore errors and continue in the hope of shutting
+ * down anyway
+ */
+ if ( undi.opened ) eb_pxenv_undi_close();
+ if ( undi.started ) {
+ eb_pxenv_undi_cleanup();
+ /* We may get spurious UNDI API errors at this
+ * point. If startup() succeeded but
+ * initialize() failed then according to the
+ * spec, we should call shutdown(). However,
+ * some NICS will fail with a status code
+ * 0x006a (INVALID_STATE).
+ */
+ eb_pxenv_undi_shutdown();
+ }
+ if ( undi.irq != IRQ_NONE ) {
+ remove_undi_irq_handler ( undi.irq );
+ undi.irq = IRQ_NONE;
+ }
+ undi_unload_base_code();
+ if ( undi.prestarted ) {
+ eb_pxenv_stop_undi();
+ /* Success OR Failure indicates that memory
+ * can be freed. Any other status code means
+ * that it can't.
+ */
+ if (( undi.pxs->Status == PXENV_STATUS_KEEP_UNDI ) ||
+ ( undi.pxs->Status == PXENV_STATUS_KEEP_ALL ) ) {
+ printf ("Could not free memory allocated to "
+ "UNDI driver: possible memory leak\n");
+ return 0;
+ }
+ }
+ }
+ /* Free memory allocated to UNDI driver */
+ if ( undi.driver_code != NULL ) {
+ /* Clear contents in order to eliminate !PXE and PXENV
+ * signatures to prevent spurious detection via base
+ * memory scan.
+ */
+ memset ( undi.driver_code, 0, undi.driver_code_size );
+ forget_base_memory ( undi.driver_code, undi.driver_code_size );
+ undi.driver_code = NULL;
+ undi.driver_code_size = 0;
+ }
+ if ( undi.driver_data != NULL ) {
+ forget_base_memory ( undi.driver_data, undi.driver_data_size );
+ undi.driver_data = NULL;
+ undi.driver_data_size = 0;
+ }
+ /* !PXE structure now gone; memory freed */
+ undi.pxe = NULL;
+ return 1;
+}
+
+/**************************************************************************
+POLL - Wait for a frame
+***************************************************************************/
+static int undi_poll(struct nic *nic, int retrieve)
+{
+ /* Fun, fun, fun. UNDI drivers don't use polling; they use
+ * interrupts. We therefore cheat and pretend that an
+ * interrupt has occurred every time undi_poll() is called.
+ * This isn't too much of a hack; PCI devices share IRQs and
+ * so the first thing that a proper ISR should do is call
+ * PXENV_UNDI_ISR to determine whether or not the UNDI NIC
+ * generated the interrupt; there is no harm done by spurious
+ * calls to PXENV_UNDI_ISR. Similarly, we wouldn't be
+ * handling them any more rapidly than the usual rate of
+ * undi_poll() being called even if we did implement a full
+ * ISR. So it should work. Ha!
+ *
+ * Addendum (21/10/03). Some cards don't play nicely with
+ * this trick, so instead of doing it the easy way we have to
+ * go to all the hassle of installing a genuine interrupt
+ * service routine and dealing with the wonderful 8259
+ * Programmable Interrupt Controller. Joy.
+ */
+
+ /* See if a hardware interrupt has occurred since the last poll().
+ */
+ if ( ! undi_irq_triggered ( undi.irq ) ) return 0;
+
+ /* Given the frailty of PXE stacks, it's probably not safe to
+ * risk calling PXENV_UNDI_ISR with
+ * FuncFlag=PXENV_UNDI_ISR_START twice for the same interrupt,
+ * so we cheat slightly and assume that there is something
+ * ready to retrieve as long as an interrupt has occurred.
+ */
+ if ( ! retrieve ) return 1;
+
+#ifdef UNDI_NONTRIVIAL_IRQ
+ /* With the nontrivial IRQ handler, we have already called
+ * PXENV_UNDI_ISR with PXENV_UNDI_ISR_IN_START and determined
+ * that it is one of ours.
+ */
+#else
+ /* Ask the UNDI driver if this is "our" interrupt.
+ */
+ undi.pxs->undi_isr.FuncFlag = PXENV_UNDI_ISR_IN_START;
+ if ( ! eb_pxenv_undi_isr() ) return 0;
+ if ( undi.pxs->undi_isr.FuncFlag == PXENV_UNDI_ISR_OUT_NOT_OURS ) {
+ /* "Not our interrupt" translates to "no packet ready
+ * to read".
+ */
+ /* FIXME: Technically, we shouldn't be the one sending
+ * EOI. However, since our IRQ handlers don't yet
+ * support chaining, nothing else gets the chance to.
+ * One nice side-effect of doing this is that it means
+ * we can cheat and claim the timer interrupt as our
+ * NIC interrupt; it will be inefficient but will
+ * work.
+ */
+ send_specific_eoi ( undi.irq );
+ return 0;
+ }
+#endif
+
+ /* At this stage, the device should have cleared its interrupt
+ * line so we can send EOI to the 8259.
+ */
+ send_specific_eoi ( undi.irq );
+
+ /* We might have received a packet, or this might be a
+ * "transmit completed" interrupt. Zero nic->packetlen,
+ * increment whenever we receive a bit of a packet, test
+ * nic->packetlen when we're done to see whether or not we
+ * actually received anything.
+ */
+ nic->packetlen = 0;
+ undi.pxs->undi_isr.FuncFlag = PXENV_UNDI_ISR_IN_PROCESS;
+ if ( ! eb_pxenv_undi_isr() ) return 0;
+ while ( undi.pxs->undi_isr.FuncFlag != PXENV_UNDI_ISR_OUT_DONE ) {
+ switch ( undi.pxs->undi_isr.FuncFlag ) {
+ case PXENV_UNDI_ISR_OUT_TRANSMIT:
+ /* We really don't care about transmission complete
+ * interrupts.
+ */
+ break;
+ case PXENV_UNDI_ISR_OUT_BUSY:
+ /* This should never happen.
+ */
+ printf ( "UNDI ISR thinks it's being re-entered!\n"
+ "Aborting receive\n" );
+ return 0;
+ case PXENV_UNDI_ISR_OUT_RECEIVE:
+ /* Copy data to receive buffer */
+ memcpy ( nic->packet + nic->packetlen,
+ VIRTUAL( undi.pxs->undi_isr.Frame.segment,
+ undi.pxs->undi_isr.Frame.offset ),
+ undi.pxs->undi_isr.BufferLength );
+ nic->packetlen += undi.pxs->undi_isr.BufferLength;
+ break;
+ default:
+ printf ( "UNDI ISR returned bizzare status code %d\n",
+ undi.pxs->undi_isr.FuncFlag );
+ }
+ undi.pxs->undi_isr.FuncFlag = PXENV_UNDI_ISR_IN_GET_NEXT;
+ if ( ! eb_pxenv_undi_isr() ) return 0;
+ }
+ return nic->packetlen > 0 ? 1 : 0;
+}
+
+/**************************************************************************
+TRANSMIT - Transmit a frame
+***************************************************************************/
+static void undi_transmit(
+ struct nic *nic __unused,
+ const char *d, /* Destination */
+ unsigned int t, /* Type */
+ unsigned int s, /* size */
+ const char *p) /* Packet */
+{
+ /* Copy destination to buffer in base memory */
+ memcpy ( undi.xmit_data->destaddr, d, sizeof(MAC_ADDR) );
+
+ /* Translate packet type to UNDI packet type */
+ switch ( t ) {
+ case IP : undi.pxs->undi_transmit.Protocol = P_IP; break;
+ case ARP: undi.pxs->undi_transmit.Protocol = P_ARP; break;
+ case RARP: undi.pxs->undi_transmit.Protocol = P_RARP; break;
+ default: printf ( "Unknown packet type %hx\n", t );
+ return;
+ }
+
+ /* Store packet length in TBD */
+ undi.xmit_data->tbd.ImmedLength = s;
+
+ /* Check to see if data to be transmitted is currently in base
+ * memory. If not, allocate temporary storage in base memory
+ * and copy it there.
+ */
+ if ( SEGMENT( p ) <= 0xffff ) {
+ undi.xmit_data->tbd.Xmit.segment = SEGMENT( p );
+ undi.xmit_data->tbd.Xmit.offset = OFFSET( p );
+ } else {
+ memcpy ( undi.xmit_buffer, p, s );
+ undi.xmit_data->tbd.Xmit.segment = SEGMENT( undi.xmit_buffer );
+ undi.xmit_data->tbd.Xmit.offset = OFFSET( undi.xmit_buffer );
+ }
+
+ eb_pxenv_undi_transmit_packet();
+}
+
+/**************************************************************************
+DISABLE - Turn off ethernet interface
+***************************************************************************/
+static void undi_disable ( struct dev *dev __unused ) {
+ undi_full_shutdown();
+ free_base_mem_data();
+}
+
+/**************************************************************************
+PROBE - Look for an adapter, this routine's visible to the outside
+***************************************************************************/
+
+/* Locate an UNDI driver by first scanning through base memory for an
+ * installed driver and then by scanning for UNDI ROMs and attempting
+ * to install their drivers.
+ */
+
+static int hunt_pixies_and_undi_roms ( void ) {
+ static uint8_t hunt_type = HUNT_FOR_PIXIES;
+
+ if ( hunt_type == HUNT_FOR_PIXIES ) {
+ if ( hunt_pixie() ) {
+ return 1;
+ }
+ }
+ hunt_type = HUNT_FOR_UNDI_ROMS;
+ while ( hunt_undi_rom() ) {
+ if ( undi_loader() ) {
+ return 1;
+ }
+ undi_full_shutdown(); /* Free any allocated memory */
+ }
+ hunt_type = HUNT_FOR_PIXIES;
+ return 0;
+}
+
+/* The actual Etherboot probe routine.
+ */
+
+static int undi_probe(struct dev *dev, struct pci_device *pci)
+{
+ struct nic *nic = (struct nic *)dev;
+
+ /* Zero out global undi structure */
+ memset ( &undi, 0, sizeof(undi) );
+
+ /* Store PCI parameters; we will need them to initialize the
+ * UNDI driver later. If not a PCI device, leave as 0.
+ */
+ if ( pci ) {
+ memcpy ( &undi.pci, pci, sizeof(undi.pci) );
+ }
+
+ /* Find the BIOS' $PnP structure */
+ if ( ! hunt_pnp_bios() ) {
+ /* Not all PXE stacks actually insist on a PnP BIOS.
+ * In particular, an Etherboot PXE stack will work
+ * just fine without one.
+ *
+ * We used to make this a fatal error, but now we just
+ * warn and continue. Note that this is necessary in
+ * order to be able to debug the Etherboot PXE stack
+ * under Bochs, since Bochs' BIOS is non-PnP.
+ */
+ printf ( "WARNING: No PnP BIOS found\n" );
+ }
+
+ /* Allocate base memory data structures */
+ if ( ! allocate_base_mem_data() ) return 0;
+
+ /* Search thoroughly for UNDI drivers */
+ for ( ; hunt_pixies_and_undi_roms(); undi_full_shutdown() ) {
+ /* Try to initialise UNDI driver */
+ printf ( "Initializing UNDI driver. Please wait...\n" );
+ if ( ! undi_full_startup() ) {
+ if ( undi.pxs->Status ==
+ PXENV_STATUS_UNDI_MEDIATEST_FAILED ) {
+ printf ( "Cable not connected (code %#hx)\n",
+ PXENV_STATUS_UNDI_MEDIATEST_FAILED );
+ }
+ continue;
+ }
+ /* Basic information: MAC, IO addr, IRQ */
+ if ( ! eb_pxenv_undi_get_information() ) continue;
+ printf ( "Initialized UNDI NIC with IO %#hx, IRQ %d, MAC %!\n",
+ undi.pxs->undi_get_information.BaseIo,
+ undi.pxs->undi_get_information.IntNumber,
+ undi.pxs->undi_get_information.CurrentNodeAddress );
+ /* Fill out MAC address in nic structure */
+ memcpy ( nic->node_addr,
+ undi.pxs->undi_get_information.CurrentNodeAddress,
+ ETH_ALEN );
+ /* More diagnostic information including link speed */
+ if ( ! eb_pxenv_undi_get_iface_info() ) continue;
+ printf ( "NDIS type %s interface at %d Mbps\n",
+ undi.pxs->undi_get_iface_info.IfaceType,
+ undi.pxs->undi_get_iface_info.LinkSpeed / 1000000 );
+ dev->disable = undi_disable;
+ nic->poll = undi_poll;
+ nic->transmit = undi_transmit;
+ return 1;
+ }
+ undi_disable ( dev ); /* To free base memory structures */
+ return 0;
+}
+
+static int undi_isa_probe ( struct dev *dev,
+ unsigned short *probe_addrs __unused ) {
+ return undi_probe ( dev, NULL );
+}
+
+
+/* UNDI driver states that it is suitable for any PCI NIC (i.e. any
+ * PCI device of class PCI_CLASS_NETWORK_ETHERNET). If there are any
+ * obscure UNDI NICs that have the incorrect PCI class, add them to
+ * this list.
+ */
+static struct pci_id undi_nics[] = {
+ /* PCI_ROM(0x0000, 0x0000, "undi", "UNDI adaptor"), */
+};
+
+static struct pci_driver undi_driver __pci_driver = {
+ .type = NIC_DRIVER,
+ .name = "UNDI",
+ .probe = undi_probe,
+ .ids = undi_nics,
+ .id_count = sizeof(undi_nics)/sizeof(undi_nics[0]),
+ .class = PCI_CLASS_NETWORK_ETHERNET,
+};
+
+static struct isa_driver undi_isa_driver __isa_driver = {
+ .type = NIC_DRIVER,
+ .name = "UNDI",
+ .probe = undi_isa_probe,
+ .ioaddrs = 0,
+};
+
+#endif /* PCBIOS */
diff --git a/src/arch/i386/drivers/net/undi.h b/src/arch/i386/drivers/net/undi.h
new file mode 100644
index 00000000..3ba76c35
--- /dev/null
+++ b/src/arch/i386/drivers/net/undi.h
@@ -0,0 +1,178 @@
+/**************************************************************************
+Etherboot - BOOTP/TFTP Bootstrap Program
+UNDI NIC driver for Etherboot - header file
+
+This file Copyright (C) 2003 Michael Brown <mbrown@fensystems.co.uk>
+of Fen Systems Ltd. (http://www.fensystems.co.uk/). All rights
+reserved.
+
+$Id$
+***************************************************************************/
+
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2, or (at
+ * your option) any later version.
+ */
+
+#include "pxe.h"
+#include "pic8259.h"
+
+/* A union that can function as the parameter block for any UNDI API call.
+ */
+typedef t_PXENV_ANY pxenv_structure_t;
+
+/* BIOS PnP parameter block. We scan for this so that we can pass it
+ * to the UNDI driver.
+ */
+
+#define PNP_BIOS_SIGNATURE ( ('$'<<0) + ('P'<<8) + ('n'<<16) + ('P'<<24) )
+typedef struct pnp_bios {
+ uint32_t signature;
+ uint8_t version;
+ uint8_t length;
+ uint16_t control;
+ uint8_t checksum;
+ uint8_t dontcare[24];
+} PACKED pnp_bios_t;
+
+/* Structures within the PXE ROM.
+ */
+
+#define ROM_SIGNATURE 0xaa55
+typedef struct rom {
+ uint16_t signature;
+ uint8_t unused[0x14];
+ uint16_t undi_rom_id_off;
+ uint16_t pcir_off;
+ uint16_t pnp_off;
+} PACKED rom_t;
+
+#define PCIR_SIGNATURE ( ('P'<<0) + ('C'<<8) + ('I'<<16) + ('R'<<24) )
+typedef struct pcir_header {
+ uint32_t signature;
+ uint16_t vendor_id;
+ uint16_t device_id;
+} PACKED pcir_header_t;
+
+#define PNP_SIGNATURE ( ('$'<<0) + ('P'<<8) + ('n'<<16) + ('P'<<24) )
+typedef struct pnp_header {
+ uint32_t signature;
+ uint8_t struct_revision;
+ uint8_t length;
+ uint16_t next;
+ uint8_t reserved;
+ uint8_t checksum;
+ uint16_t id[2];
+ uint16_t manuf_str_off;
+ uint16_t product_str_off;
+ uint8_t base_type;
+ uint8_t sub_type;
+ uint8_t interface_type;
+ uint8_t indicator;
+ uint16_t boot_connect_off;
+ uint16_t disconnect_off;
+ uint16_t initialise_off;
+ uint16_t reserved2;
+ uint16_t info;
+} PACKED pnp_header_t;
+
+#define UNDI_SIGNATURE ( ('U'<<0) + ('N'<<8) + ('D'<<16) + ('I'<<24) )
+typedef struct undi_rom_id {
+ uint32_t signature;
+ uint8_t struct_length;
+ uint8_t struct_cksum;
+ uint8_t struct_rev;
+ uint8_t undi_rev[3];
+ uint16_t undi_loader_off;
+ uint16_t stack_size;
+ uint16_t data_size;
+ uint16_t code_size;
+} PACKED undi_rom_id_t;
+
+/* Nontrivial IRQ handler structure */
+typedef struct {
+ segoff_t chain_to;
+ uint8_t irq_chain, pad1, pad2, pad3;
+ segoff_t entry;
+ uint16_t count_all;
+ uint16_t count_ours;
+ t_PXENV_UNDI_ISR undi_isr;
+ char code[0];
+} PACKED undi_irq_handler_t ;
+
+/* Storage buffers that we need in base memory. We collect these into
+ * a single structure to make allocation simpler.
+ */
+
+typedef struct undi_base_mem_xmit_data {
+ MAC_ADDR destaddr;
+ t_PXENV_UNDI_TBD tbd;
+} undi_base_mem_xmit_data_t;
+
+typedef struct undi_base_mem_data {
+ pxenv_structure_t pxs;
+ undi_base_mem_xmit_data_t xmit_data;
+ char xmit_buffer[ETH_FRAME_LEN];
+ /* Must be last in structure and paragraph-aligned */
+ union {
+ char e820mangler[0];
+ char irq_handler[0];
+ undi_irq_handler_t nontrivial_irq_handler;
+ } __attribute__ ((aligned(16)));
+} undi_base_mem_data_t;
+
+/* Macros and data structures used when freeing bits of base memory
+ * used by the UNDI driver.
+ */
+
+#define FIRING_SQUAD_TARGET_SIZE 8
+#define FIRING_SQUAD_TARGET_INDEX(x) ( (x) / FIRING_SQUAD_TARGET_SIZE )
+#define FIRING_SQUAD_TARGET_BIT(x) ( (x) % FIRING_SQUAD_TARGET_SIZE )
+typedef struct firing_squad_lineup {
+ uint8_t targets[ 640 / FIRING_SQUAD_TARGET_SIZE ];
+} firing_squad_lineup_t;
+typedef enum firing_squad_shoot {
+ DONTSHOOT = 0,
+ SHOOT = 1
+} firing_squad_shoot_t;
+
+/* Driver private data structure.
+ */
+
+typedef struct undi {
+ /* Pointers to various data structures */
+ pnp_bios_t *pnp_bios;
+ rom_t *rom;
+ undi_rom_id_t *undi_rom_id;
+ pxe_t *pxe;
+ pxenv_structure_t *pxs;
+ undi_base_mem_xmit_data_t *xmit_data;
+ /* Pointers and sizes to keep track of allocated base memory */
+ undi_base_mem_data_t *base_mem_data;
+ void *driver_code;
+ size_t driver_code_size;
+ void *driver_data;
+ size_t driver_data_size;
+ char *xmit_buffer;
+ /* Flags. We keep our own instead of trusting the UNDI driver
+ * to have implemented PXENV_UNDI_GET_STATE correctly. Plus
+ * there's the small issue of PXENV_UNDI_GET_STATE being the
+ * same API call as PXENV_STOP_UNDI...
+ */
+ uint8_t prestarted; /* pxenv_start_undi() has been called */
+ uint8_t started; /* pxenv_undi_startup() has been called */
+ uint8_t initialized; /* pxenv_undi_initialize() has been called */
+ uint8_t opened; /* pxenv_undi_open() has been called */
+ /* Parameters that we need to store for future reference
+ */
+ struct pci_device pci;
+ irq_t irq;
+} undi_t;
+
+/* Constants
+ */
+
+#define HUNT_FOR_PIXIES 0
+#define HUNT_FOR_UNDI_ROMS 1
diff --git a/src/arch/i386/firmware/pcbios/basemem.c b/src/arch/i386/firmware/pcbios/basemem.c
new file mode 100644
index 00000000..c93b19e9
--- /dev/null
+++ b/src/arch/i386/firmware/pcbios/basemem.c
@@ -0,0 +1,317 @@
+#ifdef PCBIOS
+
+#include "etherboot.h"
+#include "realmode.h" /* for real_mode_stack */
+
+/* Routines to allocate base memory in a BIOS-compatible way, by
+ * updating the Free Base Memory Size counter at 40:13h.
+ *
+ * Michael Brown <mbrown@fensystems.co.uk> (mcb30)
+ * $Id$
+ */
+
+#define fbms ( ( uint16_t * ) phys_to_virt ( 0x413 ) )
+#define BASE_MEMORY_MAX ( 640 )
+#define FREE_BLOCK_MAGIC ( ('!'<<0) + ('F'<<8) + ('R'<<16) + ('E'<<24) )
+#define FREE_BASE_MEMORY ( (uint32_t) ( *fbms << 10 ) )
+
+/* Prototypes */
+void * _allot_base_memory ( size_t size );
+void _forget_base_memory ( void *ptr, size_t size );
+
+typedef struct free_base_memory_block {
+ uint32_t magic;
+ uint16_t size_kb;
+} free_base_memory_block_t;
+
+/* Return amount of free base memory in bytes
+ */
+
+uint32_t get_free_base_memory ( void ) {
+ return FREE_BASE_MEMORY;
+}
+
+/* Start of our image in base memory.
+ */
+#define __text16_nocompress __attribute__ ((section (".text16.nocompress")))
+uint32_t image_basemem __text16_nocompress = 0;
+uint32_t image_basemem_size __text16_nocompress = 0;
+
+/* Allot/free the real-mode stack
+ */
+
+void allot_real_mode_stack ( void )
+{
+ void *new_real_mode_stack;
+
+ if ( lock_real_mode_stack )
+ return;
+
+ /* This is evil hack.
+ * Until we have a real_mode stack use 0x7c00.
+ * Except for 0 - 0x600 membory below 0x7c00 is hardly every used.
+ * This stack should never be used unless the stack allocation fails,
+ * or if someone has placed a print statement in a dangerous location.
+ */
+ if (!real_mode_stack) {
+ real_mode_stack = 0x7c00;
+ }
+ new_real_mode_stack = _allot_base_memory ( real_mode_stack_size );
+ if ( ! new_real_mode_stack ) {
+ printf ( "FATAL: No real-mode stack\n" );
+ while ( 1 ) {};
+ }
+ real_mode_stack = virt_to_phys ( new_real_mode_stack );
+ get_memsizes();
+}
+
+void forget_real_mode_stack ( void )
+{
+ if ( lock_real_mode_stack )
+ return;
+
+ if ( real_mode_stack) {
+ _forget_base_memory ( phys_to_virt(real_mode_stack),
+ real_mode_stack_size );
+ /* get_memsizes() uses the real_mode stack we just freed
+ * for it's BIOS calls.
+ */
+ get_memsizes();
+ real_mode_stack = 0;
+ }
+}
+
+/* Allocate N bytes of base memory. Amount allocated will be rounded
+ * up to the nearest kB, since that's the granularity of the BIOS FBMS
+ * counter. Returns NULL if memory cannot be allocated.
+ */
+
+static void * _allot_base_memory ( size_t size )
+{
+ uint16_t size_kb = ( size + 1023 ) >> 10;
+ void *ptr = NULL;
+
+#ifdef DEBUG_BASEMEM
+ printf ( "Trying to allocate %d kB of base memory from %d kB free\n",
+ size_kb, *fbms );
+#endif
+
+ /* Free up any unused memory before we start */
+ free_unused_base_memory();
+
+ /* Check available base memory */
+ if ( size_kb > *fbms ) { return NULL; }
+
+ /* Reduce available base memory */
+ *fbms -= size_kb;
+
+ /* Calculate address of memory allocated */
+ ptr = phys_to_virt ( FREE_BASE_MEMORY );
+
+ /* Zero out memory. We do this so that allocation of
+ * already-used space will show up in the form of a crash as
+ * soon as possible.
+ *
+ * Update: there's another reason for doing this. If we don't
+ * zero the contents, then they could still retain our "free
+ * block" markers and be liable to being freed whenever a
+ * base-memory allocation routine is next called.
+ */
+ memset ( ptr, 0, size_kb << 10 );
+
+#ifdef DEBUG_BASEMEM
+ printf ( "Allocated %d kB at [%x,%x)\n", size_kb,
+ virt_to_phys ( ptr ),
+ virt_to_phys ( ptr ) + size_kb * 1024 );
+#endif
+
+ return ptr;
+}
+
+void * allot_base_memory ( size_t size )
+{
+ void *ptr;
+
+ /* Free real-mode stack, allocate memory, reallocate real-mode
+ * stack.
+ */
+ forget_real_mode_stack();
+ ptr = _allot_base_memory ( size );
+ get_memsizes();
+ return ptr;
+}
+
+/* Free base memory allocated by allot_base_memory. The BIOS provides
+ * nothing better than a LIFO mechanism for freeing memory (i.e. it
+ * just has the single "total free memory" counter), but we improve
+ * upon this slightly; as long as you free all the allotted blocks, it
+ * doesn't matter what order you free them in. (This will only work
+ * for blocks that are freed via forget_base_memory()).
+ *
+ * Yes, it's annoying that you have to remember the size of the blocks
+ * you've allotted. However, since our granularity of allocation is
+ * 1K, the alternative is to risk wasting the occasional kB of base
+ * memory, which is a Bad Thing. Really, you should be using as
+ * little base memory as possible, so consider the awkwardness of the
+ * API to be a feature! :-)
+ */
+
+static void _forget_base_memory ( void *ptr, size_t size )
+{
+ uint16_t remainder = virt_to_phys(ptr) & 1023;
+ uint16_t size_kb = ( size + remainder + 1023 ) >> 10;
+ free_base_memory_block_t *free_block =
+ ( free_base_memory_block_t * ) ( ptr - remainder );
+
+ if ( ( ptr == NULL ) || ( size == 0 ) ) {
+ return;
+ }
+
+#ifdef DEBUG_BASEMEM
+ printf ( "Trying to free %d bytes base memory at 0x%x\n",
+ size, virt_to_phys ( ptr ) );
+ if ( remainder > 0 ) {
+ printf ( "WARNING: destructively expanding free block "
+ "downwards to 0x%x\n",
+ virt_to_phys ( ptr - remainder ) );
+ }
+#endif
+
+ /* Mark every kilobyte within this block as free. This is
+ * overkill for normal purposes, but helps when something has
+ * allocated base memory with a granularity finer than the
+ * BIOS granularity of 1kB. PXE ROMs tend to do this when
+ * they allocate their own memory. This method allows us to
+ * free their blocks (admittedly in a rather dangerous,
+ * tread-on-anything-either-side sort of way, but there's no
+ * other way to do it).
+ *
+ * Since we're marking every kB as free, there's actually no
+ * need for recording the size of the blocks. However, we
+ * keep this in so that debug messages are friendlier. It
+ * probably adds around 8 bytes to the overall code size.
+ */
+ while ( size_kb > 0 ) {
+ /* Mark this block as unused */
+ free_block->magic = FREE_BLOCK_MAGIC;
+ free_block->size_kb = size_kb;
+ /* Move up by 1 kB */
+ free_block = (void *)(((char *)free_block) + (1 << 10));
+ size_kb--;
+ }
+
+ /* Free up unused base memory */
+ free_unused_base_memory();
+}
+
+void forget_base_memory ( void *ptr, size_t size )
+{
+ /* Free memory, free real-mode stack, re-allocate real-mode
+ * stack. Do this so that we don't end up wasting a huge
+ * block of memory trapped behind the real-mode stack.
+ */
+ _forget_base_memory ( ptr, size );
+ forget_real_mode_stack();
+ get_memsizes();
+}
+
+/* Do the actual freeing of memory. This is split out from
+ * forget_base_memory() so that it may be called separately. It
+ * should be called whenever base memory is deallocated by an external
+ * entity (if we can detect that it has done so) so that we get the
+ * chance to free up our own blocks.
+ */
+static void free_unused_base_memory ( void ) {
+ free_base_memory_block_t *free_block = NULL;
+
+ /* Try to release memory back to the BIOS. Free all
+ * consecutive blocks marked as free.
+ */
+ while ( 1 ) {
+ /* Calculate address of next potential free block */
+ free_block = ( free_base_memory_block_t * )
+ phys_to_virt ( FREE_BASE_MEMORY );
+
+ /* Stop processing if we're all the way up to 640K or
+ * if this is not a free block
+ */
+ if ( ( *fbms == BASE_MEMORY_MAX ) ||
+ ( free_block->magic != FREE_BLOCK_MAGIC ) ) {
+ break;
+ }
+
+ /* Return memory to BIOS */
+ *fbms += free_block->size_kb;
+
+#ifdef DEBUG_BASEMEM
+ printf ( "Freed %d kB base memory, %d kB now free\n",
+ free_block->size_kb, *fbms );
+#endif
+
+ /* Zero out freed block. We do this in case
+ * the block contained any structures that
+ * might be located by scanning through
+ * memory.
+ */
+ memset ( free_block, 0, free_block->size_kb << 10 );
+ }
+}
+
+/* Free base memory used by the prefix. Called once at start of
+ * Etherboot by arch_main().
+ */
+void forget_prefix_base_memory ( void )
+{
+ /* runtime_start_kb is _text rounded down to a physical kB boundary */
+ uint32_t runtime_start_kb = virt_to_phys(_text) & ~0x3ff;
+ /* prefix_size_kb is the prefix size excluding any portion
+ * that overlaps into the first kB used by the runtime image
+ */
+ uint32_t prefix_size_kb = runtime_start_kb - image_basemem;
+
+#ifdef DEBUG_BASEMEM
+ printf ( "Attempting to free base memory used by prefix\n" );
+#endif
+
+ /* If the decompressor is in allocated base memory
+ * *and* the Etherboot text is in base
+ * memory, then free the decompressor.
+ */
+ if ( ( image_basemem >= FREE_BASE_MEMORY ) &&
+ ( runtime_start_kb >= FREE_BASE_MEMORY ) &&
+ ( runtime_start_kb <= ( BASE_MEMORY_MAX << 10 ) ) )
+ {
+ forget_base_memory ( phys_to_virt ( image_basemem ),
+ prefix_size_kb );
+ /* Update image_basemem and image_basemem_size to
+ * indicate that our allocation now starts with _text
+ */
+ image_basemem = runtime_start_kb;
+ image_basemem_size -= prefix_size_kb;
+ }
+}
+
+/* Free base memory used by the runtime image. Called after
+ * relocation by arch_relocated_from().
+ */
+void forget_runtime_base_memory ( unsigned long old_addr )
+{
+ /* text_start_kb is old _text rounded down to a physical KB boundary */
+ uint32_t old_text_start_kb = old_addr & ~0x3ff;
+
+#ifdef DEBUG_BASEMEM
+ printf ( "Attempting to free base memory used by runtime image\n" );
+#endif
+
+ if ( ( image_basemem >= FREE_BASE_MEMORY ) &&
+ ( image_basemem == old_text_start_kb ) )
+ {
+ forget_base_memory ( phys_to_virt ( image_basemem ),
+ image_basemem_size );
+ /* Update image_basemem to show no longer in use */
+ image_basemem = 0;
+ image_basemem_size = 0;
+ }
+}
+
+#endif /* PCBIOS */
diff --git a/src/arch/i386/firmware/pcbios/bios.c b/src/arch/i386/firmware/pcbios/bios.c
new file mode 100644
index 00000000..70f26703
--- /dev/null
+++ b/src/arch/i386/firmware/pcbios/bios.c
@@ -0,0 +1,155 @@
+/* Etherboot routines for PCBIOS firmware.
+ *
+ * Body of routines taken from old pcbios.S
+ */
+
+#ifdef PCBIOS
+
+#include "etherboot.h"
+#include "realmode.h"
+#include "segoff.h"
+
+#define CF ( 1 << 0 )
+
+/**************************************************************************
+CURRTICKS - Get Time
+Use direct memory access to BIOS variables, longword 0040:006C (ticks
+today) and byte 0040:0070 (midnight crossover flag) instead of calling
+timeofday BIOS interrupt.
+**************************************************************************/
+#if defined(CONFIG_TSC_CURRTICKS)
+#undef CONFIG_BIOS_CURRTICKS
+#else
+#define CONFIG_BIOS_CURRTICKS 1
+#endif
+#if defined(CONFIG_BIOS_CURRTICKS)
+unsigned long currticks (void)
+{
+ static uint32_t days = 0;
+ uint32_t *ticks = VIRTUAL(0x0040,0x006c);
+ uint8_t *midnight = VIRTUAL(0x0040,0x0070);
+
+ /* Re-enable interrupts so that the timer interrupt can occur
+ */
+ RM_FRAGMENT(rm_currticks,
+ "sti\n\t"
+ "nop\n\t"
+ "nop\n\t"
+ "cli\n\t"
+ );
+
+ real_call ( rm_currticks, NULL, NULL );
+
+ if ( *midnight ) {
+ *midnight = 0;
+ days += 0x1800b0;
+ }
+ return ( days + *ticks );
+}
+#endif /* CONFIG_BIOS_CURRTICKS */
+
+/**************************************************************************
+INT15 - Call Interrupt 0x15
+**************************************************************************/
+int int15 ( int ax )
+{
+ struct {
+ reg16_t ax;
+ } PACKED in_stack;
+ struct {
+ reg16_t flags;
+ } PACKED out_stack;
+ reg16_t ret_ax;
+
+ RM_FRAGMENT(rm_int15,
+ "sti\n\t"
+ "popw %ax\n\t"
+ "stc\n\t"
+ "int $0x15\n\t"
+ "pushf\n\t"
+ "cli\n\t"
+ );
+
+ in_stack.ax.word = ax;
+ ret_ax.word = real_call ( rm_int15, &in_stack, &out_stack );
+
+ /* Carry flag clear indicates function not supported */
+ if ( ! ( out_stack.flags.word & CF ) ) return 0;
+ return ret_ax.h;
+}
+
+#ifdef POWERSAVE
+/**************************************************************************
+CPU_NAP - Save power by halting the CPU until the next interrupt
+**************************************************************************/
+void cpu_nap ( void )
+{
+ RM_FRAGMENT(rm_cpu_nap,
+ "sti\n\t"
+ "hlt\n\t"
+ "cli\n\t"
+ );
+
+ real_call ( rm_cpu_nap, NULL, NULL );
+}
+#endif /* POWERSAVE */
+
+#if (TRY_FLOPPY_FIRST > 0)
+/**************************************************************************
+DISK_INIT - Initialize the disk system
+**************************************************************************/
+void disk_init ( void )
+{
+ RM_FRAGMENT(rm_disk_init,
+ "sti\n\t"
+ "xorw %ax,%ax\n\t"
+ "movb $0x80,%dl\n\t"
+ "int $0x13\n\t"
+ "cli\n\t"
+ );
+
+ real_call ( rm_disk_init, NULL, NULL );
+}
+
+/**************************************************************************
+DISK_READ - Read a sector from disk
+**************************************************************************/
+unsigned int pcbios_disk_read ( int drive, int cylinder, int head, int sector,
+ char *buf ) {
+ struct {
+ reg16_t ax;
+ reg16_t cx;
+ reg16_t dx;
+ segoff_t buffer;
+ } PACKED in_stack;
+ struct {
+ reg16_t flags;
+ } PACKED out_stack;
+ reg16_t ret_ax;
+
+ RM_FRAGMENT(rm_pcbios_disk_read,
+ "sti\n\t"
+ "popw %ax\n\t"
+ "popw %cx\n\t"
+ "popw %dx\n\t"
+ "popw %bx\n\t"
+ "popw %es\n\t"
+ "int $0x13\n\t"
+ "pushfw\n\t"
+ "cli\n\t"
+ );
+
+ in_stack.ax.h = 2; /* INT 13,2 - Read disk sector */
+ in_stack.ax.l = 1; /* Read one sector */
+ in_stack.cx.h = cylinder & 0xff;
+ in_stack.cx.l = ( ( cylinder >> 8 ) & 0x3 ) | sector;
+ in_stack.dx.h = head;
+ in_stack.dx.l = drive;
+ in_stack.buffer.segment = SEGMENT ( buf );
+ in_stack.buffer.offset = OFFSET ( buf );
+ ret_ax.word = real_call ( rm_pcbios_disk_read, &in_stack, &out_stack );
+ return ( out_stack.flags.word & CF ) ? ret_ax.word : 0;
+}
+#endif /* TRY_FLOPPY_FIRST */
+
+#endif /* PCBIOS */
diff --git a/src/arch/i386/firmware/pcbios/console.c b/src/arch/i386/firmware/pcbios/console.c
new file mode 100644
index 00000000..f994f06e
--- /dev/null
+++ b/src/arch/i386/firmware/pcbios/console.c
@@ -0,0 +1,85 @@
+/* Etherboot routines for PCBIOS firmware.
+ *
+ * Body of routines taken from old pcbios.S
+ */
+
+#ifdef PCBIOS
+
+#include "etherboot.h"
+#include "realmode.h"
+#include "segoff.h"
+
+#define ZF ( 1 << 6 )
+
+/**************************************************************************
+CONSOLE_PUTC - Print a character on console
+**************************************************************************/
+void console_putc ( int character )
+{
+ struct {
+ reg16_t ax;
+ } PACKED in_stack;
+
+ RM_FRAGMENT(rm_console_putc,
+ "sti\n\t"
+ "popw %ax\n\t"
+ "movb $0x0e, %ah\n\t"
+ "movl $1, %ebx\n\t"
+ "int $0x10\n\t"
+ "cli\n\t"
+ );
+
+ in_stack.ax.l = character;
+ real_call ( rm_console_putc, &in_stack, NULL );
+}
+
+/**************************************************************************
+CONSOLE_GETC - Get a character from console
+**************************************************************************/
+int console_getc ( void )
+{
+ RM_FRAGMENT(rm_console_getc,
+ "sti\n\t"
+ "xorw %ax, %ax\n\t"
+ "int $0x16\n\t"
+ "xorb %ah, %ah\n\t"
+ "cli\n\t"
+ );
+
+ return real_call ( rm_console_getc, NULL, NULL );
+}
+
+/**************************************************************************
+CONSOLE_ISCHAR - Check for keyboard interrupt
+**************************************************************************/
+int console_ischar ( void )
+{
+ RM_FRAGMENT(rm_console_ischar,
+ "sti\n\t"
+ "movb $1, %ah\n\t"
+ "int $0x16\n\t"
+ "pushfw\n\t"
+ "popw %ax\n\t"
+ "cli\n\t"
+ );
+
+ return ( ( real_call ( rm_console_ischar, NULL, NULL ) & ZF ) == 0 );
+}
+
+/**************************************************************************
+GETSHIFT - Get keyboard shift state
+**************************************************************************/
+int getshift ( void )
+{
+ RM_FRAGMENT(rm_getshift,
+ "sti\n\t"
+ "movb $2, %ah\n\t"
+ "int $0x16\n\t"
+ "andw $0x3, %ax\n\t"
+ "cli\n\t"
+ );
+
+ return real_call ( rm_getshift, NULL, NULL );
+}
+
+#endif /* PCBIOS */
diff --git a/src/arch/i386/firmware/pcbios/e820mangler.S b/src/arch/i386/firmware/pcbios/e820mangler.S
new file mode 100644
index 00000000..9349cf2b
--- /dev/null
+++ b/src/arch/i386/firmware/pcbios/e820mangler.S
@@ -0,0 +1,296 @@
+#undef CODE16
+#if defined(PCBIOS)
+#define CODE16
+#endif
+
+#ifdef CODE16
+
+#define BOCHSBP xchgw %bx,%bx
+
+ .text
+ .arch i386
+ .section ".text16", "ax", @progbits
+ .code16
+
+/****************************************************************************
+ * Memory map mangling code
+ ****************************************************************************
+ */
+
+ .globl e820mangler
+e820mangler:
+
+/* Macro to calculate offset of labels within code segment in
+ * installed copy of code.
+ */
+#define INSTALLED(x) ( (x) - e820mangler )
+
+/****************************************************************************
+ * Intercept INT 15 memory calls and remove the hidden memory ranges
+ * from the resulting memory map.
+ ****************************************************************************
+ */
+ .globl _intercept_int15
+_intercept_int15:
+ /* Preserve registers */
+ pushw %bp
+ /* Store %ax for future reference */
+ pushw %ax
+ /* Make INT-style call to old INT15 routine */
+ pushfw
+ lcall %cs:*INSTALLED(_intercepted_int15)
+ /* Preserve flags returned by original E820 routine */
+ pushfw
+ /* Check for valid INT15 routine */
+ jc intercept_int15_exit
+ /* Check for a routine we want to intercept */
+ movw %sp, %bp
+ cmpw $0xe820, 2(%bp)
+ je intercept_e820
+ cmpw $0xe801, 2(%bp)
+ je intercept_e801
+ cmpb $0x88, 3(%bp)
+ je intercept_88
+intercept_int15_exit:
+ /* Restore registers and return */
+ popfw
+ popw %bp /* discard original %ax */
+ popw %bp
+ lret $2 /* 'iret' - flags already loaded */
+
+ .globl _intercepted_int15
+_intercepted_int15: .word 0,0
+
+/****************************************************************************
+ * Exclude an address range from a potentially overlapping address range
+ *
+ * Note: this *can* be called even if the range doesn't overlap; it
+ * will simply return the range unaltered. It copes with all the
+ * possible cases of overlap, including total overlap (which will
+ * modify the range to length zero). If the to-be-excluded range is
+ * in the middle of the target range, then the larger remaining
+ * portion will be returned. If %di is nonzero on entry then the
+ * range will only be truncated from the high end, i.e. the base
+ * address will never be altered. All this in less than 30
+ * instructions. :)
+ *
+ * Parameters:
+ * %eax Base address of memory range
+ * %ecx Length of memory range
+ * %ebx Base address of memory range to exclude
+ * %edx Length of memory range to exclude
+ * %di 0 => truncate either end, 1 => truncate high end only
+ * Returns:
+ * %eax Updated base address of range
+ * %ecx Updated length of range
+ * %ebx,%edx Undefined
+ * All other registers (including %di) preserved
+ *
+ * Note: "ja" is used rather than "jg" because we are comparing
+ * unsigned ints
+ ****************************************************************************
+ */
+#ifdef TEST_EXCLUDE_ALGORITHM
+ .code32
+#endif /* TEST_EXCLUDE_ALGORITHM */
+exclude_memory_range:
+ /* Convert (start,length) to (start,end) */
+ addl %eax, %ecx
+ addl %ebx, %edx
+ /* Calculate "prefix" length */
+ subl %eax, %ebx /* %ebx = "prefix" length */
+ ja 1f
+ xorl %ebx, %ebx /* Truncate to zero if negative */
+1: /* %di == 0 => truncate either end
+ * %di != 0 => truncate only high end
+ */
+ testw %di, %di
+ je use_either
+ cmpl %eax, %edx
+ jbe 99f /* excl. range is below target range */
+use_prefix: /* Use prefix, discard suffix */
+ addl %eax, %ebx /* %ebx = candidate end address */
+ cmpl %ecx, %ebx /* %ecx = min ( %ebx, %ecx ) */
+ ja 1f
+ movl %ebx, %ecx
+1: jmp 99f
+use_either:
+ /* Calculate "suffix" length */
+ subl %ecx, %edx /* %edx = -( "suffix" length ) */
+ jb 1f
+ xorl %edx, %edx /* Truncate to zero if negative */
+1: negl %edx /* %edx = "suffix" length */
+ /* Use whichever is longest of "prefix" and "suffix" */
+ cmpl %ebx, %edx
+ jbe use_prefix
+use_suffix: /* Use suffix, discard prefix */
+ negl %edx
+ addl %ecx, %edx /* %edx = candidate start address */
+ cmpl %eax, %edx /* %eax = max ( %eax, %edx ) */
+ jb 1f
+ movl %edx, %eax
+1:
+99: subl %eax, %ecx /* Convert back to (start,length) */
+ ret
+
+#ifdef TEST_EXCLUDE_ALGORITHM
+ .globl __test_exclude
+__test_exclude:
+ pushl %ebx
+ pushl %edi
+ movl 12(%esp), %eax
+ movl 16(%esp), %ecx
+ movl 20(%esp), %ebx
+ movl 24(%esp), %edx
+ movl 28(%esp), %edi
+ call exclude_memory_range
+ shll $16, %eax
+ orl %ecx, %eax
+ popl %edi
+ popl %ebx
+ ret
+ .code16
+#endif /* TEST_EXCLUDE_ALGORITHM */
+
+/****************************************************************************
+ * Exclude Etherboot-reserved address ranges from a potentially
+ * overlapping address range
+ *
+ * Parameters:
+ * %eax Base address of memory range
+ * %ecx Length of memory range
+ * %di 0 => truncate either end, 1 => truncate high end only
+ * Returns:
+ * %eax Updated base address of range
+ * %ecx Updated length of range
+ * All other registers (including %di) preserved
+ ****************************************************************************
+ */
+exclude_hidden_memory_ranges:
+ pushw %si
+ pushl %ebx
+ pushl %edx
+ movw $INSTALLED(_hide_memory), %si
+2: movl %cs:0(%si), %ebx
+ movl %cs:4(%si), %edx
+ call exclude_memory_range
+ addw $8, %si
+ cmpw $INSTALLED(_hide_memory_end), %si
+ jl 2b
+ popl %edx
+ popl %ebx
+ popw %si
+ ret
+
+ .globl _hide_memory
+_hide_memory:
+ .long 0,0 /* Etherboot text (base,length) */
+ .long 0,0 /* Heap (base,length) */
+_hide_memory_end:
+
+/****************************************************************************
+ * Intercept INT 15,E820 calls and remove the hidden memory ranges
+ * from the resulting memory map.
+ ****************************************************************************
+ */
+#define SMAP ( 0x534d4150 )
+intercept_e820:
+ /* Check for valid E820 routine */
+ cmpl $SMAP, %eax
+ jne intercept_int15_exit
+ /* If base address isn't in the low 4GB, return unaltered
+ * (since we never claim memory above 4GB). WARNING: we cheat
+ * by assuming that no E820 region will straddle the 4GB
+ * boundary: if this is not a valid assumption then things
+ * will probably break.
+ */
+ cmpl $0, %es:4(%di)
+ jne intercept_int15_exit
+ /* Preserve registers */
+ pushl %eax
+ pushl %ecx
+ /* Update returned memory range */
+ movl %es:0(%di), %eax /* Base */
+ movl %es:8(%di), %ecx /* Length */
+ pushw %di
+ xorw %di, %di /* "truncate either end" flag */
+ call exclude_hidden_memory_ranges
+ popw %di
+ movl %eax, %es:0(%di) /* Store updated base */
+ movl %ecx, %es:8(%di) /* Store updated length */
+ /* Restore registers and return */
+ popl %ecx
+ popl %eax
+ jmp intercept_int15_exit
+
+/****************************************************************************
+ * Intercept INT 15,E801 calls and remove the hidden memory ranges
+ * from the resulting memory map.
+ ****************************************************************************
+ */
+intercept_e801:
+ /* Adjust return values */
+ call e801_adjust
+ xchgw %ax, %cx
+ xchgw %bx, %dx
+ call e801_adjust
+ xchgw %ax, %cx
+ xchgw %bx, %dx
+ jmp intercept_int15_exit
+
+ /* %ax = #KB from 1MB+, %bx = #64KB from 16MB+
+ * Return with modified values in %ax, %bx. Preserver other regs.
+ */
+e801_adjust:
+ pushw %di
+ pushl %ecx
+ pushl %eax
+ movw $1, %di /* "truncate only high end" flag */
+
+ /* Truncate #64KB from 16MB+ as appropriate */
+ movw %bx, %cx /* (no need to zero high word) */
+ shll $16, %ecx /* %ecx = length in bytes */
+ movl $(1<<24), %eax /* 16MB start address */
+ call exclude_hidden_memory_ranges
+ shrl $16, %ecx /* %cx = updated length in 64KB */
+ movw %cx, %bx /* Return in %bx */
+
+ /* Truncate #KB from 1MB+ as appropriate */
+ popw %cx /* Orig. %ax (high word already 0) */
+ shll $10, %ecx /* %ecx = length in bytes */
+ shrl $4, %eax /* 1MB start address */
+ call exclude_hidden_memory_ranges
+ shrl $10, %ecx /* %cx = updated length in KB */
+ pushw %cx /* Will be picked up in %eax */
+
+ popl %eax
+ popl %ecx
+ popw %di
+ ret
+
+/****************************************************************************
+ * Intercept INT 15,88 calls and remove the hidden memory ranges
+ * from the resulting memory map.
+ ****************************************************************************
+ */
+intercept_88:
+ pushw %bx /* E801 adjust, ignore %bx */
+ call e801_adjust
+ popw %bx
+ jmp intercept_int15_exit
+
+ .globl e820mangler_end
+e820mangler_end:
+
+ .globl _e820mangler_size
+ .equ _e820mangler_size, e820mangler_end - e820mangler
+ .globl e820mangler_size
+e820mangler_size:
+ .word _e820mangler_size
+
+#else
+
+ .globl _e820mangler_size
+ .equ _e820mangler_size, 0
+
+#endif /* CODE16 */
diff --git a/src/arch/i386/firmware/pcbios/hidemem.c b/src/arch/i386/firmware/pcbios/hidemem.c
new file mode 100644
index 00000000..a9ae001e
--- /dev/null
+++ b/src/arch/i386/firmware/pcbios/hidemem.c
@@ -0,0 +1,94 @@
+/* Utility functions to hide Etherboot by manipulating the E820 memory
+ * map. These could go in memsizes.c, but are placed here because not
+ * all images will need them.
+ */
+
+#include "etherboot.h"
+#include "hidemem.h"
+
+#ifdef CODE16
+
+static int mangling = 0;
+static void *mangler = NULL;
+
+#define INSTALLED(x) ( (typeof(&x)) ( (void*)(&x) - (void*)e820mangler \
+ + mangler ) )
+#define intercept_int15 INSTALLED(_intercept_int15)
+#define intercepted_int15 INSTALLED(_intercepted_int15)
+#define hide_memory INSTALLED(_hide_memory)
+#define INT15_VECTOR ( (segoff_t*) ( phys_to_virt( 4 * 0x15 ) ) )
+
+int install_e820mangler ( void *new_mangler ) {
+ if ( mangling ) return 0;
+ memcpy ( new_mangler, &e820mangler, e820mangler_size );
+ mangler = new_mangler;
+ return 1;
+}
+
+/* Intercept INT15 calls and pass them through the mangler. The
+ * mangler must have been copied to base memory before making this
+ * call, and "mangler" must point to the base memory copy, which must
+ * be 16-byte aligned.
+ */
+int hide_etherboot ( void ) {
+ if ( mangling ) return 1;
+ if ( !mangler ) return 0;
+
+ /* Hook INT15 handler */
+ *intercepted_int15 = *INT15_VECTOR;
+ (*hide_memory)[0].start = virt_to_phys(_text);
+ (*hide_memory)[0].length = _end - _text;
+ /* IMPORTANT, possibly even FIXME:
+ *
+ * Etherboot has a tendency to claim a very large area of
+ * memory as possible heap; enough to make it impossible to
+ * load an OS if we hide all of it. We hide only the portion
+ * that's currently in use. This means that we MUST NOT
+ * perform further allocations from the heap while the mangler
+ * is active.
+ */
+ (*hide_memory)[1].start = heap_ptr;
+ (*hide_memory)[1].length = heap_bot - heap_ptr;
+ INT15_VECTOR->segment = SEGMENT(mangler);
+ INT15_VECTOR->offset = 0;
+
+ mangling = 1;
+ return 1;
+}
+
+int unhide_etherboot ( void ) {
+ if ( !mangling ) return 1;
+
+ /* Restore original INT15 handler
+ */
+ if ( VIRTUAL(INT15_VECTOR->segment,INT15_VECTOR->offset) != mangler ) {
+ /* Oh dear... */
+
+#ifdef WORK_AROUND_BPBATCH_BUG
+ /* BpBatch intercepts INT15, so can't unhook it, and
+ * then proceeds to ignore our PXENV_KEEP_UNDI return
+ * status, which means that it ends up zeroing out the
+ * INT15 handler routine.
+ *
+ * This rather ugly hack involves poking into
+ * BpBatch's code and changing it's stored value for
+ * the "next handler" in the INT15 chain.
+ */
+ segoff_t *bp_chain = VIRTUAL ( 0x0060, 0x8254 );
+
+ if ( ( bp_chain->segment == SEGMENT(mangler) ) &&
+ ( bp_chain->offset == 0 ) ) {
+ printf ( "\nBPBATCH bug workaround enabled\n" );
+ *bp_chain = *intercepted_int15;
+ }
+#endif /* WORK_AROUND_BPBATCH_BUG */
+
+ return 0;
+ }
+ *INT15_VECTOR = *intercepted_int15;
+
+ mangling = 0;
+ return 1;
+}
+
+#endif /* CODE16 */
diff --git a/src/arch/i386/firmware/pcbios/memsizes.c b/src/arch/i386/firmware/pcbios/memsizes.c
new file mode 100644
index 00000000..18ae86da
--- /dev/null
+++ b/src/arch/i386/firmware/pcbios/memsizes.c
@@ -0,0 +1,201 @@
+#ifdef PCBIOS
+
+#include "etherboot.h"
+#include "realmode.h"
+
+#define CF ( 1 << 0 )
+
+#ifndef MEMSIZES_DEBUG
+#define MEMSIZES_DEBUG 0
+#endif
+
+/* by Eric Biederman */
+
+struct meminfo meminfo;
+
+/**************************************************************************
+BASEMEMSIZE - Get size of the conventional (base) memory
+**************************************************************************/
+unsigned short basememsize ( void )
+{
+ RM_FRAGMENT(rm_basememsize,
+ "int $0x12\n\t"
+ );
+ return real_call ( rm_basememsize, NULL, NULL );
+}
+
+/**************************************************************************
+MEMSIZE - Determine size of extended memory
+**************************************************************************/
+unsigned int memsize ( void )
+{
+ struct {
+ reg16_t ax;
+ } PACKED in_stack;
+ struct {
+ reg16_t ax;
+ reg16_t bx;
+ reg16_t cx;
+ reg16_t dx;
+ reg16_t flags;
+ } PACKED out_stack;
+ int memsize;
+
+ RM_FRAGMENT(rm_memsize,
+ /* Some buggy BIOSes don't clear/set carry on pass/error of
+ * e801h memory size call or merely pass cx,dx through without
+ * changing them, so we set carry and zero cx,dx before call.
+ */
+ "stc\n\t"
+ "xorw %cx,%cx\n\t"
+ "xorw %dx,%dx\n\t"
+ "popw %ax\n\t"
+ "int $0x15\n\t"
+ "pushfw\n\t"
+ "pushw %dx\n\t"
+ "pushw %cx\n\t"
+ "pushw %bx\n\t"
+ "pushw %ax\n\t"
+ );
+
+ /* Try INT 15,e801 first */
+ in_stack.ax.word = 0xe801;
+ real_call ( rm_memsize, &in_stack, &out_stack );
+ if ( out_stack.flags.word & CF ) {
+ /* INT 15,e801 not supported: try INT 15,88 */
+ in_stack.ax.word = 0x8800;
+ memsize = real_call ( rm_memsize, &in_stack, &out_stack );
+ } else {
+ /* Some BIOSes report extended memory via ax,bx rather
+ * than cx,dx
+ */
+ if ( (out_stack.cx.word==0) && (out_stack.dx.word==0) ) {
+ /* Use ax,bx */
+ memsize = ( out_stack.bx.word<<6 ) + out_stack.ax.word;
+ } else {
+ /* Use cx,dx */
+ memsize = ( out_stack.dx.word<<6 ) + out_stack.cx.word;
+ }
+ }
+ return memsize;
+}
+
+#define SMAP ( 0x534d4150 )
+int meme820 ( struct e820entry *buf, int count )
+{
+ struct {
+ reg16_t flags;
+ reg32_t eax;
+ reg32_t ebx;
+ struct e820entry entry;
+ } PACKED stack;
+ int index = 0;
+
+ RM_FRAGMENT(rm_meme820,
+ "addw $6, %sp\n\t" /* skip flags, eax */
+ "popl %ebx\n\t"
+ "pushw %ss\n\t" /* es:di = ss:sp */
+ "popw %es\n\t"
+ "movw %sp, %di\n\t"
+ "movl $0xe820, %eax\n\t"
+ "movl $" RM_STR(SMAP) ", %edx\n\t"
+ "movl $" RM_STR(E820ENTRY_SIZE) ", %ecx\n\t"
+ "int $0x15\n\t"
+ "pushl %ebx\n\t"
+ "pushl %eax\n\t"
+ "pushfw\n\t"
+ );
+
+ stack.ebx.dword = 0; /* 'EOF' marker */
+ while ( ( index < count ) &&
+ ( ( index == 0 ) || ( stack.ebx.dword != 0 ) ) ) {
+ real_call ( rm_meme820, &stack, &stack );
+ if ( stack.eax.dword != SMAP ) return 0;
+ if ( stack.flags.word & CF ) return 0;
+ buf[index++] = stack.entry;
+ }
+ return index;
+}
+
+void get_memsizes(void)
+{
+ /* Ensure we don't stomp bios data structutres.
+ * the interrupt table: 0x000 - 0x3ff
+ * the bios data area: 0x400 - 0x502
+ * Dos variables: 0x502 - 0x5ff
+ */
+ static const unsigned min_addr = 0x600;
+ unsigned i;
+ unsigned basemem;
+ basemem = get_free_base_memory();
+ meminfo.basememsize = basememsize();
+ meminfo.memsize = memsize();
+#ifndef IGNORE_E820_MAP
+ meminfo.map_count = meme820(meminfo.map, E820MAX);
+#else
+ meminfo.map_count = 0;
+#endif
+ if (meminfo.map_count == 0) {
+ /* If we don't have an e820 memory map fake it */
+ meminfo.map_count = 2;
+ meminfo.map[0].addr = 0;
+ meminfo.map[0].size = meminfo.basememsize << 10;
+ meminfo.map[0].type = E820_RAM;
+ meminfo.map[1].addr = 1024*1024;
+ meminfo.map[1].size = meminfo.memsize << 10;
+ meminfo.map[1].type = E820_RAM;
+ }
+ /* Scrub the e820 map */
+ for(i = 0; i < meminfo.map_count; i++) {
+ if (meminfo.map[i].type != E820_RAM) {
+ continue;
+ }
+ /* Reserve the bios data structures */
+ if (meminfo.map[i].addr < min_addr) {
+ unsigned long delta;
+ delta = min_addr - meminfo.map[i].addr;
+ if (delta > meminfo.map[i].size) {
+ delta = meminfo.map[i].size;
+ }
+ meminfo.map[i].addr = min_addr;
+ meminfo.map[i].size -= delta;
+ }
+ /* Ensure the returned e820 map is in sync
+ * with the actual memory state
+ */
+ if ((meminfo.map[i].addr < 0xa0000) &&
+ ((meminfo.map[i].addr + meminfo.map[i].size) > basemem))
+ {
+ if (meminfo.map[i].addr <= basemem) {
+ meminfo.map[i].size = basemem - meminfo.map[i].addr;
+ } else {
+ meminfo.map[i].addr = basemem;
+ meminfo.map[i].size = 0;
+ }
+ }
+ }
+#if MEMSIZES_DEBUG
+{
+ int i;
+ printf("basememsize %d\n", meminfo.basememsize);
+ printf("memsize %d\n", meminfo.memsize);
+ printf("Memory regions(%d):\n", meminfo.map_count);
+ for(i = 0; i < meminfo.map_count; i++) {
+ unsigned long long r_start, r_end;
+ r_start = meminfo.map[i].addr;
+ r_end = r_start + meminfo.map[i].size;
+ printf("[%X%X, %X%X) type %d\n",
+ (unsigned long)(r_start >> 32),
+ (unsigned long)r_start,
+ (unsigned long)(r_end >> 32),
+ (unsigned long)r_end,
+ meminfo.map[i].type);
+#if defined(CONSOLE_FIRMWARE)
+ sleep(1); /* No way to see 32 entries on a standard 80x25 screen... */
+#endif
+ }
+}
+#endif
+}
+
+#endif /* PCBIOS */
diff --git a/src/arch/i386/include/bits/byteswap.h b/src/arch/i386/include/bits/byteswap.h
new file mode 100644
index 00000000..1bc84fda
--- /dev/null
+++ b/src/arch/i386/include/bits/byteswap.h
@@ -0,0 +1,45 @@
+#ifndef ETHERBOOT_BITS_BYTESWAP_H
+#define ETHERBOOT_BITS_BYTESWAP_H
+
+static inline uint16_t __i386_bswap_16(uint16_t x)
+{
+ __asm__("xchgb %b0,%h0\n\t"
+ : "=q" (x)
+ : "0" (x));
+ return x;
+}
+
+static inline uint32_t __i386_bswap_32(uint32_t x)
+{
+ __asm__("xchgb %b0,%h0\n\t"
+ "rorl $16,%0\n\t"
+ "xchgb %b0,%h0"
+ : "=q" (x)
+ : "0" (x));
+ return x;
+}
+
+
+#define __bswap_constant_16(x) \
+ ((uint16_t)((((uint16_t)(x) & 0x00ff) << 8) | \
+ (((uint16_t)(x) & 0xff00) >> 8)))
+
+#define __bswap_constant_32(x) \
+ ((uint32_t)((((uint32_t)(x) & 0x000000ffU) << 24) | \
+ (((uint32_t)(x) & 0x0000ff00U) << 8) | \
+ (((uint32_t)(x) & 0x00ff0000U) >> 8) | \
+ (((uint32_t)(x) & 0xff000000U) >> 24)))
+
+#define __bswap_16(x) \
+ ((uint16_t)(__builtin_constant_p(x) ? \
+ __bswap_constant_16(x) : \
+ __i386_bswap_16(x)))
+
+
+#define __bswap_32(x) \
+ ((uint32_t)(__builtin_constant_p(x) ? \
+ __bswap_constant_32(x) : \
+ __i386_bswap_32(x)))
+
+
+#endif /* ETHERBOOT_BITS_BYTESWAP_H */
diff --git a/src/arch/i386/include/bits/cpu.h b/src/arch/i386/include/bits/cpu.h
new file mode 100644
index 00000000..2e9b27ba
--- /dev/null
+++ b/src/arch/i386/include/bits/cpu.h
@@ -0,0 +1,243 @@
+#ifndef I386_BITS_CPU_H
+#define I386_BITS_CPU_H
+
+
+/* Sample usage: CPU_FEATURE_P(cpu.x86_capability, FPU) */
+#define CPU_FEATURE_P(CAP, FEATURE) \
+ (!!(CAP[(X86_FEATURE_##FEATURE)/32] & ((X86_FEATURE_##FEATURE) & 0x1f)))
+
+#define NCAPINTS 4 /* Currently we have 4 32-bit words worth of info */
+
+/* Intel-defined CPU features, CPUID level 0x00000001, word 0 */
+#define X86_FEATURE_FPU (0*32+ 0) /* Onboard FPU */
+#define X86_FEATURE_VME (0*32+ 1) /* Virtual Mode Extensions */
+#define X86_FEATURE_DE (0*32+ 2) /* Debugging Extensions */
+#define X86_FEATURE_PSE (0*32+ 3) /* Page Size Extensions */
+#define X86_FEATURE_TSC (0*32+ 4) /* Time Stamp Counter */
+#define X86_FEATURE_MSR (0*32+ 5) /* Model-Specific Registers, RDMSR, WRMSR */
+#define X86_FEATURE_PAE (0*32+ 6) /* Physical Address Extensions */
+#define X86_FEATURE_MCE (0*32+ 7) /* Machine Check Architecture */
+#define X86_FEATURE_CX8 (0*32+ 8) /* CMPXCHG8 instruction */
+#define X86_FEATURE_APIC (0*32+ 9) /* Onboard APIC */
+#define X86_FEATURE_SEP (0*32+11) /* SYSENTER/SYSEXIT */
+#define X86_FEATURE_MTRR (0*32+12) /* Memory Type Range Registers */
+#define X86_FEATURE_PGE (0*32+13) /* Page Global Enable */
+#define X86_FEATURE_MCA (0*32+14) /* Machine Check Architecture */
+#define X86_FEATURE_CMOV (0*32+15) /* CMOV instruction (FCMOVCC and FCOMI too if FPU present) */
+#define X86_FEATURE_PAT (0*32+16) /* Page Attribute Table */
+#define X86_FEATURE_PSE36 (0*32+17) /* 36-bit PSEs */
+#define X86_FEATURE_PN (0*32+18) /* Processor serial number */
+#define X86_FEATURE_CLFLSH (0*32+19) /* Supports the CLFLUSH instruction */
+#define X86_FEATURE_DTES (0*32+21) /* Debug Trace Store */
+#define X86_FEATURE_ACPI (0*32+22) /* ACPI via MSR */
+#define X86_FEATURE_MMX (0*32+23) /* Multimedia Extensions */
+#define X86_FEATURE_FXSR (0*32+24) /* FXSAVE and FXRSTOR instructions (fast save and restore */
+ /* of FPU context), and CR4.OSFXSR available */
+#define X86_FEATURE_XMM (0*32+25) /* Streaming SIMD Extensions */
+#define X86_FEATURE_XMM2 (0*32+26) /* Streaming SIMD Extensions-2 */
+#define X86_FEATURE_SELFSNOOP (0*32+27) /* CPU self snoop */
+#define X86_FEATURE_HT (0*32+28) /* Hyper-Threading */
+#define X86_FEATURE_ACC (0*32+29) /* Automatic clock control */
+#define X86_FEATURE_IA64 (0*32+30) /* IA-64 processor */
+
+/* AMD-defined CPU features, CPUID level 0x80000001, word 1 */
+/* Don't duplicate feature flags which are redundant with Intel! */
+#define X86_FEATURE_SYSCALL (1*32+11) /* SYSCALL/SYSRET */
+#define X86_FEATURE_MMXEXT (1*32+22) /* AMD MMX extensions */
+#define X86_FEATURE_LM (1*32+29) /* Long Mode (x86-64) */
+#define X86_FEATURE_3DNOWEXT (1*32+30) /* AMD 3DNow! extensions */
+#define X86_FEATURE_3DNOW (1*32+31) /* 3DNow! */
+
+/* Transmeta-defined CPU features, CPUID level 0x80860001, word 2 */
+#define X86_FEATURE_RECOVERY (2*32+ 0) /* CPU in recovery mode */
+#define X86_FEATURE_LONGRUN (2*32+ 1) /* Longrun power control */
+#define X86_FEATURE_LRTI (2*32+ 3) /* LongRun table interface */
+
+/* Other features, Linux-defined mapping, word 3 */
+/* This range is used for feature bits which conflict or are synthesized */
+#define X86_FEATURE_CXMMX (3*32+ 0) /* Cyrix MMX extensions */
+#define X86_FEATURE_K6_MTRR (3*32+ 1) /* AMD K6 nonstandard MTRRs */
+#define X86_FEATURE_CYRIX_ARR (3*32+ 2) /* Cyrix ARRs (= MTRRs) */
+#define X86_FEATURE_CENTAUR_MCR (3*32+ 3) /* Centaur MCRs (= MTRRs) */
+
+#define MAX_X86_VENDOR_ID 16
+struct cpuinfo_x86 {
+ uint8_t x86; /* CPU family */
+ uint8_t x86_model;
+ uint8_t x86_mask;
+
+ int cpuid_level; /* Maximum supported CPUID level, -1=no CPUID */
+ unsigned x86_capability[NCAPINTS];
+ char x86_vendor_id[MAX_X86_VENDOR_ID];
+};
+
+
+#define X86_VENDOR_INTEL 0
+#define X86_VENDOR_CYRIX 1
+#define X86_VENDOR_AMD 2
+#define X86_VENDOR_UMC 3
+#define X86_VENDOR_NEXGEN 4
+#define X86_VENDOR_CENTAUR 5
+#define X86_VENDOR_RISE 6
+#define X86_VENDOR_TRANSMETA 7
+#define X86_VENDOR_NSC 8
+#define X86_VENDOR_UNKNOWN 0xff
+
+/*
+ * EFLAGS bits
+ */
+#define X86_EFLAGS_CF 0x00000001 /* Carry Flag */
+#define X86_EFLAGS_PF 0x00000004 /* Parity Flag */
+#define X86_EFLAGS_AF 0x00000010 /* Auxillary carry Flag */
+#define X86_EFLAGS_ZF 0x00000040 /* Zero Flag */
+#define X86_EFLAGS_SF 0x00000080 /* Sign Flag */
+#define X86_EFLAGS_TF 0x00000100 /* Trap Flag */
+#define X86_EFLAGS_IF 0x00000200 /* Interrupt Flag */
+#define X86_EFLAGS_DF 0x00000400 /* Direction Flag */
+#define X86_EFLAGS_OF 0x00000800 /* Overflow Flag */
+#define X86_EFLAGS_IOPL 0x00003000 /* IOPL mask */
+#define X86_EFLAGS_NT 0x00004000 /* Nested Task */
+#define X86_EFLAGS_RF 0x00010000 /* Resume Flag */
+#define X86_EFLAGS_VM 0x00020000 /* Virtual Mode */
+#define X86_EFLAGS_AC 0x00040000 /* Alignment Check */
+#define X86_EFLAGS_VIF 0x00080000 /* Virtual Interrupt Flag */
+#define X86_EFLAGS_VIP 0x00100000 /* Virtual Interrupt Pending */
+#define X86_EFLAGS_ID 0x00200000 /* CPUID detection flag */
+
+/*
+ * Generic CPUID function
+ */
+static inline void cpuid(int op,
+ unsigned int *eax, unsigned int *ebx, unsigned int *ecx, unsigned int *edx)
+{
+ __asm__("cpuid"
+ : "=a" (*eax),
+ "=b" (*ebx),
+ "=c" (*ecx),
+ "=d" (*edx)
+ : "0" (op));
+}
+
+/*
+ * CPUID functions returning a single datum
+ */
+static inline unsigned int cpuid_eax(unsigned int op)
+{
+ unsigned int eax;
+
+ __asm__("cpuid"
+ : "=a" (eax)
+ : "0" (op)
+ : "bx", "cx", "dx");
+ return eax;
+}
+static inline unsigned int cpuid_ebx(unsigned int op)
+{
+ unsigned int eax, ebx;
+
+ __asm__("cpuid"
+ : "=a" (eax), "=b" (ebx)
+ : "0" (op)
+ : "cx", "dx" );
+ return ebx;
+}
+static inline unsigned int cpuid_ecx(unsigned int op)
+{
+ unsigned int eax, ecx;
+
+ __asm__("cpuid"
+ : "=a" (eax), "=c" (ecx)
+ : "0" (op)
+ : "bx", "dx" );
+ return ecx;
+}
+static inline unsigned int cpuid_edx(unsigned int op)
+{
+ unsigned int eax, edx;
+
+ __asm__("cpuid"
+ : "=a" (eax), "=d" (edx)
+ : "0" (op)
+ : "bx", "cx");
+ return edx;
+}
+
+/*
+ * Intel CPU features in CR4
+ */
+#define X86_CR4_VME 0x0001 /* enable vm86 extensions */
+#define X86_CR4_PVI 0x0002 /* virtual interrupts flag enable */
+#define X86_CR4_TSD 0x0004 /* disable time stamp at ipl 3 */
+#define X86_CR4_DE 0x0008 /* enable debugging extensions */
+#define X86_CR4_PSE 0x0010 /* enable page size extensions */
+#define X86_CR4_PAE 0x0020 /* enable physical address extensions */
+#define X86_CR4_MCE 0x0040 /* Machine check enable */
+#define X86_CR4_PGE 0x0080 /* enable global pages */
+#define X86_CR4_PCE 0x0100 /* enable performance counters at ipl 3 */
+#define X86_CR4_OSFXSR 0x0200 /* enable fast FPU save and restore */
+#define X86_CR4_OSXMMEXCPT 0x0400 /* enable unmasked SSE exceptions */
+
+
+#define MSR_K6_EFER 0xC0000080
+/* EFER bits: */
+#define _EFER_SCE 0 /* SYSCALL/SYSRET */
+#define _EFER_LME 8 /* Long mode enable */
+#define _EFER_LMA 10 /* Long mode active (read-only) */
+#define _EFER_NX 11 /* No execute enable */
+
+#define EFER_SCE (1<<_EFER_SCE)
+#define EFER_LME (1<<EFER_LME)
+#define EFER_LMA (1<<EFER_LMA)
+#define EFER_NX (1<<_EFER_NX)
+
+#define rdmsr(msr,val1,val2) \
+ __asm__ __volatile__("rdmsr" \
+ : "=a" (val1), "=d" (val2) \
+ : "c" (msr))
+
+#define wrmsr(msr,val1,val2) \
+ __asm__ __volatile__("wrmsr" \
+ : /* no outputs */ \
+ : "c" (msr), "a" (val1), "d" (val2))
+
+
+#define read_cr0() ({ \
+ unsigned int __dummy; \
+ __asm__( \
+ "movl %%cr0, %0\n\t" \
+ :"=r" (__dummy)); \
+ __dummy; \
+})
+#define write_cr0(x) \
+ __asm__("movl %0,%%cr0": :"r" (x));
+
+#define read_cr3() ({ \
+ unsigned int __dummy; \
+ __asm__( \
+ "movl %%cr3, %0\n\t" \
+ :"=r" (__dummy)); \
+ __dummy; \
+})
+#define write_cr3x(x) \
+ __asm__("movl %0,%%cr3": :"r" (x));
+
+
+#define read_cr4() ({ \
+ unsigned int __dummy; \
+ __asm__( \
+ "movl %%cr4, %0\n\t" \
+ :"=r" (__dummy)); \
+ __dummy; \
+})
+#define write_cr4x(x) \
+ __asm__("movl %0,%%cr4": :"r" (x));
+
+
+extern struct cpuinfo_x86 cpu_info;
+#ifdef CONFIG_X86_64
+extern void cpu_setup(void);
+#else
+#define cpu_setup() do {} while(0)
+#endif
+
+#endif /* I386_BITS_CPU_H */
diff --git a/src/arch/i386/include/bits/elf.h b/src/arch/i386/include/bits/elf.h
new file mode 100644
index 00000000..dad9c7b8
--- /dev/null
+++ b/src/arch/i386/include/bits/elf.h
@@ -0,0 +1,91 @@
+#ifndef I386_BITS_ELF_H
+#define I386_BITS_ELF_H
+
+#include "cpu.h"
+
+#ifdef CONFIG_X86_64
+/* ELF Defines for the 64bit version of the current architecture */
+#define EM_CURRENT_64 EM_X86_64
+#define EM_CURRENT_64_PRESENT ( \
+ CPU_FEATURE_P(cpu_info.x86_capability, LM) && \
+ CPU_FEATURE_P(cpu_info.x86_capability, PAE) && \
+ CPU_FEATURE_P(cpu_info.x86_capability, PSE))
+
+#define ELF_CHECK_X86_64_ARCH(x) \
+ (EM_CURRENT_64_PRESENT && ((x).e_machine == EM_X86_64))
+#define __unused_i386
+#else
+#define ELF_CHECK_X86_64_ARCH(x) 0
+#define __unused_i386 __unused
+#endif
+
+
+/* ELF Defines for the current architecture */
+#define EM_CURRENT EM_386
+#define ELFDATA_CURRENT ELFDATA2LSB
+
+#define ELF_CHECK_I386_ARCH(x) \
+ (((x).e_machine == EM_386) || ((x).e_machine == EM_486))
+
+#define ELF_CHECK_ARCH(x) \
+ ((ELF_CHECK_I386_ARCH(x) || ELF_CHECK_X86_64_ARCH(x)) && \
+ ((x).e_entry <= 0xffffffffUL))
+
+#ifdef IMAGE_FREEBSD
+/*
+ * FreeBSD has this rather strange "feature" of its design.
+ * At some point in its evolution, FreeBSD started to rely
+ * externally on private/static/debug internal symbol information.
+ * That is, some of the interfaces that software uses to access
+ * and work with the FreeBSD kernel are made available not
+ * via the shared library symbol information (the .DYNAMIC section)
+ * but rather the debug symbols. This means that any symbol, not
+ * just publicly defined symbols can be (and are) used by system
+ * tools to make the system work. (such as top, swapinfo, swapon,
+ * etc)
+ *
+ * Even worse, however, is the fact that standard ELF loaders do
+ * not know how to load the symbols since they are not within
+ * an ELF PT_LOAD section. The kernel needs these symbols to
+ * operate so the following changes/additions to the boot
+ * loading of EtherBoot have been made to get the kernel to load.
+ * All of the changes are within IMAGE_FREEBSD such that the
+ * extra/changed code only compiles when FREEBSD support is
+ * enabled.
+ */
+
+/*
+ * Section header for FreeBSD (debug symbol kludge!) support
+ */
+typedef struct {
+ Elf32_Word sh_name; /* Section name (index into the
+ section header string table). */
+ Elf32_Word sh_type; /* Section type. */
+ Elf32_Word sh_flags; /* Section flags. */
+ Elf32_Addr sh_addr; /* Address in memory image. */
+ Elf32_Off sh_offset; /* Offset in file. */
+ Elf32_Size sh_size; /* Size in bytes. */
+ Elf32_Word sh_link; /* Index of a related section. */
+ Elf32_Word sh_info; /* Depends on section type. */
+ Elf32_Size sh_addralign; /* Alignment in bytes. */
+ Elf32_Size sh_entsize; /* Size of each entry in section. */
+} Elf32_Shdr;
+
+/* sh_type */
+#define SHT_SYMTAB 2 /* symbol table section */
+#define SHT_STRTAB 3 /* string table section */
+
+/*
+ * Module information subtypes (for the metadata that we need to build)
+ */
+#define MODINFO_END 0x0000 /* End of list */
+#define MODINFO_NAME 0x0001 /* Name of module (string) */
+#define MODINFO_TYPE 0x0002 /* Type of module (string) */
+#define MODINFO_METADATA 0x8000 /* Module-specfic */
+
+#define MODINFOMD_SSYM 0x0003 /* start of symbols */
+#define MODINFOMD_ESYM 0x0004 /* end of symbols */
+
+#endif /* IMAGE_FREEBSD */
+
+#endif /* I386_BITS_ELF_H */
diff --git a/src/arch/i386/include/bits/elf_x.h b/src/arch/i386/include/bits/elf_x.h
new file mode 100644
index 00000000..86c67250
--- /dev/null
+++ b/src/arch/i386/include/bits/elf_x.h
@@ -0,0 +1,5 @@
+#define ARCH_ELF_CLASS ELFCLASS32
+#define ARCH_ELF_DATA ELFDATA2LSB
+#define ARCH_ELF_MACHINE_OK(x) ((x)==EM_386 || (x)==EM_486)
+typedef Elf32_Ehdr Elf_ehdr;
+typedef Elf32_Phdr Elf_phdr;
diff --git a/src/arch/i386/include/bits/eltorito.h b/src/arch/i386/include/bits/eltorito.h
new file mode 100644
index 00000000..d43e9aac
--- /dev/null
+++ b/src/arch/i386/include/bits/eltorito.h
@@ -0,0 +1,3 @@
+#ifndef ELTORITO_PLATFORM
+#define ELTORITO_PLATFORM ELTORITO_PLATFORM_X86
+#endif /* ELTORITO_PLATFORM */
diff --git a/src/arch/i386/include/bits/endian.h b/src/arch/i386/include/bits/endian.h
new file mode 100644
index 00000000..b23b233a
--- /dev/null
+++ b/src/arch/i386/include/bits/endian.h
@@ -0,0 +1,9 @@
+#ifndef ETHERBOOT_BITS_ENDIAN_H
+#define ETHERBOOT_BITS_ENDIAN_H
+
+#define __BYTE_ORDER __LITTLE_ENDIAN
+
+#define le32_to_cpup(x) (*(uint32_t *)(x))
+#define cpu_to_le16p(x) (*(uint16_t*)(x))
+
+#endif /* ETHERBOOT_BITS_ENDIAN_H */
diff --git a/src/arch/i386/include/bits/string.h b/src/arch/i386/include/bits/string.h
new file mode 100644
index 00000000..3743d011
--- /dev/null
+++ b/src/arch/i386/include/bits/string.h
@@ -0,0 +1,99 @@
+#ifndef ETHERBOOT_BITS_STRING_H
+#define ETHERBOOT_BITS_STRING_H
+/*
+ * Taken from Linux /usr/include/asm/string.h
+ * All except memcpy, memmove, memset and memcmp removed.
+ */
+
+/*
+ * This string-include defines all string functions as inline
+ * functions. Use gcc. It also assumes ds=es=data space, this should be
+ * normal. Most of the string-functions are rather heavily hand-optimized,
+ * see especially strtok,strstr,str[c]spn. They should work, but are not
+ * very easy to understand. Everything is done entirely within the register
+ * set, making the functions fast and clean. String instructions have been
+ * used through-out, making for "slightly" unclear code :-)
+ *
+ * NO Copyright (C) 1991, 1992 Linus Torvalds,
+ * consider these trivial functions to be PD.
+ */
+
+
+#define __HAVE_ARCH_MEMMOVE
+static inline void * memmove(void * dest,const void * src, size_t n)
+{
+int d0, d1, d2;
+if (dest<src)
+__asm__ __volatile__(
+ "cld\n\t"
+ "rep\n\t"
+ "movsb"
+ : "=&c" (d0), "=&S" (d1), "=&D" (d2)
+ :"0" (n),"1" (src),"2" (dest)
+ : "memory");
+else
+__asm__ __volatile__(
+ "std\n\t"
+ "rep\n\t"
+ "movsb\n\t"
+ "cld"
+ : "=&c" (d0), "=&S" (d1), "=&D" (d2)
+ :"0" (n),
+ "1" (n-1+(const char *)src),
+ "2" (n-1+(char *)dest)
+ :"memory");
+return dest;
+}
+
+#define __HAVE_ARCH_MEMSET
+static inline void *memset(void *s, int c,size_t count)
+{
+int d0, d1;
+__asm__ __volatile__(
+ "cld\n\t"
+ "rep\n\t"
+ "stosb"
+ : "=&c" (d0), "=&D" (d1)
+ :"a" (c),"1" (s),"0" (count)
+ :"memory");
+return s;
+}
+
+#define __HAVE_ARCH_STRNCMP
+static inline int strncmp(const char * cs,const char * ct,size_t count)
+{
+register int __res;
+int d0, d1, d2;
+__asm__ __volatile__(
+ "1:\tdecl %3\n\t"
+ "js 2f\n\t"
+ "lodsb\n\t"
+ "scasb\n\t"
+ "jne 3f\n\t"
+ "testb %%al,%%al\n\t"
+ "jne 1b\n"
+ "2:\txorl %%eax,%%eax\n\t"
+ "jmp 4f\n"
+ "3:\tsbbl %%eax,%%eax\n\t"
+ "orb $1,%%al\n"
+ "4:"
+ :"=a" (__res), "=&S" (d0), "=&D" (d1), "=&c" (d2)
+ :"1" (cs),"2" (ct),"3" (count));
+return __res;
+}
+
+#define __HAVE_ARCH_STRLEN
+static inline size_t strlen(const char * s)
+{
+int d0;
+register int __res;
+__asm__ __volatile__(
+ "repne\n\t"
+ "scasb\n\t"
+ "notl %0\n\t"
+ "decl %0"
+ :"=c" (__res), "=&D" (d0) :"1" (s),"a" (0), "0" (0xffffffff));
+return __res;
+}
+
+#endif /* ETHERBOOT_BITS_STRING_H */
diff --git a/src/arch/i386/include/callbacks_arch.h b/src/arch/i386/include/callbacks_arch.h
new file mode 100644
index 00000000..f9cba488
--- /dev/null
+++ b/src/arch/i386/include/callbacks_arch.h
@@ -0,0 +1,243 @@
+/* Callout/callback interface for Etherboot
+ *
+ * This file provides the mechanisms for making calls from Etherboot
+ * to external programs and vice-versa.
+ *
+ * Initial version by Michael Brown <mbrown@fensystems.co.uk>, January 2004.
+ *
+ * $Id$
+ */
+
+#ifndef CALLBACKS_ARCH_H
+#define CALLBACKS_ARCH_H
+
+/* Skip the definitions that won't make sense to the assembler */
+#ifndef ASSEMBLY
+
+/* Struct to hold general-purpose register values. PUSHAL and POPAL
+ * can work directly with this structure; do not change the order of
+ * registers.
+ */
+typedef struct {
+ union {
+ uint16_t di;
+ uint32_t edi;
+ };
+ union {
+ uint16_t si;
+ uint32_t esi;
+ };
+ union {
+ uint16_t bp;
+ uint32_t ebp;
+ };
+ union {
+ uint16_t sp;
+ uint32_t esp;
+ };
+ union {
+ struct {
+ uint8_t bl;
+ uint8_t bh;
+ } PACKED;
+ uint16_t bx;
+ uint32_t ebx;
+ };
+ union {
+ struct {
+ uint8_t dl;
+ uint8_t dh;
+ } PACKED;
+ uint16_t dx;
+ uint32_t edx;
+ };
+ union {
+ struct {
+ uint8_t cl;
+ uint8_t ch;
+ } PACKED;
+ uint16_t cx;
+ uint32_t ecx;
+ };
+ union {
+ struct {
+ uint8_t al;
+ uint8_t ah;
+ } PACKED;
+ uint16_t ax;
+ uint32_t eax;
+ };
+} regs_t;
+
+/* Struct to hold segment register values. Don't change the order;
+ * many bits of assembly code rely on it.
+ */
+typedef struct {
+ uint16_t cs;
+ uint16_t ss;
+ uint16_t ds;
+ uint16_t es;
+ uint16_t fs;
+ uint16_t gs;
+} PACKED seg_regs_t;
+
+/* Struct for a GDT descriptor */
+typedef struct {
+ uint16_t limit;
+ uint32_t address;
+ uint16_t padding;
+} PACKED gdt_descriptor_t;
+
+/* Struct for a GDT entry. Use GDT_SEGMENT() to fill it in.
+ */
+typedef struct {
+ uint16_t limit_0_15;
+ uint16_t base_0_15;
+ uint8_t base_16_23;
+ uint8_t accessed__type__sflag__dpl__present;
+ uint8_t limit_16_19__avl__size__granularity;
+ uint8_t base_24_31;
+} PACKED gdt_segment_t;
+
+#define GDT_SEGMENT(base,limit,type,sflag,dpl,avl,size,granularity) \
+ ( (gdt_segment_t) { \
+ ( (limit) & 0xffff ), \
+ ( (base) & 0xffff ), \
+ ( ( (base) >> 16 ) & 0xff ), \
+ ( ( 1 << 0 ) | ( (type) << 1 ) | \
+ ( (sflag) << 4 ) | ( (dpl) << 5 ) | ( 1 << 7 ) ), \
+ ( ( (limit) >> 16 ) | \
+ ( (avl) << 4 ) | ( (size) << 5 ) | ( (granularity) << 7 ) ),\
+ ( (base) >> 24 ) \
+ } )
+#define GDT_SEGMENT_BASE(gdt_segment) \
+ ( (gdt_segment)->base_0_15 | \
+ (gdt_segment)->base_16_23 << 16 | \
+ (gdt_segment)->base_24_31 << 24 )
+#define GDT_SEGMENT_LIMIT(gdt_segment) \
+ ( (gdt_segment)->limit_0_15 | \
+ ( ( (gdt_segment)->limit_16_19__avl__size__granularity \
+ & 0xf ) << 16 ) )
+#define GDT_SEGMENT_GRANULARITY(gdt_segment) \
+ ( ( (gdt_segment)->limit_16_19__avl__size__granularity \
+ & 0x80 ) >> 7 )
+#define GDT_SEGMENT_TYPE(gdt_segment) \
+ ( ( (gdt_segment)->accessed__type__sflag__dpl__present & 0x0e ) >> 1 )
+#define GDT_SEGMENT_SIZE(gdt_segment) \
+ ( ( (gdt_segment)->limit_16_19__avl__size__granularity \
+ & 0x60 ) >> 5 )
+
+#define GDT_TYPE_DATA (0x0)
+#define GDT_TYPE_STACK (0x2)
+#define GDT_TYPE_WRITEABLE (0x1)
+#define GDT_TYPE_CODE (0x6)
+#define GDT_TYPE_EXEC_ONLY_CODE (0x4)
+#define GDT_TYPE_CONFORMING (0x1)
+#define GDT_SFLAG_SYSTEM (0)
+#define GDT_SFLAG_NORMAL (1)
+#define GDT_AVL_NORMAL (0)
+#define GDT_SIZE_16BIT (0x0)
+#define GDT_SIZE_32BIT (0x2)
+#define GDT_SIZE_64BIT (0x1)
+#define GDT_SIZE_UNKNOWN (0x3)
+#define GDT_GRANULARITY_SMALL (0)
+#define GDT_GRANULARITY_LARGE (1)
+#define GDT_SEGMENT_NORMAL(base,limit,type,size,granularity) \
+ GDT_SEGMENT ( base, limit, type, \
+ GDT_SFLAG_NORMAL, 0, GDT_AVL_NORMAL, \
+ size, granularity )
+
+/* Protected mode code segment */
+#define GDT_SEGMENT_PMCS(base) GDT_SEGMENT_NORMAL ( \
+ base, 0xfffff, GDT_TYPE_CODE | GDT_TYPE_CONFORMING, \
+ GDT_SIZE_32BIT, GDT_GRANULARITY_LARGE )
+#define GDT_SEGMENT_PMCS_PHYS GDT_SEGMENT_PMCS(0)
+/* Protected mode data segment */
+#define GDT_SEGMENT_PMDS(base) GDT_SEGMENT_NORMAL ( \
+ base, 0xfffff, GDT_TYPE_DATA | GDT_TYPE_WRITEABLE, \
+ GDT_SIZE_32BIT, GDT_GRANULARITY_LARGE )
+#define GDT_SEGMENT_PMDS_PHYS GDT_SEGMENT_PMDS(0)
+/* Real mode code segment */
+/* Not sure if there's any reason to use GDT_TYPE_EXEC_ONLY_CODE
+ * instead of just GDT_TYPE_CODE, but that's what our old GDT did and
+ * it worked, so I'm not changing it.
+ */
+#define GDT_SEGMENT_RMCS(base) GDT_SEGMENT_NORMAL ( \
+ base, 0xffff, GDT_TYPE_EXEC_ONLY_CODE | GDT_TYPE_CONFORMING, \
+ GDT_SIZE_16BIT, GDT_GRANULARITY_SMALL )
+/* Real mode data segment */
+#define GDT_SEGMENT_RMDS(base) GDT_SEGMENT_NORMAL ( \
+ base, 0xffff, GDT_TYPE_DATA | GDT_TYPE_WRITEABLE, \
+ GDT_SIZE_16BIT, GDT_GRANULARITY_SMALL )
+/* Long mode code segment */
+#define GDT_SEGMENT_LMCS(base) GDT_SEGMENT_NORMAL ( \
+ base, 0xfffff, GDT_TYPE_CODE | GDT_TYPE_CONFORMING, \
+ GDT_SIZE_64BIT, GDT_GRANULARITY_LARGE )
+#define GDT_SEGMENT_LMCS_PHYS GDT_SEGMENT_LMCS(0)
+/* Long mode data segment */
+/* AFIACT, GDT_SIZE_64BIT applies only to code segments */
+#define GDT_SEGMENT_LMDS(base) GDT_SEGMENT_NORMAL ( \
+ base, 0xfffff, GDT_TYPE_DATA | GDT_TYPE_WRITEABLE, \
+ GDT_SIZE_32BIT, GDT_GRANULARITY_LARGE )
+#define GDT_SEGMENT_LMDS_PHYS GDT_SEGMENT_LMDS(0)
+
+/* Template for creating GDT structures (including segment register
+ * lists), suitable for passing as parameters to external_call().
+ */
+#define GDT_STRUCT_t(num_segments) \
+ struct { \
+ gdt_descriptor_t descriptor; \
+ gdt_segment_t segments[num_segments]; \
+ } PACKED
+/* And utility function for filling it in */
+#define GDT_ADJUST(structure) { \
+ (structure)->descriptor.address = \
+ virt_to_phys(&((structure)->descriptor.limit)); \
+ (structure)->descriptor.limit = \
+ sizeof((structure)->segments) + 8 - 1; \
+ (structure)->descriptor.padding = 0; \
+}
+
+/* Data passed in to in_call() by assembly wrapper.
+ */
+typedef struct {
+ regs_t regs;
+ seg_regs_t seg_regs;
+ gdt_descriptor_t gdt_desc;
+ uint32_t flags;
+ struct {
+ uint32_t offset;
+ uint32_t segment;
+ } ret_addr;
+} PACKED i386_pm_in_call_data_t;
+
+typedef struct {
+ seg_regs_t seg_regs;
+ union {
+ uint16_t pad;
+ uint16_t prefix_sp;
+ };
+ uint16_t flags;
+ struct {
+ uint16_t offset;
+ uint16_t segment;
+ } ret_addr;
+ uint32_t orig_opcode;
+} PACKED i386_rm_in_call_data_t;
+
+typedef struct {
+ i386_pm_in_call_data_t *pm;
+ i386_rm_in_call_data_t *rm;
+} i386_in_call_data_t;
+#define in_call_data_t i386_in_call_data_t
+
+/* Function prototypes
+ */
+extern int install_rm_callback_interface ( void *address, size_t available );
+
+#endif /* ASSEMBLY */
+
+#define RM_IN_CALL (0)
+#define RM_IN_CALL_FAR (2)
+
+#endif /* CALLBACKS_ARCH_H */
diff --git a/src/arch/i386/include/hidemem.h b/src/arch/i386/include/hidemem.h
new file mode 100644
index 00000000..600a8692
--- /dev/null
+++ b/src/arch/i386/include/hidemem.h
@@ -0,0 +1,21 @@
+#ifndef HIDEMEM_H
+#define HIDEMEM_H
+
+#include "segoff.h"
+
+extern int install_e820mangler ( void *new_mangler );
+extern int hide_etherboot ( void );
+extern int unhide_etherboot ( void );
+
+/* Symbols in e820mangler.S */
+extern void e820mangler ( void );
+extern void _intercept_int15 ( void );
+extern segoff_t _intercepted_int15;
+typedef struct {
+ uint32_t start;
+ uint32_t length;
+} exclude_range_t;
+extern exclude_range_t _hide_memory[2];
+extern uint16_t e820mangler_size;
+
+#endif /* HIDEMEM_H */
diff --git a/src/arch/i386/include/hooks.h b/src/arch/i386/include/hooks.h
new file mode 100644
index 00000000..0764edaf
--- /dev/null
+++ b/src/arch/i386/include/hooks.h
@@ -0,0 +1,9 @@
+#ifndef ETHERBOOT_I386_HOOKS_H
+#define ETHERBOOT_I386_HOOKS_H
+
+void arch_main(in_call_data_t *data, va_list params);
+void arch_on_exit(int status);
+#define arch_relocate_to(addr)
+void arch_relocated_from ( uint32_t old_addr );
+
+#endif /* ETHERBOOT_I386_HOOKS_H */
diff --git a/src/arch/i386/include/io.h b/src/arch/i386/include/io.h
new file mode 100644
index 00000000..e351a0c1
--- /dev/null
+++ b/src/arch/i386/include/io.h
@@ -0,0 +1,246 @@
+#ifndef ETHERBOOT_IO_H
+#define ETHERBOOT_IO_H
+
+
+/* Amount of relocation etherboot is experiencing */
+extern unsigned long virt_offset;
+
+/* Don't require identity mapped physical memory,
+ * osloader.c is the only valid user at the moment.
+ */
+static inline unsigned long virt_to_phys(volatile const void *virt_addr)
+{
+ return ((unsigned long)virt_addr) + virt_offset;
+}
+
+static inline void *phys_to_virt(unsigned long phys_addr)
+{
+ return (void *)(phys_addr - virt_offset);
+}
+
+/* virt_to_bus converts an addresss inside of etherboot [_start, _end]
+ * into a memory access cards can use.
+ */
+#define virt_to_bus virt_to_phys
+
+
+/* bus_to_virt reverses virt_to_bus, the address must be output
+ * from virt_to_bus to be valid. This function does not work on
+ * all bus addresses.
+ */
+#define bus_to_virt phys_to_virt
+
+/* ioremap converts a random 32bit bus address into something
+ * etherboot can access.
+ */
+static inline void *ioremap(unsigned long bus_addr, unsigned long length __unused)
+{
+ return bus_to_virt(bus_addr);
+}
+
+/* iounmap cleans up anything ioremap had to setup */
+static inline void iounmap(void *virt_addr __unused)
+{
+ return;
+}
+
+/*
+ * This file contains the definitions for the x86 IO instructions
+ * inb/inw/inl/outb/outw/outl and the "string versions" of the same
+ * (insb/insw/insl/outsb/outsw/outsl). You can also use "pausing"
+ * versions of the single-IO instructions (inb_p/inw_p/..).
+ *
+ * This file is not meant to be obfuscating: it's just complicated
+ * to (a) handle it all in a way that makes gcc able to optimize it
+ * as well as possible and (b) trying to avoid writing the same thing
+ * over and over again with slight variations and possibly making a
+ * mistake somewhere.
+ */
+
+/*
+ * Thanks to James van Artsdalen for a better timing-fix than
+ * the two short jumps: using outb's to a nonexistent port seems
+ * to guarantee better timings even on fast machines.
+ *
+ * On the other hand, I'd like to be sure of a non-existent port:
+ * I feel a bit unsafe about using 0x80 (should be safe, though)
+ *
+ * Linus
+ */
+
+#ifdef SLOW_IO_BY_JUMPING
+#define __SLOW_DOWN_IO __asm__ __volatile__("jmp 1f\n1:\tjmp 1f\n1:")
+#else
+#define __SLOW_DOWN_IO __asm__ __volatile__("outb %al,$0x80")
+#endif
+
+#ifdef REALLY_SLOW_IO
+#define SLOW_DOWN_IO { __SLOW_DOWN_IO; __SLOW_DOWN_IO; __SLOW_DOWN_IO; __SLOW_DOWN_IO; }
+#else
+#define SLOW_DOWN_IO __SLOW_DOWN_IO
+#endif
+
+/*
+ * readX/writeX() are used to access memory mapped devices. On some
+ * architectures the memory mapped IO stuff needs to be accessed
+ * differently. On the x86 architecture, we just read/write the
+ * memory location directly.
+ */
+#define readb(addr) (*(volatile unsigned char *) (addr))
+#define readw(addr) (*(volatile unsigned short *) (addr))
+#define readl(addr) (*(volatile unsigned int *) (addr))
+
+#define writeb(b,addr) ((*(volatile unsigned char *) (addr)) = (b))
+#define writew(b,addr) ((*(volatile unsigned short *) (addr)) = (b))
+#define writel(b,addr) ((*(volatile unsigned int *) (addr)) = (b))
+
+#define memcpy_fromio(a,b,c) memcpy((a),(void *)(b),(c))
+#define memcpy_toio(a,b,c) memcpy((void *)(a),(b),(c))
+
+/*
+ * Force strict CPU ordering.
+ * And yes, this is required on UP too when we're talking
+ * to devices.
+ *
+ * For now, "wmb()" doesn't actually do anything, as all
+ * Intel CPU's follow what Intel calls a *Processor Order*,
+ * in which all writes are seen in the program order even
+ * outside the CPU.
+ *
+ * I expect future Intel CPU's to have a weaker ordering,
+ * but I'd also expect them to finally get their act together
+ * and add some real memory barriers if so.
+ *
+ * Some non intel clones support out of order store. wmb() ceases to be a
+ * nop for these.
+ */
+
+#define mb() __asm__ __volatile__ ("lock; addl $0,0(%%esp)": : :"memory")
+#define rmb() mb()
+#define wmb() mb();
+
+
+/*
+ * Talk about misusing macros..
+ */
+
+#define __OUT1(s,x) \
+extern void __out##s(unsigned x value, unsigned short port); \
+extern inline void __out##s(unsigned x value, unsigned short port) {
+
+#define __OUT2(s,s1,s2) \
+__asm__ __volatile__ ("out" #s " %" s1 "0,%" s2 "1"
+
+#define __OUT(s,s1,x) \
+__OUT1(s,x) __OUT2(s,s1,"w") : : "a" (value), "d" (port)); } \
+__OUT1(s##c,x) __OUT2(s,s1,"") : : "a" (value), "id" (port)); } \
+__OUT1(s##_p,x) __OUT2(s,s1,"w") : : "a" (value), "d" (port)); SLOW_DOWN_IO; } \
+__OUT1(s##c_p,x) __OUT2(s,s1,"") : : "a" (value), "id" (port)); SLOW_DOWN_IO; }
+
+#define __IN1(s,x) \
+extern unsigned x __in##s(unsigned short port); \
+extern inline unsigned x __in##s(unsigned short port) { unsigned x _v;
+
+#define __IN2(s,s1,s2) \
+__asm__ __volatile__ ("in" #s " %" s2 "1,%" s1 "0"
+
+#define __IN(s,s1,x,i...) \
+__IN1(s,x) __IN2(s,s1,"w") : "=a" (_v) : "d" (port) ,##i ); return _v; } \
+__IN1(s##c,x) __IN2(s,s1,"") : "=a" (_v) : "id" (port) ,##i ); return _v; } \
+__IN1(s##_p,x) __IN2(s,s1,"w") : "=a" (_v) : "d" (port) ,##i ); SLOW_DOWN_IO; return _v; } \
+__IN1(s##c_p,x) __IN2(s,s1,"") : "=a" (_v) : "id" (port) ,##i ); SLOW_DOWN_IO; return _v; }
+
+#define __INS(s) \
+extern void ins##s(unsigned short port, void * addr, unsigned long count); \
+extern inline void ins##s(unsigned short port, void * addr, unsigned long count) \
+{ __asm__ __volatile__ ("cld ; rep ; ins" #s \
+: "=D" (addr), "=c" (count) : "d" (port),"0" (addr),"1" (count)); }
+
+#define __OUTS(s) \
+extern void outs##s(unsigned short port, const void * addr, unsigned long count); \
+extern inline void outs##s(unsigned short port, const void * addr, unsigned long count) \
+{ __asm__ __volatile__ ("cld ; rep ; outs" #s \
+: "=S" (addr), "=c" (count) : "d" (port),"0" (addr),"1" (count)); }
+
+__IN(b,"", char)
+__IN(w,"",short)
+__IN(l,"", long)
+
+__OUT(b,"b",char)
+__OUT(w,"w",short)
+__OUT(l,,int)
+
+__INS(b)
+__INS(w)
+__INS(l)
+
+__OUTS(b)
+__OUTS(w)
+__OUTS(l)
+
+/*
+ * Note that due to the way __builtin_constant_p() works, you
+ * - can't use it inside a inline function (it will never be true)
+ * - you don't have to worry about side effects within the __builtin..
+ */
+#define outb(val,port) \
+((__builtin_constant_p((port)) && (port) < 256) ? \
+ __outbc((val),(port)) : \
+ __outb((val),(port)))
+
+#define inb(port) \
+((__builtin_constant_p((port)) && (port) < 256) ? \
+ __inbc(port) : \
+ __inb(port))
+
+#define outb_p(val,port) \
+((__builtin_constant_p((port)) && (port) < 256) ? \
+ __outbc_p((val),(port)) : \
+ __outb_p((val),(port)))
+
+#define inb_p(port) \
+((__builtin_constant_p((port)) && (port) < 256) ? \
+ __inbc_p(port) : \
+ __inb_p(port))
+
+#define outw(val,port) \
+((__builtin_constant_p((port)) && (port) < 256) ? \
+ __outwc((val),(port)) : \
+ __outw((val),(port)))
+
+#define inw(port) \
+((__builtin_constant_p((port)) && (port) < 256) ? \
+ __inwc(port) : \
+ __inw(port))
+
+#define outw_p(val,port) \
+((__builtin_constant_p((port)) && (port) < 256) ? \
+ __outwc_p((val),(port)) : \
+ __outw_p((val),(port)))
+
+#define inw_p(port) \
+((__builtin_constant_p((port)) && (port) < 256) ? \
+ __inwc_p(port) : \
+ __inw_p(port))
+
+#define outl(val,port) \
+((__builtin_constant_p((port)) && (port) < 256) ? \
+ __outlc((val),(port)) : \
+ __outl((val),(port)))
+
+#define inl(port) \
+((__builtin_constant_p((port)) && (port) < 256) ? \
+ __inlc(port) : \
+ __inl(port))
+
+#define outl_p(val,port) \
+((__builtin_constant_p((port)) && (port) < 256) ? \
+ __outlc_p((val),(port)) : \
+ __outl_p((val),(port)))
+
+#define inl_p(port) \
+((__builtin_constant_p((port)) && (port) < 256) ? \
+ __inlc_p(port) : \
+ __inl_p(port))
+
+#endif /* ETHERBOOT_IO_H */
diff --git a/src/arch/i386/include/latch.h b/src/arch/i386/include/latch.h
new file mode 100644
index 00000000..38a8bd21
--- /dev/null
+++ b/src/arch/i386/include/latch.h
@@ -0,0 +1,10 @@
+#ifndef LATCH_H
+#define LATCH_H
+
+#define TICKS_PER_SEC 18
+
+/* For different calibrators of the TSC move the declaration of
+ * sleep_latch and the definitions of it's length here...
+ */
+
+#endif /* LATCH_H */
diff --git a/src/arch/i386/include/limits.h b/src/arch/i386/include/limits.h
new file mode 100644
index 00000000..f13db267
--- /dev/null
+++ b/src/arch/i386/include/limits.h
@@ -0,0 +1,59 @@
+#ifndef LIMITS_H
+#define LIMITS_H 1
+
+/* Number of bits in a `char' */
+#define CHAR_BIT 8
+
+/* Minimum and maximum values a `signed char' can hold */
+#define SCHAR_MIN (-128)
+#define SCHAR_MAX 127
+
+/* Maximum value an `unsigned char' can hold. (Minimum is 0.) */
+#define UCHAR_MAX 255
+
+/* Minimum and maximum values a `char' can hold */
+#define CHAR_MIN SCHAR_MIN
+#define CHAR_MAX SCHAR_MAX
+
+/* Minimum and maximum values a `signed short int' can hold */
+#define SHRT_MIN (-32768)
+#define SHRT_MAX 32767
+
+/* Maximum value an `unsigned short' can hold. (Minimum is 0.) */
+#define USHRT_MAX 65535
+
+
+/* Minimum and maximum values a `signed int' can hold */
+#define INT_MIN (-INT_MAX - 1)
+#define INT_MAX 2147483647
+
+/* Maximum value an `unsigned int' can hold. (Minimum is 0.) */
+#define UINT_MAX 4294967295U
+
+
+/* Minimum and maximum values a `signed int' can hold */
+#define INT_MAX 2147483647
+#define INT_MIN (-INT_MAX - 1)
+
+
+/* Maximum value an `unsigned int' can hold. (Minimum is 0.) */
+#define UINT_MAX 4294967295U
+
+
+/* Minimum and maximum values a `signed long' can hold */
+#define LONG_MAX 2147483647
+#define LONG_MIN (-LONG_MAX - 1L)
+
+/* Maximum value an `unsigned long' can hold. (Minimum is 0.) */
+#define ULONG_MAX 4294967295UL
+
+/* Minimum and maximum values a `signed long long' can hold */
+#define LLONG_MAX 9223372036854775807LL
+#define LLONG_MIN (-LONG_MAX - 1LL)
+
+
+/* Maximum value an `unsigned long long' can hold. (Minimum is 0.) */
+#define ULLONG_MAX 18446744073709551615ULL
+
+
+#endif /* LIMITS_H */
diff --git a/src/arch/i386/include/pic8259.h b/src/arch/i386/include/pic8259.h
new file mode 100644
index 00000000..694e9d13
--- /dev/null
+++ b/src/arch/i386/include/pic8259.h
@@ -0,0 +1,96 @@
+/*
+ * Basic support for controlling the 8259 Programmable Interrupt Controllers.
+ *
+ * Initially written by Michael Brown (mcb30).
+ */
+
+#ifndef PIC8259_H
+#define PIC8259_H
+
+/* For segoff_t */
+#include "segoff.h"
+
+#define IRQ_PIC_CUTOFF (8)
+
+/* 8259 register locations */
+#define PIC1_ICW1 (0x20)
+#define PIC1_OCW2 (0x20)
+#define PIC1_OCW3 (0x20)
+#define PIC1_ICR (0x20)
+#define PIC1_IRR (0x20)
+#define PIC1_ISR (0x20)
+#define PIC1_ICW2 (0x21)
+#define PIC1_ICW3 (0x21)
+#define PIC1_ICW4 (0x21)
+#define PIC1_IMR (0x21)
+#define PIC2_ICW1 (0xa0)
+#define PIC2_OCW2 (0xa0)
+#define PIC2_OCW3 (0xa0)
+#define PIC2_ICR (0xa0)
+#define PIC2_IRR (0xa0)
+#define PIC2_ISR (0xa0)
+#define PIC2_ICW2 (0xa1)
+#define PIC2_ICW3 (0xa1)
+#define PIC2_ICW4 (0xa1)
+#define PIC2_IMR (0xa1)
+
+/* Register command values */
+#define OCW3_ID (0x08)
+#define OCW3_READ_IRR (0x03)
+#define OCW3_READ_ISR (0x02)
+#define ICR_EOI_NON_SPECIFIC (0x20)
+#define ICR_EOI_NOP (0x40)
+#define ICR_EOI_SPECIFIC (0x60)
+#define ICR_EOI_SET_PRIORITY (0xc0)
+
+/* Macros to enable/disable IRQs */
+#define IMR_REG(x) ( (x) < IRQ_PIC_CUTOFF ? PIC1_IMR : PIC2_IMR )
+#define IMR_BIT(x) ( 1 << ( (x) % IRQ_PIC_CUTOFF ) )
+#define irq_enabled(x) ( ( inb ( IMR_REG(x) ) & IMR_BIT(x) ) == 0 )
+#define enable_irq(x) outb ( inb( IMR_REG(x) ) & ~IMR_BIT(x), IMR_REG(x) )
+#define disable_irq(x) outb ( inb( IMR_REG(x) ) | IMR_BIT(x), IMR_REG(x) )
+
+/* Macros for acknowledging IRQs */
+#define ICR_REG(x) ( (x) < IRQ_PIC_CUTOFF ? PIC1_ICR : PIC2_ICR )
+#define ICR_VALUE(x) ( (x) % IRQ_PIC_CUTOFF )
+#define CHAINED_IRQ 2
+
+/* Utility macros to convert IRQ numbers to INT numbers and INT vectors */
+#define IRQ_INT(x) ( (x)<IRQ_PIC_CUTOFF ? (x)+0x08 : (x)-IRQ_PIC_CUTOFF+0x70 )
+#define INT_VECTOR(x) ( (segoff_t*) phys_to_virt( 4 * (x) ) )
+#define IRQ_VECTOR(x) ( INT_VECTOR ( IRQ_INT(x) ) )
+
+/* Other constants */
+typedef uint8_t irq_t;
+#define IRQ_MAX (15)
+#define IRQ_NONE (0xff)
+
+/* Function prototypes
+ */
+int install_irq_handler ( irq_t irq, segoff_t *handler,
+ uint8_t *previously_enabled,
+ segoff_t *previous_handler );
+int remove_irq_handler ( irq_t irq, segoff_t *handler,
+ uint8_t *previously_enabled,
+ segoff_t *previous_handler );
+int install_trivial_irq_handler ( irq_t irq );
+int remove_trivial_irq_handler ( irq_t irq );
+int trivial_irq_triggered ( irq_t irq );
+int copy_trivial_irq_handler ( void *target, size_t target_size );
+void send_non_specific_eoi ( irq_t irq );
+void send_specific_eoi ( irq_t irq );
+void fake_irq ( irq_t irq );
+#ifdef DEBUG_IRQ
+void dump_irq_status ( void );
+#else
+#define dump_irq_status()
+#endif
+
+/* Other code (e.g. undi.c) needs to know the size of the trivial IRQ
+ * handler code, so we put prototypes and the size macro here.
+ */
+extern void _trivial_irq_handler ( void );
+extern void _trivial_irq_handler_end ( void );
+#define TRIVIAL_IRQ_HANDLER_SIZE FRAGMENT_SIZE(_trivial_irq_handler)
+
+#endif /* PIC8259_H */
diff --git a/src/arch/i386/include/pxe_callbacks.h b/src/arch/i386/include/pxe_callbacks.h
new file mode 100644
index 00000000..9b941931
--- /dev/null
+++ b/src/arch/i386/include/pxe_callbacks.h
@@ -0,0 +1,33 @@
+/* Header for pxe_callbacks.c.
+ */
+
+#ifndef PXE_CALLBACKS_H
+#define PXE_CALLBACKS_H
+
+#include "etherboot.h"
+#include "segoff.h"
+#include "pxe.h"
+
+typedef struct {
+ segoff_t orig_retaddr;
+ uint16_t opcode;
+ segoff_t segoff;
+} PACKED pxe_call_params_t;
+
+/*
+ * These values are hard-coded into the PXE spec
+ */
+#define PXE_LOAD_SEGMENT (0x0000)
+#define PXE_LOAD_OFFSET (0x7c00)
+#define PXE_LOAD_ADDRESS ( ( PXE_LOAD_SEGMENT << 4 ) + PXE_LOAD_OFFSET )
+
+/* Function prototypes
+ */
+extern pxe_stack_t * install_pxe_stack ( void *base );
+extern void use_undi_ds_for_rm_stack ( uint16_t ds );
+extern int hook_pxe_stack ( void );
+extern int unhook_pxe_stack ( void );
+extern void remove_pxe_stack ( void );
+extern int xstartpxe ( void );
+
+#endif /* PXE_CALLBACKS_H */
diff --git a/src/arch/i386/include/pxe_types.h b/src/arch/i386/include/pxe_types.h
new file mode 100644
index 00000000..7b093e6a
--- /dev/null
+++ b/src/arch/i386/include/pxe_types.h
@@ -0,0 +1,35 @@
+/*
+ * Architecture-specific portion of pxe.h for Etherboot
+ *
+ * This file has to define the types SEGOFF16_t, SEGDESC_t and
+ * SEGSEL_t for use in other PXE structures. See pxe.h for details.
+ */
+
+#ifndef PXE_TYPES_H
+#define PXE_TYPES_H
+
+/* SEGOFF16_t defined in separate header
+ */
+#include "segoff.h"
+typedef segoff_t I386_SEGOFF16_t;
+#define SEGOFF16_t I386_SEGOFF16_t
+
+#define IS_NULL_SEGOFF16(x) ( ( (x).segment == 0 ) && ( (x).offset == 0 ) )
+#define SEGOFF16_TO_PTR(x) ( VIRTUAL( (x).segment, (x).offset ) )
+#define PTR_TO_SEGOFF16(ptr,segoff16) \
+ (segoff16).segment = SEGMENT(ptr); \
+ (segoff16).offset = OFFSET(ptr);
+
+typedef struct {
+ uint16_t Seg_Addr;
+ uint32_t Phy_Addr;
+ uint16_t Seg_Size;
+} PACKED I386_SEGDESC_t; /* PACKED is required, otherwise gcc pads
+ * this out to 12 bytes -
+ * mbrown@fensystems.co.uk (mcb30) 17/5/03 */
+#define SEGDESC_t I386_SEGDESC_t
+
+typedef uint16_t I386_SEGSEL_t;
+#define SEGSEL_t I386_SEGSEL_t
+
+#endif /* PXE_TYPES_H */
diff --git a/src/arch/i386/include/realmode.h b/src/arch/i386/include/realmode.h
new file mode 100644
index 00000000..eca92b9b
--- /dev/null
+++ b/src/arch/i386/include/realmode.h
@@ -0,0 +1,124 @@
+/* Real-mode interface
+ */
+
+#ifndef ASSEMBLY
+
+#include "etherboot.h"
+#include "segoff.h"
+
+typedef union {
+ struct {
+ union {
+ uint8_t l;
+ uint8_t byte;
+ };
+ uint8_t h;
+ } PACKED;
+ uint16_t word;
+} PACKED reg16_t;
+
+typedef union {
+ reg16_t w;
+ uint32_t dword;
+} PACKED reg32_t;
+
+/* Macros to help with defining inline real-mode trampoline fragments.
+ */
+#define RM_XSTR(x) #x /* Macro hackery needed to stringify */
+#define RM_STR(x) RM_XSTR(x)
+#define RM_FRAGMENT(name, asm_code_str) \
+ extern void name ( void ); \
+ extern void name ## _end (void); \
+ __asm__( \
+ ".section \".text16\"\n\t" \
+ ".code16\n\t" \
+ ".arch i386\n\t" \
+ ".globl " #name " \n\t" \
+ #name ":\n\t" \
+ asm_code_str "\n\t" \
+ ".globl " #name "_end\n\t" \
+ #name "_end:\n\t" \
+ ".code32\n\t" \
+ ".previous\n\t" \
+ )
+
+#define FRAGMENT_SIZE(fragment) ( (size_t) ( ( (void*) fragment ## _end )\
+ - ( (void*) (fragment) ) ) )
+
+/* Data structures in _prot_to_real and _real_to_prot. These
+ * structures are accessed by assembly code as well as C code.
+ */
+typedef struct {
+ uint32_t esp;
+ uint16_t cs;
+ uint16_t ss;
+ uint32_t r2p_params;
+} PACKED prot_to_real_params_t;
+
+typedef struct {
+ uint32_t ret_addr;
+ uint32_t esp;
+ uint32_t ebx;
+ uint32_t esi;
+ uint32_t edi;
+ uint32_t ebp;
+ uint32_t out_stack;
+ uint32_t out_stack_len;
+} PACKED real_to_prot_params_t;
+
+/* Function prototypes: realmode.c
+ */
+#define real_call( fragment, in_stack, out_stack ) \
+ _real_call ( fragment, FRAGMENT_SIZE(fragment), \
+ (void*)(in_stack), \
+ ( (in_stack) == NULL ? 0 : sizeof(*(in_stack)) ), \
+ (void*)(out_stack), \
+ ( (out_stack) == NULL ? 0 : sizeof(*(out_stack)) ) )
+extern uint16_t _real_call ( void *fragment, int fragment_len,
+ void *in_stack, int in_stack_len,
+ void *out_stack, int out_stack_len );
+/* Function prototypes: realmode_asm.S
+ */
+extern void rm_callback_interface;
+extern uint16_t rm_callback_interface_size;
+extern uint32_t rm_etherboot_location;
+extern void _rm_in_call ( void );
+extern void _rm_in_call_far ( void );
+
+extern void _prot_to_real_prefix ( void );
+extern void _prot_to_real_prefix_end ( void );
+extern uint16_t prot_to_real_prefix_size;
+
+extern void _real_to_prot_suffix ( void );
+extern void _real_to_prot_suffix_end ( void );
+extern uint16_t real_to_prot_suffix_size;
+
+/* PXE assembler bits */
+extern void pxe_callback_interface;
+extern uint16_t pxe_callback_interface_size;
+extern void _pxe_in_call_far ( void );
+extern void _pxenv_in_call_far ( void );
+extern void _pxe_intercept_int1a ( void );
+extern segoff_t _pxe_intercepted_int1a;
+extern segoff_t _pxe_pxenv_location;
+
+/* Global variables
+ */
+extern uint32_t real_mode_stack;
+extern size_t real_mode_stack_size;
+extern int lock_real_mode_stack;
+
+
+/* Function prototypes from basemem.c
+ */
+#ifdef LINUXBIOS
+/* A silly hard code that let's the code compile and work.
+ * When this becomes a problem feel free to implement
+ * something better.
+ */
+static inline void allot_real_mode_stack(void) { real_mode_stack = 0x7c00; }
+#else
+void allot_real_mode_stack(void);
+#endif
+
+#endif /* ASSEMBLY */
diff --git a/src/arch/i386/include/segoff.h b/src/arch/i386/include/segoff.h
new file mode 100644
index 00000000..822ddd33
--- /dev/null
+++ b/src/arch/i386/include/segoff.h
@@ -0,0 +1,41 @@
+/*
+ * Segment:offset types and macros
+ *
+ * Initially written by Michael Brown (mcb30).
+ */
+
+#ifndef SEGOFF_H
+#define SEGOFF_H
+
+#include <stdint.h>
+#include <osdep.h>
+#include <io.h>
+
+/* Segment:offset structure. Note that the order within the structure
+ * is offset:segment.
+ */
+typedef struct {
+ uint16_t offset;
+ uint16_t segment;
+} segoff_t;
+
+/* Macros for converting from virtual to segment:offset addresses,
+ * when we don't actually care which of the many isomorphic results we
+ * get.
+ */
+#ifdef DEBUG_SEGMENT
+uint16_t SEGMENT ( const void * const ptr ) {
+ uint32_t phys = virt_to_phys ( ptr );
+ if ( phys > 0xfffff ) {
+ printf ( "FATAL ERROR: segment address out of range\n" );
+ }
+ return phys >> 4;
+}
+#else
+#define SEGMENT(x) ( virt_to_phys ( x ) >> 4 )
+#endif
+#define OFFSET(x) ( virt_to_phys ( x ) & 0xf )
+#define SEGOFF(x) { OFFSET(x), SEGMENT(x) }
+#define VIRTUAL(x,y) ( phys_to_virt ( ( ( x ) << 4 ) + ( y ) ) )
+
+#endif /* SEGOFF_H */
diff --git a/src/arch/i386/include/setjmp.h b/src/arch/i386/include/setjmp.h
new file mode 100644
index 00000000..ed2be270
--- /dev/null
+++ b/src/arch/i386/include/setjmp.h
@@ -0,0 +1,12 @@
+#ifndef ETHERBOOT_SETJMP_H
+#define ETHERBOOT_SETJMP_H
+
+
+/* Define a type for use by setjmp and longjmp */
+#define JBLEN 6
+typedef unsigned long jmp_buf[JBLEN];
+
+extern int setjmp (jmp_buf env);
+extern void longjmp (jmp_buf env, int val);
+
+#endif /* ETHERBOOT_SETJMP_H */
diff --git a/src/arch/i386/include/stdint.h b/src/arch/i386/include/stdint.h
new file mode 100644
index 00000000..42b04830
--- /dev/null
+++ b/src/arch/i386/include/stdint.h
@@ -0,0 +1,16 @@
+#ifndef STDINT_H
+#define STDINT_H
+
+typedef unsigned size_t;
+
+typedef unsigned char uint8_t;
+typedef unsigned short uint16_t;
+typedef unsigned long uint32_t;
+typedef unsigned long long uint64_t;
+
+typedef signed char int8_t;
+typedef signed short int16_t;
+typedef signed long int32_t;
+typedef signed long long int64_t;
+
+#endif /* STDINT_H */
diff --git a/src/arch/i386/include/vga.h b/src/arch/i386/include/vga.h
new file mode 100644
index 00000000..01fc39d8
--- /dev/null
+++ b/src/arch/i386/include/vga.h
@@ -0,0 +1,228 @@
+/*
+ *
+ * modified
+ * by Steve M. Gehlbach <steve@kesa.com>
+ *
+ * Originally from linux/drivers/video/vga16.c by
+ * Ben Pfaff <pfaffben@debian.org> and Petr Vandrovec <VANDROVE@vc.cvut.cz>
+ * Copyright 1999 Ben Pfaff <pfaffben@debian.org> and Petr Vandrovec <VANDROVE@vc.cvut.cz>
+ * Based on VGA info at http://www.goodnet.com/~tinara/FreeVGA/home.htm
+ * Based on VESA framebuffer (c) 1998 Gerd Knorr <kraxel@goldbach.in-berlin.de>
+ *
+ */
+
+#ifndef VGA_H_INCL
+#define VGA_H_INCL 1
+
+//#include <cpu/p5/io.h>
+
+#define u8 unsigned char
+#define u16 unsigned short
+#define u32 unsigned int
+#define __u32 u32
+
+#define VERROR -1
+#define CHAR_HEIGHT 16
+#define LINES 25
+#define COLS 80
+
+// macros for writing to vga regs
+#define write_crtc(data,addr) outb(addr,CRT_IC); outb(data,CRT_DC)
+#define write_att(data,addr) inb(IS1_RC); inb(0x80); outb(addr,ATT_IW); inb(0x80); outb(data,ATT_IW); inb(0x80)
+#define write_seq(data,addr) outb(addr,SEQ_I); outb(data,SEQ_D)
+#define write_gra(data,addr) outb(addr,GRA_I); outb(data,GRA_D)
+u8 read_seq_b(u16 addr);
+u8 read_gra_b(u16 addr);
+u8 read_crtc_b(u16 addr);
+u8 read_att_b(u16 addr);
+
+
+#ifdef VGA_HARDWARE_FIXUP
+void vga_hardware_fixup(void);
+#else
+#define vga_hardware_fixup() do{} while(0)
+#endif
+
+#define SYNC_HOR_HIGH_ACT 1 /* horizontal sync high active */
+#define SYNC_VERT_HIGH_ACT 2 /* vertical sync high active */
+#define SYNC_EXT 4 /* external sync */
+#define SYNC_COMP_HIGH_ACT 8 /* composite sync high active */
+#define SYNC_BROADCAST 16 /* broadcast video timings */
+ /* vtotal = 144d/288n/576i => PAL */
+ /* vtotal = 121d/242n/484i => NTSC */
+
+#define SYNC_ON_GREEN 32 /* sync on green */
+
+#define VMODE_NONINTERLACED 0 /* non interlaced */
+#define VMODE_INTERLACED 1 /* interlaced */
+#define VMODE_DOUBLE 2 /* double scan */
+#define VMODE_MASK 255
+
+#define VMODE_YWRAP 256 /* ywrap instead of panning */
+#define VMODE_SMOOTH_XPAN 512 /* smooth xpan possible (internally used) */
+#define VMODE_CONUPDATE 512 /* don't update x/yoffset */
+
+/* VGA data register ports */
+#define CRT_DC 0x3D5 /* CRT Controller Data Register - color emulation */
+#define CRT_DM 0x3B5 /* CRT Controller Data Register - mono emulation */
+#define ATT_R 0x3C1 /* Attribute Controller Data Read Register */
+#define GRA_D 0x3CF /* Graphics Controller Data Register */
+#define SEQ_D 0x3C5 /* Sequencer Data Register */
+
+#define MIS_R 0x3CC // Misc Output Read Register
+#define MIS_W 0x3C2 // Misc Output Write Register
+
+#define IS1_RC 0x3DA /* Input Status Register 1 - color emulation */
+#define IS1_RM 0x3BA /* Input Status Register 1 - mono emulation */
+#define PEL_D 0x3C9 /* PEL Data Register */
+#define PEL_MSK 0x3C6 /* PEL mask register */
+
+/* EGA-specific registers */
+#define GRA_E0 0x3CC /* Graphics enable processor 0 */
+#define GRA_E1 0x3CA /* Graphics enable processor 1 */
+
+
+/* VGA index register ports */
+#define CRT_IC 0x3D4 /* CRT Controller Index - color emulation */
+#define CRT_IM 0x3B4 /* CRT Controller Index - mono emulation */
+#define ATT_IW 0x3C0 /* Attribute Controller Index & Data Write Register */
+#define GRA_I 0x3CE /* Graphics Controller Index */
+#define SEQ_I 0x3C4 /* Sequencer Index */
+#define PEL_IW 0x3C8 /* PEL Write Index */
+#define PEL_IR 0x3C7 /* PEL Read Index */
+
+/* standard VGA indexes max counts */
+#define CRTC_C 25 /* 25 CRT Controller Registers sequentially set*/
+ // the remainder are not in the par array
+#define ATT_C 21 /* 21 Attribute Controller Registers */
+#define GRA_C 9 /* 9 Graphics Controller Registers */
+#define SEQ_C 5 /* 5 Sequencer Registers */
+#define MIS_C 1 /* 1 Misc Output Register */
+
+#define CRTC_H_TOTAL 0
+#define CRTC_H_DISP 1
+#define CRTC_H_BLANK_START 2
+#define CRTC_H_BLANK_END 3
+#define CRTC_H_SYNC_START 4
+#define CRTC_H_SYNC_END 5
+#define CRTC_V_TOTAL 6
+#define CRTC_OVERFLOW 7
+#define CRTC_PRESET_ROW 8
+#define CRTC_MAX_SCAN 9
+#define CRTC_CURSOR_START 0x0A
+#define CRTC_CURSOR_END 0x0B
+#define CRTC_START_HI 0x0C
+#define CRTC_START_LO 0x0D
+#define CRTC_CURSOR_HI 0x0E
+#define CRTC_CURSOR_LO 0x0F
+#define CRTC_V_SYNC_START 0x10
+#define CRTC_V_SYNC_END 0x11
+#define CRTC_V_DISP_END 0x12
+#define CRTC_OFFSET 0x13
+#define CRTC_UNDERLINE 0x14
+#define CRTC_V_BLANK_START 0x15
+#define CRTC_V_BLANK_END 0x16
+#define CRTC_MODE 0x17
+#define CRTC_LINE_COMPARE 0x18
+
+#define ATC_MODE 0x10
+#define ATC_OVERSCAN 0x11
+#define ATC_PLANE_ENABLE 0x12
+#define ATC_PEL 0x13
+#define ATC_COLOR_PAGE 0x14
+
+#define SEQ_CLOCK_MODE 0x01
+#define SEQ_PLANE_WRITE 0x02
+#define SEQ_CHARACTER_MAP 0x03
+#define SEQ_MEMORY_MODE 0x04
+
+#define GDC_SR_VALUE 0x00
+#define GDC_SR_ENABLE 0x01
+#define GDC_COMPARE_VALUE 0x02
+#define GDC_DATA_ROTATE 0x03
+#define GDC_PLANE_READ 0x04
+#define GDC_MODE 0x05
+#define GDC_MISC 0x06
+#define GDC_COMPARE_MASK 0x07
+#define GDC_BIT_MASK 0x08
+
+// text attributes
+#define VGA_ATTR_CLR_RED 0x4
+#define VGA_ATTR_CLR_GRN 0x2
+#define VGA_ATTR_CLR_BLU 0x1
+#define VGA_ATTR_CLR_YEL (VGA_ATTR_CLR_RED | VGA_ATTR_CLR_GRN)
+#define VGA_ATTR_CLR_CYN (VGA_ATTR_CLR_GRN | VGA_ATTR_CLR_BLU)
+#define VGA_ATTR_CLR_MAG (VGA_ATTR_CLR_BLU | VGA_ATTR_CLR_RED)
+#define VGA_ATTR_CLR_BLK 0
+#define VGA_ATTR_CLR_WHT (VGA_ATTR_CLR_RED | VGA_ATTR_CLR_GRN | VGA_ATTR_CLR_BLU)
+#define VGA_ATTR_BNK 0x80
+#define VGA_ATTR_ITN 0x08
+
+/*
+ * vga register parameters
+ * these are copied to the
+ * registers.
+ *
+ */
+struct vga_par {
+ u8 crtc[CRTC_C];
+ u8 atc[ATT_C];
+ u8 gdc[GRA_C];
+ u8 seq[SEQ_C];
+ u8 misc; // the misc register, MIS_W
+ u8 vss;
+};
+
+
+/* Interpretation of offset for color fields: All offsets are from the right,
+ * inside a "pixel" value, which is exactly 'bits_per_pixel' wide (means: you
+ * can use the offset as right argument to <<). A pixel afterwards is a bit
+ * stream and is written to video memory as that unmodified. This implies
+ * big-endian byte order if bits_per_pixel is greater than 8.
+ */
+struct fb_bitfield {
+ __u32 offset; /* beginning of bitfield */
+ __u32 length; /* length of bitfield */
+ __u32 msb_right; /* != 0 : Most significant bit is */
+ /* right */
+};
+
+struct screeninfo {
+ __u32 xres; /* visible resolution */
+ __u32 yres;
+ __u32 xres_virtual; /* virtual resolution */
+ __u32 yres_virtual;
+ __u32 xoffset; /* offset from virtual to visible */
+ __u32 yoffset; /* resolution */
+
+ __u32 bits_per_pixel; /* guess what */
+ __u32 grayscale; /* != 0 Graylevels instead of colors */
+
+ struct fb_bitfield red; /* bitfield in fb mem if true color, */
+ struct fb_bitfield green; /* else only length is significant */
+ struct fb_bitfield blue;
+ struct fb_bitfield transp; /* transparency */
+
+ __u32 nonstd; /* != 0 Non standard pixel format */
+
+ __u32 activate; /* see FB_ACTIVATE_* */
+
+ __u32 height; /* height of picture in mm */
+ __u32 width; /* width of picture in mm */
+
+ __u32 accel_flags; /* acceleration flags (hints) */
+
+ /* Timing: All values in pixclocks, except pixclock (of course) */
+ __u32 pixclock; /* pixel clock in ps (pico seconds) */
+ __u32 left_margin; /* time from sync to picture */
+ __u32 right_margin; /* time from picture to sync */
+ __u32 upper_margin; /* time from sync to picture */
+ __u32 lower_margin;
+ __u32 hsync_len; /* length of horizontal sync */
+ __u32 vsync_len; /* length of vertical sync */
+ __u32 sync; /* sync polarity */
+ __u32 vmode; /* interlaced etc */
+ __u32 reserved[6]; /* Reserved for future compatibility */
+};
+
+#endif
diff --git a/src/arch/i386/prefix/bImageprefix.S b/src/arch/i386/prefix/bImageprefix.S
new file mode 100644
index 00000000..a478713d
--- /dev/null
+++ b/src/arch/i386/prefix/bImageprefix.S
@@ -0,0 +1,614 @@
+/*
+ Copyright (C) 2000, Entity Cyber, Inc.
+
+ Authors: Gary Byers (gb@thinguin.org)
+ Marty Connor (mdc@thinguin.org)
+ Eric Biederman (ebiederman@lnxi.com)
+
+ This code also derives a lot from arch/i386/boot/setup.S in
+ the linux kernel.
+
+ This software may be used and distributed according to the terms
+ of the GNU Public License (GPL), incorporated herein by reference.
+
+ Description:
+
+ This is just a little bit of code and data that can get prepended
+ to an Etherboot ROM image in order to allow LILO to load the
+ result as if it were a Linux kernel image.
+
+ A real Linux kernel image consists of a one-sector boot loader
+ (to load the image from a floppy disk), followed a few sectors
+ of setup code, followed by the kernel code itself. There's
+ a table in the first sector (starting at offset 497) that indicates
+ how many sectors of setup code follow the first sector and which
+ contains some other parameters that aren't interesting in this
+ case.
+
+ When LILO loads the sectors that comprise a kernel image, it doesn't
+ execute the code in the first sector (since that code would try to
+ load the image from a floppy disk.) The code in the first sector
+ below doesn't expect to get executed (and prints an error message
+ if it ever -is- executed.) LILO's only interested in knowing the
+ number of setup sectors advertised in the table (at offset 497 in
+ the first sector.)
+
+ Etherboot doesn't require much in the way of setup code.
+ Historically, the Linux kernel required at least 4 sectors of
+ setup code. Current versions of LILO look at the byte at
+ offset 497 in the first sector to indicate how many sectors
+ of setup code are contained in the image.
+
+ The setup code that is present here does a lot of things
+ exactly the way the linux kernel does them instead of in
+ ways more typical of etherboot. Generally this is so
+ the code can be strongly compatible with the linux kernel.
+ In addition the general etherboot technique of enabling the a20
+ after we switch into protected mode does not work if etherboot
+ is being loaded at 1MB.
+*/
+
+ .equ CR0_PE,1
+
+#ifdef GAS291
+#define DATA32 data32;
+#define ADDR32 addr32;
+#define LJMPI(x) ljmp x
+#else
+#define DATA32 data32
+#define ADDR32 addr32
+/* newer GAS295 require #define LJMPI(x) ljmp *x */
+#define LJMPI(x) ljmp x
+#endif
+
+/* Simple and small GDT entries for booting only */
+#define GDT_ENTRY_BOOT_CS 2
+#define GDT_ENTRY_BOOT_DS (GDT_ENTRY_BOOT_CS + 1)
+#define __BOOT_CS (GDT_ENTRY_BOOT_CS * 8)
+#define __BOOT_DS (GDT_ENTRY_BOOT_DS * 8)
+
+
+#define SETUPSECS 4 /* Minimal nr of setup-sectors */
+#define PREFIXSIZE ((SETUPSECS+1)*512)
+#define PREFIXPGH (PREFIXSIZE / 16 )
+#define BOOTSEG 0x07C0 /* original address of boot-sector */
+#define INITSEG 0x9000 /* we move boot here - out of the way */
+#define SETUPSEG 0x9020 /* setup starts here */
+#define SYSSEG 0x1000 /* system loaded at 0x10000 (65536). */
+
+#define DELTA_INITSEG (SETUPSEG - INITSEG) /* 0x0020 */
+
+/* Signature words to ensure LILO loaded us right */
+#define SIG1 0xAA55
+#define SIG2 0x5A5A
+
+ .text
+ .code16
+ .arch i386
+ .org 0
+ .section ".prefix", "ax", @progbits
+ .globl _prefix
+_prefix:
+
+/*
+ This is a minimal boot sector. If anyone tries to execute it (e.g., if
+ a .lilo file is dd'ed to a floppy), print an error message.
+*/
+
+bootsector:
+ jmp $BOOTSEG, $go - _prefix /* reload cs:ip to match relocation addr */
+go:
+ movw $0x2000, %di /* 0x2000 is arbitrary value >= length
+ of bootsect + room for stack */
+
+ movw $BOOTSEG, %ax
+ movw %ax,%ds
+ movw %ax,%es
+
+ cli
+ movw %ax, %ss /* put stack at BOOTSEG:0x2000. */
+ movw %di,%sp
+ sti
+
+ movw $why_end-why, %cx
+ movw $why - _prefix, %si
+
+ movw $0x0007, %bx /* page 0, attribute 7 (normal) */
+ movb $0x0e, %ah /* write char, tty mode */
+prloop:
+ lodsb
+ int $0x10
+ loop prloop
+freeze: jmp freeze
+
+why: .ascii "This image cannot be loaded from a floppy disk.\r\n"
+why_end:
+
+
+ .org 497
+setup_sects:
+ .byte SETUPSECS
+root_flags:
+ .word 0
+syssize:
+ .word _verbatim_size_pgh - PREFIXPGH
+swap_dev:
+ .word 0
+ram_size:
+ .word 0
+vid_mode:
+ .word 0
+root_dev:
+ .word 0
+boot_flag:
+ .word 0xAA55
+
+/*
+ We're now at the beginning of the second sector of the image -
+ where the setup code goes.
+
+ We don't need to do too much setup for Etherboot.
+
+ This code gets loaded at SETUPSEG:0. It wants to start
+ executing the Etherboot image that's loaded at SYSSEG:0 and
+ whose entry point is SYSSEG:0.
+*/
+setup_code:
+ jmp trampoline
+# This is the setup header, and it must start at %cs:2 (old 0x9020:2)
+
+ .ascii "HdrS" # header signature
+ .word 0x0203 # header version number (>= 0x0105)
+ # or else old loadlin-1.5 will fail)
+realmode_swtch: .word 0, 0 # default_switch, SETUPSEG
+start_sys_seg: .word SYSSEG # low load segment (obsolete)
+ .word kernel_version - setup_code
+ # pointing to kernel version string
+ # above section of header is compatible
+ # with loadlin-1.5 (header v1.5). Don't
+ # change it.
+
+type_of_loader: .byte 0 # = 0, old one (LILO, Loadlin,
+ # Bootlin, SYSLX, bootsect...)
+ # See Documentation/i386/boot.txt for
+ # assigned ids
+
+# flags, unused bits must be zero (RFU) bit within loadflags
+loadflags:
+LOADED_HIGH = 1 # If set, the kernel is loaded high
+CAN_USE_HEAP = 0x80 # If set, the loader also has set
+ # heap_end_ptr to tell how much
+ # space behind setup.S can be used for
+ # heap purposes.
+ # Only the loader knows what is free
+ .byte LOADED_HIGH
+
+setup_move_size: .word 0x8000 # size to move, when setup is not
+ # loaded at 0x90000. We will move setup
+ # to 0x90000 then just before jumping
+ # into the kernel. However, only the
+ # loader knows how much data behind
+ # us also needs to be loaded.
+
+code32_start: # here loaders can put a different
+ # start address for 32-bit code.
+ .long 0x100000 # 0x100000 = default for big kernel
+
+ramdisk_image: .long 0 # address of loaded ramdisk image
+ # Here the loader puts the 32-bit
+ # address where it loaded the image.
+ # This only will be read by the kernel.
+
+ramdisk_size: .long 0 # its size in bytes
+
+bootsect_kludge:
+ .long 0 # obsolete
+
+heap_end_ptr: .word 0 # (Header version 0x0201 or later)
+ # space from here (exclusive) down to
+ # end of setup code can be used by setup
+ # for local heap purposes.
+
+pad1: .word 0
+cmd_line_ptr: .long 0 # (Header version 0x0202 or later)
+ # If nonzero, a 32-bit pointer
+ # to the kernel command line.
+ # The command line should be
+ # located between the start of
+ # setup and the end of low
+ # memory (0xa0000), or it may
+ # get overwritten before it
+ # gets read. If this field is
+ # used, there is no longer
+ # anything magical about the
+ # 0x90000 segment; the setup
+ # can be located anywhere in
+ # low memory 0x10000 or higher.
+
+ramdisk_max: .long 0 # (Header version 0x0203 or later)
+ # The highest safe address for
+ # the contents of an initrd
+
+trampoline: call start_of_setup
+trampoline_end:
+ .space 1024
+# End of setup header #####################################################
+
+start_of_setup:
+# Set %ds = %cs, we know that SETUPSEG = %cs at this point
+ movw %cs, %ax # aka SETUPSEG
+ movw %ax, %ds
+# Check signature at end of setup
+ cmpw $SIG1, (setup_sig1 - setup_code)
+ jne bad_sig
+
+ cmpw $SIG2, (setup_sig2 - setup_code)
+ jne bad_sig
+
+ jmp good_sig1
+
+# Routine to print asciiz string at ds:si
+prtstr:
+ lodsb
+ andb %al, %al
+ jz fin
+
+ call prtchr
+ jmp prtstr
+
+fin: ret
+
+# Part of above routine, this one just prints ascii al
+prtchr: pushw %ax
+ pushw %cx
+ movw $7,%bx
+ movw $0x01, %cx
+ movb $0x0e, %ah
+ int $0x10
+ popw %cx
+ popw %ax
+ ret
+
+no_sig_mess: .string "No setup signature found ..."
+
+good_sig1:
+ jmp good_sig
+
+# We now have to find the rest of the setup code/data
+bad_sig:
+ movw %cs, %ax # SETUPSEG
+ subw $DELTA_INITSEG, %ax # INITSEG
+ movw %ax, %ds
+ xorb %bh, %bh
+ movb (497), %bl # get setup sect from bootsect
+ subw $4, %bx # LILO loads 4 sectors of setup
+ shlw $8, %bx # convert to words (1sect=2^8 words)
+ movw %bx, %cx
+ shrw $3, %bx # convert to segment
+ addw $SYSSEG, %bx
+ movw %bx, %cs:(start_sys_seg - setup_code)
+# Move rest of setup code/data to here
+ movw $2048, %di # four sectors loaded by LILO
+ subw %si, %si
+ pushw %cs
+ popw %es
+ movw $SYSSEG, %ax
+ movw %ax, %ds
+ rep
+ movsw
+ movw %cs, %ax # aka SETUPSEG
+ movw %ax, %ds
+ cmpw $SIG1, (setup_sig1 - setup_code)
+ jne no_sig
+
+ cmpw $SIG2, (setup_sig2 - setup_code)
+ jne no_sig
+
+ jmp good_sig
+
+no_sig:
+ lea (no_sig_mess - setup_code), %si
+ call prtstr
+
+no_sig_loop:
+ hlt
+ jmp no_sig_loop
+
+good_sig:
+ cmpw $0, %cs:(realmode_swtch - setup_code)
+ jz rmodeswtch_normal
+
+ lcall *%cs:(realmode_swtch - setup_code)
+ jmp rmodeswtch_end
+
+rmodeswtch_normal:
+ pushw %cs
+ call default_switch
+
+rmodeswtch_end:
+# we get the code32 start address and modify the below 'jmpi'
+# (loader may have changed it)
+ movl %cs:(code32_start - setup_code), %eax
+ movl %eax, %cs:(code32 - setup_code)
+
+# then we load the segment descriptors
+ movw %cs, %ax # aka SETUPSEG
+ movw %ax, %ds
+
+#
+# Enable A20. This is at the very best an annoying procedure.
+# A20 code ported from SYSLINUX 1.52-1.63 by H. Peter Anvin.
+#
+
+A20_TEST_LOOPS = 32 # Iterations per wait
+A20_ENABLE_LOOPS = 255 # Total loops to try
+
+a20_try_loop:
+
+ # First, see if we are on a system with no A20 gate.
+a20_none:
+ call a20_test
+ jnz a20_done
+
+ # Next, try the BIOS (INT 0x15, AX=0x2401)
+a20_bios:
+ movw $0x2401, %ax
+ pushfl # Be paranoid about flags
+ int $0x15
+ popfl
+
+ call a20_test
+ jnz a20_done
+
+ # Try enabling A20 through the keyboard controller
+a20_kbc:
+ call empty_8042
+
+ call a20_test # Just in case the BIOS worked
+ jnz a20_done # but had a delayed reaction.
+
+ movb $0xD1, %al # command write
+ outb %al, $0x64
+ call empty_8042
+
+ movb $0xDF, %al # A20 on
+ outb %al, $0x60
+ call empty_8042
+
+ # Wait until a20 really *is* enabled; it can take a fair amount of
+ # time on certain systems; Toshiba Tecras are known to have this
+ # problem.
+a20_kbc_wait:
+ xorw %cx, %cx
+a20_kbc_wait_loop:
+ call a20_test
+ jnz a20_done
+ loop a20_kbc_wait_loop
+
+ # Final attempt: use "configuration port A"
+a20_fast:
+ inb $0x92, %al # Configuration Port A
+ orb $0x02, %al # "fast A20" version
+ andb $0xFE, %al # don't accidentally reset
+ outb %al, $0x92
+
+ # Wait for configuration port A to take effect
+a20_fast_wait:
+ xorw %cx, %cx
+a20_fast_wait_loop:
+ call a20_test
+ jnz a20_done
+ loop a20_fast_wait_loop
+
+ # A20 is still not responding. Try frobbing it again.
+ #
+ decb (a20_tries - setup_code)
+ jnz a20_try_loop
+
+ movw $(a20_err_msg - setup_code), %si
+ call prtstr
+
+a20_die:
+ hlt
+ jmp a20_die
+
+a20_tries:
+ .byte A20_ENABLE_LOOPS
+
+a20_err_msg:
+ .ascii "linux: fatal error: A20 gate not responding!"
+ .byte 13, 10, 0
+
+ # If we get here, all is good
+a20_done:
+ # Leave the idt alone
+
+ # set up gdt
+ xorl %eax, %eax # Compute gdt_base
+ movw %ds, %ax # (Convert %ds:gdt to a linear ptr)
+ shll $4, %eax
+ addl $(bImage_gdt - setup_code), %eax
+ movl %eax, (bImage_gdt_48+2 - setup_code)
+ DATA32 lgdt %ds:(bImage_gdt_48 - setup_code) # load gdt with whatever is
+ # appropriate
+
+ # Switch to protected mode
+ movl %cr0, %eax
+ orb $CR0_PE, %al
+ movl %eax, %cr0
+
+ DATA32 ljmp %ds:(code32 - setup_code)
+code32:
+ .long 0x100000
+ .word __BOOT_CS, 0
+
+# Here's a bunch of information about your current kernel..
+kernel_version: .ascii "Etherboot "
+ .ascii VERSION
+ .byte 0
+
+# This is the default real mode switch routine.
+# to be called just before protected mode transition
+default_switch:
+ cli # no interrupts allowed !
+ movb $0x80, %al # disable NMI for bootup
+ # sequence
+ outb %al, $0x70
+ lret
+
+# This routine tests whether or not A20 is enabled. If so, it
+# exits with zf = 0.
+#
+# The memory address used, 0x200, is the int $0x80 vector, which
+# should be safe.
+
+A20_TEST_ADDR = 4*0x80
+
+a20_test:
+ pushw %cx
+ pushw %ax
+ xorw %cx, %cx
+ movw %cx, %fs # Low memory
+ decw %cx
+ movw %cx, %gs # High memory area
+ movw $A20_TEST_LOOPS, %cx
+ movw %fs:(A20_TEST_ADDR), %ax
+ pushw %ax
+a20_test_wait:
+ incw %ax
+ movw %ax, %fs:(A20_TEST_ADDR)
+ call delay # Serialize and make delay constant
+ cmpw %gs:(A20_TEST_ADDR+0x10), %ax
+ loope a20_test_wait
+
+ popw %fs:(A20_TEST_ADDR)
+ popw %ax
+ popw %cx
+ ret
+
+
+# This routine checks that the keyboard command queue is empty
+# (after emptying the output buffers)
+#
+# Some machines have delusions that the keyboard buffer is always full
+# with no keyboard attached...
+#
+# If there is no keyboard controller, we will usually get 0xff
+# to all the reads. With each IO taking a microsecond and
+# a timeout of 100,000 iterations, this can take about half a
+# second ("delay" == outb to port 0x80). That should be ok,
+# and should also be plenty of time for a real keyboard controller
+# to empty.
+#
+
+empty_8042:
+ pushl %ecx
+ movl $100000, %ecx
+
+empty_8042_loop:
+ decl %ecx
+ jz empty_8042_end_loop
+
+ call delay
+
+ inb $0x64, %al # 8042 status port
+ testb $1, %al # output buffer?
+ jz no_output
+
+ call delay
+ inb $0x60, %al # read it
+ jmp empty_8042_loop
+
+no_output:
+ testb $2, %al # is input buffer full?
+ jnz empty_8042_loop # yes - loop
+empty_8042_end_loop:
+ popl %ecx
+
+
+# Delay is needed after doing I/O
+delay:
+ outb %al,$0x80
+ ret
+
+# Descriptor tables
+#
+# NOTE: The intel manual says gdt should be sixteen bytes aligned for
+# efficiency reasons. However, there are machines which are known not
+# to boot with misaligned GDTs, so alter this at your peril! If you alter
+# GDT_ENTRY_BOOT_CS (in asm/segment.h) remember to leave at least two
+# empty GDT entries (one for NULL and one reserved).
+#
+# NOTE: On some CPUs, the GDT must be 8 byte aligned. This is
+# true for the Voyager Quad CPU card which will not boot without
+# This directive. 16 byte aligment is recommended by intel.
+#
+ .balign 16
+bImage_gdt:
+ .fill GDT_ENTRY_BOOT_CS,8,0
+
+ .word 0xFFFF # 4Gb - (0x100000*0x1000 = 4Gb)
+ .word 0 # base address = 0
+ .word 0x9A00 # code read/exec
+ .word 0x00CF # granularity = 4096, 386
+ # (+5th nibble of limit)
+
+ .word 0xFFFF # 4Gb - (0x100000*0x1000 = 4Gb)
+ .word 0 # base address = 0
+ .word 0x9200 # data read/write
+ .word 0x00CF # granularity = 4096, 386
+ # (+5th nibble of limit)
+bImage_gdt_end:
+ .balign 4
+
+ .word 0 # alignment byte
+bImage_idt_48:
+ .word 0 # idt limit = 0
+ .long 0 # idt base = 0L
+
+ .word 0 # alignment byte
+bImage_gdt_48:
+ .word bImage_gdt_end - bImage_gdt - 1 # gdt limit
+ .long bImage_gdt_48 - setup_code # gdt base (filled in later)
+
+ .section ".text16", "ax", @progbits
+ .globl prefix_exit
+prefix_exit:
+ int $0x19 /* should try to boot machine */
+ .globl prefix_exit_end
+prefix_exit_end:
+ .previous
+
+
+ .org (PREFIXSIZE - 4)
+# Setup signature -- must be last
+setup_sig1: .word SIG1
+setup_sig2: .word SIG2
+ /* Etherboot expects to be contiguous in memory once loaded.
+ * The linux bImage protocol does not do this, but since we
+ * don't need any information that's left in the prefix, it
+ * doesn't matter: we just have to ensure that we make it to _start
+ *
+ * protected_start will live at 0x100000 and it will be the
+ * the first code called as we enter protected mode.
+ */
+ .code32
+protected_start:
+ /* Load segment registers */
+ movw $__BOOT_DS, %ax
+ movw %ax, %ss
+ movw %ax, %ds
+ movw %ax, %es
+ movw %ax, %fs
+ movw %ax, %gs
+
+ /* Use the internal etherboot stack */
+ movl $(_prefix_stack_end - protected_start + 0x100000), %esp
+
+ pushl $0 /* No parameters to preserve for exit path */
+ pushl $0 /* Use prefix exit path mechanism */
+
+ jmp _start
+/*
+ That's about it.
+*/
diff --git a/src/arch/i386/prefix/boot1a.s b/src/arch/i386/prefix/boot1a.s
new file mode 100644
index 00000000..557462f1
--- /dev/null
+++ b/src/arch/i386/prefix/boot1a.s
@@ -0,0 +1,410 @@
+# This code is no longer used in Etherboot. It is not maintained and
+# may not work.
+
+
+#
+# Copyright (c) 1998 Robert Nordier
+# All rights reserved.
+# Very small bootrom changes by Luigi Rizzo
+# <comment author="Luigi Rizzo">
+# I recently had the problem of downloading the etherboot code
+# from a hard disk partition instead of a floppy, and noticed that
+# floppyload.S does not do the job. With a bit of hacking to
+# the FreeBSD's boot1.s code, I managed to obtain a boot sector
+# which works both for floppies and hard disks -- basically you
+# do something like
+#
+# cat boot1a bin32/<yourcard>.lzrom > /dev/ad0s4
+#
+# (or whatever is the HD partition you are using, I am using slice
+# 4 on FreeBSD) and you are up and running.
+# Then with "fdisk" you have to mark your partition as having type "1"
+# (which is listed as DOS-- but basically it must be something matching
+# the variable PRT_BSD in the assembly source below).
+# </comment>
+#
+# Redistribution and use in source and binary forms are freely
+# permitted provided that the above copyright notice and this
+# paragraph and the following disclaimer are duplicated in all
+# such forms.
+#
+# This software is provided "AS IS" and without any express or
+# implied warranties, including, without limitation, the implied
+# warranties of merchantability and fitness for a particular
+# purpose.
+#
+# Makefile:
+#boot1a: boot1a.out
+# objcopy -S -O binary boot1a.out boot1a
+#
+#boot1a.out: boot1a.o
+# ld -nostdlib -static -N -e start -Ttext 0x7c00 -o boot1a.out boot1a.o
+#
+#boot1a.o: boot1a.s
+# as --defsym FLAGS=0x80 boot1a.s -o boot1a.o
+#
+#
+
+# $FreeBSD: src/sys/boot/i386/boot2/boot1.s,v 1.10.2.2 2000/07/07 21:12:32 jhb Exp $
+
+# Memory Locations
+ .set MEM_REL,0x700 # Relocation address
+ .set MEM_ARG,0x900 # Arguments
+ .set MEM_ORG,0x7c00 # Origin
+ .set MEM_BUF,0x8c00 # Load area
+ .set MEM_BTX,0x9000 # BTX start
+ .set MEM_JMP,0x9010 # BTX entry point
+ .set MEM_USR,0xa000 # Client start
+ .set BDA_BOOT,0x472 # Boot howto flag
+
+# Partition Constants
+ .set PRT_OFF,0x1be # Partition offset
+ .set PRT_NUM,0x4 # Partitions
+ .set PRT_BSD,0x1 # Partition type
+
+# Flag Bits
+ .set FL_PACKET,0x80 # Packet mode
+
+# Misc. Constants
+ .set SIZ_PAG,0x1000 # Page size
+ .set SIZ_SEC,0x200 # Sector size
+
+ .globl start
+ .globl xread
+ .code16
+
+start: jmp main # Start recognizably
+
+ .org 0x4,0x90
+#
+# Trampoline used by boot2 to call read to read data from the disk via
+# the BIOS. Call with:
+#
+# %cx:%ax - long - LBA to read in
+# %es:(%bx) - caddr_t - buffer to read data into
+# %dl - byte - drive to read from
+# %dh - byte - num sectors to read
+#
+
+xread: push %ss # Address
+ pop %ds # data
+#
+# Setup an EDD disk packet and pass it to read
+#
+xread.1: # Starting
+ pushl $0x0 # absolute
+ push %cx # block
+ push %ax # number
+ push %es # Address of
+ push %bx # transfer buffer
+ xor %ax,%ax # Number of
+ movb %dh,%al # blocks to
+ push %ax # transfer
+ push $0x10 # Size of packet
+ mov %sp,%bp # Packet pointer
+ callw read # Read from disk
+ lea 0x10(%bp),%sp # Clear stack
+ lret # To far caller
+#
+# Load the rest of boot2 and BTX up, copy the parts to the right locations,
+# and start it all up.
+#
+
+#
+# Setup the segment registers to flat addressing (segment 0) and setup the
+# stack to end just below the start of our code.
+#
+main: cld # String ops inc
+ xor %cx,%cx # Zero
+ mov %cx,%es # Address
+ mov %cx,%ds # data
+ mov %cx,%ss # Set up
+ mov $start,%sp # stack
+#
+# Relocate ourself to MEM_REL. Since %cx == 0, the inc %ch sets
+# %cx == 0x100.
+#
+ mov %sp,%si # Source
+ mov $MEM_REL,%di # Destination
+ incb %ch # Word count
+ rep # Copy
+ movsw # code
+#
+# If we are on a hard drive, then load the MBR and look for the first
+# FreeBSD slice. We use the fake partition entry below that points to
+# the MBR when we call nread. The first pass looks for the first active
+# FreeBSD slice. The second pass looks for the first non-active FreeBSD
+# slice if the first one fails.
+#
+ mov $part4,%si # Partition
+ cmpb $0x80,%dl # Hard drive?
+ jb main.4 # No
+ movb $0x1,%dh # Block count
+ callw nread # Read MBR
+ mov $0x1,%cx # Two passes
+main.1: mov $MEM_BUF+PRT_OFF,%si # Partition table
+ movb $0x1,%dh # Partition
+main.2: cmpb $PRT_BSD,0x4(%si) # Our partition type?
+ jne main.3 # No
+ jcxz main.5 # If second pass
+ testb $0x80,(%si) # Active?
+ jnz main.5 # Yes
+main.3: add $0x10,%si # Next entry
+ incb %dh # Partition
+ cmpb $0x1+PRT_NUM,%dh # In table?
+ jb main.2 # Yes
+ dec %cx # Do two
+ jcxz main.1 # passes
+#
+# If we get here, we didn't find any FreeBSD slices at all, so print an
+# error message and die.
+#
+booterror: mov $msg_part,%si # Message
+ jmp error # Error
+#
+# Floppies use partition 0 of drive 0.
+#
+main.4: xor %dx,%dx # Partition:drive
+#
+# Ok, we have a slice and drive in %dx now, so use that to locate and load
+# boot2. %si references the start of the slice we are looking for, so go
+# ahead and load up the first 16 sectors (boot1 + boot2) from that. When
+# we read it in, we conveniently use 0x8c00 as our transfer buffer. Thus,
+# boot1 ends up at 0x8c00, and boot2 starts at 0x8c00 + 0x200 = 0x8e00.
+# The first part of boot2 is the disklabel, which is 0x200 bytes long.
+# The second part is BTX, which is thus loaded into 0x9000, which is where
+# it also runs from. The boot2.bin binary starts right after the end of
+# BTX, so we have to figure out where the start of it is and then move the
+# binary to 0xb000. Normally, BTX clients start at MEM_USR, or 0xa000, but
+# when we use btxld create boot2, we use an entry point of 0x1000. That
+# entry point is relative to MEM_USR; thus boot2.bin starts at 0xb000.
+#
+main.5: mov %dx,MEM_ARG # Save args
+ movb $0x2,%dh # Sector count
+ mov $0x7e00, %bx
+ callw nreadbx # Read disk
+ movb $0x40,%dh # Sector count
+ movb %dh, %al
+ callw puthex
+ mov $0x7e00, %bx
+ callw nreadbx # Read disk
+ push %si
+ mov $msg_r1,%si
+ callw putstr
+ pop %si
+ lcall $0x800,$0 # enter the rom code
+ int $0x19
+
+msg_r1: .asciz " done\r\n"
+
+.if 0
+ mov $MEM_BTX,%bx # BTX
+ mov 0xa(%bx),%si # Get BTX length and set
+ add %bx,%si # %si to start of boot2.bin
+ mov $MEM_USR+SIZ_PAG,%di # Client page 1
+ mov $MEM_BTX+0xe*SIZ_SEC,%cx # Byte
+ sub %si,%cx # count
+ rep # Relocate
+ movsb # client
+ sub %di,%cx # Byte count
+ xorb %al,%al # Zero assumed bss from
+ rep # the end of boot2.bin
+ stosb # up to 0x10000
+ callw seta20 # Enable A20
+ jmp start+MEM_JMP-MEM_ORG # Start BTX
+#
+# Enable A20 so we can access memory above 1 meg.
+#
+seta20: cli # Disable interrupts
+seta20.1: inb $0x64,%al # Get status
+ testb $0x2,%al # Busy?
+ jnz seta20.1 # Yes
+ movb $0xd1,%al # Command: Write
+ outb %al,$0x64 # output port
+seta20.2: inb $0x64,%al # Get status
+ testb $0x2,%al # Busy?
+ jnz seta20.2 # Yes
+ movb $0xdf,%al # Enable
+ outb %al,$0x60 # A20
+ sti # Enable interrupts
+ retw # To caller
+.endif
+#
+# Trampoline used to call read from within boot1.
+#
+nread: mov $MEM_BUF,%bx # Transfer buffer
+nreadbx: # same but address is in bx
+ mov 0x8(%si),%ax # Get
+ mov 0xa(%si),%cx # LBA
+ push %bx
+ push %ax
+ callw putword
+ pop %ax
+ pop %bx
+ push %cs # Read from
+ callw xread.1 # disk
+ jnc return # If success, return
+ mov $msg_read,%si # Otherwise, set the error
+ # message and fall through to
+ # the error routine
+#
+# Print out the error message pointed to by %ds:(%si) followed
+# by a prompt, wait for a keypress, and then reboot the machine.
+#
+error: callw putstr # Display message
+ mov $prompt,%si # Display
+ callw putstr # prompt
+ xorb %ah,%ah # BIOS: Get
+ int $0x16 # keypress
+ movw $0x1234, BDA_BOOT # Do a warm boot
+ ljmp $0xffff,$0x0 # reboot the machine
+#
+# Display a null-terminated string using the BIOS output.
+#
+putstr.0: call putchar
+putstr: lodsb # Get char
+ testb %al,%al # End of string?
+ jne putstr.0 # No
+ retw
+
+putword: push %ax
+ movb $'.', %al
+ callw putchar
+ movb %ah, %al
+ callw puthex
+ pop %ax
+puthex: push %ax
+ shr $4, %al
+ callw putdigit
+ pop %ax
+putdigit:
+ andb $0xf, %al
+ addb $0x30, %al
+ cmpb $0x39, %al
+ jbe putchar
+ addb $7, %al
+putchar: push %ax
+ mov $0x7,%bx
+ movb $0xe,%ah
+ int $0x10
+ pop %ax
+ retw
+
+#
+# Overused return code. ereturn is used to return an error from the
+# read function. Since we assume putstr succeeds, we (ab)use the
+# same code when we return from putstr.
+#
+ereturn: movb $0x1,%ah # Invalid
+ stc # argument
+return: retw # To caller
+#
+# Reads sectors from the disk. If EDD is enabled, then check if it is
+# installed and use it if it is. If it is not installed or not enabled, then
+# fall back to using CHS. Since we use a LBA, if we are using CHS, we have to
+# fetch the drive parameters from the BIOS and divide it out ourselves.
+# Call with:
+#
+# %dl - byte - drive number
+# stack - 10 bytes - EDD Packet
+#
+read: push %dx # Save
+ movb $0x8,%ah # BIOS: Get drive
+ int $0x13 # parameters
+ movb %dh,%ch # Max head number
+ pop %dx # Restore
+ jc return # If error
+ andb $0x3f,%cl # Sectors per track
+ jz ereturn # If zero
+ cli # Disable interrupts
+ mov 0x8(%bp),%eax # Get LBA
+ push %dx # Save
+ movzbl %cl,%ebx # Divide by
+ xor %edx,%edx # sectors
+ div %ebx # per track
+ movb %ch,%bl # Max head number
+ movb %dl,%ch # Sector number
+ inc %bx # Divide by
+ xorb %dl,%dl # number
+ div %ebx # of heads
+ movb %dl,%bh # Head number
+ pop %dx # Restore
+ cmpl $0x3ff,%eax # Cylinder number supportable?
+ sti # Enable interrupts
+ ja read.7 # No, try EDD
+ xchgb %al,%ah # Set up cylinder
+ rorb $0x2,%al # number
+ orb %ch,%al # Merge
+ inc %ax # sector
+ xchg %ax,%cx # number
+ movb %bh,%dh # Head number
+ subb %ah,%al # Sectors this track
+ mov 0x2(%bp),%ah # Blocks to read
+ cmpb %ah,%al # To read
+ jb read.2 # this
+ movb %ah,%al # track
+read.2: mov $0x5,%di # Try count
+read.3: les 0x4(%bp),%bx # Transfer buffer
+ push %ax # Save
+ movb $0x2,%ah # BIOS: Read
+ int $0x13 # from disk
+ pop %bx # Restore
+ jnc read.4 # If success
+ dec %di # Retry?
+ jz read.6 # No
+ xorb %ah,%ah # BIOS: Reset
+ int $0x13 # disk system
+ xchg %bx,%ax # Block count
+ jmp read.3 # Continue
+read.4: movzbw %bl,%ax # Sectors read
+ add %ax,0x8(%bp) # Adjust
+ jnc read.5 # LBA,
+ incw 0xa(%bp) # transfer
+read.5: shlb %bl # buffer
+ add %bl,0x5(%bp) # pointer,
+ sub %al,0x2(%bp) # block count
+ ja read # If not done
+read.6: retw # To caller
+read.7: testb $FL_PACKET,%cs:MEM_REL+flags-start # LBA support enabled?
+ jz ereturn # No, so return an error
+ mov $0x55aa,%bx # Magic
+ push %dx # Save
+ movb $0x41,%ah # BIOS: Check
+ int $0x13 # extensions present
+ pop %dx # Restore
+ jc return # If error, return an error
+ cmp $0xaa55,%bx # Magic?
+ jne ereturn # No, so return an error
+ testb $0x1,%cl # Packet interface?
+ jz ereturn # No, so return an error
+ mov %bp,%si # Disk packet
+ movb $0x42,%ah # BIOS: Extended
+ int $0x13 # read
+ retw # To caller
+
+# Messages
+
+msg_read: .asciz "Rd"
+msg_part: .asciz "Boot"
+
+prompt: .asciz " err\r\n"
+
+flags: .byte FLAGS # Flags
+
+ .org PRT_OFF,0x90
+
+# Partition table
+
+ .fill 0x30,0x1,0x0
+part4: .byte 0x80
+ .byte 0x00 # start head
+ .byte 0x01 # start sector (6 bits) + start cyl (2 bit)
+ .byte 0x00 # start cyl (low 8 bits)
+ .byte 0x1 # part.type
+ .byte 0xff # end head
+ .byte 0xff # end sect (6) + end_cyl(2)
+ .byte 0xff # end cyl
+ .byte 0x00, 0x00, 0x00, 0x00 # explicit start
+ .byte 0x50, 0xc3, 0x00, 0x00 # 50000 sectors long, bleh
+
+ .word 0xaa55 # Magic number
diff --git a/src/arch/i386/prefix/comprefix.S b/src/arch/i386/prefix/comprefix.S
new file mode 100644
index 00000000..8be8db77
--- /dev/null
+++ b/src/arch/i386/prefix/comprefix.S
@@ -0,0 +1,49 @@
+/* We need a real mode stack that won't be stomped on by Etherboot
+ which starts at 0x20000. Choose something that's sufficiently high,
+ but not in DOC territory. Note that we couldn't do this in a real
+ .com program since stack variables are in the same segment as the
+ code and data, but this isn't really a .com program, it just looks
+ like one to make DOS load it into memory. It still has the 64kB
+ limitation of .com files though. */
+#define STACK_SEG 0x7000
+#define STACK_SIZE 0x4000
+
+ .text
+ .code16
+ .arch i386
+ .section ".prefix", "ax", @progbits
+ .globl _prefix
+
+/* Cheat a little with the relocations: .COM files are loaded at 0x100 */
+_prefix:
+ /* Set up temporary stack */
+ movw $STACK_SEG, %ax
+ movw %ax, %ss
+ movw $STACK_SIZE, %sp
+
+ pushl $0 /* No parameters to preserve for exit path */
+ pushw $0 /* Dummy return address - use prefix_exit */
+
+ /* Calculate segment address of image start */
+ pushw %cs
+ popw %ax
+ addw $(0x100/16), %ax
+ pushw %ax
+ pushw $_start
+ /* Calculated lcall to _start with %cs:0000 = image start */
+ lret
+
+ .section ".text16", "ax", @progbits
+ .globl prefix_exit
+prefix_exit:
+ movw $0x4c00,%ax /* return to DOS */
+ int $0x21 /* reach this on Quit */
+ .globl prefix_exit_end
+prefix_exit_end:
+ .previous
+
+/* The body of etherboot is attached here at build time.
+ * Force 16 byte alignment
+ */
+ .align 16,0
+_body:
diff --git a/src/arch/i386/prefix/elf_dprefix.S b/src/arch/i386/prefix/elf_dprefix.S
new file mode 100644
index 00000000..d2453be4
--- /dev/null
+++ b/src/arch/i386/prefix/elf_dprefix.S
@@ -0,0 +1,96 @@
+#include "elf.h"
+
+ .arch i386
+ .section ".prefix", "a", @progbits
+
+#define LOAD_ADDR 0x10000
+
+ /* ELF Header */
+ .globl elf_header
+elf_header:
+e_ident: .byte 0x7f, 'E', 'L', 'F', 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0
+e_type: .short ET_DYN
+e_machine: .short EM_386
+e_version: .long 1
+e_entry: .long LOAD_ADDR + _start - elf_header
+e_phoff: .long elf_program_header - elf_header
+e_shoff: .long 0
+e_flags: .long 0
+e_ehsize: .short elf_header_end - elf_header
+e_phentsize: .short ELF32_PHDR_SIZE
+e_phnum: .short (elf_program_header_end - elf_program_header)/ELF32_PHDR_SIZE
+e_shentsize: .short 0
+e_shnum: .short 0
+e_shstrndx: .short 0
+elf_header_end:
+
+elf_program_header:
+phdr1_p_type: .long PT_NOTE
+phdr1_p_offset: .long elf_note - elf_header
+phdr1_p_vaddr: .long elf_note
+phdr1_p_paddr: .long elf_note
+phdr1_p_filesz: .long elf_note_end - elf_note
+phdr1_p_memsz: .long elf_note_end - elf_note
+phdr1_p_flags: .long PF_R | PF_W | PF_X
+phdr1_p_align: .long 0
+
+/* The decompressor */
+phdr2_p_type: .long PT_LOAD
+phdr2_p_offset: .long 0
+phdr2_p_vaddr: .long elf_header
+phdr2_p_paddr: .long LOAD_ADDR
+phdr2_p_filesz: .long _verbatim_size
+phdr2_p_memsz: .long _image_size
+phdr2_p_flags: .long PF_R | PF_W | PF_X
+phdr2_p_align: .long 16
+
+elf_program_header_end:
+
+ .globl elf_note
+elf_note:
+ .balign 4
+ .int 2f - 1f
+ .int 4f - 3f
+ .int EIN_PROGRAM_NAME
+1: .asciz "ELFBoot"
+2:
+ .balign 4
+3:
+ .asciz "Etherboot"
+4:
+
+
+ .balign 4
+ .int 2f - 1f
+ .int 4f - 3f
+ .int EIN_PROGRAM_VERSION
+1: .asciz "ELFBoot"
+2:
+ .balign 4
+3:
+ .asciz VERSION
+4:
+
+#if 0
+ .balign 4
+ .int 2f - 1f
+ .int 4f - 3f
+ .int EIN_PROGRAM_CHECKSUM
+1: .asciz "ELFBoot"
+2:
+ .balign 4
+3:
+ .word 0
+4:
+#endif
+ .balign 4
+elf_note_end:
+
+ /* Dummy routines to satisfy the build */
+ .section ".text16", "ax", @progbits
+ .globl prefix_exit
+prefix_exit:
+
+ .globl prefix_exit_end
+prefix_exit_end:
+ .previous
diff --git a/src/arch/i386/prefix/elfprefix.S b/src/arch/i386/prefix/elfprefix.S
new file mode 100644
index 00000000..c5200678
--- /dev/null
+++ b/src/arch/i386/prefix/elfprefix.S
@@ -0,0 +1,96 @@
+#include "elf.h"
+
+ .arch i386
+ .section ".prefix", "a", @progbits
+
+#define LOAD_ADDR 0x10000
+
+ /* ELF Header */
+ .globl elf_header
+elf_header:
+e_ident: .byte 0x7f, 'E', 'L', 'F', 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0
+e_type: .short ET_EXEC
+e_machine: .short EM_386
+e_version: .long 1
+e_entry: .long LOAD_ADDR + _start - elf_header
+e_phoff: .long elf_program_header - elf_header
+e_shoff: .long 0
+e_flags: .long 0
+e_ehsize: .short elf_header_end - elf_header
+e_phentsize: .short ELF32_PHDR_SIZE
+e_phnum: .short (elf_program_header_end - elf_program_header)/ELF32_PHDR_SIZE
+e_shentsize: .short 0
+e_shnum: .short 0
+e_shstrndx: .short 0
+elf_header_end:
+
+elf_program_header:
+phdr1_p_type: .long PT_NOTE
+phdr1_p_offset: .long elf_note - elf_header
+phdr1_p_vaddr: .long elf_note
+phdr1_p_paddr: .long elf_note
+phdr1_p_filesz: .long elf_note_end - elf_note
+phdr1_p_memsz: .long elf_note_end - elf_note
+phdr1_p_flags: .long PF_R | PF_W | PF_X
+phdr1_p_align: .long 0
+
+/* The decompressor */
+phdr2_p_type: .long PT_LOAD
+phdr2_p_offset: .long 0
+phdr2_p_vaddr: .long elf_header
+phdr2_p_paddr: .long LOAD_ADDR
+phdr2_p_filesz: .long _verbatim_size
+phdr2_p_memsz: .long _image_size
+phdr2_p_flags: .long PF_R | PF_W | PF_X
+phdr2_p_align: .long 16
+
+elf_program_header_end:
+
+ .globl elf_note
+elf_note:
+ .balign 4
+ .int 2f - 1f
+ .int 4f - 3f
+ .int EIN_PROGRAM_NAME
+1: .asciz "ELFBoot"
+2:
+ .balign 4
+3:
+ .asciz "Etherboot"
+4:
+
+
+ .balign 4
+ .int 2f - 1f
+ .int 4f - 3f
+ .int EIN_PROGRAM_VERSION
+1: .asciz "ELFBoot"
+2:
+ .balign 4
+3:
+ .asciz VERSION
+4:
+
+#if 0
+ .balign 4
+ .int 2f - 1f
+ .int 4f - 3f
+ .int EIN_PROGRAM_CHECKSUM
+1: .asciz "ELFBoot"
+2:
+ .balign 4
+3:
+ .word 0
+4:
+#endif
+ .balign 4
+elf_note_end:
+
+ /* Dummy routines to satisfy the build */
+ .section ".text16", "ax", @progbits
+ .globl prefix_exit
+prefix_exit:
+
+ .globl prefix_exit_end
+prefix_exit_end:
+ .previous
diff --git a/src/arch/i386/prefix/exeprefix.S b/src/arch/i386/prefix/exeprefix.S
new file mode 100755
index 00000000..d169563e
--- /dev/null
+++ b/src/arch/i386/prefix/exeprefix.S
@@ -0,0 +1,44 @@
+/*
+ Prefix for .exe images
+ Doesn't work yet, even though it starts off the same as a .com
+ image as shown by DOS debug.
+*/
+
+ .text
+ .code16
+ .arch i386
+ .section ".prefix", "ax", @progbits
+ .globl _prefix
+
+_prefix:
+ .byte 'M', 'Z'
+ .short _exe_size_tail /* tail */
+ .short _exe_size_pages /* pages */
+ .short 0 /* relocations */
+ .short 2 /* header paras */
+ .short _exe_bss_size /* min */
+ .short 0xFFFF /* max paras */
+ .short _exe_ss_offset /* SS */
+ .short _stack_size /* SP */
+ .short 0 /* checksum */
+ .short 0 /* IP */
+ .short 0 /* CS */
+ .short 0x1C /* reloc offset */
+ .short 0 /* overlay number */
+ .short 0 /* fill */
+ .short 0 /* fill */
+
+ .section ".text16", "ax", @progbits
+ .globl prefix_exit
+prefix_exit:
+ movw $0x4c00,%ax /* return to DOS */
+ int $0x21 /* reach this on Quit */
+ .globl prefix_exit_end
+prefix_exit_end:
+ .previous
+
+/* The body of etherboot is attached here at build time.
+ * Force 16 byte alignment
+ */
+ .align 16,0
+_body:
diff --git a/src/arch/i386/prefix/floppyprefix.S b/src/arch/i386/prefix/floppyprefix.S
new file mode 100644
index 00000000..18bed4c8
--- /dev/null
+++ b/src/arch/i386/prefix/floppyprefix.S
@@ -0,0 +1,359 @@
+/* NOTE: this boot sector contains instructions that need at least an 80186.
+ * Yes, as86 has a bug somewhere in the valid instruction set checks.
+ *
+ * SYS_SIZE is the number of clicks (16 bytes) to be loaded.
+ */
+.equ SYSSIZE, 8192 # 8192 * 16 bytes = 128kB maximum size of .ROM file
+
+/* floppyload.S Copyright (C) 1991, 1992 Linus Torvalds
+ * modified by Drew Eckhardt
+ * modified by Bruce Evans (bde)
+ *
+ * floppyprefix.S is loaded at 0x0000:0x7c00 by the bios-startup routines.
+ *
+ * It then loads the system at SYSSEG<<4, using BIOS interrupts.
+ *
+ * The loader has been made as simple as possible, and continuous read errors
+ * will result in a unbreakable loop. Reboot by hand. It loads pretty fast by
+ * getting whole tracks at a time whenever possible.
+ */
+
+.equ BOOTSEG, 0x07C0 /* original address of boot-sector */
+
+.equ SYSSEG, 0x1000 /* system loaded at SYSSEG<<4 */
+
+ .org 0
+ .arch i386
+ .text
+ .section ".prefix", "ax", @progbits
+ .code16
+
+ jmp $BOOTSEG, $go /* reload cs:ip to match relocation addr */
+go:
+ movw $0x2000-12, %di /* 0x2000 is arbitrary value >= length */
+ /* of bootsect + room for stack + 12 for */
+ /* saved disk parm block */
+
+ movw $BOOTSEG, %ax
+ movw %ax,%ds
+ movw %ax,%es
+ movw %ax,%ss /* put stack at BOOTSEG:0x4000-12. */
+ movw %di,%sp
+
+/* Many BIOS's default disk parameter tables will not recognize multi-sector
+ * reads beyond the maximum sector number specified in the default diskette
+ * parameter tables - this may mean 7 sectors in some cases.
+ *
+ * Since single sector reads are slow and out of the question, we must take care
+ * of this by creating new parameter tables (for the first disk) in RAM. We
+ * will set the maximum sector count to 36 - the most we will encounter on an
+ * ED 2.88. High doesn't hurt. Low does.
+ *
+ * Segments are as follows: ds=es=ss=cs - BOOTSEG
+ */
+
+ xorw %cx,%cx
+ movw %cx,%es /* access segment 0 */
+ movw $0x78, %bx /* 0:bx is parameter table address */
+ pushw %ds /* save ds */
+/* 0:bx is parameter table address */
+ ldsw %es:(%bx),%si /* loads ds and si */
+
+ movw %ax,%es /* ax is BOOTSECT (loaded above) */
+ movb $6, %cl /* copy 12 bytes */
+ cld
+ pushw %di /* keep a copy for later */
+ rep
+ movsw /* ds:si is source, es:di is dest */
+ popw %di
+
+ movb $36,%es:4(%di)
+
+ movw %cx,%ds /* access segment 0 */
+ xchgw %di,(%bx)
+ movw %es,%si
+ xchgw %si,2(%bx)
+ popw %ds /* restore ds */
+ movw %di, dpoff /* save old parameters */
+ movw %si, dpseg /* to restore just before finishing */
+ pushw %ds
+ popw %es /* reload es */
+
+/* Note that es is already set up. Also cx is 0 from rep movsw above. */
+
+ xorb %ah,%ah /* reset FDC */
+ xorb %dl,%dl
+ int $0x13
+
+/* Get disk drive parameters, specifically number of sectors/track.
+ *
+ * It seems that there is no BIOS call to get the number of sectors. Guess
+ * 36 sectors if sector 36 can be read, 18 sectors if sector 18 can be read,
+ * 15 if sector 15 can be read. Otherwise guess 9.
+ */
+
+ movw $disksizes, %si /* table of sizes to try */
+
+probe_loop:
+ lodsb
+ cbtw /* extend to word */
+ movw %ax, sectors
+ cmpw $disksizes+4, %si
+ jae got_sectors /* if all else fails, try 9 */
+ xchgw %cx,%ax /* cx = track and sector */
+ xorw %dx,%dx /* drive 0, head 0 */
+ movw $0x0200, %bx /* address after boot sector */
+ /* (512 bytes from origin, es = cs) */
+ movw $0x0201, %ax /* service 2, 1 sector */
+ int $0x13
+ jc probe_loop /* try next value */
+
+got_sectors:
+ movw $msg1end-msg1, %cx
+ movw $msg1, %si
+ call print_str
+
+/* ok, we've written the Loading... message, now we want to load the system */
+
+ pushw %es /* = ds */
+ movw $SYSSEG, %ax
+ movw %ax,%es /* segment of SYSSEG<<4 */
+ pushw %es
+ call read_it
+
+/* This turns off the floppy drive motor, so that we enter the kernel in a
+ * known state, and don't have to worry about it later.
+ */
+ movw $0x3f2, %dx
+ xorb %al,%al
+ outb %al,%dx
+
+ call print_nl
+ pop %es /* = SYSSEG */
+ pop %es /* balance push/pop es */
+sigok:
+
+/* Restore original disk parameters */
+ movw $0x78, %bx
+ movw dpoff, %di
+ movw dpseg, %si
+ xorw %ax,%ax
+ movw %ax,%ds
+ movw %di,(%bx)
+ movw %si,2(%bx)
+
+/* after that (everything loaded), we call to the .ROM file loaded. */
+
+ pushl $0 /* No parameters to preserve for exit path */
+ pushw $0 /* Use prefix exit path mechanism */
+ ljmp $SYSSEG, $_start
+
+ .section ".text16", "ax", @progbits
+ .globl prefix_exit
+prefix_exit:
+ xchgw %bx, %bx
+ int $0x19 /* should try to boot machine */
+ .globl prefix_exit_end
+prefix_exit_end:
+ .previous
+
+/* This routine loads the system at address SYSSEG<<4, making sure no 64kB
+ * boundaries are crossed. We try to load it as fast as possible, loading whole
+ * tracks whenever we can.
+ *
+ * in: es - starting address segment (normally SYSSEG)
+ */
+read_it:
+ movw $0,sread /* read whole image incl boot sector */
+ movw %es,%ax
+ testw $0x0fff, %ax
+die: jne die /* es must be at 64kB boundary */
+ xorw %bx,%bx /* bx is starting address within segment */
+rp_read:
+ movw %es,%ax
+ movw %bx,%dx
+ movb $4, %cl
+ shrw %cl,%dx /* bx is always divisible by 16 */
+ addw %dx,%ax
+ cmpw $SYSSEG+SYSSIZE, %ax /* have we loaded all yet? */
+ jb ok1_read
+ ret
+ok1_read:
+ movw sectors, %ax
+ subw sread, %ax
+ movw %ax,%cx
+ shlw $9, %cx
+ addw %bx,%cx
+ jnc ok2_read
+ je ok2_read
+ xorw %ax,%ax
+ subw %bx,%ax
+ shrw $9, %ax
+ok2_read:
+ call read_track
+ movw %ax,%cx
+ addw sread, %ax
+ cmpw sectors, %ax
+ jne ok3_read
+ movw $1, %ax
+ subw head, %ax
+ jne ok4_read
+ incw track
+ok4_read:
+ movw %ax, head
+ xorw %ax,%ax
+ok3_read:
+ movw %ax, sread
+ shlw $9, %cx
+ addw %cx,%bx
+ jnc rp_read
+ movw %es,%ax
+ addb $0x10, %ah
+ movw %ax,%es
+ xorw %bx,%bx
+ jmp rp_read
+
+read_track:
+ pusha
+ pushw %ax
+ pushw %bx
+ pushw %bp /* just in case the BIOS is buggy */
+ movw $0x0e2e, %ax /* 0x2e = . */
+ movw $0x0007, %bx
+ int $0x10
+ popw %bp
+ popw %bx
+ popw %ax
+
+ movw track, %dx
+ movw sread, %cx
+ incw %cx
+ movb %dl,%ch
+ movw head, %dx
+ movb %dl,%dh
+ andw $0x0100, %dx
+ movb $2, %ah
+
+ pushw %dx /* save for error dump */
+ pushw %cx
+ pushw %bx
+ pushw %ax
+
+ int $0x13
+ jc bad_rt
+ addw $8, %sp
+ popa
+ ret
+
+bad_rt: pushw %ax /* save error code */
+ call print_all /* ah = error, al = read */
+
+ xorb %ah,%ah
+ xorb %dl,%dl
+ int $0x13
+
+ addw $10, %sp
+ popa
+ jmp read_track
+
+/* print_all is for debugging purposes. It will print out all of the registers.
+ * The assumption is that this is called from a routine, with a stack frame like
+ * dx
+ * cx
+ * bx
+ * ax
+ * error
+ * ret <- sp
+ */
+
+print_all:
+ call print_nl /* nl for readability */
+ movw $5, %cx /* error code + 4 registers */
+ movw %sp,%bp
+
+print_loop:
+ pushw %cx /* save count left */
+
+ cmpb $5, %cl
+ jae no_reg /* see if register name is needed */
+
+ movw $0x0007, %bx /* page 0, attribute 7 (normal) */
+ movw $0xe05+0x41-1, %ax
+ subb %cl,%al
+ int $0x10
+
+ movb $0x58, %al /* 'X' */
+ int $0x10
+
+ movb $0x3A, %al /* ':' */
+ int $0x10
+
+no_reg:
+ addw $2, %bp /* next register */
+ call print_hex /* print it */
+ movb $0x20, %al /* print a space */
+ int $0x10
+ popw %cx
+ loop print_loop
+ call print_nl /* nl for readability */
+ ret
+
+print_str:
+ movw $0x0007, %bx /* page 0, attribute 7 (normal) */
+ movb $0x0e, %ah /* write char, tty mode */
+prloop:
+ lodsb
+ int $0x10
+ loop prloop
+ ret
+
+print_nl:
+ movw $0x0007, %bx /* page 0, attribute 7 (normal) */
+ movw $0xe0d, %ax /* CR */
+ int $0x10
+ movb $0xa, %al /* LF */
+ int $0x10
+ ret
+
+/* print_hex prints the word pointed to by ss:bp in hexadecimal. */
+
+print_hex:
+ movw (%bp),%dx /* load word into dx */
+ movb $4, %cl
+ movb $0x0e, %ah /* write char, tty mode */
+ movw $0x0007, %bx /* page 0, attribute 7 (normal) */
+ call print_digit
+ call print_digit
+ call print_digit
+/* fall through */
+print_digit:
+ rol %cl,%dx /* rotate so that lowest 4 bits are used */
+ movb $0x0f, %al /* mask for nybble */
+ andb %dl,%al
+ addb $0x90, %al /* convert al to ascii hex (four instructions) */
+ daa
+ adcb $0x40, %al
+ daa
+ int $0x10
+ ret
+
+sread: .word 0 /* sectors read of current track */
+head: .word 0 /* current head */
+track: .word 0 /* current track */
+
+sectors:
+ .word 0
+
+dpseg: .word 0
+dpoff: .word 0
+
+disksizes:
+ .byte 36,18,15,9
+
+msg1:
+ .ascii "Loading ROM image"
+msg1end:
+
+ .org 510, 0
+ .word 0xAA55
+
diff --git a/src/arch/i386/prefix/huf.lds b/src/arch/i386/prefix/huf.lds
new file mode 100644
index 00000000..1e8da162
--- /dev/null
+++ b/src/arch/i386/prefix/huf.lds
@@ -0,0 +1,6 @@
+OUTPUT_FORMAT("elf32-i386", "elf32-i386", "elf32-i386")
+OUTPUT_ARCH(i386)
+
+SECTIONS {
+ .huf : { *(*) }
+}
diff --git a/src/arch/i386/prefix/img.lds b/src/arch/i386/prefix/img.lds
new file mode 100644
index 00000000..e383d89e
--- /dev/null
+++ b/src/arch/i386/prefix/img.lds
@@ -0,0 +1,6 @@
+OUTPUT_FORMAT("elf32-i386", "elf32-i386", "elf32-i386")
+OUTPUT_ARCH(i386)
+
+SECTIONS {
+ .img : { *(*) }
+}
diff --git a/src/arch/i386/prefix/liloprefix.S b/src/arch/i386/prefix/liloprefix.S
new file mode 100644
index 00000000..1aeb071f
--- /dev/null
+++ b/src/arch/i386/prefix/liloprefix.S
@@ -0,0 +1,144 @@
+/*
+ Copyright (C) 2000, Entity Cyber, Inc.
+
+ Authors: Gary Byers (gb@thinguin.org)
+ Marty Connor (mdc@thinguin.org)
+
+ This software may be used and distributed according to the terms
+ of the GNU Public License (GPL), incorporated herein by reference.
+
+ Description:
+
+ This is just a little bit of code and data that can get prepended
+ to an Etherboot ROM image in order to allow LILO to load the
+ result as if it were a Linux kernel image.
+
+ A real Linux kernel image consists of a one-sector boot loader
+ (to load the image from a floppy disk), followed a few sectors
+ of setup code, followed by the kernel code itself. There's
+ a table in the first sector (starting at offset 497) that indicates
+ how many sectors of setup code follow the first sector and which
+ contains some other parameters that aren't interesting in this
+ case.
+
+ When LILO loads the sectors that comprise a kernel image, it doesn't
+ execute the code in the first sector (since that code would try to
+ load the image from a floppy disk.) The code in the first sector
+ below doesn't expect to get executed (and prints an error message
+ if it ever -is- executed.) LILO's only interested in knowing the
+ number of setup sectors advertised in the table (at offset 497 in
+ the first sector.)
+
+ Etherboot doesn't require much in the way of setup code.
+ Historically, the Linux kernel required at least 4 sectors of
+ setup code. Current versions of LILO look at the byte at
+ offset 497 in the first sector to indicate how many sectors
+ of setup code are contained in the image.
+
+*/
+
+#define SETUPSECS 4 /* Minimal nr of setup-sectors */
+#define PREFIXSIZE ((SETUPSECS+1)*512)
+#define PREFIXPGH (PREFIXSIZE / 16 )
+#define BOOTSEG 0x07C0 /* original address of boot-sector */
+#define INITSEG 0x9000 /* we move boot here - out of the way */
+#define SETUPSEG 0x9020 /* setup starts here */
+#define SYSSEG 0x1000 /* system loaded at 0x10000 (65536). */
+
+ .text
+ .code16
+ .arch i386
+ .org 0
+ .section ".prefix", "ax", @progbits
+ .globl _prefix
+_prefix:
+
+/*
+ This is a minimal boot sector. If anyone tries to execute it (e.g., if
+ a .lilo file is dd'ed to a floppy), print an error message.
+*/
+
+bootsector:
+ jmp $BOOTSEG, $go - _prefix /* reload cs:ip to match relocation addr */
+go:
+ movw $0x2000, %di /* 0x2000 is arbitrary value >= length
+ of bootsect + room for stack */
+
+ movw $BOOTSEG, %ax
+ movw %ax,%ds
+ movw %ax,%es
+
+ cli
+ movw %ax, %ss /* put stack at BOOTSEG:0x2000. */
+ movw %di,%sp
+ sti
+
+ movw $why_end-why, %cx
+ movw $why - _prefix, %si
+
+ movw $0x0007, %bx /* page 0, attribute 7 (normal) */
+ movb $0x0e, %ah /* write char, tty mode */
+prloop:
+ lodsb
+ int $0x10
+ loop prloop
+freeze: jmp freeze
+
+why: .ascii "This image cannot be loaded from a floppy disk.\r\n"
+why_end:
+
+
+ .org 497
+setup_sects:
+ .byte SETUPSECS
+root_flags:
+ .word 0
+syssize:
+ .word _verbatim_size_pgh - PREFIXPGH
+swap_dev:
+ .word 0
+ram_size:
+ .word 0
+vid_mode:
+ .word 0
+root_dev:
+ .word 0
+boot_flag:
+ .word 0xAA55
+
+/*
+ We're now at the beginning of the second sector of the image -
+ where the setup code goes.
+
+ We don't need to do too much setup for Etherboot.
+
+ This code gets loaded at SETUPSEG:0. It wants to start
+ executing the Etherboot image that's loaded at SYSSEG:0 and
+ whose entry point is SYSSEG:0.
+*/
+setup_code:
+ pushl $0 /* No parameters to preserve for exit path */
+ pushw $0 /* Use prefix exit path mechanism */
+ /* Etherboot expects to be contiguous in memory once loaded.
+ * LILO doesn't do this, but since we don't need any
+ * information that's left in the prefix, it doesn't matter:
+ * we just have to ensure that %cs:0000 is where the start of
+ * the Etherboot image *would* be.
+ */
+ ljmp $(SYSSEG-(PREFIXSIZE/16)), $_start
+
+ .section ".text16", "ax", @progbits
+ .globl prefix_exit
+prefix_exit:
+ int $0x19 /* should try to boot machine */
+ .globl prefix_exit_end
+prefix_exit_end:
+ .previous
+
+ .org (PREFIXSIZE-1)
+ .byte 0
+prefix_end:
+/*
+ That's about it.
+*/
+
diff --git a/src/arch/i386/prefix/lmelf_dprefix.S b/src/arch/i386/prefix/lmelf_dprefix.S
new file mode 100644
index 00000000..65fe1356
--- /dev/null
+++ b/src/arch/i386/prefix/lmelf_dprefix.S
@@ -0,0 +1,163 @@
+#include "elf.h"
+ .arch sledgehammer
+ .code32
+ .equ FLAT_CODE_SEG,_pmcs-_gdt
+ .equ FLAT_DATA_SEG,_pmds-_gdt
+ .equ MSR_K6_EFER, 0xC0000080
+ .equ EFER_LME, 0x00000100
+ .equ X86_CR4_PAE, 0x00000020
+ .equ CR0_PG, 0x80000000
+
+ .section ".prefix", "ax", @progbits
+
+#define LOAD_ADDR 0x10000
+
+ /* ELF Header */
+ .globl elf_header
+elf_header:
+e_ident: .byte 0x7f, 'E', 'L', 'F', 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0
+e_type: .short ET_DYN
+e_machine: .short EM_X86_64
+e_version: .long 1
+e_entry: .long LOAD_ADDR + elf_start - elf_header
+e_phoff: .long elf_program_header - elf_header
+e_shoff: .long 0
+e_flags: .long 0
+e_ehsize: .short elf_header_end - elf_header
+e_phentsize: .short ELF32_PHDR_SIZE
+e_phnum: .short (elf_program_header_end - elf_program_header)/ELF32_PHDR_SIZE
+e_shentsize: .short 0
+e_shnum: .short 0
+e_shstrndx: .short 0
+elf_header_end:
+
+elf_program_header:
+phdr1_p_type: .long PT_NOTE
+phdr1_p_offset: .long elf_note - elf_header
+phdr1_p_vaddr: .long elf_note
+phdr1_p_paddr: .long elf_note
+phdr1_p_filesz: .long elf_note_end - elf_note
+phdr1_p_memsz: .long elf_note_end - elf_note
+phdr1_p_flags: .long PF_R | PF_W | PF_X
+phdr1_p_align: .long 0
+
+/* The decompressor */
+phdr2_p_type: .long PT_LOAD
+phdr2_p_offset: .long 0
+phdr2_p_vaddr: .long elf_header
+phdr2_p_paddr: .long LOAD_ADDR
+phdr2_p_filesz: .long _verbatim_size
+phdr2_p_memsz: .long _image_size
+phdr2_p_flags: .long PF_R | PF_W | PF_X
+phdr2_p_align: .long 16
+
+elf_program_header_end:
+
+ .globl elf_note
+elf_note:
+ .balign 4
+ .int 2f - 1f
+ .int 4f - 3f
+ .int EIN_PROGRAM_NAME
+1: .asciz "ELFBoot"
+2:
+ .balign 4
+3:
+ .asciz "Etherboot"
+4:
+
+
+ .balign 4
+ .int 2f - 1f
+ .int 4f - 3f
+ .int EIN_PROGRAM_VERSION
+1: .asciz "ELFBoot"
+2:
+ .balign 4
+3:
+ .asciz VERSION
+4:
+
+#if 0
+ .balign 4
+ .int 2f - 1f
+ .int 4f - 3f
+ .int EIN_PROGRAM_CHECKSUM
+1: .asciz "ELFBoot"
+2:
+ .balign 4
+3:
+ .word 0
+4:
+#endif
+ .balign 4
+elf_note_end:
+
+elf_start:
+ .code64
+ /* Reload the gdt to something I know */
+ leaq _gdt(%rip), %rax
+ movq %rax, 0x02 + gdtptr(%rip)
+ lgdt gdtptr(%rip)
+
+ /* Enter 32bit compatibility mode */
+ leaq elf_start32(%rip), %rax
+ movl %eax, 0x00 + elf_start32_addr(%rip)
+ ljmp *elf_start32_addr(%rip)
+
+elf_start32:
+ .code32
+ /* Reload the data segments */
+ movl $FLAT_DATA_SEG, %eax
+ movl %eax, %ds
+ movl %eax, %es
+ movl %eax, %ss
+
+ /* Disable paging */
+ movl %cr0, %eax
+ andl $~CR0_PG, %eax
+ movl %eax, %cr0
+
+ /* Disable long mode */
+ movl $MSR_K6_EFER, %ecx
+ rdmsr
+ andl $~EFER_LME, %eax
+ wrmsr
+
+ /* Disable PAE */
+ movl %cr4, %eax
+ andl $~X86_CR4_PAE, %eax
+ movl %eax, %cr4
+
+ /* Save the first argument */
+ pushl %ebx
+ jmp _start
+
+gdtptr:
+ .word _gdt_end - _gdt -1
+ .long _gdt
+ .long 0
+_gdt:
+elf_start32_addr:
+ .long elf_start32
+ .long FLAT_CODE_SEG
+_pmcs:
+ /* 32 bit protected mode code segment, base 0 */
+ .word 0xffff,0
+ .byte 0,0x9f,0xcf,0
+
+_pmds:
+ /* 32 bit protected mode data segment, base 0 */
+ .word 0xffff,0
+ .byte 0,0x93,0xcf,0
+_gdt_end:
+
+
+ /* Dummy routines to satisfy the build */
+ .section ".text16", "ax", @progbits
+ .globl prefix_exit
+prefix_exit:
+
+ .globl prefix_exit_end
+prefix_exit_end:
+ .previous
diff --git a/src/arch/i386/prefix/lmelf_prefix.S b/src/arch/i386/prefix/lmelf_prefix.S
new file mode 100644
index 00000000..7488f452
--- /dev/null
+++ b/src/arch/i386/prefix/lmelf_prefix.S
@@ -0,0 +1,163 @@
+#include "elf.h"
+ .arch sledgehammer
+ .code32
+ .equ FLAT_CODE_SEG,_pmcs-_gdt
+ .equ FLAT_DATA_SEG,_pmds-_gdt
+ .equ MSR_K6_EFER, 0xC0000080
+ .equ EFER_LME, 0x00000100
+ .equ X86_CR4_PAE, 0x00000020
+ .equ CR0_PG, 0x80000000
+
+ .section ".prefix", "ax", @progbits
+
+#define LOAD_ADDR 0x10000
+
+ /* ELF Header */
+ .globl elf_header
+elf_header:
+e_ident: .byte 0x7f, 'E', 'L', 'F', 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0
+e_type: .short ET_EXEC
+e_machine: .short EM_X86_64
+e_version: .long 1
+e_entry: .long LOAD_ADDR + elf_start - elf_header
+e_phoff: .long elf_program_header - elf_header
+e_shoff: .long 0
+e_flags: .long 0
+e_ehsize: .short elf_header_end - elf_header
+e_phentsize: .short ELF32_PHDR_SIZE
+e_phnum: .short (elf_program_header_end - elf_program_header)/ELF32_PHDR_SIZE
+e_shentsize: .short 0
+e_shnum: .short 0
+e_shstrndx: .short 0
+elf_header_end:
+
+elf_program_header:
+phdr1_p_type: .long PT_NOTE
+phdr1_p_offset: .long elf_note - elf_header
+phdr1_p_vaddr: .long elf_note
+phdr1_p_paddr: .long elf_note
+phdr1_p_filesz: .long elf_note_end - elf_note
+phdr1_p_memsz: .long elf_note_end - elf_note
+phdr1_p_flags: .long PF_R | PF_W | PF_X
+phdr1_p_align: .long 0
+
+/* The decompressor */
+phdr2_p_type: .long PT_LOAD
+phdr2_p_offset: .long 0
+phdr2_p_vaddr: .long elf_header
+phdr2_p_paddr: .long LOAD_ADDR
+phdr2_p_filesz: .long _verbatim_size
+phdr2_p_memsz: .long _image_size
+phdr2_p_flags: .long PF_R | PF_W | PF_X
+phdr2_p_align: .long 16
+
+elf_program_header_end:
+
+ .globl elf_note
+elf_note:
+ .balign 4
+ .int 2f - 1f
+ .int 4f - 3f
+ .int EIN_PROGRAM_NAME
+1: .asciz "ELFBoot"
+2:
+ .balign 4
+3:
+ .asciz "Etherboot"
+4:
+
+
+ .balign 4
+ .int 2f - 1f
+ .int 4f - 3f
+ .int EIN_PROGRAM_VERSION
+1: .asciz "ELFBoot"
+2:
+ .balign 4
+3:
+ .asciz VERSION
+4:
+
+#if 0
+ .balign 4
+ .int 2f - 1f
+ .int 4f - 3f
+ .int EIN_PROGRAM_CHECKSUM
+1: .asciz "ELFBoot"
+2:
+ .balign 4
+3:
+ .word 0
+4:
+#endif
+ .balign 4
+elf_note_end:
+
+elf_start:
+ .code64
+ /* Reload the gdt to something I know */
+ leaq _gdt(%rip), %rax
+ movq %rax, 0x02 + gdtptr(%rip)
+ lgdt gdtptr(%rip)
+
+ /* Enter 32bit compatibility mode */
+ leaq elf_start32(%rip), %rax
+ movl %eax, 0x00 + elf_start32_addr(%rip)
+ ljmp *elf_start32_addr(%rip)
+
+elf_start32:
+ .code32
+ /* Reload the data segments */
+ movl $FLAT_DATA_SEG, %eax
+ movl %eax, %ds
+ movl %eax, %es
+ movl %eax, %ss
+
+ /* Disable paging */
+ movl %cr0, %eax
+ andl $~CR0_PG, %eax
+ movl %eax, %cr0
+
+ /* Disable long mode */
+ movl $MSR_K6_EFER, %ecx
+ rdmsr
+ andl $~EFER_LME, %eax
+ wrmsr
+
+ /* Disable PAE */
+ movl %cr4, %eax
+ andl $~X86_CR4_PAE, %eax
+ movl %eax, %cr4
+
+ /* Save the first argument */
+ pushl %ebx
+ jmp _start
+
+gdtptr:
+ .word _gdt_end - _gdt -1
+ .long _gdt
+ .long 0
+_gdt:
+elf_start32_addr:
+ .long elf_start32
+ .long FLAT_CODE_SEG
+_pmcs:
+ /* 32 bit protected mode code segment, base 0 */
+ .word 0xffff,0
+ .byte 0,0x9f,0xcf,0
+
+_pmds:
+ /* 32 bit protected mode data segment, base 0 */
+ .word 0xffff,0
+ .byte 0,0x93,0xcf,0
+_gdt_end:
+
+
+ /* Dummy routines to satisfy the build */
+ .section ".text16", "ax", @progbits
+ .globl prefix_exit
+prefix_exit:
+
+ .globl prefix_exit_end
+prefix_exit_end:
+ .previous
diff --git a/src/arch/i386/prefix/nullprefix.S b/src/arch/i386/prefix/nullprefix.S
new file mode 100644
index 00000000..dbee141b
--- /dev/null
+++ b/src/arch/i386/prefix/nullprefix.S
@@ -0,0 +1,16 @@
+ .org 0
+ .text
+ .arch i386
+
+ .section ".prefix", "ax", @progbits
+ .code16
+ .globl _prefix
+_prefix:
+
+ .section ".text16", "ax", @progbits
+ .globl prefix_exit
+prefix_exit:
+
+ .globl prefix_exit_end
+prefix_exit_end:
+ .previous
diff --git a/src/arch/i386/prefix/pxeprefix.S b/src/arch/i386/prefix/pxeprefix.S
new file mode 100644
index 00000000..246e22b2
--- /dev/null
+++ b/src/arch/i386/prefix/pxeprefix.S
@@ -0,0 +1,398 @@
+/* Offsets of words containing ROM's CS and size (in 512 byte blocks)
+ * from start of floppy boot block at 0x7c00
+ * Offsets must match those in etherboot.h
+ */
+#define FLOPPY_SEGMENT 0x7c0
+
+#define PXENV_UNDI_CLEANUP 0x02
+#define PXENV_UNDI_SHUTDOWN 0x05
+#define PXENV_STOP_UNDI 0x15
+#define PXENV_UNLOAD_STACK 0x70
+#define PXENV_STOP_BASE 0x76
+
+#define PUSHA_SIZE 16
+#define PXE_STACK_MAGIC 0x57ac /* 'STac' */
+
+ .text
+ .code16
+ .arch i386
+ .org 0
+ .section ".prefix", "ax", @progbits
+ .globl _prefix
+/*****************************************************************************
+ * Entry point: set cs, ds, bp, print welcome message
+ *****************************************************************************
+ */
+_prefix:
+ jmp $FLOPPY_SEGMENT, $code_start-_prefix
+10: .asciz "PXE->EB "
+code_start:
+ pusha /* Preserve all registers */
+ push %ds
+ movw %sp, %bp /* %bp must be preserved, hence do
+ * this after the pusha */
+ push $PXE_STACK_MAGIC /* PXE stack magic marker */
+
+ push %cs /* Set up data segment */
+ pop %ds
+ mov $0x40, %cx /* Set up %fs for access to 40:13 */
+ mov %cx, %fs
+ movw $10b-_prefix, %si /* Print welcome message */
+ call print_message
+
+/*****************************************************************************
+ * Detect type of PXE available (!PXE, PXENV+ or none)
+ *****************************************************************************
+ */
+detect_pxe:
+ les 4+PUSHA_SIZE+2(%bp), %di /* !PXE structure */
+ cmpl $0x45585021, %es:(%di) /* '!PXE' signature */
+ je detected_pxe
+ mov $0x5650, %ax
+ int $0x1a
+ cmp $0x564e, %ax
+ jne detected_nothing
+ cmpl $0x4e455850, %es:(%bx) /* 'PXEN' signature */
+ jne detected_nothing
+ cmpw $0x2b56, %es:4(%bx) /* 'V+' signature */
+ je detected_pxenv
+
+detected_nothing:
+ movw $10f-_prefix, %si
+ call print_message
+ jmp finished_with_error
+10: .asciz "No PXE "
+
+detected_pxenv: /* es:bx points to PXENV+ structure */
+ push %es
+ push %bx
+ push %es:0x24(%bx) /* UNDI code segment */
+ push %es:0x26(%bx) /* UNDI code size */
+ push %es:0x20(%bx) /* UNDI data segment */
+ push %es:0x22(%bx) /* UNDI data size */
+ les %es:0x0a(%bx), %di /* Entry point to %es:%di */
+ movw $10f-_prefix, %si
+ jmp pxe_setup_done
+10: .asciz "PXENV+ "
+
+detected_pxe: /* es:di points to !PXE structure */
+ push %es
+ push %di
+ push %es:0x30(%di) /* UNDI code segment */
+ push %es:0x36(%di) /* UNDI code size */
+ push %es:0x28(%di) /* UNDI data segment */
+ push %es:0x2e(%di) /* UNDI data size */
+ les %es:0x10(%di), %di /* Entry point to %es:%di */
+ movw $10f-_prefix, %si
+ jmp pxe_setup_done
+10: .asciz "!PXE "
+
+pxe_setup_done:
+ mov %es, pxe_entry_segment - _prefix
+ mov %di, pxe_entry_offset - _prefix
+ pop %ax
+ mov %ax, undi_data_size - _prefix
+ pop %ax
+ mov %ax, undi_data_segment - _prefix
+ pop %ax
+ mov %ax, undi_code_size - _prefix
+ pop %ax
+ mov %ax, undi_code_segment - _prefix
+ call print_message
+ pop %di
+ pop %es /* Exit with %es:%di containing structure address */
+
+/*****************************************************************************
+ * Print information about located structure
+ *****************************************************************************
+ */
+print_structure_information:
+ call print_segoff /* %es:%di contains address of structure */
+ les %ds:(pxe_entry_segoff - _prefix), %di
+ call print_segoff
+ les %ds:(undi_code_segoff - _prefix), %di
+ call print_segoff
+ les %ds:(undi_data_segoff - _prefix), %di
+ call print_segoff
+
+/*****************************************************************************
+ * Unload PXE base code and UNDI driver
+ *****************************************************************************
+ */
+#ifdef PXELOADER_KEEP_ALL
+ xor %ax, %ax /* Force zero flag to show success */
+ jmp do_not_free_base_mem /* Skip the unloading */
+#endif /* PXELOADER_KEEP_ALL */
+
+unload_pxe:
+ mov $PXENV_UNLOAD_STACK, %bx
+ call pxe_call
+ mov $PXENV_STOP_UNDI, %bx
+ call pxe_call
+ pushfw /* Ignore PXENV_UNDI_CLEANUP errors */
+ mov $PXENV_UNDI_CLEANUP, %bx
+ call pxe_call
+ popfw
+ /* On exit, zero flag is set iff all calls were successful */
+
+/*****************************************************************************
+ * Free base memory
+ *****************************************************************************
+ */
+free_base_mem:
+ jnz do_not_free_base_mem /* Using zero flag from unload_pxe */
+
+ mov undi_code_segment - _prefix, %bx
+ mov undi_data_segment - _prefix, %cx
+ mov undi_code_size - _prefix, %ax
+ cmp %bx, %cx
+ jb 1f
+ mov %cx, %bx
+ mov undi_data_size - _prefix, %ax
+1: add $0x0f, %ax /* Round up to next segment */
+ shr $4, %ax
+ add %bx, %ax /* Highest segment address into %ax */
+ add $(1024 / 16 - 1), %ax /* Round up to next kb */
+ shr $6, %ax /* New free basemem size in %ax */
+ mov %fs:(0x13), %bx /* Old free base memory in %bx */
+ mov %ax, %fs:(0x13) /* Store new free base memory size */
+
+ /* Note that zero_mem_loop will also zero out our stack, so make
+ * sure the stack is empty at this point.
+ */
+ mov %ax, %dx
+ sub %bx, %dx /* numberof kb to zero in %dx */
+ shl $6, %bx /* Segment address into %bx */
+zero_mem_loop:
+ mov %bx, %es /* kB boundary into %es:00 */
+ xor %ax, %ax
+ xor %di, %di
+ mov $0x400, %cx
+ rep stosb /* fill kB with zeroes */
+ add $(1024 / 16), %bx
+ dec %dx
+ jnz zero_mem_loop
+ /* Will exit here with zero flag set, so no need to set it explicitly
+ * in order to indicate success.
+ */
+
+do_not_free_base_mem:
+ pushf /* Save success (zero) flag status */
+ mov %fs:(0x13), %ax /* Free base memory in %ax */
+ call print_hex_word /* Print free base memory */
+ popf /* Restore success (zero) flag */
+
+/*****************************************************************************
+ * Exit point
+ * Jump to finished with the zero flag set to indicate success, or to
+ * finished_with_error to always report an error
+ *****************************************************************************
+ */
+finished:
+ movw $10f-_prefix, %si
+ jz 1f
+finished_with_error:
+ movw $20f-_prefix, %si
+1:
+ call print_message
+ jmp 99f
+10: .asciz " ok\n"
+20: .asciz " err\n"
+
+
+ /* We place a stack here. It doesn't get used until after all
+ * the above code is finished, so we can happily write all
+ * over it. Putting the stack here ensures that it doesn't
+ * accidentally go over the 512 byte threshold, which would
+ * cause problems when returning via start32's prefix
+ * relocation mechanism.
+ */
+_estack:
+99:
+
+/*****************************************************************************
+ * Run Etherboot main code
+ *****************************************************************************
+ */
+run_etherboot:
+ /* Very temporarily switch stacks to one internal to the
+ * prefix. Do this because the stack provided by the PXE ROM
+ * could be absolutely anywhere, including in an area of
+ * memory that the call to prelocate will vapourise...
+ */
+ pushw %ss /* PXE stack pointer to ES:DI */
+ popw %es
+ movw %sp, %di
+ pushw %ds /* Set up stack in "safe" area */
+ popw %ss
+ movw $_estack-_prefix, %sp
+ pushw %es /* Record PXE stack pointer */
+ pushw %di
+ /* Relocate payload and stack to claimed base memory */
+ pushw $4 /* Preserve old PXE stack pointer */
+ call prelocate
+ popw %ax /* Remove parameter */
+ pushl $4 /* Preserve old PXE stack pointer */
+ pushw $0 /* Indicate prefix exit mechanism */
+ jmp _start /* Run Etherboot */
+
+ .section ".text16", "ax", @progbits
+ .globl prefix_exit
+prefix_exit:
+ pushw %cs /* Set %ds, %bp for access to text */
+ popw %ds
+ call 1f
+1: popw %bp
+ popw %di /* Old PXE stack to %es:di */
+ popw %es
+ cmpw $PXE_STACK_MAGIC, %es:0(%di) /* See if PXE stack intact */
+ jne exit_via_int18
+exit_via_pxe: /* Stack OK, return to PXE */
+ push %es /* Restore PXE stack pointer */
+ pop %ss
+ mov %di, %sp
+ pop %ax /* Discard PXE_STACK_MAGIC marker */
+ leaw (10f-1b)(%bp), %si
+ call print_exit_message
+ pop %ds /* Restore PXE's DS */
+ popa /* Restore PXE's other registers */
+ movw $0, %ax /* Return PXENV_STATUS_SUCCESS */
+ lret /* Return control to PXE ROM */
+10: .asciz "EB->PXE\r\n"
+exit_via_int18: /* Stack damaged, do int 18 */
+ leaw (10f-1b)(%bp), %si
+ call print_exit_message
+ int $0x18
+10: .asciz "EB->BIOS\r\n"
+print_exit_message:
+ movw $0x0007, %bx /* page 0, attribute 7 (normal) */
+ movb $0x0e, %ah /* write char, tty mode */
+1: lodsb
+ testb %al, %al
+ je 2f
+ int $0x10
+ jmp 1b
+2: ret
+ .globl prefix_exit_end
+prefix_exit_end:
+ .previous
+
+/*****************************************************************************
+ * Subroutine: print character in %al (with LF -> LF,CR translation)
+ *****************************************************************************
+ */
+print_character:
+ movw $0x0007, %bx /* page 0, attribute 7 (normal) */
+ movb $0x0e, %ah /* write char, tty mode */
+ cmpb $0x0a, %al /* '\n'? */
+ jne 1f
+ int $0x10
+ movb $0x0d, %al
+1: int $0x10
+ ret
+
+/*****************************************************************************
+ * Subroutine: print a zero-terminated message starting at %si
+ *****************************************************************************
+ */
+print_message:
+1: lodsb
+ testb %al, %al
+ je 2f
+ call print_character
+ jmp 1b
+2: ret
+
+/*****************************************************************************
+ * Subroutine: print hex word in %ax
+ *****************************************************************************
+ */
+print_hex_word:
+ mov $4, %cx
+1:
+ push %ax
+ shr $12, %ax
+ /* Courtesy of Norbert Juffa <norbert.juffa@amd.com> */
+ cmp $10, %al
+ sbb $0x69, %al
+ das
+ call print_character
+ pop %ax
+ shl $4, %ax
+ loop 1b
+ ret
+
+/*****************************************************************************
+ * Subroutine: print segment:offset address in %es:%di
+ *****************************************************************************
+ */
+print_segoff:
+ push %di
+ push %es
+ pop %ax
+ call print_hex_word
+ movb $0x3a,%al /* ':' */
+ call print_character
+ pop %ax
+ call print_hex_word
+ mov $0x20, %al /* ' ' */
+ call print_character
+ ret
+
+/*****************************************************************************
+ * Make a PXE API call. Works with either !PXE or PXENV+ API.
+ * Opcode in %bx. pxe_parameter_structure always used.
+ * Returns status code (not exit code) in %bx and prints it.
+ * ORs status code with overall status code in pxe_overall_status, returns
+ * with zero flag set iff all PXE API calls have been successful.
+ *****************************************************************************
+ */
+pxe_call:
+ /* Set up registers for PXENV+ API. %bx already set up */
+ push %ds
+ pop %es
+ mov $pxe_parameter_structure - _prefix, %di
+ /* Set up stack for !PXE API */
+ pushw %cs
+ pushw %di
+ pushw %bx
+ /* Make the API call */
+ lcall *(pxe_entry_segoff - _prefix)
+ /* Reset the stack */
+ add $6, %sp
+ mov pxe_parameter_structure - _prefix, %ax
+ push %ax
+ call print_hex_word
+ mov $0x20, %ax /* ' ' */
+ call print_character
+ pop %bx
+ or %bx, pxe_overall_status - _prefix
+ ret
+
+/*****************************************************************************
+ * PXE data structures
+ *****************************************************************************
+ */
+
+pxe_overall_status: .word 0
+
+pxe_entry_segoff:
+pxe_entry_offset: .word 0
+pxe_entry_segment: .word 0
+
+undi_code_segoff:
+undi_code_size: .word 0
+undi_code_segment: .word 0
+
+undi_data_segoff:
+undi_data_size: .word 0
+undi_data_segment: .word 0
+
+pxe_parameter_structure:
+ .word 0
+ .word 0,0,0,0,0
+
+end_of_pxeloader:
+
+ .balign 16, 0
+payload:
diff --git a/src/arch/i386/prefix/romprefix.S b/src/arch/i386/prefix/romprefix.S
new file mode 100644
index 00000000..c6c83489
--- /dev/null
+++ b/src/arch/i386/prefix/romprefix.S
@@ -0,0 +1,416 @@
+/* At entry, the processor is in 16 bit real mode and the code is being
+ * executed from an address it was not linked to. Code must be pic and
+ * 32 bit sensitive until things are fixed up.
+ *
+ * Also be very careful as the stack is at the rear end of the interrupt
+ * table so using a noticeable amount of stack space is a no-no.
+ */
+
+/* Define DELAYED_INT when NO_DELAYED_INT is not defined.
+ * This allows positive tests instead of tests that contain
+ * double negatives, and become confusing.
+ */
+#ifndef NO_DELAYED_INT
+#define DELAYED_INT
+#endif
+
+/* We need some unique magic ID, if we defer startup thru the INT18H or INT19H
+ * handler. This way, we can check if we have already been installed.
+ */
+#ifndef MAGIC
+#define MAGIC 0xE44C
+#endif
+
+/* Hook into INT18H or INT19H handler */
+#ifdef BOOT_INT18H
+#define BOOT_INT 0x18
+#else
+#define BOOT_INT 0x19
+#endif
+
+#define BOOT_INT_VEC BOOT_INT*4
+#define SCRATCHVEC 0x300
+
+/* Prefix exit codes. We store these on the stack so that we will
+ * know how to return control to the BIOS when Etherboot exits.
+ */
+#define EXIT_VIA_LRET 0x0
+#define EXIT_VIA_INT_18 0x1
+#define EXIT_VIA_BOOT_INT 0x2
+
+ .text
+ .code16
+ .arch i386
+ .org 0
+ .section ".prefix", "ax", @progbits
+ .globl _prefix
+_prefix:
+ .word 0xAA55 /* BIOS extension signature */
+size: .byte 0 /* number of 512 byte blocks */
+ /* = number of 256 word blocks */
+ /* filled in by makerom program */
+ jmp over /* skip over checksum */
+ .byte 0 /* checksum */
+ jmp legacyentry /* alternate entry point +6 */
+ /* used by mknbi-rom */
+
+#ifdef PCI_PNP_HEADER
+mfgstr:
+ .asciz "Etherboot"
+
+#ifdef PXE_EXPORT
+ .org 0x16
+ .word UNDIROMID - _prefix
+#endif /* PXE_EXPORT */
+
+ .org 0x18
+ .word PCI - _prefix
+ .word PnP - _prefix
+
+PCI:
+ .ascii "PCIR"
+ .word 0x0000 /* vendor ID, filled in by makerom */
+ .word 0x0000 /* device ID, filled in by makerom */
+ .word 0x0000 /* pointer to vital product data */
+ .word 0x0018 /* PCI data structure length */
+ .byte 0x00 /* PCI data structure revision */
+ .byte 0x02 /* Device Base Type code */
+ .byte 0x00 /* Device Sub-Type code */
+ .byte 0x00 /* Device Interface Type code */
+ .word 0x0000 /* Image length same as offset 02h */
+ .word 0x0001 /* revision level of code/data */
+ .byte 0x00 /* code type */
+ .byte 0x80 /* indicator (last PCI data structure) */
+ .word 0x0000 /* reserved */
+
+PnP:
+ .ascii "$PnP"
+ .byte 0x01 /* structure revision */
+ .byte 0x02 /* length (in 16 byte increments) */
+ .word 0x0000 /* offset of next header */
+ .byte 0x00 /* Reserved */
+ .byte 0x00 /* checksum filled by makerom */
+ .long 0x00000000 /* Device identifier */
+ .word mfgstr - _prefix
+ .word 0x0 /* pointer to product name */
+ /* filled by makerom */
+ .byte 0x02 /* Device Base Type code */
+ .byte 0x00 /* Device Sub-Type code */
+ .byte 0x00 /* Device Interface Type code */
+ .byte 0x14 /* device indicator */
+ .word 0x0000 /* boot connection vector */
+ .word 0x0000 /* disconnect vector */
+ .word pnpentry - _prefix
+ .word 0x0000 /* reserved */
+ .word 0x0000 /* static resource information vector */
+#ifdef PXE_EXPORT
+UNDIROMID:
+ .ascii "UNDI"
+ .byte UNDIROMID_end - UNDIROMID /* length of structure */
+ .byte 0 /* Checksum */
+ .byte 0 /* Structure revision */
+ .byte 0,1,2 /* PXE version 2.1.0 */
+ .word UNDILoader - _prefix /* Offset to loader routine */
+ .word UNDIStackSize /* Stack segment size */
+ .word UNDIDataSize /* Data segment size */
+ .word UNDICodeSize /* Code segment size */
+ .ascii "PCIR"
+
+ /* The code segment contains our pxe_stack_t plus the PXE and
+ * RM callback interfaces. We don't actually use a data
+ * segment, but we put a nonzero value here to avoid confusing
+ * things. 16k of stack space should be enough.
+ *
+ * When we claim our own memory, we fill out the data segment
+ * with the address and size of the real-mode stack, so that
+ * NBPs will free that area of memory for us. When the UNDI
+ * loader is used to initialise us, we will never need a
+ * real-mode stack because we will only ever be called via the
+ * PXE API, hence our stack is already in base memory.
+ */
+ .equ UNDICodeSize, _pxe_stack_size
+ .equ UNDIDataSize, _real_mode_stack_size
+ .equ UNDIStackSize, _real_mode_stack_size
+UNDIROMID_end:
+#endif /* PXE_EXPORT */
+
+#endif /* PCI_PNP_HEADER */
+
+/*
+ * Explicitly specify DI is wrt ES to avoid problems with some BIOSes
+ * Discovered by Eric Biederman
+ * In addition, some BIOSes don't point DI to the string $PnP so
+ * we need another #define to take care of that.
+ */
+over:
+#ifdef DEBUG_ROMPREFIX
+ call print_bcv
+#endif
+/* Omit this test for ISA cards anyway */
+#ifdef PCI_PNP_HEADER
+/* Accept old name too for backward compatibility */
+#if !defined(BBS_BUT_NOT_PNP_COMPLIANT) && !defined(PNP_BUT_NOT_BBS_COMPLIANT)
+ cmpw $'$'+'P'*256,%es:0(%di)
+ jne notpnp
+ cmpw $'n'+'P'*256,%es:2(%di)
+ jne notpnp
+#endif /* BBS_BUT_NOT_PNP_COMPLIANT */
+ movw $0x20,%ax
+ lret
+#endif /* PCI_PNP_HEADER */
+notpnp:
+#ifdef DEBUG_ROMPREFIX
+ call print_notpnp
+#endif
+#ifdef DELAYED_INT
+ pushw %ax
+ pushw %ds
+ xorw %ax,%ax
+ movw %ax,%ds /* access first 64kB segment */
+ movw SCRATCHVEC+4, %ax /* check if already installed */
+ cmpw $MAGIC, %ax /* check magic word */
+ jz installed
+ movw BOOT_INT_VEC, %ax /* hook into INT18H or INT19H */
+ movw %ax, SCRATCHVEC
+ movw BOOT_INT_VEC+2, %ax
+ movw %ax, SCRATCHVEC+2
+ movw $start_int - _prefix, %ax
+ movw %ax, BOOT_INT_VEC
+ movw %cs,%ax
+ movw %ax, BOOT_INT_VEC+2
+ movw $MAGIC, %ax /* set magic word */
+ movw %ax, SCRATCHVEC+4
+#ifdef DEBUG_ROMPREFIX
+ call print_installed
+#endif
+installed:
+ popw %ds
+ popw %ax
+ movw $0x20,%ax
+ lret
+
+start_int: /* clobber magic id, so that we will */
+#ifdef DEBUG_ROMPREFIX
+ call print_start_int
+#endif
+ xorw %ax,%ax /* not inadvertendly end up in an */
+ movw %ax,%ds /* endless loop */
+ movw %ax, SCRATCHVEC+4
+ movw SCRATCHVEC+2, %ax /* restore original INT19h handler */
+ movw %ax, BOOT_INT_VEC+2
+ movw SCRATCHVEC, %ax
+ movw %ax, BOOT_INT_VEC
+ pushl %eax /* padding */
+ pushw $EXIT_VIA_BOOT_INT
+ jmp invoke
+#endif /* DELAYED_INT */
+
+
+
+
+legacyentry:
+#ifdef DEBUG_ROMPREFIX
+ call print_legacyentry
+#endif
+ pushw $EXIT_VIA_LRET
+ jmp invoke
+
+
+
+#ifdef PCI_PNP_HEADER
+pnpentry:
+#ifdef DEBUG_ROMPREFIX
+ call print_bev
+#endif
+ pushl %eax /* padding */
+ pushw $EXIT_VIA_INT_18
+ jmp invoke
+#endif /* PCI_PNP_HEADER */
+
+
+invoke:
+ /* Store ROM segment and size on stack */
+ pushw %ax
+ pushw %ds
+ pushw %cs
+ movzbw %cs:(size-_prefix), %ax
+ shlw $9, %ax /* 512-byte blocks */
+ pushw %ax
+ /* Relocate to free base memory, switch stacks */
+ pushw $12 /* Preserve exit code & far ret addr */
+ call prelocate
+ /* We are now running in RAM */
+ popw %ax /* padding */
+ movw %cs, %ax
+ movw %ax, %ds
+ popw %ds:(_prefix_rom+2) /* ROM size */
+ popw %ds:(_prefix_rom+0) /* ROM segment */
+ popw %ds /* Original %ds */
+ popw %ax /* Original %ax */
+ pushw %ax /* 4-byte alignment */
+ pushl $8 /* Preserve exit code & far ret addr */
+ pushw $0 /* Set null return address */
+ jmp _start
+
+
+ .section ".text16", "ax", @progbits
+ .globl prefix_exit
+prefix_exit:
+ popw %ax /* padding */
+ popw %ax /* %ax = exit code */
+ cmpw $EXIT_VIA_LRET, %ax
+ jne 1f
+ /* Exit via LRET */
+ lret
+1: addw $4, %sp /* Strip padding */
+ cmpw $EXIT_VIA_BOOT_INT, %ax
+ jne 2f
+ /* Exit via int BOOT_INT */
+ int $BOOT_INT /* Try original vector */
+2: /* Exit via int $0x18 */
+ int $0x18 /* As per BIOS Boot Spec, next dev */
+ .globl prefix_exit_end
+prefix_exit_end:
+ .previous
+
+
+
+#ifdef PXE_EXPORT
+
+#include "callbacks.h"
+#define PXENV_UNDI_LOADER 0x104d
+
+ .section ".prefix"
+UNDILoader:
+ /* Loader API is different to the usual PXE API; there is no
+ * opcode on the stack. We arrange the stack to look like a
+ * normal PXE API call; this makes the Etherboot internals
+ * cleaner and avoids adding an extra API type just for the
+ * PXE loader.
+ */
+ pushw %bx
+ movw %sp, %ax /* Store original %ss:sp */
+ pushw %ss
+ pushw %ax
+ pushl %eax /* Space for loader structure ptr */
+ pushw %bp
+ movw %sp, %bp
+ movw 16(%bp), %ax /* Copy loader structure ptr */
+ movw %ax, 2(%bp)
+ movw 18(%bp), %ax
+ movw %ax, 4(%bp)
+ popw %bp
+ pushw $PXENV_UNDI_LOADER /* PXE 'opcode' */
+ pushl %eax /* dummy return address */
+ /* Stack now looks like a normal PXE API call */
+ /* Store ROM segment and size on stack */
+ pushw %ax
+ pushw %cs
+ movzbw %cs:(size-_prefix), %ax
+ shlw $9, %ax /* 512-byte blocks */
+ pushw %ax
+ /* Unpack Etherboot into temporarily claimed base memory */
+ pushw $20 /* Dummy ret, PXE params, orig ss:sp */
+ call prelocate
+ popw %ax /* discard */
+ popw %cs:(_prefix_rom+2) /* ROM size */
+ popw %cs:(_prefix_rom+0) /* ROM segment */
+ popw %ax /* Original %ax */
+ /* Inhibit automatic deallocation of base memory */
+ movl $0, %cs:_prefix_image_basemem
+ /* Make PXE API call to Etherboot */
+ pushl $0x201 /* PXE API version */
+ /* Need to USE_INTERNAL_STACK, since we will call relocate() */
+ pushl $(EB_OPCODE_PXE|EB_USE_INTERNAL_STACK) /* PXE API call type */
+ call _entry
+ addw $18, %sp /* discard */
+ popw %bx /* Restore original %ss:sp */
+ popw %ss
+ movw %bx, %sp
+ popw %bx
+ call deprelocate
+ lret $2 /* Skip our PXE 'opcode' */
+#endif /* PXE_EXPORT */
+
+#ifdef DEBUG_ROMPREFIX
+ .section ".prefix"
+
+print_bcv:
+ pushw %si
+ movw $1f-_prefix, %si
+ call print_message
+ popw %si
+ ret
+1: .asciz "ROM detected\r\n"
+
+print_bev:
+ pushw %si
+ movw $1f-_prefix, %si
+ call print_message
+ popw %si
+ ret
+1: .asciz "booting\r\n"
+
+print_notpnp:
+ pushw %si
+ movw $1f-_prefix, %si
+ call print_message
+ popw %si
+ ret
+1: .asciz ": Non-PnP BIOS detected!\r\n"
+
+print_legacyentry:
+ pushw %si
+ movw $1f-_prefix, %si
+ call print_message
+ popw %si
+ ret
+1: .asciz "ROM using legacy boot mechanism\r\n"
+
+print_installed:
+ pushw %si
+ movw $1f-_prefix, %si
+ call print_message
+ popw %si
+ ret
+1: .ascii "hooked boot via INT"
+#ifdef BOOT_INT18H
+ .asciz "18\r\n"
+#else
+ .asciz "19\r\n"
+#endif
+
+print_start_int:
+ pushw %si
+ movw $1f-_prefix, %si
+ call print_message
+ popw %si
+ ret
+1: .asciz "booting via hooked interrupt\r\n"
+
+print_message:
+ pushaw
+ pushw %ds
+ pushw %cs
+ popw %ds
+ pushw %si
+ movw $1f-_prefix, %si
+ call print_string
+ popw %si
+ call print_string
+ popw %ds
+ popaw
+ ret
+1: .asciz "Etherboot "
+
+print_string:
+1: lodsb
+ testb %al,%al
+ je 2f
+ movw $0x0007, %bx /* page 0, attribute 7 (normal) */
+ movb $0x0e, %ah /* write char, tty mode */
+ int $0x10
+ jmp 1b
+2: ret
+
+#endif
diff --git a/src/arch/i386/prefix/unhuf.S b/src/arch/i386/prefix/unhuf.S
new file mode 100644
index 00000000..390556e3
--- /dev/null
+++ b/src/arch/i386/prefix/unhuf.S
@@ -0,0 +1,400 @@
+/*****************************************************************************
+ * NOTE: This code is no longer used in Etherboot. The obsolete
+ * Makefile target .lzrom refers to it, but it is no longer being
+ * maintained and may no longer work. Use .zrom instead (which uses
+ * the unnrv2b decompressor).
+ *****************************************************************************
+ */
+
+/* At entry, the processor is in 16 bit real mode and the code is being
+ * executed from an address it was not linked to. Code must be pic and
+ * 32 bit sensitive until things are fixed up.
+ */
+
+
+/* LZHuf (LZSS) Decompressing boot loader for ROM images
+ *
+ * this code is based on the work of Haruyasu Yoshizaki and Haruhiko Okumura
+ * who implemented the original compressor and decompressor in C code
+ *
+ * Converted to 32bit assembly 16 July 2002 Eric Biederman <ebiederman@lnxi.com>
+ * Made PIC 10 Aug 2002 Eric Biederman <ebiederman@lnxi.com>
+ *
+ * Copyright 1997 by M. Gutschke <gutschk@math.uni-muenster.de>
+ *
+ * Compression pays off, as soon as the uncompressed image is bigger than
+ * about 1.5kB. This assumes an average compressibility of about 60%.
+ */
+
+
+/* Do not change these values unless you really know what you are doing
+ * the pre-computed lookup tables rely on the buffer size being 4kB or
+ * smaller. The buffer size must be a power of two. The lookahead size has
+ * to fit into 6 bits. If you change any of these numbers, you will also
+ * have to adjust the compressor accordingly.
+ */
+#define BUFSZ 4096
+#define LOOKAHEAD 60
+#define THRESHOLD 2
+#define NCHAR (256+LOOKAHEAD-THRESHOLD)
+#define TABLESZ (NCHAR+NCHAR-1)
+#define ROOT (TABLESZ-1)
+
+ .text
+ .arch i386
+ .globl _start
+_start:
+ cli
+
+ /* Save the initial register values */
+ pushal
+
+ /*
+ * See where I am running, and compute %ebp
+ */
+ call 1f
+1: pop %ebp
+ subl $1b, %ebp
+
+/*
+ * INIT -- initializes all data structures
+ * ====
+ */
+
+init:
+ cld
+ leal dcodrle(%ebp), %esi /* uncompress run length encoded */
+ leal dcode(%ebp), %edi /* lookup table for codes */
+ movb $6, %dl
+ movb $0x20, %dh
+ xorb %bh,%bh
+init0:
+ lodsb
+ movb %al,%bl
+init1:
+ xorl %ecx, %ecx
+ movb %dh,%cl
+ movb %bh,%al
+ rep
+ stosb
+ incb %bh
+ decb %bl
+ jnz init1
+ shrb %dh
+ decb %dl
+ jnz init0
+ movb $1, %bl /* uncompress run length encoded */
+ movb $6, %bh /* lookup table for code lengths */
+init2:
+ lodsb
+ xorl %ecx, %ecx
+ movb %al,%cl
+ movb %bl,%al
+ rep
+ stosb
+ incb %bl
+ decb %bh
+ jnz init2
+
+ movl $NCHAR, %ecx /* set all frequencies of leaf nodes */
+ movw $1, %ax /* to one */
+ rep
+ stosw
+ leal freq(%ebp), %esi
+ movl $ROOT+1-NCHAR, %ecx
+init3:
+ lodsw /* update frequencies of non-leaf nodes */
+ movw %ax,%bx
+ lodsw
+ addw %bx,%ax
+ stosw
+ loop init3
+ movw $0xFFFF, %ax
+ stosw /* sentinel with infinite frequency */
+ movl $NCHAR, %ecx
+ movw $TABLESZ, %ax
+init4:
+ stosw /* update son pointers for leaf nodes */
+ incw %ax
+ loop init4
+ movl $ROOT+1-NCHAR, %ecx
+ xorw %ax,%ax
+init5:
+ stosw /* update son ptrs for non-leaf nodes */
+ addw $2, %ax
+ loop init5
+ movl $ROOT+1-NCHAR, %ecx
+ movw $NCHAR, %ax
+init6:
+ stosw /* update parent ptrs for non-leaf nd. */
+ stosw
+ incw %ax
+ loop init6
+ movl $NCHAR, %ecx
+ xorw %ax,%ax
+ stosw /* root node has no parent */
+init7:
+ stosw /* update parent ptrs for leaf nodes */
+ incw %ax
+ loop init7
+ xorw %ax,%ax
+ stosb /* clear getlen */
+ stosw /* clear getbuf */
+ movb $0x20, %al /* fill text buffer with spaces */
+ leal spaces(%ebp), %edi
+ movl $BUFSZ-LOOKAHEAD, %ecx
+ rep
+
+ stosb
+ /* fall thru */
+
+/*
+ * MAIN -- reads compressed codes and writes decompressed data
+ * ====
+ */
+
+ leal _payload(%ebp), %esi /* get length of compressed data stream */
+ leal uncompressed(%ebp), %edi
+
+ lodsl
+ movl %eax, %ecx
+main1:
+ pushl %ecx
+ call dcdchr /* decode one code symbol */
+ orb %ah,%ah /* test if 8bit character */
+ jnz main2
+ stosb /* store verbatim */
+ popl %ecx
+ loop main1 /* proceed with next compressed code */
+ jmp done /* until end of input is detected */
+main2:
+ pushl %eax
+ call dcdpos /* compute position in output buffer */
+ movl %esi, %eax
+ subl %edi, %ebx
+ notl %ebx
+ movl %ebx, %esi /* si := di - dcdpos() - 1 */
+ popl %ecx
+ subl $255-THRESHOLD, %ecx /* compute length of code sequence */
+ movl %ecx, %edx
+ rep
+ movsb
+ movl %eax,%esi
+ popl %ecx
+ subl %edx, %ecx /* check end of input condition */
+ jnz main1 /* proceed with next compressed code */
+done:
+ /* Start Etherboot */
+ popal
+ jmp uncompressed
+/*
+ * GETBIT -- gets one bit pointed to by DS:ESI
+ * ======
+ *
+ * changes: AX,CX,DL
+ */
+
+getbit:
+ movb $8, %cl
+ movb getlen(%ebp), %dl /* compute number of bits required */
+ subb %dl,%cl /* to fill read buffer */
+ jae getbit1
+ movw getbuf(%ebp), %ax /* there is still enough read ahead data */
+ jmp getbit2
+getbit1:
+ lodsb /* get next byte from input stream */
+ xorb %ah,%ah
+ shlw %cl,%ax /* shift, so that it will fit into */
+ movw getbuf(%ebp), %cx /* read ahead buffer */
+ orw %cx,%ax
+ addb $8, %dl /* update number of bits in buffer */
+getbit2:
+ movw %ax,%cx
+ shlw %cx /* extract one bit from buffer */
+ movw %cx, getbuf(%ebp)
+ decb %dl
+ movb %dl, getlen(%ebp) /* and update number of bits */
+ shlw %ax /* return in carry flag */
+ ret
+
+
+/*
+ * DCDPOS -- decodes position in textbuffer as pointed to by DS:SI, result in BX
+ * ======
+ *
+ * changes: AX,EBX,ECX,DX
+ */
+
+dcdpos:
+ movl $0x0800, %ebx
+dcdpos1:
+ shlb %bl /* read one byte */
+ call getbit
+ jnc dcdpos2
+ incb %bl
+dcdpos2:
+ decb %bh
+ jnz dcdpos1
+ movb %bl,%dh /* read length of code from table */
+ xorb %bh,%bh
+ xorl %ecx, %ecx
+ movb dlen(%ebx, %ebp),%cl
+ movb dcode(%ebx, %ebp),%bl /* get top six bits from table */
+ shll $6, %ebx
+dcdpos3:
+ pushl %ecx /* read the rest from the input stream */
+ shlb %dh
+ call getbit
+ jnc dcdpos4
+ incb %dh
+dcdpos4:
+ popl %ecx
+ loop dcdpos3
+ andb $0x3f, %dh /* combine upper and lower half of code */
+ orb %dh,%bl
+ ret
+
+/*
+ * DCDCHR -- decodes one compressed character pointed to by DS:SI
+ * ======
+ *
+ * changes: AX,BX,CX,DX
+ */
+
+dcdchr:
+ movl $ROOT, %ebx /* start at root entry */
+ shll %ebx
+ movzwl son(%ebx, %ebp),%ebx
+dcdchr1:
+ call getbit /* get a single bit */
+ jnc dcdchr2
+ incl %ebx /* travel left or right */
+dcdchr2:
+ shll %ebx
+ movzwl son(%ebx, %ebp), %ebx
+ cmpl $TABLESZ, %ebx /* until we come to a leaf node */
+ jb dcdchr1
+ movl %ebx, %eax
+ subl $TABLESZ, %eax
+ /* fall thru */
+
+/*
+ * UPDATE -- updates huffman tree after incrementing frequency for code in BX
+ * ======
+ *
+ * changes: BX,CX,DX
+ */
+
+update:
+ /* we do not check whether the frequency count has overrun.
+ * this will cause problems for large files, but be should be fine
+ * as long as the compressed size does not exceed 32kB and we
+ * cannot do more than this anyways, because we load into the
+ * upper 32kB of conventional memory
+ */
+ pushl %esi
+ pushl %eax
+ shll %ebx
+ movzwl parent(%ebx, %ebp),%ebx
+update1:
+ shll %ebx
+ movzwl freq(%ebx, %ebp), %edx
+ incl %edx /* increment frequency count by one */
+ movw %dx, freq(%ebx, %ebp)
+ leal 2+freq(%ebx, %ebp), %esi
+ lodsw /* check if nodes need reordering */
+ cmpw %ax, %dx
+ jbe update5
+update2:
+ lodsw
+ cmpw %dx, %ax
+ jb update2
+ movzwl -4(%esi), %ecx
+ movw %cx, freq(%ebx, %ebp) /* swap frequency of entries */
+ movw %dx, -4(%esi)
+
+ movl %esi, %eax /* compute index of new entry */
+ subl $freq+4, %eax
+ subl %ebp, %eax
+
+ movl %eax, %edx
+ shrl %eax
+ movzwl son(%ebx, %ebp), %ecx /* get son of old entry */
+ movl %ecx, %esi
+ addl %esi, %esi
+ movw %ax, parent(%esi, %ebp) /* and update the ptr to new parent */
+ cmpl $TABLESZ, %ecx
+ jae update3 /* do this for both branches */
+ movw %ax, parent+2(%esi, %ebp) /* if not a leaf node */
+update3:
+ movl %edx, %esi
+ movzwl son(%esi, %ebp), %edx /* get son of new entry */
+ movw %cx, son(%esi, %ebp) /* update its contents */
+ movl %edx, %esi
+ addl %esi, %esi
+ movl %ebx, %ecx
+ shrl %ecx
+ movw %cx, parent(%esi, %ebp) /* and update the ptr to new paren */
+ cmpl $TABLESZ, %edx
+ jae update4 /* do this for both branches */
+ movw %cx, parent+2(%esi, %ebp) /* if not a leaf node */
+update4:
+ movw %dx, son(%ebx, %ebp) /* update son of old entry */
+ movl %eax, %ebx /* continue with new entry */
+ shll %ebx
+update5:
+ movzwl parent(%ebx, %ebp), %ebx /* continue with parent */
+ orl %ebx, %ebx
+ jnz update1 /* until we found the root entry */
+ popl %eax
+ popl %esi
+ ret
+
+/*
+ * constant data. this part of the program resides in ROM and cannot be
+ * changed
+ *
+ * run length encoded tables will be uncompressed into the bss segment
+ * take care with any symbols here for .com files to add 0x100 to address
+ */
+
+dcodrle: .byte 0x01,0x03,0x08,0x0C,0x18,0x10
+dlenrle: .byte 0x20,0x30,0x40,0x30,0x30,0x10
+
+/*
+ * variable data segment (bss)
+ * this segment will always be found at 0x90000 (i.e. at RELOC - SCRATCH)
+ *
+ * do not change the order or the sizes of any of the following tables
+ * the initialization code makes assumptions on the exact layout of the
+ * data structures...
+ */
+
+.bss
+/* lookup table for index into buffer of recently output characters */
+dcode: .skip 256
+
+/* lookup table for length of code sequence from buffer of recent characters */
+dlen: .skip 256
+
+/* table with frequency counts for all codes */
+freq: .skip 2*(TABLESZ+1)
+
+/* pointer to child nodes */
+son: .skip 2*(TABLESZ)
+
+/* the first part of this table contains all the codes (0..TABLESZ-1) */
+/* the second part contains all leaf nodes (TABLESZ..) */
+parent: .skip 2*(TABLESZ+NCHAR)
+
+/* temporary storage for extracting bits from compressed data stream */
+getlen: .skip 1
+getbuf: .skip 1
+
+ /* the initial buffer has to be filled with spaces */
+ .balign 4
+spaces:
+ .skip BUFSZ - LOOKAHEAD
+ /* uncompressed data will be written here */
+uncompressed:
+
diff --git a/src/arch/i386/prefix/unhuf.lds b/src/arch/i386/prefix/unhuf.lds
new file mode 100644
index 00000000..00d6b55b
--- /dev/null
+++ b/src/arch/i386/prefix/unhuf.lds
@@ -0,0 +1,33 @@
+OUTPUT_FORMAT("elf32-i386", "elf32-i386", "elf32-i386")
+OUTPUT_ARCH(i386)
+
+SECTIONS
+{
+ . = 0;
+ .text : {
+ _text = .;
+ *(.head)
+ *(.text)
+ } = 0x9090
+ .rodata : {
+ *(.rodata)
+ }
+ _etext = . ;
+ .data : {
+ *(.data)
+ /* Force 4 byte alignment */
+ . = ALIGN(4);
+ _payload = . ;
+ *(.huf)
+ _epayload = . ;
+ }
+ _edata = . ;
+ _data_size = _edata - _start;
+ /* Etherboot needs to be 16 byte aligned */
+ . = ALIGN(16);
+ .bss : {
+ *(.bss)
+ }
+ _end = . ;
+ _image_size = _end - _start;
+}
diff --git a/src/arch/i386/prefix/unnrv2b.S b/src/arch/i386/prefix/unnrv2b.S
new file mode 100644
index 00000000..1836fa71
--- /dev/null
+++ b/src/arch/i386/prefix/unnrv2b.S
@@ -0,0 +1,129 @@
+/*
+ * Copyright (C) 1996-2002 Markus Franz Xaver Johannes Oberhumer
+ *
+ * This file is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * Originally this code was part of ucl the data compression library
+ * for upx the ``Ultimate Packer of eXecutables''.
+ *
+ * - Converted to gas assembly, and refitted to work with etherboot.
+ * Eric Biederman 20 Aug 2002
+ *
+ * - Structure modified to be a subroutine call rather than an
+ * executable prefix.
+ * Michael Brown 30 Mar 2004
+ */
+
+
+ .text
+ .arch i386
+ .section ".prefix", "ax", @progbits
+ .code32
+
+ .globl decompress
+decompress:
+ /* Save the initial register values */
+ pushal
+
+ /*
+ * See where I am running, and compute %ebp
+ * %ebp holds delta between physical and virtual addresses.
+ */
+ call 1f
+1: popl %ebp
+ subl $1b, %ebp
+
+ /* "compressed" and "decompress_to" defined by linker script */
+ /* move compressed image up to temporary area before decompressing */
+ std
+ movl $_compressed_size, %ecx
+ leal _compressed+4-1(%ebp, %ecx), %esi
+ leal _compressed_copy-1(%ebp, %ecx), %edi
+ rep movsb
+ /* Setup to run the decompressor */
+ cld
+ leal _compressed_copy(%ebp), %esi
+ leal decompress_to(%ebp), %edi
+ movl $-1, %ebp /* last_m_off = -1 */
+ jmp dcl1_n2b
+
+/* ------------- DECOMPRESSION -------------
+
+ Input:
+ %esi - source
+ %edi - dest
+ %ebp - -1
+ cld
+
+ Output:
+ %eax - 0
+ %ecx - 0
+*/
+
+.macro getbit bits
+.if \bits == 1
+ addl %ebx, %ebx
+ jnz 1f
+.endif
+ movl (%esi), %ebx
+ subl $-4, %esi /* sets carry flag */
+ adcl %ebx, %ebx
+1:
+.endm
+
+decompr_literals_n2b:
+ movsb
+
+decompr_loop_n2b:
+ addl %ebx, %ebx
+ jnz dcl2_n2b
+dcl1_n2b:
+ getbit 32
+dcl2_n2b:
+ jc decompr_literals_n2b
+ xorl %eax, %eax
+ incl %eax /* m_off = 1 */
+loop1_n2b:
+ getbit 1
+ adcl %eax, %eax /* m_off = m_off*2 + getbit() */
+ getbit 1
+ jnc loop1_n2b /* while(!getbit()) */
+ xorl %ecx, %ecx
+ subl $3, %eax
+ jb decompr_ebpeax_n2b /* if (m_off == 2) goto decompr_ebpeax_n2b ? */
+ shll $8, %eax
+ movb (%esi), %al /* m_off = (m_off - 3)*256 + src[ilen++] */
+ incl %esi
+ xorl $-1, %eax
+ jz decompr_end_n2b /* if (m_off == 0xffffffff) goto decomp_end_n2b */
+ movl %eax, %ebp /* last_m_off = m_off ?*/
+decompr_ebpeax_n2b:
+ getbit 1
+ adcl %ecx, %ecx /* m_len = getbit() */
+ getbit 1
+ adcl %ecx, %ecx /* m_len = m_len*2 + getbit()) */
+ jnz decompr_got_mlen_n2b /* if (m_len == 0) goto decompr_got_mlen_n2b */
+ incl %ecx /* m_len++ */
+loop2_n2b:
+ getbit 1
+ adcl %ecx, %ecx /* m_len = m_len*2 + getbit() */
+ getbit 1
+ jnc loop2_n2b /* while(!getbit()) */
+ incl %ecx
+ incl %ecx /* m_len += 2 */
+decompr_got_mlen_n2b:
+ cmpl $-0xd00, %ebp
+ adcl $1, %ecx /* m_len = m_len + 1 + (last_m_off > 0xd00) */
+ pushl %esi
+ leal (%edi,%ebp), %esi /* m_pos = dst + olen + -m_off */
+ rep
+ movsb /* dst[olen++] = *m_pos++ while(m_len > 0) */
+ popl %esi
+ jmp decompr_loop_n2b
+decompr_end_n2b:
+ /* Restore the initial register values */
+ popal
+ ret
diff --git a/src/arch/ia64/Config b/src/arch/ia64/Config
new file mode 100644
index 00000000..6b7405f1
--- /dev/null
+++ b/src/arch/ia64/Config
@@ -0,0 +1,22 @@
+# Config for ia64 Etherboot
+#
+# Do not delete the tag OptionDescription and /OptionDescription
+# It is used to automatically generate the documentation.
+#
+# @OptionDescrition@
+#
+# BIOS interface options:
+#
+# -DCONFIG_EFI
+# Compile in support for EFI
+#
+# @/OptionDescription@
+
+CFLAGS+= -DCONSOLE_FIRMWARE
+CFLAGS+= -DCONFIG_EFI
+
+CFLAGS+= -fpic -mconstant-gp -mauto-pic
+ASFLAGS+= -mconstant-gp -mauto-pic
+
+LDFLAGS+= -static -shared -Bsymbolic --warn-multiple-gp --warn-common
+
diff --git a/src/arch/ia64/Makefile b/src/arch/ia64/Makefile
new file mode 100644
index 00000000..42e0b47b
--- /dev/null
+++ b/src/arch/ia64/Makefile
@@ -0,0 +1,125 @@
+ARCH_FORMAT= elf64-ia64-little
+
+LCONFIG+=
+
+
+BUILD_EFIS= $(patsubst %.img, %.efi, $(IMGS)) $(patsubst %.img, %.zefi, $(IMGS))
+
+START= $(BIN)/start.o $(BIN)/reloc.o
+#START+= $(BIN)/efi_main.o
+
+SRCS+= arch/ia64/prefix/efi_prefix.S arch/ia64/prefix/unnrv2b.S
+SRCS+= arch/ia64/core/__call.S
+SRCS+= arch/ia64/core/ia64_timer.c
+SRCS+= arch/ia64/core/idiv32.S
+SRCS+= arch/ia64/core/idiv64.S
+SRCS+= arch/ia64/core/longjmp.S
+SRCS+= arch/ia64/core/memmove.S
+SRCS+= arch/ia64/core/memset.S
+SRCS+= arch/ia64/core/pal.c
+SRCS+= arch/ia64/core/pci_io.c
+SRCS+= arch/ia64/core/reloc.S
+SRCS+= arch/ia64/core/relocate_to.S
+SRCS+= arch/ia64/core/sal.c
+SRCS+= arch/ia64/core/setjmp.S
+SRCS+= arch/ia64/core/start.S
+SRCS+= arch/ia64/core/efi.c
+
+ROMLIMIT:=3276800
+
+include $(BIN)/Roms
+
+# We need allefis because $(IMGS) is not defined until
+# the Makefile fragment "Roms" is read.
+allefis: $(BUILD_EFIS)
+
+
+#BOBJS+= $(BIN)/acpi.o
+BOBJS+= $(BIN)/sal.o $(BIN)/pal.o
+BOBJS+= $(BIN)/efi.o
+BOBJS+= $(BIN)/memset.o $(BIN)/memmove.o
+BOBJS+= $(BIN)/setjmp.o $(BIN)/longjmp.o
+BOBJS+= $(BIN)/relocate_to.o $(BIN)/__call.o
+BOBJS+= $(BIN)/pci_io.o $(BIN)/ia64_timer.o
+BOBJS+= $(BIN)/__divdi3.o $(BIN)/__udivdi3.o $(BIN)/__moddi3.o $(BIN)/__umoddi3.o
+BOBJS+= $(BIN)/__divsi3.o $(BIN)/__udivsi3.o $(BIN)/__modsi3.o $(BIN)/__umodsi3.o
+
+
+# IA64 Division routines
+$(BIN)/__divdi3.o: arch/ia64/core/idiv64.S
+ $(CPP) $(CFLAGS) -DASSEMBLY $< | $(AS) $(ASFLAGS) -o $@
+
+$(BIN)/__udivdi3.o: arch/ia64/core/idiv64.S
+ $(CPP) $(CFLAGS) -DASSEMBLY -DUNSIGNED $< | $(AS) $(ASFLAGS) -o $@
+
+$(BIN)/__moddi3.o: arch/ia64/core/idiv64.S
+ $(CPP) $(CFLAGS) -DASSEMBLY -DMODULO $< | $(AS) $(ASFLAGS) -o $@
+
+$(BIN)/__umoddi3.o: arch/ia64/core/idiv64.S
+ $(CPP) $(CFLAGS) -DASSEMBLY -DUNSIGNED -DMODULO $< | $(AS) $(ASFLAGS) -o $@
+
+$(BIN)/__divsi3.o: arch/ia64/core/idiv32.S
+ $(CPP) $(CFLAGS) -DASSEMBLY $< | $(AS) $(ASFLAGS) -o $@
+
+$(BIN)/__udivsi3.o: arch/ia64/core/idiv32.S
+ $(CPP) $(CFLAGS) -DASSEMBLY -DUNSIGNED $< | $(AS) $(ASFLAGS) -o $@
+
+$(BIN)/__modsi3.o: arch/ia64/core/idiv32.S
+ $(CPP) $(CFLAGS) -DASSEMBLY -DMODULO $< | $(AS) $(ASFLAGS) -o $@
+
+$(BIN)/__umodsi3.o: arch/ia64/core/idiv32.S
+ $(CPP) $(CFLAGS) -DASSEMBLY -DUNSIGNED -DMODULO $< | $(AS) $(ASFLAGS) -o $@
+
+
+
+# Utilities
+$(BIN)/nrv2b: util/nrv2b.c
+ $(HOST_CC) -O2 -DENCODE -DDECODE -DMAIN -DVERBOSE -DNDEBUG -DBITSIZE=64 -DENDIAN=0 -o $@ $<
+
+# Pattern Rules
+
+# General for compiling assembly source files
+$(BIN)/%.o: arch/ia64/core/%.c $(MAKEDEPS)
+ $(CC) $(CFLAGS) -o $@ -c $<
+
+$(BIN)/%.o: arch/ia64/prefix/%.S $(MAKEDEPS)
+ $(CPP) $(CFLAGS) -DASSEMBLY $< | $(AS) $(ASFLAGS) -o $@
+
+$(BIN)/%.o: arch/ia64/core/%.S $(MAKEDEPS)
+ $(CPP) $(CFLAGS) -DASSEMBLY $< | $(AS) $(ASFLAGS) -o $@
+
+$(BIN)/%.o: $(BIN)/%.s
+ $(AS) $(ASFLAGS) -o $@ $<
+
+# General rules for bootable images
+
+# Rules for nrv2b compressed images
+$(BIN)/unnrv2b.tmp: $(BIN)/unnrv2b.o arch/ia64/prefix/unnrv2b.lds $(MAKEDEPS)
+ $(LD) -T arch/ia64/prefix/unnrv2b.lds $< -o $@
+
+$(BIN)/unnrv2b: $(BIN)/unnrv2b.tmp $(MAKEDEPS)
+ $(OBJCOPY) -O binary $< $@
+
+$(BIN)/%.zimg: $(BIN)/%.z $(BIN)/unnrv2b arch/ia64/prefix/apply_unnrv2b_prefix.pl $(MAKEDEPS)
+ $(PERL) arch/ia64/prefix/apply_unnrv2b_prefix.pl $(BIN)/unnrv2b $< > $@
+
+# Placeholder; add no extra symbols to %.sym
+$(BIN)/%.zsym: $(BIN)/%.sym $(MAKEDEPS)
+ cp -f $< $@
+
+# rules to generate efi loadable image
+SUFFIXES += efi zefi
+$(BIN)/efi_prefix.tmp: $(BIN)/efi_prefix.o arch/ia64/prefix/efi_prefix.lds $(MAKEDEPS)
+ $(LD) -T arch/ia64/prefix/efi_prefix.lds $< -o $@
+
+$(BIN)/efi_prefix: $(BIN)/efi_prefix.tmp $(MAKEDEPS)
+ $(OBJCOPY) -O binary $< $@
+
+$(BIN)/%.efi: $(BIN)/%.img $(BIN)/%.tmp $(BIN)/efi_prefix arch/ia64/prefix/apply_efi_prefix.pl $(MAKEDEPS)
+ @$(SIZE) $(BIN)/$(*).tmp | (read l1; read d1 d2 bss rest ; echo $$bss )
+ $(PERL) arch/ia64/prefix/apply_efi_prefix.pl $(BIN)/efi_prefix $< `$(SIZE) $(BIN)/$(*).tmp | (read l1; read d1 d2 bss rest ; echo $$bss )` > $@
+
+$(BIN)/%.zefi: $(BIN)/%.zimg $(BIN)/%.tmp $(BIN)/efi_prefix arch/ia64/prefix/apply_efi_prefix.pl $(MAKEDEPS)
+ @$(SIZE) $(BIN)/$(*).tmp | (read l1; read d1 d2 d3 size rest ; echo $$size )
+ $(PERL) arch/ia64/prefix/apply_efi_prefix.pl $(BIN)/efi_prefix $< `$(SIZE) $(BIN)/$(*).tmp | (read l1; read d1 d2 d3 size rest ; echo $$size )` > $@
+
diff --git a/src/arch/ia64/core/__call.S b/src/arch/ia64/core/__call.S
new file mode 100644
index 00000000..a11b8264
--- /dev/null
+++ b/src/arch/ia64/core/__call.S
@@ -0,0 +1,68 @@
+ /* Trampoline for calling outside of etherboot */
+
+ .text
+ .globl __call
+ .proc __call
+__call:
+ alloc loc0=ar.pfs,8,3,8,0 /* in, local, out, rotating */
+ mov loc1=rp
+ mov loc2=gp
+ ld8 r14=[in0],8
+ ;;
+ ld8 gp=[in0]
+ mov r28=in1 /* So we can use stacked pal calling conventions */
+ mov out0=in1
+ mov out1=in2
+ mov out2=in3
+ mov out3=in4
+ mov out4=in5
+ mov out5=in6
+ mov out6=in7
+ mov out7=0 /* So we can work with sal calling conventions */
+
+ mov b6=r14
+ ;;
+ br.call.sptk.few rp=b6
+ ;;
+ rsm psr.i /* disable interrupts */
+ ;;
+ mov gp=loc2
+ mov rp=loc1
+ ;;
+ mov ar.pfs=loc0
+ br.ret.sptk.many rp
+
+ .size __call, . - __call
+ .endp __call
+
+
+ .text
+ .globl pal_call
+ .proc pal_call
+pal_call:
+ alloc loc0 = ar.pfs,4,3,0,0 /* in, local, out, rotating */
+ mov loc1 = rp
+ mov loc2 = gp
+ add r8 = @gprel(pal_entry),gp
+ add r9 = @gprel(pal_ret),gp
+ ;;
+ ld8 r14 = [r8]
+ ;;
+ mov r28 = in0
+ mov r29 = in1
+ mov r30 = in2
+ mov r31 = in3
+ mov b6 = r14
+ mov rp = r9
+ rsm psr.i /* disable interrupts */
+ ;;
+ br.sptk.few b6
+ ;;
+pal_ret:
+ rsm psr.i /* disable interrupts */
+ ;;
+ mov gp=loc2
+ mov rp=loc1
+ ;;
+ mov ar.pfs=loc0
+ br.ret.sptk.many rp
diff --git a/src/arch/ia64/core/efi.c b/src/arch/ia64/core/efi.c
new file mode 100644
index 00000000..97e585fc
--- /dev/null
+++ b/src/arch/ia64/core/efi.c
@@ -0,0 +1,1026 @@
+#include "efi/efi.h"
+#include "etherboot.h"
+#include "elf.h"
+#include "sal.h"
+#include "pal.h"
+
+#warning "Place a declaration of lookup_efi_nic somewhere useful"
+EFI_NETWORK_INTERFACE_IDENTIFIER_INTERFACE *lookup_efi_nic(int index);
+
+#warning "Place the declaraction of __call someplace more appropriate\n"
+extern EFI_STATUS __call(void *,...);
+
+/* Keep 16M free in case EFI needs to allocate some memory.
+ * In the worst case this is only 1/8 the memory on an Itanium.
+ */
+#define EFI_RESERVE_LOW_PAGES ((8*1024*1024)/EFI_PAGE_SIZE)
+#define EFI_RESERVE_HIGH_PAGES ((8*1024*1024)/EFI_PAGE_SIZE)
+
+struct console_info {
+ uint16_t num_cols;
+ uint16_t num_rows;
+ uint16_t orig_x;
+ uint16_t orig_y;
+};
+
+struct efi_mem_map {
+ uint64_t map_size;
+ uint64_t map_key;
+ uint64_t descriptor_size;
+ uint32_t descriptor_version;
+ uint32_t pad;
+ EFI_MEMORY_DESCRIPTOR map[64];
+};
+
+struct efi_info {
+ int flags;
+#define READ_SYSTAB 1
+#define READ_FPSWA 2
+#define READ_MEMMAP 4
+#define READ_CONINFO 8
+ EFI_SYSTEM_TABLE *systab;
+ void *fpswa;
+ struct efi_mem_map mem_map;
+ struct console_info coninfo;
+};
+
+
+unsigned long io_base;
+
+/* local globals */
+static struct efi_info efi_info;
+static EFI_HANDLE etherboot_handle;
+static EFI_BOOT_SERVICES *boot_services;
+static SIMPLE_TEXT_OUTPUT_INTERFACE *conout;
+static SIMPLE_INPUT_INTERFACE *conin;
+static void *mps_table;
+static void *acpi20_table;
+static void *smbios_table;
+static void *nii_table;
+
+/* local functions */
+
+static EFI_STATUS efi_locate_handle(
+ EFI_LOCATE_SEARCH_TYPE search_type,
+ EFI_GUID *protocol, void *search_key,
+ UINTN *buffer_size, EFI_HANDLE *buffer)
+{
+ if (!boot_services)
+ return EFI_NOT_FOUND;
+ return __call(boot_services->LocateHandle,
+ search_type, protocol, search_key, buffer_size, buffer);
+}
+
+static EFI_STATUS efi_handle_protocol(EFI_HANDLE handle, EFI_GUID *protocol, void **interface)
+{
+ if (!boot_services)
+ return EFI_UNSUPPORTED;
+ return __call(boot_services->HandleProtocol, handle, protocol, interface);
+}
+
+static EFI_STATUS efi_locate_device_path(EFI_GUID *protocol, EFI_DEVICE_PATH **device_path,
+ EFI_HANDLE *device)
+{
+ if (!boot_services)
+ return EFI_NOT_FOUND;
+ return __call(boot_services->LocateDevicePath, protocol, device_path, device);
+}
+
+static EFI_STATUS efi_allocate_pages(EFI_ALLOCATE_TYPE type, EFI_MEMORY_TYPE memory_type,
+ UINTN pages, EFI_PHYSICAL_ADDRESS *memory)
+{
+ if (!boot_services)
+ return EFI_OUT_OF_RESOURCES;
+ return __call(boot_services->AllocatePages,
+ type, memory_type, pages, memory);
+}
+
+static EFI_STATUS efi_free_pages(EFI_PHYSICAL_ADDRESS memory, UINTN pages)
+{
+ if(pages == 0)
+ return EFI_SUCCESS;
+ if (!boot_services)
+ return EFI_INVALID_PARAMETER;
+ return __call(boot_services->FreePages, memory, pages);
+}
+
+static EFI_STATUS efi_get_memory_map(UINTN *map_size, EFI_MEMORY_DESCRIPTOR *map,
+ UINTN *map_key, UINTN *descriptor_size, UINT32 *descriptor_version)
+{
+ if (!boot_services)
+ return EFI_INVALID_PARAMETER;
+ return __call(boot_services->GetMemoryMap,
+ map_size, map, map_key, descriptor_size, descriptor_version);
+}
+
+static EFI_STATUS efi_free_pool(void *buffer)
+{
+ if (!boot_services)
+ return EFI_INVALID_PARAMETER;
+ return __call(boot_services->FreePool, buffer);
+}
+static EFI_STATUS efi_stall(UINTN microseconds)
+{
+ if (!boot_services)
+ return EFI_UNSUPPORTED;
+ return __call(boot_services->Stall, microseconds);
+}
+
+static EFI_STATUS efi_set_watchdog_timer(
+ UINTN timeout, UINT64 watchdog_code, UINTN data_size, CHAR16 *watchdog_data)
+{
+ if (!boot_services)
+ return EFI_UNSUPPORTED;
+ return __call(boot_services->SetWatchdogTimer,
+ timeout, watchdog_code, data_size, watchdog_data);
+}
+
+
+static void efi_exit_boot_services(struct efi_mem_map *map)
+{
+ EFI_STATUS status;
+ if (!boot_services)
+ return;
+ status = __call(boot_services->ExitBootServices,
+ etherboot_handle, map->map_key);
+ if (status != EFI_SUCCESS) {
+ printf("ExitBootServices failed: %lx\n", status);
+ }
+ conout = 0;
+ conin = 0;
+ boot_services = 0;
+}
+
+static void efi_free_memory(struct efi_mem_map *map)
+{
+ EFI_MEMORY_DESCRIPTOR *desc, *tail;
+#define next_desc(desc, size) ((EFI_MEMORY_DESCRIPTOR *)(((char *)(desc)) + (size)))
+ tail = next_desc(map->map, map->map_size);
+ for(desc = map->map; desc < tail; desc = next_desc(desc, map->descriptor_size)) {
+ EFI_STATUS status;
+ EFI_PHYSICAL_ADDRESS start, end;
+ UINTN pages;
+ int may_free;
+
+ start = desc->PhysicalStart;
+ pages = desc->NumberOfPages;
+ end = start + pages * EFI_PAGE_SIZE;
+
+
+ may_free = 0;
+ /* The only canidates are Loader Code and Data */
+ if ((desc->Type == EfiLoaderData) ||
+ (desc->Type == EfiLoaderCode))
+ may_free = 1;
+
+ /* Don't free anything etherboot lives in */
+ if ((may_free) &&
+ (start < virt_to_phys(_end)) &&
+ (end > virt_to_phys(_text)))
+ may_free = 0;
+
+ /* Continue if it is not memory we want to free */
+ if (!may_free)
+ continue;
+
+ status = efi_free_pages(start, pages);
+ if (status != EFI_SUCCESS) {
+ printf("free_pages: %lx\n", status);
+ }
+ }
+#undef next_desc
+}
+
+static void read_efi_mem_map(struct efi_mem_map *map)
+{
+ EFI_STATUS status;
+ map->map_size = sizeof(map->map);
+ status = efi_get_memory_map(
+ &map->map_size, map->map, &map->map_key,
+ &map->descriptor_size, &map->descriptor_version);
+ if (status != EFI_SUCCESS) {
+ printf("read_efi_mem_map failed: %lx\n", status);
+ map->map_size = 0;
+ }
+ /* map->descriptor_size should only grow larger */
+ /* map->descriptor_version should only increase and retain
+ * a backward compatible format.
+ */
+}
+
+#if 0
+static const char *efi_mem_type_name(uint32_t type)
+{
+ const char *type_name;
+ if (type == EfiReservedMemoryType)
+ type_name = "EfiReservedMemoryType ";
+ else if (type == EfiLoaderCode)
+ type_name = "EfiLoaderCode ";
+ else if (type == EfiLoaderData)
+ type_name = "EfiLoaderData ";
+ else if (type == EfiBootServicesCode)
+ type_name = "EfiBootServicesCode ";
+ else if (type == EfiBootServicesData)
+ type_name = "EfiBootServicesData ";
+ else if (type == EfiRuntimeServicesCode)
+ type_name = "EfiRuntimeServicesCode ";
+ else if (type == EfiRuntimeServicesData)
+ type_name = "EfiRuntimeServicesData ";
+ else if (type == EfiConventionalMemory)
+ type_name = "EfiConventionalMemory ";
+ else if (type == EfiUnusableMemory)
+ type_name = "EfiUnusableMemory ";
+ else if (type == EfiACPIReclaimMemory)
+ type_name = "EfiACPIReclaimMemory ";
+ else if (type == EfiACPIMemoryNVS)
+ type_name = "EfiACPIMemoryNVS ";
+ else if (type == EfiMemoryMappedIO)
+ type_name = "EfiMemoryMappedIO ";
+ else if (type == EfiMemoryMappedIOPortSpace)
+ type_name = "EfiMemoryMappedIOPortSpace";
+ else if (type == EfiPalCode)
+ type_name = "EfiPalCode ";
+ else
+ type_name = "???? ";
+ return type_name;
+}
+
+static void print_efi_mem_map(struct efi_mem_map *map)
+{
+ EFI_MEMORY_DESCRIPTOR *desc, *end;
+#define next_desc(desc, size) ((EFI_MEMORY_DESCRIPTOR *)(((char *)(desc)) + (size)))
+ end = next_desc(map->map, map->map_size);
+ for(desc = map->map; desc < end ; desc = next_desc(desc, map->descriptor_size)) {
+ const char *mem_type;
+ unsigned long start, end, virt, virt_end;
+ uint64_t attr;
+ mem_type = efi_mem_type_name(desc->Type);
+ start = desc->PhysicalStart;
+ end = start + desc->NumberOfPages*EFI_PAGE_SIZE;
+ virt = desc->VirtualStart;
+ virt_end = virt + desc->NumberOfPages*EFI_PAGE_SIZE;
+ attr = desc->Attribute;
+ printf( "mem: %hhx %s @ %#lx-%#lx",
+ desc->Type, mem_type, start, end);
+ if (attr & EFI_MEMORY_UC)
+ printf("UC ");
+ if (attr & EFI_MEMORY_WC)
+ printf("WC ");
+ if (attr & EFI_MEMORY_WT)
+ printf("WT ");
+ if (attr & EFI_MEMORY_WB)
+ printf("WB ");
+ if (attr & EFI_MEMORY_UCE)
+ printf("UCE ");
+
+ if (attr & EFI_MEMORY_WP)
+ printf("WP ");
+ if (attr & EFI_MEMORY_RP)
+ printf("RP ");
+ if (attr & EFI_MEMORY_XP)
+ printf("XP ");
+
+ if (attr & EFI_MEMORY_RUNTIME)
+ printf("RUNTIME ");
+
+ printf("\n");
+ }
+#undef next_desc
+}
+#endif
+
+static void efi_allocate_memory(struct efi_mem_map *map)
+{
+ EFI_MEMORY_DESCRIPTOR *desc, *end;
+ unsigned long low_free, high_free;
+
+#define next_desc(desc, size) ((EFI_MEMORY_DESCRIPTOR *)(((char *)(desc)) + (size)))
+ end = next_desc(map->map, map->map_size);
+ /* Find out how much memory is free */
+ low_free = high_free = 0;
+ for(desc = map->map; desc < end ; desc = next_desc(desc, map->descriptor_size)) {
+ unsigned long start, middle, end;
+ if (desc->Type != EfiConventionalMemory)
+ continue;
+ start = desc->PhysicalStart;
+ end = desc->PhysicalStart + (desc->NumberOfPages*EFI_PAGE_SIZE);
+ if (start < 0x100000000UL) {
+ if (end > 0x100000000UL) {
+ middle = 0x10000000UL;
+ } else {
+ middle = end;
+ }
+ } else {
+ middle = start;
+ }
+
+ low_free += (middle - start)/EFI_PAGE_SIZE;
+ high_free += (end - middle)/EFI_PAGE_SIZE;
+ }
+ /* O.k. Now allocate all of the conventional memory, reserving only a tiny
+ * fraction for efi.
+ */
+ for(desc = map->map; desc < end ; desc = next_desc(desc, map->descriptor_size)) {
+ EFI_STATUS status;
+ EFI_PHYSICAL_ADDRESS address;
+ UINTN pages;
+ unsigned long start, middle, end;
+ unsigned long low_pages, high_pages;
+ if (desc->Type != EfiConventionalMemory)
+ continue;
+ start = desc->PhysicalStart;
+ end = desc->PhysicalStart + (desc->NumberOfPages*EFI_PAGE_SIZE);
+ if (start < 0x100000000UL) {
+ if (end > 0x100000000UL) {
+ middle = 0x10000000UL;
+ } else {
+ middle = end;
+ }
+ } else {
+ middle = start;
+ }
+ low_pages = (middle - start)/EFI_PAGE_SIZE;
+ high_pages = (end - middle)/EFI_PAGE_SIZE;
+ if (low_pages && (low_free > EFI_RESERVE_LOW_PAGES)) {
+ address = start;
+ pages = low_pages;
+ if ((low_free - pages) < EFI_RESERVE_LOW_PAGES) {
+ pages = low_free - EFI_RESERVE_LOW_PAGES;
+ }
+ status = efi_allocate_pages(
+ AllocateAddress, EfiLoaderData, pages, &address);
+ if (status != EFI_SUCCESS) {
+ printf("allocate_pages @%lx for %ld pages failed: %ld\n",
+ desc->PhysicalStart, pages, status);
+ }
+ low_free -= pages;
+ }
+ if (high_pages && (high_free > EFI_RESERVE_HIGH_PAGES)) {
+ address = middle;
+ pages = high_pages;
+ if ((high_free - pages) < EFI_RESERVE_HIGH_PAGES) {
+ pages = high_free - EFI_RESERVE_HIGH_PAGES;
+ }
+ status = efi_allocate_pages(
+ AllocateAddress, EfiLoaderData, pages, &address);
+ if (status != EFI_SUCCESS) {
+ printf("allocate_pages @%lx for %ld pages failed: %ld\n",
+ desc->PhysicalStart, pages, status);
+ }
+ high_free -= pages;
+ }
+ }
+#undef next_desc
+}
+
+static void set_io_base(struct efi_mem_map *map)
+{
+ EFI_MEMORY_DESCRIPTOR *desc, *end;
+
+ io_base = ia64_get_kr0(); /* Default to ar.kr0 */
+
+#define next_desc(desc, size) ((EFI_MEMORY_DESCRIPTOR *)(((char *)(desc)) + (size)))
+ end = next_desc(map->map, map->map_size);
+
+ for(desc = map->map; desc < end ; desc = next_desc(desc, map->descriptor_size)) {
+ if (desc->Type == EfiMemoryMappedIOPortSpace) {
+ io_base = desc->PhysicalStart;
+ break;
+ }
+ }
+#undef next_desc
+}
+
+#define MAX_EFI_DEVICES 32
+static void efi_stop_nics(void)
+{
+ static EFI_GUID simple_net_protocol = EFI_SIMPLE_NETWORK_PROTOCOL;
+ EFI_SIMPLE_NETWORK *simple;
+ EFI_STATUS status;
+ EFI_HANDLE handles[MAX_EFI_DEVICES];
+ EFI_HANDLE handle;
+ UINTN devices;
+ unsigned i;
+
+ if (!boot_services)
+ return;
+
+ devices = sizeof(handles);
+ status = efi_locate_handle(
+ ByProtocol, &simple_net_protocol, 0, &devices, handles);
+ if (status != EFI_SUCCESS)
+ return;
+ devices /= sizeof(handles[0]);
+ for(i = 0; i < devices; i++) {
+ void *that;
+ handle = handles[i];
+ status = efi_handle_protocol(handle, &simple_net_protocol, &that);
+ if (status != EFI_SUCCESS)
+ continue;
+ simple = that;
+ if ((simple->Mode->State == EfiSimpleNetworkInitialized)) {
+ status = __call(simple->Shutdown, simple);
+ status = __call(simple->Stop, simple);
+ }
+ else if (simple->Mode->State == EfiSimpleNetworkStarted) {
+ status = __call(simple->Stop, simple);
+ }
+ }
+}
+
+static void efi_get_coninfo(struct console_info *info)
+{
+ EFI_STATUS status;
+ UINTN cols, rows;
+
+ /* Initialize with some silly safe values */
+ info->num_cols = 80;
+ info->num_rows = 24;
+ info->orig_x = 0;
+ info->orig_y = 0;
+
+ status = EFI_UNSUPPORTED;
+ if (conout) {
+ status = __call(conout->QueryMode, conout, conout->Mode->Mode, &cols, &rows);
+ if (status) {
+ printf("QueryMode Failed cannout get console parameters: %ld\n", status);
+ } else {
+ info->num_cols = cols;
+ info->num_rows = rows;
+ info->orig_x = conout->Mode->CursorColumn;
+ info->orig_y = conout->Mode->CursorRow;
+ }
+ }
+}
+
+static void *efi_get_fpswa(void)
+{
+ static EFI_GUID fpswa_protocol = FPSWA_PROTOCOL;
+ EFI_STATUS status;
+ EFI_HANDLE fpswa_handle;
+ UINTN devices;
+ void *result;
+
+ /* The FPSWA is the Floating Point Software Assist driver,
+ * to some extent it makes sense but it has one large flaw.
+ * It fails to install an EFI Configuration table, so the
+ * OS does not need assistance from the bootloader to find it.
+ */
+ devices = sizeof(fpswa_handle);
+ status = efi_locate_handle(
+ ByProtocol, &fpswa_protocol, 0, &devices, &fpswa_handle);
+ if (status != EFI_SUCCESS)
+ return 0;
+
+ status = efi_handle_protocol(
+ fpswa_handle, &fpswa_protocol, &result);
+ if (status != EFI_SUCCESS)
+ return 0;
+
+ return result;
+}
+
+
+/* Exported functions */
+
+
+void arch_main(in_call_data_t *data, va_list params)
+{
+ EFI_STATUS status;
+ unsigned char *note, *end;
+
+ /* IA64 doesn't have an in_call() implementation; start.S
+ * passes in this parameter directly on the stack instead of
+ * as part of the in_call_data_t structure or the parameter
+ * list. params is unusable: don't attempt to access it.
+ */
+ struct Elf_Bhdr *ptr = (struct Elf_Bhdr *)data;
+
+ memset(&efi_info, 0, sizeof(efi_info));
+ note = ((char *)bhdr) + sizeof(*bhdr);
+ end = ((char *)bhdr) + bhdr->b_size;
+ if (bhdr->b_signature != 0x0E1FB007) {
+ printf("Bad bhdr(%lx) signature(%x)!\n",
+ (unsigned long) bhdr, bhdr->b_signature);
+ note = end = 0;
+ }
+ while(note < end) {
+ Elf_Nhdr *hdr;
+ unsigned char *n_name, *n_desc, *next;
+ hdr = (Elf_Nhdr *)note;
+ n_name = note + sizeof(*hdr);
+ n_desc = n_name + ((hdr->n_namesz + 3) & ~3);
+ next = n_desc + ((hdr->n_descsz + 3) & ~3);
+ if (next > end)
+ break;
+#if 0
+ printf("n_type: %x n_name(%d): n_desc(%d): \n",
+ hdr->n_type, hdr->n_namesz, hdr->n_descsz);
+#endif
+ if ((hdr->n_namesz == 10) &&
+ (memcmp(n_name, "Etherboot", 10) == 0)) {
+ switch(hdr->n_type) {
+ case EB_IA64_IMAGE_HANDLE:
+ {
+ uint64_t *handlep = (void *)n_desc;
+ etherboot_handle = (EFI_HANDLE)(*handlep);
+ break;
+ }
+ case EB_IA64_SYSTAB:
+ {
+ uint64_t *systabp = (void *)n_desc;
+ efi_info.systab = (void *)(*systabp);
+ efi_info.flags |= READ_SYSTAB;
+ break;
+ }
+ case EB_IA64_FPSWA:
+ {
+ uint64_t*fpswap = (void *)n_desc;
+ efi_info.fpswa = (void *)(*fpswap);
+ efi_info.flags |= READ_FPSWA;
+ break;
+ }
+ case EB_IA64_CONINFO:
+ {
+ struct console_info *coninfop = (void *)n_desc;
+ efi_info.coninfo = *coninfop;
+ efi_info.flags |= READ_CONINFO;
+ break;
+ }
+ case EB_IA64_MEMMAP:
+ {
+ struct efi_mem_map *mem_mapp = (void *)n_desc;
+ efi_info.mem_map = *mem_mapp;
+ efi_info.flags |= READ_MEMMAP;
+ break;
+ }
+ default:
+ break;
+ }
+ }
+ note = next;
+ }
+ if (!(efi_info.flags & READ_SYSTAB)) {
+ printf("No EFI systab\n");
+ return;
+ }
+
+ /* If I have an efi memory map assume ExitBootServices has been called.
+ */
+#warning "FIXME see if there is a better test for boot services still being active "
+ printf("FIXME Develop a better test for boot services still being active\n");
+ if (!(efi_info.flags & READ_MEMMAP)) {
+ conout = efi_info.systab->ConOut;
+ conin = efi_info.systab->ConIn;
+ boot_services = efi_info.systab->BootServices;
+ }
+
+ if (!(efi_info.flags & READ_CONINFO)) {
+ efi_info.flags |= READ_CONINFO;
+ efi_get_coninfo(&efi_info.coninfo);
+ }
+ if (!(efi_info.flags & READ_FPSWA)) {
+ efi_info.flags |= READ_FPSWA;
+ efi_info.fpswa = efi_get_fpswa();
+ }
+ if (!(efi_info.flags & READ_MEMMAP)) {
+ efi_info.flags |= READ_MEMMAP;
+ read_efi_mem_map(&efi_info.mem_map);
+ /* Allocate all of the memory efi can spare */
+ efi_allocate_memory(&efi_info.mem_map);
+ /* Now refresh the memory map */
+ read_efi_mem_map(&efi_info.mem_map);
+ }
+ /* Get the io_base for legacy i/o */
+ set_io_base(&efi_info.mem_map);
+
+ /* Attempt to disable the watchdog timer..
+ * Nothing useful can be done if this fails, so ignore the return code.
+ */
+ status = efi_set_watchdog_timer(0, 1, 0, 0);
+
+ /* Shutdown efi network drivers so efi doesn't get too confused */
+ efi_stop_nics();
+
+ if (efi_info.systab) {
+ static const EFI_GUID mps_table_guid = MPS_TABLE_GUID;
+ static const EFI_GUID acpi20_table_guid = ACPI_20_TABLE_GUID;
+ static const EFI_GUID smbios_table_guid = SMBIOS_TABLE_GUID;
+ static const EFI_GUID sal_system_table_guid = SAL_SYSTEM_TABLE_GUID;
+ static const EFI_GUID nii_table_guid = EFI_NETWORK_INTERFACE_IDENTIFIER_PROTOCOL;
+ EFI_SYSTEM_TABLE *systab;
+ unsigned i;
+ systab = efi_info.systab;
+ for(i = 0; i < systab->NumberOfTableEntries; i++) {
+ EFI_GUID *guid;
+ void *table = systab->ConfigurationTable[i].VendorTable;
+ guid = &systab->ConfigurationTable[i].VendorGuid;
+
+#if 0
+ printf("GUID: %x-%hx-%hx-%hhx-%hhx-%hhx-%hhx-%hhx-%hhx-%hhx-%hhx Table: %lx\n",
+ guid->Data1, guid->Data2, guid->Data3,
+ guid->Data4[0], guid->Data4[1], guid->Data4[2], guid->Data4[3],
+ guid->Data4[4], guid->Data4[5], guid->Data4[6], guid->Data4[7],
+ table);
+#endif
+
+ if (memcmp(guid, &mps_table_guid, 16) == 0) {
+ mps_table = table;
+ }
+ if (memcmp(guid, &acpi20_table_guid, 16) == 0) {
+ acpi20_table = table;
+ }
+ if (memcmp(guid, &smbios_table_guid, 16) == 0) {
+ smbios_table = table;
+ }
+ if (memcmp(guid, &sal_system_table_guid, 16) == 0) {
+ parse_sal_system_table(table);
+ }
+ if (memcmp(guid, &nii_table_guid, 16) == 0) {
+ nii_table = table;
+ }
+ }
+ }
+}
+
+void arch_on_exit(int status __unused)
+{
+ if (!boot_services)
+ return;
+ read_efi_mem_map(&efi_info.mem_map);
+ efi_free_memory(&efi_info.mem_map);
+}
+
+void arch_relocate_to(unsigned long addr)
+{
+ EFI_PHYSICAL_ADDRESS address, end;
+ UINTN pages;
+ EFI_STATUS status;
+
+ if (!boot_services)
+ return;
+
+ /* Find the efi pages where the new etherboot will sit */
+ address = addr & ~(EFI_PAGE_SIZE -1);
+ end = (addr + (_end - _text) + EFI_PAGE_SIZE -1) & ~EFI_PAGE_SIZE;
+ pages = (end - address)/EFI_PAGE_SIZE;
+
+ /* Reallocate the memory for the new copy of etherboot as LoaderCode */
+ status = efi_free_pages(address, pages);
+ if (status != EFI_SUCCESS) {
+ printf("efi_free_pages failed!: %lx\n", status);
+ return;
+ }
+ status = efi_allocate_pages(AllocateAddress, EfiLoaderCode, pages, &address);
+ if (status != EFI_SUCCESS) {
+ printf("efi_allocate_pages failed! %lx\n", status);
+ return;
+ }
+}
+
+
+struct meminfo meminfo;
+void get_memsizes(void)
+{
+ EFI_MEMORY_DESCRIPTOR *desc, *end;
+ struct efi_mem_map *map;
+#define next_desc(desc, size) ((EFI_MEMORY_DESCRIPTOR *)(((char *)(desc)) + (size)))
+
+ map = &efi_info.mem_map;
+ end = next_desc(map->map, map->map_size);
+
+ meminfo.map_count = 0;
+ for(desc = map->map; desc < end ; desc = next_desc(desc, map->descriptor_size)) {
+ uint64_t start, size, end;
+ unsigned long mem_k;
+
+ start = desc->PhysicalStart;
+ size = desc->NumberOfPages*EFI_PAGE_SIZE;
+ end = start + size;
+
+ if ((desc->Type != EfiLoaderCode) &&
+ (desc->Type != EfiLoaderData)) {
+ continue;
+ }
+
+ meminfo.map[meminfo.map_count].addr = start;
+ meminfo.map[meminfo.map_count].size = size;
+ meminfo.map[meminfo.map_count].type = E820_RAM;
+ meminfo.map_count++;
+
+ end >>= 10;
+ mem_k = end;
+ if (end & 0xFFFFFFFF00000000ULL) {
+ mem_k = 0xFFFFFFFF;
+ }
+ /* Set the base basememsize */
+ if ((mem_k <= 640) && (meminfo.basememsize <= mem_k)) {
+ meminfo.basememsize = mem_k;
+ }
+ /* Set the total memsize */
+ if ((mem_k >= 1024) && (meminfo.memsize <= (mem_k - 1024))) {
+ meminfo.memsize = mem_k - 1024;
+ }
+ if (meminfo.map_count == E820MAX)
+ break;
+ }
+#undef next_desc
+}
+
+
+EFI_NETWORK_INTERFACE_IDENTIFIER_INTERFACE *lookup_efi_nic(int index)
+{
+ static EFI_GUID protocol = EFI_NETWORK_INTERFACE_IDENTIFIER_PROTOCOL;
+ EFI_HANDLE handles[MAX_EFI_DEVICES];
+ EFI_STATUS status;
+ UINTN devices;
+ void *that;
+
+ if (!boot_services)
+ return 0;
+ if (index < 0) {
+ return 0;
+ }
+ devices = sizeof(handles);
+ status = efi_locate_handle(
+ ByProtocol, &protocol, 0, &devices, handles);
+ if (status != EFI_SUCCESS)
+ return 0;
+ devices /= sizeof(handles[0]);
+ if (index >= devices)
+ return 0;
+ status = efi_handle_protocol(handles[index], &protocol, &that);
+ if (status != EFI_SUCCESS)
+ return 0;
+ return that;
+}
+
+#if defined(CONSOLE_FIRMWARE)
+void console_putc(int c)
+{
+ CHAR16 str[2];
+ if (!conout)
+ return;
+ str[0] = c;
+ str[1] = 0;
+ __call(conout->OutputString, conout, str);
+}
+
+static int efi_have_key = 0;
+static int efi_key;
+int console_ischar(void)
+{
+ EFI_STATUS status;
+ EFI_INPUT_KEY new_key;
+ if (!conin)
+ return 0;
+ if (efi_have_key) {
+ return 1;
+ }
+ status = __call(conin->ReadKeyStroke, conin, &new_key);
+ if (status == EFI_SUCCESS) {
+ if ((new_key.UnicodeChar >= 0) && (new_key.UnicodeChar < 0x7f)) {
+ efi_have_key = 1;
+ efi_key = new_key.UnicodeChar;
+ }
+ else if (new_key.ScanCode == 0x17) {
+ efi_have_key = 1;
+ efi_key = K_ESC;
+ }
+ }
+ return efi_have_key;
+}
+
+int console_getc(void)
+{
+ if (efi_have_key) {
+ efi_have_key = 0;
+ }
+
+ return efi_key;
+}
+#endif /* CONSOLE_FIRMWARE */
+
+#define NAME "Etherboot"
+#define FIRMWARE "EFI"
+
+#define SZ(X) ((sizeof(X)+3) & ~3)
+#define CP(D,S) (memcpy(&(D), &(S), sizeof(S)))
+
+
+struct elf_notes {
+ /* CAREFUL this structure is carefully arranged to avoid
+ * alignment problems.
+ */
+ /* The note header */
+ struct Elf_Bhdr hdr;
+
+ /* First the Fixed sized entries that must be well aligned */
+
+ /* Insert a nop record so the next record is 64bit aligned */
+ Elf_Nhdr nf0;
+
+ /* Pointer to bootp data */
+ Elf_Nhdr nf1;
+ char nf1_name[SZ(EB_PARAM_NOTE)];
+ uint64_t nf1_bootp_data;
+
+ /* Pointer to ELF header */
+ Elf_Nhdr nf2;
+ char nf2_name[SZ(EB_PARAM_NOTE)];
+ uint64_t nf2_header;
+
+ /* The EFI systab pointer */
+ Elf_Nhdr nf3;
+ char nf3_name[SZ(EB_PARAM_NOTE)];
+ uint64_t nf3_systab;
+
+ /* The FPSWA pointer */
+ Elf_Nhdr nf4;
+ char nf4_name[SZ(EB_PARAM_NOTE)];
+ uint64_t nf4_fpswa;
+
+ /* The memory map */
+ Elf_Nhdr nf5;
+ char nf5_name[SZ(EB_PARAM_NOTE)];
+ struct efi_mem_map nf5_map;
+
+ /* The console info, silly but elilo passes it... */
+ Elf_Nhdr nf6;
+ char nf6_name[SZ(EB_PARAM_NOTE)];
+ struct console_info nf6_coninfo;
+
+ /* Then the variable sized data string data where alignment does not matter */
+
+ /* The bootloader name */
+ Elf_Nhdr nv1;
+ char nv1_desc[SZ(NAME)];
+ /* The bootloader version */
+ Elf_Nhdr nv2;
+ char nv2_desc[SZ(VERSION)];
+ /* The firmware type */
+ Elf_Nhdr nv3;
+ char nv3_desc[SZ(FIRMWARE)];
+ /* Name of the loaded image */
+ Elf_Nhdr nv4;
+ char nv4_loaded_image[128];
+ /* An empty command line */
+ Elf_Nhdr nv5;
+ char nv5_cmdline[SZ("")];
+};
+
+#define ELF_NOTE_COUNT (6+5)
+
+static struct elf_notes notes;
+struct Elf_Bhdr *prepare_boot_params(void *header)
+{
+ /* Shutdown the boot services */
+ if (boot_services) {
+ efi_get_coninfo(&efi_info.coninfo);
+ read_efi_mem_map(&efi_info.mem_map);
+ efi_exit_boot_services(&efi_info.mem_map);
+ }
+
+ memset(&notes, 0, sizeof(notes));
+ notes.hdr.b_signature = 0x0E1FB007;
+ notes.hdr.b_size = sizeof(notes);
+ notes.hdr.b_checksum = 0;
+ notes.hdr.b_records = ELF_NOTE_COUNT;
+
+ /* Initialize the fixed length entries. */
+
+ /* Align the fixed length entries to a 64bit boundary */
+ notes.nf0.n_namesz = 0;
+ notes.nf0.n_descsz = 0;
+ notes.nf0.n_type = EBN_NOP;
+
+ notes.nf1.n_namesz = sizeof(EB_PARAM_NOTE);
+ notes.nf1.n_descsz = sizeof(notes.nf1_bootp_data);
+ notes.nf1.n_type = EB_BOOTP_DATA;
+ CP(notes.nf1_name, EB_PARAM_NOTE);
+ notes.nf1_bootp_data = virt_to_phys(BOOTP_DATA_ADDR);
+
+ notes.nf2.n_namesz = sizeof(EB_PARAM_NOTE);
+ notes.nf2.n_descsz = sizeof(notes.nf2_header);
+ notes.nf2.n_type = EB_HEADER;
+ CP(notes.nf2_name, EB_PARAM_NOTE);
+ notes.nf2_header = virt_to_phys(header);
+
+ notes.nf3.n_namesz = sizeof(EB_PARAM_NOTE);
+ notes.nf3.n_descsz = sizeof(notes.nf3_systab);
+ notes.nf3.n_type = EB_IA64_SYSTAB;
+ CP(notes.nf3_name, EB_PARAM_NOTE);
+ notes.nf3_systab = (unsigned long)efi_info.systab;
+
+ notes.nf4.n_namesz = sizeof(EB_PARAM_NOTE);
+ notes.nf4.n_descsz = sizeof(notes.nf4_fpswa);
+ notes.nf4.n_type = EB_IA64_FPSWA;
+ CP(notes.nf4_name, EB_PARAM_NOTE);
+ notes.nf4_fpswa = (unsigned long)efi_info.fpswa;
+
+ notes.nf5.n_namesz = sizeof(EB_PARAM_NOTE);
+ notes.nf5.n_descsz = sizeof(notes.nf5_map);
+ notes.nf5.n_type = EB_IA64_MEMMAP;
+ CP(notes.nf5_name, EB_PARAM_NOTE);
+ notes.nf5_map = efi_info.mem_map;
+
+ notes.nf6.n_namesz = sizeof(EB_PARAM_NOTE);
+ notes.nf6.n_descsz = sizeof(notes.nf6_coninfo);
+ notes.nf6.n_type = EB_IA64_CONINFO;
+ CP(notes.nf6_name, EB_PARAM_NOTE);
+ notes.nf6_coninfo = efi_info.coninfo;
+
+ /* Initialize the variable length entries */
+ notes.nv1.n_namesz = 0;
+ notes.nv1.n_descsz = sizeof(NAME);
+ notes.nv1.n_type = EBN_BOOTLOADER_NAME;
+ CP(notes.nv1_desc, NAME);
+
+ notes.nv2.n_namesz = 0;
+ notes.nv2.n_descsz = sizeof(VERSION);
+ notes.nv2.n_type = EBN_BOOTLOADER_VERSION;
+ CP(notes.nv2_desc, VERSION);
+
+ notes.nv3.n_namesz = 0;
+ notes.nv3.n_descsz = sizeof(FIRMWARE);
+ notes.nv3.n_type = EBN_FIRMWARE_TYPE;
+ CP(notes.nv3_desc, FIRMWARE);
+
+ /* Attempt to pass the name of the loaded image */
+ notes.nv4.n_namesz = 0;
+ notes.nv4.n_descsz = sizeof(notes.nv4_loaded_image);
+ notes.nv4.n_type = EBN_LOADED_IMAGE;
+ memcpy(&notes.nv4_loaded_image, KERNEL_BUF, sizeof(notes.nv4_loaded_image));
+
+ /* Pass an empty command line for now */
+ notes.nv5.n_namesz = 0;
+ notes.nv5.n_descsz = sizeof("");
+ notes.nv5.n_type = EBN_COMMAND_LINE;
+ CP(notes.nv5_cmdline, "");
+
+ notes.hdr.b_checksum = ipchksum(&notes, sizeof(notes));
+ /* Like UDP invert a 0 checksum to show that a checksum is present */
+ if (notes.hdr.b_checksum == 0) {
+ notes.hdr.b_checksum = 0xffff;
+ }
+
+ return &notes.hdr;
+}
+
+int elf_start(unsigned long machine __unused, unsigned long entry, unsigned long params)
+{
+ struct elf_notes *notes;
+ int result;
+ /* Since we can do both be polite and also pass the linux
+ * ia64_boot_param table.
+ */
+ static struct ia64_boot_param {
+ uint64_t command_line; /* physical address of command line arguments */
+ uint64_t efi_systab; /* physical address of EFI system table */
+ uint64_t efi_memmap; /* physical address of EFI memory map */
+ uint64_t efi_memmap_size; /* size of EFI memory map */
+ uint64_t efi_memdesc_size; /* size of an EFI memory map descriptor */
+ uint32_t efi_memdesc_version; /* memory descriptor version */
+ struct {
+ uint16_t num_cols; /* number of columns on console output device */
+ uint16_t num_rows; /* number of rows on console output device */
+ uint16_t orig_x; /* cursor's x position */
+ uint16_t orig_y; /* cursor's y position */
+ } console_info;
+ uint64_t fpswa; /* physical address of the fpswa interface */
+ uint64_t initrd_start;
+ uint64_t initrd_size;
+ } bp;
+
+ notes = phys_to_virt(params);
+ /* If I don't have notes don't attempt to start the image */
+ if (notes == 0) {
+ return -2;
+ }
+
+ bp.command_line = (unsigned long)&notes->nv5_cmdline;
+ bp.efi_systab = notes->nf3_systab;
+ bp.efi_memmap = (unsigned long)&notes->nf5_map.map;
+ bp.efi_memmap_size = notes->nf5_map.map_size;
+ bp.efi_memdesc_size = notes->nf5_map.descriptor_size;
+ bp.efi_memdesc_version = notes->nf5_map.descriptor_version;
+ bp.console_info.num_cols = notes->nf6_coninfo.num_cols;
+ bp.console_info.num_rows = notes->nf6_coninfo.num_rows;
+ bp.console_info.orig_x = notes->nf6_coninfo.orig_x;
+ bp.console_info.orig_y = notes->nf6_coninfo.orig_y;
+ bp.fpswa = notes->nf4_fpswa;
+ bp.initrd_start = 0;
+ bp.initrd_size = 0;
+
+
+ asm volatile(
+ ";;\n\t"
+ "mov r28=%2\n\t"
+ "mov out0=%3\n\t"
+ "br.call.sptk.few rp=%1\n\t"
+ "mov %0=r8\n\t"
+ : "=r" (result)
+ : "b"(entry), "r"(&bp),"r"(params)
+ );
+ return result;
+}
diff --git a/src/arch/ia64/core/etherboot.lds b/src/arch/ia64/core/etherboot.lds
new file mode 100644
index 00000000..216cce92
--- /dev/null
+++ b/src/arch/ia64/core/etherboot.lds
@@ -0,0 +1,82 @@
+OUTPUT_FORMAT("elf64-ia64-little")
+
+OUTPUT_ARCH(ia64)
+
+ENTRY(_start)
+SECTIONS {
+ . = 0;
+ __gp = . + 0x200000;
+ _virt_start = .;
+ _text = . ;
+ .text : {
+ /* Start address of etherboot in the virtual address space */
+ *(.text)
+ *(.text.*)
+ _etext = . ;
+
+ _rodata = . ;
+ . = ALIGN(16);
+ *(.rodata)
+ *(.rodata.*)
+ *(.srodata)
+ . = ALIGN(16);
+ pci_drivers = . ;
+ *(.drivers.pci);
+ pci_drivers_end = . ;
+ . = ALIGN(16);
+ isa_drivers = . ;
+ *(.drivers.isa);
+ isa_drivers_end = . ;
+ . = ALIGN(16);
+
+ . = ALIGN(16);
+ _rela = . ;
+ *(.rela.text)
+ *(.rela.rodata)
+ *(.rela.drivers.pci)
+ *(.rela.drivers.isa)
+ *(.rela.drivers.efi)
+ *(.rela.data)
+ *(.rela.sdata)
+ *(.rela.got)
+ _erela = . ;
+ . = ALIGN(16);
+ _erodata = . ;
+ }
+ _rela_size = _erela - _rela ;
+ .data : {
+ _data = . ;
+ *(.data)
+ *(.got.plt)
+ *(.got)
+ *(.sdata)
+ *(.sbss)
+ *(.scommon)
+ *(.data.*)
+ *(.data1)
+ . = ALIGN(16);
+ _edata = . ;
+ }
+ _bss = . ;
+ .bss : {
+ *(.sbss)
+ *(.scommon)
+ *(.dynbss)
+ *(.bss)
+ *(COMMON)
+ }
+ _ebss = .;
+ _end = .;
+ /DISCARD/ : {
+ *(.comment)
+ *(.note)
+ *(.hash)
+ *(.dynstr)
+ *(.dynsym)
+ *(.IA_64.unwind)
+ *(.IA_64.unwind_info)
+ *(.IA64_unwind)
+ *(.IA64_unwind_info)
+ *(.dynamic)
+ }
+}
diff --git a/src/arch/ia64/core/ia64_timer.c b/src/arch/ia64/core/ia64_timer.c
new file mode 100644
index 00000000..3115714a
--- /dev/null
+++ b/src/arch/ia64/core/ia64_timer.c
@@ -0,0 +1,89 @@
+#include "etherboot.h"
+#include "timer.h"
+#include "sal.h"
+#include "pal.h"
+
+static inline unsigned long get_cycles(void)
+{
+ unsigned long result;
+ __asm__ __volatile__(";;mov %0=ar.itc;;" : "=r"(result));
+ return result;
+}
+
+/* ------ Calibrate the TSC -------
+ * Time how long it takes to excute a loop that runs in known time.
+ * And find the convertion needed to get to CLOCK_TICK_RATE
+ */
+
+static unsigned long calibrate_cycles(void)
+{
+ unsigned long platform_ticks_per_second, drift_info;
+ struct pal_freq_ratio itc_ratio;
+ long result;
+ result = sal_freq_base(SAL_FREQ_BASE_PLATFORM, &platform_ticks_per_second, &drift_info);
+ if (result != 0) {
+ printf("sal_freq_base failed: %lx\n",result);
+ exit(1);
+ } else {
+ result = pal_freq_ratios(0,0,&itc_ratio);
+ if (result != 0) {
+ printf("pal_freq_ratios failed: %lx\n", result);
+ exit(1);
+ }
+ }
+ /* Avoid division by zero */
+ if (itc_ratio.den == 0)
+ itc_ratio.den = 1;
+
+ return (platform_ticks_per_second *itc_ratio.num)/(itc_ratio.den*TICKS_PER_SEC);
+}
+
+static unsigned long clocks_per_tick;
+void setup_timers(void)
+{
+ if (!clocks_per_tick) {
+ clocks_per_tick = calibrate_cycles();
+ /* Display the CPU Mhz to easily test if the calibration was bad */
+ printf("ITC %ld Mhz\n", (clocks_per_tick/1000 * TICKS_PER_SEC)/1000);
+ }
+}
+
+unsigned long currticks(void)
+{
+ return get_cycles()/clocks_per_tick;
+}
+
+static unsigned long timer_timeout;
+static int __timer_running(void)
+{
+ return get_cycles() < timer_timeout;
+}
+
+void udelay(unsigned int usecs)
+{
+ unsigned long now;
+ now = get_cycles();
+ timer_timeout = now + usecs * ((clocks_per_tick * TICKS_PER_SEC)/(1000*1000));
+ while(__timer_running());
+}
+void ndelay(unsigned int nsecs)
+{
+ unsigned long now;
+ now = get_cycles();
+ timer_timeout = now + nsecs * ((clocks_per_tick * TICKS_PER_SEC)/(1000*1000*1000));
+ while(__timer_running());
+}
+
+void load_timer2(unsigned int timer2_ticks)
+{
+ unsigned long now;
+ unsigned long clocks;
+ now = get_cycles();
+ clocks = timer2_ticks * ((clocks_per_tick * TICKS_PER_SEC)/CLOCK_TICK_RATE);
+ timer_timeout = now + clocks;
+}
+
+int timer2_running(void)
+{
+ return __timer_running();
+}
diff --git a/src/arch/ia64/core/idiv32.S b/src/arch/ia64/core/idiv32.S
new file mode 100644
index 00000000..283eff0e
--- /dev/null
+++ b/src/arch/ia64/core/idiv32.S
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2000 Hewlett-Packard Co
+ * Copyright (C) 2000 David Mosberger-Tang <davidm@hpl.hp.com>
+ *
+ * 32-bit integer division.
+ *
+ * This code is based on the application note entitled "Divide, Square Root
+ * and Remainder Algorithms for the IA-64 Architecture". This document
+ * is available as Intel document number 248725-002 or via the web at
+ * http://developer.intel.com/software/opensource/numerics/
+ *
+ * For more details on the theory behind these algorithms, see "IA-64
+ * and Elementary Functions" by Peter Markstein; HP Professional Books
+ * (http://www.hp.com/go/retailbooks/)
+ */
+
+#ifdef MODULO
+# define OP mod
+#else
+# define OP div
+#endif
+
+#ifdef UNSIGNED
+# define SGN u
+# define EXTEND zxt4
+# define INT_TO_FP(a,b) fcvt.xuf.s1 a=b
+# define FP_TO_INT(a,b) fcvt.fxu.trunc.s1 a=b
+#else
+# define SGN
+# define EXTEND sxt4
+# define INT_TO_FP(a,b) fcvt.xf a=b
+# define FP_TO_INT(a,b) fcvt.fx.trunc.s1 a=b
+#endif
+
+#define PASTE1(a,b) a##b
+#define PASTE(a,b) PASTE1(a,b)
+#define NAME PASTE(PASTE(__,SGN),PASTE(OP,si3))
+
+ .text
+ .global NAME
+ .proc NAME
+NAME :
+ .regstk 2,0,0,0
+ // Transfer inputs to FP registers.
+ mov r2 = 0xffdd // r2 = -34 + 65535 (fp reg format bias)
+ EXTEND in0 = in0 // in0 = a
+ EXTEND in1 = in1 // in1 = b
+ ;;
+ setf.sig f8 = in0
+ setf.sig f9 = in1
+#ifdef MODULO
+ sub in1 = r0, in1 // in1 = -b
+#endif
+ ;;
+ // Convert the inputs to FP, to avoid FP software-assist faults.
+ INT_TO_FP(f8, f8)
+ INT_TO_FP(f9, f9)
+ ;;
+ setf.exp f7 = r2 // f7 = 2^-34
+ frcpa.s1 f6, p6 = f8, f9 // y0 = frcpa(b)
+ ;;
+(p6) fmpy.s1 f8 = f8, f6 // q0 = a*y0
+(p6) fnma.s1 f6 = f9, f6, f1 // e0 = -b*y0 + 1
+ ;;
+#ifdef MODULO
+ setf.sig f9 = in1 // f9 = -b
+#endif
+(p6) fma.s1 f8 = f6, f8, f8 // q1 = e0*q0 + q0
+(p6) fma.s1 f6 = f6, f6, f7 // e1 = e0*e0 + 2^-34
+ ;;
+#ifdef MODULO
+ setf.sig f7 = in0
+#endif
+(p6) fma.s1 f6 = f6, f8, f8 // q2 = e1*q1 + q1
+ ;;
+ FP_TO_INT(f6, f6) // q = trunc(q2)
+ ;;
+#ifdef MODULO
+ xma.l f6 = f6, f9, f7 // r = q*(-b) + a
+ ;;
+#endif
+ getf.sig r8 = f6 // transfer result to result register
+ br.ret.sptk.many rp
+
+ .size NAME, . - NAME
+ .endp NAME
diff --git a/src/arch/ia64/core/idiv64.S b/src/arch/ia64/core/idiv64.S
new file mode 100644
index 00000000..d989a2f5
--- /dev/null
+++ b/src/arch/ia64/core/idiv64.S
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 1999-2000, 2002 Hewlett-Packard Co
+ * Copyright (C) 1999-2000 David Mosberger-Tang <davidm@hpl.hp.com>
+ *
+ * 64-bit integer division.
+ *
+ * This code is based on the application note entitled "Divide, Square Root
+ * and Remainder Algorithms for the IA-64 Architecture". This document
+ * is available as Intel document number 248725-002 or via the web at
+ * http://developer.intel.com/software/opensource/numerics/
+ *
+ * For more details on the theory behind these algorithms, see "IA-64
+ * and Elementary Functions" by Peter Markstein; HP Professional Books
+ * (http://www.hp.com/go/retailbooks/)
+ */
+
+#ifdef MODULO
+# define OP mod
+#else
+# define OP div
+#endif
+
+#ifdef UNSIGNED
+# define SGN u
+# define INT_TO_FP(a,b) fcvt.xuf.s1 a=b
+# define FP_TO_INT(a,b) fcvt.fxu.trunc.s1 a=b
+#else
+# define SGN
+# define INT_TO_FP(a,b) fcvt.xf a=b
+# define FP_TO_INT(a,b) fcvt.fx.trunc.s1 a=b
+#endif
+
+#define PASTE1(a,b) a##b
+#define PASTE(a,b) PASTE1(a,b)
+#define NAME PASTE(PASTE(__,SGN),PASTE(OP,di3))
+
+ .text
+ .global NAME
+ .proc NAME
+NAME :
+ .prologue
+ .regstk 2,0,0,0
+ // Transfer inputs to FP registers.
+ setf.sig f8 = in0
+ setf.sig f9 = in1
+ ;;
+ .fframe 16
+ .save.f 0x20
+ stf.spill [sp] = f17,-16
+
+ // Convert the inputs to FP, to avoid FP software-assist faults.
+ INT_TO_FP(f8, f8)
+ ;;
+
+ .save.f 0x10
+ stf.spill [sp] = f16
+ .body
+ INT_TO_FP(f9, f9)
+ ;;
+ frcpa.s1 f17, p6 = f8, f9 // y0 = frcpa(b)
+ ;;
+(p6) fmpy.s1 f7 = f8, f17 // q0 = a*y0
+(p6) fnma.s1 f6 = f9, f17, f1 // e0 = -b*y0 + 1
+ ;;
+(p6) fma.s1 f16 = f7, f6, f7 // q1 = q0*e0 + q0
+(p6) fmpy.s1 f7 = f6, f6 // e1 = e0*e0
+ ;;
+#ifdef MODULO
+ sub in1 = r0, in1 // in1 = -b
+#endif
+(p6) fma.s1 f16 = f16, f7, f16 // q2 = q1*e1 + q1
+(p6) fma.s1 f6 = f17, f6, f17 // y1 = y0*e0 + y0
+ ;;
+(p6) fma.s1 f6 = f6, f7, f6 // y2 = y1*e1 + y1
+(p6) fnma.s1 f7 = f9, f16, f8 // r = -b*q2 + a
+ ;;
+#ifdef MODULO
+ setf.sig f8 = in0 // f8 = a
+ setf.sig f9 = in1 // f9 = -b
+#endif
+(p6) fma.s1 f17 = f7, f6, f16 // q3 = r*y2 + q2
+ ;;
+ .restore sp
+ ldf.fill f16 = [sp], 16
+ FP_TO_INT(f17, f17) // q = trunc(q3)
+ ;;
+#ifdef MODULO
+ xma.l f17 = f17, f9, f8 // r = q*(-b) + a
+ ;;
+#endif
+ getf.sig r8 = f17 // transfer result to result register
+ ldf.fill f17 = [sp]
+ br.ret.sptk.many rp
+
+ .size NAME, . - NAME
+ .endp NAME
diff --git a/src/arch/ia64/core/longjmp.S b/src/arch/ia64/core/longjmp.S
new file mode 100644
index 00000000..d5c8e5ab
--- /dev/null
+++ b/src/arch/ia64/core/longjmp.S
@@ -0,0 +1,163 @@
+/* Copyright (C) 1999, 2000, 2001 Free Software Foundation, Inc.
+ Contributed by David Mosberger-Tang <davidm@hpl.hp.com>.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ The GNU C Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the GNU C Library; if not, write to the Free
+ Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ 02111-1307 USA.
+
+ Note that __sigsetjmp() did NOT flush the register stack. Instead,
+ we do it here since __longjmp() is usually much less frequently
+ invoked than __sigsetjmp(). The only difficulty is that __sigsetjmp()
+ didn't (and wouldn't be able to) save ar.rnat either. This is a problem
+ because if we're not careful, we could end up loading random NaT bits.
+ There are two cases:
+
+ (i) ar.bsp < ia64_rse_rnat_addr(jmpbuf.ar_bsp)
+ ar.rnat contains the desired bits---preserve ar.rnat
+ across loadrs and write to ar.bspstore
+
+ (ii) ar.bsp >= ia64_rse_rnat_addr(jmpbuf.ar_bsp)
+ The desired ar.rnat is stored in
+ ia64_rse_rnat_addr(jmpbuf.ar_bsp). Load those
+ bits into ar.rnat after setting ar.bspstore. */
+
+
+# define pPos p6 /* is rotate count positive? */
+# define pNeg p7 /* is rotate count negative? */
+
+
+ /* longjmp(__jmp_buf buf, int val) */
+
+ .text
+ .global longjmp
+ .proc longjmp
+longjmp:
+
+ alloc r8=ar.pfs,2,1,0,0
+ mov r27=ar.rsc
+ add r2=0x98,in0 // r2 <- &jmpbuf.orig_jmp_buf_addr
+ ;;
+ ld8 r8=[r2],-16 // r8 <- orig_jmp_buf_addr
+ mov r10=ar.bsp
+ and r11=~0x3,r27 // clear ar.rsc.mode
+ ;;
+ flushrs // flush dirty regs to backing store (must be first in insn grp)
+ ld8 r23=[r2],8 // r23 <- jmpbuf.ar_bsp
+ sub r8=r8,in0 // r8 <- &orig_jmpbuf - &jmpbuf
+ ;;
+ ld8 r25=[r2] // r25 <- jmpbuf.ar_unat
+ extr.u r8=r8,3,6 // r8 <- (&orig_jmpbuf - &jmpbuf)/8 & 0x3f
+ ;;
+ cmp.lt pNeg,pPos=r8,r0
+ mov r2=in0
+ ;;
+(pPos) mov r16=r8
+(pNeg) add r16=64,r8
+(pPos) sub r17=64,r8
+(pNeg) sub r17=r0,r8
+ ;;
+ mov ar.rsc=r11 // put RSE in enforced lazy mode
+ shr.u r8=r25,r16
+ add r3=8,in0 // r3 <- &jmpbuf.r1
+ shl r9=r25,r17
+ ;;
+ or r25=r8,r9
+ ;;
+ mov r26=ar.rnat
+ mov ar.unat=r25 // setup ar.unat (NaT bits for r1, r4-r7, and r12)
+ ;;
+ ld8.fill.nta sp=[r2],16 // r12 (sp)
+ ld8.fill.nta gp=[r3],16 // r1 (gp)
+ dep r11=-1,r23,3,6 // r11 <- ia64_rse_rnat_addr(jmpbuf.ar_bsp)
+ ;;
+ ld8.nta r16=[r2],16 // caller's unat
+ ld8.nta r17=[r3],16 // fpsr
+ ;;
+ ld8.fill.nta r4=[r2],16 // r4
+ ld8.fill.nta r5=[r3],16 // r5 (gp)
+ cmp.geu p8,p0=r10,r11 // p8 <- (ar.bsp >= jmpbuf.ar_bsp)
+ ;;
+ ld8.fill.nta r6=[r2],16 // r6
+ ld8.fill.nta r7=[r3],16 // r7
+ ;;
+ mov ar.unat=r16 // restore caller's unat
+ mov ar.fpsr=r17 // restore fpsr
+ ;;
+ ld8.nta r16=[r2],16 // b0
+ ld8.nta r17=[r3],16 // b1
+ ;;
+(p8) ld8 r26=[r11] // r26 <- *ia64_rse_rnat_addr(jmpbuf.ar_bsp)
+ mov ar.bspstore=r23 // restore ar.bspstore
+ ;;
+ ld8.nta r18=[r2],16 // b2
+ ld8.nta r19=[r3],16 // b3
+ ;;
+ ld8.nta r20=[r2],16 // b4
+ ld8.nta r21=[r3],16 // b5
+ ;;
+ ld8.nta r11=[r2],16 // ar.pfs
+ ld8.nta r22=[r3],56 // ar.lc
+ ;;
+ ld8.nta r24=[r2],32 // pr
+ mov b0=r16
+ ;;
+ ldf.fill.nta f2=[r2],32
+ ldf.fill.nta f3=[r3],32
+ mov b1=r17
+ ;;
+ ldf.fill.nta f4=[r2],32
+ ldf.fill.nta f5=[r3],32
+ mov b2=r18
+ ;;
+ ldf.fill.nta f16=[r2],32
+ ldf.fill.nta f17=[r3],32
+ mov b3=r19
+ ;;
+ ldf.fill.nta f18=[r2],32
+ ldf.fill.nta f19=[r3],32
+ mov b4=r20
+ ;;
+ ldf.fill.nta f20=[r2],32
+ ldf.fill.nta f21=[r3],32
+ mov b5=r21
+ ;;
+ ldf.fill.nta f22=[r2],32
+ ldf.fill.nta f23=[r3],32
+ mov ar.lc=r22
+ ;;
+ ldf.fill.nta f24=[r2],32
+ ldf.fill.nta f25=[r3],32
+ cmp.eq p8,p9=0,in1
+ ;;
+ ldf.fill.nta f26=[r2],32
+ ldf.fill.nta f27=[r3],32
+ mov ar.pfs=r11
+ ;;
+ ldf.fill.nta f28=[r2],32
+ ldf.fill.nta f29=[r3],32
+ ;;
+ ldf.fill.nta f30=[r2]
+ ldf.fill.nta f31=[r3]
+(p8) mov r8=1
+
+ mov ar.rnat=r26 // restore ar.rnat
+ ;;
+ mov ar.rsc=r27 // restore ar.rsc
+(p9) mov r8=in1
+
+ invala // virt. -> phys. regnum mapping may change
+ mov pr=r24,-1
+ br.ret.sptk.few b0
+ .size longjmp, . - longjmp
+ .endp longjmp
diff --git a/src/arch/ia64/core/memmove.S b/src/arch/ia64/core/memmove.S
new file mode 100644
index 00000000..63e093d9
--- /dev/null
+++ b/src/arch/ia64/core/memmove.S
@@ -0,0 +1,244 @@
+/* Optimized version of the standard memmove() function.
+ This file is part of the GNU C Library.
+ Copyright (C) 2000, 2001 Free Software Foundation, Inc.
+ Contributed by Dan Pop <Dan.Pop@cern.ch>.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ The GNU C Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the GNU C Library; if not, write to the Free
+ Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ 02111-1307 USA. */
+
+/* Return: dest
+
+ Inputs:
+ in0: dest
+ in1: src
+ in2: byte count
+
+ The core of the function is the memcpy implementation used in memcpy.S.
+ When bytes have to be copied backwards, only the easy case, when
+ all arguments are multiples of 8, is optimised.
+
+ In this form, it assumes little endian mode. For big endian mode,
+ sh1 must be computed using an extra instruction: sub sh1 = 64, sh1
+ or the UM.be bit should be cleared at the beginning and set at the end. */
+
+
+#define OP_T_THRES 16
+#define OPSIZ 8
+
+#define adest r15
+#define saved_pr r17
+#define saved_lc r18
+#define dest r19
+#define src r20
+#define len r21
+#define asrc r22
+#define tmp2 r23
+#define tmp3 r24
+#define tmp4 r25
+#define ptable r26
+#define ploop56 r27
+#define loopaddr r28
+#define sh1 r29
+#define loopcnt r30
+#define value r31
+
+#define LOOP(shift) \
+ .align 32 ; \
+.loop##shift##: \
+(p[0]) ld8 r[0] = [asrc], 8 ; /* w1 */ \
+(p[MEMLAT+1]) st8 [dest] = value, 8 ; \
+(p[MEMLAT]) shrp value = r[MEMLAT], r[MEMLAT+1], shift ; \
+ nop.b 0 ; \
+ nop.b 0 ; \
+ br.ctop.sptk .loop##shift ; \
+ br.cond.sptk .cpyfew ; /* deal with the remaining bytes */
+
+#define MEMLAT 21
+#define Nrot (((2*MEMLAT+3) + 7) & ~7)
+
+ .text
+ .global memmove, memcpy
+ .proc memove
+memcpy:
+memmove:
+ .prologue
+ alloc r2 = ar.pfs, 3, Nrot - 3, 0, Nrot
+ .rotr r[MEMLAT + 2], q[MEMLAT + 1]
+ .rotp p[MEMLAT + 2]
+ mov ret0 = in0 // return value = dest
+ .save pr, saved_pr
+ mov saved_pr = pr // save the predicate registers
+ .save ar.lc, saved_lc
+ mov saved_lc = ar.lc // save the loop counter
+ .body
+ or tmp3 = in0, in1 ;; // tmp3 = dest | src
+ or tmp3 = tmp3, in2 // tmp3 = dest | src | len
+ mov dest = in0 // dest
+ mov src = in1 // src
+ mov len = in2 // len
+ sub tmp2 = r0, in0 // tmp2 = -dest
+ cmp.eq p6, p0 = in2, r0 // if (len == 0)
+(p6) br.cond.spnt .restore_and_exit;;// return dest;
+ and tmp4 = 7, tmp3 // tmp4 = (dest | src | len) & 7
+ cmp.le p6, p0 = dest, src // if dest <= src it's always safe
+(p6) br.cond.spnt .forward // to copy forward
+ add tmp3 = src, len;;
+ cmp.lt p6, p0 = dest, tmp3 // if dest > src && dest < src + len
+(p6) br.cond.spnt .backward // we have to copy backward
+
+.forward:
+ shr.u loopcnt = len, 4 ;; // loopcnt = len / 16
+ cmp.ne p6, p0 = tmp4, r0 // if ((dest | src | len) & 7 != 0)
+(p6) br.cond.sptk .next // goto next;
+
+// The optimal case, when dest, src and len are all multiples of 8
+
+ and tmp3 = 0xf, len
+ mov pr.rot = 1 << 16 // set rotating predicates
+ mov ar.ec = MEMLAT + 1 ;; // set the epilog counter
+ cmp.ne p6, p0 = tmp3, r0 // do we have to copy an extra word?
+ adds loopcnt = -1, loopcnt;; // --loopcnt
+(p6) ld8 value = [src], 8;;
+(p6) st8 [dest] = value, 8 // copy the "odd" word
+ mov ar.lc = loopcnt // set the loop counter
+ cmp.eq p6, p0 = 8, len
+(p6) br.cond.spnt .restore_and_exit;;// the one-word special case
+ adds adest = 8, dest // set adest one word ahead of dest
+ adds asrc = 8, src ;; // set asrc one word ahead of src
+ nop.b 0 // get the "golden" alignment for
+ nop.b 0 // the next loop
+.l0:
+(p[0]) ld8 r[0] = [src], 16
+(p[0]) ld8 q[0] = [asrc], 16
+(p[MEMLAT]) st8 [dest] = r[MEMLAT], 16
+(p[MEMLAT]) st8 [adest] = q[MEMLAT], 16
+ br.ctop.dptk .l0 ;;
+
+ mov pr = saved_pr, -1 // restore the predicate registers
+ mov ar.lc = saved_lc // restore the loop counter
+ br.ret.sptk.many b0
+.next:
+ cmp.ge p6, p0 = OP_T_THRES, len // is len <= OP_T_THRES
+ and loopcnt = 7, tmp2 // loopcnt = -dest % 8
+(p6) br.cond.spnt .cpyfew // copy byte by byte
+ ;;
+ cmp.eq p6, p0 = loopcnt, r0
+(p6) br.cond.sptk .dest_aligned
+ sub len = len, loopcnt // len -= -dest % 8
+ adds loopcnt = -1, loopcnt // --loopcnt
+ ;;
+ mov ar.lc = loopcnt
+.l1: // copy -dest % 8 bytes
+ ld1 value = [src], 1 // value = *src++
+ ;;
+ st1 [dest] = value, 1 // *dest++ = value
+ br.cloop.dptk .l1
+.dest_aligned:
+ and sh1 = 7, src // sh1 = src % 8
+ and tmp2 = -8, len // tmp2 = len & -OPSIZ
+ and asrc = -8, src // asrc = src & -OPSIZ -- align src
+ shr.u loopcnt = len, 3 // loopcnt = len / 8
+ and len = 7, len;; // len = len % 8
+ adds loopcnt = -1, loopcnt // --loopcnt
+ addl tmp4 = @ltoff(.table), gp
+ addl tmp3 = @ltoff(.loop56), gp
+ mov ar.ec = MEMLAT + 1 // set EC
+ mov pr.rot = 1 << 16;; // set rotating predicates
+ mov ar.lc = loopcnt // set LC
+ cmp.eq p6, p0 = sh1, r0 // is the src aligned?
+(p6) br.cond.sptk .src_aligned
+ add src = src, tmp2 // src += len & -OPSIZ
+ shl sh1 = sh1, 3 // sh1 = 8 * (src % 8)
+ ld8 ploop56 = [tmp3] // ploop56 = &loop56
+ ld8 ptable = [tmp4];; // ptable = &table
+ add tmp3 = ptable, sh1;; // tmp3 = &table + sh1
+ mov ar.ec = MEMLAT + 1 + 1 // one more pass needed
+ ld8 tmp4 = [tmp3];; // tmp4 = loop offset
+ sub loopaddr = ploop56,tmp4 // loopadd = &loop56 - loop offset
+ ld8 r[1] = [asrc], 8;; // w0
+ mov b6 = loopaddr;;
+ br b6 // jump to the appropriate loop
+
+ LOOP(8)
+ LOOP(16)
+ LOOP(24)
+ LOOP(32)
+ LOOP(40)
+ LOOP(48)
+ LOOP(56)
+
+.src_aligned:
+.l3:
+(p[0]) ld8 r[0] = [src], 8
+(p[MEMLAT]) st8 [dest] = r[MEMLAT], 8
+ br.ctop.dptk .l3
+.cpyfew:
+ cmp.eq p6, p0 = len, r0 // is len == 0 ?
+ adds len = -1, len // --len;
+(p6) br.cond.spnt .restore_and_exit ;;
+ mov ar.lc = len
+.l4:
+ ld1 value = [src], 1
+ ;;
+ st1 [dest] = value, 1
+ br.cloop.dptk .l4 ;;
+.restore_and_exit:
+ mov pr = saved_pr, -1 // restore the predicate registers
+ mov ar.lc = saved_lc // restore the loop counter
+ br.ret.sptk.many b0
+
+// In the case of a backward copy, optimise only the case when everything
+// is a multiple of 8, otherwise copy byte by byte. The backward copy is
+// used only when the blocks are overlapping and dest > src.
+
+.backward:
+ shr.u loopcnt = len, 3 // loopcnt = len / 8
+ add src = src, len // src points one byte past the end
+ add dest = dest, len ;; // dest points one byte past the end
+ mov ar.ec = MEMLAT + 1 // set the epilog counter
+ mov pr.rot = 1 << 16 // set rotating predicates
+ adds loopcnt = -1, loopcnt // --loopcnt
+ cmp.ne p6, p0 = tmp4, r0 // if ((dest | src | len) & 7 != 0)
+(p6) br.cond.sptk .bytecopy ;; // copy byte by byte backward
+ adds src = -8, src // src points to the last word
+ adds dest = -8, dest // dest points to the last word
+ mov ar.lc = loopcnt;; // set the loop counter
+.l5:
+(p[0]) ld8 r[0] = [src], -8
+(p[MEMLAT]) st8 [dest] = r[MEMLAT], -8
+ br.ctop.dptk .l5
+ br.cond.sptk .restore_and_exit
+.bytecopy:
+ adds src = -1, src // src points to the last byte
+ adds dest = -1, dest // dest points to the last byte
+ adds loopcnt = -1, len;; // loopcnt = len - 1
+ mov ar.lc = loopcnt;; // set the loop counter
+.l6:
+(p[0]) ld1 r[0] = [src], -1
+(p[MEMLAT]) st1 [dest] = r[MEMLAT], -1
+ br.ctop.dptk .l6
+ br.cond.sptk .restore_and_exit
+.table:
+ data8 0 // dummy entry
+ data8 .loop56 - .loop8
+ data8 .loop56 - .loop16
+ data8 .loop56 - .loop24
+ data8 .loop56 - .loop32
+ data8 .loop56 - .loop40
+ data8 .loop56 - .loop48
+ data8 .loop56 - .loop56
+
+ .size memmove, . - memove
+ .endp memmove
diff --git a/src/arch/ia64/core/memset.S b/src/arch/ia64/core/memset.S
new file mode 100644
index 00000000..a6cc40fc
--- /dev/null
+++ b/src/arch/ia64/core/memset.S
@@ -0,0 +1,133 @@
+/*
+ * Copyright (C) 1999-2002 Hewlett-Packard Co.
+ * Contributed by Stephane Eranian <eranian@hpl.hp.com>
+ *
+ * This file is part of the ELILO, the EFI Linux boot loader.
+ *
+ * ELILO is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * ELILO is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with ELILO; see the file COPYING. If not, write to the Free
+ * Software Foundation, 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ *
+ * Please check out the elilo.txt for complete documentation on how
+ * to use this program.
+ *
+ * This code is derived from the Linux/ia64 source code.
+ */
+
+/*
+ *
+ * Optimized version of the standard memset() function
+ *
+ * Return: none
+ *
+ * Inputs:
+ * in0: address of buffer
+ * in1: byte value to use for storing
+ * in2: length of the buffer
+ *
+ */
+
+// arguments
+//
+#define buf r32
+#define val r33
+#define len r34
+
+//
+// local registers
+//
+#define saved_pfs r14
+#define cnt r18
+#define buf2 r19
+#define saved_lc r20
+#define tmp r21
+ .text
+ .global memset
+ .proc memset
+memset:
+ .prologue
+ .save ar.pfs, saved_pfs
+ alloc saved_pfs=ar.pfs,3,0,0,0 // cnt is sink here
+ cmp.eq p8,p0=r0,len // check for zero length
+ .save ar.lc, saved_lc
+ mov saved_lc=ar.lc // preserve ar.lc (slow)
+ ;;
+
+ .body
+
+ adds tmp=-1,len // br.ctop is repeat/until
+ tbit.nz p6,p0=buf,0 // odd alignment
+(p8) br.ret.spnt.few rp
+
+ cmp.lt p7,p0=16,len // if len > 16 then long memset
+ mux1 val=val,@brcst // prepare value
+(p7) br.cond.dptk.few long_memset
+ ;;
+ mov ar.lc=tmp // initialize lc for small count
+ ;; // avoid RAW and WAW on ar.lc
+1: // worst case 15 cyles, avg 8 cycles
+ st1 [buf]=val,1
+ br.cloop.dptk.few 1b
+ ;; // avoid RAW on ar.lc
+ mov ar.lc=saved_lc
+ mov ar.pfs=saved_pfs
+ br.ret.sptk.few rp // end of short memset
+
+ // at this point we know we have more than 16 bytes to copy
+ // so we focus on alignment
+long_memset:
+(p6) st1 [buf]=val,1 // 1-byte aligned
+(p6) adds len=-1,len;; // sync because buf is modified
+ tbit.nz p6,p0=buf,1
+ ;;
+(p6) st2 [buf]=val,2 // 2-byte aligned
+(p6) adds len=-2,len;;
+ tbit.nz p6,p0=buf,2
+ ;;
+(p6) st4 [buf]=val,4 // 4-byte aligned
+(p6) adds len=-4,len;;
+ tbit.nz p6,p0=buf,3
+ ;;
+(p6) st8 [buf]=val,8 // 8-byte aligned
+(p6) adds len=-8,len;;
+ shr.u cnt=len,4 // number of 128-bit (2x64bit) words
+ ;;
+ cmp.eq p6,p0=r0,cnt
+ adds tmp=-1,cnt
+(p6) br.cond.dpnt.few .dotail // we have less than 16 bytes left
+ ;;
+ adds buf2=8,buf // setup second base pointer
+ mov ar.lc=tmp
+ ;;
+2: // 16bytes/iteration
+ st8 [buf]=val,16
+ st8 [buf2]=val,16
+ br.cloop.dptk.few 2b
+ ;;
+.dotail: // tail correction based on len only
+ tbit.nz p6,p0=len,3
+ ;;
+(p6) st8 [buf]=val,8 // at least 8 bytes
+ tbit.nz p6,p0=len,2
+ ;;
+(p6) st4 [buf]=val,4 // at least 4 bytes
+ tbit.nz p6,p0=len,1
+ ;;
+(p6) st2 [buf]=val,2 // at least 2 bytes
+ tbit.nz p6,p0=len,0
+ mov ar.lc=saved_lc
+ ;;
+(p6) st1 [buf]=val // only 1 byte left
+ br.ret.dptk.few rp
+ .endp memset
diff --git a/src/arch/ia64/core/pal.c b/src/arch/ia64/core/pal.c
new file mode 100644
index 00000000..cfb518d0
--- /dev/null
+++ b/src/arch/ia64/core/pal.c
@@ -0,0 +1,84 @@
+#include "etherboot.h"
+#include "sal.h"
+#include "pal.h"
+
+struct fptr pal_entry;
+/*
+ * Note that some of these calls use a static-register only calling
+ * convention which has nothing to do with the regular calling
+ * convention.
+ */
+#define PAL_CACHE_FLUSH 1 /* flush i/d cache */
+#define PAL_CACHE_INFO 2 /* get detailed i/d cache info */
+#define PAL_CACHE_INIT 3 /* initialize i/d cache */
+#define PAL_CACHE_SUMMARY 4 /* get summary of cache heirarchy */
+#define PAL_MEM_ATTRIB 5 /* list supported memory attributes */
+#define PAL_PTCE_INFO 6 /* purge TLB info */
+#define PAL_VM_INFO 7 /* return supported virtual memory features */
+#define PAL_VM_SUMMARY 8 /* return summary on supported vm features */
+#define PAL_BUS_GET_FEATURES 9 /* return processor bus interface features settings */
+#define PAL_BUS_SET_FEATURES 10 /* set processor bus features */
+#define PAL_DEBUG_INFO 11 /* get number of debug registers */
+#define PAL_FIXED_ADDR 12 /* get fixed component of processors's directed address */
+#define PAL_FREQ_BASE 13 /* base frequency of the platform */
+#define PAL_FREQ_RATIOS 14 /* ratio of processor, bus and ITC frequency */
+#define PAL_PERF_MON_INFO 15 /* return performance monitor info */
+#define PAL_PLATFORM_ADDR 16 /* set processor interrupt block and IO port space addr */
+#define PAL_PROC_GET_FEATURES 17 /* get configurable processor features & settings */
+#define PAL_PROC_SET_FEATURES 18 /* enable/disable configurable processor features */
+#define PAL_RSE_INFO 19 /* return rse information */
+#define PAL_VERSION 20 /* return version of PAL code */
+#define PAL_MC_CLEAR_LOG 21 /* clear all processor log info */
+#define PAL_MC_DRAIN 22 /* drain operations which could result in an MCA */
+#define PAL_MC_EXPECTED 23 /* set/reset expected MCA indicator */
+#define PAL_MC_DYNAMIC_STATE 24 /* get processor dynamic state */
+#define PAL_MC_ERROR_INFO 25 /* get processor MCA info and static state */
+#define PAL_MC_RESUME 26 /* Return to interrupted process */
+#define PAL_MC_REGISTER_MEM 27 /* Register memory for PAL to use during MCAs and inits */
+#define PAL_HALT 28 /* enter the low power HALT state */
+#define PAL_HALT_LIGHT 29 /* enter the low power light halt state*/
+#define PAL_COPY_INFO 30 /* returns info needed to relocate PAL */
+#define PAL_CACHE_LINE_INIT 31 /* init tags & data of cache line */
+#define PAL_PMI_ENTRYPOINT 32 /* register PMI memory entry points with the processor */
+#define PAL_ENTER_IA_32_ENV 33 /* enter IA-32 system environment */
+#define PAL_VM_PAGE_SIZE 34 /* return vm TC and page walker page sizes */
+
+#define PAL_MEM_FOR_TEST 37 /* get amount of memory needed for late processor test */
+#define PAL_CACHE_PROT_INFO 38 /* get i/d cache protection info */
+#define PAL_REGISTER_INFO 39 /* return AR and CR register information*/
+#define PAL_SHUTDOWN 40 /* enter processor shutdown state */
+#define PAL_PREFETCH_VISIBILITY 41
+
+#define PAL_COPY_PAL 256 /* relocate PAL procedures and PAL PMI */
+#define PAL_HALT_INFO 257 /* return the low power capabilities of processor */
+#define PAL_TEST_PROC 258 /* perform late processor self-test */
+#define PAL_CACHE_READ 259 /* read tag & data of cacheline for diagnostic testing */
+#define PAL_CACHE_WRITE 260 /* write tag & data of cacheline for diagnostic testing */
+#define PAL_VM_TR_READ 261 /* read contents of translation register */
+
+
+/*
+ * Get the ratios for processor frequency, bus frequency and interval timer to
+ * to base frequency of the platform
+ */
+long pal_freq_ratios(struct pal_freq_ratio *proc_ratio,
+ struct pal_freq_ratio *bus_ratio, struct pal_freq_ratio *itc_ratio)
+{
+ struct freq_ratios {
+ long status;
+ struct pal_freq_ratio proc_ratio;
+ struct pal_freq_ratio bus_ratio;
+ struct pal_freq_ratio itc_ratio;
+ };
+ struct freq_ratios result;
+ extern struct freq_ratios pal_call(unsigned long which, ...);
+ result = pal_call(PAL_FREQ_RATIOS, 0, 0, 0);
+ if (proc_ratio)
+ *proc_ratio = result.proc_ratio;
+ if (bus_ratio)
+ *bus_ratio = result.bus_ratio;
+ if (itc_ratio)
+ *itc_ratio = result.itc_ratio;
+ return result.status;
+
+}
diff --git a/src/arch/ia64/core/pci_io.c b/src/arch/ia64/core/pci_io.c
new file mode 100644
index 00000000..f8069bb9
--- /dev/null
+++ b/src/arch/ia64/core/pci_io.c
@@ -0,0 +1,62 @@
+#include "etherboot.h"
+#include "pci.h"
+#include "sal.h"
+
+int pcibios_read_config_byte(unsigned int bus, unsigned int devfn, unsigned int reg, uint8_t *rvalue)
+{
+ unsigned long value;
+ long result;
+ result = sal_pci_config_read(PCI_SAL_ADDRESS(0,bus, 0, devfn, reg), 1, &value);
+ *rvalue = value;
+ return result;
+}
+int pcibios_read_config_word(unsigned int bus, unsigned int devfn, unsigned int reg, uint16_t *rvalue)
+{
+ unsigned long value;
+ long result;
+ result = sal_pci_config_read(PCI_SAL_ADDRESS(0,bus, 0, devfn, reg), 2, &value);
+ *rvalue = value;
+ return result;
+}
+int pcibios_read_config_dword(unsigned int bus, unsigned int devfn, unsigned int reg, uint32_t *rvalue)
+{
+ unsigned long value;
+ long result;
+ result = sal_pci_config_read(PCI_SAL_ADDRESS(0,bus, 0, devfn, reg), 4, &value);
+ *rvalue = value;
+ return result;
+}
+
+int pcibios_write_config_byte(unsigned int bus, unsigned int devfn, unsigned int reg, uint8_t value)
+{
+ return sal_pci_config_write(PCI_SAL_ADDRESS(0,bus, 0, devfn, reg), 1, value);
+}
+
+int pcibios_write_config_word(unsigned int bus, unsigned int devfn, unsigned int reg, uint16_t value)
+{
+ return sal_pci_config_write(PCI_SAL_ADDRESS(0,bus, 0, devfn, reg), 2, value);
+}
+
+int pcibios_write_config_dword(unsigned int bus, unsigned int devfn, unsigned int reg, uint32_t value)
+{
+ return sal_pci_config_write(PCI_SAL_ADDRESS(0,bus, 0, devfn, reg), 4, value);
+}
+
+/* So far I have not see a non-zero PCI_BUS_OFFSET
+ * and an AML parser to get it much to much trouble.
+ */
+#ifndef PCI_BUS_OFFSET
+#define PCI_BUS_OFFSET 0
+#endif
+
+unsigned long pcibios_bus_base(unsigned int bus)
+{
+ return PCI_BUS_OFFSET;
+}
+
+void find_pci(int type, struct pci_device *dev)
+{
+ /* Should I check for sal functions being present? */
+ return scan_pci_bus(type, dev);
+}
+
diff --git a/src/arch/ia64/core/reloc.S b/src/arch/ia64/core/reloc.S
new file mode 100644
index 00000000..31689bfb
--- /dev/null
+++ b/src/arch/ia64/core/reloc.S
@@ -0,0 +1,133 @@
+/* reloc.S - position independent IA-64 ELF shared object relocator
+ Copyright (C) 1999 Hewlett-Packard Co.
+ Contributed by David Mosberger <davidm@hpl.hp.com>.
+ Copyright (C) 2002 Eric Biederman sponsored by Linux Networx
+
+ This file is part of etherboot.
+ This file was derived from reloc_ia64.S from GNU-EFI, the GNU EFI development environment.
+
+ GNU EFI is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ GNU EFI is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GNU EFI; see the file COPYING. If not, write to the Free
+ Software Foundation, 59 Temple Place - Suite 330, Boston, MA
+ 02111-1307, USA. */
+
+/*
+ * This is written in assembly because the entire code needs to be position
+ * independent. Note that the compiler does not generate code that's position
+ * independent by itself because it relies on the global offset table being
+ * relocated.
+ *
+ * This code assumes the code was compiled with -mconstant-gp -mauto-pic -static -shared
+ * Which generates position independent code, but not position indepedent data.
+ * This code assumes in the linker script the rela entries are bracked with:
+ * _rela, _erela and _rela_size gives the total size of the rela entries.
+ * This gives a much smaller binary than when compiled as a true shared object.
+ *
+ * This code assumes the original shared object was initially relocated,
+ * So that it only needs to apply changes for the new address the code is linked
+ * at.
+ */
+ .text
+ .psr abi64
+ .psr lsb
+ .lsb
+
+
+#define ST_VALUE_OFF 8 /* offset of st_value in elf sym */
+
+#define RET_SUCCESS 0
+#define RET_LOAD_ERROR 1
+
+#define R_IA64_NONE 0
+#define R_IA64_REL64MSB 0x6e
+#define R_IA64_REL64LSB 0x6f
+#define R_IA64_DIR64MSB 0x26
+#define R_IA64_DIR64LSB 0x27
+#define R_IA64_FPTR64MSB 0x46
+#define R_IA64_FPTR64LSB 0x47
+
+#define delta in0 /* Chaing in load address (address of .text) */
+
+#define ldbase r15
+#define target r16
+#define val r17
+#define rela r18
+#define relasz r19
+#define relaent r20
+#define addr r21
+#define r_info r22
+#define r_offset r23
+#define r_type r25
+
+#define Pmore p6
+
+#define Pnone p6
+#define Prel p7
+#define Pdir p8
+
+
+ .global _relocate
+_relocate:
+ alloc r2=ar.pfs,1,0,0,0
+ add rela=@gprel(_rela),gp
+ add ldbase=@gprel(_text),gp
+ add r3=@ltoff(_rela_size),gp
+ ;;
+ ld8 relasz = [r3]
+ mov relaent=24
+ br.sptk.few apply_relocs
+
+apply_loop:
+ ld8 r_offset = [rela]
+ add addr = 8,rela
+ sub relasz = relasz,relaent
+
+ ;;
+ ld8 r_info = [addr], 8
+ ;;
+ add target = ldbase, r_offset
+ add rela = rela,relaent
+ extr.u r_type = r_info, 0, 32
+ ;;
+ cmp.eq Pnone,p0 = R_IA64_NONE, r_type
+ cmp.eq Prel,p0 = R_IA64_REL64LSB, r_type
+ cmp.eq Pdir,p0 = R_IA64_DIR64LSB, r_type /* Needed? */
+ ;;
+(Pnone) br.cond.sptk.few apply_relocs
+(Prel) br.cond.sptk.few apply_REL64
+(Pdir) br.cond.sptk.few apply_DIR64 /* Needed? */
+ ;;
+
+apply_error:
+ mov r8 = RET_LOAD_ERROR
+ br.ret.sptk.few rp
+
+apply_REL64:
+apply_DIR64:
+ ld8 val = [target]
+ ;;
+ add val = val, delta
+ ;;
+ st8 [target] = val
+ ;;
+ /* fall through to apply_relocs */
+apply_relocs:
+ cmp.ltu Pmore,p0=0,relasz
+(Pmore) br.cond.sptk.few apply_loop
+ ;;
+
+ mov r8 = RET_SUCCESS
+ br.ret.sptk.few rp
+
+ .size _relocate, . - _relocate
+ .endp _relocate
diff --git a/src/arch/ia64/core/relocate_to.S b/src/arch/ia64/core/relocate_to.S
new file mode 100644
index 00000000..37b5c9f8
--- /dev/null
+++ b/src/arch/ia64/core/relocate_to.S
@@ -0,0 +1,92 @@
+ /* Temporarily ignore the stack, as I am still using efi's stack */
+
+#define newbase in0
+#define base loc2
+#define newgp loc3
+#define len loc4
+
+ .explicit
+ .globl relocate_to
+ .proc relocate_to
+relocate_to:
+ /* In incoming variable the new base addres of etherboot. */
+ alloc loc0=ar.pfs,1,5,3,0 /* in, local, out, rotating */
+ mov loc1=rp
+
+ /* Compute the current location of _text */
+ /* Compute the new gp value */
+ add base=@gprel(_text),gp
+ add len =@gprel(_end),gp
+ movl newgp=@gprel(_text)
+ ;;
+ sub newgp=newbase,newgp /* gp = _text - @gprel(_text) */
+ sub len=len,base
+ ;;
+
+ /* Copy etherboot to the new location */
+ mov out0=newbase
+ mov out1=base
+ mov out2=len
+ br.call.sptk.few rp=memcpy
+ ;;
+
+ /* Jump to my __relocate_to in the new location */
+ movl r14=@gprel(__relocate_to)
+ ;;
+ add r14=r14,newgp
+ ;;
+ mov b6=r14
+ ;;
+ br.cond.sptk.few b6
+ ;;
+__relocate_to:
+ /* I am at the new location set the newgp as the default */
+ mov gp=newgp
+ ;;
+ /* New apply relocations to the new copy */
+ sub out0=newbase,base
+ br.call.sptk.few rp=_relocate
+ ;;
+
+ /* Lookup restart_etherboot */
+ add out0=@gprel(restart_etherboot),gp
+ ;;
+
+ /* Adjust the gp and return address.
+ * NOTE: This only works when the setjmp can modify it's caller's gp
+ * address. Essentially this means etherboot must be compiled with
+ * compiled with -mconstant-gp, though an inline version of setjmp might work.
+ */
+ add r14=0x40,out0
+ add r16=0x08,out0
+ ;;
+ ld8 r15=[r14]
+ ld8 r17=[r16]
+ ;;
+ sub r15=r15,base
+ sub r17=r17,base
+ ;;
+ add r15=r15,newbase
+ add r17=r17,newbase
+ ;;
+ st8 [r14]=r15
+ st8 [r16]=r17
+ ;;
+ mov out1=256
+ br.call.sptk.few rp=longjmp
+
+ /* And just in case lonjmp returns... */
+
+
+ /* Adjust my return address and return */
+ sub loc1=loc1,base
+ ;;
+ add loc1=loc1,newbase
+ ;;
+ mov ar.pfs=loc0
+ mov rp=loc1
+ ;;
+ br.ret.sptk.few rp
+
+ .size relocate_to, . - relocate_to
+ .endp relocate_to
diff --git a/src/arch/ia64/core/sal.c b/src/arch/ia64/core/sal.c
new file mode 100644
index 00000000..aa040f80
--- /dev/null
+++ b/src/arch/ia64/core/sal.c
@@ -0,0 +1,278 @@
+#include "etherboot.h"
+#include "sal.h"
+
+struct sal_entry_base {
+ uint8_t entry_type;
+#define SAL_TYPE_ENTRYPOINT 0
+#define SAL_TYPE_MEMORY 1
+#define SAL_TYPE_PLATFORM_FEATURES 2
+#define SAL_TYPE_TRANSLATION_REGISTER 3
+#define SAL_TYPE_PURGE_DOMAIN 4
+#define SAL_TYPE_AP_WAKEUP 5
+};
+
+struct sal_entry_point_descriptor {
+ uint8_t entry_type;
+ uint8_t reserved[7];
+ uint64_t pal_proc;
+ uint64_t sal_proc;
+ uint64_t sal_gp;
+ uint8_t reserved2[16];
+};
+
+struct sal_memory_descriptor {
+ uint8_t entry_type;
+ uint8_t sal_needs_virt_mapping;
+ uint8_t mem_attr;
+#define MEM_ATTR_WB 0
+#define MEM_ATTR_UC 8
+#define MEM_ATTR_UCE 9
+#define MEM_ATTR_WC 10
+ uint8_t access_rights;
+ uint8_t mem_attr_support;
+#define MEM_ATTR_SUPPORTS_WB 1
+#define MEM_ATTR_SUPPORTS_UC 2
+#define MEM_ATTR_SUPPORTS_UCE 4
+#define MEM_ATTR_SUPPORTS_WC 8
+ uint8_t reserved;
+ uint8_t mem_type;
+#define MEM_TYPE_RAM 0
+#define MEM_TYPE_MIO 1
+#define MEM_TYPE_SAPIC 2
+#define MEM_TYPE_PIO 3
+#define MEM_TYPE_FIRMWARE 4
+#define MEM_TYPE_BAD_RAM 9
+#define MEM_TYPE_BLACK_HOLE 10
+ uint8_t mem_usage;
+#define MEM_USAGE_UNSPECIFIED 0
+#define MEM_USAGE_PAL_CODE 1
+#define MEM_USAGE_BOOT_SERVICES_CODE 2
+#define MEM_USAGE_BOOT_SERVICES_DATA 3
+#define MEM_USAGE_RUNTIME_SERVICES_CODE 4
+#define MEM_USAGE_RUNTIME_SERVICES_DATA 5
+#define MEM_USAGE_IA32_OPTION_ROM 6
+#define MEM_USAGE_IA32_SYSTEM_ROM 7
+#define MEM_USAGE_ACPI_RECLAIM_MEMORY 8
+#define MEM_USAGE_ACPI_NVS_MEMORY 9
+#define MEM_USAGE_SAL_PMI_CODE 10
+#define MEM_USAGE_SAL_PMI_DATA 11
+#define MEM_USAGE_FIRMWARE_RESERVED_RAM 12
+
+#define MEM_USAGE_CPU_TO_IO 0
+ uint64_t phys_address;
+ uint32_t pages; /* In 4k pages */
+ uint32_t reserved2;
+ uint8_t oem_reserved[8];
+};
+
+struct sal_platform_features {
+ uint8_t entry_type;
+ uint8_t feature_list;
+#define SAL_FEATURE_BUS_LOCK 1
+#define SAL_FEATURE_PLATFORM_REDIRECTION_HINT 2
+#define SAL_FEATURE_PROCESSOR_REDIRECTION_HINT 3
+ uint8_t reserved[14];
+};
+struct sal_translation_register {
+ uint8_t entry_type;
+ uint8_t tr_type;
+#define SAL_ITR 0
+#define SAL_DTR 1
+ uint8_t tr_number;
+ uint8_t reserved[5];
+ uint64_t virtual_address;
+ uint64_t page_size;
+ uint8_t reserved2[8];
+};
+
+struct sal_purge_translation_cache_coherency_domain {
+ uint8_t entry_type;
+ uint8_t reserved[3];
+ uint32_t coherence_domain_count;
+ uint64_t coherence_domain_addr;
+};
+
+struct sal_ap_wakeup_descriptor {
+ uint8_t entry_type;
+ uint8_t wakeup_mechanism;
+ uint8_t reserved[6];
+ uint64_t interrupt;
+};
+
+struct sal_entry {
+ union {
+ struct sal_entry_base base;
+ struct sal_entry_point_descriptor entry_point;
+ struct sal_memory_descriptor mem;
+ struct sal_platform_features features;
+ struct sal_translation_register tr;
+ struct sal_purge_translation_cache_coherency_domain purge;
+ struct sal_ap_wakeup_descriptor ap_wakeup;
+ };
+};
+
+struct sal_system_table {
+ uint8_t signature[4]; /* SST_ */
+ uint32_t table_length;
+
+ uint16_t sal_rev;
+ uint16_t entry_count;
+ uint8_t checksum;
+ uint8_t reserved1[7];
+ uint16_t sal_a_version;
+ uint16_t sal_b_version;
+
+ uint8_t oem_id[32];
+ uint8_t product_id[32];
+ uint8_t reserved2[8];
+ struct sal_entry entry[0];
+};
+
+static struct sal_system_table *sal;
+struct fptr sal_entry;
+
+int parse_sal_system_table(void *table)
+{
+ struct sal_system_table *salp = table;
+ uint8_t *ptr;
+ uint8_t checksum;
+ struct sal_entry *entry;
+ unsigned i;
+ if (memcmp(salp->signature, "SST_", 4) != 0) {
+ return 0;
+ }
+ ptr = table;
+ checksum = 0;
+ for(i = 0; i < salp->table_length; i++) {
+ checksum += ptr[i];
+ }
+ if (checksum != 0) {
+ return 0;
+ }
+#if 0
+ printf("SALA: %hx SALB: %hx\n",
+ salp->sal_a_version,
+ salp->sal_b_version);
+ printf("SAL OEM: ");
+ for(i = 0; i < sizeof(salp->oem_id); i++) {
+ uint8_t ch = salp->oem_id[i];
+ if (ch == 0)
+ break;
+ printf("%c", ch);
+ }
+ printf("\n");
+
+ printf("SAL PRODUCT: ");
+ for(i = 0; i < sizeof(salp->product_id); i++) {
+ uint8_t ch = salp->product_id[i];
+ if (ch == 0)
+ break;
+ printf("%c", ch);
+ }
+ printf("\n");
+#endif
+ sal = salp;
+ pal_entry.entry = 0;
+ pal_entry.gp = 0;
+ sal_entry.entry = 0;
+ sal_entry.gp = 0;
+ entry = sal->entry;
+ i = 0;
+ while(i < salp->entry_count) {
+ unsigned long size = 0;
+
+ switch(entry->base.entry_type) {
+ case SAL_TYPE_ENTRYPOINT:
+ size = sizeof(entry->entry_point);
+ pal_entry.entry = entry->entry_point.pal_proc;
+ sal_entry.entry = entry->entry_point.sal_proc;
+ sal_entry.gp = entry->entry_point.sal_gp;
+ break;
+ case SAL_TYPE_MEMORY:
+ size = sizeof(entry->mem);
+ break;
+ case SAL_TYPE_PLATFORM_FEATURES:
+ size = sizeof(entry->features);
+ break;
+ case SAL_TYPE_TRANSLATION_REGISTER:
+ size = sizeof(entry->tr);
+ break;
+ case SAL_TYPE_PURGE_DOMAIN:
+ size = sizeof(entry->purge);
+ break;
+ case SAL_TYPE_AP_WAKEUP:
+ size = sizeof(entry->ap_wakeup);
+ break;
+ default:
+ break;
+ }
+ entry = (struct sal_entry *)(((char *)entry) + size);
+ i++;
+ }
+ return 1;
+}
+
+#define SAL_SET_VECTORS 0x01000000
+#define SAL_GET_STATE_INFO 0x01000001
+#define SAL_GET_STATE_INFO_SIZE 0x01000002
+#define SAL_CLEAR_STATE_INFO 0x01000003
+#define SAL_MC_RENDEZ 0x01000004
+#define SAL_MC_SET_PARAMS 0x01000005
+#define SAL_REGISTER_PHYSICAL_ADDR 0x01000006
+
+#define SAL_CACHE_FLUSH 0x01000008
+#define SAL_CACHE_INIT 0x01000009
+#define SAL_PCI_CONFIG_READ 0x01000010
+#define SAL_PCI_CONFIG_WRITE 0x01000011
+#define SAL_FREQ_BASE 0x01000012
+
+#define SAL_UPDATE_PAL 0x01000020
+
+/*
+ * Now define a couple of inline functions for improved type checking
+ * and convenience.
+ */
+long sal_freq_base (unsigned long which, unsigned long *ticks_per_second,
+ unsigned long *drift_info)
+{
+ struct {
+ long status;
+ unsigned long ticks_per_second;
+ unsigned long drift_info;
+ } result, __call(void *,...);
+
+ result = __call(&sal_entry, SAL_FREQ_BASE, which, 0, 0, 0, 0, 0, 0);
+
+ *ticks_per_second = result.ticks_per_second;
+ *drift_info = result.drift_info;
+ return result.status;
+}
+
+
+
+/* Read from PCI configuration space */
+long sal_pci_config_read (
+ unsigned long pci_config_addr, unsigned long size, unsigned long *value)
+{
+ struct {
+ long status;
+ unsigned long value;
+ } result, __call(void *,...);
+
+ result = __call(&sal_entry, SAL_PCI_CONFIG_READ, pci_config_addr, size, 0, 0, 0, 0, 0);
+ if (value)
+ *value = result.value;
+ return result.status;
+}
+
+/* Write to PCI configuration space */
+long sal_pci_config_write (
+ unsigned long pci_config_addr, unsigned long size, unsigned long value)
+{
+ struct {
+ long status;
+ } result, __call(void *,...);
+
+ result = __call(&sal_entry, SAL_PCI_CONFIG_WRITE, pci_config_addr, size, value, 0, 0, 0, 0);
+ return result.status;
+}
diff --git a/src/arch/ia64/core/setjmp.S b/src/arch/ia64/core/setjmp.S
new file mode 100644
index 00000000..f3e57678
--- /dev/null
+++ b/src/arch/ia64/core/setjmp.S
@@ -0,0 +1,173 @@
+/* Copyright (C) 1999, 2000, 2001 Free Software Foundation, Inc.
+ Contributed by David Mosberger-Tang <davidm@hpl.hp.com>.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ The GNU C Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the GNU C Library; if not, write to the Free
+ Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ 02111-1307 USA.
+
+ The layout of the jmp_buf is as follows. This is subject to change
+ and user-code should never depend on the particular layout of
+ jmp_buf!
+
+
+ offset: description:
+ ------- ------------
+ 0x000 stack pointer (r12) ; unchangeable (see _JMPBUF_UNWINDS)
+ 0x008 r1 (gp)
+ 0x010 caller's unat
+ 0x018 fpsr
+ 0x020 r4
+ 0x028 r5
+ 0x030 r6
+ 0x038 r7
+ 0x040 rp (b0)
+ 0x048 b1
+ 0x050 b2
+ 0x058 b3
+ 0x060 b4
+ 0x068 b5
+ 0x070 ar.pfs
+ 0x078 ar.lc
+ 0x080 pr
+ 0x088 ar.bsp ; unchangeable (see __longjmp.S)
+ 0x090 ar.unat
+ 0x098 &__jmp_buf ; address of the jmpbuf (needed to locate NaT bits in unat)
+ 0x0a0 f2
+ 0x0b0 f3
+ 0x0c0 f4
+ 0x0d0 f5
+ 0x0e0 f16
+ 0x0f0 f17
+ 0x100 f18
+ 0x110 f19
+ 0x120 f20
+ 0x130 f21
+ 0x130 f22
+ 0x140 f23
+ 0x150 f24
+ 0x160 f25
+ 0x170 f26
+ 0x180 f27
+ 0x190 f28
+ 0x1a0 f29
+ 0x1b0 f30
+ 0x1c0 f31 */
+
+
+ /* The following two entry points are the traditional entry points: */
+
+ .text
+ .global setjmp
+ .proc setjmp
+setjmp:
+ alloc r8=ar.pfs,2,0,0,0
+ mov in1=1
+ br.cond.sptk.many __sigsetjmp
+ .size setjmp, . - setjmp
+ .endp setjmp
+
+ /* __sigsetjmp(__jmp_buf buf, int savemask) */
+
+__sigsetjmp:
+#if 0
+ .prologue ASM_UNW_PRLG_RP|ASM_UNW_PRLG_PFS, ASM_UNW_PRLG_GRSAVE(2)
+#endif
+ alloc loc1=ar.pfs,2,2,2,0
+ mov r16=ar.unat
+ ;;
+ mov r17=ar.fpsr
+ mov r2=in0
+ add r3=8,in0
+ ;;
+ st8.spill.nta [r2]=sp,16 // r12 (sp)
+ st8.spill.nta [r3]=gp,16 // r1 (gp)
+ ;;
+ st8.nta [r2]=r16,16 // save caller's unat
+ st8.nta [r3]=r17,16 // save fpsr
+ add r8=0xa0,in0
+ ;;
+ st8.spill.nta [r2]=r4,16 // r4
+ st8.spill.nta [r3]=r5,16 // r5
+ add r9=0xb0,in0
+ ;;
+ stf.spill.nta [r8]=f2,32
+ stf.spill.nta [r9]=f3,32
+ mov loc0=rp
+ .body
+ ;;
+ stf.spill.nta [r8]=f4,32
+ stf.spill.nta [r9]=f5,32
+ mov r17=b1
+ ;;
+ stf.spill.nta [r8]=f16,32
+ stf.spill.nta [r9]=f17,32
+ mov r18=b2
+ ;;
+ stf.spill.nta [r8]=f18,32
+ stf.spill.nta [r9]=f19,32
+ mov r19=b3
+ ;;
+ stf.spill.nta [r8]=f20,32
+ stf.spill.nta [r9]=f21,32
+ mov r20=b4
+ ;;
+ stf.spill.nta [r8]=f22,32
+ stf.spill.nta [r9]=f23,32
+ mov r21=b5
+ ;;
+ stf.spill.nta [r8]=f24,32
+ stf.spill.nta [r9]=f25,32
+ mov r22=ar.lc
+ ;;
+ stf.spill.nta [r8]=f26,32
+ stf.spill.nta [r9]=f27,32
+ mov r24=pr
+ ;;
+ stf.spill.nta [r8]=f28,32
+ stf.spill.nta [r9]=f29,32
+ ;;
+ stf.spill.nta [r8]=f30
+ stf.spill.nta [r9]=f31
+
+ st8.spill.nta [r2]=r6,16 // r6
+ st8.spill.nta [r3]=r7,16 // r7
+ ;;
+ mov r23=ar.bsp
+ mov r25=ar.unat
+ mov out0=in0
+
+ st8.nta [r2]=loc0,16 // b0
+ st8.nta [r3]=r17,16 // b1
+ mov out1=in1
+ ;;
+ st8.nta [r2]=r18,16 // b2
+ st8.nta [r3]=r19,16 // b3
+ ;;
+ st8.nta [r2]=r20,16 // b4
+ st8.nta [r3]=r21,16 // b5
+ ;;
+ st8.nta [r2]=loc1,16 // ar.pfs
+ st8.nta [r3]=r22,16 // ar.lc
+ ;;
+ st8.nta [r2]=r24,16 // pr
+ st8.nta [r3]=r23,16 // ar.bsp
+ ;;
+ st8.nta [r2]=r25 // ar.unat
+ st8.nta [r3]=in0 // &__jmp_buf
+ mov r8=0
+ mov rp=loc0
+ mov ar.pfs=loc1
+ br.ret.sptk.many rp
+
+ .endp __sigsetjmp
diff --git a/src/arch/ia64/core/start.S b/src/arch/ia64/core/start.S
new file mode 100644
index 00000000..72b94ce1
--- /dev/null
+++ b/src/arch/ia64/core/start.S
@@ -0,0 +1,28 @@
+ .text
+ .align 4
+ .proc _start
+ .globl _start
+_start:
+ {
+ alloc loc0 = ar.pfs,1,2,1,0 /* in, local, out, rotating */
+ mov loc1 = rp
+ mov r14 = ip /* Get the address of _start */
+ }
+ movl r15 = @gprel(_start)
+ ;;
+ sub gp = r14,r15
+ ;;
+ rsm psr.i /* disable interrupts */
+ ;;
+ add out0 = @gprel(_text),gp
+ br.call.sptk.few rp = _relocate
+ ;;
+ cmp.eq p6,p7 = r0,r8 /* r8 == SUCCESS? */
+ mov ar.pfs = loc0
+ mov rp = loc1
+ ;;
+(p6) br.cond.sptk.few main
+(p7) br.ret.sptk.few rp
+
+ .size _start, . - _start
+ .endp _start
diff --git a/src/arch/ia64/drivers/net/undi_nii.c b/src/arch/ia64/drivers/net/undi_nii.c
new file mode 100644
index 00000000..c1aabf00
--- /dev/null
+++ b/src/arch/ia64/drivers/net/undi_nii.c
@@ -0,0 +1,1079 @@
+#include "efi/efi.h"
+#include "etherboot.h"
+#include "isa.h"
+#include "dev.h"
+#include "nic.h"
+#include "timer.h"
+
+#warning "Place the declaraction of __call someplace more appropriate\n"
+extern EFI_STATUS __call(void *,...);
+#warning "Place a declaration of lookup_efi_nic somewhere useful"
+EFI_NETWORK_INTERFACE_IDENTIFIER_INTERFACE *lookup_efi_nic(int index);
+
+struct sw_undi {
+ uint8_t signature[4];
+ uint8_t len;
+ uint8_t fudge;
+ uint8_t rev;
+ uint8_t ifcnt;
+ uint8_t major;
+ uint8_t minor;
+ uint16_t reserved1;
+ uint32_t implementation;
+#define UNDI_IMP_CMD_COMPLETE_INT_SUPPORTED 0x00000001
+#define UNDI_IMP_PACKET_RX_INT_SUPPORTED 0x00000002
+#define UNDI_IMP_TX_COMPLETE_INT_SUPPORTED 0x00000004
+#define UNDI_IMP_SOFTWARE_INT_SUPPORTED 0x00000008
+#define UNDI_IMP_FILTERED_MULTICAST_RX_SUPPORTED 0x00000010
+#define UNDI_IMP_BROADCAST_RX_SUPPORTED 0x00000020
+#define UNDI_IMP_PROMISCUOUS_RX_SUPPORTED 0x00000040
+#define UNDI_IMP_PROMISCUOUS_MULTICAST_RX_SUPPORTED 0x00000080
+#define UNDI_IMP_STATION_ADDR_SETTABLE 0x00000100
+#define UNDI_IMP_STATISTICS_SUPPORTED 0x00000200
+#define UNDI_IMP_NVDATA_SUPPORT_MASK 0x00000C00
+#define UNDI_IMP_NVDATA_NOT_AVAILABLE 0x00000000
+#define UNDI_IMP_NVDATA_READ_ONLY 0x00000400
+#define UNDI_IMP_NVDATA_SPARSE_WRITEABLE 0x00000800
+#define UNDI_IMP_NVDATA_BULK_WRITEABLE 0x00000C00
+#define UNDI_IMP_MULTI_FRAME_SUPPORTED 0x00001000
+#define UNDI_IMP_CMD_QUEUE_SUPPORTED 0x00002000
+#define UNDI_IMP_CMD_LINK_SUPPORTED 0x00004000
+#define UNDI_IMP_FRAG_SUPPORTED 0x00008000
+#define UNDI_IMP_64BIT_DEVICE 0x00010000
+#define UNDI_IMP_SW_VIRT_ADDR 0x40000000
+#define UNDI_IMP_HW_UNDI 0x80000000
+ uint64_t entry_point;
+ uint8_t reserved2[3];
+ uint8_t bus_type_cnt;
+ uint32_t bus_type[0];
+};
+
+struct cdb {
+ uint16_t op_code;
+#define CDB_OP_GET_STATE 0x0000
+#define CDB_OP_START 0x0001
+#define CDB_OP_STOP 0x0002
+#define CDB_OP_GET_INIT_INFO 0x0003
+#define CDB_OP_GET_CONFIG_INFO 0x0004
+#define CDB_OP_INITIALIZE 0x0005
+#define CDB_OP_RESET 0x0006
+#define CDB_OP_SHUTDOWN 0x0007
+#define CDB_OP_INTERRUPT_ENABLES 0x0008
+#define CDB_OP_RECEIVE_FILTERS 0x0009
+#define CDB_OP_STATION_ADDRESS 0x000a
+#define CDB_OP_STATISTICS 0x000b
+#define CDB_OP_MCAST_IP_TO_MAC 0x000c
+#define CDB_OP_NVDATA 0x000d
+#define CDB_OP_GET_STATUS 0x000e
+#define CDB_OP_FILL_HEADER 0x000f
+#define CDB_OP_TRANSMIT 0x0010
+#define CDB_OP_RECEIVE 0x0011
+ uint16_t op_flags;
+#define CDB_OPFLAGS_NOT_USED 0x0000
+/* Initialize */
+#define CDB_OPFLAGS_INIT_CABLE_DETECT_MASK 0x0001
+#define CDB_OPFLAGS_INIT_DETECT_CABLE 0x0000
+#define CDB_OPFLAGS_INIT_DO_NOT_DETECT_CABLE 0x0001
+/* Reset */
+#define CDB_OPFLAGS_RESET_DISABLE_INTERRUPTS 0x0001
+#define CDB_OPFLAGS_RESET_DISABLE_FILTERS 0x0002
+/* Interrupt Enables */
+#define CDB_OPFLAGS_INTERRUPT_OPMASK 0xc000
+#define CDB_OPFLAGS_INTERRUPT_ENABLE 0x8000
+#define CDB_OPFLAGS_INTERRUPT_DISABLE 0x4000
+#define CDB_OPFLAGS_INTERRUPT_READ 0x0000
+#define CDB_OPFLAGS_INTERRUPT_RECEIVE 0x0001
+#define CDB_OPFLAGS_INTERRUPT_TRANSMIT 0x0002
+#define CDB_OPFLAGS_INTERRUPT_COMMAND 0x0004
+#define CDB_OPFLAGS_INTERRUPT_SOFTWARE 0x0008
+/* Receive Filters */
+#define CDB_OPFLAGS_RECEIVE_FILTER_OPMASK 0xc000
+#define CDB_OPFLAGS_RECEIVE_FILTER_ENABLE 0x8000
+#define CDB_OPFLAGS_RECEIVE_FILTER_DISABLE 0x4000
+#define CDB_OPFLAGS_RECEIVE_FILTER_READ 0x0000
+#define CDB_OPFLAGS_RECEIVE_FILTER_RESET_MCAST_LIST 0x2000
+#define CDB_OPFLAGS_RECEIVE_FILTER_UNICAST 0x0001
+#define CDB_OPFLAGS_RECEIVE_FILTER_BROADCAST 0x0002
+#define CDB_OPFLAGS_RECEIVE_FILTER_FILTERED_MULTICAST 0x0004
+#define CDB_OPFLAGS_RECEIVE_FILTER_PROMISCUOUS 0x0008
+#define CDB_OPFLAGS_RECEIVE_FILTER_ALL_MULTICAST 0x0010
+/* Station Address */
+#define CDB_OPFLAGS_STATION_ADDRESS_READ 0x0000
+#define CDB_OPFLAGS_STATION_ADDRESS_WRITE 0x0000
+#define CDB_OPFLAGS_STATION_ADDRESS_RESET 0x0001
+/* Statistics */
+#define CDB_OPFLAGS_STATISTICS_READ 0x0000
+#define CDB_OPFLAGS_STATISTICS_RESET 0x0001
+/* MCast IP to MAC */
+#define CDB_OPFLAGS_MCAST_IP_TO_MAC_OPMASK 0x0003
+#define CDB_OPFLAGS_MCAST_IPV4_TO_MAC 0x0000
+#define CDB_OPFLAGS_MCAST_IPV6_TO_MAC 0x0001
+/* NvData */
+#define CDB_OPFLAGS_NVDATA_OPMASK 0x0001
+#define CDB_OPFLAGS_NVDATA_READ 0x0000
+#define CDB_OPFLAGS_NVDATA_WRITE 0x0001
+/* Get Status */
+#define CDB_OPFLAGS_GET_INTERRUPT_STATUS 0x0001
+#define CDB_OPFLAGS_GET_TRANSMITTED_BUFFERS 0x0002
+/* Fill Header */
+#define CDB_OPFLAGS_FILL_HEADER_OPMASK 0x0001
+#define CDB_OPFLAGS_FILL_HEADER_FRAGMENTED 0x0001
+#define CDB_OPFLAGS_FILL_HEADER_WHOLE 0x0000
+/* Transmit */
+#define CDB_OPFLAGS_SWUNDI_TRANSMIT_OPMASK 0x0001
+#define CDB_OPFLAGS_TRANSMIT_BLOCK 0x0001
+#define CDB_OPFLAGS_TRANSMIT_DONT_BLOCK 0x0000
+
+#define CDB_OPFLAGS_TRANSMIT_OPMASK 0x0002
+#define CDB_OPFLAGS_TRANSMIT_FRAGMENTED 0x0002
+#define CDB_OPFLAGS_TRANSMIT_WHOLE 0x0000
+/* Receive */
+ uint16_t cpb_size;
+ uint16_t db_size;
+ uint64_t cpb_addr;
+ uint64_t db_addr;
+ uint16_t stat_code;
+#define CDB_STATCODE_INITIALIZE 0x0000
+/* Common stat_code values */
+#define CDB_STATCODE_SUCCESS 0x0000
+#define CDB_STATCODE_INVALID_CDB 0x0001
+#define CDB_STATCODE_INVALID_CPB 0x0002
+#define CDB_STATCODE_BUSY 0x0003
+#define CDB_STATCODE_QUEUE_FULL 0x0004
+#define CDB_STATCODE_ALREADY_STARTED 0x0005
+#define CDB_STATCODE_NOT_STARTED 0x0006
+#define CDB_STATCODE_NOT_SHUTDOWN 0x0007
+#define CDB_STATCODE_ALREADY_INITIALIZED 0x0008
+#define CDB_STATCODE_NOT_INITIALIZED 0x0009
+#define CDB_STATCODE_DEVICE_FAILURE 0x000A
+#define CDB_STATCODE_NVDATA_FAILURE 0x000B
+#define CDB_STATCODE_UNSUPPORTED 0x000C
+#define CDB_STATCODE_BUFFER_FULL 0x000D
+#define CDB_STATCODE_INVALID_PARAMETER 0x000E
+#define CDB_STATCODE_INVALID_UNDI 0x000F
+#define CDB_STATCODE_IPV4_NOT_SUPPORTED 0x0010
+#define CDB_STATCODE_IPV6_NOT_SUPPORTED 0x0011
+#define CDB_STATCODE_NOT_ENOUGH_MEMORY 0x0012
+#define CDB_STATCODE_NO_DATA 0x0013
+
+ uint16_t stat_flags;
+#define CDB_STATFLAGS_INITIALIZE 0x0000
+/* Common stat_flags */
+#define CDB_STATFLAGS_STATUS_MASK 0xc000
+#define CDB_STATFLAGS_COMMAND_COMPLETE 0xc000
+#define CDB_STATFLAGS_COMMAND_FAILED 0x8000
+#define CDB_STATFLAGS_COMMAND_QUEUED 0x4000
+/* Get State */
+#define CDB_STATFLAGS_GET_STATE_MASK 0x0003
+#define CDB_STATFLAGS_GET_STATE_INITIALIZED 0x0002
+#define CDB_STATFLAGS_GET_STATE_STARTED 0x0001
+#define CDB_STATFLAGS_GET_STATE_STOPPED 0x0000
+/* Start */
+/* Get Init Info */
+#define CDB_STATFLAGS_CABLE_DETECT_MASK 0x0001
+#define CDB_STATFLAGS_CABLE_DETECT_NOT_SUPPORTED 0x0000
+#define CDB_STATFLAGS_CABLE_DETECT_SUPPORTED 0x0001
+/* Initialize */
+#define CDB_STATFLAGS_INITIALIZED_NO_MEDIA 0x0001
+/* Reset */
+#define CDB_STATFLAGS_RESET_NO_MEDIA 0x0001
+/* Shutdown */
+/* Interrupt Enables */
+#define CDB_STATFLAGS_INTERRUPT_RECEIVE 0x0001
+#define CDB_STATFLAGS_INTERRUPT_TRANSMIT 0x0002
+#define CDB_STATFLAGS_INTERRUPT_COMMAND 0x0004
+/* Receive Filters */
+#define CDB_STATFLAGS_RECEIVE_FILTER_UNICAST 0x0001
+#define CDB_STATFLAGS_RECEIVE_FILTER_BROADCAST 0x0002
+#define CDB_STATFLAGS_RECEIVE_FILTER_FILTERED_MULTICAST 0x0004
+#define CDB_STATFLAGS_RECEIVE_FILTER_PROMISCUOUS 0x0008
+#define CDB_STATFLAGS_RECEIVE_FILTER_ALL_MULTICAST 0x0010
+/* Statistics */
+/* MCast IP to MAC */
+/* NvData */
+/* Get Status */
+#define CDB_STATFLAGS_GET_STATUS_INTERRUPT_MASK 0x000F
+#define CDB_STATFLAGS_GET_STATUS_NO_INTERRUPTS 0x0000
+#define CDB_STATFLAGS_GET_STATUS_RECEIVE 0x0001
+#define CDB_STATFLAGS_GET_STATUS_TRANSMIT 0x0002
+#define CDB_STATFLAGS_GET_STATUS_COMMAND 0x0004
+#define CDB_STATFLAGS_GET_STATUS_SOFTWARE 0x0008
+#define CDB_STATFLAGS_GET_STATUS_TXBUF_QUEUE_EMPTY 0x0010
+#define CDB_STATFLAGS_GET_STATUS_NO_TXBUFS_WRITTEN 0x0020
+/* Fill Header */
+/* Transmit */
+/* Receive */
+ uint16_t ifnum;
+#define CDB_IFNUM_START 0x0000
+#define CDB_IFNUM_INVALID 0x0000
+ uint16_t control;
+#define CDB_CONTROL_QUEUE_IF_BUSY 0x0002
+
+#define CDB_CONTROL_LINK 0x0001
+#define CDB_CONTROL_LAST_CDB_IN_LIST 0x0000
+};
+
+#define UNDI_MAC_LENGTH 32
+typedef uint8_t undi_mac_addr[UNDI_MAC_LENGTH];
+typedef uint16_t undi_media_protocol;
+typedef uint8_t undi_frame_type;
+#define UNDI_FRAME_TYPE_NONE 0x00
+#define UNDI_FRAME_TYPE_UNICAST 0x01
+#define UNDI_FRAME_TYPE_BROADCAST 0x02
+#define UNDI_FRAME_TYPE_MULTICAST 0x03
+#define UNDI_FRAME_TYPE_PROMISCUOUS 0x04
+
+#define UNDI_MAX_XMIT_BUFFERS 32
+#define UNDI_MAX_MCAST_ADDRESS_CNT 8
+
+#define UNDI_BUS_TYPE(a,b,c,d) \
+ ((((d) & 0xff) << 24) | \
+ (((c) & 0xff) << 16) | \
+ (((b) & 0xff) << 8) | \
+ (((a) & 0xff) << 0))
+
+#define UNDI_BUS_TYPE_PCI UNDI_BUS_TYPE('P','C','I','R')
+#define UNDI_BUS_TYPE_PCC UNDI_BUS_TYPE('P','C','C','R')
+#define UNDI_BUS_TYPE_USB UNDI_BUS_TYPE('U','S','B','R')
+#define UNDI_BUS_TYPE_1394 UNDI_BUS_TYPE('1','3','9','4')
+
+struct cpb_start {
+ void *delay;
+ void *block;
+ void *virt2phys;
+ void *mem_io;
+} PACKED;
+
+struct db_init_info {
+ uint32_t memory_required;
+ uint32_t frame_data_len;
+ uint32_t link_speeds[4];
+ uint32_t nv_count;
+ uint16_t nv_width;
+ uint16_t media_header_len;
+ uint16_t hw_addr_len;
+ uint16_t mcast_filter_cnt;
+ uint16_t tx_buf_cnt;
+ uint16_t tx_buf_size;
+ uint16_t rx_buf_cnt;
+ uint16_t rx_buf_size;
+ uint8_t if_type;
+ uint8_t duplex;
+#define UNDI_DUPLEX_ENABLE_FULL_SUPPORTED 1
+#define UNDI_DUPLEX_FORCE_FULL_SUPPORTED 2
+ uint8_t loopback;
+#define UNDI_LOOPBACK_INTERNAL_SUPPORTED 1
+#define UNDI_LOOPBACK_EXTERNAL_SUPPORTED 2
+} PACKED;
+
+
+struct db_pci_config_info {
+ uint32_t bus_type;
+ uint16_t bus;
+ uint8_t device;
+ uint8_t function;
+ uint8_t config[256];
+};
+struct db_pcc_config_info {
+ uint32_t bus_type;
+ uint16_t bus;
+ uint8_t device;
+ uint8_t function;
+ uint8_t config[256];
+};
+struct db_usb_config_info {
+ uint32_t bus_type;
+};
+struct db_iee1394_config_info {
+ uint32_t bus_type;
+};
+struct db_config_info {
+ union {
+ struct db_pci_config_info pci;
+ struct db_pcc_config_info pcc;
+ struct db_usb_config_info usb;
+ struct db_iee1394_config_info iee1394;
+ };
+};
+
+struct cpb_initialize {
+ uint64_t memory_addr;
+ uint32_t memory_length;
+ uint32_t link_speed;
+ uint16_t tx_buf_cnt;
+ uint16_t tx_buf_size;
+ uint16_t rx_buf_cnt;
+ uint16_t rx_buf_size;
+ uint8_t duplex;
+ uint8_t loopback;
+} PACKED;
+
+struct db_initialize {
+ uint32_t memory_used;
+ uint16_t tx_buf_cnt;
+ uint16_t tx_buf_size;
+ uint16_t rx_buf_cnt;
+ uint16_t rx_buf_size;
+} PACKED;
+
+struct cpb_station_address {
+ undi_mac_addr station_addr;
+} PACKED;
+
+struct db_station_address {
+ undi_mac_addr station_address;
+ undi_mac_addr broadcast_address;
+ undi_mac_addr permanent_address;
+} PACKED;
+
+struct cpb_receive_filters {
+ undi_mac_addr mcast_list[UNDI_MAX_MCAST_ADDRESS_CNT];
+} PACKED;
+
+struct db_receive_filters {
+ undi_mac_addr mcast_list[UNDI_MAX_MCAST_ADDRESS_CNT];
+} PACKED;
+
+
+struct db_get_status {
+ uint32_t rx_frame_len;
+ uint32_t reserved;
+ uint64_t tx_buffer[UNDI_MAX_XMIT_BUFFERS];
+} PACKED;
+
+struct cpb_transmit {
+ uint64_t frame_addr;
+ uint32_t data_len;
+ uint16_t media_header_len;
+ uint16_t reserved;
+} PACKED;
+
+struct cpb_receive {
+ uint64_t buffer_addr;
+ uint32_t buffer_len;
+ uint32_t reserved;
+} PACKED;
+struct db_receive {
+ undi_mac_addr src_addr;
+ undi_mac_addr dest_addr;
+ uint32_t frame_len;
+ undi_media_protocol protocol;
+ uint16_t media_header_len;
+ undi_frame_type type;
+ uint8_t reserved[7];
+} PACKED;
+struct fptr {
+ void *func;
+ void *gp;
+};
+
+extern char __gp[];
+
+/* Variables */
+static unsigned undi_ifnum;
+static void *undi_entry_point;
+static struct cdb cdb;
+static char buffer[1024*1024];
+
+/* SW UNDI callbacks */
+static void undi_udelay(uint64_t microseconds)
+{
+#if 0
+ printf("undi_udelay(%lx)\n", microseconds);
+#endif
+ if (microseconds < 10) {
+ microseconds = 10;
+ }
+ if (microseconds > 1000) {
+ mdelay(microseconds/1000);
+ microseconds%=1000;
+ }
+ udelay(microseconds);
+}
+static struct fptr fptr_undi_udelay = {
+ .func = &undi_udelay,
+ .gp = &__gp,
+};
+static void undi_block(uint32_t enable __unused)
+{
+#if 0
+ printf("undi_block(%x)\n",
+ enable);
+#endif
+ return;
+}
+static struct fptr fptr_undi_block = {
+ .func = &undi_block,
+ .gp = &__gp,
+};
+static void undi_virt2phys(uint64_t virtual, uint64_t *ptr)
+{
+#if 0
+ printf("undi_virt2phys(%lx, %lx)\n",
+ virtual, ptr);
+#endif
+ *ptr = virt_to_phys((void *)virtual);
+}
+static struct fptr fptr_undi_virt2phys = {
+ .func = &undi_virt2phys,
+ .gp = &__gp,
+};
+#define UNDI_IO_READ 0
+#define UNDI_IO_WRITE 1
+#define UNDI_MEM_READ 2
+#define UNDI_MEM_WRITE 3
+static void undi_mem_io(uint8_t read_write, uint8_t len, uint64_t port, uint64_t buf_addr)
+{
+ printf("undi_mem_io(%hhx, %hhx, %lx, %lx)\n",
+ read_write, len, port, buf_addr);
+#if 0
+ if ((read_write == UNDI_IO_READ) && (len == 1)) {
+ uint8_t *buf = (void *)buf_addr;
+ *buf = inb(port);
+ }
+ else if ((read_write == UNDI_IO_READ) && (len == 2)) {
+ uint16_t *buf = (void *)buf_addr;
+ *buf = inw(port);
+ }
+ else if ((read_write == UNDI_IO_READ) && (len == 4)) {
+ uint32_t *buf = (void *)buf_addr;
+ *buf = inl(port);
+ }
+ else if ((read_write == UNDI_IO_READ) && (len == 8)) {
+ uint64_t *buf = (void *)buf_addr;
+ *buf = inq(port);
+ }
+ else if ((read_write == UNDI_IO_WRITE) && (len == 1)) {
+ uint8_t *buf = (void *)buf_addr;
+ outb(*buf, port);
+ }
+ else if ((read_write == UNDI_IO_WRITE) && (len == 2)) {
+ uint16_t *buf = (void *)buf_addr;
+ outw(*buf, port);
+ }
+ else if ((read_write == UNDI_IO_WRITE) && (len == 4)) {
+ uint32_t *buf = (void *)buf_addr;
+ outl(*buf, port);
+ }
+ else if ((read_write == UNDI_IO_WRITE) && (len == 8)) {
+ uint64_t *buf = (void *)buf_addr;
+ outq(*buf, port);
+ }
+ else if ((read_write == UNDI_MEM_READ) && (len == 1)) {
+ uint8_t *buf = (void *)buf_addr;
+ *buf = readb(port);
+ }
+ else if ((read_write == UNDI_MEM_READ) && (len == 2)) {
+ uint16_t *buf = (void *)buf_addr;
+ *buf = readw(port);
+ }
+ else if ((read_write == UNDI_MEM_READ) && (len == 4)) {
+ uint32_t *buf = (void *)buf_addr;
+ *buf = readl(port);
+ }
+ else if ((read_write == UNDI_MEM_READ) && (len == 8)) {
+ uint64_t *buf = (void *)buf_addr;
+ *buf = readq(port);
+ }
+ else if ((read_write == UNDI_MEM_WRITE) && (len == 1)) {
+ uint8_t *buf = (void *)buf_addr;
+ writeb(*buf, port);
+ }
+ else if ((read_write == UNDI_MEM_WRITE) && (len == 2)) {
+ uint16_t *buf = (void *)buf_addr;
+ writew(*buf, port);
+ }
+ else if ((read_write == UNDI_MEM_WRITE) && (len == 4)) {
+ uint32_t *buf = (void *)buf_addr;
+ writel(*buf, port);
+ }
+ else if ((read_write == UNDI_MEM_WRITE) && (len == 8)) {
+ uint64_t *buf = (void *)buf_addr;
+ writeq(*buf, port);
+ }
+#endif
+}
+static struct fptr fptr_undi_mem_io = {
+ .func = &undi_mem_io,
+ .gp = &__gp,
+};
+
+/* static void undi_memio(this, width, address, count, buffer);??? */
+
+
+/* Wrappers to call the undi functions */
+static int undi_call(struct cdb *cdb)
+{
+ int result = 1;
+ cdb->stat_code = CDB_STATCODE_INITIALIZE;
+ cdb->stat_flags = CDB_STATFLAGS_INITIALIZE;
+ cdb->ifnum = undi_ifnum;
+ cdb->control = CDB_CONTROL_LAST_CDB_IN_LIST;
+ __call(undi_entry_point, cdb);
+ /* Wait until the command executes... */
+ while((cdb->stat_flags & CDB_STATFLAGS_STATUS_MASK) == 0)
+ ;
+ if ((cdb->stat_flags & CDB_STATFLAGS_STATUS_MASK) !=
+ CDB_STATFLAGS_COMMAND_COMPLETE)
+ result = 0;
+ if (cdb->stat_code != CDB_STATCODE_SUCCESS)
+ result = 0;
+ return result;
+}
+
+static int get_state(struct cdb *cdb)
+{
+ memset(cdb, 0, sizeof(*cdb));
+ cdb->op_code = CDB_OP_GET_STATE;
+ cdb->op_flags = CDB_OPFLAGS_NOT_USED;
+ return undi_call(cdb);
+}
+
+static int start(struct cdb *cdb)
+{
+ static struct cpb_start cpb;
+ memset(&cpb, 0, sizeof(cpb));
+ cpb.delay = &fptr_undi_udelay;
+ cpb.block = &fptr_undi_block;
+ cpb.virt2phys = &fptr_undi_virt2phys;
+ cpb.mem_io = &fptr_undi_mem_io;
+
+ memset(cdb, 0, sizeof(*cdb));
+ cdb->op_code = CDB_OP_START;
+ cdb->op_flags = CDB_OPFLAGS_NOT_USED;
+ cdb->cpb_size = sizeof(cpb);
+ cdb->cpb_addr = virt_to_phys(&cpb);
+
+ return undi_call(cdb);
+}
+static int stop(struct cdb *cdb)
+{
+ memset(cdb, 0, sizeof(*cdb));
+ cdb->op_code = CDB_OP_STOP;
+ cdb->op_flags = CDB_OPFLAGS_NOT_USED;
+ return undi_call(cdb);
+}
+static int get_init_info(struct cdb *cdb, struct db_init_info *info)
+{
+ memset(info, 0, sizeof(*info));
+ memset(cdb, 0, sizeof(*cdb));
+ cdb->op_code = CDB_OP_GET_INIT_INFO;
+ cdb->op_flags = CDB_OPFLAGS_NOT_USED;
+ cdb->db_size = sizeof(*info);
+ cdb->db_addr = virt_to_phys(info);
+ return undi_call(cdb);
+}
+
+#if 0
+/* get_config_info crashes broadcoms pxe driver */
+static int get_config_info(struct cdb *cdb, struct db_config_info *info)
+{
+ memset(info, 0, sizeof(*info));
+ memset(cdb, 0, sizeof(*cdb));
+ cdb->op_code = CDB_OP_GET_CONFIG_INFO;
+ cdb->op_flags = CDB_OPFLAGS_NOT_USED;
+ cdb->db_size = sizeof(*info);
+ cdb->db_addr = virt_to_phys(info);
+ return undi_call(cdb);
+}
+#endif
+static int initialize(struct cdb *cdb, int media_detect,
+ struct cpb_initialize *cpb, struct db_initialize *db)
+{
+ memset(db, 0, sizeof(*db));
+ memset(cdb, 0, sizeof(*cdb));
+ cdb->op_code = CDB_OP_INITIALIZE;
+ cdb->op_flags = media_detect?
+ CDB_OPFLAGS_INIT_DETECT_CABLE:CDB_OPFLAGS_INIT_DO_NOT_DETECT_CABLE;
+ cdb->cpb_size = sizeof(*cpb);
+ cdb->cpb_addr = virt_to_phys(cpb);
+ cdb->db_size = sizeof(*db);
+ cdb->db_addr = virt_to_phys(db);
+ return undi_call(cdb);
+}
+static int shutdown(struct cdb *cdb)
+{
+ memset(cdb, 0, sizeof(*cdb));
+ cdb->op_code = CDB_OP_SHUTDOWN;
+ cdb->op_flags = CDB_OPFLAGS_NOT_USED;
+ return undi_call(cdb);
+}
+static int station_address_read(struct cdb *cdb, struct db_station_address *db)
+{
+ memset(db, 0, sizeof(*db));
+ memset(cdb, 0, sizeof(*cdb));
+ cdb->op_code = CDB_OP_STATION_ADDRESS;
+ cdb->op_flags = CDB_OPFLAGS_STATION_ADDRESS_READ;
+ cdb->db_size = sizeof(*db);
+ cdb->db_addr = virt_to_phys(db);
+ return undi_call(cdb);
+}
+static int receive_filters(struct cdb *cdb, unsigned opflags)
+{
+ /* I currently do not support setting
+ * or returning the multicast filter list.
+ * So do not even attempt to pass them.
+ */
+ memset(cdb, 0, sizeof(*cdb));
+ cdb->op_code = CDB_OP_RECEIVE_FILTERS;
+ cdb->op_flags = opflags;
+ return undi_call(cdb);
+}
+static int get_transmitted_status(struct cdb *cdb, struct db_get_status *db)
+{
+ memset(db, 0, sizeof(*db));
+ memset(cdb, 0, sizeof(*cdb));
+ cdb->op_code = CDB_OP_GET_STATUS;
+ cdb->op_flags = CDB_OPFLAGS_GET_TRANSMITTED_BUFFERS;
+ cdb->db_size = sizeof(*db);
+ cdb->db_addr = virt_to_phys(db);
+ return undi_call(cdb);
+}
+
+static int transmit(struct cdb *cdb, struct cpb_transmit *cpb)
+{
+ memset(cdb, 0, sizeof(*cdb));
+ cdb->op_code = CDB_OP_TRANSMIT;
+ cdb->op_flags = CDB_OPFLAGS_TRANSMIT_WHOLE | CDB_OPFLAGS_TRANSMIT_DONT_BLOCK;
+ cdb->cpb_size = sizeof(*cpb);
+ cdb->cpb_addr = virt_to_phys(cpb);
+ return undi_call(cdb);
+}
+
+static int receive(struct cdb *cdb,
+ struct cpb_receive *cpb, struct db_receive *db)
+{
+ memset(db, 0, sizeof(*db));
+ memset(cdb, 0, sizeof(*cdb));
+ cdb->op_code = CDB_OP_RECEIVE;
+ cdb->op_flags = CDB_OPFLAGS_NOT_USED;
+ cdb->cpb_size = sizeof(*cpb);
+ cdb->cpb_addr = virt_to_phys(cpb);
+ cdb->db_size = sizeof(*db);
+ cdb->db_addr = virt_to_phys(db);
+ return undi_call(cdb);
+}
+
+/* The work horse functions */
+static int nic_poll(struct nic *nic )
+{
+ int result;
+ struct cpb_receive cpb;
+ struct db_receive db;
+
+ memset(&cpb, 0, sizeof(cpb));
+ cpb.buffer_addr = virt_to_phys(nic->packet);
+ cpb.buffer_len = ETH_FRAME_LEN;
+ result = receive(&cdb, &cpb, &db);
+ if (result) {
+ nic->packetlen = db.frame_len;
+ return 1;
+ }
+ else if (cdb.stat_code != CDB_STATCODE_NO_DATA) {
+ printf("Receive failed: %lx\n", cdb.stat_code);
+ }
+ return 0; /* initially as this is called to flush the input */
+}
+
+static void nic_transmit(struct nic *nic, const char *dest, unsigned int type,
+ unsigned int size, const char *data)
+{
+ int result;
+ static struct {
+ uint8_t dst_addr[ETH_ALEN];
+ uint8_t src_addr[ETH_ALEN];
+ uint16_t type;
+ uint8_t data[ETH_MAX_MTU];
+ } packet;
+ struct cpb_transmit cpb;
+ struct db_get_status db;
+ int done;
+
+ /* Build the packet to transmit in my buffer */
+ memcpy(&packet.dst_addr, dest, ETH_ALEN);
+ memcpy(&packet.src_addr, nic->node_addr, ETH_ALEN);
+ packet.type = htons(type);
+ memcpy(&packet.data, data, size);
+
+ /* send the packet to destination */
+ cpb.frame_addr = virt_to_phys(&packet);
+ cpb.data_len = ETH_HLEN + size;
+ cpb.media_header_len = ETH_HLEN;
+ result = transmit(&cdb, &cpb);
+ if (!result) {
+ printf("transmit failed: %lx\n", cdb.stat_code);
+ return;
+ }
+ /* Wait until the packet is actually transmitted,
+ * indicating it is safe to reuse my trasmit buffer.
+ */
+ done = 0;
+ while(!done) {
+ int i;
+ result = get_transmitted_status(&cdb, &db);
+ for(i = 0; i < UNDI_MAX_XMIT_BUFFERS; i++) {
+ if (db.tx_buffer[i] == virt_to_phys(&packet)) {
+ done = 1;
+ }
+ }
+ }
+}
+
+static void nic_disable(struct dev *dev)
+{
+ struct nic *nic = (struct nic *)dev;
+ int result;
+ result = shutdown(&cdb);
+ if (!result) {
+ printf("UNDI nic does not want to shutdown: %x\n", cdb.stat_code);
+ }
+ result = stop(&cdb);
+ if (!result) {
+ printf("UNDI nic does not want to stop: %x\n", cdb.stat_code);
+ }
+ undi_ifnum = 0;
+ undi_entry_point = 0;
+}
+
+static uint8_t undi_checksum(struct sw_undi *undi)
+{
+ uint8_t undi_sum, *ptr;
+ int i;
+ ptr = (uint8_t *)undi;
+ undi_sum = 0;
+ for(i = 0; i < undi->len; i++) {
+ undi_sum += ((char *)undi)[i];
+ }
+ return undi_sum;
+}
+
+#if 0
+/* Debug functions */
+void print_nii(EFI_NETWORK_INTERFACE_IDENTIFIER_INTERFACE *nii)
+{
+ printf("NII Revision: %lx\n", nii->Revision);
+ printf("NII ID: %lx\n", nii->ID);
+ printf("NII ImageAddr: %lx\n", nii->ImageAddr);
+ printf("NII ImageSize: %x\n", nii->ImageSize);
+ printf("NII StringID: %c%c%c%c\n",
+ nii->StringId[0], nii->StringId[1], nii->StringId[2], nii->StringId[3]);
+ printf("NII Type: %hhx\n", nii->Type);
+ printf("NII Version: %d.%d\n", nii->MajorVer, nii->MinorVer);
+ printf("NII IfNum: %hhx\n", nii->IfNum);
+ printf("\n");
+}
+void print_sw_undi(struct sw_undi *undi)
+{
+ int i;
+ printf("UNDI signature: %c%c%c%c\n",
+ undi->signature[0], undi->signature[1], undi->signature[2], undi->signature[3]);
+ printf("UNDI len: %hhx\n", undi->len);
+ printf("UNDI fudge: %hhx\n", undi->fudge);
+ printf("UNDI rev: %hhx\n", undi->rev);
+ printf("UNDI ifcnt: %hhx\n", undi->ifcnt);
+ printf("UNDI version: %d.%d\n", undi->major, undi->minor);
+ printf("UNDI implementation: %x\n", undi->implementation);
+ printf("UNDI entry point: %lx\n", undi->entry_point);
+ printf("UNDI bus type cnt: %d\n", undi->bus_type_cnt);
+ for(i = 0; i < undi->bus_type_cnt; i++) {
+ printf("UNDI bus type: %c%c%c%c\n",
+ ((undi->bus_type[i]) >> 0) & 0xff,
+ ((undi->bus_type[i]) >> 8) & 0xff,
+ ((undi->bus_type[i]) >> 16) & 0xff,
+ ((undi->bus_type[i]) >> 24) & 0xff);
+ }
+ printf("UNDI sum: %hhx\n", undi_checksum(undi));
+ printf("\n");
+}
+void print_init_info(struct db_init_info *info)
+{
+ printf("init_info.memory_required: %d\n", info->memory_required);
+ printf("init_info.frame_data_len: %d\n", info->frame_data_len);
+ printf("init_info.link_speeds: %d %d %d %d\n",
+ info->link_speeds[0], info->link_speeds[1],
+ info->link_speeds[2], info->link_speeds[3]);
+ printf("init_info.media_header_len: %d\n", info->media_header_len);
+ printf("init_info.hw_addr_len: %d\n", info->hw_addr_len);
+ printf("init_info.mcast_filter_cnt: %d\n", info->mcast_filter_cnt);
+ printf("init_info.tx_buf_cnt: %d\n", info->tx_buf_cnt);
+ printf("init_info.tx_buf_size: %d\n", info->tx_buf_size);
+ printf("init_info.rx_buf_cnt: %d\n", info->rx_buf_cnt);
+ printf("init_info.rx_buf_size: %d\n", info->rx_buf_size);
+ printf("init_info.if_type: %hhx\n", info->if_type);
+ printf("init_info.duplex: %hhx\n", info->duplex);
+ printf("init_info.loopback: %hhx\n", info->loopback);
+ printf("\n");
+}
+void print_config_info(struct db_config_info *info)
+{
+ int i;
+ printf("config_info.bus_type: %c%c%c%c\n",
+ ((info->pci.bus_type) >> 0) & 0xff,
+ ((info->pci.bus_type) >> 8) & 0xff,
+ ((info->pci.bus_type) >> 16) & 0xff,
+ ((info->pci.bus_type) >> 24) & 0xff);
+ if (info->pci.bus_type != UNDI_BUS_TYPE_PCI) {
+ return;
+ }
+ printf("config_info.bus: %hx\n", info->pci.bus);
+ printf("config_info.device: %hhx\n", info->pci.device);
+ printf("config_info.function: %hhx\n", info->pci.function);
+ printf("config_info.config:\n");
+ for(i = 0; i < 256; i++) {
+ if ((i & 0xf) == 0) {
+ printf("[%hhx]", i);
+ }
+ printf(" %hhx", info->pci.config[i]);
+ if ((i & 0xf) == 0xf) {
+ printf("\n");
+ }
+ }
+ printf("\n");
+
+}
+void print_cdb(struct cdb *cdb)
+{
+ printf("\n");
+ printf("cdb.op_code: %hx\n", cdb->op_code);
+ printf("cdb.op_flags: %hx\n", cdb->op_flags);
+ printf("cdb.cpb_size: %d\n", cdb->cpb_size);
+ printf("cdb.db_size: %d\n", cdb->db_size);
+ printf("cdb.cpb_addr: %lx\n", cdb->cpb_addr);
+ printf("cdb.db_addr: %lx\n", cdb->db_addr);
+ printf("cdb.stat_code: %lx\n", cdb->stat_code);
+ printf("cdb.stat_flags: %lx\n", cdb->stat_flags);
+ printf("cdb.ifnum %d\n", cdb->ifnum);
+ printf("cdb.control: %hx\n", cdb->control);
+ printf("\n");
+}
+#endif
+#define ARPHRD_ETHER 1
+static int nic_setup(struct dev *dev,
+ EFI_NETWORK_INTERFACE_IDENTIFIER_INTERFACE *nii)
+{
+ struct nic *nic = (struct nic *)dev;
+ int result;
+ struct sw_undi *undi;
+ struct db_init_info init_info;
+ struct cpb_initialize cpb_initialize;
+ struct db_initialize db_initialize;
+ struct db_station_address db_station_address;
+ int media_detect;
+ unsigned filter, no_filter;
+ int i;
+
+ /* Fail if I I'm not passed a valid nii */
+ if (!nii)
+ return 0;
+
+ /* Fail if this nit a SW UNDI interface */
+ if (nii->ID == 0)
+ return 0;
+
+ undi = phys_to_virt(nii->ID);
+
+ /* Verify the undi structure */
+
+ /* It must have a pxe signature */
+ if (memcmp(undi->signature, "!PXE", 4) != 0)
+ return 0;
+ /* It must have a valid checksum */
+ if (undi_checksum(undi) != 0)
+ return 0;
+ /* It must be software undi */
+ if (undi->implementation & UNDI_IMP_HW_UNDI)
+ return 0;
+
+ /* Setup to do undi calls */
+ undi_ifnum = nii->IfNum;
+ undi_entry_point = (void *)undi->entry_point;
+
+ /* Find the UNDI state... */
+ result = get_state(&cdb);
+ if (!result)
+ return 0;
+
+ /* See if the device is already initialized */
+ if ((cdb.stat_flags & CDB_STATFLAGS_GET_STATE_MASK) !=
+ CDB_STATFLAGS_GET_STATE_STOPPED) {
+
+ /* If so attempt to stop it */
+ if ((cdb.stat_flags & CDB_STATFLAGS_GET_STATE_MASK) ==
+ CDB_STATFLAGS_GET_STATE_INITIALIZED) {
+ result = shutdown(&cdb);
+ result = stop(&cdb);
+ }
+ else if ((cdb.stat_flags & CDB_STATFLAGS_GET_STATE_MASK) ==
+ CDB_STATFLAGS_GET_STATE_STARTED) {
+ result = stop(&cdb);
+ }
+
+ /* See if it did stop */
+ result = get_state(&cdb);
+ if (!result)
+ return 0;
+
+ /* If it didn't stop give up */
+ if ((cdb.stat_flags & CDB_STATFLAGS_GET_STATE_MASK) !=
+ CDB_STATFLAGS_GET_STATE_STOPPED)
+ return 0;
+
+ }
+
+ result = start(&cdb);
+ if (!result) {
+ printf("Device would not start: %x\n", cdb.stat_code);
+ return 0;
+ }
+ result = get_init_info(&cdb, &init_info);
+ if (!result) {
+ printf("Device wount not give init info: %x\n", cdb.stat_code);
+ stop(&cdb);
+ return 0;
+ }
+ /* See if the NIC can detect the presence of a cable */
+ media_detect = (cdb.stat_flags & CDB_STATFLAGS_CABLE_DETECT_MASK) ==
+ CDB_STATFLAGS_CABLE_DETECT_SUPPORTED;
+
+ if ((init_info.if_type != ARPHRD_ETHER) ||
+ (init_info.hw_addr_len != ETH_ALEN)) {
+ printf("Not ethernet\n");
+ stop(&cdb);
+ return 0;
+ }
+ if (init_info.memory_required > sizeof(buffer)) {
+ printf("NIC wants %d bytes I only have %ld bytes\n",
+ init_info.memory_required, sizeof(buffer));
+ stop(&cdb);
+ return 0;
+ }
+ /* Initialize the device */
+ memset(buffer, 0, sizeof(buffer));
+ memset(&cpb_initialize, 0, sizeof(cpb_initialize));
+ cpb_initialize.memory_addr = virt_to_phys(&buffer);
+ cpb_initialize.memory_length = init_info.memory_required;
+ cpb_initialize.link_speed = 0; /* auto detect */
+ /* UNDI nics will not take suggestions :(
+ * So let them figure out an appropriate buffer stragety on their own.
+ */
+ cpb_initialize.tx_buf_cnt = 0;
+ cpb_initialize.tx_buf_size = 0;
+ cpb_initialize.rx_buf_cnt = 0;
+ cpb_initialize.rx_buf_size = 0;
+ cpb_initialize.duplex = 0;
+ cpb_initialize.loopback = 0;
+ result = initialize(&cdb, media_detect, &cpb_initialize, &db_initialize);
+ if (!result) {
+ printf("Device would not initialize: %x\n", cdb.stat_code);
+ stop(&cdb);
+ return 0;
+ }
+#if 0
+ /* It appears the memory_used parameter is never set correctly, ignore it */
+ if (db_initialize.memory_used > sizeof(buffer)) {
+ printf("NIC is using %d bytes I only have %ld bytes\n",
+ db_initialize.memory_used, sizeof(buffer));
+ printf("tx_buf_cnt: %d\n", db_initialize.tx_buf_cnt);
+ printf("tx_buf_size: %d\n", db_initialize.tx_buf_size);
+ printf("rx_buf_cnt: %d\n", db_initialize.rx_buf_cnt);
+ printf("rx_buf_size: %d\n", db_initialize.rx_buf_size);
+ nic_disable(dev);
+ return 0;
+ }
+ printf("NIC is using %d bytes\n",
+ db_initialize.memory_used);
+#endif
+ if (media_detect && (
+ (cdb.stat_flags & ~CDB_STATFLAGS_STATUS_MASK) ==
+ CDB_STATFLAGS_INITIALIZED_NO_MEDIA)) {
+ printf("No media present\n");
+ nic_disable(dev);
+ return 0;
+ }
+
+ /* Get the mac address */
+ result = station_address_read(&cdb, &db_station_address);
+ if (!result) {
+ printf("Could not read station address: %x\n",
+ cdb.stat_code);
+ nic_disable(dev);
+ return 0;
+ }
+ for(i = 0; i < ETH_ALEN; i++) {
+ nic->node_addr[i] = db_station_address.station_address[i];
+ }
+ printf("Ethernet addr: %!\n", nic->node_addr);
+
+ filter = CDB_OPFLAGS_RECEIVE_FILTER_ENABLE |
+ CDB_OPFLAGS_RECEIVE_FILTER_UNICAST |
+ CDB_OPFLAGS_RECEIVE_FILTER_BROADCAST;
+ no_filter = CDB_OPFLAGS_RECEIVE_FILTER_DISABLE |
+ CDB_OPFLAGS_RECEIVE_FILTER_RESET_MCAST_LIST |
+ CDB_OPFLAGS_RECEIVE_FILTER_FILTERED_MULTICAST;
+
+ if (undi->implementation & UNDI_IMP_PROMISCUOUS_MULTICAST_RX_SUPPORTED) {
+ filter |= CDB_OPFLAGS_RECEIVE_FILTER_ALL_MULTICAST;
+ no_filter |= CDB_OPFLAGS_RECEIVE_FILTER_PROMISCUOUS;
+ }
+ else if (undi->implementation & UNDI_IMP_PROMISCUOUS_RX_SUPPORTED) {
+ filter |= CDB_OPFLAGS_RECEIVE_FILTER_PROMISCUOUS;
+ }
+
+ result = receive_filters(&cdb, no_filter);
+ if (!result) {
+ printf("Could not clear receive filters: %x\n",
+ cdb.stat_code);
+ nic_disable(dev);
+ return 0;
+ }
+ result = receive_filters(&cdb, filter);
+ if (!result) {
+ printf("Could not set receive filters: %x\n",
+ cdb.stat_code);
+ nic_disable(dev);
+ return 0;
+ }
+
+ /* It would be nice to call get_config_info so I could pass
+ * the type of nic, but that crashes some efi drivers.
+ */
+ /* Everything worked! */
+ dev->disable = nic_disable;
+ nic->poll = nic_poll;
+ nic->transmit = nic_transmit;
+
+ return 1;
+}
+
+/**************************************************************************
+PROBE - Look for an adapter, this routine's visible to the outside
+***************************************************************************/
+static int nic_probe(struct dev *dev, unsigned short *dummy __unused)
+{
+ EFI_NETWORK_INTERFACE_IDENTIFIER_INTERFACE *nii;
+ int index;
+ int result;
+
+ index = dev->index+ 1;
+ if (dev->how_probe == PROBE_AWAKE) {
+ index--;
+ }
+ for(result = 0; !result && (nii = lookup_efi_nic(index)); index++) {
+ result = nic_setup(dev, nii);
+ if (result) {
+ break;
+ }
+ }
+ dev->index = result ? index : -1;
+ return result;
+}
+
+
+
+
+static struct isa_driver nic_driver __isa_driver = {
+ .type = NIC_DRIVER,
+ .name = "undi_nii",
+ .probe = nic_probe,
+ .ioaddrs = 0,
+};
diff --git a/src/arch/ia64/include/bits/byteswap.h b/src/arch/ia64/include/bits/byteswap.h
new file mode 100644
index 00000000..a8a11585
--- /dev/null
+++ b/src/arch/ia64/include/bits/byteswap.h
@@ -0,0 +1,36 @@
+#ifndef ETHERBOOT_BITS_BYTESWAP_H
+#define ETHERBOOT_BITS_BYTESWAP_H
+
+static inline uint64_t __ia64_bswap_64(uint64_t x)
+{
+ uint64_t result;
+ __asm__ volatile(
+ "mux1 %0=%1,@rev" :
+ "=r" (result)
+ : "r" (x));
+ return result;
+}
+
+#define __bswap_constant_16(x) \
+ ((uint16_t)((((uint16_t)(x) & 0x00ff) << 8) | \
+ (((uint16_t)(x) & 0xff00) >> 8)))
+
+#define __bswap_constant_32(x) \
+ ((uint32_t)((((uint32_t)(x) & 0x000000ffU) << 24) | \
+ (((uint32_t)(x) & 0x0000ff00U) << 8) | \
+ (((uint32_t)(x) & 0x00ff0000U) >> 8) | \
+ (((uint32_t)(x) & 0xff000000U) >> 24)))
+
+#define __bswap_16(x) \
+ (__builtin_constant_p(x) ? \
+ __bswap_constant_16(x) : \
+ (__ia64_bswap_64(x) >> 48))
+
+
+#define __bswap_32(x) \
+ (__builtin_constant_p(x) ? \
+ __bswap_constant_32(x) : \
+ (__ia64_bswap_64(x) >> 32))
+
+
+#endif /* ETHERBOOT_BITS_BYTESWAP_H */
diff --git a/src/arch/ia64/include/bits/cpu.h b/src/arch/ia64/include/bits/cpu.h
new file mode 100644
index 00000000..d8fe1cbb
--- /dev/null
+++ b/src/arch/ia64/include/bits/cpu.h
@@ -0,0 +1,6 @@
+#ifndef IA64_BITS_CPU_H
+#define IA64_BITS_CPU_H
+
+#define cpu_setup() do {} while(0)
+
+#endif /* IA64_BITS_CPU_H */
diff --git a/src/arch/ia64/include/bits/elf.h b/src/arch/ia64/include/bits/elf.h
new file mode 100644
index 00000000..c68f8456
--- /dev/null
+++ b/src/arch/ia64/include/bits/elf.h
@@ -0,0 +1,11 @@
+#ifndef IA64_BITS_ELF_H
+#define IA64_BITS_ELF_H
+
+/* ELF Defines for the current architecture */
+#define EM_CURRENT EM_IA_64
+#define ELFDATA_CURRENT ELFDATA2LSB
+
+#define ELF_CHECK_ARCH(x) \
+ ((x).e_machine == EM_CURRENT)
+
+#endif /* IA64_BITS_ELF_H */
diff --git a/src/arch/ia64/include/bits/endian.h b/src/arch/ia64/include/bits/endian.h
new file mode 100644
index 00000000..413e702d
--- /dev/null
+++ b/src/arch/ia64/include/bits/endian.h
@@ -0,0 +1,6 @@
+#ifndef ETHERBOOT_BITS_ENDIAN_H
+#define ETHERBOOT_BITS_ENDIAN_H
+
+#define __BYTE_ORDER __LITTLE_ENDIAN
+
+#endif /* ETHERBOOT_BITS_ENDIAN_H */
diff --git a/src/arch/ia64/include/bits/string.h b/src/arch/ia64/include/bits/string.h
new file mode 100644
index 00000000..31e94b7d
--- /dev/null
+++ b/src/arch/ia64/include/bits/string.h
@@ -0,0 +1,6 @@
+#ifndef ETHERBOOT_BITS_STRING_H
+#define ETHERBOOT_BITS_STRING_H
+
+/* define inline optimized string functions here */
+
+#endif /* ETHERBOOT_BITS_STRING_H */
diff --git a/src/arch/ia64/include/hooks.h b/src/arch/ia64/include/hooks.h
new file mode 100644
index 00000000..d8f1f06a
--- /dev/null
+++ b/src/arch/ia64/include/hooks.h
@@ -0,0 +1,12 @@
+#ifndef ETHERBOOT_IA64_HOOKS_H
+#define ETHERBOOT_IA64_HOOKS_H
+
+#include <stdarg.h>
+
+void arch_main(in_call_data_t *data, va_list params);
+void arch_on_exit(int status);
+void arch_relocate_to(unsigned long addr);
+#define arch_relocated_from(old_addr) do {} while(0)
+
+
+#endif /* ETHERBOOT_IA64_HOOKS_H */
diff --git a/src/arch/ia64/include/io.h b/src/arch/ia64/include/io.h
new file mode 100644
index 00000000..be5a5ce1
--- /dev/null
+++ b/src/arch/ia64/include/io.h
@@ -0,0 +1,228 @@
+#ifndef ETHERBOOT_IO_H
+#define ETHERBOOT_IO_H
+
+/* 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);
+}
+
+static inline void *phys_to_virt(unsigned long phys_addr)
+{
+ return (void *)(phys_addr);
+}
+
+/* virt_to_bus converts an addresss inside of etherboot [_start, _end]
+ * into a memory address cards can use.
+ */
+#define virt_to_bus virt_to_phys
+
+
+/* bus_to_virt reverses virt_to_bus, the address must be output
+ * from virt_to_bus to be valid. This function does not work on
+ * all bus addresses.
+ */
+#define bus_to_virt phys_to_virt
+
+/* ioremap converts a random 32bit bus address into something
+ * etherboot can access.
+ */
+static inline void *ioremap(unsigned long bus_addr, unsigned long length __unused)
+{
+ return bus_to_virt(bus_addr);
+}
+
+/* iounmap cleans up anything ioremap had to setup */
+static inline void iounmap(void *virt_addr __unused)
+{
+ return;
+}
+
+/* In physical mode the offset of uncached pages */
+#define PHYS_BASE (0x8000000000000000UL)
+
+/* Memory mapped IO primitives, we avoid the cache... */
+static inline uint8_t readb(unsigned long addr)
+{
+ return *((volatile uint8_t *)(PHYS_BASE | addr));
+}
+
+static inline uint16_t readw(unsigned long addr)
+{
+ return *((volatile uint16_t *)(PHYS_BASE | addr));
+}
+
+static inline uint32_t readl(unsigned long addr)
+{
+ return *((volatile uint32_t *)(PHYS_BASE | addr));
+}
+
+static inline uint64_t readq(unsigned long addr)
+{
+ return *((volatile uint64_t *)(PHYS_BASE | addr));
+}
+
+
+static inline void writeb(uint8_t val, unsigned long addr)
+{
+ *((volatile uint8_t *)(PHYS_BASE | addr)) = val;
+}
+
+static inline void writew(uint16_t val, unsigned long addr)
+{
+ *((volatile uint16_t *)(PHYS_BASE | addr)) = val;
+}
+
+static inline void writel(uint32_t val, unsigned long addr)
+{
+ *((volatile uint32_t *)(PHYS_BASE | addr)) = val;
+}
+
+static inline void writeq(uint64_t val, unsigned long addr)
+{
+ *((volatile uint64_t *)(PHYS_BASE | addr)) = val;
+}
+
+
+static inline void memcpy_fromio(void *dest, unsigned long src, size_t n)
+{
+ size_t i;
+ uint8_t *dp = dest;
+ for(i = 0; i < n; i++) {
+ *dp = readb(src);
+ dp++;
+ src++;
+ }
+}
+
+static inline void memcpy_toio(unsigned long dest , const void *src, size_t n)
+{
+ size_t i;
+ const uint8_t *sp = src;
+ for(i = 0; i < n; i++) {
+ writeb(*sp, dest);
+ sp++;
+ dest++;
+ }
+}
+
+/* IO space IO primitives, Itanium has a strange architectural mapping... */
+extern unsigned long io_base;
+#define __ia64_mf_a() __asm__ __volatile__ ("mf.a" ::: "memory")
+#define __ia64_io_addr(port) ((void *)(PHYS_BASE | io_base | (((port) >> 2) << 12) | ((port) & 0xfff)))
+
+static inline uint8_t inb(unsigned long port)
+{
+ uint8_t result;
+
+ result = *((volatile uint8_t *)__ia64_io_addr(port));
+ __ia64_mf_a();
+ return result;
+}
+
+static inline uint16_t inw(unsigned long port)
+{
+ uint8_t result;
+ result = *((volatile uint16_t *)__ia64_io_addr(port));
+ __ia64_mf_a();
+ return result;
+}
+
+static inline uint32_t inl(unsigned long port)
+{
+ uint32_t result;
+ result = *((volatile uint32_t *)__ia64_io_addr(port));
+ __ia64_mf_a();
+ return result;
+}
+
+static inline void outb(uint8_t val, unsigned long port)
+{
+ *((volatile uint8_t *)__ia64_io_addr(port)) = val;
+ __ia64_mf_a();
+}
+
+static inline void outw(uint16_t val, unsigned long port)
+{
+ *((volatile uint16_t *)__ia64_io_addr(port)) = val;
+ __ia64_mf_a();
+}
+
+static inline void outl(uint32_t val, unsigned long port)
+{
+ *((volatile uint32_t *)__ia64_io_addr(port)) = val;
+ __ia64_mf_a();
+}
+
+
+
+static inline void insb(unsigned long port, void *dst, unsigned long count)
+{
+ volatile uint8_t *addr = __ia64_io_addr(port);
+ uint8_t *dp = dst;
+ __ia64_mf_a();
+ while(count--)
+ *dp++ = *addr;
+ __ia64_mf_a();
+}
+
+static inline void insw(unsigned long port, void *dst, unsigned long count)
+{
+ volatile uint16_t *addr = __ia64_io_addr(port);
+ uint16_t *dp = dst;
+ __ia64_mf_a();
+ while(count--)
+ *dp++ = *addr;
+ __ia64_mf_a();
+}
+
+static inline void insl(unsigned long port, void *dst, unsigned long count)
+{
+ volatile uint32_t *addr = __ia64_io_addr(port);
+ uint32_t *dp = dst;
+ __ia64_mf_a();
+ while(count--)
+ *dp++ = *addr;
+ __ia64_mf_a();
+}
+
+static inline void outsb(unsigned long port, void *src, unsigned long count)
+{
+ const uint8_t *sp = src;
+ volatile uint8_t *addr = __ia64_io_addr(port);
+
+ while (count--)
+ *addr = *sp++;
+ __ia64_mf_a();
+}
+
+static inline void outsw(unsigned long port, void *src, unsigned long count)
+{
+ const uint16_t *sp = src;
+ volatile uint16_t *addr = __ia64_io_addr(port);
+
+ while (count--)
+ *addr = *sp++;
+ __ia64_mf_a();
+}
+
+static inline void outsl(unsigned long port, void *src, unsigned long count)
+{
+ const uint32_t *sp = src;
+ volatile uint32_t *addr = __ia64_io_addr(port);
+
+ while (count--)
+ *addr = *sp++;
+ __ia64_mf_a();
+}
+
+static inline unsigned long ia64_get_kr0(void)
+{
+ unsigned long r;
+ asm volatile ("mov %0=ar.k0" : "=r"(r));
+ return r;
+}
+
+#endif /* ETHERBOOT_IO_H */
diff --git a/src/arch/ia64/include/latch.h b/src/arch/ia64/include/latch.h
new file mode 100644
index 00000000..87195b42
--- /dev/null
+++ b/src/arch/ia64/include/latch.h
@@ -0,0 +1,11 @@
+#ifndef LATCH_H
+#define LATCH_H
+
+#define TICKS_PER_SEC (1000UL)
+
+/* Fixed timer interval used for calibrating a more precise timer */
+#define LATCHES_PER_SEC 10
+
+void sleep_latch(void);
+
+#endif /* LATCH_H */
diff --git a/src/arch/ia64/include/limits.h b/src/arch/ia64/include/limits.h
new file mode 100644
index 00000000..0c6f21f9
--- /dev/null
+++ b/src/arch/ia64/include/limits.h
@@ -0,0 +1,57 @@
+#ifndef LIMITS_H
+#define LIMITS_H 1
+
+/* Number of bits in a `char' */
+#define CHAR_BIT 8
+
+/* Minimum and maximum values a `signed char' can hold */
+#define SCHAR_MIN (-128)
+#define SCHAR_MAX 127
+
+/* Maximum value an `unsigned char' can hold. (Minimum is 0.) */
+#define UCHAR_MAX 255
+
+/* Minimum and maximum values a `char' can hold */
+#define CHAR_MIN SCHAR_MIN
+#define CHAR_MAX SCHAR_MAX
+
+/* Minimum and maximum values a `signed short int' can hold */
+#define SHRT_MIN (-32768)
+#define SHRT_MAX 32767
+
+/* Maximum value an `unsigned short' can hold. (Minimum is 0.) */
+#define USHRT_MAX 65535
+
+
+/* Minimum and maximum values a `signed int' can hold */
+#define INT_MIN (-INT_MAX - 1)
+#define INT_MAX 2147483647
+
+/* Maximum value an `unsigned int' can hold. (Minimum is 0.) */
+#define UINT_MAX 4294967295U
+
+
+/* Minimum and maximum values a `signed int' can hold */
+#define INT_MIN (-INT_MAX - 1)
+#define INT_MAX 2147483647
+
+/* Maximum value an `unsigned int' can hold. (Minimum is 0.) */
+#define UINT_MAX 4294967295U
+
+/* Minimum and maximum values a `signed long' can hold */
+#define LONG_MAX 9223372036854775807L
+#define LONG_MIN (-LONG_MAX - 1L)
+
+/* Maximum value an `unsigned long' can hold. (Minimum is 0.) */
+#define ULONG_MAX 18446744073709551615UL
+
+/* Minimum and maximum values a `signed long long' can hold */
+#define LLONG_MAX 9223372036854775807LL
+#define LLONG_MIN (-LONG_MAX - 1LL)
+
+
+/* Maximum value an `unsigned long long' can hold. (Minimum is 0.) */
+#define ULLONG_MAX 18446744073709551615ULL
+
+
+#endif /* LIMITS_H */
diff --git a/src/arch/ia64/include/pal.h b/src/arch/ia64/include/pal.h
new file mode 100644
index 00000000..6cda19d1
--- /dev/null
+++ b/src/arch/ia64/include/pal.h
@@ -0,0 +1,11 @@
+#ifndef IA64_PAL_H
+#define IA64_PAL_H
+
+struct pal_freq_ratio {
+ unsigned long den : 32, num : 32; /* numerator & denominator */
+};
+extern long pal_freq_ratios(struct pal_freq_ratio *proc_ratio,
+ struct pal_freq_ratio *bus_ratio, struct pal_freq_ratio *itc_ratio);
+
+
+#endif /* IA64_PAL_H */
diff --git a/src/arch/ia64/include/sal.h b/src/arch/ia64/include/sal.h
new file mode 100644
index 00000000..7a1b57ec
--- /dev/null
+++ b/src/arch/ia64/include/sal.h
@@ -0,0 +1,29 @@
+#ifndef IA64_SAL_H
+#define IA64_SAL_H
+
+struct fptr {
+ unsigned long entry;
+ unsigned long gp;
+};
+extern struct fptr sal_entry;
+extern struct fptr pal_entry;
+extern int parse_sal_system_table(void *table);
+
+#define SAL_FREQ_BASE_PLATFORM 0
+#define SAL_FREQ_BASE_INTERVAL_TIMER 1
+#define SAL_FREQ_BASE_REALTIME_CLOCK 2
+
+long sal_freq_base (unsigned long which, unsigned long *ticks_per_second,
+ unsigned long *drift_info);
+
+#define PCI_SAL_ADDRESS(seg, bus, dev, fn, reg) \
+ ((unsigned long)(seg << 24) | (unsigned long)(bus << 16) | \
+ (unsigned long)(dev << 11) | (unsigned long)(fn << 8) | \
+ (unsigned long)(reg))
+
+long sal_pci_config_read (
+ unsigned long pci_config_addr, unsigned long size, unsigned long *value);
+long sal_pci_config_write (
+ unsigned long pci_config_addr, unsigned long size, unsigned long value);
+
+#endif /* IA64_SAL_H */
diff --git a/src/arch/ia64/include/setjmp.h b/src/arch/ia64/include/setjmp.h
new file mode 100644
index 00000000..a1fac2dc
--- /dev/null
+++ b/src/arch/ia64/include/setjmp.h
@@ -0,0 +1,13 @@
+#ifndef ETHERBOOT_SETJMP_H
+#define ETHERBOOT_SETJMP_H
+
+
+/* Define a type for use by setjmp and longjmp */
+#define JBLEN 70
+
+typedef long jmp_buf[JBLEN] __attribute__ ((aligned (16))); /* guarantees 128-bit alignment! */
+
+extern int setjmp (jmp_buf env);
+extern void longjmp (jmp_buf env, int val);
+
+#endif /* ETHERBOOT_SETJMP_H */
diff --git a/src/arch/ia64/include/stdint.h b/src/arch/ia64/include/stdint.h
new file mode 100644
index 00000000..2f9c592c
--- /dev/null
+++ b/src/arch/ia64/include/stdint.h
@@ -0,0 +1,16 @@
+#ifndef STDINT_H
+#define STDINT_H
+
+typedef unsigned long size_t;
+
+typedef unsigned char uint8_t;
+typedef unsigned short uint16_t;
+typedef unsigned int uint32_t;
+typedef unsigned long uint64_t;
+
+typedef signed char int8_t;
+typedef signed short int16_t;
+typedef signed int int32_t;
+typedef signed long int64_t;
+
+#endif /* STDINT_H */
diff --git a/src/arch/ia64/prefix/apply_efi_prefix.pl b/src/arch/ia64/prefix/apply_efi_prefix.pl
new file mode 100755
index 00000000..999c43b4
--- /dev/null
+++ b/src/arch/ia64/prefix/apply_efi_prefix.pl
@@ -0,0 +1,63 @@
+#!/usr/bin/perl -w
+#
+# Program to apply an efi header to an ia64 etherboot file.
+#
+# GPL Eric Biederman 2002
+#
+
+use strict;
+
+use bytes;
+
+main(@ARGV);
+
+sub usage
+{
+ my ($err) = @_;
+ print STDERR $err , "\n";
+ die "Usage $0 prrefix file bss_size\n";
+}
+sub main
+{
+ my ($prefix_name, $suffix_name, $bss_size) = @_;
+ usage("No prefix") unless (defined($prefix_name));
+ usage("No suffix") unless (defined($suffix_name));
+ usage("No bss size") unless (defined($bss_size));
+
+ open(PREFIX, "<$prefix_name") or die "Cannot open $prefix_name";
+ open(SUFFIX, "<$suffix_name") or die "Cannot open $suffix_name";
+
+ $/ = undef;
+ my $prefix = <PREFIX>; close(PREFIX);
+ my $suffix = <SUFFIX>; close(SUFFIX);
+
+ # Payload sizes.
+ my $payload_size = length($suffix);
+ my $payload_bss = $bss_size;
+
+ # Update the image size
+ my $hdr_off = unpack("V",substr($prefix, 0x3c, 4));
+ my $image_size_off = 0x050 + $hdr_off;
+ my $img_mem_size_off = 0x0c0 + $hdr_off;
+ my $img_size_off = 0x0c8 + $hdr_off;
+
+ my $image_size = unpack("V", substr($prefix, $image_size_off, 4));
+ my $img_mem_size = unpack("V", substr($prefix, $img_mem_size_off, 4));
+ my $img_size = unpack("V", substr($prefix, $img_size_off, 4));
+
+ $image_size += $payload_size + $payload_bss;
+ $img_mem_size += $payload_size + $payload_bss;
+ $img_size += $payload_size;
+
+ substr($prefix, $image_size_off, 4) = pack("V", $image_size);
+ substr($prefix, $img_mem_size_off, 4) = pack("V", $img_mem_size);
+ substr($prefix, $img_size_off, 4) = pack("V", $img_size);
+
+ #print(STDERR "image_size: $image_size\n");
+ #print(STDERR "img_mem_size: $img_mem_size\n");
+ #print(STDERR "img_size: $img_size\n");
+
+ print $prefix;
+ print $suffix;
+}
+
diff --git a/src/arch/ia64/prefix/apply_unnrv2b_prefix.pl b/src/arch/ia64/prefix/apply_unnrv2b_prefix.pl
new file mode 100644
index 00000000..71b2b247
--- /dev/null
+++ b/src/arch/ia64/prefix/apply_unnrv2b_prefix.pl
@@ -0,0 +1,198 @@
+#!/usr/bin/perl -w
+#
+# Program to apply an unnrv2b decompressor header to an ia64 etherboot file.
+#
+# GPL Eric Biederman 2002
+#
+
+use strict;
+
+use bytes;
+
+main(@ARGV);
+
+sub usage
+{
+ my ($err) = @_;
+ print STDERR $err , "\n";
+ die "Usage $0 prefix file\n";
+}
+
+sub getbits
+{
+ my ($bundle, $start, $size) = @_;
+
+ # Compute the mask
+ my $mask = 0xffffffff;
+ $mask = $mask >> (32 - $size);
+
+ # Compute the substring, and shift
+ my ($first, $end, $count, $shift);
+ $first = int($start / 8);
+ $end = int(($start + $size + 7)/8);
+ $count = $end - $first;
+ $shift = $start % 8;
+
+ # Compute the unpack type
+ my $type;
+ if ($count == 1) {
+ $type = "C"
+ }
+ elsif ($count == 2) {
+ $type = "v";
+ }
+ elsif (($count >= 3) && ($count <= 4)) {
+ $type = "V";
+ }
+ else {
+ die "bad count $count";
+ }
+
+ # Now compute the value
+ my $val = (unpack($type, substr($bundle, $first, $count)) >> $shift) & $mask;
+
+ # Now return the value
+ return $val;
+}
+
+sub putbits
+{
+ my ($bundle, $start, $size, $val) = @_;
+
+
+ # Compute the mask
+ my $mask = 0xffffffff;
+ $mask >>= 32 - $size;
+
+ # Compute the substring, and shift
+ my ($first, $end, $count, $shift);
+ $first = int($start / 8);
+ $end = int(($start + $size + 7)/8);
+ $count = $end - $first;
+ $shift = $start % 8;
+
+ # Compute the unpack type
+ my $type;
+ if ($count == 1) {
+ $type = "C"
+ }
+ elsif ($count == 2) {
+ $type = "v";
+ }
+ elsif (($count >= 3) && ($count <= 4)) {
+ $type = "V";
+ }
+ else {
+ die "bad count $count";
+ }
+
+ # Adjust the mask
+ $mask <<= $shift;
+
+ # Now set the value, preserving the untouched bits
+ substr($bundle, $first, $count) =
+ pack($type,
+ ((unpack($type, substr($bundle, $first, $count)) & ~$mask) |
+ (($val << $shift) & $mask)));
+
+ # Now return the new value;
+ return $bundle;
+}
+
+sub main
+{
+ my ($prefix_name, $suffix_name) = @_;
+ usage("No prefix") unless (defined($prefix_name));
+ usage("No suffix") unless (defined($suffix_name));
+
+ open(PREFIX, "<$prefix_name") or die "Cannot open $prefix_name";
+ open(SUFFIX, "<$suffix_name") or die "Cannot open $suffix_name";
+
+ $/ = undef;
+ my $prefix = <PREFIX>; close(PREFIX);
+ my $suffix = <SUFFIX>; close(SUFFIX);
+
+ # Payload sizes
+ my $prefix_len = length($prefix);
+ my $suffix_len = length($suffix);
+ my $payload_size = $suffix_len;
+ my $uncompressed_offset = ($prefix_len + $suffix_len + 15) & ~15;
+ my $pad = $uncompressed_offset - ($prefix_len + $suffix_len);
+
+ # Itaninum instruction bundle we will be updating
+ # 0 - 4 template == 5
+ # 5 - 45 slot 0 M-Unit
+ # 46 - 86 slot 1 L-Unit
+ # 87 - 127 slot 2 X-Unit
+ # Itaninum instruction format
+ # 40 - 37 Major opcode
+ # ...
+ #
+
+ # slot 1
+ # 0 - 40 [41] imm-41
+ # 10 - 40 [31] imm-41-hi
+ # 0 - 9 [10] imm-41-lo
+
+ # slot 2
+ # 0 - 5 [6] qp
+ # 6 - 12 [7] r1
+ # 13 - 19 [7] imm-7b
+ # 20 [1] vc
+ # 21 [1] immc
+ # 22 - 26 [5] imm-5c
+ # 27 - 35 [9] imm-9d
+ # 36 [1] imm0
+ # 37 - 40 [4] major opcode
+ #
+
+ # major opcode should be 6
+
+ # Update the image size
+ my $uncompressed_offset_bundle_off = 16;
+ my $bundle = substr($prefix, $uncompressed_offset_bundle_off, 16);
+
+ my $template = getbits($bundle, 0, 5);
+ my $op1_base = 46;
+ my $op2_base = 87;
+ my $major_opcode = getbits($bundle, 37 + $op2_base, 4);
+
+ if (($template != 5) ||
+ ($major_opcode != 6)) {
+ die "unknown second bundle cannot patch";
+ }
+
+ die "uncompressed_offset to big!\n" if ($uncompressed_offset > 0xffffffff);
+ my $immhi = 0;
+ my $immlo = $uncompressed_offset;
+
+ my $imm0 = ($immhi >> 31) & ((1 << 1) - 1);
+ my $imm41_hi = ($immhi >> 0) & ((1 << 31) - 1);
+
+ my $imm41_lo = ($immlo >> 22) & ((1 << 10) - 1);
+ my $immc = ($immlo >> 21) & ((1 << 1) - 1);
+ my $imm5c = ($immlo >> 16) & ((1 << 5) - 1);
+ my $imm9d = ($immlo >> 7) & ((1 << 9) - 1);
+ my $imm7b = ($immlo >> 0) & ((1 << 7) - 1);
+
+ $bundle = putbits($bundle, 10 + $op1_base, 31, $imm41_hi);
+ $bundle = putbits($bundle, 0 + $op1_base, 10, $imm41_lo);
+ $bundle = putbits($bundle, 36 + $op2_base, 1 , $imm0);
+ $bundle = putbits($bundle, 27 + $op2_base, 9 , $imm9d);
+ $bundle = putbits($bundle, 22 + $op2_base, 5 , $imm5c);
+ $bundle = putbits($bundle, 21 + $op2_base, 1 , $immc);
+ $bundle = putbits($bundle, 13 + $op2_base, 7 , $imm7b);
+
+ substr($prefix, $uncompressed_offset_bundle_off, 16) = $bundle;
+
+ #print (STDERR "prefix: $prefix_len\n");
+ #print (STDERR "suffix: $suffix_len\n");
+ #print (STDERR "pad: $pad\n");
+ #print (STDERR "uncompressed_offset: $uncompressed_offset\n");
+
+ print $prefix;
+ print $suffix;
+ # Pad the resulting image by a few extra bytes...
+ print pack("C", 0) x $pad;
+}
+
diff --git a/src/arch/ia64/prefix/efi_prefix.S b/src/arch/ia64/prefix/efi_prefix.S
new file mode 100644
index 00000000..1fb1501f
--- /dev/null
+++ b/src/arch/ia64/prefix/efi_prefix.S
@@ -0,0 +1,195 @@
+#include "elf.h"
+ .explicit
+
+ .section ".hdrs", "a"
+ /* First the DOS file header */
+
+
+dos_header:
+ .byte 'M', 'Z' /* Signature */
+ .short dos_program_end - dos_program /* Length of image mod 512 bytes*/
+ .short 0x0001 /* Length of image in 512 byte pages */ /* FIXME */
+ .short 0x0000 /* Number of relocation items following header */
+ .short (dos_header_end - dos_header)/16 /* Size of header in 16 byte paragraphs */
+ .short 0x0001 /* Minimum number of paragraphs needed to run image */
+ .short 0x0001 /* Maximum number of paragraphs program would like */
+ .short 0x0000 /* Initial SS */
+
+ .short 0x00b8 /* Initial SP */
+ .short 0x0000 /* Negative checksum of image */
+ .short 0x0000 /* Initial IP */
+ .short 0x0000 /* Initial CS */
+ .short 0x0010 /* Offset in EXEC of first relocation item */
+ .short 0x0000 /* Overlay number */
+
+ .balign 16
+dos_header_end:
+dos_program:
+ .byte 0xdb /* retf */
+ .balign 16
+dos_program_end:
+ .org 0x3c
+ .byte pe_signature - dos_header, 0x00, 0x00, 0x00
+ .org 0x40 /* NOTE: set this to 0x80 for debugging, 0x40 otherwise */
+pe_signature:
+ .byte 'P', 'E', 0, 0
+coff_header:
+#define IMAGE_MACHINE_IA64 0x0200
+coff_machine: .short IMAGE_MACHINE_IA64
+coff_nsections: .short (section_headers_end - section_headers)/40
+coff_timdat: .int 1038168747 /* Sun Nov 24 12:12:27 2002 */
+coff_symptr: .int 0x00000000
+
+coff_nsyms: .int 0x00000000
+coff_opthdr: .short pe_end - pe_header
+#define CF_RELOC_STRIPPED 0x0001
+#define CF_EXECUTABLE 0x0002
+#define CF_LINE_STRIPPED 0x0004
+#define CF_LOCAL_STRIPPED 0x0008
+#define CF_DEBUG_STRIPPED 0x0206
+coff_flags: .short CF_EXECUTABLE | CF_LINE_STRIPPED | CF_LOCAL_STRIPPED | CF_DEBUG_STRIPPED
+
+ /* Option header */
+pe_header:
+pe_magic: .short 0x020b /* 020b or 010b? */
+pe_linker: .byte 0x02, 0x38
+pe_text_size: .int _text_size
+pe_data_size: .int _data_size
+pe_bss_size: .int _bss_size
+pe_entry: .int _start_plabel_rva
+pe_text_base: .int _text_rva
+pe_image_base: .quad _image_base
+pe_sec_align: .int _sect_align
+pe_file_align: .int _file_align
+pe_os_major: .short 0
+pe_os_minor: .short 0
+pe_image_major: .short 0
+pe_image_minro: .short 0
+pe_sub_major: .short 0
+pe_sub_minor: .short 0
+pe_reserved: .int 0
+pe_image_size: .int _image_size
+pe_hdrs_size: .int _hdrs_size
+pe_checksum: .int 0 /* FIXME how do I compute the checksum, unnecessary */
+#define SUBSYS_EFI_APP 10
+#define SUBSYS_EFI_BOOT_SERVICE_DRIVER 11
+#define SUBSYS_EFI_RUNTIME_DRIVER 12
+pe_subsys: .short SUBSYS_EFI_APP
+pe_dll_flags: .short 0
+pe_stack_res: .quad 0
+pe_stack_commit:.quad 0
+pe_heap_res: .quad 0
+pe_heap_commit: .quad 0
+pe_ld_flags: .int 0
+pe_rvas: .int (rvas_end - rvas_start)/8
+
+rvas_start:
+rva_0_rva: .int 0
+rva_0_size: .int 0
+rva_1_rva: .int 0
+rva_1_size: .int 0
+rva_2_rva: .int 0
+rva_2_size: .int 0
+rva_3_rva: .int 0
+rva_3_size: .int 0
+rva_4_rva: .int 0
+rva_4_size: .int 0
+rva_5_rva: .int _reloc_rva
+rva_5_size: .int __reloc_size
+rvas_end:
+pe_end:
+
+section_headers:
+#define SCN_CNT_CODE 0x00000020
+#define SCN_CNT_INITIALIZED_DATA 0x00000040
+#define SCN_CNT_UNINITIALIZED_DATA 0x00000080
+#define SCN_MEM_DISCARDABLE 0x02000000
+#define SCN_MEM_SHARED 0x10000000
+#define SCN_MEM_EXECUTE 0x20000000
+#define SCN_MEM_READ 0x40000000
+#define SCN_MEM_WRITE 0x80000000
+
+sec1_name: .byte '.', 'i', 'm', 'g', 0 , 0, 0, 0
+sec1_virt_size: .int _img_mem_size
+sec1_virt_addr: .int _img_rva
+sec1_file_size: .int _img_size
+sec1_file_off: .int _img_off
+sec1_reloc_off: .int 0
+sec1_line_off: .int 0
+sec1_reloc_cnt: .short 0
+sec1_line_cnt: .short 0
+sec1_flags: .int SCN_CNT_CODE | SCN_CNT_INITIALIZED_DATA \
+ | SCN_MEM_EXECUTE | SCN_MEM_READ | SCN_MEM_WRITE
+
+section_headers_end:
+
+ .text
+ .psr abi64
+ .psr lsb
+ .global _start
+_start:
+ {
+ alloc r8=ar.pfs,2,0,0,0
+ mov gp=ip /* Get the address of _start/_text/__gp */
+ }
+ ;;
+ add r14=@gprel(n1_desc),gp
+ add r15=@gprel(n2_desc),gp
+ add r16=@gprel(bhdr),gp
+ ;;
+ st8 [r14]=in0
+ st8 [r15]=in1
+ ;;
+ mov ar.pfs=r8
+ ;;
+ mov r32=r16
+ ;;
+ br.sptk.few _payload_start
+
+ .data
+ .global _start_plabel
+ .balign 16
+_start_plabel:
+ .quad _start
+ .quad 0 /* I don't need a gp value... */
+
+ /* hand-crafted bhdr and parameters */
+ .balign 16
+bhdr:
+b_signature: .int 0x0E1FB007
+b_size: .int bhdr_end - bhdr
+b_checksum: .short 0
+b_records: .short 3
+ /* A NOP note to 64bit align later data */
+ .balign 4
+n0_namesz: .int 0
+n0_descsz: .int 0
+n0_type: .int EBN_NOP
+ .balign 4
+n1_namesz: .int 10
+n1_descsz: .int 8
+n1_type: .int EB_IA64_IMAGE_HANDLE
+n1_name: .asciz "Etherboot"
+ .balign 4
+n1_desc: .quad 0
+ .balign 4
+n2_namesz: .int 10
+n2_descsz: .int 8
+n2_type: .int EB_IA64_SYSTAB
+n2_name: .asciz "Etherboot"
+ .balign 4
+n2_desc: .quad 0
+bhdr_end:
+
+
+ /* hand-craft a .reloc section for the plabel */
+#define IMAGE_REL_BASED_ABS 0
+#define IMAGE_REL_BASED_DIR64 10
+
+ .section ".reloc", "a"
+ .int _start_plabel_rva // PAGE RVA
+ .int 12 // Block Size (2*4+2*2)
+ .short (IMAGE_REL_BASED_DIR64<<12) + 0 // reloc for plabel's entry point
+ .short (IMAGE_REL_BASED_ABS <<12) + 0 // dummy reloc for good alignment
+
+
diff --git a/src/arch/ia64/prefix/efi_prefix.lds b/src/arch/ia64/prefix/efi_prefix.lds
new file mode 100644
index 00000000..aa71ea2e
--- /dev/null
+++ b/src/arch/ia64/prefix/efi_prefix.lds
@@ -0,0 +1,91 @@
+/* OUTPUT_FORMAT("binary") */
+OUTPUT_FORMAT("elf64-ia64-little")
+
+OUTPUT_ARCH(ia64)
+
+ENTRY(_start_plabel)
+_sect_align = 16; /* normally 512 */
+_file_align = 16; /* normally 4096 */
+/* Symbols for hardcoding the payload and _bss size, with apply_efi_prefix
+ * there is no need to set these, and in will get confused if these are not 0.
+ */
+_payload_size = 0;
+_payload_bss = 0;
+SECTIONS {
+ /* We can arbitrarily set image base to anything we want,
+ * but efi does not honor it, so it is a pointless exercise.
+ * So we just set the start address to 0.
+ */
+ . = 0;
+ _link_base = . ;
+ _image_base = . ;
+ .hdrs : {
+ _hdrs = . ;
+ *(.hdrs)
+ . = ALIGN(_file_align) ;
+ _ehdrs = . ;
+ }
+ . = ALIGN(_sect_align);
+ .img : {
+ _img = . ;
+ _text = . ;
+ __gp = . ;
+ *(.text)
+ _etext = .;
+ . = ALIGN(16);
+ _data = . ;
+ *(.data)
+ _edata = .;
+ . = ALIGN(16);
+ _reloc = . ;
+ *(.reloc)
+ __ereloc = . ;
+ _ereloc = . ;
+ . = ALIGN(16);
+ /* . = ALIGN(_file_align) ; */
+ }
+ _payload_start = . ;
+ . = . + _payload_size ;
+ _payload_end = . ;
+ _eimg = . ;
+ . = ALIGN(_sect_align) ;
+ _bss = . ;
+ .bss : {
+ *(.bss)
+ . = . + _payload_bss;
+ }
+ _ebss = . ;
+ _end = . ;
+ /DISCARD/ : {
+ *(*)
+ }
+
+ _hdrs_size = _ehdrs - _hdrs;
+ _hdrs_off = 0;
+
+ _text_size = _etext - _text ;
+ _text_rva = _text - _image_base ;
+ _text_off = _text - _link_base;
+
+ _data_size = _edata - _data ;
+ _data_rva = _data - _image_base;
+ _data_off = _data - _link_base;
+
+ __reloc_size = __ereloc - _reloc ;
+ _reloc_size = _ereloc - _reloc ;
+ _reloc_rva = _reloc - _image_base;
+ _reloc_off = _reloc - _link_base;
+
+ _bss_size = _ebss - _bss;
+ _bss_rva = _bss - _image_base;
+
+ _img_size = _eimg - _img ;
+ _img_rva = _img - _image_base;
+ _img_off = _img - _link_base;
+
+ _img_mem_size = _ebss - _img;
+
+ _image_size = _ebss - _link_base ;
+
+ _start_plabel_rva = _start_plabel - _image_base;
+}
diff --git a/src/arch/ia64/prefix/unnrv2b.S b/src/arch/ia64/prefix/unnrv2b.S
new file mode 100644
index 00000000..bd7013b2
--- /dev/null
+++ b/src/arch/ia64/prefix/unnrv2b.S
@@ -0,0 +1,196 @@
+/*
+ * Copyright (C) 1996-2002 Markus Franz Xaver Johannes Oberhumer
+ * Copyright (C) 2002 Eric Biederman
+ *
+ * This file is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * Originally this code was part of ucl the data compression library
+ * for upx the ``Ultimate Packer of eXecutables''.
+ *
+ * - Converted to gas assembly, and refitted to work with etherboot.
+ * Eric Biederman 20 Aug 2002
+ *
+ * - Converted to functional ia64 assembly (Can this get smaller?)
+ * Eric Biederman 5 Dec 2002
+ */
+ .text
+ .globl _start
+_start:
+ /* See where I am running, and compute gp */
+ {
+ /* Do no call alloc here as I do not know how many argument
+ * registers are being passed through the decompressor, and if I report
+ * to few the unreported registers may get stomped.
+ *
+ * Instead just explicitly get the value of ar.pfs.
+ */
+ mov r17=0
+ mov r8=ar.pfs
+ mov gp = ip /* The linker scripts sets gp at _start */
+
+ }
+ {.mlx
+ movl r9=0x123456789abcdef0 /* Get uncompressed_offset into r9 */
+ }
+ ;;
+ {
+ add r14 = @gprel(payload + 4),gp
+ add r15 = r9,gp
+ mov r16=1 /* last_m_off = 1 */
+ }
+ {
+ mov r20 = 0xd00
+ add r21 = r9,gp
+ br.sptk.few decompr_loop_n2b
+ }
+
+/* ------------- DECOMPRESSION -------------
+
+ Input:
+ r8 - ar.pfs
+ r14 - source
+ r15 - dest
+ r16 - 1
+ r17 - (buffer) 0
+ r20 - 0xd00 (constant)
+ r21 - start address
+ Usage:
+ r9 - scratch register for memory copies
+ r18 - scratch register for getbit
+ r19 - scratch register for loads and stores
+ Output:
+ r2 - 0
+ r3 - 0
+*/
+
+getbit:
+ add r18 = r17,r17
+ ;;
+ cmp.ne p8,p0 = r0,r18
+ cmp.leu p6,p7 = r18,r17
+ ;;
+ mov r17 = r18
+(p8) br.cond.sptk.few getbit_end
+ /* Do a unaligned 64bit load */
+ ;;
+ ld1 r17 = [r14],1
+ ;;
+ ld1 r18 = [r14],1
+ ;;
+ dep r17 = r18,r17,8,8
+ ld1 r18 = [r14],1
+ ;;
+ dep r17 = r18,r17,16,8
+ ld1 r18 = [r14],1
+ ;;
+ dep r17 = r18,r17,24,8
+ ld1 r18 = [r14],1
+ ;;
+ dep r17 = r18,r17,32,8
+ ld1 r18 = [r14],1
+ ;;
+ dep r17 = r18,r17,40,8
+ ld1 r18 = [r14],1
+ ;;
+ dep r17 = r18,r17,48,8
+ ld1 r18 = [r14],1
+ ;;
+ dep r17 = r18,r17,56,8
+ ;;
+ add r18 = r17,r17,1
+ ;;
+ cmp.leu p6,p7=r18,r17
+ ;;
+ mov r17=r18
+ ;;
+getbit_end:
+ br.ret.sptk.few b6
+
+
+decompr_literals_n2b:
+ ld1 r19 = [r14],1
+ ;;
+ st1 [r15] = r19,1
+ ;;
+decompr_loop_n2b:
+ br.call.sptk.few b6 = getbit
+ ;;
+(p6) br.cond.sptk.few decompr_literals_n2b
+(p7) add r2 = 1,r0 /* m_off = 1 */
+ ;;
+loop1_n2b:
+ br.call.sptk.few b6 = getbit
+ ;;
+(p6) add r2 = r2,r2,1 /* m_off = m_off*2 + getbit() */
+(p7) add r2 = r2,r2
+ br.call.sptk.few b6 = getbit
+ ;;
+(p7) br.cond.sptk.few loop1_n2b /* while(!getbit()) */
+ ;;
+ mov r3 = r0
+ cmp.eq p6,p0 = 2,r2
+ add r2 = -3,r2
+(p6) br.cond.sptk.few decompr_ebpeax_n2b /* if (m_off == 2) goto decompr_ebpeax_n2b ? */
+ ;;
+ ld1 r19 = [r14],1
+ shl r2 = r2,8
+ ;;
+ dep r2 = r19,r2,0,8 /* m_off = (m_off - 3)*256 + src[ilen++] */
+ ;;
+ cmp4.eq p6,p0 = -1,r2 /* if (m_off == 0xffffffff) goto decomp_end_n2b */
+ ;;
+(p6) br.cond.sptk.few decompr_end_n2b
+ mov r16 = r2 /* last_m_off = m_off */
+ ;;
+decompr_ebpeax_n2b:
+ br.call.sptk.few b6 = getbit
+ ;;
+(p6) add r3 = r3,r3,1 /* m_len = getbit() */
+(p7) add r3 = r3,r3
+ br.call.sptk.few b6 = getbit
+ ;;
+(p6) add r3 = r3,r3,1 /* m_len = m_len*2 + getbit()) */
+(p7) add r3 = r3,r3
+ ;;
+ cmp.ne p6,p0 = r0,r3
+(p6) br.cond.sptk.few decompr_got_mlen_n2b /* if (m_len == 0) goto decompr_got_mlen_n2b */
+ add r3 = 1,r3 /* m_len++ */
+ ;;
+loop2_n2b:
+ br.call.sptk.few b6 = getbit
+ ;;
+(p6) add r3 = r3,r3,1 /* m_len = m_len*2 + getbit() */
+(p7) add r3 = r3,r3
+ br.call.sptk.few b6 = getbit
+ ;;
+(p7) br.cond.sptk.few loop2_n2b /* while(!getbit()) */
+ add r3 = 2, r3 /* m_len += 2 */
+ ;;
+decompr_got_mlen_n2b:
+ cmp.gtu p6,p7 = r16, r20
+ ;;
+(p6) add r3 = 2, r3 /* m_len = m_len + 1 + (last_m_off > 0xd00) */
+(p7) add r3 = 1, r3
+ sub r9 = r15, r16,1 /* m_pos = dst + olen - last_m_off - 1 */
+ ;;
+1:
+ ld1 r19 = [r9],1
+ add r3 = -1,r3
+ ;;
+ st1 [r15] = r19,1 /* dst[olen++] = *m_pos++ while(m_len > 0) */
+ cmp.ne p6,p0 = r0,r3
+(p6) br.cond.sptk.few 1b
+ ;;
+ br.cond.sptk.few decompr_loop_n2b
+decompr_end_n2b:
+ /* Branch to the start address */
+ mov ar.pfs=r8
+ ;;
+ mov b6 = r21
+ ;;
+ br.sptk.few b6
+
+payload:
diff --git a/src/arch/ia64/prefix/unnrv2b.lds b/src/arch/ia64/prefix/unnrv2b.lds
new file mode 100644
index 00000000..728f7009
--- /dev/null
+++ b/src/arch/ia64/prefix/unnrv2b.lds
@@ -0,0 +1,28 @@
+OUTPUT_FORMAT("elf64-ia64-little")
+
+OUTPUT_ARCH(ia64)
+
+ENTRY(_start)
+SECTIONS {
+ . = 0;
+ __gp = .;
+ _text = . ;
+ .text : {
+ *(.text)
+ }
+ /DISCARD/ : {
+ *(.comment)
+ *(.note)
+ *(.hash)
+ *(.data)
+ *(.sbss)
+ *(.bss)
+ *(.dynstr)
+ *(.dynsym)
+ *(.IA_64.unwind)
+ *(.IA_64.unwind_info)
+ *(.IA64_unwind)
+ *(.IA64_unwind_info)
+ *(.dynamic)
+ }
+}