Choice of name core for directories is unfortunate as cvs import ignores it. v5.4.0
authorken_yap <ken_yap>
Mon, 4 Apr 2005 22:36:14 +0000 (22:36 +0000)
committerken_yap <ken_yap>
Mon, 4 Apr 2005 22:36:14 +0000 (22:36 +0000)
90 files changed:
src/arch/armnommu/core/arm_timer.c [new file with mode: 0644]
src/arch/armnommu/core/etherboot.lds [new file with mode: 0644]
src/arch/armnommu/core/lib1funcs.c [new file with mode: 0644]
src/arch/armnommu/core/mem.c [new file with mode: 0644]
src/arch/armnommu/core/raw_loader.c [new file with mode: 0644]
src/arch/armnommu/core/serial.c [new file with mode: 0644]
src/arch/armnommu/core/setjmp.S [new file with mode: 0644]
src/arch/armnommu/core/start.S [new file with mode: 0644]
src/arch/e1/core/coff_loader.c [new file with mode: 0644]
src/arch/e1/core/e132_xs.c [new file with mode: 0644]
src/arch/e1/core/e1_timer.c [new file with mode: 0644]
src/arch/e1/core/etherboot.lds [new file with mode: 0644]
src/arch/e1/core/longjmp.c [new file with mode: 0644]
src/arch/e1/core/memcmp.S [new file with mode: 0644]
src/arch/e1/core/memcpy.S [new file with mode: 0644]
src/arch/e1/core/memset.S [new file with mode: 0644]
src/arch/e1/core/setjmp.c [new file with mode: 0644]
src/arch/e1/core/start.S [new file with mode: 0644]
src/arch/e1/core/strcmp.S [new file with mode: 0644]
src/arch/i386/core/aout_loader.c [new file with mode: 0644]
src/arch/i386/core/callbacks.c [new file with mode: 0644]
src/arch/i386/core/cpu.c [new file with mode: 0644]
src/arch/i386/core/elf.c [new file with mode: 0644]
src/arch/i386/core/etherboot.lds [new file with mode: 0644]
src/arch/i386/core/etherboot.prefix.lds [new file with mode: 0644]
src/arch/i386/core/freebsd_loader.c [new file with mode: 0644]
src/arch/i386/core/hooks.c [new file with mode: 0644]
src/arch/i386/core/i386_timer.c [new file with mode: 0644]
src/arch/i386/core/init.S [new file with mode: 0644]
src/arch/i386/core/multiboot_loader.c [new file with mode: 0644]
src/arch/i386/core/pci_io.c [new file with mode: 0644]
src/arch/i386/core/pic8259.c [new file with mode: 0644]
src/arch/i386/core/prefixudata.lds [new file with mode: 0644]
src/arch/i386/core/prefixzdata.lds [new file with mode: 0644]
src/arch/i386/core/pxe_callbacks.c [new file with mode: 0644]
src/arch/i386/core/pxe_loader.c [new file with mode: 0644]
src/arch/i386/core/realmode.c [new file with mode: 0644]
src/arch/i386/core/realmode_asm.S [new file with mode: 0644]
src/arch/i386/core/start16.S [new file with mode: 0644]
src/arch/i386/core/start16.lds [new file with mode: 0644]
src/arch/i386/core/start16z.lds [new file with mode: 0644]
src/arch/i386/core/start32.S [new file with mode: 0644]
src/arch/i386/core/tagged_loader.c [new file with mode: 0644]
src/arch/i386/core/video_subr.c [new file with mode: 0644]
src/arch/i386/core/wince_loader.c [new file with mode: 0644]
src/arch/ia64/core/__call.S [new file with mode: 0644]
src/arch/ia64/core/efi.c [new file with mode: 0644]
src/arch/ia64/core/etherboot.lds [new file with mode: 0644]
src/arch/ia64/core/ia64_timer.c [new file with mode: 0644]
src/arch/ia64/core/idiv32.S [new file with mode: 0644]
src/arch/ia64/core/idiv64.S [new file with mode: 0644]
src/arch/ia64/core/longjmp.S [new file with mode: 0644]
src/arch/ia64/core/memmove.S [new file with mode: 0644]
src/arch/ia64/core/memset.S [new file with mode: 0644]
src/arch/ia64/core/pal.c [new file with mode: 0644]
src/arch/ia64/core/pci_io.c [new file with mode: 0644]
src/arch/ia64/core/reloc.S [new file with mode: 0644]
src/arch/ia64/core/relocate_to.S [new file with mode: 0644]
src/arch/ia64/core/sal.c [new file with mode: 0644]
src/arch/ia64/core/setjmp.S [new file with mode: 0644]
src/arch/ia64/core/start.S [new file with mode: 0644]
src/core/btext.c [new file with mode: 0644]
src/core/config.c [new file with mode: 0644]
src/core/disk.c [new file with mode: 0644]
src/core/dns_resolver.c [new file with mode: 0644]
src/core/elf_loader.c [new file with mode: 0644]
src/core/heap.c [new file with mode: 0644]
src/core/i82365.c [new file with mode: 0644]
src/core/isa_probe.c [new file with mode: 0644]
src/core/isapnp.c [new file with mode: 0644]
src/core/main.c [new file with mode: 0644]
src/core/misc.c [new file with mode: 0644]
src/core/nfs.c [new file with mode: 0644]
src/core/nic.c [new file with mode: 0644]
src/core/osloader.c [new file with mode: 0644]
src/core/pc_kbd.c [new file with mode: 0644]
src/core/pci.c [new file with mode: 0644]
src/core/pci_probe.c [new file with mode: 0644]
src/core/pcmcia.c [new file with mode: 0644]
src/core/proto_eth_slow.c [new file with mode: 0644]
src/core/proto_fsp.c [new file with mode: 0644]
src/core/proto_http.c [new file with mode: 0644]
src/core/proto_slam.c [new file with mode: 0644]
src/core/proto_tftm.c [new file with mode: 0644]
src/core/pxe_export.c [new file with mode: 0644]
src/core/relocate.c [new file with mode: 0644]
src/core/serial.c [new file with mode: 0644]
src/core/string.c [new file with mode: 0644]
src/core/timer.c [new file with mode: 0644]
src/core/vsprintf.c [new file with mode: 0644]

diff --git a/src/arch/armnommu/core/arm_timer.c b/src/arch/armnommu/core/arm_timer.c
new file mode 100644 (file)
index 0000000..b858ca8
--- /dev/null
@@ -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 (file)
index 0000000..0901a2f
--- /dev/null
@@ -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 (file)
index 0000000..e9e3a8f
--- /dev/null
@@ -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 (file)
index 0000000..a185c5d
--- /dev/null
@@ -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 (file)
index 0000000..1d49ffe
--- /dev/null
@@ -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 (file)
index 0000000..3ae98d4
--- /dev/null
@@ -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 (file)
index 0000000..7e0a3ee
--- /dev/null
@@ -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 (file)
index 0000000..61b1d31
--- /dev/null
@@ -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/e1/core/coff_loader.c b/src/arch/e1/core/coff_loader.c
new file mode 100644 (file)
index 0000000..d628e5e
--- /dev/null
@@ -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 (file)
index 0000000..cc4eaae
--- /dev/null
@@ -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 (file)
index 0000000..d3ddb64
--- /dev/null
@@ -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 (file)
index 0000000..75d2dba
--- /dev/null
@@ -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 (file)
index 0000000..1665f07
--- /dev/null
@@ -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 (file)
index 0000000..2347317
--- /dev/null
@@ -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 (file)
index 0000000..0581c9d
--- /dev/null
@@ -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 (file)
index 0000000..a8ceb53
--- /dev/null
@@ -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 (file)
index 0000000..63bcc48
--- /dev/null
@@ -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 (file)
index 0000000..691f39b
--- /dev/null
@@ -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 (file)
index 0000000..5f09daa
--- /dev/null
@@ -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/i386/core/aout_loader.c b/src/arch/i386/core/aout_loader.c
new file mode 100644 (file)
index 0000000..f85620e
--- /dev/null
@@ -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 (file)
index 0000000..d45e63e
--- /dev/null
@@ -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 (file)
index 0000000..8a0f733
--- /dev/null
@@ -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 (file)
index 0000000..6ef78c7
--- /dev/null
@@ -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 (file)
index 0000000..6f40632
--- /dev/null
@@ -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 (file)
index 0000000..3550a2a
--- /dev/null
@@ -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 (file)
index 0000000..464f6d9
--- /dev/null
@@ -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 = 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 = 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 *)phys_to_virt(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 (file)
index 0000000..9ca6f48
--- /dev/null
@@ -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 (file)
index 0000000..531183d
--- /dev/null
@@ -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 (file)
index 0000000..71717c2
--- /dev/null
@@ -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 (file)
index 0000000..e9785f1
--- /dev/null
@@ -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 (file)
index 0000000..e89561e
--- /dev/null
@@ -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 (file)
index 0000000..cc2071f
--- /dev/null
@@ -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 (file)
index 0000000..1c76128
--- /dev/null
@@ -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 (file)
index 0000000..bf6ea97
--- /dev/null
@@ -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 (file)
index 0000000..344d34f
--- /dev/null
@@ -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 (file)
index 0000000..1b61189
--- /dev/null
@@ -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 (file)
index 0000000..ef4ede8
--- /dev/null
@@ -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 (file)
index 0000000..28a5bfe
--- /dev/null
@@ -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 (file)
index 0000000..72fcfbf
--- /dev/null
@@ -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 (file)
index 0000000..544fc78
--- /dev/null
@@ -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 (file)
index 0000000..711bcf7
--- /dev/null
@@ -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 (file)
index 0000000..6dc3f20
--- /dev/null
@@ -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 (file)
index 0000000..9c6d6c2
--- /dev/null
@@ -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 (file)
index 0000000..dccdd97
--- /dev/null
@@ -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 (file)
index 0000000..f452b65
--- /dev/null
@@ -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/ia64/core/__call.S b/src/arch/ia64/core/__call.S
new file mode 100644 (file)
index 0000000..a11b826
--- /dev/null
@@ -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 (file)
index 0000000..97e585f
--- /dev/null
@@ -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 (file)
index 0000000..216cce9
--- /dev/null
@@ -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